Commit | Line | Data |
---|---|---|
558a398b | 1 | /*- |
984263bc MD |
2 | * Copyright (c) 2002 Orion Hodson <orion@freebsd.org> |
3 | * Portions of this code derived from via82c686.c: | |
4 | * Copyright (c) 2000 David Jones <dej@ox.org> | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
26 | * SUCH DAMAGE. | |
27 | */ | |
28 | ||
558a398b SS |
29 | /* |
30 | * Credits due to: | |
984263bc MD |
31 | * |
32 | * Grzybowski Rafal, Russell Davies, Mark Handley, Daniel O'Connor for | |
33 | * comments, machine time, testing patches, and patience. VIA for | |
34 | * providing specs. ALSA for helpful comments and some register poke | |
2a1ad637 | 35 | * ordering. |
984263bc MD |
36 | */ |
37 | ||
2a1ad637 FT |
38 | #ifdef HAVE_KERNEL_OPTION_HEADERS |
39 | #include "opt_snd.h" | |
40 | #endif | |
41 | ||
984263bc MD |
42 | #include <dev/sound/pcm/sound.h> |
43 | #include <dev/sound/pcm/ac97.h> | |
44 | ||
67931cc4 FT |
45 | #include <bus/pci/pcireg.h> |
46 | #include <bus/pci/pcivar.h> | |
984263bc MD |
47 | #include <sys/sysctl.h> |
48 | ||
49 | #include <dev/sound/pci/via8233.h> | |
50 | ||
2a1ad637 | 51 | SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/via8233.c 267581 2014-06-17 16:07:57Z jhb $"); |
984263bc MD |
52 | |
53 | #define VIA8233_PCI_ID 0x30591106 | |
54 | ||
558a398b SS |
55 | #define VIA8233_REV_ID_8233PRE 0x10 |
56 | #define VIA8233_REV_ID_8233C 0x20 | |
57 | #define VIA8233_REV_ID_8233 0x30 | |
58 | #define VIA8233_REV_ID_8233A 0x40 | |
59 | #define VIA8233_REV_ID_8235 0x50 | |
60 | #define VIA8233_REV_ID_8237 0x60 | |
61 | #define VIA8233_REV_ID_8251 0x70 | |
62 | ||
984263bc | 63 | #define SEGS_PER_CHAN 2 /* Segments per channel */ |
558a398b SS |
64 | #define NDXSCHANS 4 /* No of DXS channels */ |
65 | #define NMSGDCHANS 1 /* No of multichannel SGD */ | |
66 | #define NWRCHANS 1 /* No of write channels */ | |
67 | #define NCHANS (NWRCHANS + NDXSCHANS + NMSGDCHANS) | |
984263bc | 68 | #define NSEGS NCHANS * SEGS_PER_CHAN /* Segments in SGD table */ |
2a1ad637 FT |
69 | #define VIA_SEGS_MIN 2 |
70 | #define VIA_SEGS_MAX 64 | |
71 | #define VIA_SEGS_DEFAULT 2 | |
72 | #define VIA_BLK_MIN 32 | |
73 | #define VIA_BLK_ALIGN (~(VIA_BLK_MIN - 1)) | |
984263bc MD |
74 | |
75 | #define VIA_DEFAULT_BUFSZ 0x1000 | |
76 | ||
984263bc MD |
77 | /* we rely on this struct being packed to 64 bits */ |
78 | struct via_dma_op { | |
2a1ad637 FT |
79 | volatile uint32_t ptr; |
80 | volatile uint32_t flags; | |
984263bc MD |
81 | #define VIA_DMAOP_EOL 0x80000000 |
82 | #define VIA_DMAOP_FLAG 0x40000000 | |
83 | #define VIA_DMAOP_STOP 0x20000000 | |
84 | #define VIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF) | |
85 | }; | |
86 | ||
87 | struct via_info; | |
88 | ||
89 | struct via_chinfo { | |
90 | struct via_info *parent; | |
91 | struct pcm_channel *channel; | |
92 | struct snd_dbuf *buffer; | |
93 | struct via_dma_op *sgd_table; | |
558a398b | 94 | bus_addr_t sgd_addr; |
2a1ad637 FT |
95 | int dir, rbase, active; |
96 | unsigned int blksz, blkcnt; | |
97 | unsigned int ptr, prevptr; | |
984263bc MD |
98 | }; |
99 | ||
100 | struct via_info { | |
2a1ad637 FT |
101 | device_t dev; |
102 | ||
984263bc MD |
103 | bus_space_tag_t st; |
104 | bus_space_handle_t sh; | |
105 | bus_dma_tag_t parent_dmat; | |
106 | bus_dma_tag_t sgd_dmat; | |
107 | bus_dmamap_t sgd_dmamap; | |
558a398b | 108 | bus_addr_t sgd_addr; |
984263bc MD |
109 | |
110 | struct resource *reg, *irq; | |
111 | int regid, irqid; | |
112 | void *ih; | |
113 | struct ac97_info *codec; | |
114 | ||
2a1ad637 | 115 | unsigned int bufsz, blkcnt; |
558a398b | 116 | int dxs_src, dma_eol_wake; |
984263bc | 117 | |
558a398b SS |
118 | struct via_chinfo pch[NDXSCHANS + NMSGDCHANS]; |
119 | struct via_chinfo rch[NWRCHANS]; | |
984263bc | 120 | struct via_dma_op *sgd_table; |
2a1ad637 FT |
121 | uint16_t codec_caps; |
122 | uint16_t n_dxs_registered; | |
123 | int play_num, rec_num; | |
67931cc4 | 124 | struct lock *lock; |
2a1ad637 FT |
125 | struct callout poll_timer; |
126 | int poll_ticks, polling; | |
984263bc MD |
127 | }; |
128 | ||
2a1ad637 FT |
129 | static uint32_t via_fmt[] = { |
130 | SND_FORMAT(AFMT_U8, 1, 0), | |
131 | SND_FORMAT(AFMT_U8, 2, 0), | |
132 | SND_FORMAT(AFMT_S16_LE, 1, 0), | |
133 | SND_FORMAT(AFMT_S16_LE, 2, 0), | |
984263bc MD |
134 | 0 |
135 | }; | |
136 | ||
137 | static struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 }; | |
138 | static struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 }; | |
139 | ||
2a1ad637 FT |
140 | static __inline int |
141 | via_chan_active(struct via_info *via) | |
142 | { | |
143 | int i, ret = 0; | |
144 | ||
145 | if (via == NULL) | |
146 | return (0); | |
147 | ||
148 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) | |
149 | ret += via->pch[i].active; | |
150 | ||
151 | for (i = 0; i < NWRCHANS; i++) | |
152 | ret += via->rch[i].active; | |
153 | ||
154 | return (ret); | |
155 | } | |
156 | ||
558a398b SS |
157 | static int |
158 | sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS) | |
159 | { | |
160 | struct via_info *via; | |
161 | device_t dev; | |
162 | uint32_t r; | |
163 | int err, new_en; | |
164 | ||
165 | dev = oidp->oid_arg1; | |
166 | via = pcm_getdevinfo(dev); | |
167 | snd_mtxlock(via->lock); | |
168 | r = pci_read_config(dev, VIA_PCI_SPDIF, 1); | |
169 | snd_mtxunlock(via->lock); | |
170 | new_en = (r & VIA_SPDIF_EN) ? 1 : 0; | |
4520cefc | 171 | err = sysctl_handle_int(oidp, &new_en, 0, req); |
558a398b SS |
172 | |
173 | if (err || req->newptr == NULL) | |
2a1ad637 | 174 | return (err); |
558a398b | 175 | if (new_en < 0 || new_en > 1) |
2a1ad637 | 176 | return (EINVAL); |
558a398b SS |
177 | |
178 | if (new_en) | |
179 | r |= VIA_SPDIF_EN; | |
180 | else | |
181 | r &= ~VIA_SPDIF_EN; | |
182 | snd_mtxlock(via->lock); | |
183 | pci_write_config(dev, VIA_PCI_SPDIF, r, 1); | |
184 | snd_mtxunlock(via->lock); | |
185 | ||
2a1ad637 | 186 | return (0); |
558a398b SS |
187 | } |
188 | ||
558a398b SS |
189 | static int |
190 | sysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS) | |
191 | { | |
192 | struct via_info *via; | |
193 | device_t dev; | |
194 | int err, val; | |
195 | ||
196 | dev = oidp->oid_arg1; | |
197 | via = pcm_getdevinfo(dev); | |
198 | snd_mtxlock(via->lock); | |
199 | val = via->dxs_src; | |
200 | snd_mtxunlock(via->lock); | |
4520cefc | 201 | err = sysctl_handle_int(oidp, &val, 0, req); |
558a398b SS |
202 | |
203 | if (err || req->newptr == NULL) | |
2a1ad637 | 204 | return (err); |
558a398b | 205 | if (val < 0 || val > 1) |
2a1ad637 | 206 | return (EINVAL); |
558a398b SS |
207 | |
208 | snd_mtxlock(via->lock); | |
209 | via->dxs_src = val; | |
210 | snd_mtxunlock(via->lock); | |
211 | ||
2a1ad637 FT |
212 | return (0); |
213 | } | |
214 | ||
215 | static int | |
216 | sysctl_via_polling(SYSCTL_HANDLER_ARGS) | |
217 | { | |
218 | struct via_info *via; | |
219 | device_t dev; | |
220 | int err, val; | |
221 | ||
222 | dev = oidp->oid_arg1; | |
223 | via = pcm_getdevinfo(dev); | |
224 | if (via == NULL) | |
225 | return (EINVAL); | |
226 | snd_mtxlock(via->lock); | |
227 | val = via->polling; | |
228 | snd_mtxunlock(via->lock); | |
229 | err = sysctl_handle_int(oidp, &val, 0, req); | |
230 | ||
231 | if (err || req->newptr == NULL) | |
232 | return (err); | |
233 | if (val < 0 || val > 1) | |
234 | return (EINVAL); | |
235 | ||
236 | snd_mtxlock(via->lock); | |
237 | if (val != via->polling) { | |
238 | if (via_chan_active(via) != 0) | |
239 | err = EBUSY; | |
240 | else if (val == 0) | |
241 | via->polling = 0; | |
242 | else | |
243 | via->polling = 1; | |
244 | } | |
245 | snd_mtxunlock(via->lock); | |
246 | ||
247 | return (err); | |
558a398b | 248 | } |
558a398b SS |
249 | |
250 | static void | |
251 | via_init_sysctls(device_t dev) | |
252 | { | |
2a1ad637 FT |
253 | /* XXX: an user should be able to set this with a control tool, |
254 | if not done before 7.0-RELEASE, this needs to be converted to | |
255 | a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*() | |
256 | as discussed on multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */ | |
257 | SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |
258 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | |
259 | "spdif_enabled", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), | |
260 | sysctl_via8233_spdif_enable, "I", | |
261 | "Enable S/PDIF output on primary playback channel"); | |
262 | SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |
263 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | |
264 | "dxs_src", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), | |
265 | sysctl_via8233_dxs_src, "I", | |
266 | "Enable VIA DXS Sample Rate Converter"); | |
267 | SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |
268 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | |
269 | "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), | |
270 | sysctl_via_polling, "I", | |
271 | "Enable polling mode"); | |
558a398b SS |
272 | } |
273 | ||
2a1ad637 | 274 | static __inline uint32_t |
984263bc MD |
275 | via_rd(struct via_info *via, int regno, int size) |
276 | { | |
277 | switch (size) { | |
278 | case 1: | |
2a1ad637 | 279 | return (bus_space_read_1(via->st, via->sh, regno)); |
984263bc | 280 | case 2: |
2a1ad637 | 281 | return (bus_space_read_2(via->st, via->sh, regno)); |
984263bc | 282 | case 4: |
2a1ad637 | 283 | return (bus_space_read_4(via->st, via->sh, regno)); |
984263bc | 284 | default: |
2a1ad637 | 285 | return (0xFFFFFFFF); |
984263bc MD |
286 | } |
287 | } | |
288 | ||
558a398b | 289 | static __inline void |
2a1ad637 | 290 | via_wr(struct via_info *via, int regno, uint32_t data, int size) |
984263bc MD |
291 | { |
292 | ||
293 | switch (size) { | |
294 | case 1: | |
295 | bus_space_write_1(via->st, via->sh, regno, data); | |
296 | break; | |
297 | case 2: | |
298 | bus_space_write_2(via->st, via->sh, regno, data); | |
299 | break; | |
300 | case 4: | |
301 | bus_space_write_4(via->st, via->sh, regno, data); | |
302 | break; | |
303 | } | |
304 | } | |
305 | ||
306 | /* -------------------------------------------------------------------- */ | |
307 | /* Codec interface */ | |
308 | ||
309 | static int | |
310 | via_waitready_codec(struct via_info *via) | |
311 | { | |
312 | int i; | |
313 | ||
314 | /* poll until codec not busy */ | |
315 | for (i = 0; i < 1000; i++) { | |
316 | if ((via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_BUSY) == 0) | |
2a1ad637 | 317 | return (0); |
984263bc MD |
318 | DELAY(1); |
319 | } | |
2a1ad637 FT |
320 | device_printf(via->dev, "%s: codec busy\n", __func__); |
321 | return (1); | |
984263bc MD |
322 | } |
323 | ||
324 | static int | |
325 | via_waitvalid_codec(struct via_info *via) | |
326 | { | |
327 | int i; | |
328 | ||
329 | /* poll until codec valid */ | |
330 | for (i = 0; i < 1000; i++) { | |
331 | if (via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_CODEC00_VALID) | |
2a1ad637 | 332 | return (0); |
984263bc MD |
333 | DELAY(1); |
334 | } | |
2a1ad637 FT |
335 | device_printf(via->dev, "%s: codec invalid\n", __func__); |
336 | return (1); | |
984263bc MD |
337 | } |
338 | ||
339 | static int | |
2a1ad637 | 340 | via_write_codec(kobj_t obj, void *addr, int reg, uint32_t val) |
984263bc MD |
341 | { |
342 | struct via_info *via = addr; | |
343 | ||
2a1ad637 FT |
344 | if (via_waitready_codec(via)) |
345 | return (-1); | |
984263bc | 346 | |
2a1ad637 | 347 | via_wr(via, VIA_AC97_CONTROL, |
984263bc MD |
348 | VIA_AC97_CODEC00_VALID | VIA_AC97_INDEX(reg) | |
349 | VIA_AC97_DATA(val), 4); | |
350 | ||
2a1ad637 | 351 | return (0); |
984263bc MD |
352 | } |
353 | ||
354 | static int | |
355 | via_read_codec(kobj_t obj, void *addr, int reg) | |
356 | { | |
357 | struct via_info *via = addr; | |
358 | ||
359 | if (via_waitready_codec(via)) | |
2a1ad637 | 360 | return (-1); |
984263bc | 361 | |
2a1ad637 FT |
362 | via_wr(via, VIA_AC97_CONTROL, VIA_AC97_CODEC00_VALID | |
363 | VIA_AC97_READ | VIA_AC97_INDEX(reg), 4); | |
984263bc MD |
364 | |
365 | if (via_waitready_codec(via)) | |
2a1ad637 | 366 | return (-1); |
984263bc MD |
367 | |
368 | if (via_waitvalid_codec(via)) | |
2a1ad637 | 369 | return (-1); |
984263bc | 370 | |
2a1ad637 | 371 | return (via_rd(via, VIA_AC97_CONTROL, 2)); |
984263bc MD |
372 | } |
373 | ||
374 | static kobj_method_t via_ac97_methods[] = { | |
2a1ad637 FT |
375 | KOBJMETHOD(ac97_read, via_read_codec), |
376 | KOBJMETHOD(ac97_write, via_write_codec), | |
7774cda2 | 377 | KOBJMETHOD_END |
984263bc MD |
378 | }; |
379 | AC97_DECLARE(via_ac97); | |
380 | ||
381 | /* -------------------------------------------------------------------- */ | |
382 | ||
383 | static int | |
384 | via_buildsgdt(struct via_chinfo *ch) | |
385 | { | |
2a1ad637 FT |
386 | uint32_t phys_addr, flag; |
387 | int i; | |
984263bc | 388 | |
558a398b | 389 | phys_addr = sndbuf_getbufaddr(ch->buffer); |
984263bc | 390 | |
2a1ad637 FT |
391 | for (i = 0; i < ch->blkcnt; i++) { |
392 | flag = (i == ch->blkcnt - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG; | |
393 | ch->sgd_table[i].ptr = phys_addr + (i * ch->blksz); | |
394 | ch->sgd_table[i].flags = flag | ch->blksz; | |
984263bc MD |
395 | } |
396 | ||
2a1ad637 | 397 | return (0); |
984263bc MD |
398 | } |
399 | ||
558a398b SS |
400 | /* -------------------------------------------------------------------- */ |
401 | /* Format setting functions */ | |
402 | ||
984263bc | 403 | static int |
2a1ad637 | 404 | via8233wr_setformat(kobj_t obj, void *data, uint32_t format) |
558a398b SS |
405 | { |
406 | struct via_chinfo *ch = data; | |
407 | struct via_info *via = ch->parent; | |
408 | ||
2a1ad637 | 409 | uint32_t f = WR_FORMAT_STOP_INDEX; |
558a398b | 410 | |
2a1ad637 | 411 | if (AFMT_CHANNEL(format) > 1) |
558a398b SS |
412 | f |= WR_FORMAT_STEREO; |
413 | if (format & AFMT_S16_LE) | |
414 | f |= WR_FORMAT_16BIT; | |
415 | snd_mtxlock(via->lock); | |
416 | via_wr(via, VIA_WR0_FORMAT, f, 4); | |
417 | snd_mtxunlock(via->lock); | |
418 | ||
2a1ad637 | 419 | return (0); |
558a398b SS |
420 | } |
421 | ||
422 | static int | |
2a1ad637 | 423 | via8233dxs_setformat(kobj_t obj, void *data, uint32_t format) |
558a398b SS |
424 | { |
425 | struct via_chinfo *ch = data; | |
426 | struct via_info *via = ch->parent; | |
2a1ad637 | 427 | uint32_t r, v; |
558a398b SS |
428 | |
429 | r = ch->rbase + VIA8233_RP_DXS_RATEFMT; | |
430 | snd_mtxlock(via->lock); | |
431 | v = via_rd(via, r, 4); | |
432 | ||
433 | v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT); | |
2a1ad637 | 434 | if (AFMT_CHANNEL(format) > 1) |
558a398b | 435 | v |= VIA8233_DXS_RATEFMT_STEREO; |
2a1ad637 | 436 | if (format & AFMT_16BIT) |
558a398b SS |
437 | v |= VIA8233_DXS_RATEFMT_16BIT; |
438 | via_wr(via, r, v, 4); | |
439 | snd_mtxunlock(via->lock); | |
440 | ||
2a1ad637 | 441 | return (0); |
558a398b SS |
442 | } |
443 | ||
444 | static int | |
2a1ad637 | 445 | via8233msgd_setformat(kobj_t obj, void *data, uint32_t format) |
984263bc MD |
446 | { |
447 | struct via_chinfo *ch = data; | |
448 | struct via_info *via = ch->parent; | |
449 | ||
2a1ad637 FT |
450 | uint32_t s = 0xff000000; |
451 | uint8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT; | |
984263bc | 452 | |
2a1ad637 | 453 | if (AFMT_CHANNEL(format) > 1) { |
984263bc MD |
454 | v |= MC_SGD_CHANNELS(2); |
455 | s |= SLOT3(1) | SLOT4(2); | |
456 | } else { | |
457 | v |= MC_SGD_CHANNELS(1); | |
458 | s |= SLOT3(1) | SLOT4(1); | |
459 | } | |
460 | ||
558a398b | 461 | snd_mtxlock(via->lock); |
984263bc MD |
462 | via_wr(via, VIA_MC_SLOT_SELECT, s, 4); |
463 | via_wr(via, VIA_MC_SGD_FORMAT, v, 1); | |
558a398b | 464 | snd_mtxunlock(via->lock); |
984263bc | 465 | |
2a1ad637 | 466 | return (0); |
984263bc MD |
467 | } |
468 | ||
558a398b SS |
469 | /* -------------------------------------------------------------------- */ |
470 | /* Speed setting functions */ | |
471 | ||
2a1ad637 FT |
472 | static uint32_t |
473 | via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed) | |
984263bc MD |
474 | { |
475 | struct via_chinfo *ch = data; | |
476 | struct via_info *via = ch->parent; | |
984263bc | 477 | |
558a398b | 478 | if (via->codec_caps & AC97_EXTCAP_VRA) |
2a1ad637 | 479 | return (ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed)); |
558a398b | 480 | |
2a1ad637 | 481 | return (48000); |
558a398b SS |
482 | } |
483 | ||
2a1ad637 FT |
484 | static uint32_t |
485 | via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed) | |
558a398b SS |
486 | { |
487 | struct via_chinfo *ch = data; | |
488 | struct via_info *via = ch->parent; | |
2a1ad637 | 489 | uint32_t r, v; |
558a398b SS |
490 | |
491 | r = ch->rbase + VIA8233_RP_DXS_RATEFMT; | |
492 | snd_mtxlock(via->lock); | |
493 | v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K; | |
494 | ||
495 | /* Careful to avoid overflow (divide by 48 per vt8233c docs) */ | |
496 | ||
497 | v |= VIA8233_DXS_RATEFMT_48K * (speed / 48) / (48000 / 48); | |
498 | via_wr(via, r, v, 4); | |
499 | snd_mtxunlock(via->lock); | |
500 | ||
2a1ad637 | 501 | return (speed); |
984263bc MD |
502 | } |
503 | ||
2a1ad637 FT |
504 | static uint32_t |
505 | via8233msgd_setspeed(kobj_t obj, void *data, uint32_t speed) | |
984263bc MD |
506 | { |
507 | struct via_chinfo *ch = data; | |
508 | struct via_info *via = ch->parent; | |
509 | ||
510 | if (via->codec_caps & AC97_EXTCAP_VRA) | |
2a1ad637 | 511 | return (ac97_setrate(via->codec, AC97_REGEXT_FDACRATE, speed)); |
984263bc | 512 | |
2a1ad637 | 513 | return (48000); |
984263bc MD |
514 | } |
515 | ||
558a398b SS |
516 | /* -------------------------------------------------------------------- */ |
517 | /* Format probing functions */ | |
518 | ||
519 | static struct pcmchan_caps * | |
520 | via8233wr_getcaps(kobj_t obj, void *data) | |
984263bc MD |
521 | { |
522 | struct via_chinfo *ch = data; | |
523 | struct via_info *via = ch->parent; | |
524 | ||
558a398b SS |
525 | /* Controlled by ac97 registers */ |
526 | if (via->codec_caps & AC97_EXTCAP_VRA) | |
2a1ad637 FT |
527 | return (&via_vracaps); |
528 | return (&via_caps); | |
984263bc MD |
529 | } |
530 | ||
558a398b SS |
531 | static struct pcmchan_caps * |
532 | via8233dxs_getcaps(kobj_t obj, void *data) | |
533 | { | |
534 | struct via_chinfo *ch = data; | |
535 | struct via_info *via = ch->parent; | |
536 | ||
537 | /* | |
538 | * Controlled by onboard registers | |
539 | * | |
540 | * Apparently, few boards can do DXS sample rate | |
541 | * conversion. | |
542 | */ | |
543 | if (via->dxs_src) | |
2a1ad637 FT |
544 | return (&via_vracaps); |
545 | return (&via_caps); | |
558a398b SS |
546 | } |
547 | ||
548 | static struct pcmchan_caps * | |
549 | via8233msgd_getcaps(kobj_t obj, void *data) | |
550 | { | |
551 | struct via_chinfo *ch = data; | |
552 | struct via_info *via = ch->parent; | |
553 | ||
554 | /* Controlled by ac97 registers */ | |
555 | if (via->codec_caps & AC97_EXTCAP_VRA) | |
2a1ad637 FT |
556 | return (&via_vracaps); |
557 | return (&via_caps); | |
558a398b SS |
558 | } |
559 | ||
560 | /* -------------------------------------------------------------------- */ | |
561 | /* Common functions */ | |
562 | ||
984263bc | 563 | static int |
2a1ad637 FT |
564 | via8233chan_setfragments(kobj_t obj, void *data, |
565 | uint32_t blksz, uint32_t blkcnt) | |
984263bc MD |
566 | { |
567 | struct via_chinfo *ch = data; | |
2a1ad637 FT |
568 | struct via_info *via = ch->parent; |
569 | ||
570 | blksz &= VIA_BLK_ALIGN; | |
571 | ||
572 | if (blksz > (sndbuf_getmaxsize(ch->buffer) / VIA_SEGS_MIN)) | |
573 | blksz = sndbuf_getmaxsize(ch->buffer) / VIA_SEGS_MIN; | |
574 | if (blksz < VIA_BLK_MIN) | |
575 | blksz = VIA_BLK_MIN; | |
576 | if (blkcnt > VIA_SEGS_MAX) | |
577 | blkcnt = VIA_SEGS_MAX; | |
578 | if (blkcnt < VIA_SEGS_MIN) | |
579 | blkcnt = VIA_SEGS_MIN; | |
580 | ||
581 | while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) { | |
582 | if ((blkcnt >> 1) >= VIA_SEGS_MIN) | |
583 | blkcnt >>= 1; | |
584 | else if ((blksz >> 1) >= VIA_BLK_MIN) | |
585 | blksz >>= 1; | |
586 | else | |
587 | break; | |
588 | } | |
589 | ||
590 | if ((sndbuf_getblksz(ch->buffer) != blksz || | |
591 | sndbuf_getblkcnt(ch->buffer) != blkcnt) && | |
592 | sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) | |
593 | device_printf(via->dev, "%s: failed blksz=%u blkcnt=%u\n", | |
594 | __func__, blksz, blkcnt); | |
984263bc | 595 | |
984263bc | 596 | ch->blksz = sndbuf_getblksz(ch->buffer); |
2a1ad637 FT |
597 | ch->blkcnt = sndbuf_getblkcnt(ch->buffer); |
598 | ||
599 | return (0); | |
984263bc MD |
600 | } |
601 | ||
2a1ad637 FT |
602 | static uint32_t |
603 | via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) | |
604 | { | |
605 | struct via_chinfo *ch = data; | |
606 | struct via_info *via = ch->parent; | |
607 | ||
608 | via8233chan_setfragments(obj, data, blksz, via->blkcnt); | |
609 | ||
610 | return (ch->blksz); | |
611 | } | |
612 | ||
613 | static uint32_t | |
984263bc MD |
614 | via8233chan_getptr(kobj_t obj, void *data) |
615 | { | |
616 | struct via_chinfo *ch = data; | |
617 | struct via_info *via = ch->parent; | |
2a1ad637 | 618 | uint32_t v, index, count, ptr; |
558a398b SS |
619 | |
620 | snd_mtxlock(via->lock); | |
2a1ad637 FT |
621 | if (via->polling != 0) { |
622 | ptr = ch->ptr; | |
623 | snd_mtxunlock(via->lock); | |
624 | } else { | |
625 | v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4); | |
626 | snd_mtxunlock(via->lock); | |
627 | index = v >> 24; /* Last completed buffer */ | |
628 | count = v & 0x00ffffff; /* Bytes remaining */ | |
629 | ptr = (index + 1) * ch->blksz - count; | |
630 | ptr %= ch->blkcnt * ch->blksz; /* Wrap to available space */ | |
631 | } | |
984263bc | 632 | |
2a1ad637 | 633 | return (ptr); |
984263bc MD |
634 | } |
635 | ||
984263bc MD |
636 | static void |
637 | via8233chan_reset(struct via_info *via, struct via_chinfo *ch) | |
638 | { | |
639 | via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1); | |
640 | via_wr(via, ch->rbase + VIA_RP_CONTROL, 0x00, 1); | |
2a1ad637 FT |
641 | via_wr(via, ch->rbase + VIA_RP_STATUS, |
642 | SGD_STATUS_EOL | SGD_STATUS_FLAG, 1); | |
984263bc MD |
643 | } |
644 | ||
558a398b SS |
645 | /* -------------------------------------------------------------------- */ |
646 | /* Channel initialization functions */ | |
647 | ||
648 | static void | |
649 | via8233chan_sgdinit(struct via_info *via, struct via_chinfo *ch, int chnum) | |
650 | { | |
2a1ad637 FT |
651 | ch->sgd_table = &via->sgd_table[chnum * VIA_SEGS_MAX]; |
652 | ch->sgd_addr = via->sgd_addr + chnum * VIA_SEGS_MAX * | |
653 | sizeof(struct via_dma_op); | |
558a398b SS |
654 | } |
655 | ||
984263bc | 656 | static void* |
558a398b | 657 | via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, |
2a1ad637 | 658 | struct pcm_channel *c, int dir) |
984263bc MD |
659 | { |
660 | struct via_info *via = devinfo; | |
2a1ad637 FT |
661 | struct via_chinfo *ch; |
662 | int num; | |
984263bc | 663 | |
2a1ad637 FT |
664 | snd_mtxlock(via->lock); |
665 | num = via->rec_num++; | |
666 | ch = &via->rch[num]; | |
984263bc MD |
667 | ch->parent = via; |
668 | ch->channel = c; | |
669 | ch->buffer = b; | |
670 | ch->dir = dir; | |
2a1ad637 FT |
671 | ch->blkcnt = via->blkcnt; |
672 | ch->rbase = VIA_WR_BASE(num); | |
558a398b SS |
673 | via_wr(via, ch->rbase + VIA_WR_RP_SGD_FORMAT, WR_FIFO_ENABLE, 1); |
674 | snd_mtxunlock(via->lock); | |
675 | ||
2a1ad637 FT |
676 | if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0) |
677 | return (NULL); | |
558a398b SS |
678 | |
679 | snd_mtxlock(via->lock); | |
2a1ad637 | 680 | via8233chan_sgdinit(via, ch, num); |
558a398b SS |
681 | via8233chan_reset(via, ch); |
682 | snd_mtxunlock(via->lock); | |
683 | ||
2a1ad637 | 684 | return (ch); |
558a398b SS |
685 | } |
686 | ||
687 | static void* | |
688 | via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, | |
2a1ad637 | 689 | struct pcm_channel *c, int dir) |
558a398b SS |
690 | { |
691 | struct via_info *via = devinfo; | |
2a1ad637 FT |
692 | struct via_chinfo *ch; |
693 | int num; | |
558a398b | 694 | |
2a1ad637 FT |
695 | snd_mtxlock(via->lock); |
696 | num = via->play_num++; | |
697 | ch = &via->pch[num]; | |
558a398b SS |
698 | ch->parent = via; |
699 | ch->channel = c; | |
700 | ch->buffer = b; | |
701 | ch->dir = dir; | |
2a1ad637 | 702 | ch->blkcnt = via->blkcnt; |
558a398b SS |
703 | |
704 | /* | |
705 | * All cards apparently support DXS3, but not other DXS | |
706 | * channels. We therefore want to align first DXS channel to | |
707 | * DXS3. | |
708 | */ | |
558a398b SS |
709 | ch->rbase = VIA_DXS_BASE(NDXSCHANS - 1 - via->n_dxs_registered); |
710 | via->n_dxs_registered++; | |
711 | snd_mtxunlock(via->lock); | |
712 | ||
2a1ad637 FT |
713 | if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0) |
714 | return (NULL); | |
558a398b SS |
715 | |
716 | snd_mtxlock(via->lock); | |
2a1ad637 | 717 | via8233chan_sgdinit(via, ch, NWRCHANS + num); |
558a398b SS |
718 | via8233chan_reset(via, ch); |
719 | snd_mtxunlock(via->lock); | |
720 | ||
2a1ad637 | 721 | return (ch); |
558a398b | 722 | } |
984263bc | 723 | |
558a398b SS |
724 | static void* |
725 | via8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, | |
2a1ad637 | 726 | struct pcm_channel *c, int dir) |
558a398b SS |
727 | { |
728 | struct via_info *via = devinfo; | |
2a1ad637 FT |
729 | struct via_chinfo *ch; |
730 | int num; | |
558a398b | 731 | |
2a1ad637 FT |
732 | snd_mtxlock(via->lock); |
733 | num = via->play_num++; | |
734 | ch = &via->pch[num]; | |
558a398b SS |
735 | ch->parent = via; |
736 | ch->channel = c; | |
737 | ch->buffer = b; | |
738 | ch->dir = dir; | |
739 | ch->rbase = VIA_MC_SGD_STATUS; | |
2a1ad637 FT |
740 | ch->blkcnt = via->blkcnt; |
741 | snd_mtxunlock(via->lock); | |
558a398b | 742 | |
2a1ad637 FT |
743 | if (sndbuf_alloc(ch->buffer, via->parent_dmat, 0, via->bufsz) != 0) |
744 | return (NULL); | |
984263bc | 745 | |
558a398b | 746 | snd_mtxlock(via->lock); |
2a1ad637 | 747 | via8233chan_sgdinit(via, ch, NWRCHANS + num); |
984263bc | 748 | via8233chan_reset(via, ch); |
558a398b | 749 | snd_mtxunlock(via->lock); |
984263bc | 750 | |
2a1ad637 | 751 | return (ch); |
984263bc MD |
752 | } |
753 | ||
558a398b SS |
754 | static void |
755 | via8233chan_mute(struct via_info *via, struct via_chinfo *ch, int muted) | |
756 | { | |
757 | if (BASE_IS_VIA_DXS_REG(ch->rbase)) { | |
758 | int r; | |
759 | muted = (muted) ? VIA8233_DXS_MUTE : 0; | |
760 | via_wr(via, ch->rbase + VIA8233_RP_DXS_LVOL, muted, 1); | |
761 | via_wr(via, ch->rbase + VIA8233_RP_DXS_RVOL, muted, 1); | |
2a1ad637 FT |
762 | r = via_rd(via, ch->rbase + VIA8233_RP_DXS_LVOL, 1) & |
763 | VIA8233_DXS_MUTE; | |
764 | if (r != muted) | |
765 | device_printf(via->dev, | |
766 | "%s: failed to set dxs volume " | |
767 | "(dxs base 0x%02x).\n", __func__, ch->rbase); | |
768 | } | |
769 | } | |
770 | ||
771 | static __inline int | |
772 | via_poll_channel(struct via_chinfo *ch) | |
773 | { | |
774 | struct via_info *via; | |
775 | uint32_t sz, delta; | |
776 | uint32_t v, index, count; | |
777 | int ptr; | |
778 | ||
779 | if (ch == NULL || ch->channel == NULL || ch->active == 0) | |
780 | return (0); | |
781 | ||
782 | via = ch->parent; | |
783 | sz = ch->blksz * ch->blkcnt; | |
784 | v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4); | |
785 | index = v >> 24; | |
786 | count = v & 0x00ffffff; | |
787 | ptr = ((index + 1) * ch->blksz) - count; | |
788 | ptr %= sz; | |
789 | ptr &= ~(ch->blksz - 1); | |
790 | ch->ptr = ptr; | |
791 | delta = (sz + ptr - ch->prevptr) % sz; | |
792 | ||
793 | if (delta < ch->blksz) | |
794 | return (0); | |
795 | ||
796 | ch->prevptr = ptr; | |
797 | ||
798 | return (1); | |
799 | } | |
800 | ||
801 | static void | |
802 | via_poll_callback(void *arg) | |
803 | { | |
804 | struct via_info *via = arg; | |
805 | uint32_t ptrigger = 0, rtrigger = 0; | |
806 | int i; | |
807 | ||
808 | if (via == NULL) | |
809 | return; | |
810 | ||
811 | snd_mtxlock(via->lock); | |
812 | if (via->polling == 0 || via_chan_active(via) == 0) { | |
813 | snd_mtxunlock(via->lock); | |
814 | return; | |
815 | } | |
816 | ||
817 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) | |
818 | ptrigger |= (via_poll_channel(&via->pch[i]) != 0) ? | |
819 | (1 << i) : 0; | |
820 | ||
821 | for (i = 0; i < NWRCHANS; i++) | |
822 | rtrigger |= (via_poll_channel(&via->rch[i]) != 0) ? | |
823 | (1 << i) : 0; | |
824 | ||
825 | /* XXX */ | |
826 | callout_reset(&via->poll_timer, 1/*via->poll_ticks*/, | |
827 | via_poll_callback, via); | |
828 | ||
829 | snd_mtxunlock(via->lock); | |
830 | ||
831 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { | |
832 | if (ptrigger & (1 << i)) | |
833 | chn_intr(via->pch[i].channel); | |
834 | } | |
835 | for (i = 0; i < NWRCHANS; i++) { | |
836 | if (rtrigger & (1 << i)) | |
837 | chn_intr(via->rch[i].channel); | |
558a398b SS |
838 | } |
839 | } | |
840 | ||
2a1ad637 FT |
841 | static int |
842 | via_poll_ticks(struct via_info *via) | |
843 | { | |
844 | struct via_chinfo *ch; | |
845 | int i; | |
846 | int ret = hz; | |
847 | int pollticks; | |
848 | ||
849 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { | |
850 | ch = &via->pch[i]; | |
851 | if (ch->channel == NULL || ch->active == 0) | |
852 | continue; | |
853 | pollticks = ((uint64_t)hz * ch->blksz) / | |
854 | ((uint64_t)sndbuf_getalign(ch->buffer) * | |
855 | sndbuf_getspd(ch->buffer)); | |
856 | pollticks >>= 2; | |
857 | if (pollticks > hz) | |
858 | pollticks = hz; | |
859 | if (pollticks < 1) | |
860 | pollticks = 1; | |
861 | if (pollticks < ret) | |
862 | ret = pollticks; | |
863 | } | |
864 | ||
865 | for (i = 0; i < NWRCHANS; i++) { | |
866 | ch = &via->rch[i]; | |
867 | if (ch->channel == NULL || ch->active == 0) | |
868 | continue; | |
869 | pollticks = ((uint64_t)hz * ch->blksz) / | |
870 | ((uint64_t)sndbuf_getalign(ch->buffer) * | |
871 | sndbuf_getspd(ch->buffer)); | |
872 | pollticks >>= 2; | |
873 | if (pollticks > hz) | |
874 | pollticks = hz; | |
875 | if (pollticks < 1) | |
876 | pollticks = 1; | |
877 | if (pollticks < ret) | |
878 | ret = pollticks; | |
879 | } | |
880 | ||
881 | return (ret); | |
882 | } | |
883 | ||
984263bc MD |
884 | static int |
885 | via8233chan_trigger(kobj_t obj, void* data, int go) | |
886 | { | |
887 | struct via_chinfo *ch = data; | |
888 | struct via_info *via = ch->parent; | |
2a1ad637 FT |
889 | int pollticks; |
890 | ||
891 | if (!PCMTRIG_COMMON(go)) | |
892 | return (0); | |
984263bc | 893 | |
558a398b | 894 | snd_mtxlock(via->lock); |
984263bc MD |
895 | switch(go) { |
896 | case PCMTRIG_START: | |
897 | via_buildsgdt(ch); | |
558a398b SS |
898 | via8233chan_mute(via, ch, 0); |
899 | via_wr(via, ch->rbase + VIA_RP_TABLE_PTR, ch->sgd_addr, 4); | |
2a1ad637 FT |
900 | if (via->polling != 0) { |
901 | ch->ptr = 0; | |
902 | ch->prevptr = 0; | |
903 | pollticks = ((uint64_t)hz * ch->blksz) / | |
904 | ((uint64_t)sndbuf_getalign(ch->buffer) * | |
905 | sndbuf_getspd(ch->buffer)); | |
906 | pollticks >>= 2; | |
907 | if (pollticks > hz) | |
908 | pollticks = hz; | |
909 | if (pollticks < 1) | |
910 | pollticks = 1; | |
911 | if (via_chan_active(via) == 0 || | |
912 | pollticks < via->poll_ticks) { | |
913 | if (bootverbose) { | |
914 | if (via_chan_active(via) == 0) | |
67931cc4 | 915 | kprintf("%s: pollticks=%d\n", |
2a1ad637 FT |
916 | __func__, pollticks); |
917 | else | |
67931cc4 | 918 | kprintf("%s: " |
2a1ad637 FT |
919 | "pollticks %d -> %d\n", |
920 | __func__, via->poll_ticks, | |
921 | pollticks); | |
922 | } | |
923 | via->poll_ticks = pollticks; | |
924 | callout_reset(&via->poll_timer, 1, | |
925 | via_poll_callback, via); | |
926 | } | |
927 | } | |
984263bc | 928 | via_wr(via, ch->rbase + VIA_RP_CONTROL, |
2a1ad637 FT |
929 | SGD_CONTROL_START | SGD_CONTROL_AUTOSTART | |
930 | ((via->polling == 0) ? | |
931 | (SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG) : 0), 1); | |
932 | ch->active = 1; | |
984263bc MD |
933 | break; |
934 | case PCMTRIG_STOP: | |
935 | case PCMTRIG_ABORT: | |
936 | via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1); | |
558a398b | 937 | via8233chan_mute(via, ch, 1); |
984263bc | 938 | via8233chan_reset(via, ch); |
2a1ad637 FT |
939 | ch->active = 0; |
940 | if (via->polling != 0) { | |
941 | if (via_chan_active(via) == 0) { | |
942 | callout_stop(&via->poll_timer); | |
943 | via->poll_ticks = 1; | |
944 | } else { | |
945 | pollticks = via_poll_ticks(via); | |
946 | if (pollticks > via->poll_ticks) { | |
947 | if (bootverbose) | |
67931cc4 | 948 | kprintf("%s: pollticks " |
2a1ad637 FT |
949 | "%d -> %d\n", |
950 | __func__, via->poll_ticks, | |
951 | pollticks); | |
952 | via->poll_ticks = pollticks; | |
953 | callout_reset(&via->poll_timer, | |
954 | 1, via_poll_callback, | |
955 | via); | |
956 | } | |
957 | } | |
958 | } | |
959 | break; | |
960 | default: | |
984263bc MD |
961 | break; |
962 | } | |
558a398b | 963 | snd_mtxunlock(via->lock); |
2a1ad637 | 964 | return (0); |
984263bc MD |
965 | } |
966 | ||
558a398b | 967 | static kobj_method_t via8233wr_methods[] = { |
2a1ad637 FT |
968 | KOBJMETHOD(channel_init, via8233wr_init), |
969 | KOBJMETHOD(channel_setformat, via8233wr_setformat), | |
970 | KOBJMETHOD(channel_setspeed, via8233wr_setspeed), | |
971 | KOBJMETHOD(channel_getcaps, via8233wr_getcaps), | |
972 | KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), | |
973 | KOBJMETHOD(channel_setfragments, via8233chan_setfragments), | |
974 | KOBJMETHOD(channel_trigger, via8233chan_trigger), | |
975 | KOBJMETHOD(channel_getptr, via8233chan_getptr), | |
7774cda2 | 976 | KOBJMETHOD_END |
558a398b SS |
977 | }; |
978 | CHANNEL_DECLARE(via8233wr); | |
979 | ||
980 | static kobj_method_t via8233dxs_methods[] = { | |
2a1ad637 FT |
981 | KOBJMETHOD(channel_init, via8233dxs_init), |
982 | KOBJMETHOD(channel_setformat, via8233dxs_setformat), | |
983 | KOBJMETHOD(channel_setspeed, via8233dxs_setspeed), | |
984 | KOBJMETHOD(channel_getcaps, via8233dxs_getcaps), | |
985 | KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), | |
986 | KOBJMETHOD(channel_setfragments, via8233chan_setfragments), | |
987 | KOBJMETHOD(channel_trigger, via8233chan_trigger), | |
988 | KOBJMETHOD(channel_getptr, via8233chan_getptr), | |
7774cda2 | 989 | KOBJMETHOD_END |
984263bc | 990 | }; |
558a398b | 991 | CHANNEL_DECLARE(via8233dxs); |
984263bc | 992 | |
558a398b | 993 | static kobj_method_t via8233msgd_methods[] = { |
2a1ad637 FT |
994 | KOBJMETHOD(channel_init, via8233msgd_init), |
995 | KOBJMETHOD(channel_setformat, via8233msgd_setformat), | |
996 | KOBJMETHOD(channel_setspeed, via8233msgd_setspeed), | |
997 | KOBJMETHOD(channel_getcaps, via8233msgd_getcaps), | |
998 | KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), | |
999 | KOBJMETHOD(channel_setfragments, via8233chan_setfragments), | |
1000 | KOBJMETHOD(channel_trigger, via8233chan_trigger), | |
1001 | KOBJMETHOD(channel_getptr, via8233chan_getptr), | |
7774cda2 | 1002 | KOBJMETHOD_END |
984263bc | 1003 | }; |
558a398b | 1004 | CHANNEL_DECLARE(via8233msgd); |
984263bc MD |
1005 | |
1006 | /* -------------------------------------------------------------------- */ | |
1007 | ||
1008 | static void | |
1009 | via_intr(void *p) | |
1010 | { | |
1011 | struct via_info *via = p; | |
2a1ad637 | 1012 | uint32_t ptrigger = 0, rtrigger = 0; |
558a398b SS |
1013 | int i, reg, stat; |
1014 | ||
558a398b | 1015 | snd_mtxlock(via->lock); |
2a1ad637 FT |
1016 | if (via->polling != 0) { |
1017 | snd_mtxunlock(via->lock); | |
1018 | return; | |
1019 | } | |
1020 | /* Poll playback channels */ | |
558a398b | 1021 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { |
2a1ad637 | 1022 | if (via->pch[i].channel == NULL || via->pch[i].active == 0) |
558a398b SS |
1023 | continue; |
1024 | reg = via->pch[i].rbase + VIA_RP_STATUS; | |
1025 | stat = via_rd(via, reg, 1); | |
1026 | if (stat & SGD_STATUS_INTR) { | |
1027 | if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) || | |
2a1ad637 FT |
1028 | !(stat & SGD_STATUS_ACTIVE))) |
1029 | via_wr(via, via->pch[i].rbase + VIA_RP_CONTROL, | |
1030 | SGD_CONTROL_START | SGD_CONTROL_AUTOSTART | | |
1031 | SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1); | |
558a398b | 1032 | via_wr(via, reg, stat, 1); |
2a1ad637 | 1033 | ptrigger |= 1 << i; |
558a398b | 1034 | } |
984263bc | 1035 | } |
558a398b SS |
1036 | /* Poll record channels */ |
1037 | for (i = 0; i < NWRCHANS; i++) { | |
2a1ad637 | 1038 | if (via->rch[i].channel == NULL || via->rch[i].active == 0) |
558a398b SS |
1039 | continue; |
1040 | reg = via->rch[i].rbase + VIA_RP_STATUS; | |
1041 | stat = via_rd(via, reg, 1); | |
1042 | if (stat & SGD_STATUS_INTR) { | |
1043 | if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) || | |
2a1ad637 FT |
1044 | !(stat & SGD_STATUS_ACTIVE))) |
1045 | via_wr(via, via->rch[i].rbase + VIA_RP_CONTROL, | |
1046 | SGD_CONTROL_START | SGD_CONTROL_AUTOSTART | | |
1047 | SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1); | |
558a398b | 1048 | via_wr(via, reg, stat, 1); |
2a1ad637 | 1049 | rtrigger |= 1 << i; |
558a398b | 1050 | } |
984263bc | 1051 | } |
558a398b | 1052 | snd_mtxunlock(via->lock); |
2a1ad637 FT |
1053 | |
1054 | for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { | |
1055 | if (ptrigger & (1 << i)) | |
1056 | chn_intr(via->pch[i].channel); | |
1057 | } | |
1058 | for (i = 0; i < NWRCHANS; i++) { | |
1059 | if (rtrigger & (1 << i)) | |
1060 | chn_intr(via->rch[i].channel); | |
1061 | } | |
984263bc MD |
1062 | } |
1063 | ||
1064 | /* | |
1065 | * Probe and attach the card | |
1066 | */ | |
1067 | static int | |
1068 | via_probe(device_t dev) | |
1069 | { | |
1070 | switch(pci_get_devid(dev)) { | |
1071 | case VIA8233_PCI_ID: | |
1072 | switch(pci_get_revid(dev)) { | |
2a1ad637 | 1073 | case VIA8233_REV_ID_8233PRE: |
984263bc | 1074 | device_set_desc(dev, "VIA VT8233 (pre)"); |
2a1ad637 | 1075 | return (BUS_PROBE_DEFAULT); |
558a398b | 1076 | case VIA8233_REV_ID_8233C: |
984263bc | 1077 | device_set_desc(dev, "VIA VT8233C"); |
2a1ad637 | 1078 | return (BUS_PROBE_DEFAULT); |
558a398b | 1079 | case VIA8233_REV_ID_8233: |
984263bc | 1080 | device_set_desc(dev, "VIA VT8233"); |
2a1ad637 | 1081 | return (BUS_PROBE_DEFAULT); |
558a398b | 1082 | case VIA8233_REV_ID_8233A: |
984263bc | 1083 | device_set_desc(dev, "VIA VT8233A"); |
2a1ad637 | 1084 | return (BUS_PROBE_DEFAULT); |
558a398b | 1085 | case VIA8233_REV_ID_8235: |
984263bc | 1086 | device_set_desc(dev, "VIA VT8235"); |
2a1ad637 | 1087 | return (BUS_PROBE_DEFAULT); |
558a398b SS |
1088 | case VIA8233_REV_ID_8237: |
1089 | device_set_desc(dev, "VIA VT8237"); | |
2a1ad637 | 1090 | return (BUS_PROBE_DEFAULT); |
558a398b SS |
1091 | case VIA8233_REV_ID_8251: |
1092 | device_set_desc(dev, "VIA VT8251"); | |
2a1ad637 | 1093 | return (BUS_PROBE_DEFAULT); |
984263bc MD |
1094 | default: |
1095 | device_set_desc(dev, "VIA VT8233X"); /* Unknown */ | |
2a1ad637 FT |
1096 | return (BUS_PROBE_DEFAULT); |
1097 | } | |
984263bc | 1098 | } |
2a1ad637 | 1099 | return (ENXIO); |
984263bc MD |
1100 | } |
1101 | ||
1102 | static void | |
1103 | dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) | |
1104 | { | |
558a398b SS |
1105 | struct via_info *via = (struct via_info *)p; |
1106 | via->sgd_addr = bds->ds_addr; | |
984263bc MD |
1107 | } |
1108 | ||
1109 | static int | |
1110 | via_chip_init(device_t dev) | |
1111 | { | |
2a1ad637 | 1112 | uint32_t data, cnt; |
558a398b SS |
1113 | |
1114 | /* Wake up and reset AC97 if necessary */ | |
1115 | data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1); | |
1116 | ||
1117 | if ((data & VIA_PCI_ACLINK_C00_READY) == 0) { | |
1118 | /* Cold reset per ac97r2.3 spec (page 95) */ | |
1119 | /* Assert low */ | |
2a1ad637 FT |
1120 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1121 | VIA_PCI_ACLINK_EN, 1); | |
558a398b | 1122 | /* Wait T_rst_low */ |
2a1ad637 | 1123 | DELAY(100); |
558a398b | 1124 | /* Assert high */ |
2a1ad637 FT |
1125 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1126 | VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST, 1); | |
558a398b SS |
1127 | /* Wait T_rst2clk */ |
1128 | DELAY(5); | |
1129 | /* Assert low */ | |
2a1ad637 FT |
1130 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1131 | VIA_PCI_ACLINK_EN, 1); | |
558a398b SS |
1132 | } else { |
1133 | /* Warm reset */ | |
1134 | /* Force no sync */ | |
2a1ad637 FT |
1135 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1136 | VIA_PCI_ACLINK_EN, 1); | |
558a398b SS |
1137 | DELAY(100); |
1138 | /* Sync */ | |
2a1ad637 FT |
1139 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1140 | VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_SYNC, 1); | |
558a398b SS |
1141 | /* Wait T_sync_high */ |
1142 | DELAY(5); | |
1143 | /* Force no sync */ | |
2a1ad637 FT |
1144 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, |
1145 | VIA_PCI_ACLINK_EN, 1); | |
558a398b SS |
1146 | /* Wait T_sync2clk */ |
1147 | DELAY(5); | |
1148 | } | |
984263bc | 1149 | |
558a398b SS |
1150 | /* Power everything up */ |
1151 | pci_write_config(dev, VIA_PCI_ACLINK_CTRL, VIA_PCI_ACLINK_DESIRED, 1); | |
984263bc | 1152 | |
558a398b SS |
1153 | /* Wait for codec to become ready (largest reported delay 310ms) */ |
1154 | for (cnt = 0; cnt < 2000; cnt++) { | |
1155 | data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1); | |
2a1ad637 FT |
1156 | if (data & VIA_PCI_ACLINK_C00_READY) |
1157 | return (0); | |
558a398b | 1158 | DELAY(5000); |
984263bc | 1159 | } |
558a398b | 1160 | device_printf(dev, "primary codec not ready (cnt = 0x%02x)\n", cnt); |
2a1ad637 | 1161 | return (ENXIO); |
984263bc MD |
1162 | } |
1163 | ||
1164 | static int | |
1165 | via_attach(device_t dev) | |
1166 | { | |
2a1ad637 | 1167 | struct via_info *via = 0; |
984263bc | 1168 | char status[SND_STATUSLEN]; |
558a398b | 1169 | int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum; |
2a1ad637 | 1170 | int nsegs; |
558a398b | 1171 | uint32_t revid; |
984263bc | 1172 | |
67931cc4 | 1173 | via = kmalloc(sizeof *via, M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 FT |
1174 | via->lock = snd_mtxcreate(device_get_nameunit(dev), |
1175 | "snd_via8233 softc"); | |
1176 | via->dev = dev; | |
1177 | ||
67931cc4 | 1178 | callout_init_mp(&via->poll_timer); |
2a1ad637 FT |
1179 | via->poll_ticks = 1; |
1180 | ||
1181 | if (resource_int_value(device_get_name(dev), | |
1182 | device_get_unit(dev), "polling", &i) == 0 && i != 0) | |
1183 | via->polling = 1; | |
1184 | else | |
1185 | via->polling = 0; | |
984263bc | 1186 | |
984263bc MD |
1187 | pci_set_powerstate(dev, PCI_POWERSTATE_D0); |
1188 | pci_enable_busmaster(dev); | |
558a398b SS |
1189 | |
1190 | via->regid = PCIR_BAR(0); | |
1191 | via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid, | |
1192 | RF_ACTIVE); | |
984263bc MD |
1193 | if (!via->reg) { |
1194 | device_printf(dev, "cannot allocate bus resource."); | |
1195 | goto bad; | |
1196 | } | |
1197 | via->st = rman_get_bustag(via->reg); | |
1198 | via->sh = rman_get_bushandle(via->reg); | |
1199 | ||
984263bc | 1200 | via->irqid = 0; |
558a398b | 1201 | via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid, |
2a1ad637 FT |
1202 | RF_ACTIVE | RF_SHAREABLE); |
1203 | if (!via->irq || | |
1204 | snd_setup_intr(dev, via->irq, INTR_MPSAFE, | |
1205 | via_intr, via, &via->ih)) { | |
984263bc MD |
1206 | device_printf(dev, "unable to map interrupt\n"); |
1207 | goto bad; | |
1208 | } | |
1209 | ||
2a1ad637 FT |
1210 | via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536); |
1211 | if (resource_int_value(device_get_name(dev), | |
1212 | device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { | |
1213 | i &= VIA_BLK_ALIGN; | |
1214 | if (i < VIA_BLK_MIN) | |
1215 | i = VIA_BLK_MIN; | |
1216 | via->blkcnt = via->bufsz / i; | |
1217 | i = 0; | |
1218 | while (via->blkcnt >> i) | |
1219 | i++; | |
1220 | via->blkcnt = 1 << (i - 1); | |
1221 | if (via->blkcnt < VIA_SEGS_MIN) | |
1222 | via->blkcnt = VIA_SEGS_MIN; | |
1223 | else if (via->blkcnt > VIA_SEGS_MAX) | |
1224 | via->blkcnt = VIA_SEGS_MAX; | |
1225 | ||
1226 | } else | |
1227 | via->blkcnt = VIA_SEGS_DEFAULT; | |
984263bc | 1228 | |
558a398b SS |
1229 | revid = pci_get_revid(dev); |
1230 | ||
1231 | /* | |
1232 | * VIA8251 lost its interrupt after DMA EOL, and need | |
1233 | * a gentle spank on its face within interrupt handler. | |
1234 | */ | |
1235 | if (revid == VIA8233_REV_ID_8251) | |
1236 | via->dma_eol_wake = 1; | |
1237 | else | |
1238 | via->dma_eol_wake = 0; | |
984263bc | 1239 | |
558a398b SS |
1240 | /* |
1241 | * Decide whether DXS had to be disabled or not | |
1242 | */ | |
1243 | if (revid == VIA8233_REV_ID_8233A) { | |
1244 | /* | |
1245 | * DXS channel is disabled. Reports from multiple users | |
1246 | * that it plays at half-speed. Do not see this behaviour | |
1247 | * on available 8233C or when emulating 8233A register set | |
1248 | * on 8233C (either with or without ac97 VRA). | |
1249 | */ | |
1250 | via_dxs_disabled = 1; | |
1251 | } else if (resource_int_value(device_get_name(dev), | |
2a1ad637 FT |
1252 | device_get_unit(dev), "via_dxs_disabled", |
1253 | &via_dxs_disabled) == 0) | |
558a398b SS |
1254 | via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0; |
1255 | else | |
1256 | via_dxs_disabled = 0; | |
1257 | ||
1258 | if (via_dxs_disabled) { | |
1259 | via_dxs_chnum = 0; | |
1260 | via_sgd_chnum = 1; | |
1261 | } else { | |
1262 | if (resource_int_value(device_get_name(dev), | |
2a1ad637 FT |
1263 | device_get_unit(dev), "via_dxs_channels", |
1264 | &via_dxs_chnum) != 0) | |
558a398b SS |
1265 | via_dxs_chnum = NDXSCHANS; |
1266 | if (resource_int_value(device_get_name(dev), | |
2a1ad637 FT |
1267 | device_get_unit(dev), "via_sgd_channels", |
1268 | &via_sgd_chnum) != 0) | |
558a398b SS |
1269 | via_sgd_chnum = NMSGDCHANS; |
1270 | } | |
1271 | if (via_dxs_chnum > NDXSCHANS) | |
1272 | via_dxs_chnum = NDXSCHANS; | |
1273 | else if (via_dxs_chnum < 0) | |
1274 | via_dxs_chnum = 0; | |
1275 | if (via_sgd_chnum > NMSGDCHANS) | |
1276 | via_sgd_chnum = NMSGDCHANS; | |
1277 | else if (via_sgd_chnum < 0) | |
1278 | via_sgd_chnum = 0; | |
1279 | if (via_dxs_chnum + via_sgd_chnum < 1) { | |
1280 | /* Minimalist ? */ | |
1281 | via_dxs_chnum = 1; | |
1282 | via_sgd_chnum = 0; | |
1283 | } | |
1284 | if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev), | |
2a1ad637 | 1285 | device_get_unit(dev), "via_dxs_src", &via_dxs_src) == 0) |
558a398b SS |
1286 | via->dxs_src = (via_dxs_src > 0) ? 1 : 0; |
1287 | else | |
1288 | via->dxs_src = 0; | |
2a1ad637 FT |
1289 | |
1290 | nsegs = (via_dxs_chnum + via_sgd_chnum + NWRCHANS) * VIA_SEGS_MAX; | |
1291 | ||
1292 | /* DMA tag for buffers */ | |
1293 | if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, | |
1294 | /*boundary*/0, | |
1295 | /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, | |
1296 | /*highaddr*/BUS_SPACE_MAXADDR, | |
2a1ad637 | 1297 | /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, |
4923c0ea FT |
1298 | /*flags*/0, |
1299 | &via->parent_dmat) != 0) { | |
2a1ad637 FT |
1300 | device_printf(dev, "unable to create dma tag\n"); |
1301 | goto bad; | |
1302 | } | |
1303 | ||
1304 | /* | |
1305 | * DMA tag for SGD table. The 686 uses scatter/gather DMA and | |
1306 | * requires a list in memory of work to do. We need only 16 bytes | |
1307 | * for this list, and it is wasteful to allocate 16K. | |
1308 | */ | |
1309 | if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, | |
1310 | /*boundary*/0, | |
1311 | /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, | |
1312 | /*highaddr*/BUS_SPACE_MAXADDR, | |
2a1ad637 FT |
1313 | /*maxsize*/nsegs * sizeof(struct via_dma_op), |
1314 | /*nsegments*/1, /*maxsegz*/0x3ffff, | |
4923c0ea FT |
1315 | /*flags*/0, |
1316 | &via->sgd_dmat) != 0) { | |
2a1ad637 FT |
1317 | device_printf(dev, "unable to create dma tag\n"); |
1318 | goto bad; | |
1319 | } | |
1320 | ||
1321 | if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, | |
1322 | BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1) | |
1323 | goto bad; | |
1324 | if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, | |
1325 | nsegs * sizeof(struct via_dma_op), dma_cb, via, 0)) | |
1326 | goto bad; | |
1327 | ||
1328 | if (via_chip_init(dev)) | |
1329 | goto bad; | |
1330 | ||
1331 | via->codec = AC97_CREATE(dev, via, via_ac97); | |
1332 | if (!via->codec) | |
1333 | goto bad; | |
1334 | ||
1335 | mixer_init(dev, ac97_getmixerclass(), via->codec); | |
1336 | ||
1337 | via->codec_caps = ac97_getextcaps(via->codec); | |
1338 | ||
1339 | /* Try to set VRA without generating an error, VRM not reqrd yet */ | |
1340 | if (via->codec_caps & | |
1341 | (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM | AC97_EXTCAP_DRA)) { | |
1342 | uint16_t ext = ac97_getextmode(via->codec); | |
1343 | ext |= (via->codec_caps & | |
1344 | (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM)); | |
1345 | ext &= ~AC97_EXTCAP_DRA; | |
1346 | ac97_setextmode(via->codec, ext); | |
1347 | } | |
1348 | ||
67931cc4 | 1349 | ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", |
2a1ad637 FT |
1350 | rman_get_start(via->reg), rman_get_start(via->irq), |
1351 | PCM_KLDSTRING(snd_via8233)); | |
1352 | ||
558a398b SS |
1353 | /* Register */ |
1354 | if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS)) | |
1355 | goto bad; | |
1356 | for (i = 0; i < via_dxs_chnum; i++) | |
1357 | pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via); | |
1358 | for (i = 0; i < via_sgd_chnum; i++) | |
1359 | pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via); | |
1360 | for (i = 0; i < NWRCHANS; i++) | |
1361 | pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via); | |
1362 | if (via_dxs_chnum > 0) | |
1363 | via_init_sysctls(dev); | |
1364 | device_printf(dev, "<VIA DXS %sabled: DXS%s %d / SGD %d / REC %d>\n", | |
2a1ad637 FT |
1365 | (via_dxs_chnum > 0) ? "En" : "Dis", (via->dxs_src) ? "(SRC)" : "", |
1366 | via_dxs_chnum, via_sgd_chnum, NWRCHANS); | |
984263bc MD |
1367 | |
1368 | pcm_setstatus(dev, status); | |
1369 | ||
2a1ad637 | 1370 | return (0); |
984263bc | 1371 | bad: |
2a1ad637 FT |
1372 | if (via->codec) |
1373 | ac97_destroy(via->codec); | |
1374 | if (via->reg) | |
1375 | bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); | |
1376 | if (via->ih) | |
1377 | bus_teardown_intr(dev, via->irq, via->ih); | |
1378 | if (via->irq) | |
1379 | bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); | |
1380 | if (via->parent_dmat) | |
1381 | bus_dma_tag_destroy(via->parent_dmat); | |
1382 | if (via->sgd_addr) | |
1383 | bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); | |
1384 | if (via->sgd_table) | |
1385 | bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); | |
1386 | if (via->sgd_dmat) | |
1387 | bus_dma_tag_destroy(via->sgd_dmat); | |
1388 | if (via->lock) | |
1389 | snd_mtxfree(via->lock); | |
1390 | if (via) | |
67931cc4 | 1391 | kfree(via, M_DEVBUF); |
2a1ad637 | 1392 | return (ENXIO); |
984263bc MD |
1393 | } |
1394 | ||
1395 | static int | |
1396 | via_detach(device_t dev) | |
1397 | { | |
1398 | int r; | |
2a1ad637 | 1399 | struct via_info *via; |
984263bc MD |
1400 | |
1401 | r = pcm_unregister(dev); | |
2a1ad637 FT |
1402 | if (r) |
1403 | return (r); | |
984263bc MD |
1404 | |
1405 | via = pcm_getdevinfo(dev); | |
2a1ad637 FT |
1406 | |
1407 | if (via != NULL && (via->play_num != 0 || via->rec_num != 0)) { | |
1408 | snd_mtxlock(via->lock); | |
1409 | via->polling = 0; | |
1410 | callout_stop(&via->poll_timer); | |
1411 | snd_mtxunlock(via->lock); | |
1412 | callout_drain(&via->poll_timer); | |
1413 | } | |
1414 | ||
984263bc MD |
1415 | bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); |
1416 | bus_teardown_intr(dev, via->irq, via->ih); | |
1417 | bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); | |
1418 | bus_dma_tag_destroy(via->parent_dmat); | |
1419 | bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); | |
4886ec58 | 1420 | bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); |
984263bc | 1421 | bus_dma_tag_destroy(via->sgd_dmat); |
558a398b | 1422 | snd_mtxfree(via->lock); |
67931cc4 | 1423 | kfree(via, M_DEVBUF); |
2a1ad637 | 1424 | return (0); |
984263bc MD |
1425 | } |
1426 | ||
1427 | ||
1428 | static device_method_t via_methods[] = { | |
1429 | DEVMETHOD(device_probe, via_probe), | |
1430 | DEVMETHOD(device_attach, via_attach), | |
1431 | DEVMETHOD(device_detach, via_detach), | |
2a1ad637 | 1432 | { 0, 0} |
984263bc MD |
1433 | }; |
1434 | ||
1435 | static driver_t via_driver = { | |
1436 | "pcm", | |
1437 | via_methods, | |
1438 | PCM_SOFTC_SIZE, | |
1439 | }; | |
1440 | ||
aa6ac96e | 1441 | DRIVER_MODULE(snd_via8233, pci, via_driver, pcm_devclass, NULL, NULL); |
558a398b | 1442 | MODULE_DEPEND(snd_via8233, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); |
984263bc | 1443 | MODULE_VERSION(snd_via8233, 1); |