xref: /freebsd/sys/dev/sound/pci/t4dwave.c (revision 874f2e78b47661ef3103e8ad0f51eb7783a322dd)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * All rights reserved.
4987e5972SCameron Grant  *
5987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
6987e5972SCameron Grant  * modification, are permitted provided that the following conditions
7987e5972SCameron Grant  * are met:
8987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
9987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
10987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
11987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
12987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
13987e5972SCameron Grant  *
14987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24987e5972SCameron Grant  * SUCH DAMAGE.
25987e5972SCameron Grant  */
26987e5972SCameron Grant 
27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
29ef9308b1SCameron Grant #include <dev/sound/pci/t4dwave.h>
30987e5972SCameron Grant 
31987e5972SCameron Grant #include <pci/pcireg.h>
32987e5972SCameron Grant #include <pci/pcivar.h>
33987e5972SCameron Grant 
3467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3567b1dce3SCameron Grant 
36987e5972SCameron Grant /* -------------------------------------------------------------------- */
37987e5972SCameron Grant 
38dedf4424SCameron Grant #define TDX_PCI_ID 	0x20001023
39dedf4424SCameron Grant #define TNX_PCI_ID 	0x20011023
40874f2e78SGeorge C A Reid #define SPA_PCI_ID	0x70181039
41dedf4424SCameron Grant 
427ed9700bSCameron Grant #define TR_BUFFSIZE 	0x1000
43dedf4424SCameron Grant #define TR_TIMEOUT_CDC	0xffff
44dedf4424SCameron Grant #define TR_MAXPLAYCH	4
45dedf4424SCameron Grant 
46987e5972SCameron Grant struct tr_info;
47987e5972SCameron Grant 
48987e5972SCameron Grant /* channel registers */
49987e5972SCameron Grant struct tr_chinfo {
50987e5972SCameron Grant 	u_int32_t cso, alpha, fms, fmc, ec;
51987e5972SCameron Grant 	u_int32_t lba;
52987e5972SCameron Grant 	u_int32_t eso, delta;
53987e5972SCameron Grant 	u_int32_t rvol, cvol;
54987e5972SCameron Grant 	u_int32_t gvsel, pan, vol, ctrl;
557ed9700bSCameron Grant 	int index, bufhalf;
5666ef8af5SCameron Grant 	struct snd_dbuf *buffer;
5766ef8af5SCameron Grant 	struct pcm_channel *channel;
58987e5972SCameron Grant 	struct tr_info *parent;
59987e5972SCameron Grant };
60987e5972SCameron Grant 
61f0fb042eSCameron Grant struct tr_rchinfo {
62f0fb042eSCameron Grant 	u_int32_t delta;
6366ef8af5SCameron Grant 	struct snd_dbuf *buffer;
6466ef8af5SCameron Grant 	struct pcm_channel *channel;
65f0fb042eSCameron Grant 	struct tr_info *parent;
66f0fb042eSCameron Grant };
67f0fb042eSCameron Grant 
68987e5972SCameron Grant /* device private data */
69987e5972SCameron Grant struct tr_info {
70987e5972SCameron Grant 	u_int32_t type;
71987e5972SCameron Grant 
72987e5972SCameron Grant 	bus_space_tag_t st;
73987e5972SCameron Grant 	bus_space_handle_t sh;
74987e5972SCameron Grant 	bus_dma_tag_t parent_dmat;
75987e5972SCameron Grant 
76987e5972SCameron Grant 	struct resource *reg, *irq;
77987e5972SCameron Grant 	int		regtype, regid, irqid;
78987e5972SCameron Grant 	void		*ih;
79987e5972SCameron Grant 
8066ef8af5SCameron Grant 	void *lock;
8166ef8af5SCameron Grant 
82987e5972SCameron Grant 	u_int32_t playchns;
83987e5972SCameron Grant 	struct tr_chinfo chinfo[TR_MAXPLAYCH];
84f0fb042eSCameron Grant 	struct tr_rchinfo recchinfo;
85987e5972SCameron Grant };
86987e5972SCameron Grant 
87987e5972SCameron Grant /* -------------------------------------------------------------------- */
88987e5972SCameron Grant 
89513693beSCameron Grant static u_int32_t tr_recfmt[] = {
90513693beSCameron Grant 	AFMT_U8,
91513693beSCameron Grant 	AFMT_STEREO | AFMT_U8,
92513693beSCameron Grant 	AFMT_S8,
93513693beSCameron Grant 	AFMT_STEREO | AFMT_S8,
94513693beSCameron Grant 	AFMT_S16_LE,
95513693beSCameron Grant 	AFMT_STEREO | AFMT_S16_LE,
96513693beSCameron Grant 	AFMT_U16_LE,
97513693beSCameron Grant 	AFMT_STEREO | AFMT_U16_LE,
98513693beSCameron Grant 	0
99987e5972SCameron Grant };
10066ef8af5SCameron Grant static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0};
101987e5972SCameron Grant 
102513693beSCameron Grant static u_int32_t tr_playfmt[] = {
103513693beSCameron Grant 	AFMT_U8,
104513693beSCameron Grant 	AFMT_STEREO | AFMT_U8,
105513693beSCameron Grant 	AFMT_S8,
106513693beSCameron Grant 	AFMT_STEREO | AFMT_S8,
107513693beSCameron Grant 	AFMT_S16_LE,
108513693beSCameron Grant 	AFMT_STEREO | AFMT_S16_LE,
109513693beSCameron Grant 	AFMT_U16_LE,
110513693beSCameron Grant 	AFMT_STEREO | AFMT_U16_LE,
111513693beSCameron Grant 	0
112987e5972SCameron Grant };
11366ef8af5SCameron Grant static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0};
114987e5972SCameron Grant 
115987e5972SCameron Grant /* -------------------------------------------------------------------- */
116987e5972SCameron Grant 
117987e5972SCameron Grant /* Hardware */
118987e5972SCameron Grant 
119987e5972SCameron Grant static u_int32_t
120987e5972SCameron Grant tr_rd(struct tr_info *tr, int regno, int size)
121987e5972SCameron Grant {
122987e5972SCameron Grant 	switch(size) {
123987e5972SCameron Grant 	case 1:
124987e5972SCameron Grant 		return bus_space_read_1(tr->st, tr->sh, regno);
125987e5972SCameron Grant 	case 2:
126987e5972SCameron Grant 		return bus_space_read_2(tr->st, tr->sh, regno);
127987e5972SCameron Grant 	case 4:
128987e5972SCameron Grant 		return bus_space_read_4(tr->st, tr->sh, regno);
129987e5972SCameron Grant 	default:
130987e5972SCameron Grant 		return 0xffffffff;
131987e5972SCameron Grant 	}
132987e5972SCameron Grant }
133987e5972SCameron Grant 
134987e5972SCameron Grant static void
135987e5972SCameron Grant tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
136987e5972SCameron Grant {
137987e5972SCameron Grant 	switch(size) {
138987e5972SCameron Grant 	case 1:
139987e5972SCameron Grant 		bus_space_write_1(tr->st, tr->sh, regno, data);
140987e5972SCameron Grant 		break;
141987e5972SCameron Grant 	case 2:
142987e5972SCameron Grant 		bus_space_write_2(tr->st, tr->sh, regno, data);
143987e5972SCameron Grant 		break;
144987e5972SCameron Grant 	case 4:
145987e5972SCameron Grant 		bus_space_write_4(tr->st, tr->sh, regno, data);
146987e5972SCameron Grant 		break;
147987e5972SCameron Grant 	}
148987e5972SCameron Grant }
149987e5972SCameron Grant 
1500f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
151987e5972SCameron Grant /* ac97 codec */
152987e5972SCameron Grant 
1530f55ac6cSCameron Grant static int
1540f55ac6cSCameron Grant tr_rdcd(kobj_t obj, void *devinfo, int regno)
155987e5972SCameron Grant {
156987e5972SCameron Grant 	struct tr_info *tr = (struct tr_info *)devinfo;
157987e5972SCameron Grant 	int i, j, treg, trw;
158987e5972SCameron Grant 
159987e5972SCameron Grant 	switch (tr->type) {
160874f2e78SGeorge C A Reid 	case SPA_PCI_ID:
161874f2e78SGeorge C A Reid 		treg=SPA_REG_CODECRD;
162874f2e78SGeorge C A Reid 		trw=SPA_CDC_RWSTAT;
163874f2e78SGeorge C A Reid 		break;
164987e5972SCameron Grant 	case TDX_PCI_ID:
165987e5972SCameron Grant 		treg=TDX_REG_CODECRD;
166987e5972SCameron Grant 		trw=TDX_CDC_RWSTAT;
167987e5972SCameron Grant 		break;
168987e5972SCameron Grant 	case TNX_PCI_ID:
169987e5972SCameron Grant 		treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
170987e5972SCameron Grant 		trw=TNX_CDC_RWSTAT;
171987e5972SCameron Grant 		break;
172987e5972SCameron Grant 	default:
173987e5972SCameron Grant 		printf("!!! tr_rdcd defaulted !!!\n");
1740f55ac6cSCameron Grant 		return -1;
175987e5972SCameron Grant 	}
176987e5972SCameron Grant 
177987e5972SCameron Grant 	regno &= 0x7f;
17866ef8af5SCameron Grant 	snd_mtxlock(tr->lock);
179987e5972SCameron Grant 	tr_wr(tr, treg, regno | trw, 4);
180987e5972SCameron Grant 	j=trw;
181987e5972SCameron Grant 	for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
18266ef8af5SCameron Grant 	snd_mtxunlock(tr->lock);
183987e5972SCameron Grant 	if (i == 0) printf("codec timeout during read of register %x\n", regno);
184987e5972SCameron Grant 	return (j >> TR_CDC_DATA) & 0xffff;
185987e5972SCameron Grant }
186987e5972SCameron Grant 
1870f55ac6cSCameron Grant static int
1880f55ac6cSCameron Grant tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
189987e5972SCameron Grant {
190987e5972SCameron Grant 	struct tr_info *tr = (struct tr_info *)devinfo;
191987e5972SCameron Grant 	int i, j, treg, trw;
192987e5972SCameron Grant 
193987e5972SCameron Grant 	switch (tr->type) {
194874f2e78SGeorge C A Reid 	case SPA_PCI_ID:
195874f2e78SGeorge C A Reid 		treg=SPA_REG_CODECWR;
196874f2e78SGeorge C A Reid 		trw=SPA_CDC_RWSTAT;
197874f2e78SGeorge C A Reid 		break;
198987e5972SCameron Grant 	case TDX_PCI_ID:
199987e5972SCameron Grant 		treg=TDX_REG_CODECWR;
200987e5972SCameron Grant 		trw=TDX_CDC_RWSTAT;
201987e5972SCameron Grant 		break;
202987e5972SCameron Grant 	case TNX_PCI_ID:
203987e5972SCameron Grant 		treg=TNX_REG_CODECWR;
204987e5972SCameron Grant 		trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
205987e5972SCameron Grant 		break;
206987e5972SCameron Grant 	default:
207987e5972SCameron Grant 		printf("!!! tr_wrcd defaulted !!!");
2080f55ac6cSCameron Grant 		return -1;
209987e5972SCameron Grant 	}
210987e5972SCameron Grant 
211987e5972SCameron Grant 	regno &= 0x7f;
212987e5972SCameron Grant #if 0
213987e5972SCameron Grant 	printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
214987e5972SCameron Grant #endif
215987e5972SCameron Grant 	j=trw;
21666ef8af5SCameron Grant 	snd_mtxlock(tr->lock);
217987e5972SCameron Grant 	for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
218987e5972SCameron Grant 	tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
219987e5972SCameron Grant #if 0
220987e5972SCameron Grant 	printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
221987e5972SCameron Grant #endif
22266ef8af5SCameron Grant 	snd_mtxunlock(tr->lock);
223987e5972SCameron Grant 	if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
2240f55ac6cSCameron Grant 	return (i > 0)? 0 : -1;
225987e5972SCameron Grant }
226987e5972SCameron Grant 
2270f55ac6cSCameron Grant static kobj_method_t tr_ac97_methods[] = {
2280f55ac6cSCameron Grant     	KOBJMETHOD(ac97_read,		tr_rdcd),
2290f55ac6cSCameron Grant     	KOBJMETHOD(ac97_write,		tr_wrcd),
2300f55ac6cSCameron Grant 	{ 0, 0 }
2310f55ac6cSCameron Grant };
2320f55ac6cSCameron Grant AC97_DECLARE(tr_ac97);
2330f55ac6cSCameron Grant 
2340f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
235987e5972SCameron Grant /* playback channel interrupts */
236987e5972SCameron Grant 
2377ed9700bSCameron Grant #if 0
238987e5972SCameron Grant static u_int32_t
239f0fb042eSCameron Grant tr_testint(struct tr_chinfo *ch)
240987e5972SCameron Grant {
241f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
242f0fb042eSCameron Grant 	int bank, chan;
243f0fb042eSCameron Grant 
244f0fb042eSCameron Grant 	bank = (ch->index & 0x20) ? 1 : 0;
245f0fb042eSCameron Grant 	chan = ch->index & 0x1f;
246f0fb042eSCameron Grant 	return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan);
247987e5972SCameron Grant }
2487ed9700bSCameron Grant #endif
249987e5972SCameron Grant 
250987e5972SCameron Grant static void
251f0fb042eSCameron Grant tr_clrint(struct tr_chinfo *ch)
252987e5972SCameron Grant {
253f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
254f0fb042eSCameron Grant 	int bank, chan;
255f0fb042eSCameron Grant 
256f0fb042eSCameron Grant 	bank = (ch->index & 0x20) ? 1 : 0;
257f0fb042eSCameron Grant 	chan = ch->index & 0x1f;
258f0fb042eSCameron Grant 	tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4);
259987e5972SCameron Grant }
260987e5972SCameron Grant 
261987e5972SCameron Grant static void
262f0fb042eSCameron Grant tr_enaint(struct tr_chinfo *ch, int enable)
263987e5972SCameron Grant {
264f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
265f0fb042eSCameron Grant        	u_int32_t i, reg;
266f0fb042eSCameron Grant 	int bank, chan;
267f0fb042eSCameron Grant 
26866ef8af5SCameron Grant 	snd_mtxlock(tr->lock);
269f0fb042eSCameron Grant 	bank = (ch->index & 0x20) ? 1 : 0;
270f0fb042eSCameron Grant 	chan = ch->index & 0x1f;
271f0fb042eSCameron Grant 	reg = bank? TR_REG_INTENB : TR_REG_INTENA;
272f0fb042eSCameron Grant 
273f0fb042eSCameron Grant 	i = tr_rd(tr, reg, 4);
274f0fb042eSCameron Grant 	i &= ~(1 << chan);
275f0fb042eSCameron Grant 	i |= (enable? 1 : 0) << chan;
276f0fb042eSCameron Grant 
277f0fb042eSCameron Grant 	tr_clrint(ch);
278987e5972SCameron Grant 	tr_wr(tr, reg, i, 4);
27966ef8af5SCameron Grant 	snd_mtxunlock(tr->lock);
280987e5972SCameron Grant }
281987e5972SCameron Grant 
282987e5972SCameron Grant /* playback channels */
283987e5972SCameron Grant 
284987e5972SCameron Grant static void
285f0fb042eSCameron Grant tr_selch(struct tr_chinfo *ch)
286987e5972SCameron Grant {
287f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
288f0fb042eSCameron Grant 	int i;
289f0fb042eSCameron Grant 
290f0fb042eSCameron Grant 	i = tr_rd(tr, TR_REG_CIR, 4);
291987e5972SCameron Grant 	i &= ~TR_CIR_MASK;
292f0fb042eSCameron Grant 	i |= ch->index & 0x3f;
293987e5972SCameron Grant 	tr_wr(tr, TR_REG_CIR, i, 4);
294987e5972SCameron Grant }
295987e5972SCameron Grant 
296987e5972SCameron Grant static void
297f0fb042eSCameron Grant tr_startch(struct tr_chinfo *ch)
298987e5972SCameron Grant {
299f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
300f0fb042eSCameron Grant 	int bank, chan;
301f0fb042eSCameron Grant 
302f0fb042eSCameron Grant 	bank = (ch->index & 0x20) ? 1 : 0;
303f0fb042eSCameron Grant 	chan = ch->index & 0x1f;
304f0fb042eSCameron Grant 	tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4);
305987e5972SCameron Grant }
306987e5972SCameron Grant 
307987e5972SCameron Grant static void
308f0fb042eSCameron Grant tr_stopch(struct tr_chinfo *ch)
309987e5972SCameron Grant {
310f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
311f0fb042eSCameron Grant 	int bank, chan;
312f0fb042eSCameron Grant 
313f0fb042eSCameron Grant 	bank = (ch->index & 0x20) ? 1 : 0;
314f0fb042eSCameron Grant 	chan = ch->index & 0x1f;
315f0fb042eSCameron Grant 	tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4);
316987e5972SCameron Grant }
317987e5972SCameron Grant 
318987e5972SCameron Grant static void
319f0fb042eSCameron Grant tr_wrch(struct tr_chinfo *ch)
320987e5972SCameron Grant {
321f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
322987e5972SCameron Grant 	u_int32_t cr[TR_CHN_REGS], i;
323987e5972SCameron Grant 
324987e5972SCameron Grant 	ch->gvsel 	&= 0x00000001;
325987e5972SCameron Grant 	ch->fmc		&= 0x00000003;
326987e5972SCameron Grant 	ch->fms		&= 0x0000000f;
327987e5972SCameron Grant 	ch->ctrl	&= 0x0000000f;
328987e5972SCameron Grant 	ch->pan 	&= 0x0000007f;
329987e5972SCameron Grant 	ch->rvol	&= 0x0000007f;
330987e5972SCameron Grant 	ch->cvol 	&= 0x0000007f;
331987e5972SCameron Grant 	ch->vol		&= 0x000000ff;
332987e5972SCameron Grant 	ch->ec		&= 0x00000fff;
333987e5972SCameron Grant 	ch->alpha	&= 0x00000fff;
334987e5972SCameron Grant 	ch->delta	&= 0x0000ffff;
335987e5972SCameron Grant 	ch->lba		&= 0x3fffffff;
336987e5972SCameron Grant 
337987e5972SCameron Grant 	cr[1]=ch->lba;
3387ed9700bSCameron Grant 	cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol);
339987e5972SCameron Grant 	cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec);
340987e5972SCameron Grant 
341987e5972SCameron Grant 	switch (tr->type) {
342874f2e78SGeorge C A Reid 	case SPA_PCI_ID:
343987e5972SCameron Grant 	case TDX_PCI_ID:
344987e5972SCameron Grant 		ch->cso &= 0x0000ffff;
345987e5972SCameron Grant 		ch->eso &= 0x0000ffff;
346987e5972SCameron Grant 		cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
347987e5972SCameron Grant 		cr[2]=(ch->eso<<16) | (ch->delta);
348987e5972SCameron Grant 		break;
349987e5972SCameron Grant 	case TNX_PCI_ID:
350987e5972SCameron Grant 		ch->cso &= 0x00ffffff;
351987e5972SCameron Grant 		ch->eso &= 0x00ffffff;
352987e5972SCameron Grant 		cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
35348875621SCameron Grant 		cr[2]=((ch->delta>>8)<<24) | (ch->eso);
354987e5972SCameron Grant 		cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
355987e5972SCameron Grant 		break;
356987e5972SCameron Grant 	}
35766ef8af5SCameron Grant 	snd_mtxlock(tr->lock);
358f0fb042eSCameron Grant 	tr_selch(ch);
359987e5972SCameron Grant 	for (i=0; i<TR_CHN_REGS; i++)
360987e5972SCameron Grant 		tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
36166ef8af5SCameron Grant 	snd_mtxunlock(tr->lock);
362987e5972SCameron Grant }
363987e5972SCameron Grant 
364987e5972SCameron Grant static void
365f0fb042eSCameron Grant tr_rdch(struct tr_chinfo *ch)
366987e5972SCameron Grant {
367f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
368987e5972SCameron Grant 	u_int32_t cr[5], i;
369f0fb042eSCameron Grant 
37066ef8af5SCameron Grant 	snd_mtxlock(tr->lock);
371f0fb042eSCameron Grant 	tr_selch(ch);
372f0fb042eSCameron Grant 	for (i=0; i<5; i++)
373f0fb042eSCameron Grant 		cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
37466ef8af5SCameron Grant 	snd_mtxunlock(tr->lock);
375f0fb042eSCameron Grant 
376f0fb042eSCameron Grant 
377987e5972SCameron Grant 	ch->lba=	(cr[1] & 0x3fffffff);
378987e5972SCameron Grant 	ch->fmc=	(cr[3] & 0x0000c000) >> 14;
379987e5972SCameron Grant 	ch->rvol=	(cr[3] & 0x00003f80) >> 7;
380987e5972SCameron Grant 	ch->cvol=	(cr[3] & 0x0000007f);
381987e5972SCameron Grant 	ch->gvsel=	(cr[4] & 0x80000000) >> 31;
382987e5972SCameron Grant 	ch->pan=	(cr[4] & 0x7f000000) >> 24;
383987e5972SCameron Grant 	ch->vol=	(cr[4] & 0x00ff0000) >> 16;
384987e5972SCameron Grant 	ch->ctrl=	(cr[4] & 0x0000f000) >> 12;
385987e5972SCameron Grant 	ch->ec=		(cr[4] & 0x00000fff);
386987e5972SCameron Grant 	switch(tr->type) {
387874f2e78SGeorge C A Reid 	case SPA_PCI_ID:
388987e5972SCameron Grant 	case TDX_PCI_ID:
389987e5972SCameron Grant 		ch->cso=	(cr[0] & 0xffff0000) >> 16;
390987e5972SCameron Grant 		ch->alpha=	(cr[0] & 0x0000fff0) >> 4;
391987e5972SCameron Grant 		ch->fms=	(cr[0] & 0x0000000f);
392987e5972SCameron Grant 		ch->eso=	(cr[2] & 0xffff0000) >> 16;
393987e5972SCameron Grant 		ch->delta=	(cr[2] & 0x0000ffff);
394987e5972SCameron Grant 		break;
395987e5972SCameron Grant 	case TNX_PCI_ID:
396987e5972SCameron Grant 		ch->cso=	(cr[0] & 0x00ffffff);
397987e5972SCameron Grant 		ch->eso=	(cr[2] & 0x00ffffff);
398f0fb042eSCameron Grant 		ch->delta=	((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24);
399987e5972SCameron Grant 		ch->alpha=	(cr[3] & 0xfff00000) >> 20;
400987e5972SCameron Grant 		ch->fms=	(cr[3] & 0x000f0000) >> 16;
401987e5972SCameron Grant 		break;
402987e5972SCameron Grant 	}
403987e5972SCameron Grant }
404987e5972SCameron Grant 
4057ed9700bSCameron Grant static u_int32_t
4067ed9700bSCameron Grant tr_fmttobits(u_int32_t fmt)
4077ed9700bSCameron Grant {
4087ed9700bSCameron Grant 	u_int32_t bits;
4097ed9700bSCameron Grant 
4107ed9700bSCameron Grant 	bits = 0;
4117ed9700bSCameron Grant 	bits |= (fmt & AFMT_SIGNED)? 0x2 : 0;
4127ed9700bSCameron Grant 	bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
4137ed9700bSCameron Grant 	bits |= (fmt & AFMT_16BIT)? 0x8 : 0;
4147ed9700bSCameron Grant 
4157ed9700bSCameron Grant 	return bits;
4167ed9700bSCameron Grant }
4177ed9700bSCameron Grant 
4180f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
419987e5972SCameron Grant /* channel interface */
420987e5972SCameron Grant 
4210f55ac6cSCameron Grant static void *
42266ef8af5SCameron Grant trpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
423987e5972SCameron Grant {
424987e5972SCameron Grant 	struct tr_info *tr = devinfo;
425987e5972SCameron Grant 	struct tr_chinfo *ch;
426f0fb042eSCameron Grant 
427f0fb042eSCameron Grant 	KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction"));
428987e5972SCameron Grant 	ch = &tr->chinfo[tr->playchns];
429987e5972SCameron Grant 	ch->index = tr->playchns++;
430987e5972SCameron Grant 	ch->buffer = b;
431987e5972SCameron Grant 	ch->parent = tr;
432987e5972SCameron Grant 	ch->channel = c;
433f0fb042eSCameron Grant 	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1)
434f0fb042eSCameron Grant 		return NULL;
435f0fb042eSCameron Grant 
436f0fb042eSCameron Grant 	return ch;
437987e5972SCameron Grant }
438987e5972SCameron Grant 
439987e5972SCameron Grant static int
440f0fb042eSCameron Grant trpchan_setformat(kobj_t obj, void *data, u_int32_t format)
441987e5972SCameron Grant {
442987e5972SCameron Grant 	struct tr_chinfo *ch = data;
443f0fb042eSCameron Grant 
444f0fb042eSCameron Grant 	ch->ctrl = tr_fmttobits(format) | 0x01;
445f0fb042eSCameron Grant 
446f0fb042eSCameron Grant 	return 0;
447f0fb042eSCameron Grant }
448f0fb042eSCameron Grant 
449f0fb042eSCameron Grant static int
450f0fb042eSCameron Grant trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
451f0fb042eSCameron Grant {
452f0fb042eSCameron Grant 	struct tr_chinfo *ch = data;
453f0fb042eSCameron Grant 
454f0fb042eSCameron Grant 	ch->delta = (speed << 12) / 48000;
455f0fb042eSCameron Grant 	return (ch->delta * 48000) >> 12;
456f0fb042eSCameron Grant }
457f0fb042eSCameron Grant 
458f0fb042eSCameron Grant static int
459f0fb042eSCameron Grant trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
460f0fb042eSCameron Grant {
461f0fb042eSCameron Grant 	struct tr_chinfo *ch = data;
462f0fb042eSCameron Grant 
463f0fb042eSCameron Grant 	sndbuf_resize(ch->buffer, 2, blocksize);
464f0fb042eSCameron Grant 	return blocksize;
465f0fb042eSCameron Grant }
466f0fb042eSCameron Grant 
467f0fb042eSCameron Grant static int
468f0fb042eSCameron Grant trpchan_trigger(kobj_t obj, void *data, int go)
469f0fb042eSCameron Grant {
470f0fb042eSCameron Grant 	struct tr_chinfo *ch = data;
471f0fb042eSCameron Grant 
472f0fb042eSCameron Grant 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
473f0fb042eSCameron Grant 		return 0;
474f0fb042eSCameron Grant 
475f0fb042eSCameron Grant 	if (go == PCMTRIG_START) {
4767ed9700bSCameron Grant 		ch->fmc = 3;
4777ed9700bSCameron Grant 		ch->fms = 0;
4787ed9700bSCameron Grant 		ch->ec = 0;
4797ed9700bSCameron Grant 		ch->alpha = 0;
480350a5fafSCameron Grant 		ch->lba = vtophys(sndbuf_getbuf(ch->buffer));
481987e5972SCameron Grant 		ch->cso = 0;
482f0fb042eSCameron Grant 		ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1;
4837ed9700bSCameron Grant 		ch->rvol = ch->cvol = 0x7f;
484987e5972SCameron Grant 		ch->gvsel = 0;
485987e5972SCameron Grant 		ch->pan = 0;
486987e5972SCameron Grant 		ch->vol = 0;
4877ed9700bSCameron Grant 		ch->bufhalf = 0;
488f0fb042eSCameron Grant    		tr_wrch(ch);
489f0fb042eSCameron Grant 		tr_enaint(ch, 1);
490f0fb042eSCameron Grant 		tr_startch(ch);
491f0fb042eSCameron Grant 	} else
492f0fb042eSCameron Grant 		tr_stopch(ch);
493f0fb042eSCameron Grant 
494f0fb042eSCameron Grant 	return 0;
495f0fb042eSCameron Grant }
496f0fb042eSCameron Grant 
497f0fb042eSCameron Grant static int
498f0fb042eSCameron Grant trpchan_getptr(kobj_t obj, void *data)
499f0fb042eSCameron Grant {
500f0fb042eSCameron Grant 	struct tr_chinfo *ch = data;
501f0fb042eSCameron Grant 
502f0fb042eSCameron Grant 	tr_rdch(ch);
503f0fb042eSCameron Grant 	return ch->cso * sndbuf_getbps(ch->buffer);
504f0fb042eSCameron Grant }
505f0fb042eSCameron Grant 
50666ef8af5SCameron Grant static struct pcmchan_caps *
507f0fb042eSCameron Grant trpchan_getcaps(kobj_t obj, void *data)
508f0fb042eSCameron Grant {
509f0fb042eSCameron Grant 	return &tr_playcaps;
510f0fb042eSCameron Grant }
511f0fb042eSCameron Grant 
512f0fb042eSCameron Grant static kobj_method_t trpchan_methods[] = {
513f0fb042eSCameron Grant     	KOBJMETHOD(channel_init,		trpchan_init),
514f0fb042eSCameron Grant     	KOBJMETHOD(channel_setformat,		trpchan_setformat),
515f0fb042eSCameron Grant     	KOBJMETHOD(channel_setspeed,		trpchan_setspeed),
516f0fb042eSCameron Grant     	KOBJMETHOD(channel_setblocksize,	trpchan_setblocksize),
517f0fb042eSCameron Grant     	KOBJMETHOD(channel_trigger,		trpchan_trigger),
518f0fb042eSCameron Grant     	KOBJMETHOD(channel_getptr,		trpchan_getptr),
519f0fb042eSCameron Grant     	KOBJMETHOD(channel_getcaps,		trpchan_getcaps),
520f0fb042eSCameron Grant 	{ 0, 0 }
521f0fb042eSCameron Grant };
522f0fb042eSCameron Grant CHANNEL_DECLARE(trpchan);
523f0fb042eSCameron Grant 
524f0fb042eSCameron Grant /* -------------------------------------------------------------------- */
525f0fb042eSCameron Grant /* rec channel interface */
526f0fb042eSCameron Grant 
527f0fb042eSCameron Grant static void *
52866ef8af5SCameron Grant trrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
529f0fb042eSCameron Grant {
530f0fb042eSCameron Grant 	struct tr_info *tr = devinfo;
531f0fb042eSCameron Grant 	struct tr_rchinfo *ch;
532f0fb042eSCameron Grant 
533f0fb042eSCameron Grant 	KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction"));
534f0fb042eSCameron Grant 	ch = &tr->recchinfo;
535f0fb042eSCameron Grant 	ch->buffer = b;
536f0fb042eSCameron Grant 	ch->parent = tr;
537f0fb042eSCameron Grant 	ch->channel = c;
538f0fb042eSCameron Grant 	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1)
539f0fb042eSCameron Grant 		return NULL;
540f0fb042eSCameron Grant 
541f0fb042eSCameron Grant 	return ch;
542f0fb042eSCameron Grant }
543f0fb042eSCameron Grant 
544f0fb042eSCameron Grant static int
545f0fb042eSCameron Grant trrchan_setformat(kobj_t obj, void *data, u_int32_t format)
546f0fb042eSCameron Grant {
547f0fb042eSCameron Grant 	struct tr_rchinfo *ch = data;
548f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
549f0fb042eSCameron Grant 	u_int32_t i, bits;
550f0fb042eSCameron Grant 
551f0fb042eSCameron Grant 	bits = tr_fmttobits(format);
552f0fb042eSCameron Grant 	/* set # of samples between interrupts */
5537ed9700bSCameron Grant 	i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1;
554f0fb042eSCameron Grant 	tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
555f0fb042eSCameron Grant 	/* set sample format */
556f0fb042eSCameron Grant 	i = 0x18 | (bits << 4);
557f0fb042eSCameron Grant 	tr_wr(tr, TR_REG_SBCTRL, i, 1);
558f0fb042eSCameron Grant 
559f0fb042eSCameron Grant 	return 0;
560f0fb042eSCameron Grant 
561f0fb042eSCameron Grant }
562f0fb042eSCameron Grant 
563f0fb042eSCameron Grant static int
564f0fb042eSCameron Grant trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
565f0fb042eSCameron Grant {
566f0fb042eSCameron Grant 	struct tr_rchinfo *ch = data;
567f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
568f0fb042eSCameron Grant 
569f0fb042eSCameron Grant 	/* setup speed */
570f0fb042eSCameron Grant 	ch->delta = (48000 << 12) / speed;
571f0fb042eSCameron Grant 	tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
572f0fb042eSCameron Grant 
573f0fb042eSCameron Grant 	/* return closest possible speed */
574f0fb042eSCameron Grant 	return (48000 << 12) / ch->delta;
575f0fb042eSCameron Grant }
576f0fb042eSCameron Grant 
577f0fb042eSCameron Grant static int
578f0fb042eSCameron Grant trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
579f0fb042eSCameron Grant {
580f0fb042eSCameron Grant 	struct tr_rchinfo *ch = data;
581f0fb042eSCameron Grant 
582f0fb042eSCameron Grant 	sndbuf_resize(ch->buffer, 2, blocksize);
583f0fb042eSCameron Grant 
584f0fb042eSCameron Grant 	return blocksize;
585f0fb042eSCameron Grant }
586f0fb042eSCameron Grant 
587f0fb042eSCameron Grant static int
588f0fb042eSCameron Grant trrchan_trigger(kobj_t obj, void *data, int go)
589f0fb042eSCameron Grant {
590f0fb042eSCameron Grant 	struct tr_rchinfo *ch = data;
591f0fb042eSCameron Grant 	struct tr_info *tr = ch->parent;
592987e5972SCameron Grant 	u_int32_t i;
593f0fb042eSCameron Grant 
594f0fb042eSCameron Grant 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
595f0fb042eSCameron Grant 		return 0;
596f0fb042eSCameron Grant 
597f0fb042eSCameron Grant 	if (go == PCMTRIG_START) {
598f0fb042eSCameron Grant 		/* set up dma mode regs */
599987e5972SCameron Grant 		tr_wr(tr, TR_REG_DMAR15, 0, 1);
600987e5972SCameron Grant 		i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
601987e5972SCameron Grant 		tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
602987e5972SCameron Grant 		/* set up base address */
603350a5fafSCameron Grant 	   	tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4);
604987e5972SCameron Grant 		/* set up buffer size */
605987e5972SCameron Grant 		i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
606f0fb042eSCameron Grant 		tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4);
607f0fb042eSCameron Grant 		/* start */
608f0fb042eSCameron Grant 		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1);
609f0fb042eSCameron Grant 	} else
610f0fb042eSCameron Grant 		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1);
611f0fb042eSCameron Grant 
612f0fb042eSCameron Grant 	/* return 0 if ok */
613987e5972SCameron Grant 	return 0;
614987e5972SCameron Grant }
615987e5972SCameron Grant 
616987e5972SCameron Grant static int
617f0fb042eSCameron Grant trrchan_getptr(kobj_t obj, void *data)
618987e5972SCameron Grant {
619f0fb042eSCameron Grant  	struct tr_rchinfo *ch = data;
620987e5972SCameron Grant 	struct tr_info *tr = ch->parent;
621987e5972SCameron Grant 
622f0fb042eSCameron Grant 	/* return current byte offset of channel */
623f0fb042eSCameron Grant 	return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer));
624987e5972SCameron Grant }
625987e5972SCameron Grant 
62666ef8af5SCameron Grant static struct pcmchan_caps *
627f0fb042eSCameron Grant trrchan_getcaps(kobj_t obj, void *data)
628987e5972SCameron Grant {
629f0fb042eSCameron Grant 	return &tr_reccaps;
630987e5972SCameron Grant }
631987e5972SCameron Grant 
632f0fb042eSCameron Grant static kobj_method_t trrchan_methods[] = {
633f0fb042eSCameron Grant     	KOBJMETHOD(channel_init,		trrchan_init),
634f0fb042eSCameron Grant     	KOBJMETHOD(channel_setformat,		trrchan_setformat),
635f0fb042eSCameron Grant     	KOBJMETHOD(channel_setspeed,		trrchan_setspeed),
636f0fb042eSCameron Grant     	KOBJMETHOD(channel_setblocksize,	trrchan_setblocksize),
637f0fb042eSCameron Grant     	KOBJMETHOD(channel_trigger,		trrchan_trigger),
638f0fb042eSCameron Grant     	KOBJMETHOD(channel_getptr,		trrchan_getptr),
639f0fb042eSCameron Grant     	KOBJMETHOD(channel_getcaps,		trrchan_getcaps),
6400f55ac6cSCameron Grant 	{ 0, 0 }
6410f55ac6cSCameron Grant };
642f0fb042eSCameron Grant CHANNEL_DECLARE(trrchan);
6430f55ac6cSCameron Grant 
6440f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
645987e5972SCameron Grant /* The interrupt handler */
646987e5972SCameron Grant 
647987e5972SCameron Grant static void
648987e5972SCameron Grant tr_intr(void *p)
649987e5972SCameron Grant {
650987e5972SCameron Grant 	struct tr_info *tr = (struct tr_info *)p;
651f0fb042eSCameron Grant 	struct tr_chinfo *ch;
6527ed9700bSCameron Grant 	u_int32_t active, mask, bufhalf, chnum, intsrc;
6537ed9700bSCameron Grant 	int tmp;
654987e5972SCameron Grant 
655f0fb042eSCameron Grant 	intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
656987e5972SCameron Grant 	if (intsrc & TR_INT_ADDR) {
6577ed9700bSCameron Grant 		chnum = 0;
6587ed9700bSCameron Grant 		while (chnum < 64) {
6597ed9700bSCameron Grant 			mask = 0x00000001;
6607ed9700bSCameron Grant 			active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4);
6617ed9700bSCameron Grant 			bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4);
6627ed9700bSCameron Grant 			if (active) {
6637ed9700bSCameron Grant 				do {
6647ed9700bSCameron Grant 					if (active & mask) {
6657ed9700bSCameron Grant 						tmp = (bufhalf & mask)? 1 : 0;
6667ed9700bSCameron Grant 						if (chnum < tr->playchns) {
6677ed9700bSCameron Grant 							ch = &tr->chinfo[chnum];
6687ed9700bSCameron Grant 							/* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */
6697ed9700bSCameron Grant 							if (ch->bufhalf != tmp) {
670f0fb042eSCameron Grant 								chn_intr(ch->channel);
6717ed9700bSCameron Grant 								ch->bufhalf = tmp;
67266ef8af5SCameron Grant 							}
673987e5972SCameron Grant 						}
674987e5972SCameron Grant 					}
6757ed9700bSCameron Grant 					chnum++;
6767ed9700bSCameron Grant 					mask <<= 1;
6777ed9700bSCameron Grant 				} while (chnum & 31);
6787ed9700bSCameron Grant 			} else
6797ed9700bSCameron Grant 				chnum += 32;
6807ed9700bSCameron Grant 
6817ed9700bSCameron Grant 			tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4);
6827ed9700bSCameron Grant 		}
683987e5972SCameron Grant 	}
684987e5972SCameron Grant 	if (intsrc & TR_INT_SB) {
685987e5972SCameron Grant 		chn_intr(tr->recchinfo.channel);
686987e5972SCameron Grant 		tr_rd(tr, TR_REG_SBR9, 1);
687987e5972SCameron Grant 		tr_rd(tr, TR_REG_SBR10, 1);
688987e5972SCameron Grant 	}
689987e5972SCameron Grant }
690987e5972SCameron Grant 
691987e5972SCameron Grant /* -------------------------------------------------------------------- */
692987e5972SCameron Grant 
693987e5972SCameron Grant /*
694987e5972SCameron Grant  * Probe and attach the card
695987e5972SCameron Grant  */
696987e5972SCameron Grant 
697987e5972SCameron Grant static int
698987e5972SCameron Grant tr_init(struct tr_info *tr)
699987e5972SCameron Grant {
700874f2e78SGeorge C A Reid 	switch (tr->type) {
701874f2e78SGeorge C A Reid 	case SPA_PCI_ID:
702874f2e78SGeorge C A Reid 		tr_wr(tr, SPA_REG_GPIO, 0, 4);
703874f2e78SGeorge C A Reid 		tr_wr(tr, SPA_REG_CODECST, SPA_RST_OFF, 4);
704874f2e78SGeorge C A Reid 		break;
705874f2e78SGeorge C A Reid 	case TDX_PCI_ID:
706987e5972SCameron Grant 		tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
707874f2e78SGeorge C A Reid 		break;
708874f2e78SGeorge C A Reid 	case TNX_PCI_ID:
709874f2e78SGeorge C A Reid 		tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
710874f2e78SGeorge C A Reid 		break;
711874f2e78SGeorge C A Reid 	}
712987e5972SCameron Grant 
713987e5972SCameron Grant 	tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
714987e5972SCameron Grant 	tr->playchns = 0;
715987e5972SCameron Grant 	return 0;
716987e5972SCameron Grant }
717987e5972SCameron Grant 
718987e5972SCameron Grant static int
719987e5972SCameron Grant tr_pci_probe(device_t dev)
720987e5972SCameron Grant {
721874f2e78SGeorge C A Reid 	switch (pci_get_devid(dev)) {
722874f2e78SGeorge C A Reid 		case SPA_PCI_ID:
723874f2e78SGeorge C A Reid 			device_set_desc(dev, "SiS 7018");
724874f2e78SGeorge C A Reid 			return 0;
725874f2e78SGeorge C A Reid 		case TDX_PCI_ID:
726987e5972SCameron Grant 			device_set_desc(dev, "Trident 4DWave DX");
727987e5972SCameron Grant 			return 0;
728874f2e78SGeorge C A Reid 		case TNX_PCI_ID:
729987e5972SCameron Grant 			device_set_desc(dev, "Trident 4DWave NX");
730987e5972SCameron Grant 			return 0;
731987e5972SCameron Grant 	}
732987e5972SCameron Grant 
733987e5972SCameron Grant 	return ENXIO;
734987e5972SCameron Grant }
735987e5972SCameron Grant 
736987e5972SCameron Grant static int
737987e5972SCameron Grant tr_pci_attach(device_t dev)
738987e5972SCameron Grant {
739987e5972SCameron Grant 	u_int32_t	data;
740987e5972SCameron Grant 	struct tr_info *tr;
7417dfc9325SCameron Grant 	struct ac97_info *codec = 0;
742987e5972SCameron Grant 	int		i;
743987e5972SCameron Grant 	char 		status[SND_STATUSLEN];
744987e5972SCameron Grant 
745733a4ea7SGeorge C A Reid 	if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
746987e5972SCameron Grant 		device_printf(dev, "cannot allocate softc\n");
747987e5972SCameron Grant 		return ENXIO;
748987e5972SCameron Grant 	}
749987e5972SCameron Grant 
750987e5972SCameron Grant 	tr->type = pci_get_devid(dev);
75166ef8af5SCameron Grant 	tr->lock = snd_mtxcreate(device_get_nameunit(dev));
752987e5972SCameron Grant 
753987e5972SCameron Grant 	data = pci_read_config(dev, PCIR_COMMAND, 2);
754987e5972SCameron Grant 	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
755987e5972SCameron Grant 	pci_write_config(dev, PCIR_COMMAND, data, 2);
756987e5972SCameron Grant 	data = pci_read_config(dev, PCIR_COMMAND, 2);
757987e5972SCameron Grant 
758f0fb042eSCameron Grant 	tr->regid = PCIR_MAPS;
759987e5972SCameron Grant 	tr->regtype = SYS_RES_IOPORT;
760f0fb042eSCameron Grant 	tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE);
761987e5972SCameron Grant 	if (tr->reg) {
762987e5972SCameron Grant 		tr->st = rman_get_bustag(tr->reg);
763987e5972SCameron Grant 		tr->sh = rman_get_bushandle(tr->reg);
764f0fb042eSCameron Grant 	} else {
765987e5972SCameron Grant 		device_printf(dev, "unable to map register space\n");
766987e5972SCameron Grant 		goto bad;
767987e5972SCameron Grant 	}
768987e5972SCameron Grant 
769987e5972SCameron Grant 	if (tr_init(tr) == -1) {
770987e5972SCameron Grant 		device_printf(dev, "unable to initialize the card\n");
771987e5972SCameron Grant 		goto bad;
772987e5972SCameron Grant 	}
773987e5972SCameron Grant 
7740f55ac6cSCameron Grant 	codec = AC97_CREATE(dev, tr, tr_ac97);
775987e5972SCameron Grant 	if (codec == NULL) goto bad;
7760f55ac6cSCameron Grant 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
777987e5972SCameron Grant 
778987e5972SCameron Grant 	tr->irqid = 0;
779987e5972SCameron Grant 	tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
780987e5972SCameron Grant 				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
78166ef8af5SCameron Grant 	if (!tr->irq || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih)) {
782987e5972SCameron Grant 		device_printf(dev, "unable to map interrupt\n");
783987e5972SCameron Grant 		goto bad;
784987e5972SCameron Grant 	}
785987e5972SCameron Grant 
786987e5972SCameron Grant 	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
787987e5972SCameron Grant 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
788987e5972SCameron Grant 		/*highaddr*/BUS_SPACE_MAXADDR,
789987e5972SCameron Grant 		/*filter*/NULL, /*filterarg*/NULL,
790987e5972SCameron Grant 		/*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
791987e5972SCameron Grant 		/*flags*/0, &tr->parent_dmat) != 0) {
792987e5972SCameron Grant 		device_printf(dev, "unable to create dma tag\n");
793987e5972SCameron Grant 		goto bad;
794987e5972SCameron Grant 	}
795987e5972SCameron Grant 
796f0fb042eSCameron Grant 	snprintf(status, 64, "at io 0x%lx irq %ld",
797987e5972SCameron Grant 		 rman_get_start(tr->reg), rman_get_start(tr->irq));
798987e5972SCameron Grant 
799987e5972SCameron Grant 	if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
800f0fb042eSCameron Grant 	pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr);
801987e5972SCameron Grant 	for (i = 0; i < TR_MAXPLAYCH; i++)
802f0fb042eSCameron Grant 		pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr);
803987e5972SCameron Grant 	pcm_setstatus(dev, status);
804987e5972SCameron Grant 
805987e5972SCameron Grant 	return 0;
806987e5972SCameron Grant 
807987e5972SCameron Grant bad:
808306f91b6SCameron Grant 	if (codec) ac97_destroy(codec);
809987e5972SCameron Grant 	if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
810987e5972SCameron Grant 	if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
811987e5972SCameron Grant 	if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
812306f91b6SCameron Grant 	if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat);
81366ef8af5SCameron Grant 	if (tr->lock) snd_mtxfree(tr->lock);
814987e5972SCameron Grant 	free(tr, M_DEVBUF);
815987e5972SCameron Grant 	return ENXIO;
816987e5972SCameron Grant }
817987e5972SCameron Grant 
818306f91b6SCameron Grant static int
819306f91b6SCameron Grant tr_pci_detach(device_t dev)
820306f91b6SCameron Grant {
821306f91b6SCameron Grant 	int r;
822306f91b6SCameron Grant 	struct tr_info *tr;
823306f91b6SCameron Grant 
824306f91b6SCameron Grant 	r = pcm_unregister(dev);
825306f91b6SCameron Grant 	if (r)
826306f91b6SCameron Grant 		return r;
827306f91b6SCameron Grant 
828306f91b6SCameron Grant 	tr = pcm_getdevinfo(dev);
829306f91b6SCameron Grant 	bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
830306f91b6SCameron Grant 	bus_teardown_intr(dev, tr->irq, tr->ih);
831306f91b6SCameron Grant 	bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
832306f91b6SCameron Grant 	bus_dma_tag_destroy(tr->parent_dmat);
83366ef8af5SCameron Grant 	snd_mtxfree(tr->lock);
834306f91b6SCameron Grant 	free(tr, M_DEVBUF);
835306f91b6SCameron Grant 
836306f91b6SCameron Grant 	return 0;
837306f91b6SCameron Grant }
838306f91b6SCameron Grant 
839987e5972SCameron Grant static device_method_t tr_methods[] = {
840987e5972SCameron Grant 	/* Device interface */
841987e5972SCameron Grant 	DEVMETHOD(device_probe,		tr_pci_probe),
842987e5972SCameron Grant 	DEVMETHOD(device_attach,	tr_pci_attach),
843306f91b6SCameron Grant 	DEVMETHOD(device_detach,	tr_pci_detach),
844987e5972SCameron Grant 
845987e5972SCameron Grant 	{ 0, 0 }
846987e5972SCameron Grant };
847987e5972SCameron Grant 
848987e5972SCameron Grant static driver_t tr_driver = {
849987e5972SCameron Grant 	"pcm",
850987e5972SCameron Grant 	tr_methods,
85167b1dce3SCameron Grant 	PCM_SOFTC_SIZE,
852987e5972SCameron Grant };
853987e5972SCameron Grant 
854f314f3daSCameron Grant DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0);
855f314f3daSCameron Grant MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
856f314f3daSCameron Grant MODULE_VERSION(snd_t4dwave, 1);
857