1139749Simp/*-
2102011Sorion * Copyright (c) 2002 Orion Hodson <orion@freebsd.org>
3102011Sorion * Portions of this code derived from via82c686.c:
4102011Sorion * 	Copyright (c) 2000 David Jones <dej@ox.org>
5102011Sorion * All rights reserved.
6102011Sorion *
7102011Sorion * Redistribution and use in source and binary forms, with or without
8102011Sorion * modification, are permitted provided that the following conditions
9102011Sorion * are met:
10102011Sorion * 1. Redistributions of source code must retain the above copyright
11102011Sorion *    notice, this list of conditions and the following disclaimer.
12102011Sorion * 2. Redistributions in binary form must reproduce the above copyright
13102011Sorion *    notice, this list of conditions and the following disclaimer in the
14102011Sorion *    documentation and/or other materials provided with the distribution.
15102011Sorion *
16102011Sorion * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17102011Sorion * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18102011Sorion * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19102011Sorion * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20102011Sorion * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21102011Sorion * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22102011Sorion * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23102011Sorion * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24102011Sorion * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25102011Sorion * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26102011Sorion * SUCH DAMAGE.
27102011Sorion */
28102011Sorion
29111269Sorion/*
30111269Sorion * Credits due to:
31102011Sorion *
32102011Sorion * Grzybowski Rafal, Russell Davies, Mark Handley, Daniel O'Connor for
33102011Sorion * comments, machine time, testing patches, and patience.  VIA for
34102011Sorion * providing specs.  ALSA for helpful comments and some register poke
35164614Sariff * ordering.
36102011Sorion */
37102011Sorion
38193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
39193640Sariff#include "opt_snd.h"
40193640Sariff#endif
41193640Sariff
42102011Sorion#include <dev/sound/pcm/sound.h>
43102011Sorion#include <dev/sound/pcm/ac97.h>
44102011Sorion
45119287Simp#include <dev/pci/pcireg.h>
46119287Simp#include <dev/pci/pcivar.h>
47102011Sorion#include <sys/sysctl.h>
48102011Sorion
49102011Sorion#include <dev/sound/pci/via8233.h>
50102011Sorion
51102011SorionSND_DECLARE_FILE("$FreeBSD$");
52102011Sorion
53102011Sorion#define VIA8233_PCI_ID 0x30591106
54102011Sorion
55111269Sorion#define VIA8233_REV_ID_8233PRE	0x10
56111269Sorion#define VIA8233_REV_ID_8233C	0x20
57111269Sorion#define VIA8233_REV_ID_8233	0x30
58111269Sorion#define VIA8233_REV_ID_8233A	0x40
59111269Sorion#define VIA8233_REV_ID_8235	0x50
60129758Smatk#define VIA8233_REV_ID_8237	0x60
61157956Sariff#define VIA8233_REV_ID_8251	0x70
62111269Sorion
63102011Sorion#define SEGS_PER_CHAN	2			/* Segments per channel */
64111269Sorion#define NDXSCHANS	4			/* No of DXS channels */
65111269Sorion#define NMSGDCHANS	1			/* No of multichannel SGD */
66111269Sorion#define NWRCHANS	1			/* No of write channels */
67111269Sorion#define NCHANS		(NWRCHANS + NDXSCHANS + NMSGDCHANS)
68102011Sorion#define	NSEGS		NCHANS * SEGS_PER_CHAN	/* Segments in SGD table */
69164614Sariff#define VIA_SEGS_MIN		2
70166278Sariff#define VIA_SEGS_MAX		64
71164614Sariff#define VIA_SEGS_DEFAULT	2
72167648Sariff#define VIA_BLK_MIN		32
73167648Sariff#define VIA_BLK_ALIGN		(~(VIA_BLK_MIN - 1))
74102011Sorion
75102011Sorion#define	VIA_DEFAULT_BUFSZ	0x1000
76102011Sorion
77102011Sorion/* we rely on this struct being packed to 64 bits */
78102011Sorionstruct via_dma_op {
79164614Sariff	volatile uint32_t ptr;
80164614Sariff	volatile uint32_t flags;
81102011Sorion#define VIA_DMAOP_EOL         0x80000000
82102011Sorion#define VIA_DMAOP_FLAG        0x40000000
83102011Sorion#define VIA_DMAOP_STOP        0x20000000
84102011Sorion#define VIA_DMAOP_COUNT(x)    ((x)&0x00FFFFFF)
85102011Sorion};
86102011Sorion
87102011Sorionstruct via_info;
88102011Sorion
89102011Sorionstruct via_chinfo {
90102011Sorion	struct via_info *parent;
91102011Sorion	struct pcm_channel *channel;
92102011Sorion	struct snd_dbuf *buffer;
93102011Sorion	struct via_dma_op *sgd_table;
94111183Scognet	bus_addr_t sgd_addr;
95164614Sariff	int dir, rbase, active;
96164614Sariff	unsigned int blksz, blkcnt;
97164614Sariff	unsigned int ptr, prevptr;
98102011Sorion};
99102011Sorion
100102011Sorionstruct via_info {
101167648Sariff	device_t dev;
102167648Sariff
103102011Sorion	bus_space_tag_t st;
104102011Sorion	bus_space_handle_t sh;
105102011Sorion	bus_dma_tag_t parent_dmat;
106102011Sorion	bus_dma_tag_t sgd_dmat;
107102011Sorion	bus_dmamap_t sgd_dmamap;
108111183Scognet	bus_addr_t sgd_addr;
109102011Sorion
110102011Sorion	struct resource *reg, *irq;
111102011Sorion	int regid, irqid;
112102011Sorion	void *ih;
113102011Sorion	struct ac97_info *codec;
114102011Sorion
115164614Sariff	unsigned int bufsz, blkcnt;
116157956Sariff	int dxs_src, dma_eol_wake;
117102011Sorion
118111269Sorion	struct via_chinfo pch[NDXSCHANS + NMSGDCHANS];
119111269Sorion	struct via_chinfo rch[NWRCHANS];
120102011Sorion	struct via_dma_op *sgd_table;
121164614Sariff	uint16_t codec_caps;
122164614Sariff	uint16_t n_dxs_registered;
123170137Sariff	int play_num, rec_num;
124148596Snetchild	struct mtx *lock;
125164614Sariff	struct callout poll_timer;
126164614Sariff	int poll_ticks, polling;
127102011Sorion};
128102011Sorion
129164614Sariffstatic uint32_t via_fmt[] = {
130193640Sariff	SND_FORMAT(AFMT_U8, 1, 0),
131193640Sariff	SND_FORMAT(AFMT_U8, 2, 0),
132193640Sariff	SND_FORMAT(AFMT_S16_LE, 1, 0),
133193640Sariff	SND_FORMAT(AFMT_S16_LE, 2, 0),
134102011Sorion	0
135102011Sorion};
136102011Sorion
137102011Sorionstatic struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 };
138102011Sorionstatic struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 };
139102011Sorion
140164614Sariffstatic __inline int
141164614Sariffvia_chan_active(struct via_info *via)
142164614Sariff{
143164614Sariff	int i, ret = 0;
144164614Sariff
145164614Sariff	if (via == NULL)
146164614Sariff		return (0);
147164614Sariff
148164614Sariff	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++)
149164614Sariff		ret += via->pch[i].active;
150164614Sariff
151164614Sariff	for (i = 0; i < NWRCHANS; i++)
152164614Sariff		ret += via->rch[i].active;
153164614Sariff
154164614Sariff	return (ret);
155164614Sariff}
156164614Sariff
157148596Snetchildstatic int
158148596Snetchildsysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
159148596Snetchild{
160148596Snetchild	struct via_info *via;
161148596Snetchild	device_t dev;
162150980Snetchild	uint32_t r;
163150980Snetchild	int err, new_en;
164148596Snetchild
165148596Snetchild	dev = oidp->oid_arg1;
166148596Snetchild	via = pcm_getdevinfo(dev);
167148596Snetchild	snd_mtxlock(via->lock);
168150980Snetchild	r = pci_read_config(dev, VIA_PCI_SPDIF, 1);
169148596Snetchild	snd_mtxunlock(via->lock);
170150980Snetchild	new_en = (r & VIA_SPDIF_EN) ? 1 : 0;
171170289Sdwmalone	err = sysctl_handle_int(oidp, &new_en, 0, req);
172148596Snetchild
173148596Snetchild	if (err || req->newptr == NULL)
174164614Sariff		return (err);
175148596Snetchild	if (new_en < 0 || new_en > 1)
176164614Sariff		return (EINVAL);
177148596Snetchild
178148596Snetchild	if (new_en)
179148596Snetchild		r |= VIA_SPDIF_EN;
180150980Snetchild	else
181150980Snetchild		r &= ~VIA_SPDIF_EN;
182150980Snetchild	snd_mtxlock(via->lock);
183148596Snetchild	pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
184148596Snetchild	snd_mtxunlock(via->lock);
185148596Snetchild
186164614Sariff	return (0);
187148596Snetchild}
188148596Snetchild
189148596Snetchildstatic int
190148596Snetchildsysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS)
191148596Snetchild{
192148596Snetchild	struct via_info *via;
193148596Snetchild	device_t dev;
194148596Snetchild	int err, val;
195148596Snetchild
196148596Snetchild	dev = oidp->oid_arg1;
197148596Snetchild	via = pcm_getdevinfo(dev);
198148596Snetchild	snd_mtxlock(via->lock);
199148596Snetchild	val = via->dxs_src;
200148596Snetchild	snd_mtxunlock(via->lock);
201170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
202148596Snetchild
203148596Snetchild	if (err || req->newptr == NULL)
204164614Sariff		return (err);
205148596Snetchild	if (val < 0 || val > 1)
206164614Sariff		return (EINVAL);
207148596Snetchild
208148596Snetchild	snd_mtxlock(via->lock);
209148596Snetchild	via->dxs_src = val;
210148596Snetchild	snd_mtxunlock(via->lock);
211148596Snetchild
212164614Sariff	return (0);
213148596Snetchild}
214164614Sariff
215164614Sariffstatic int
216164614Sariffsysctl_via_polling(SYSCTL_HANDLER_ARGS)
217164614Sariff{
218164614Sariff	struct via_info *via;
219164614Sariff	device_t dev;
220164614Sariff	int err, val;
221164614Sariff
222164614Sariff	dev = oidp->oid_arg1;
223164614Sariff	via = pcm_getdevinfo(dev);
224164614Sariff	if (via == NULL)
225164614Sariff		return (EINVAL);
226164614Sariff	snd_mtxlock(via->lock);
227164614Sariff	val = via->polling;
228164614Sariff	snd_mtxunlock(via->lock);
229170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
230164614Sariff
231164614Sariff	if (err || req->newptr == NULL)
232164614Sariff		return (err);
233164614Sariff	if (val < 0 || val > 1)
234164614Sariff		return (EINVAL);
235164614Sariff
236164614Sariff	snd_mtxlock(via->lock);
237164614Sariff	if (val != via->polling) {
238164614Sariff		if (via_chan_active(via) != 0)
239164614Sariff			err = EBUSY;
240164614Sariff		else if (val == 0)
241164614Sariff			via->polling = 0;
242164614Sariff		else
243164614Sariff			via->polling = 1;
244164614Sariff	}
245164614Sariff	snd_mtxunlock(via->lock);
246164614Sariff
247164614Sariff	return (err);
248164614Sariff}
249148596Snetchild
250148596Snetchildstatic void
251148596Snetchildvia_init_sysctls(device_t dev)
252148596Snetchild{
253159732Snetchild	/* XXX: an user should be able to set this with a control tool,
254159732Snetchild	   if not done before 7.0-RELEASE, this needs to be converted to
255159732Snetchild	   a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*()
256159732Snetchild	   as discussed on multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */
257164614Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
258164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
259164614Sariff	    "spdif_enabled",  CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
260164614Sariff	    sysctl_via8233_spdif_enable, "I",
261164614Sariff	    "Enable S/PDIF output on primary playback channel");
262164614Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
263164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
264164614Sariff	    "dxs_src", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
265164614Sariff	    sysctl_via8233_dxs_src, "I",
266164614Sariff	    "Enable VIA DXS Sample Rate Converter");
267164614Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
268164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
269164614Sariff	    "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
270164614Sariff	    sysctl_via_polling, "I",
271164614Sariff	    "Enable polling mode");
272148596Snetchild}
273148596Snetchild
274164614Sariffstatic __inline uint32_t
275102011Sorionvia_rd(struct via_info *via, int regno, int size)
276102011Sorion{
277102011Sorion	switch (size) {
278102011Sorion	case 1:
279164614Sariff		return (bus_space_read_1(via->st, via->sh, regno));
280102011Sorion	case 2:
281164614Sariff		return (bus_space_read_2(via->st, via->sh, regno));
282102011Sorion	case 4:
283164614Sariff		return (bus_space_read_4(via->st, via->sh, regno));
284102011Sorion	default:
285164614Sariff		return (0xFFFFFFFF);
286102011Sorion	}
287102011Sorion}
288102011Sorion
289150980Snetchildstatic __inline void
290164614Sariffvia_wr(struct via_info *via, int regno, uint32_t data, int size)
291102011Sorion{
292102011Sorion
293102011Sorion	switch (size) {
294102011Sorion	case 1:
295102011Sorion		bus_space_write_1(via->st, via->sh, regno, data);
296102011Sorion		break;
297102011Sorion	case 2:
298102011Sorion		bus_space_write_2(via->st, via->sh, regno, data);
299102011Sorion		break;
300102011Sorion	case 4:
301102011Sorion		bus_space_write_4(via->st, via->sh, regno, data);
302102011Sorion		break;
303102011Sorion	}
304102011Sorion}
305102011Sorion
306102011Sorion/* -------------------------------------------------------------------- */
307102011Sorion/* Codec interface */
308102011Sorion
309102011Sorionstatic int
310102011Sorionvia_waitready_codec(struct via_info *via)
311102011Sorion{
312102011Sorion	int i;
313102011Sorion
314102011Sorion	/* poll until codec not busy */
315102011Sorion	for (i = 0; i < 1000; i++) {
316102011Sorion		if ((via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_BUSY) == 0)
317164614Sariff			return (0);
318102011Sorion		DELAY(1);
319102011Sorion	}
320167648Sariff	device_printf(via->dev, "%s: codec busy\n", __func__);
321164614Sariff	return (1);
322102011Sorion}
323102011Sorion
324102011Sorionstatic int
325102011Sorionvia_waitvalid_codec(struct via_info *via)
326102011Sorion{
327102011Sorion	int i;
328102011Sorion
329102011Sorion	/* poll until codec valid */
330102011Sorion	for (i = 0; i < 1000; i++) {
331102011Sorion		if (via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_CODEC00_VALID)
332164614Sariff			return (0);
333102011Sorion		DELAY(1);
334102011Sorion	}
335167648Sariff	device_printf(via->dev, "%s: codec invalid\n", __func__);
336164614Sariff	return (1);
337102011Sorion}
338102011Sorion
339102011Sorionstatic int
340164614Sariffvia_write_codec(kobj_t obj, void *addr, int reg, uint32_t val)
341102011Sorion{
342102011Sorion	struct via_info *via = addr;
343102011Sorion
344164614Sariff	if (via_waitready_codec(via))
345164614Sariff		return (-1);
346102011Sorion
347164614Sariff	via_wr(via, VIA_AC97_CONTROL,
348102011Sorion	       VIA_AC97_CODEC00_VALID | VIA_AC97_INDEX(reg) |
349102011Sorion	       VIA_AC97_DATA(val), 4);
350102011Sorion
351164614Sariff	return (0);
352102011Sorion}
353102011Sorion
354102011Sorionstatic int
355102011Sorionvia_read_codec(kobj_t obj, void *addr, int reg)
356102011Sorion{
357102011Sorion	struct via_info *via = addr;
358102011Sorion
359102011Sorion	if (via_waitready_codec(via))
360164614Sariff		return (-1);
361102011Sorion
362164614Sariff	via_wr(via, VIA_AC97_CONTROL, VIA_AC97_CODEC00_VALID |
363164614Sariff	    VIA_AC97_READ | VIA_AC97_INDEX(reg), 4);
364102011Sorion
365102011Sorion	if (via_waitready_codec(via))
366164614Sariff		return (-1);
367102011Sorion
368102011Sorion	if (via_waitvalid_codec(via))
369164614Sariff		return (-1);
370102011Sorion
371164614Sariff	return (via_rd(via, VIA_AC97_CONTROL, 2));
372102011Sorion}
373102011Sorion
374102011Sorionstatic kobj_method_t via_ac97_methods[] = {
375164614Sariff	KOBJMETHOD(ac97_read,		via_read_codec),
376164614Sariff	KOBJMETHOD(ac97_write,		via_write_codec),
377193640Sariff	KOBJMETHOD_END
378102011Sorion};
379102011SorionAC97_DECLARE(via_ac97);
380102011Sorion
381102011Sorion/* -------------------------------------------------------------------- */
382102011Sorion
383102011Sorionstatic int
384102011Sorionvia_buildsgdt(struct via_chinfo *ch)
385102011Sorion{
386164614Sariff	uint32_t phys_addr, flag;
387164614Sariff	int i;
388102011Sorion
389111183Scognet	phys_addr = sndbuf_getbufaddr(ch->buffer);
390102011Sorion
391164614Sariff	for (i = 0; i < ch->blkcnt; i++) {
392164614Sariff		flag = (i == ch->blkcnt - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
393164614Sariff		ch->sgd_table[i].ptr = phys_addr + (i * ch->blksz);
394164614Sariff		ch->sgd_table[i].flags = flag | ch->blksz;
395102011Sorion	}
396102011Sorion
397164614Sariff	return (0);
398102011Sorion}
399102011Sorion
400111269Sorion/* -------------------------------------------------------------------- */
401111269Sorion/* Format setting functions */
402111269Sorion
403102011Sorionstatic int
404164614Sariffvia8233wr_setformat(kobj_t obj, void *data, uint32_t format)
405102011Sorion{
406102011Sorion	struct via_chinfo *ch = data;
407102011Sorion	struct via_info *via = ch->parent;
408148596Snetchild
409164614Sariff	uint32_t f = WR_FORMAT_STOP_INDEX;
410102011Sorion
411193640Sariff	if (AFMT_CHANNEL(format) > 1)
412111269Sorion		f |= WR_FORMAT_STEREO;
413111269Sorion	if (format & AFMT_S16_LE)
414111269Sorion		f |= WR_FORMAT_16BIT;
415150980Snetchild	snd_mtxlock(via->lock);
416111269Sorion	via_wr(via, VIA_WR0_FORMAT, f, 4);
417150980Snetchild	snd_mtxunlock(via->lock);
418111269Sorion
419164614Sariff	return (0);
420111269Sorion}
421111269Sorion
422111269Sorionstatic int
423164614Sariffvia8233dxs_setformat(kobj_t obj, void *data, uint32_t format)
424111269Sorion{
425111269Sorion	struct via_chinfo *ch = data;
426111269Sorion	struct via_info *via = ch->parent;
427164614Sariff	uint32_t r, v;
428111269Sorion
429148596Snetchild	r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
430150980Snetchild	snd_mtxlock(via->lock);
431148596Snetchild	v = via_rd(via, r, 4);
432111269Sorion
433111269Sorion	v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT);
434193640Sariff	if (AFMT_CHANNEL(format) > 1)
435111269Sorion		v |= VIA8233_DXS_RATEFMT_STEREO;
436164614Sariff	if (format & AFMT_16BIT)
437111269Sorion		v |= VIA8233_DXS_RATEFMT_16BIT;
438111269Sorion	via_wr(via, r, v, 4);
439150980Snetchild	snd_mtxunlock(via->lock);
440111269Sorion
441164614Sariff	return (0);
442111269Sorion}
443111269Sorion
444111269Sorionstatic int
445164614Sariffvia8233msgd_setformat(kobj_t obj, void *data, uint32_t format)
446111269Sorion{
447111269Sorion	struct via_chinfo *ch = data;
448111269Sorion	struct via_info *via = ch->parent;
449111269Sorion
450164614Sariff	uint32_t s = 0xff000000;
451164614Sariff	uint8_t  v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT;
452102011Sorion
453193640Sariff	if (AFMT_CHANNEL(format) > 1) {
454102011Sorion		v |= MC_SGD_CHANNELS(2);
455102011Sorion		s |= SLOT3(1) | SLOT4(2);
456102011Sorion	} else {
457102011Sorion		v |= MC_SGD_CHANNELS(1);
458102011Sorion		s |= SLOT3(1) | SLOT4(1);
459102011Sorion	}
460102011Sorion
461150980Snetchild	snd_mtxlock(via->lock);
462102011Sorion	via_wr(via, VIA_MC_SLOT_SELECT, s, 4);
463102011Sorion	via_wr(via, VIA_MC_SGD_FORMAT, v, 1);
464150980Snetchild	snd_mtxunlock(via->lock);
465102011Sorion
466164614Sariff	return (0);
467102011Sorion}
468102011Sorion
469111269Sorion/* -------------------------------------------------------------------- */
470111269Sorion/* Speed setting functions */
471111269Sorion
472193640Sariffstatic uint32_t
473164614Sariffvia8233wr_setspeed(kobj_t obj, void *data, uint32_t speed)
474102011Sorion{
475102011Sorion	struct via_chinfo *ch = data;
476102011Sorion	struct via_info *via = ch->parent;
477102011Sorion
478148596Snetchild	if (via->codec_caps & AC97_EXTCAP_VRA)
479164614Sariff		return (ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed));
480148596Snetchild
481164614Sariff	return (48000);
482102011Sorion}
483102011Sorion
484193640Sariffstatic uint32_t
485164614Sariffvia8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed)
486102011Sorion{
487102011Sorion	struct via_chinfo *ch = data;
488102011Sorion	struct via_info *via = ch->parent;
489164614Sariff	uint32_t r, v;
490102011Sorion
491148596Snetchild	r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
492150980Snetchild	snd_mtxlock(via->lock);
493148596Snetchild	v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K;
494102011Sorion
495111269Sorion	/* Careful to avoid overflow (divide by 48 per vt8233c docs) */
496111269Sorion
497111269Sorion	v |= VIA8233_DXS_RATEFMT_48K * (speed / 48) / (48000 / 48);
498111269Sorion	via_wr(via, r, v, 4);
499150980Snetchild	snd_mtxunlock(via->lock);
500111269Sorion
501164614Sariff	return (speed);
502102011Sorion}
503102011Sorion
504193640Sariffstatic uint32_t
505164614Sariffvia8233msgd_setspeed(kobj_t obj, void *data, uint32_t speed)
506102011Sorion{
507102011Sorion	struct via_chinfo *ch = data;
508102011Sorion	struct via_info *via = ch->parent;
509102011Sorion
510111269Sorion	if (via->codec_caps & AC97_EXTCAP_VRA)
511164614Sariff		return (ac97_setrate(via->codec, AC97_REGEXT_FDACRATE, speed));
512111269Sorion
513164614Sariff	return (48000);
514102011Sorion}
515102011Sorion
516111269Sorion/* -------------------------------------------------------------------- */
517112438Sorion/* Format probing functions */
518112438Sorion
519112438Sorionstatic struct pcmchan_caps *
520112438Sorionvia8233wr_getcaps(kobj_t obj, void *data)
521112438Sorion{
522112438Sorion	struct via_chinfo *ch = data;
523112438Sorion	struct via_info *via = ch->parent;
524112438Sorion
525112438Sorion	/* Controlled by ac97 registers */
526148596Snetchild	if (via->codec_caps & AC97_EXTCAP_VRA)
527164614Sariff		return (&via_vracaps);
528164614Sariff	return (&via_caps);
529112438Sorion}
530112438Sorion
531112438Sorionstatic struct pcmchan_caps *
532112438Sorionvia8233dxs_getcaps(kobj_t obj, void *data)
533112438Sorion{
534148596Snetchild	struct via_chinfo *ch = data;
535148596Snetchild	struct via_info *via = ch->parent;
536148596Snetchild
537148596Snetchild	/*
538148596Snetchild	 * Controlled by onboard registers
539148596Snetchild	 *
540148596Snetchild	 * Apparently, few boards can do DXS sample rate
541148596Snetchild	 * conversion.
542148596Snetchild	 */
543148596Snetchild	if (via->dxs_src)
544164614Sariff		return (&via_vracaps);
545164614Sariff	return (&via_caps);
546112438Sorion}
547112438Sorion
548112438Sorionstatic struct pcmchan_caps *
549112438Sorionvia8233msgd_getcaps(kobj_t obj, void *data)
550112438Sorion{
551112438Sorion	struct via_chinfo *ch = data;
552112438Sorion	struct via_info *via = ch->parent;
553112438Sorion
554112438Sorion	/* Controlled by ac97 registers */
555148596Snetchild	if (via->codec_caps & AC97_EXTCAP_VRA)
556164614Sariff		return (&via_vracaps);
557164614Sariff	return (&via_caps);
558112438Sorion}
559112438Sorion
560112438Sorion/* -------------------------------------------------------------------- */
561111269Sorion/* Common functions */
562111269Sorion
563102011Sorionstatic int
564167648Sariffvia8233chan_setfragments(kobj_t obj, void *data,
565167648Sariff					uint32_t blksz, uint32_t blkcnt)
566102011Sorion{
567102011Sorion	struct via_chinfo *ch = data;
568167648Sariff	struct via_info *via = ch->parent;
569148596Snetchild
570167648Sariff	blksz &= VIA_BLK_ALIGN;
571164614Sariff
572167648Sariff	if (blksz > (sndbuf_getmaxsize(ch->buffer) / VIA_SEGS_MIN))
573167648Sariff		blksz = sndbuf_getmaxsize(ch->buffer) / VIA_SEGS_MIN;
574167648Sariff	if (blksz < VIA_BLK_MIN)
575167648Sariff		blksz = VIA_BLK_MIN;
576167648Sariff	if (blkcnt > VIA_SEGS_MAX)
577167648Sariff		blkcnt = VIA_SEGS_MAX;
578167648Sariff	if (blkcnt < VIA_SEGS_MIN)
579167648Sariff		blkcnt = VIA_SEGS_MIN;
580167648Sariff
581167648Sariff	while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) {
582167648Sariff		if ((blkcnt >> 1) >= VIA_SEGS_MIN)
583167648Sariff			blkcnt >>= 1;
584167648Sariff		else if ((blksz >> 1) >= VIA_BLK_MIN)
585167648Sariff			blksz >>= 1;
586167648Sariff		else
587167648Sariff			break;
588167648Sariff	}
589167648Sariff
590164614Sariff	if ((sndbuf_getblksz(ch->buffer) != blksz ||
591167648Sariff	    sndbuf_getblkcnt(ch->buffer) != blkcnt) &&
592167648Sariff	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
593167648Sariff		device_printf(via->dev, "%s: failed blksz=%u blkcnt=%u\n",
594167648Sariff		    __func__, blksz, blkcnt);
595164614Sariff
596109863Sorion	ch->blksz = sndbuf_getblksz(ch->buffer);
597167648Sariff	ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
598164614Sariff
599193640Sariff	return (0);
600167648Sariff}
601167648Sariff
602193640Sariffstatic uint32_t
603167648Sariffvia8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
604167648Sariff{
605167648Sariff	struct via_chinfo *ch = data;
606167648Sariff	struct via_info *via = ch->parent;
607167648Sariff
608167648Sariff	via8233chan_setfragments(obj, data, blksz, via->blkcnt);
609167648Sariff
610164614Sariff	return (ch->blksz);
611102011Sorion}
612102011Sorion
613193640Sariffstatic uint32_t
614102011Sorionvia8233chan_getptr(kobj_t obj, void *data)
615102011Sorion{
616102011Sorion	struct via_chinfo *ch = data;
617102011Sorion	struct via_info *via = ch->parent;
618193640Sariff	uint32_t v, index, count, ptr;
619102011Sorion
620150980Snetchild	snd_mtxlock(via->lock);
621164614Sariff	if (via->polling != 0) {
622164614Sariff		ptr = ch->ptr;
623164614Sariff		snd_mtxunlock(via->lock);
624164614Sariff	} else {
625164614Sariff		v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
626164614Sariff		snd_mtxunlock(via->lock);
627164614Sariff		index = v >> 24;		/* Last completed buffer */
628164614Sariff		count = v & 0x00ffffff;	/* Bytes remaining */
629164614Sariff		ptr = (index + 1) * ch->blksz - count;
630164614Sariff		ptr %= ch->blkcnt * ch->blksz;	/* Wrap to available space */
631164614Sariff	}
632109863Sorion
633164614Sariff	return (ptr);
634102011Sorion}
635102011Sorion
636102011Sorionstatic void
637102011Sorionvia8233chan_reset(struct via_info *via, struct via_chinfo *ch)
638102011Sorion{
639102011Sorion	via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1);
640102011Sorion	via_wr(via, ch->rbase + VIA_RP_CONTROL, 0x00, 1);
641164614Sariff	via_wr(via, ch->rbase + VIA_RP_STATUS,
642164614Sariff	    SGD_STATUS_EOL | SGD_STATUS_FLAG, 1);
643102011Sorion}
644102011Sorion
645111269Sorion/* -------------------------------------------------------------------- */
646111269Sorion/* Channel initialization functions */
647111269Sorion
648111269Sorionstatic void
649111269Sorionvia8233chan_sgdinit(struct via_info *via, struct via_chinfo *ch, int chnum)
650111269Sorion{
651167648Sariff	ch->sgd_table = &via->sgd_table[chnum * VIA_SEGS_MAX];
652167648Sariff	ch->sgd_addr = via->sgd_addr + chnum * VIA_SEGS_MAX *
653164614Sariff	    sizeof(struct via_dma_op);
654111269Sorion}
655111269Sorion
656102011Sorionstatic void*
657111269Sorionvia8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
658164614Sariff						struct pcm_channel *c, int dir)
659102011Sorion{
660102011Sorion	struct via_info *via = devinfo;
661170137Sariff	struct via_chinfo *ch;
662170137Sariff	int num;
663102011Sorion
664170137Sariff	snd_mtxlock(via->lock);
665170137Sariff	num = via->rec_num++;
666170137Sariff	ch = &via->rch[num];
667111269Sorion	ch->parent = via;
668111269Sorion	ch->channel = c;
669111269Sorion	ch->buffer = b;
670111269Sorion	ch->dir = dir;
671164614Sariff	ch->blkcnt = via->blkcnt;
672170137Sariff	ch->rbase = VIA_WR_BASE(num);
673111269Sorion	via_wr(via, ch->rbase + VIA_WR_RP_SGD_FORMAT, WR_FIFO_ENABLE, 1);
674148596Snetchild	snd_mtxunlock(via->lock);
675111269Sorion
676168847Sariff	if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0)
677164614Sariff		return (NULL);
678148596Snetchild
679148596Snetchild	snd_mtxlock(via->lock);
680170137Sariff	via8233chan_sgdinit(via, ch, num);
681111269Sorion	via8233chan_reset(via, ch);
682148596Snetchild	snd_mtxunlock(via->lock);
683111269Sorion
684164614Sariff	return (ch);
685111269Sorion}
686111269Sorion
687111269Sorionstatic void*
688111269Sorionvia8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
689164614Sariff						struct pcm_channel *c, int dir)
690111269Sorion{
691111269Sorion	struct via_info *via = devinfo;
692170137Sariff	struct via_chinfo *ch;
693170137Sariff	int num;
694111269Sorion
695170137Sariff	snd_mtxlock(via->lock);
696170137Sariff	num = via->play_num++;
697170137Sariff	ch = &via->pch[num];
698111197Sorion	ch->parent = via;
699111197Sorion	ch->channel = c;
700111197Sorion	ch->buffer = b;
701111197Sorion	ch->dir = dir;
702164614Sariff	ch->blkcnt = via->blkcnt;
703111197Sorion
704111269Sorion	/*
705111269Sorion	 * All cards apparently support DXS3, but not other DXS
706111269Sorion	 * channels.  We therefore want to align first DXS channel to
707111269Sorion	 * DXS3.
708111269Sorion	 */
709111269Sorion	ch->rbase = VIA_DXS_BASE(NDXSCHANS - 1 - via->n_dxs_registered);
710111269Sorion	via->n_dxs_registered++;
711148596Snetchild	snd_mtxunlock(via->lock);
712111269Sorion
713168847Sariff	if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0)
714164614Sariff		return (NULL);
715148596Snetchild
716148596Snetchild	snd_mtxlock(via->lock);
717170137Sariff	via8233chan_sgdinit(via, ch, NWRCHANS + num);
718111269Sorion	via8233chan_reset(via, ch);
719148596Snetchild	snd_mtxunlock(via->lock);
720102011Sorion
721164614Sariff	return (ch);
722111269Sorion}
723111269Sorion
724111269Sorionstatic void*
725111269Sorionvia8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
726164614Sariff						struct pcm_channel *c, int dir)
727111269Sorion{
728111269Sorion	struct via_info *via = devinfo;
729170137Sariff	struct via_chinfo *ch;
730170137Sariff	int num;
731111269Sorion
732170137Sariff	snd_mtxlock(via->lock);
733170137Sariff	num = via->play_num++;
734170137Sariff	ch = &via->pch[num];
735111269Sorion	ch->parent = via;
736111269Sorion	ch->channel = c;
737111269Sorion	ch->buffer = b;
738111269Sorion	ch->dir = dir;
739111269Sorion	ch->rbase = VIA_MC_SGD_STATUS;
740164614Sariff	ch->blkcnt = via->blkcnt;
741170137Sariff	snd_mtxunlock(via->lock);
742111269Sorion
743168847Sariff	if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0)
744164614Sariff		return (NULL);
745148596Snetchild
746148596Snetchild	snd_mtxlock(via->lock);
747170137Sariff	via8233chan_sgdinit(via, ch, NWRCHANS + num);
748102011Sorion	via8233chan_reset(via, ch);
749148596Snetchild	snd_mtxunlock(via->lock);
750102011Sorion
751164614Sariff	return (ch);
752102011Sorion}
753102011Sorion
754111269Sorionstatic void
755111269Sorionvia8233chan_mute(struct via_info *via, struct via_chinfo *ch, int muted)
756111269Sorion{
757111269Sorion	if (BASE_IS_VIA_DXS_REG(ch->rbase)) {
758111269Sorion		int r;
759111269Sorion		muted = (muted) ? VIA8233_DXS_MUTE : 0;
760111269Sorion		via_wr(via, ch->rbase + VIA8233_RP_DXS_LVOL, muted, 1);
761111269Sorion		via_wr(via, ch->rbase + VIA8233_RP_DXS_RVOL, muted, 1);
762164614Sariff		r = via_rd(via, ch->rbase + VIA8233_RP_DXS_LVOL, 1) &
763164614Sariff		    VIA8233_DXS_MUTE;
764167648Sariff		if (r != muted)
765167648Sariff			device_printf(via->dev,
766167648Sariff			    "%s: failed to set dxs volume "
767167648Sariff			    "(dxs base 0x%02x).\n", __func__, ch->rbase);
768111269Sorion	}
769111269Sorion}
770111269Sorion
771164614Sariffstatic __inline int
772164614Sariffvia_poll_channel(struct via_chinfo *ch)
773164614Sariff{
774164614Sariff	struct via_info *via;
775164614Sariff	uint32_t sz, delta;
776164614Sariff	uint32_t v, index, count;
777164614Sariff	int ptr;
778164614Sariff
779164614Sariff	if (ch == NULL || ch->channel == NULL || ch->active == 0)
780164614Sariff		return (0);
781164614Sariff
782164614Sariff	via = ch->parent;
783164614Sariff	sz = ch->blksz * ch->blkcnt;
784164614Sariff	v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
785164614Sariff	index = v >> 24;
786164614Sariff	count = v & 0x00ffffff;
787164614Sariff	ptr = ((index + 1) * ch->blksz) - count;
788164614Sariff	ptr %= sz;
789164614Sariff	ptr &= ~(ch->blksz - 1);
790164614Sariff	ch->ptr = ptr;
791164614Sariff	delta = (sz + ptr - ch->prevptr) % sz;
792164614Sariff
793164614Sariff	if (delta < ch->blksz)
794164614Sariff		return (0);
795164614Sariff
796164614Sariff	ch->prevptr = ptr;
797164614Sariff
798164614Sariff	return (1);
799164614Sariff}
800164614Sariff
801164614Sariffstatic void
802164614Sariffvia_poll_callback(void *arg)
803164614Sariff{
804164614Sariff	struct via_info *via = arg;
805164614Sariff	uint32_t ptrigger = 0, rtrigger = 0;
806164614Sariff	int i;
807164614Sariff
808164614Sariff	if (via == NULL)
809164614Sariff		return;
810164614Sariff
811164614Sariff	snd_mtxlock(via->lock);
812164614Sariff	if (via->polling == 0 || via_chan_active(via) == 0) {
813164614Sariff		snd_mtxunlock(via->lock);
814164614Sariff		return;
815164614Sariff	}
816164614Sariff
817164614Sariff	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++)
818164614Sariff		ptrigger |= (via_poll_channel(&via->pch[i]) != 0) ?
819164614Sariff		    (1 << i) : 0;
820164614Sariff
821164614Sariff	for (i = 0; i < NWRCHANS; i++)
822164614Sariff		rtrigger |= (via_poll_channel(&via->rch[i]) != 0) ?
823164614Sariff		    (1 << i) : 0;
824164614Sariff
825164614Sariff	/* XXX */
826164614Sariff	callout_reset(&via->poll_timer, 1/*via->poll_ticks*/,
827164614Sariff	    via_poll_callback, via);
828164614Sariff
829164614Sariff	snd_mtxunlock(via->lock);
830164614Sariff
831164614Sariff	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
832164614Sariff		if (ptrigger & (1 << i))
833164614Sariff			chn_intr(via->pch[i].channel);
834164614Sariff	}
835164614Sariff	for (i = 0; i < NWRCHANS; i++) {
836164614Sariff		if (rtrigger & (1 << i))
837164614Sariff			chn_intr(via->rch[i].channel);
838164614Sariff	}
839164614Sariff}
840164614Sariff
841102011Sorionstatic int
842164614Sariffvia_poll_ticks(struct via_info *via)
843164614Sariff{
844164614Sariff	struct via_chinfo *ch;
845164614Sariff	int i;
846164614Sariff	int ret = hz;
847164614Sariff	int pollticks;
848164614Sariff
849164614Sariff	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
850164614Sariff		ch = &via->pch[i];
851164614Sariff		if (ch->channel == NULL || ch->active == 0)
852164614Sariff			continue;
853164614Sariff		pollticks = ((uint64_t)hz * ch->blksz) /
854193640Sariff		    ((uint64_t)sndbuf_getalign(ch->buffer) *
855164614Sariff		    sndbuf_getspd(ch->buffer));
856164614Sariff		pollticks >>= 2;
857164614Sariff		if (pollticks > hz)
858164614Sariff			pollticks = hz;
859164614Sariff		if (pollticks < 1)
860164614Sariff			pollticks = 1;
861164614Sariff		if (pollticks < ret)
862164614Sariff			ret = pollticks;
863164614Sariff	}
864164614Sariff
865164614Sariff	for (i = 0; i < NWRCHANS; i++) {
866164614Sariff		ch = &via->rch[i];
867164614Sariff		if (ch->channel == NULL || ch->active == 0)
868164614Sariff			continue;
869164614Sariff		pollticks = ((uint64_t)hz * ch->blksz) /
870193640Sariff		    ((uint64_t)sndbuf_getalign(ch->buffer) *
871164614Sariff		    sndbuf_getspd(ch->buffer));
872164614Sariff		pollticks >>= 2;
873164614Sariff		if (pollticks > hz)
874164614Sariff			pollticks = hz;
875164614Sariff		if (pollticks < 1)
876164614Sariff			pollticks = 1;
877164614Sariff		if (pollticks < ret)
878164614Sariff			ret = pollticks;
879164614Sariff	}
880164614Sariff
881164614Sariff	return (ret);
882164614Sariff}
883164614Sariff
884164614Sariffstatic int
885102011Sorionvia8233chan_trigger(kobj_t obj, void* data, int go)
886102011Sorion{
887102011Sorion	struct via_chinfo *ch = data;
888102011Sorion	struct via_info *via = ch->parent;
889164614Sariff	int pollticks;
890102011Sorion
891170521Sariff	if (!PCMTRIG_COMMON(go))
892170521Sariff		return (0);
893170521Sariff
894150980Snetchild	snd_mtxlock(via->lock);
895102011Sorion	switch(go) {
896102011Sorion	case PCMTRIG_START:
897102011Sorion		via_buildsgdt(ch);
898111269Sorion		via8233chan_mute(via, ch, 0);
899111183Scognet		via_wr(via, ch->rbase + VIA_RP_TABLE_PTR, ch->sgd_addr, 4);
900164614Sariff		if (via->polling != 0) {
901164614Sariff			ch->ptr = 0;
902164614Sariff			ch->prevptr = 0;
903164614Sariff			pollticks = ((uint64_t)hz * ch->blksz) /
904193640Sariff			    ((uint64_t)sndbuf_getalign(ch->buffer) *
905164614Sariff			    sndbuf_getspd(ch->buffer));
906164614Sariff			pollticks >>= 2;
907164614Sariff			if (pollticks > hz)
908164614Sariff				pollticks = hz;
909164614Sariff			if (pollticks < 1)
910164614Sariff				pollticks = 1;
911164614Sariff			if (via_chan_active(via) == 0 ||
912164614Sariff			    pollticks < via->poll_ticks) {
913164614Sariff			    	if (bootverbose) {
914164614Sariff					if (via_chan_active(via) == 0)
915164614Sariff						printf("%s: pollticks=%d\n",
916164614Sariff						    __func__, pollticks);
917164614Sariff					else
918164614Sariff						printf("%s: "
919164614Sariff						    "pollticks %d -> %d\n",
920164614Sariff						    __func__, via->poll_ticks,
921164614Sariff						    pollticks);
922164614Sariff				}
923164614Sariff				via->poll_ticks = pollticks;
924164614Sariff				callout_reset(&via->poll_timer, 1,
925164614Sariff				    via_poll_callback, via);
926164614Sariff			}
927164614Sariff		}
928102011Sorion		via_wr(via, ch->rbase + VIA_RP_CONTROL,
929164614Sariff		    SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
930164614Sariff		    ((via->polling == 0) ?
931164614Sariff		    (SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG) : 0), 1);
932164614Sariff		ch->active = 1;
933102011Sorion		break;
934102011Sorion	case PCMTRIG_STOP:
935102011Sorion	case PCMTRIG_ABORT:
936102011Sorion		via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1);
937111269Sorion		via8233chan_mute(via, ch, 1);
938102011Sorion		via8233chan_reset(via, ch);
939164614Sariff		ch->active = 0;
940164614Sariff		if (via->polling != 0) {
941164614Sariff			if (via_chan_active(via) == 0) {
942164614Sariff				callout_stop(&via->poll_timer);
943164614Sariff				via->poll_ticks = 1;
944164614Sariff			} else {
945164614Sariff				pollticks = via_poll_ticks(via);
946164614Sariff				if (pollticks > via->poll_ticks) {
947164614Sariff					if (bootverbose)
948164614Sariff						printf("%s: pollticks "
949164614Sariff						    "%d -> %d\n",
950164614Sariff						    __func__, via->poll_ticks,
951164614Sariff						    pollticks);
952164614Sariff					via->poll_ticks = pollticks;
953164614Sariff					callout_reset(&via->poll_timer,
954164614Sariff					    1, via_poll_callback,
955164614Sariff					    via);
956164614Sariff				}
957164614Sariff			}
958164614Sariff		}
959102011Sorion		break;
960164614Sariff	default:
961164614Sariff		break;
962102011Sorion	}
963150980Snetchild	snd_mtxunlock(via->lock);
964164614Sariff	return (0);
965102011Sorion}
966102011Sorion
967111269Sorionstatic kobj_method_t via8233wr_methods[] = {
968164614Sariff	KOBJMETHOD(channel_init,		via8233wr_init),
969164614Sariff	KOBJMETHOD(channel_setformat,		via8233wr_setformat),
970164614Sariff	KOBJMETHOD(channel_setspeed,		via8233wr_setspeed),
971164614Sariff	KOBJMETHOD(channel_getcaps,		via8233wr_getcaps),
972164614Sariff	KOBJMETHOD(channel_setblocksize,	via8233chan_setblocksize),
973167648Sariff	KOBJMETHOD(channel_setfragments,	via8233chan_setfragments),
974164614Sariff	KOBJMETHOD(channel_trigger,		via8233chan_trigger),
975164614Sariff	KOBJMETHOD(channel_getptr,		via8233chan_getptr),
976193640Sariff	KOBJMETHOD_END
977102011Sorion};
978111269SorionCHANNEL_DECLARE(via8233wr);
979102011Sorion
980111269Sorionstatic kobj_method_t via8233dxs_methods[] = {
981164614Sariff	KOBJMETHOD(channel_init,		via8233dxs_init),
982164614Sariff	KOBJMETHOD(channel_setformat,		via8233dxs_setformat),
983164614Sariff	KOBJMETHOD(channel_setspeed,		via8233dxs_setspeed),
984164614Sariff	KOBJMETHOD(channel_getcaps,		via8233dxs_getcaps),
985164614Sariff	KOBJMETHOD(channel_setblocksize,	via8233chan_setblocksize),
986167648Sariff	KOBJMETHOD(channel_setfragments,	via8233chan_setfragments),
987164614Sariff	KOBJMETHOD(channel_trigger,		via8233chan_trigger),
988164614Sariff	KOBJMETHOD(channel_getptr,		via8233chan_getptr),
989193640Sariff	KOBJMETHOD_END
990102011Sorion};
991111269SorionCHANNEL_DECLARE(via8233dxs);
992102011Sorion
993111269Sorionstatic kobj_method_t via8233msgd_methods[] = {
994164614Sariff	KOBJMETHOD(channel_init,		via8233msgd_init),
995164614Sariff	KOBJMETHOD(channel_setformat,		via8233msgd_setformat),
996164614Sariff	KOBJMETHOD(channel_setspeed,		via8233msgd_setspeed),
997164614Sariff	KOBJMETHOD(channel_getcaps,		via8233msgd_getcaps),
998164614Sariff	KOBJMETHOD(channel_setblocksize,	via8233chan_setblocksize),
999167648Sariff	KOBJMETHOD(channel_setfragments,	via8233chan_setfragments),
1000164614Sariff	KOBJMETHOD(channel_trigger,		via8233chan_trigger),
1001164614Sariff	KOBJMETHOD(channel_getptr,		via8233chan_getptr),
1002193640Sariff	KOBJMETHOD_END
1003111269Sorion};
1004111269SorionCHANNEL_DECLARE(via8233msgd);
1005111269Sorion
1006102011Sorion/* -------------------------------------------------------------------- */
1007102011Sorion
1008102011Sorionstatic void
1009102011Sorionvia_intr(void *p)
1010102011Sorion{
1011102011Sorion	struct via_info *via = p;
1012164614Sariff	uint32_t ptrigger = 0, rtrigger = 0;
1013157956Sariff	int i, reg, stat;
1014102011Sorion
1015164614Sariff	snd_mtxlock(via->lock);
1016164614Sariff	if (via->polling != 0) {
1017164614Sariff		snd_mtxunlock(via->lock);
1018164614Sariff		return;
1019164614Sariff	}
1020111269Sorion	/* Poll playback channels */
1021111269Sorion	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
1022164614Sariff		if (via->pch[i].channel == NULL || via->pch[i].active == 0)
1023111269Sorion			continue;
1024157956Sariff		reg = via->pch[i].rbase + VIA_RP_STATUS;
1025157956Sariff		stat = via_rd(via, reg, 1);
1026157956Sariff		if (stat & SGD_STATUS_INTR) {
1027157956Sariff			if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
1028164614Sariff			    !(stat & SGD_STATUS_ACTIVE)))
1029164614Sariff				via_wr(via, via->pch[i].rbase + VIA_RP_CONTROL,
1030164614Sariff				    SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
1031164614Sariff				    SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1);
1032157956Sariff			via_wr(via, reg, stat, 1);
1033164614Sariff			ptrigger |= 1 << i;
1034111269Sorion		}
1035102011Sorion	}
1036111269Sorion	/* Poll record channels */
1037111269Sorion	for (i = 0; i < NWRCHANS; i++) {
1038164614Sariff		if (via->rch[i].channel == NULL || via->rch[i].active == 0)
1039111269Sorion			continue;
1040157956Sariff		reg = via->rch[i].rbase + VIA_RP_STATUS;
1041157956Sariff		stat = via_rd(via, reg, 1);
1042157956Sariff		if (stat & SGD_STATUS_INTR) {
1043157956Sariff			if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
1044164614Sariff			    !(stat & SGD_STATUS_ACTIVE)))
1045164614Sariff				via_wr(via, via->rch[i].rbase + VIA_RP_CONTROL,
1046164614Sariff				    SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
1047164614Sariff				    SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1);
1048157956Sariff			via_wr(via, reg, stat, 1);
1049164614Sariff			rtrigger |= 1 << i;
1050111269Sorion		}
1051111269Sorion	}
1052148596Snetchild	snd_mtxunlock(via->lock);
1053164614Sariff
1054164614Sariff	for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
1055164614Sariff		if (ptrigger & (1 << i))
1056164614Sariff			chn_intr(via->pch[i].channel);
1057164614Sariff	}
1058164614Sariff	for (i = 0; i < NWRCHANS; i++) {
1059164614Sariff		if (rtrigger & (1 << i))
1060164614Sariff			chn_intr(via->rch[i].channel);
1061164614Sariff	}
1062102011Sorion}
1063102011Sorion
1064102011Sorion/*
1065102011Sorion *  Probe and attach the card
1066102011Sorion */
1067102011Sorionstatic int
1068102011Sorionvia_probe(device_t dev)
1069102011Sorion{
1070102011Sorion	switch(pci_get_devid(dev)) {
1071102011Sorion	case VIA8233_PCI_ID:
1072102011Sorion		switch(pci_get_revid(dev)) {
1073164614Sariff		case VIA8233_REV_ID_8233PRE:
1074102011Sorion			device_set_desc(dev, "VIA VT8233 (pre)");
1075164614Sariff			return (BUS_PROBE_DEFAULT);
1076111269Sorion		case VIA8233_REV_ID_8233C:
1077102011Sorion			device_set_desc(dev, "VIA VT8233C");
1078164614Sariff			return (BUS_PROBE_DEFAULT);
1079111269Sorion		case VIA8233_REV_ID_8233:
1080102011Sorion			device_set_desc(dev, "VIA VT8233");
1081164614Sariff			return (BUS_PROBE_DEFAULT);
1082111269Sorion		case VIA8233_REV_ID_8233A:
1083102011Sorion			device_set_desc(dev, "VIA VT8233A");
1084164614Sariff			return (BUS_PROBE_DEFAULT);
1085111269Sorion		case VIA8233_REV_ID_8235:
1086102011Sorion			device_set_desc(dev, "VIA VT8235");
1087164614Sariff			return (BUS_PROBE_DEFAULT);
1088129758Smatk		case VIA8233_REV_ID_8237:
1089129758Smatk			device_set_desc(dev, "VIA VT8237");
1090164614Sariff			return (BUS_PROBE_DEFAULT);
1091157956Sariff		case VIA8233_REV_ID_8251:
1092157956Sariff			device_set_desc(dev, "VIA VT8251");
1093164614Sariff			return (BUS_PROBE_DEFAULT);
1094102011Sorion		default:
1095102011Sorion			device_set_desc(dev, "VIA VT8233X");	/* Unknown */
1096164614Sariff			return (BUS_PROBE_DEFAULT);
1097164614Sariff		}
1098102011Sorion	}
1099164614Sariff	return (ENXIO);
1100102011Sorion}
1101102011Sorion
1102102011Sorionstatic void
1103102011Soriondma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
1104102011Sorion{
1105111183Scognet	struct via_info *via = (struct via_info *)p;
1106111183Scognet	via->sgd_addr = bds->ds_addr;
1107102011Sorion}
1108102011Sorion
1109102011Sorionstatic int
1110102011Sorionvia_chip_init(device_t dev)
1111102011Sorion{
1112164614Sariff	uint32_t data, cnt;
1113102011Sorion
1114113598Sorion	/* Wake up and reset AC97 if necessary */
1115113598Sorion	data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1);
1116102011Sorion
1117113598Sorion	if ((data & VIA_PCI_ACLINK_C00_READY) == 0) {
1118113598Sorion		/* Cold reset per ac97r2.3 spec (page 95) */
1119113598Sorion		/* Assert low */
1120164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1121164614Sariff		    VIA_PCI_ACLINK_EN, 1);
1122113598Sorion		/* Wait T_rst_low */
1123164614Sariff		DELAY(100);
1124113598Sorion		/* Assert high */
1125164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1126164614Sariff		    VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST, 1);
1127113598Sorion		/* Wait T_rst2clk */
1128113598Sorion		DELAY(5);
1129113598Sorion		/* Assert low */
1130164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1131164614Sariff		    VIA_PCI_ACLINK_EN, 1);
1132113598Sorion	} else {
1133113598Sorion		/* Warm reset */
1134113598Sorion		/* Force no sync */
1135164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1136164614Sariff		    VIA_PCI_ACLINK_EN, 1);
1137113598Sorion		DELAY(100);
1138113598Sorion		/* Sync */
1139164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1140164614Sariff		    VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_SYNC, 1);
1141113598Sorion		/* Wait T_sync_high */
1142113598Sorion		DELAY(5);
1143113598Sorion		/* Force no sync */
1144164614Sariff		pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
1145164614Sariff		    VIA_PCI_ACLINK_EN, 1);
1146113598Sorion		/* Wait T_sync2clk */
1147113598Sorion		DELAY(5);
1148113598Sorion	}
1149102011Sorion
1150113598Sorion	/* Power everything up */
1151113598Sorion	pci_write_config(dev, VIA_PCI_ACLINK_CTRL, VIA_PCI_ACLINK_DESIRED, 1);
1152102011Sorion
1153113598Sorion	/* Wait for codec to become ready (largest reported delay 310ms) */
1154113598Sorion	for (cnt = 0; cnt < 2000; cnt++) {
1155113598Sorion		data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1);
1156164614Sariff		if (data & VIA_PCI_ACLINK_C00_READY)
1157164614Sariff			return (0);
1158113598Sorion		DELAY(5000);
1159102011Sorion	}
1160113598Sorion	device_printf(dev, "primary codec not ready (cnt = 0x%02x)\n", cnt);
1161164614Sariff	return (ENXIO);
1162102011Sorion}
1163102011Sorion
1164102011Sorionstatic int
1165102011Sorionvia_attach(device_t dev)
1166102011Sorion{
1167102011Sorion	struct via_info *via = 0;
1168102011Sorion	char status[SND_STATUSLEN];
1169148596Snetchild	int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum;
1170164614Sariff	int nsegs;
1171157956Sariff	uint32_t revid;
1172102011Sorion
1173170721Sariff	via = malloc(sizeof *via, M_DEVBUF, M_WAITOK | M_ZERO);
1174167608Sariff	via->lock = snd_mtxcreate(device_get_nameunit(dev),
1175167608Sariff	    "snd_via8233 softc");
1176167648Sariff	via->dev = dev;
1177102011Sorion
1178164614Sariff	callout_init(&via->poll_timer, CALLOUT_MPSAFE);
1179164614Sariff	via->poll_ticks = 1;
1180164614Sariff
1181164614Sariff	if (resource_int_value(device_get_name(dev),
1182164614Sariff	    device_get_unit(dev), "polling", &i) == 0 && i != 0)
1183164614Sariff		via->polling = 1;
1184164614Sariff	else
1185164614Sariff		via->polling = 0;
1186164614Sariff
1187102011Sorion	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
1188102011Sorion	pci_enable_busmaster(dev);
1189148596Snetchild
1190119690Sjhb	via->regid = PCIR_BAR(0);
1191127135Snjl	via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid,
1192127135Snjl					  RF_ACTIVE);
1193102011Sorion	if (!via->reg) {
1194102011Sorion		device_printf(dev, "cannot allocate bus resource.");
1195102011Sorion		goto bad;
1196102011Sorion	}
1197102011Sorion	via->st = rman_get_bustag(via->reg);
1198102011Sorion	via->sh = rman_get_bushandle(via->reg);
1199102011Sorion
1200102011Sorion	via->irqid = 0;
1201127135Snjl	via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
1202164614Sariff	    RF_ACTIVE | RF_SHAREABLE);
1203164614Sariff	if (!via->irq ||
1204164614Sariff	    snd_setup_intr(dev, via->irq, INTR_MPSAFE,
1205164614Sariff	    via_intr, via, &via->ih)) {
1206102011Sorion		device_printf(dev, "unable to map interrupt\n");
1207102011Sorion		goto bad;
1208102011Sorion	}
1209102011Sorion
1210164614Sariff	via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536);
1211164614Sariff	if (resource_int_value(device_get_name(dev),
1212164614Sariff	    device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
1213167648Sariff		i &= VIA_BLK_ALIGN;
1214167648Sariff		if (i < VIA_BLK_MIN)
1215167648Sariff			i = VIA_BLK_MIN;
1216164614Sariff		via->blkcnt = via->bufsz / i;
1217164614Sariff		i = 0;
1218164614Sariff		while (via->blkcnt >> i)
1219164614Sariff			i++;
1220164614Sariff		via->blkcnt = 1 << (i - 1);
1221164614Sariff		if (via->blkcnt < VIA_SEGS_MIN)
1222164614Sariff			via->blkcnt = VIA_SEGS_MIN;
1223164614Sariff		else if (via->blkcnt > VIA_SEGS_MAX)
1224164614Sariff			via->blkcnt = VIA_SEGS_MAX;
1225102011Sorion
1226164614Sariff	} else
1227164614Sariff		via->blkcnt = VIA_SEGS_DEFAULT;
1228102011Sorion
1229157956Sariff	revid = pci_get_revid(dev);
1230157956Sariff
1231148596Snetchild	/*
1232157956Sariff	 * VIA8251 lost its interrupt after DMA EOL, and need
1233157956Sariff	 * a gentle spank on its face within interrupt handler.
1234157956Sariff	 */
1235157956Sariff	if (revid == VIA8233_REV_ID_8251)
1236157956Sariff		via->dma_eol_wake = 1;
1237157956Sariff	else
1238157956Sariff		via->dma_eol_wake = 0;
1239157956Sariff
1240157956Sariff	/*
1241148596Snetchild	 * Decide whether DXS had to be disabled or not
1242148596Snetchild	 */
1243157956Sariff	if (revid == VIA8233_REV_ID_8233A) {
1244112671Sorion		/*
1245112671Sorion		 * DXS channel is disabled.  Reports from multiple users
1246112671Sorion		 * that it plays at half-speed.  Do not see this behaviour
1247112671Sorion		 * on available 8233C or when emulating 8233A register set
1248112671Sorion		 * on 8233C (either with or without ac97 VRA).
1249112671Sorion		 */
1250148596Snetchild		via_dxs_disabled = 1;
1251148596Snetchild	} else if (resource_int_value(device_get_name(dev),
1252164614Sariff	    device_get_unit(dev), "via_dxs_disabled",
1253164614Sariff	    &via_dxs_disabled) == 0)
1254148596Snetchild		via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0;
1255148596Snetchild	else
1256148596Snetchild		via_dxs_disabled = 0;
1257148596Snetchild
1258148596Snetchild	if (via_dxs_disabled) {
1259148596Snetchild		via_dxs_chnum = 0;
1260148596Snetchild		via_sgd_chnum = 1;
1261111269Sorion	} else {
1262148596Snetchild		if (resource_int_value(device_get_name(dev),
1263164614Sariff		    device_get_unit(dev), "via_dxs_channels",
1264164614Sariff		    &via_dxs_chnum) != 0)
1265148596Snetchild			via_dxs_chnum = NDXSCHANS;
1266148596Snetchild		if (resource_int_value(device_get_name(dev),
1267164614Sariff		    device_get_unit(dev), "via_sgd_channels",
1268164614Sariff		    &via_sgd_chnum) != 0)
1269148596Snetchild			via_sgd_chnum = NMSGDCHANS;
1270148596Snetchild	}
1271148596Snetchild	if (via_dxs_chnum > NDXSCHANS)
1272148596Snetchild		via_dxs_chnum = NDXSCHANS;
1273148596Snetchild	else if (via_dxs_chnum < 0)
1274148596Snetchild		via_dxs_chnum = 0;
1275148596Snetchild	if (via_sgd_chnum > NMSGDCHANS)
1276148596Snetchild		via_sgd_chnum = NMSGDCHANS;
1277148596Snetchild	else if (via_sgd_chnum < 0)
1278148596Snetchild		via_sgd_chnum = 0;
1279148596Snetchild	if (via_dxs_chnum + via_sgd_chnum < 1) {
1280148596Snetchild		/* Minimalist ? */
1281148596Snetchild		via_dxs_chnum = 1;
1282148596Snetchild		via_sgd_chnum = 0;
1283148596Snetchild	}
1284148596Snetchild	if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev),
1285164614Sariff	    device_get_unit(dev), "via_dxs_src", &via_dxs_src) == 0)
1286148596Snetchild		via->dxs_src = (via_dxs_src > 0) ? 1 : 0;
1287148596Snetchild	else
1288148596Snetchild		via->dxs_src = 0;
1289164614Sariff
1290167648Sariff	nsegs = (via_dxs_chnum + via_sgd_chnum + NWRCHANS) * VIA_SEGS_MAX;
1291164614Sariff
1292164614Sariff	/* DMA tag for buffers */
1293166904Snetchild	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
1294166904Snetchild		/*boundary*/0,
1295164614Sariff		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1296164614Sariff		/*highaddr*/BUS_SPACE_MAXADDR,
1297164614Sariff		/*filter*/NULL, /*filterarg*/NULL,
1298164614Sariff		/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
1299164614Sariff		/*flags*/0, /*lockfunc*/NULL,
1300164614Sariff		/*lockarg*/NULL, &via->parent_dmat) != 0) {
1301164614Sariff		device_printf(dev, "unable to create dma tag\n");
1302164614Sariff		goto bad;
1303164614Sariff	}
1304164614Sariff
1305164614Sariff	/*
1306164614Sariff	 *  DMA tag for SGD table.  The 686 uses scatter/gather DMA and
1307164614Sariff	 *  requires a list in memory of work to do.  We need only 16 bytes
1308164614Sariff	 *  for this list, and it is wasteful to allocate 16K.
1309164614Sariff	 */
1310166904Snetchild	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
1311166904Snetchild		/*boundary*/0,
1312164614Sariff		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1313164614Sariff		/*highaddr*/BUS_SPACE_MAXADDR,
1314164614Sariff		/*filter*/NULL, /*filterarg*/NULL,
1315164614Sariff		/*maxsize*/nsegs * sizeof(struct via_dma_op),
1316164614Sariff		/*nsegments*/1, /*maxsegz*/0x3ffff,
1317164614Sariff		/*flags*/0, /*lockfunc*/NULL,
1318164614Sariff		/*lockarg*/NULL, &via->sgd_dmat) != 0) {
1319164614Sariff		device_printf(dev, "unable to create dma tag\n");
1320164614Sariff		goto bad;
1321164614Sariff	}
1322164614Sariff
1323164614Sariff	if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table,
1324164614Sariff	    BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1)
1325164614Sariff		goto bad;
1326164614Sariff	if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table,
1327164614Sariff	    nsegs * sizeof(struct via_dma_op), dma_cb, via, 0))
1328164614Sariff		goto bad;
1329164614Sariff
1330164614Sariff	if (via_chip_init(dev))
1331164614Sariff		goto bad;
1332164614Sariff
1333164614Sariff	via->codec = AC97_CREATE(dev, via, via_ac97);
1334164614Sariff	if (!via->codec)
1335164614Sariff		goto bad;
1336164614Sariff
1337164614Sariff	mixer_init(dev, ac97_getmixerclass(), via->codec);
1338164614Sariff
1339164614Sariff	via->codec_caps = ac97_getextcaps(via->codec);
1340164614Sariff
1341164614Sariff	/* Try to set VRA without generating an error, VRM not reqrd yet */
1342164614Sariff	if (via->codec_caps &
1343164614Sariff	    (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM | AC97_EXTCAP_DRA)) {
1344164614Sariff		uint16_t ext = ac97_getextmode(via->codec);
1345164614Sariff		ext |= (via->codec_caps &
1346164614Sariff		    (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM));
1347164614Sariff		ext &= ~AC97_EXTCAP_DRA;
1348164614Sariff		ac97_setextmode(via->codec, ext);
1349164614Sariff	}
1350164614Sariff
1351164614Sariff	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
1352164614Sariff	    rman_get_start(via->reg), rman_get_start(via->irq),
1353164614Sariff	    PCM_KLDSTRING(snd_via8233));
1354164614Sariff
1355148596Snetchild	/* Register */
1356148596Snetchild	if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
1357148596Snetchild	      goto bad;
1358148596Snetchild	for (i = 0; i < via_dxs_chnum; i++)
1359148596Snetchild	      pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
1360148596Snetchild	for (i = 0; i < via_sgd_chnum; i++)
1361148596Snetchild	      pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
1362148596Snetchild	for (i = 0; i < NWRCHANS; i++)
1363148596Snetchild	      pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
1364148596Snetchild	if (via_dxs_chnum > 0)
1365112671Sorion		via_init_sysctls(dev);
1366148596Snetchild	device_printf(dev, "<VIA DXS %sabled: DXS%s %d / SGD %d / REC %d>\n",
1367164614Sariff	    (via_dxs_chnum > 0) ? "En" : "Dis", (via->dxs_src) ? "(SRC)" : "",
1368164614Sariff	    via_dxs_chnum, via_sgd_chnum, NWRCHANS);
1369102011Sorion
1370102011Sorion	pcm_setstatus(dev, status);
1371102011Sorion
1372164614Sariff	return (0);
1373102011Sorionbad:
1374164614Sariff	if (via->codec)
1375164614Sariff		ac97_destroy(via->codec);
1376164614Sariff	if (via->reg)
1377164614Sariff		bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
1378164614Sariff	if (via->ih)
1379164614Sariff		bus_teardown_intr(dev, via->irq, via->ih);
1380164614Sariff	if (via->irq)
1381164614Sariff		bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
1382164614Sariff	if (via->parent_dmat)
1383164614Sariff		bus_dma_tag_destroy(via->parent_dmat);
1384164614Sariff	if (via->sgd_dmamap)
1385164614Sariff		bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
1386167773Sariff	if (via->sgd_table)
1387167773Sariff		bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap);
1388164614Sariff	if (via->sgd_dmat)
1389164614Sariff		bus_dma_tag_destroy(via->sgd_dmat);
1390164614Sariff	if (via->lock)
1391164614Sariff		snd_mtxfree(via->lock);
1392164614Sariff	if (via)
1393164614Sariff		free(via, M_DEVBUF);
1394164614Sariff	return (ENXIO);
1395102011Sorion}
1396102011Sorion
1397102011Sorionstatic int
1398102011Sorionvia_detach(device_t dev)
1399102011Sorion{
1400102011Sorion	int r;
1401170721Sariff	struct via_info *via;
1402102011Sorion
1403102011Sorion	r = pcm_unregister(dev);
1404164614Sariff	if (r)
1405164614Sariff		return (r);
1406102011Sorion
1407102011Sorion	via = pcm_getdevinfo(dev);
1408170721Sariff
1409170721Sariff	if (via != NULL && (via->play_num != 0 || via->rec_num != 0)) {
1410170721Sariff		snd_mtxlock(via->lock);
1411170721Sariff		via->polling = 0;
1412170721Sariff		callout_stop(&via->poll_timer);
1413170721Sariff		snd_mtxunlock(via->lock);
1414170721Sariff		callout_drain(&via->poll_timer);
1415170721Sariff	}
1416170721Sariff
1417102011Sorion	bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
1418102011Sorion	bus_teardown_intr(dev, via->irq, via->ih);
1419102011Sorion	bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
1420102011Sorion	bus_dma_tag_destroy(via->parent_dmat);
1421102011Sorion	bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
1422167773Sariff	bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap);
1423102011Sorion	bus_dma_tag_destroy(via->sgd_dmat);
1424148596Snetchild	snd_mtxfree(via->lock);
1425102011Sorion	free(via, M_DEVBUF);
1426164614Sariff	return (0);
1427102011Sorion}
1428102011Sorion
1429102011Sorion
1430102011Sorionstatic device_method_t via_methods[] = {
1431102011Sorion	DEVMETHOD(device_probe,		via_probe),
1432102011Sorion	DEVMETHOD(device_attach,	via_attach),
1433102011Sorion	DEVMETHOD(device_detach,	via_detach),
1434102011Sorion	{ 0, 0}
1435102011Sorion};
1436102011Sorion
1437102011Sorionstatic driver_t via_driver = {
1438102011Sorion	"pcm",
1439102011Sorion	via_methods,
1440102011Sorion	PCM_SOFTC_SIZE,
1441102011Sorion};
1442102011Sorion
1443102011SorionDRIVER_MODULE(snd_via8233, pci, via_driver, pcm_devclass, 0, 0);
1444132236StanimuraMODULE_DEPEND(snd_via8233, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
1445102011SorionMODULE_VERSION(snd_via8233, 1);
1446