xref: /dragonfly/sys/dev/sound/pci/t4dwave.c (revision 4e7eb5cc)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/sound/pci/t4dwave.c,v 1.9.2.11 2002/10/22 08:27:13 cognet Exp $
27  * $DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.3 2003/08/07 21:17:13 dillon Exp $
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/ac97.h>
32 #include <dev/sound/pci/t4dwave.h>
33 
34 #include <bus/pci/pcireg.h>
35 #include <bus/pci/pcivar.h>
36 
37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.3 2003/08/07 21:17:13 dillon Exp $");
38 /* -------------------------------------------------------------------- */
39 
40 #define TDX_PCI_ID 	0x20001023
41 #define TNX_PCI_ID 	0x20011023
42 #define ALI_PCI_ID	0x545110b9
43 #define SPA_PCI_ID	0x70181039
44 
45 #define TR_DEFAULT_BUFSZ 	0x1000
46 #define TR_TIMEOUT_CDC	0xffff
47 #define TR_MAXPLAYCH	4
48 
49 struct tr_info;
50 
51 /* channel registers */
52 struct tr_chinfo {
53 	u_int32_t cso, alpha, fms, fmc, ec;
54 	u_int32_t lba;
55 	u_int32_t eso, delta;
56 	u_int32_t rvol, cvol;
57 	u_int32_t gvsel, pan, vol, ctrl;
58 	u_int32_t active:1, was_active:1;
59 	int index, bufhalf;
60 	struct snd_dbuf *buffer;
61 	struct pcm_channel *channel;
62 	struct tr_info *parent;
63 };
64 
65 struct tr_rchinfo {
66 	u_int32_t delta;
67 	u_int32_t active:1, was_active:1;
68 	struct snd_dbuf *buffer;
69 	struct pcm_channel *channel;
70 	struct tr_info *parent;
71 };
72 
73 /* device private data */
74 struct tr_info {
75 	u_int32_t type;
76 	u_int32_t rev;
77 
78 	bus_space_tag_t st;
79 	bus_space_handle_t sh;
80 	bus_dma_tag_t parent_dmat;
81 
82 	struct resource *reg, *irq;
83 	int regtype, regid, irqid;
84 	void *ih;
85 
86 	void *lock;
87 
88 	u_int32_t playchns;
89 	unsigned int bufsz;
90 
91 	struct tr_chinfo chinfo[TR_MAXPLAYCH];
92 	struct tr_rchinfo recchinfo;
93 };
94 
95 /* -------------------------------------------------------------------- */
96 
97 static u_int32_t tr_recfmt[] = {
98 	AFMT_U8,
99 	AFMT_STEREO | AFMT_U8,
100 	AFMT_S8,
101 	AFMT_STEREO | AFMT_S8,
102 	AFMT_S16_LE,
103 	AFMT_STEREO | AFMT_S16_LE,
104 	AFMT_U16_LE,
105 	AFMT_STEREO | AFMT_U16_LE,
106 	0
107 };
108 static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0};
109 
110 static u_int32_t tr_playfmt[] = {
111 	AFMT_U8,
112 	AFMT_STEREO | AFMT_U8,
113 	AFMT_S8,
114 	AFMT_STEREO | AFMT_S8,
115 	AFMT_S16_LE,
116 	AFMT_STEREO | AFMT_S16_LE,
117 	AFMT_U16_LE,
118 	AFMT_STEREO | AFMT_U16_LE,
119 	0
120 };
121 static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0};
122 
123 /* -------------------------------------------------------------------- */
124 
125 /* Hardware */
126 
127 static u_int32_t
128 tr_rd(struct tr_info *tr, int regno, int size)
129 {
130 	switch(size) {
131 	case 1:
132 		return bus_space_read_1(tr->st, tr->sh, regno);
133 	case 2:
134 		return bus_space_read_2(tr->st, tr->sh, regno);
135 	case 4:
136 		return bus_space_read_4(tr->st, tr->sh, regno);
137 	default:
138 		return 0xffffffff;
139 	}
140 }
141 
142 static void
143 tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
144 {
145 	switch(size) {
146 	case 1:
147 		bus_space_write_1(tr->st, tr->sh, regno, data);
148 		break;
149 	case 2:
150 		bus_space_write_2(tr->st, tr->sh, regno, data);
151 		break;
152 	case 4:
153 		bus_space_write_4(tr->st, tr->sh, regno, data);
154 		break;
155 	}
156 }
157 
158 /* -------------------------------------------------------------------- */
159 /* ac97 codec */
160 
161 static int
162 tr_rdcd(kobj_t obj, void *devinfo, int regno)
163 {
164 	struct tr_info *tr = (struct tr_info *)devinfo;
165 	int i, j, treg, trw;
166 
167 	switch (tr->type) {
168 	case SPA_PCI_ID:
169 		treg=SPA_REG_CODECRD;
170 		trw=SPA_CDC_RWSTAT;
171 		break;
172 	case ALI_PCI_ID:
173 		if (tr->rev > 0x01)
174 		  treg=TDX_REG_CODECWR;
175 		else
176 		  treg=TDX_REG_CODECRD;
177 		trw=TDX_CDC_RWSTAT;
178 		break;
179 	case TDX_PCI_ID:
180 		treg=TDX_REG_CODECRD;
181 		trw=TDX_CDC_RWSTAT;
182 		break;
183 	case TNX_PCI_ID:
184 		treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
185 		trw=TNX_CDC_RWSTAT;
186 		break;
187 	default:
188 		printf("!!! tr_rdcd defaulted !!!\n");
189 		return -1;
190 	}
191 
192 	i = j = 0;
193 
194 	regno &= 0x7f;
195 	snd_mtxlock(tr->lock);
196 	if (tr->type == ALI_PCI_ID) {
197 		u_int32_t chk1, chk2;
198 		j = trw;
199 		for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--)
200 			j = tr_rd(tr, treg, 4);
201 		if (i > 0) {
202 			chk1 = tr_rd(tr, 0xc8, 4);
203 			chk2 = tr_rd(tr, 0xc8, 4);
204 			for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2);
205 					i--)
206 				chk2 = tr_rd(tr, 0xc8, 4);
207 		}
208 	}
209 	if (tr->type != ALI_PCI_ID || i > 0) {
210 		tr_wr(tr, treg, regno | trw, 4);
211 		j=trw;
212 		for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--)
213 		       	j=tr_rd(tr, treg, 4);
214 	}
215 	snd_mtxunlock(tr->lock);
216 	if (i == 0) printf("codec timeout during read of register %x\n", regno);
217 	return (j >> TR_CDC_DATA) & 0xffff;
218 }
219 
220 static int
221 tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
222 {
223 	struct tr_info *tr = (struct tr_info *)devinfo;
224 	int i, j, treg, trw;
225 
226 	switch (tr->type) {
227 	case SPA_PCI_ID:
228 		treg=SPA_REG_CODECWR;
229 		trw=SPA_CDC_RWSTAT;
230 		break;
231 	case ALI_PCI_ID:
232 	case TDX_PCI_ID:
233 		treg=TDX_REG_CODECWR;
234 		trw=TDX_CDC_RWSTAT;
235 		break;
236 	case TNX_PCI_ID:
237 		treg=TNX_REG_CODECWR;
238 		trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
239 		break;
240 	default:
241 		printf("!!! tr_wrcd defaulted !!!");
242 		return -1;
243 	}
244 
245 	i = 0;
246 
247 	regno &= 0x7f;
248 #if 0
249 	printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
250 #endif
251 	j=trw;
252 	snd_mtxlock(tr->lock);
253 	if (tr->type == ALI_PCI_ID) {
254 		j = trw;
255 		for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--)
256 			j = tr_rd(tr, treg, 4);
257 		if (i > 0) {
258 			u_int32_t chk1, chk2;
259 			chk1 = tr_rd(tr, 0xc8, 4);
260 			chk2 = tr_rd(tr, 0xc8, 4);
261 			for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2);
262 					i--)
263 				chk2 = tr_rd(tr, 0xc8, 4);
264 		}
265 	}
266 	if (tr->type != ALI_PCI_ID || i > 0) {
267 		for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--)
268 			j=tr_rd(tr, treg, 4);
269 		if (tr->type == ALI_PCI_ID && tr->rev > 0x01)
270 		      	trw |= 0x0100;
271 		tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
272 	}
273 #if 0
274 	printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
275 #endif
276 	snd_mtxunlock(tr->lock);
277 	if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
278 	return (i > 0)? 0 : -1;
279 }
280 
281 static kobj_method_t tr_ac97_methods[] = {
282     	KOBJMETHOD(ac97_read,		tr_rdcd),
283     	KOBJMETHOD(ac97_write,		tr_wrcd),
284 	{ 0, 0 }
285 };
286 AC97_DECLARE(tr_ac97);
287 
288 /* -------------------------------------------------------------------- */
289 /* playback channel interrupts */
290 
291 #if 0
292 static u_int32_t
293 tr_testint(struct tr_chinfo *ch)
294 {
295 	struct tr_info *tr = ch->parent;
296 	int bank, chan;
297 
298 	bank = (ch->index & 0x20) ? 1 : 0;
299 	chan = ch->index & 0x1f;
300 	return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan);
301 }
302 #endif
303 
304 static void
305 tr_clrint(struct tr_chinfo *ch)
306 {
307 	struct tr_info *tr = ch->parent;
308 	int bank, chan;
309 
310 	bank = (ch->index & 0x20) ? 1 : 0;
311 	chan = ch->index & 0x1f;
312 	tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4);
313 }
314 
315 static void
316 tr_enaint(struct tr_chinfo *ch, int enable)
317 {
318 	struct tr_info *tr = ch->parent;
319        	u_int32_t i, reg;
320 	int bank, chan;
321 
322 	snd_mtxlock(tr->lock);
323 	bank = (ch->index & 0x20) ? 1 : 0;
324 	chan = ch->index & 0x1f;
325 	reg = bank? TR_REG_INTENB : TR_REG_INTENA;
326 
327 	i = tr_rd(tr, reg, 4);
328 	i &= ~(1 << chan);
329 	i |= (enable? 1 : 0) << chan;
330 
331 	tr_clrint(ch);
332 	tr_wr(tr, reg, i, 4);
333 	snd_mtxunlock(tr->lock);
334 }
335 
336 /* playback channels */
337 
338 static void
339 tr_selch(struct tr_chinfo *ch)
340 {
341 	struct tr_info *tr = ch->parent;
342 	int i;
343 
344 	i = tr_rd(tr, TR_REG_CIR, 4);
345 	i &= ~TR_CIR_MASK;
346 	i |= ch->index & 0x3f;
347 	tr_wr(tr, TR_REG_CIR, i, 4);
348 }
349 
350 static void
351 tr_startch(struct tr_chinfo *ch)
352 {
353 	struct tr_info *tr = ch->parent;
354 	int bank, chan;
355 
356 	bank = (ch->index & 0x20) ? 1 : 0;
357 	chan = ch->index & 0x1f;
358 	tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4);
359 }
360 
361 static void
362 tr_stopch(struct tr_chinfo *ch)
363 {
364 	struct tr_info *tr = ch->parent;
365 	int bank, chan;
366 
367 	bank = (ch->index & 0x20) ? 1 : 0;
368 	chan = ch->index & 0x1f;
369 	tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4);
370 }
371 
372 static void
373 tr_wrch(struct tr_chinfo *ch)
374 {
375 	struct tr_info *tr = ch->parent;
376 	u_int32_t cr[TR_CHN_REGS], i;
377 
378 	ch->gvsel 	&= 0x00000001;
379 	ch->fmc		&= 0x00000003;
380 	ch->fms		&= 0x0000000f;
381 	ch->ctrl	&= 0x0000000f;
382 	ch->pan 	&= 0x0000007f;
383 	ch->rvol	&= 0x0000007f;
384 	ch->cvol 	&= 0x0000007f;
385 	ch->vol		&= 0x000000ff;
386 	ch->ec		&= 0x00000fff;
387 	ch->alpha	&= 0x00000fff;
388 	ch->delta	&= 0x0000ffff;
389 	ch->lba		&= 0x3fffffff;
390 
391 	cr[1]=ch->lba;
392 	cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol);
393 	cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec);
394 
395 	switch (tr->type) {
396 	case SPA_PCI_ID:
397 	case ALI_PCI_ID:
398 	case TDX_PCI_ID:
399 		ch->cso &= 0x0000ffff;
400 		ch->eso &= 0x0000ffff;
401 		cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
402 		cr[2]=(ch->eso<<16) | (ch->delta);
403 		break;
404 	case TNX_PCI_ID:
405 		ch->cso &= 0x00ffffff;
406 		ch->eso &= 0x00ffffff;
407 		cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
408 		cr[2]=((ch->delta>>8)<<24) | (ch->eso);
409 		cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
410 		break;
411 	}
412 	snd_mtxlock(tr->lock);
413 	tr_selch(ch);
414 	for (i=0; i<TR_CHN_REGS; i++)
415 		tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
416 	snd_mtxunlock(tr->lock);
417 }
418 
419 static void
420 tr_rdch(struct tr_chinfo *ch)
421 {
422 	struct tr_info *tr = ch->parent;
423 	u_int32_t cr[5], i;
424 
425 	snd_mtxlock(tr->lock);
426 	tr_selch(ch);
427 	for (i=0; i<5; i++)
428 		cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
429 	snd_mtxunlock(tr->lock);
430 
431 
432 	ch->lba=	(cr[1] & 0x3fffffff);
433 	ch->fmc=	(cr[3] & 0x0000c000) >> 14;
434 	ch->rvol=	(cr[3] & 0x00003f80) >> 7;
435 	ch->cvol=	(cr[3] & 0x0000007f);
436 	ch->gvsel=	(cr[4] & 0x80000000) >> 31;
437 	ch->pan=	(cr[4] & 0x7f000000) >> 24;
438 	ch->vol=	(cr[4] & 0x00ff0000) >> 16;
439 	ch->ctrl=	(cr[4] & 0x0000f000) >> 12;
440 	ch->ec=		(cr[4] & 0x00000fff);
441 	switch(tr->type) {
442 	case SPA_PCI_ID:
443 	case ALI_PCI_ID:
444 	case TDX_PCI_ID:
445 		ch->cso=	(cr[0] & 0xffff0000) >> 16;
446 		ch->alpha=	(cr[0] & 0x0000fff0) >> 4;
447 		ch->fms=	(cr[0] & 0x0000000f);
448 		ch->eso=	(cr[2] & 0xffff0000) >> 16;
449 		ch->delta=	(cr[2] & 0x0000ffff);
450 		break;
451 	case TNX_PCI_ID:
452 		ch->cso=	(cr[0] & 0x00ffffff);
453 		ch->eso=	(cr[2] & 0x00ffffff);
454 		ch->delta=	((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24);
455 		ch->alpha=	(cr[3] & 0xfff00000) >> 20;
456 		ch->fms=	(cr[3] & 0x000f0000) >> 16;
457 		break;
458 	}
459 }
460 
461 static u_int32_t
462 tr_fmttobits(u_int32_t fmt)
463 {
464 	u_int32_t bits;
465 
466 	bits = 0;
467 	bits |= (fmt & AFMT_SIGNED)? 0x2 : 0;
468 	bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
469 	bits |= (fmt & AFMT_16BIT)? 0x8 : 0;
470 
471 	return bits;
472 }
473 
474 /* -------------------------------------------------------------------- */
475 /* channel interface */
476 
477 static void *
478 trpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
479 {
480 	struct tr_info *tr = devinfo;
481 	struct tr_chinfo *ch;
482 
483 	KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction"));
484 	ch = &tr->chinfo[tr->playchns];
485 	ch->index = tr->playchns++;
486 	ch->buffer = b;
487 	ch->parent = tr;
488 	ch->channel = c;
489 	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, tr->bufsz) == -1)
490 		return NULL;
491 
492 	return ch;
493 }
494 
495 static int
496 trpchan_setformat(kobj_t obj, void *data, u_int32_t format)
497 {
498 	struct tr_chinfo *ch = data;
499 
500 	ch->ctrl = tr_fmttobits(format) | 0x01;
501 
502 	return 0;
503 }
504 
505 static int
506 trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
507 {
508 	struct tr_chinfo *ch = data;
509 
510 	ch->delta = (speed << 12) / 48000;
511 	return (ch->delta * 48000) >> 12;
512 }
513 
514 static int
515 trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
516 {
517 	struct tr_chinfo *ch = data;
518 
519 	sndbuf_resize(ch->buffer, 2, blocksize);
520 	return blocksize;
521 }
522 
523 static int
524 trpchan_trigger(kobj_t obj, void *data, int go)
525 {
526 	struct tr_chinfo *ch = data;
527 
528 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
529 		return 0;
530 
531 	if (go == PCMTRIG_START) {
532 		ch->fmc = 3;
533 		ch->fms = 0;
534 		ch->ec = 0;
535 		ch->alpha = 0;
536 		ch->lba = vtophys(sndbuf_getbuf(ch->buffer));
537 		ch->cso = 0;
538 		ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1;
539 		ch->rvol = ch->cvol = 0x7f;
540 		ch->gvsel = 0;
541 		ch->pan = 0;
542 		ch->vol = 0;
543 		ch->bufhalf = 0;
544    		tr_wrch(ch);
545 		tr_enaint(ch, 1);
546 		tr_startch(ch);
547 		ch->active = 1;
548 	} else {
549 		tr_stopch(ch);
550 		ch->active = 0;
551 	}
552 
553 	return 0;
554 }
555 
556 static int
557 trpchan_getptr(kobj_t obj, void *data)
558 {
559 	struct tr_chinfo *ch = data;
560 
561 	tr_rdch(ch);
562 	return ch->cso * sndbuf_getbps(ch->buffer);
563 }
564 
565 static struct pcmchan_caps *
566 trpchan_getcaps(kobj_t obj, void *data)
567 {
568 	return &tr_playcaps;
569 }
570 
571 static kobj_method_t trpchan_methods[] = {
572     	KOBJMETHOD(channel_init,		trpchan_init),
573     	KOBJMETHOD(channel_setformat,		trpchan_setformat),
574     	KOBJMETHOD(channel_setspeed,		trpchan_setspeed),
575     	KOBJMETHOD(channel_setblocksize,	trpchan_setblocksize),
576     	KOBJMETHOD(channel_trigger,		trpchan_trigger),
577     	KOBJMETHOD(channel_getptr,		trpchan_getptr),
578     	KOBJMETHOD(channel_getcaps,		trpchan_getcaps),
579 	{ 0, 0 }
580 };
581 CHANNEL_DECLARE(trpchan);
582 
583 /* -------------------------------------------------------------------- */
584 /* rec channel interface */
585 
586 static void *
587 trrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
588 {
589 	struct tr_info *tr = devinfo;
590 	struct tr_rchinfo *ch;
591 
592 	KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction"));
593 	ch = &tr->recchinfo;
594 	ch->buffer = b;
595 	ch->parent = tr;
596 	ch->channel = c;
597 	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, tr->bufsz) == -1)
598 		return NULL;
599 
600 	return ch;
601 }
602 
603 static int
604 trrchan_setformat(kobj_t obj, void *data, u_int32_t format)
605 {
606 	struct tr_rchinfo *ch = data;
607 	struct tr_info *tr = ch->parent;
608 	u_int32_t i, bits;
609 
610 	bits = tr_fmttobits(format);
611 	/* set # of samples between interrupts */
612 	i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1;
613 	tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
614 	/* set sample format */
615 	i = 0x18 | (bits << 4);
616 	tr_wr(tr, TR_REG_SBCTRL, i, 1);
617 
618 	return 0;
619 
620 }
621 
622 static int
623 trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
624 {
625 	struct tr_rchinfo *ch = data;
626 	struct tr_info *tr = ch->parent;
627 
628 	/* setup speed */
629 	ch->delta = (48000 << 12) / speed;
630 	tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
631 
632 	/* return closest possible speed */
633 	return (48000 << 12) / ch->delta;
634 }
635 
636 static int
637 trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
638 {
639 	struct tr_rchinfo *ch = data;
640 
641 	sndbuf_resize(ch->buffer, 2, blocksize);
642 
643 	return blocksize;
644 }
645 
646 static int
647 trrchan_trigger(kobj_t obj, void *data, int go)
648 {
649 	struct tr_rchinfo *ch = data;
650 	struct tr_info *tr = ch->parent;
651 	u_int32_t i;
652 
653 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
654 		return 0;
655 
656 	if (go == PCMTRIG_START) {
657 		/* set up dma mode regs */
658 		tr_wr(tr, TR_REG_DMAR15, 0, 1);
659 		i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
660 		tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
661 		/* set up base address */
662 	   	tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4);
663 		/* set up buffer size */
664 		i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
665 		tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4);
666 		/* start */
667 		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1);
668 		ch->active = 1;
669 	} else {
670 		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1);
671 		ch->active = 0;
672 	}
673 
674 	/* return 0 if ok */
675 	return 0;
676 }
677 
678 static int
679 trrchan_getptr(kobj_t obj, void *data)
680 {
681  	struct tr_rchinfo *ch = data;
682 	struct tr_info *tr = ch->parent;
683 
684 	/* return current byte offset of channel */
685 	return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer));
686 }
687 
688 static struct pcmchan_caps *
689 trrchan_getcaps(kobj_t obj, void *data)
690 {
691 	return &tr_reccaps;
692 }
693 
694 static kobj_method_t trrchan_methods[] = {
695     	KOBJMETHOD(channel_init,		trrchan_init),
696     	KOBJMETHOD(channel_setformat,		trrchan_setformat),
697     	KOBJMETHOD(channel_setspeed,		trrchan_setspeed),
698     	KOBJMETHOD(channel_setblocksize,	trrchan_setblocksize),
699     	KOBJMETHOD(channel_trigger,		trrchan_trigger),
700     	KOBJMETHOD(channel_getptr,		trrchan_getptr),
701     	KOBJMETHOD(channel_getcaps,		trrchan_getcaps),
702 	{ 0, 0 }
703 };
704 CHANNEL_DECLARE(trrchan);
705 
706 /* -------------------------------------------------------------------- */
707 /* The interrupt handler */
708 
709 static void
710 tr_intr(void *p)
711 {
712 	struct tr_info *tr = (struct tr_info *)p;
713 	struct tr_chinfo *ch;
714 	u_int32_t active, mask, bufhalf, chnum, intsrc;
715 	int tmp;
716 
717 	intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
718 	if (intsrc & TR_INT_ADDR) {
719 		chnum = 0;
720 		while (chnum < 64) {
721 			mask = 0x00000001;
722 			active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4);
723 			bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4);
724 			if (active) {
725 				do {
726 					if (active & mask) {
727 						tmp = (bufhalf & mask)? 1 : 0;
728 						if (chnum < tr->playchns) {
729 							ch = &tr->chinfo[chnum];
730 							/* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */
731 							if (ch->bufhalf != tmp) {
732 								chn_intr(ch->channel);
733 								ch->bufhalf = tmp;
734 							}
735 						}
736 					}
737 					chnum++;
738 					mask <<= 1;
739 				} while (chnum & 31);
740 			} else
741 				chnum += 32;
742 
743 			tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4);
744 		}
745 	}
746 	if (intsrc & TR_INT_SB) {
747 		chn_intr(tr->recchinfo.channel);
748 		tr_rd(tr, TR_REG_SBR9, 1);
749 		tr_rd(tr, TR_REG_SBR10, 1);
750 	}
751 }
752 
753 /* -------------------------------------------------------------------- */
754 
755 /*
756  * Probe and attach the card
757  */
758 
759 static int
760 tr_init(struct tr_info *tr)
761 {
762 	switch (tr->type) {
763 	case SPA_PCI_ID:
764 		tr_wr(tr, SPA_REG_GPIO, 0, 4);
765 		tr_wr(tr, SPA_REG_CODECST, SPA_RST_OFF, 4);
766 		break;
767 	case TDX_PCI_ID:
768 		tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
769 		break;
770 	case TNX_PCI_ID:
771 		tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
772 		break;
773 	}
774 
775 	tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
776 	return 0;
777 }
778 
779 static int
780 tr_pci_probe(device_t dev)
781 {
782 	switch (pci_get_devid(dev)) {
783 		case SPA_PCI_ID:
784 			device_set_desc(dev, "SiS 7018");
785 			return 0;
786 		case ALI_PCI_ID:
787 			device_set_desc(dev, "Acer Labs M5451");
788 			return 0;
789 		case TDX_PCI_ID:
790 			device_set_desc(dev, "Trident 4DWave DX");
791 			return 0;
792 		case TNX_PCI_ID:
793 			device_set_desc(dev, "Trident 4DWave NX");
794 			return 0;
795 	}
796 
797 	return ENXIO;
798 }
799 
800 static int
801 tr_pci_attach(device_t dev)
802 {
803 	u_int32_t	data;
804 	struct tr_info *tr;
805 	struct ac97_info *codec = 0;
806 	int		i;
807 	char 		status[SND_STATUSLEN];
808 
809 	if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
810 		device_printf(dev, "cannot allocate softc\n");
811 		return ENXIO;
812 	}
813 
814 	tr->type = pci_get_devid(dev);
815 	tr->rev = pci_get_revid(dev);
816 	tr->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
817 
818 	data = pci_read_config(dev, PCIR_COMMAND, 2);
819 	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
820 	pci_write_config(dev, PCIR_COMMAND, data, 2);
821 	data = pci_read_config(dev, PCIR_COMMAND, 2);
822 
823 	tr->regid = PCIR_MAPS;
824 	tr->regtype = SYS_RES_IOPORT;
825 	tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE);
826 	if (tr->reg) {
827 		tr->st = rman_get_bustag(tr->reg);
828 		tr->sh = rman_get_bushandle(tr->reg);
829 	} else {
830 		device_printf(dev, "unable to map register space\n");
831 		goto bad;
832 	}
833 
834 	tr->bufsz = pcm_getbuffersize(dev, 4096, TR_DEFAULT_BUFSZ, 65536);
835 
836 	if (tr_init(tr) == -1) {
837 		device_printf(dev, "unable to initialize the card\n");
838 		goto bad;
839 	}
840 	tr->playchns = 0;
841 
842 	codec = AC97_CREATE(dev, tr, tr_ac97);
843 	if (codec == NULL) goto bad;
844 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
845 
846 	tr->irqid = 0;
847 	tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
848 				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
849 	if (!tr->irq || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih)) {
850 		device_printf(dev, "unable to map interrupt\n");
851 		goto bad;
852 	}
853 
854 	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
855 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
856 		/*highaddr*/BUS_SPACE_MAXADDR,
857 		/*filter*/NULL, /*filterarg*/NULL,
858 		/*maxsize*/tr->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
859 		/*flags*/0, &tr->parent_dmat) != 0) {
860 		device_printf(dev, "unable to create dma tag\n");
861 		goto bad;
862 	}
863 
864 	snprintf(status, 64, "at io 0x%lx irq %ld",
865 		 rman_get_start(tr->reg), rman_get_start(tr->irq));
866 
867 	if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
868 	pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr);
869 	for (i = 0; i < TR_MAXPLAYCH; i++)
870 		pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr);
871 	pcm_setstatus(dev, status);
872 
873 	return 0;
874 
875 bad:
876 	if (codec) ac97_destroy(codec);
877 	if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
878 	if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
879 	if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
880 	if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat);
881 	if (tr->lock) snd_mtxfree(tr->lock);
882 	free(tr, M_DEVBUF);
883 	return ENXIO;
884 }
885 
886 static int
887 tr_pci_detach(device_t dev)
888 {
889 	int r;
890 	struct tr_info *tr;
891 
892 	r = pcm_unregister(dev);
893 	if (r)
894 		return r;
895 
896 	tr = pcm_getdevinfo(dev);
897 	bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
898 	bus_teardown_intr(dev, tr->irq, tr->ih);
899 	bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
900 	bus_dma_tag_destroy(tr->parent_dmat);
901 	snd_mtxfree(tr->lock);
902 	free(tr, M_DEVBUF);
903 
904 	return 0;
905 }
906 
907 static int
908 tr_pci_suspend(device_t dev)
909 {
910 	int i;
911 	struct tr_info *tr;
912 
913 	tr = pcm_getdevinfo(dev);
914 
915 	for (i = 0; i < tr->playchns; i++) {
916 		tr->chinfo[i].was_active = tr->chinfo[i].active;
917 		if (tr->chinfo[i].active) {
918 			trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_STOP);
919 		}
920 	}
921 
922 	tr->recchinfo.was_active = tr->recchinfo.active;
923 	if (tr->recchinfo.active) {
924 		trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_STOP);
925 	}
926 
927 	return 0;
928 }
929 
930 static int
931 tr_pci_resume(device_t dev)
932 {
933 	int i;
934 	struct tr_info *tr;
935 
936 	tr = pcm_getdevinfo(dev);
937 
938 	if (tr_init(tr) == -1) {
939 		device_printf(dev, "unable to initialize the card\n");
940 		return ENXIO;
941 	}
942 
943 	if (mixer_reinit(dev) == -1) {
944 		device_printf(dev, "unable to initialize the mixer\n");
945 		return ENXIO;
946 	}
947 
948 	for (i = 0; i < tr->playchns; i++) {
949 		if (tr->chinfo[i].was_active) {
950 			trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_START);
951 		}
952 	}
953 
954 	if (tr->recchinfo.was_active) {
955 		trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_START);
956 	}
957 
958 	return 0;
959 }
960 
961 static device_method_t tr_methods[] = {
962 	/* Device interface */
963 	DEVMETHOD(device_probe,		tr_pci_probe),
964 	DEVMETHOD(device_attach,	tr_pci_attach),
965 	DEVMETHOD(device_detach,	tr_pci_detach),
966 	DEVMETHOD(device_suspend,	tr_pci_suspend),
967 	DEVMETHOD(device_resume,	tr_pci_resume),
968 	{ 0, 0 }
969 };
970 
971 static driver_t tr_driver = {
972 	"pcm",
973 	tr_methods,
974 	PCM_SOFTC_SIZE,
975 };
976 
977 DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0);
978 MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
979 MODULE_VERSION(snd_t4dwave, 1);
980