via82c686.c revision 91539
1/*
2 * Copyright (c) 2000 David Jones <dej@ox.org>
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, WHETHER IN 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 THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Support for VT8233 added by Orion Hodson <oho@acm.org> and tested
29 * by Russell Davies <rd@zip.com.au>.
30 */
31
32#include <dev/sound/pcm/sound.h>
33#include <dev/sound/pcm/ac97.h>
34
35#include <pci/pcireg.h>
36#include <pci/pcivar.h>
37#include <sys/sysctl.h>
38
39#include <dev/sound/pci/via82c686.h>
40
41SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/via82c686.c 91539 2002-03-01 20:30:13Z orion $");
42
43#define VIA686A_PCI_ID 0x30581106
44#define VIA8233_PCI_ID 0x30591106
45
46#define	NSEGS		4	/* Number of segments in SGD table */
47
48#define SEGS_PER_CHAN	(NSEGS/2)
49
50#define TIMEOUT	50
51#define	VIA_DEFAULT_BUFSZ	0x1000
52
53#undef DEB
54#define DEB(x)
55
56/* we rely on this struct being packed to 64 bits */
57struct via_dma_op {
58        u_int32_t ptr;
59        u_int32_t flags;
60#define VIA_DMAOP_EOL         0x80000000
61#define VIA_DMAOP_FLAG        0x40000000
62#define VIA_DMAOP_STOP        0x20000000
63#define VIA_DMAOP_COUNT(x)    ((x)&0x00FFFFFF)
64};
65
66struct via_info;
67
68struct via_chinfo {
69	struct via_info *parent;
70	struct pcm_channel *channel;
71	struct snd_dbuf *buffer;
72	struct via_dma_op *sgd_table;
73	int dir, blksz;
74
75	int rbase; /* base register for channel */
76};
77
78struct via_info {
79	bus_space_tag_t st;
80	bus_space_handle_t sh;
81	bus_dma_tag_t parent_dmat;
82	bus_dma_tag_t sgd_dmat;
83	bus_dmamap_t sgd_dmamap;
84
85	struct resource *reg, *irq;
86	int regid, irqid;
87	void *ih;
88	struct ac97_info *codec;
89
90	unsigned int bufsz;
91
92	struct via_chinfo pch, rch;
93	struct via_dma_op *sgd_table;
94	u_int16_t codec_caps;
95};
96
97static u_int32_t via_fmt[] = {
98	AFMT_U8,
99	AFMT_STEREO | AFMT_U8,
100	AFMT_S16_LE,
101	AFMT_STEREO | AFMT_S16_LE,
102	0
103};
104static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0};
105static struct pcmchan_caps via_caps = {48000, 48000, via_fmt, 0};
106
107static u_int32_t
108via_rd(struct via_info *via, int regno, int size)
109{
110	switch (size) {
111	case 1:
112		return bus_space_read_1(via->st, via->sh, regno);
113	case 2:
114		return bus_space_read_2(via->st, via->sh, regno);
115	case 4:
116		return bus_space_read_4(via->st, via->sh, regno);
117	default:
118		return 0xFFFFFFFF;
119	}
120}
121
122
123static void
124via_wr(struct via_info *via, int regno, u_int32_t data, int size)
125{
126
127	switch (size) {
128	case 1:
129		bus_space_write_1(via->st, via->sh, regno, data);
130		break;
131	case 2:
132		bus_space_write_2(via->st, via->sh, regno, data);
133		break;
134	case 4:
135		bus_space_write_4(via->st, via->sh, regno, data);
136		break;
137	}
138}
139
140/* -------------------------------------------------------------------- */
141/* Codec interface */
142
143static int
144via_waitready_codec(struct via_info *via)
145{
146	int i;
147
148	/* poll until codec not busy */
149	for (i = 0; (i < TIMEOUT) &&
150	    (via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_BUSY); i++)
151		DELAY(1);
152	if (i >= TIMEOUT) {
153		printf("via: codec busy\n");
154		return 1;
155	}
156
157	return 0;
158}
159
160
161static int
162via_waitvalid_codec(struct via_info *via)
163{
164	int i;
165
166	/* poll until codec valid */
167	for (i = 0; (i < TIMEOUT) &&
168	    !(via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_PRIVALID); i++)
169		    DELAY(1);
170	if (i >= TIMEOUT) {
171		printf("via: codec invalid\n");
172		return 1;
173	}
174
175	return 0;
176}
177
178
179static int
180via_write_codec(kobj_t obj, void *addr, int reg, u_int32_t val)
181{
182	struct via_info *via = addr;
183
184	if (via_waitready_codec(via)) return -1;
185
186	via_wr(via, VIA_CODEC_CTL,
187	       VIA_CODEC_PRIVALID | VIA_CODEC_INDEX(reg) | val, 4);
188
189	return 0;
190}
191
192
193static int
194via_read_codec(kobj_t obj, void *addr, int reg)
195{
196	struct via_info *via = addr;
197
198	if (via_waitready_codec(via))
199		return -1;
200
201	via_wr(via, VIA_CODEC_CTL, VIA_CODEC_PRIVALID | VIA_CODEC_READ | VIA_CODEC_INDEX(reg),4);
202
203	if (via_waitready_codec(via))
204		return -1;
205
206	if (via_waitvalid_codec(via))
207		return -1;
208
209	return via_rd(via, VIA_CODEC_CTL, 2);
210}
211
212static kobj_method_t via_ac97_methods[] = {
213    	KOBJMETHOD(ac97_read,		via_read_codec),
214    	KOBJMETHOD(ac97_write,		via_write_codec),
215	{ 0, 0 }
216};
217AC97_DECLARE(via_ac97);
218
219/* -------------------------------------------------------------------- */
220
221static int
222via_buildsgdt(struct via_chinfo *ch)
223{
224	u_int32_t phys_addr, flag;
225	int i, segs, seg_size;
226
227	/*
228	 *  Build the scatter/gather DMA (SGD) table.
229	 *  There are four slots in the table: two for play, two for record.
230	 *  This creates two half-buffers, one of which is playing; the other
231	 *  is feeding.
232	 */
233	seg_size = ch->blksz;
234	segs = sndbuf_getsize(ch->buffer) / seg_size;
235	phys_addr = vtophys(sndbuf_getbuf(ch->buffer));
236
237	for (i = 0; i < segs; i++) {
238		flag = (i == segs - 1)? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
239		ch->sgd_table[i].ptr = phys_addr + (i * seg_size);
240		ch->sgd_table[i].flags = flag | seg_size;
241	}
242
243	return 0;
244}
245
246/* channel interface */
247static void *
248viachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
249	     struct pcm_channel *c, int dir)
250{
251	struct via_info *via = devinfo;
252	struct via_chinfo *ch = (dir == PCMDIR_PLAY)? &via->pch : &via->rch;
253
254	ch->parent = via;
255	ch->channel = c;
256	ch->buffer = b;
257	ch->dir = dir;
258	ch->sgd_table = &via->sgd_table[(dir == PCMDIR_PLAY)? 0 : SEGS_PER_CHAN];
259	if (ch->dir == PCMDIR_PLAY) {
260		ch->rbase = VIA_PLAY_BASE;
261	} else {
262		ch->rbase = VIA_REC_BASE;
263	}
264
265	if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) == -1)
266		return NULL;
267	return ch;
268}
269
270static int
271viachan_setformat(kobj_t obj, void *data, u_int32_t format)
272{
273	struct via_chinfo *ch = data;
274	struct via_info *via = ch->parent;
275
276	u_int32_t r = ch->rbase + VIA_RP_MODE;
277	u_int8_t  v = via_rd(via, r, 1);
278
279	v &= ~(VIA_RPMODE_STEREO | VIA_RPMODE_16BIT);
280	if (format & AFMT_STEREO)
281		v |= VIA_RPMODE_STEREO;
282	if (format & AFMT_S16_LE)
283		v |= VIA_RPMODE_16BIT;
284	via_wr(via, r, v, 1);
285
286	return 0;
287}
288
289static int
290viachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
291{
292	struct via_chinfo *ch = data;
293	struct via_info *via = ch->parent;
294	int reg;
295
296	/*
297	 *  Basic AC'97 defines a 48 kHz sample rate only.  For other rates,
298	 *  upsampling is required.
299	 *
300	 *  The VT82C686A does not perform upsampling, and neither do we.
301	 *  If the codec supports variable-rate audio (i.e. does the upsampling
302	 *  itself), then negotiate the rate with the codec.  Otherwise,
303	 *  return 48 kHz cuz that's all you got.
304	 */
305	if (via->codec_caps & AC97_EXTCAP_VRA) {
306		reg = (ch->dir == PCMDIR_PLAY)? AC97_REGEXT_FDACRATE : AC97_REGEXT_LADCRATE;
307		return ac97_setrate(via->codec, reg, speed);
308	} else
309		return 48000;
310}
311
312static int
313viachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
314{
315	struct via_chinfo *ch = data;
316
317	ch->blksz = blocksize;
318	sndbuf_resize(ch->buffer, SEGS_PER_CHAN, ch->blksz);
319
320	return ch->blksz;
321}
322
323static int
324viachan_trigger(kobj_t obj, void *data, int go)
325{
326	struct via_chinfo *ch = data;
327	struct via_info *via = ch->parent;
328	struct via_dma_op *ado = ch->sgd_table;
329
330	switch(go) {
331	case PCMTRIG_START:
332		via_buildsgdt(ch);
333		via_wr(via, ch->rbase + VIA_RP_DMAOPS_BASE, vtophys(ado), 4);
334		via_wr(via, ch->rbase + VIA_RP_CONTROL, VIA_RPCTRL_START, 1);
335		break;
336	case PCMTRIG_STOP:
337	case PCMTRIG_ABORT:
338		via_wr(via, ch->rbase + VIA_RP_CONTROL,
339		       VIA_RPCTRL_TERMINATE, 1);
340		break;
341	}
342	return 0;
343}
344
345static int
346viachan_getptr(kobj_t obj, void *data)
347{
348	struct via_chinfo *ch = data;
349	struct via_info *via = ch->parent;
350	struct via_dma_op *ado;
351	int ptr, base, base1, len, seg;
352
353	ado = ch->sgd_table;
354	base1 = via_rd(via, ch->rbase + VIA_RP_DMAOPS_BASE, 4);
355	len = via_rd(via, ch->rbase + VIA_RP_DMAOPS_COUNT, 4);
356	base = via_rd(via, ch->rbase + VIA_RP_DMAOPS_BASE, 4);
357	if (base != base1) 	/* Avoid race hazard */
358		len = via_rd(via, ch->rbase + VIA_RP_DMAOPS_COUNT, 4);
359
360	DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base));
361
362	/* Base points to SGD segment to do, one past current */
363
364	/* Determine how many segments have been done */
365	seg = (base - vtophys(ado)) / sizeof(struct via_dma_op);
366	if (seg == 0)
367		seg = SEGS_PER_CHAN;
368
369	/* Now work out offset: seg less count */
370	ptr = (seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN) - len;
371	if (ch->dir == PCMDIR_REC) {
372		/* DMA appears to operate on memory 'lines' of 32 bytes	*/
373		/* so don't return any part line - it isn't in RAM yet	*/
374		ptr = ptr & ~0x1f;
375	}
376
377	DEB(printf("return ptr=%d\n", ptr));
378	return ptr;
379}
380
381static struct pcmchan_caps *
382viachan_getcaps(kobj_t obj, void *data)
383{
384	struct via_chinfo *ch = data;
385	struct via_info *via = ch->parent;
386
387	return (via->codec_caps & AC97_EXTCAP_VRA)? &via_vracaps : &via_caps;
388}
389
390/* ------------------------------------------------------------------------- */
391
392static kobj_method_t viachan_methods[] = {
393    	KOBJMETHOD(channel_init,		viachan_init),
394    	KOBJMETHOD(channel_setformat,		viachan_setformat),
395    	KOBJMETHOD(channel_setspeed,		viachan_setspeed),
396    	KOBJMETHOD(channel_setblocksize,	viachan_setblocksize),
397    	KOBJMETHOD(channel_trigger,		viachan_trigger),
398    	KOBJMETHOD(channel_getptr,		viachan_getptr),
399    	KOBJMETHOD(channel_getcaps,		viachan_getcaps),
400	{ 0, 0 }
401};
402CHANNEL_DECLARE(viachan);
403
404/* ------------------------------------------------------------------------- */
405/* VT8233 Specific methods */
406
407static void*
408via8233chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
409		 struct pcm_channel *c, int dir)
410{
411	struct via_info *via = devinfo;
412	struct via_chinfo *ch = viachan_init(obj, devinfo, b, c, dir);
413
414	/* Fix dxs volume - only need to do once (hopefully) */
415	if (ch) {
416		via_wr(via, ch->rbase + VIA8233_RP_DXS_LVOL, 0, 1);
417		via_wr(via, ch->rbase + VIA8233_RP_DXS_RVOL, 0, 1);
418	}
419	return ch;
420}
421
422static int
423via8233chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
424{
425	struct via_chinfo *ch = data;
426	struct via_info *via = ch->parent;
427
428	u_int32_t r = ch->rbase + VIA8233_RP_RATEFMT;
429	u_int32_t v = via_rd(via, r, 4) & ~VIA8233_RATEFMT_48K;
430
431	/* Careful to avoid overflow (divide by 16) */
432
433	v |= VIA8233_RATEFMT_48K * (speed / 16) / (48000 / 16);
434	via_wr(via, r, v, 4);
435
436	/* Need to do ac97 thing like via686 */
437	return viachan_setspeed(obj, data, speed);
438}
439
440static int
441via8233chan_setformat(kobj_t obj, void *data, u_int32_t format)
442{
443	struct via_chinfo *ch = data;
444	struct via_info *via = ch->parent;
445
446	u_int32_t r = ch->rbase + VIA8233_RP_RATEFMT;
447	u_int32_t v = via_rd(via, r, 4);
448
449	v &= ~(VIA8233_RATEFMT_STEREO | VIA8233_RATEFMT_16BIT);
450	if (format & AFMT_STEREO)
451		v |= VIA8233_RATEFMT_STEREO;
452	if (format & AFMT_16BIT)
453		v |= VIA8233_RATEFMT_16BIT;
454	via_wr(via, r, v, 4);
455
456	return 0;
457}
458
459static int
460via8233chan_trigger(kobj_t obj, void* data, int go)
461{
462	struct via_chinfo *ch = data;
463	struct via_info *via = ch->parent;
464	struct via_dma_op *ado = ch->sgd_table;
465
466	switch(go) {
467	case PCMTRIG_START:
468		via_buildsgdt(ch);
469		via_wr(via, ch->rbase + VIA_RP_DMAOPS_BASE, vtophys(ado), 4);
470		via_wr(via, ch->rbase + VIA_RP_CONTROL,
471		       VIA_RPCTRL_START | VIA_RPCTRL_AUTOSTART |
472		       VIA_RPCTRL_I_STOP | VIA_RPCTRL_I_EOL |
473		       VIA_RPCTRL_I_FLAG, 1);
474		break;
475	case PCMTRIG_STOP:
476	case PCMTRIG_ABORT:
477		via_wr(via, ch->rbase + VIA_RP_CONTROL,
478		       VIA_RPCTRL_TERMINATE, 1);
479		break;
480	}
481	return 0;
482}
483
484static kobj_method_t via8233chan_methods[] = {
485    	KOBJMETHOD(channel_init,		via8233chan_init),
486    	KOBJMETHOD(channel_setformat,		via8233chan_setformat),
487    	KOBJMETHOD(channel_setspeed,		via8233chan_setspeed),
488    	KOBJMETHOD(channel_setblocksize,	viachan_setblocksize),
489    	KOBJMETHOD(channel_trigger,		via8233chan_trigger),
490    	KOBJMETHOD(channel_getptr,		viachan_getptr),
491    	KOBJMETHOD(channel_getcaps,		viachan_getcaps),
492	{ 0, 0 }
493};
494CHANNEL_DECLARE(via8233chan);
495
496/* -------------------------------------------------------------------- */
497
498static void
499via_intr(void *p)
500{
501	struct via_info *via = p;
502
503	if (via_rd(via, VIA_PLAY_BASE + VIA_RP_STAT, 1) & VIA_RPSTAT_INTR) {
504		via_wr(via, VIA_PLAY_BASE + VIA_RP_STAT, VIA_RPSTAT_INTR, 1);
505		chn_intr(via->pch.channel);
506		DEB(printf("intr play\n"));
507	}
508	if (via_rd(via, VIA_REC_BASE + VIA_RP_STAT, 1) & VIA_RPSTAT_INTR) {
509		via_wr(via, VIA_REC_BASE + VIA_RP_STAT, VIA_RPSTAT_INTR, 1);
510		chn_intr(via->rch.channel);
511		DEB(printf("intr rec\n"));
512	}
513}
514
515/*
516 *  Probe and attach the card
517 */
518static int
519via_probe(device_t dev)
520{
521	switch(pci_get_devid(dev)) {
522	case VIA686A_PCI_ID:
523		device_set_desc(dev, "VIA VT82C686A");
524		return 0;
525	case VIA8233_PCI_ID:
526		device_set_desc(dev, "VIA VT8233");
527		return 0;
528	}
529	return ENXIO;
530}
531
532
533static void
534dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
535{
536}
537
538static int
539via_attach(device_t dev)
540{
541	struct via_info *via = 0;
542	char status[SND_STATUSLEN];
543	u_int32_t data;
544
545	if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
546		device_printf(dev, "cannot allocate softc\n");
547		return ENXIO;
548	}
549
550	/* Get resources */
551	data = pci_read_config(dev, PCIR_COMMAND, 2);
552	data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
553	pci_write_config(dev, PCIR_COMMAND, data, 2);
554	data = pci_read_config(dev, PCIR_COMMAND, 2);
555
556	pci_write_config(dev, VIA_PCICONF_MISC,
557			 VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD |
558			 VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1);
559
560	via->regid = PCIR_MAPS;
561	via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, 0, ~0,
562				      1, RF_ACTIVE);
563	if (!via->reg) {
564		device_printf(dev, "cannot allocate bus resource.");
565		goto bad;
566	}
567	via->st = rman_get_bustag(via->reg);
568	via->sh = rman_get_bushandle(via->reg);
569
570	via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536);
571
572	via->irqid = 0;
573	via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, 0, ~0, 1,
574				      RF_ACTIVE | RF_SHAREABLE);
575	if (!via->irq ||
576	    snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih)) {
577		device_printf(dev, "unable to map interrupt\n");
578		goto bad;
579	}
580
581	via_wr(via, VIA_PLAY_BASE + VIA_RP_MODE,
582	       VIA_RPMODE_AUTOSTART|VIA_RPMODE_INTR_FLAG|VIA_RPMODE_INTR_EOL,
583	       1);
584	via_wr(via, VIA_REC_BASE + VIA_RP_MODE,
585	       VIA_RPMODE_AUTOSTART|VIA_RPMODE_INTR_FLAG|VIA_RPMODE_INTR_EOL,
586	       1);
587
588	via->codec = AC97_CREATE(dev, via, via_ac97);
589	if (!via->codec)
590		goto bad;
591
592	mixer_init(dev, ac97_getmixerclass(), via->codec);
593
594	via->codec_caps = ac97_getextcaps(via->codec);
595	ac97_setextmode(via->codec,
596			via->codec_caps & (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM));
597
598	/* DMA tag for buffers */
599	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
600		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
601		/*highaddr*/BUS_SPACE_MAXADDR,
602		/*filter*/NULL, /*filterarg*/NULL,
603		/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
604		/*flags*/0, &via->parent_dmat) != 0) {
605		device_printf(dev, "unable to create dma tag\n");
606		goto bad;
607	}
608
609	/*
610	 *  DMA tag for SGD table.  The 686 uses scatter/gather DMA and
611	 *  requires a list in memory of work to do.  We need only 16 bytes
612	 *  for this list, and it is wasteful to allocate 16K.
613	 */
614	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
615		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
616		/*highaddr*/BUS_SPACE_MAXADDR,
617		/*filter*/NULL, /*filterarg*/NULL,
618		/*maxsize*/NSEGS * sizeof(struct via_dma_op),
619		/*nsegments*/1, /*maxsegz*/0x3ffff,
620		/*flags*/0, &via->sgd_dmat) != 0) {
621		device_printf(dev, "unable to create dma tag\n");
622		goto bad;
623	}
624
625	if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1)
626		goto bad;
627	if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0))
628		goto bad;
629
630	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", rman_get_start(via->reg), rman_get_start(via->irq));
631
632	/* Register */
633	if (pcm_register(dev, via, 1, 1)) goto bad;
634
635	switch (pci_get_devid(dev)) {
636	case VIA686A_PCI_ID:
637		pcm_addchan(dev, PCMDIR_PLAY, &viachan_class, via);
638		pcm_addchan(dev, PCMDIR_REC, &viachan_class, via);
639		break;
640	case VIA8233_PCI_ID:
641		pcm_addchan(dev, PCMDIR_PLAY, &via8233chan_class, via);
642		pcm_addchan(dev, PCMDIR_REC, &via8233chan_class, via);
643		break;
644	default:
645		goto bad;
646	}
647
648	pcm_setstatus(dev, status);
649
650	return 0;
651bad:
652	if (via->codec) ac97_destroy(via->codec);
653	if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
654	if (via->ih) bus_teardown_intr(dev, via->irq, via->ih);
655	if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
656	if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
657	if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
658	if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
659	if (via) free(via, M_DEVBUF);
660	return ENXIO;
661}
662
663static int
664via_detach(device_t dev)
665{
666	int r;
667	struct via_info *via = 0;
668
669	r = pcm_unregister(dev);
670	if (r)
671		return r;
672
673	via = pcm_getdevinfo(dev);
674	bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
675	bus_teardown_intr(dev, via->irq, via->ih);
676	bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
677	bus_dma_tag_destroy(via->parent_dmat);
678	bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
679	bus_dma_tag_destroy(via->sgd_dmat);
680	free(via, M_DEVBUF);
681	return 0;
682}
683
684
685static device_method_t via_methods[] = {
686	DEVMETHOD(device_probe,		via_probe),
687	DEVMETHOD(device_attach,	via_attach),
688	DEVMETHOD(device_detach,	via_detach),
689	{ 0, 0}
690};
691
692static driver_t via_driver = {
693	"pcm",
694	via_methods,
695	PCM_SOFTC_SIZE,
696};
697
698DRIVER_MODULE(snd_via82c686, pci, via_driver, pcm_devclass, 0, 0);
699MODULE_DEPEND(snd_via82c686, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
700MODULE_VERSION(snd_via82c686, 1);
701