xref: /openbsd/sys/dev/pci/auvia.c (revision db3296cf)
1 /*	$OpenBSD: auvia.c,v 1.25 2003/05/21 19:32:20 drahn Exp $ */
2 /*	$NetBSD: auvia.c,v 1.7 2000/11/15 21:06:33 jdolecek Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Tyler C. Sarna
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * VIA Technologies VT82C686A Southbridge Audio Driver
42  *
43  * Documentation links:
44  *
45  * ftp://ftp.alsa-project.org/pub/manuals/via/686a.pdf
46  * ftp://ftp.alsa-project.org/pub/manuals/general/ac97r21.pdf
47  * ftp://ftp.alsa-project.org/pub/manuals/ad/AD1881_0.pdf (example AC'97 codec)
48  */
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/malloc.h>
53 #include <sys/device.h>
54 #include <sys/audioio.h>
55 
56 #include <uvm/uvm_extern.h>
57 
58 #include <dev/pci/pcidevs.h>
59 #include <dev/pci/pcivar.h>
60 
61 #include <dev/audio_if.h>
62 #include <dev/mulaw.h>
63 #include <dev/auconv.h>
64 
65 #include <dev/ic/ac97.h>
66 
67 #include <dev/pci/auviavar.h>
68 
69 struct auvia_dma {
70 	struct auvia_dma *next;
71 	caddr_t addr;
72 	size_t size;
73 	bus_dmamap_t map;
74 	bus_dma_segment_t seg;
75 };
76 
77 struct auvia_dma_op {
78 	u_int32_t ptr;
79 	u_int32_t flags;
80 #define AUVIA_DMAOP_EOL		0x80000000
81 #define AUVIA_DMAOP_FLAG	0x40000000
82 #define AUVIA_DMAOP_STOP	0x20000000
83 #define AUVIA_DMAOP_COUNT(x)	((x)&0x00FFFFFF)
84 };
85 
86 /* rev. H and later seem to support only fixed rate 44.1 kHz */
87 #define	AUVIA_FIXED_RATE	44100
88 
89 int	auvia_match(struct device *, void *, void *);
90 void	auvia_attach(struct device *, struct device *, void *);
91 int	auvia_open(void *, int);
92 void	auvia_close(void *);
93 int	auvia_query_encoding(void *addr, struct audio_encoding *fp);
94 int	auvia_set_params(void *, int, int, struct audio_params *,
95 	struct audio_params *);
96 int	auvia_round_blocksize(void *, int);
97 int	auvia_halt_output(void *);
98 int	auvia_halt_input(void *);
99 int	auvia_getdev(void *, struct audio_device *);
100 int	auvia_set_port(void *, mixer_ctrl_t *);
101 int	auvia_get_port(void *, mixer_ctrl_t *);
102 int	auvia_query_devinfo(void *, mixer_devinfo_t *);
103 void *	auvia_malloc(void *, int, size_t, int, int);
104 void	auvia_free(void *, void *, int);
105 size_t	auvia_round_buffersize(void *, int, size_t);
106 paddr_t	auvia_mappage(void *, void *, off_t, int);
107 int	auvia_get_props(void *);
108 int	auvia_build_dma_ops(struct auvia_softc *, struct auvia_softc_chan *,
109 	struct auvia_dma *, void *, void *, int);
110 int	auvia_trigger_output(void *, void *, void *, int, void (*)(void *),
111 	void *, struct audio_params *);
112 int	auvia_trigger_input(void *, void *, void *, int, void (*)(void *),
113 	void *, struct audio_params *);
114 
115 int	auvia_intr(void *);
116 
117 struct  cfdriver auvia_cd = {
118 	NULL, "auvia", DV_DULL
119 };
120 
121 struct cfattach auvia_ca = {
122 	sizeof (struct auvia_softc), auvia_match, auvia_attach
123 };
124 
125 #define AUVIA_PCICONF_JUNK	0x40
126 #define		AUVIA_PCICONF_ENABLES	 0x00FF0000	/* reg 42 mask */
127 #define		AUVIA_PCICONF_ACLINKENAB 0x00008000	/* ac link enab */
128 #define		AUVIA_PCICONF_ACNOTRST	 0x00004000	/* ~(ac reset) */
129 #define		AUVIA_PCICONF_ACSYNC	 0x00002000	/* ac sync */
130 #define		AUVIA_PCICONF_ACVSR	 0x00000800	/* var. samp. rate */
131 #define		AUVIA_PCICONF_ACSGD	 0x00000400	/* SGD enab */
132 #define		AUVIA_PCICONF_ACFM	 0x00000200	/* FM enab */
133 #define		AUVIA_PCICONF_ACSB	 0x00000100	/* SB enab */
134 
135 #define AUVIA_PLAY_BASE			0x00
136 #define AUVIA_RECORD_BASE		0x10
137 
138 #define	AUVIA_RP_STAT			0x00
139 #define		AUVIA_RPSTAT_INTR		0x03
140 #define AUVIA_RP_CONTROL		0x01
141 #define		AUVIA_RPCTRL_START		0x80
142 #define		AUVIA_RPCTRL_TERMINATE		0x40
143 #define		AUVIA_RPCTRL_AUTOSTART		0x20
144 /* The following are 8233 specific */
145 #define		AUVIA_RPCTRL_STOP		0x04
146 #define		AUVIA_RPCTRL_EOL		0x02
147 #define		AUVIA_RPCTRL_FLAG		0x01
148 #define AUVIA_RP_MODE			0x02
149 #define		AUVIA_RPMODE_INTR_FLAG		0x01
150 #define		AUVIA_RPMODE_INTR_EOL		0x02
151 #define		AUVIA_RPMODE_STEREO		0x10
152 #define		AUVIA_RPMODE_16BIT		0x20
153 #define		AUVIA_RPMODE_AUTOSTART		0x80
154 #define	AUVIA_RP_DMAOPS_BASE		0x04
155 
156 #define	VIA8233_RP_DXS_LVOL		0x02
157 #define	VIA8233_RP_DXS_RVOL		0x03
158 #define	VIA8233_RP_RATEFMT		0x08
159 #define		VIA8233_RATEFMT_48K	0xfffff
160 #define		VIA8233_RATEFMT_STEREO	0x00100000
161 #define		VIA8233_RATEFMT_16BIT	0x00200000
162 
163 #define VIA_RP_DMAOPS_COUNT		0x0C
164 
165 #define	AUVIA_CODEC_CTL			0x80
166 #define		AUVIA_CODEC_READ		0x00800000
167 #define		AUVIA_CODEC_BUSY		0x01000000
168 #define		AUVIA_CODEC_PRIVALID		0x02000000
169 #define		AUVIA_CODEC_INDEX(x)		((x)<<16)
170 
171 #define TIMEOUT	50
172 
173 struct audio_hw_if auvia_hw_if = {
174 	auvia_open,
175 	auvia_close,
176 	NULL, /* drain */
177 	auvia_query_encoding,
178 	auvia_set_params,
179 	auvia_round_blocksize,
180 	NULL, /* commit_settings */
181 	NULL, /* init_output */
182 	NULL, /* init_input */
183 	NULL, /* start_output */
184 	NULL, /* start_input */
185 	auvia_halt_output,
186 	auvia_halt_input,
187 	NULL, /* speaker_ctl */
188 	auvia_getdev,
189 	NULL, /* setfd */
190 	auvia_set_port,
191 	auvia_get_port,
192 	auvia_query_devinfo,
193 	auvia_malloc,
194 	auvia_free,
195 	auvia_round_buffersize,
196 	auvia_mappage,
197 	auvia_get_props,
198 	auvia_trigger_output,
199 	auvia_trigger_input
200 };
201 
202 int	auvia_attach_codec(void *, struct ac97_codec_if *);
203 int	auvia_write_codec(void *, u_int8_t, u_int16_t);
204 int	auvia_read_codec(void *, u_int8_t, u_int16_t *);
205 void	auvia_reset_codec(void *);
206 int	auvia_waitready_codec(struct auvia_softc *sc);
207 int	auvia_waitvalid_codec(struct auvia_softc *sc);
208 
209 const struct pci_matchid auvia_devices[] = {
210 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_AC97 },
211 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_AC97 },
212 };
213 
214 int
215 auvia_match(struct device *parent, void *match, void *aux)
216 {
217 	return (pci_matchbyid((struct pci_attach_args *)aux, auvia_devices,
218 	    sizeof(auvia_devices)/sizeof(auvia_devices[0])));
219 }
220 
221 
222 void
223 auvia_attach(struct device *parent, struct device *self, void *aux)
224 {
225 	struct pci_attach_args *pa = aux;
226 	struct auvia_softc *sc = (struct auvia_softc *) self;
227 	const char *intrstr = NULL;
228 	struct mixer_ctrl ctl;
229 	pci_chipset_tag_t pc = pa->pa_pc;
230 	pcitag_t pt = pa->pa_tag;
231 	pci_intr_handle_t ih;
232 	bus_size_t iosize;
233 	pcireg_t pr;
234 	int r, i;
235 
236 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97)
237 		sc->sc_flags |= AUVIA_FLAGS_VT8233;
238 
239 	if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
240 	    &sc->sc_ioh, NULL, &iosize, 0)) {
241 		printf(": can't map i/o space\n");
242 		return;
243 	}
244 
245 	sc->sc_dmat = pa->pa_dmat;
246 	sc->sc_pc = pc;
247 	sc->sc_pt = pt;
248 
249 	if (pci_intr_map(pa, &ih)) {
250 		printf(": couldn't map interrupt\n");
251 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
252 		return;
253 	}
254 	intrstr = pci_intr_string(pc, ih);
255 
256 	sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, auvia_intr, sc,
257 	    sc->sc_dev.dv_xname);
258 	if (sc->sc_ih == NULL) {
259 		printf(": couldn't establish interrupt");
260 		if (intrstr != NULL)
261 			printf(" at %s", intrstr);
262 		printf("\n");
263 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
264 		return;
265 	}
266 
267 	printf(": %s\n", intrstr);
268 
269 	/* disable SBPro compat & others */
270 	pr = pci_conf_read(pc, pt, AUVIA_PCICONF_JUNK);
271 
272 	pr &= ~AUVIA_PCICONF_ENABLES; /* clear compat function enables */
273 	/* XXX what to do about MIDI, FM, joystick? */
274 
275 	pr |= (AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST |
276 	    AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD);
277 
278 	pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);
279 
280 	pci_conf_write(pc, pt, AUVIA_PCICONF_JUNK, pr);
281 
282 	sc->host_if.arg = sc;
283 	sc->host_if.attach = auvia_attach_codec;
284 	sc->host_if.read = auvia_read_codec;
285 	sc->host_if.write = auvia_write_codec;
286 	sc->host_if.reset = auvia_reset_codec;
287 
288 	if ((r = ac97_attach(&sc->host_if)) != 0) {
289 		printf("%s: can't attach codec (error 0x%X)\n",
290 		    sc->sc_dev.dv_xname, r);
291 		pci_intr_disestablish(pc, sc->sc_ih);
292 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
293 		return;
294 	}
295 
296 	/* disable mutes */
297 	for (i = 0; i < 4; i++) {
298 		static struct {
299 			char *class, *device;
300 		} d[] = {
301 			{ AudioCoutputs, AudioNmaster},
302 			{ AudioCinputs, AudioNdac},
303 			{ AudioCinputs, AudioNcd},
304 			{ AudioCrecord, AudioNvolume},
305 		};
306 
307 		ctl.type = AUDIO_MIXER_ENUM;
308 		ctl.un.ord = 0;
309 
310 		ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
311 		    d[i].class, d[i].device, AudioNmute);
312 		auvia_set_port(sc, &ctl);
313 	}
314 
315 	/* set a reasonable default volume */
316 
317 	ctl.type = AUDIO_MIXER_VALUE;
318 	ctl.un.value.num_channels = 2;
319 	ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = \
320 	ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 199;
321 
322 	ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
323 	    AudioCoutputs, AudioNmaster, NULL);
324 	auvia_set_port(sc, &ctl);
325 
326 	audio_attach_mi(&auvia_hw_if, sc, &sc->sc_dev);
327 }
328 
329 
330 int
331 auvia_attach_codec(void *addr, struct ac97_codec_if *cif)
332 {
333 	struct auvia_softc *sc = addr;
334 
335 	sc->codec_if = cif;
336 
337 	return 0;
338 }
339 
340 
341 void
342 auvia_reset_codec(void *addr)
343 {
344 #ifdef notyet /* XXX seems to make codec become unready... ??? */
345 	struct auvia_softc *sc = addr;
346 	pcireg_t r;
347 
348 	/* perform a codec cold reset */
349 
350 	r = pci_conf_read(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK);
351 
352 	r &= ~AUVIA_PCICONF_ACNOTRST;	/* enable RESET (active low) */
353 	pci_conf_write(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK, r);
354 	delay(2);
355 
356 	r |= AUVIA_PCICONF_ACNOTRST;	/* disable RESET (inactive high) */
357 	pci_conf_write(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK, r);
358 	delay(200);
359 
360 	auvia_waitready_codec(sc);
361 #endif
362 }
363 
364 
365 int
366 auvia_waitready_codec(struct auvia_softc *sc)
367 {
368 	int i;
369 
370 	/* poll until codec not busy */
371 	for (i = 0; (i < TIMEOUT) && (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
372 	     AUVIA_CODEC_CTL) & AUVIA_CODEC_BUSY); i++)
373 		delay(1);
374 
375 	if (i >= TIMEOUT) {
376 		printf("%s: codec busy\n", sc->sc_dev.dv_xname);
377 		return 1;
378 	}
379 
380 	return 0;
381 }
382 
383 
384 int
385 auvia_waitvalid_codec(struct auvia_softc *sc)
386 {
387 	int i;
388 
389 	/* poll until codec valid */
390 	for (i = 0; (i < TIMEOUT) && !(bus_space_read_4(sc->sc_iot, sc->sc_ioh,
391 	     AUVIA_CODEC_CTL) & AUVIA_CODEC_PRIVALID); i++)
392 		delay(1);
393 
394 	if (i >= TIMEOUT) {
395 		printf("%s: codec invalid\n", sc->sc_dev.dv_xname);
396 		return 1;
397 	}
398 
399 	return 0;
400 }
401 
402 
403 int
404 auvia_write_codec(void *addr, u_int8_t reg, u_int16_t val)
405 {
406 	struct auvia_softc *sc = addr;
407 
408 	if (auvia_waitready_codec(sc))
409 		return 1;
410 
411 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL,
412 	    AUVIA_CODEC_PRIVALID | AUVIA_CODEC_INDEX(reg) | val);
413 
414 	return 0;
415 }
416 
417 
418 int
419 auvia_read_codec(void *addr, u_int8_t reg, u_int16_t *val)
420 {
421 	struct auvia_softc *sc = addr;
422 
423 	if (auvia_waitready_codec(sc))
424 		return 1;
425 
426 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL,
427 	    AUVIA_CODEC_PRIVALID | AUVIA_CODEC_READ | AUVIA_CODEC_INDEX(reg));
428 
429 	if (auvia_waitready_codec(sc))
430 		return 1;
431 
432 	if (auvia_waitvalid_codec(sc))
433 		return 1;
434 
435 	*val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL);
436 
437 	return 0;
438 }
439 
440 
441 int
442 auvia_open(void *addr, int flags)
443 {
444 	return 0;
445 }
446 
447 
448 void
449 auvia_close(void *addr)
450 {
451 	struct auvia_softc *sc = addr;
452 
453 	auvia_halt_output(sc);
454 	auvia_halt_input(sc);
455 
456 	sc->sc_play.sc_intr = NULL;
457 	sc->sc_record.sc_intr = NULL;
458 }
459 
460 
461 int
462 auvia_query_encoding(void *addr, struct audio_encoding *fp)
463 {
464 	switch (fp->index) {
465 	case 0:
466 		strlcpy(fp->name, AudioEulinear, sizeof fp->name);
467 		fp->encoding = AUDIO_ENCODING_ULINEAR;
468 		fp->precision = 8;
469 		fp->flags = 0;
470 		return (0);
471 	case 1:
472 		strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
473 		fp->encoding = AUDIO_ENCODING_ULAW;
474 		fp->precision = 8;
475 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
476 		return (0);
477 	case 2:
478 		strlcpy(fp->name, AudioEalaw, sizeof fp->name);
479 		fp->encoding = AUDIO_ENCODING_ALAW;
480 		fp->precision = 8;
481 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
482 		return (0);
483 	case 3:
484 		strlcpy(fp->name, AudioEslinear, sizeof fp->name);
485 		fp->encoding = AUDIO_ENCODING_SLINEAR;
486 		fp->precision = 8;
487 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
488 		return (0);
489 	case 4:
490 		strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
491 		fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
492 		fp->precision = 16;
493 		fp->flags = 0;
494 		return (0);
495 	case 5:
496 		strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
497 		fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
498 		fp->precision = 16;
499 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
500 		return (0);
501 	case 6:
502 		strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
503 		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
504 		fp->precision = 16;
505 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
506 		return (0);
507 	case 7:
508 		strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
509 		fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
510 		fp->precision = 16;
511 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
512 		return (0);
513 	default:
514 		return (EINVAL);
515 	}
516 }
517 
518 
519 int
520 auvia_set_params(void *addr, int setmode, int usemode,
521     struct audio_params *play, struct audio_params *rec)
522 {
523 	struct auvia_softc *sc = addr;
524 	struct audio_params *p;
525 	u_int16_t regval;
526 	int mode, base;
527 
528 	/* for mode in (RECORD, PLAY) */
529 	for (mode = AUMODE_RECORD; mode != -1;
530 	     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
531 		if ((setmode & mode) == 0)
532 			continue;
533 
534 		if (mode == AUMODE_PLAY) {
535 			p = play;
536 			base = AUVIA_PLAY_BASE;
537 		} else {
538 			p = rec;
539 			base = AUVIA_RECORD_BASE;
540 		}
541 
542 		if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
543 			u_int32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
544 			    base + VIA8233_RP_RATEFMT) & ~(VIA8233_RATEFMT_48K
545 			    | VIA8233_RATEFMT_STEREO | VIA8233_RATEFMT_16BIT);
546 
547 			v |= VIA8233_RATEFMT_48K *
548 			    (p->sample_rate / 20) / (48000 / 20);
549 
550 			if (p->channels == 2)
551 				v |= VIA8233_RATEFMT_STEREO;
552 			if (p->precision == 16)
553 				v |= VIA8233_RATEFMT_16BIT;
554 
555 			bus_space_write_4(sc->sc_iot, sc->sc_ioh,
556 			    base + VIA8233_RP_RATEFMT, v);
557 		}
558 
559 		if (ac97_set_rate(sc->codec_if, p, mode))
560 			return (EINVAL);
561 
562 		if ((p->precision != 8 && p->precision != 16) ||
563 		    (p->channels != 1 && p->channels != 2))
564 			return (EINVAL);
565 
566 		p->factor = 1;
567 		p->sw_code = 0;
568 		switch (p->encoding) {
569 		case AUDIO_ENCODING_SLINEAR_BE:
570 			if (p->precision == 16)
571 				p->sw_code = swap_bytes;
572 			else
573 				p->sw_code = change_sign8;
574 			break;
575 		case AUDIO_ENCODING_SLINEAR_LE:
576 			if (p->precision != 16)
577 				p->sw_code = change_sign8;
578 			break;
579 		case AUDIO_ENCODING_ULINEAR_BE:
580 			if (p->precision == 16)
581 				p->sw_code = mode == AUMODE_PLAY?
582 				    swap_bytes_change_sign16_le :
583 				    change_sign16_swap_bytes_le;
584 			break;
585 		case AUDIO_ENCODING_ULINEAR_LE:
586 			if (p->precision == 16)
587 				p->sw_code = change_sign16_le;
588 			break;
589 		case AUDIO_ENCODING_ULAW:
590 			if (mode == AUMODE_PLAY) {
591 				p->factor = 2;
592 				p->sw_code = mulaw_to_slinear16_le;
593 			} else
594 				p->sw_code = ulinear8_to_mulaw;
595 			break;
596 		case AUDIO_ENCODING_ALAW:
597 			if (mode == AUMODE_PLAY) {
598 				p->factor = 2;
599 				p->sw_code = alaw_to_slinear16_le;
600 			} else
601 				p->sw_code = ulinear8_to_alaw;
602 			break;
603 		default:
604 			return (EINVAL);
605 		}
606 
607 		regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
608 			| (p->precision * p->factor == 16 ?
609 				AUVIA_RPMODE_16BIT : 0)
610 			| AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
611 			| AUVIA_RPMODE_AUTOSTART;
612 
613 		if (mode == AUMODE_PLAY)
614 			sc->sc_play.sc_reg = regval;
615 		else
616 			sc->sc_record.sc_reg = regval;
617 	}
618 
619 	return 0;
620 }
621 
622 
623 int
624 auvia_round_blocksize(void *addr, int blk)
625 {
626 	return (blk & -32);
627 }
628 
629 
630 int
631 auvia_halt_output(void *addr)
632 {
633 	struct auvia_softc *sc = addr;
634 
635 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
636 	    AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
637 
638 	return 0;
639 }
640 
641 
642 int
643 auvia_halt_input(void *addr)
644 {
645 	struct auvia_softc *sc = addr;
646 
647 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
648 	    AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
649 
650 	return 0;
651 }
652 
653 
654 int
655 auvia_getdev(void *addr, struct audio_device *retp)
656 {
657 	struct auvia_softc *sc = addr;
658 
659 	if (retp) {
660 		strncpy(retp->name,
661 		    sc->sc_flags & AUVIA_FLAGS_VT8233? "VIA VT8233" :
662 		    "VIA VT82C686A", sizeof(retp->name));
663 		strncpy(retp->version, sc->sc_revision, sizeof(retp->version));
664 		strncpy(retp->config, "auvia", sizeof(retp->config));
665 	}
666 
667 	return 0;
668 }
669 
670 
671 int
672 auvia_set_port(void *addr, mixer_ctrl_t *cp)
673 {
674 	struct auvia_softc *sc = addr;
675 
676 	return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
677 }
678 
679 
680 int
681 auvia_get_port(void *addr, mixer_ctrl_t *cp)
682 {
683 	struct auvia_softc *sc = addr;
684 
685 	return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
686 }
687 
688 
689 int
690 auvia_query_devinfo(void *addr, mixer_devinfo_t *dip)
691 {
692 	struct auvia_softc *sc = addr;
693 
694 	return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
695 }
696 
697 
698 void *
699 auvia_malloc(void *addr, int direction, size_t size, int pool, int flags)
700 {
701 	struct auvia_softc *sc = addr;
702 	struct auvia_dma *p;
703 	int error;
704 	int rseg;
705 
706 	p = malloc(sizeof(*p), pool, flags);
707 	if (!p)
708 		return 0;
709 
710 	p->size = size;
711 	if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &p->seg,
712 	    1, &rseg, BUS_DMA_NOWAIT)) != 0) {
713 		printf("%s: unable to allocate dma, error = %d\n",
714 		    sc->sc_dev.dv_xname, error);
715 		goto fail_alloc;
716 	}
717 
718 	if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
719 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
720 		printf("%s: unable to map dma, error = %d\n",
721 		    sc->sc_dev.dv_xname, error);
722 		goto fail_map;
723 	}
724 
725 	if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
726 	    BUS_DMA_NOWAIT, &p->map)) != 0) {
727 		printf("%s: unable to create dma map, error = %d\n",
728 		    sc->sc_dev.dv_xname, error);
729 		goto fail_create;
730 	}
731 
732 	if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
733 	    BUS_DMA_NOWAIT)) != 0) {
734 		printf("%s: unable to load dma map, error = %d\n",
735 		    sc->sc_dev.dv_xname, error);
736 		goto fail_load;
737 	}
738 
739 	p->next = sc->sc_dmas;
740 	sc->sc_dmas = p;
741 
742 	return p->addr;
743 
744 
745 fail_load:
746 	bus_dmamap_destroy(sc->sc_dmat, p->map);
747 fail_create:
748 	bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
749 fail_map:
750 	bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
751 fail_alloc:
752 	free(p, pool);
753 	return 0;
754 }
755 
756 
757 void
758 auvia_free(void *addr, void *ptr, int pool)
759 {
760 	struct auvia_softc *sc = addr;
761 	struct auvia_dma **pp, *p;
762 
763 	for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
764 		if (p->addr == ptr) {
765 			bus_dmamap_unload(sc->sc_dmat, p->map);
766 			bus_dmamap_destroy(sc->sc_dmat, p->map);
767 			bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
768 			bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
769 
770 			*pp = p->next;
771 			free(p, pool);
772 			return;
773 		}
774 
775 	panic("auvia_free: trying to free unallocated memory");
776 }
777 
778 
779 size_t
780 auvia_round_buffersize(void *addr, int direction, size_t size)
781 {
782 	return size;
783 }
784 
785 
786 paddr_t
787 auvia_mappage(void *addr, void *mem, off_t off, int prot)
788 {
789 	struct auvia_softc *sc = addr;
790 	struct auvia_dma *p;
791 
792 	if (off < 0)
793 		return -1;
794 
795 	for (p = sc->sc_dmas; p && p->addr != mem; p = p->next)
796 		;
797 
798 	if (!p)
799 		return -1;
800 
801 	return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot,
802 	    BUS_DMA_WAITOK);
803 }
804 
805 
806 int
807 auvia_get_props(void *addr)
808 {
809 	return (AUDIO_PROP_MMAP |  AUDIO_PROP_INDEPENDENT |
810 	    AUDIO_PROP_FULLDUPLEX);
811 }
812 
813 
814 int
815 auvia_build_dma_ops(struct auvia_softc *sc, struct auvia_softc_chan *ch,
816     struct auvia_dma *p, void *start, void *end, int blksize)
817 {
818 	struct auvia_dma_op *op;
819 	struct auvia_dma *dp;
820 	bus_addr_t s, e;
821 	size_t l;
822 	int segs;
823 
824 	s = p->map->dm_segs[0].ds_addr;
825 	l = ((char *)end - (char *)start);
826 	e = s + l;
827 	segs = (l + blksize - 1) / blksize;
828 
829 	if (segs > (ch->sc_dma_op_count)) {
830 		/* if old list was too small, free it */
831 		if (ch->sc_dma_ops)
832 			auvia_free(sc, ch->sc_dma_ops, M_DEVBUF);
833 
834 		ch->sc_dma_ops = auvia_malloc(sc, 0,
835 		    sizeof(struct auvia_dma_op) * segs, M_DEVBUF, M_WAITOK);
836 
837 		for (dp = sc->sc_dmas; dp &&
838 		     dp->addr != (void *)(ch->sc_dma_ops); dp = dp->next)
839 			;
840 
841 		if (!dp)
842 			panic("%s: build_dma_ops: where'd my memory go??? "
843 			    "address (%p)", sc->sc_dev.dv_xname,
844 			    ch->sc_dma_ops);
845 
846 		ch->sc_dma_op_count = segs;
847 		ch->sc_dma_ops_dma = dp;
848 	}
849 
850 	dp = ch->sc_dma_ops_dma;
851 	op = ch->sc_dma_ops;
852 
853 	while (l) {
854 		op->ptr = htole32(s);
855 		l = l - blksize;
856 		/* if last block */
857 		op->flags = htole32((l? AUVIA_DMAOP_FLAG : AUVIA_DMAOP_EOL) | blksize);
858 		s += blksize;
859 		op++;
860 	}
861 
862 	return 0;
863 }
864 
865 
866 int
867 auvia_trigger_output(void *addr, void *start, void *end, int blksize,
868     void (*intr)(void *), void *arg, struct audio_params *param)
869 {
870 	struct auvia_softc *sc = addr;
871 	struct auvia_softc_chan *ch = &(sc->sc_play);
872 	struct auvia_dma *p;
873 
874 	for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
875 		;
876 
877 	if (!p)
878 		panic("auvia_trigger_output: request with bad start "
879 		    "address (%p)", start);
880 
881 	if (auvia_build_dma_ops(sc, ch, p, start, end, blksize)) {
882 		return 1;
883 	}
884 
885 	ch->sc_intr = intr;
886 	ch->sc_arg = arg;
887 
888 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
889 	    AUVIA_PLAY_BASE + AUVIA_RP_DMAOPS_BASE,
890 	    ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
891 
892 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
893 	    AUVIA_PLAY_BASE + AUVIA_RP_MODE, ch->sc_reg);
894 
895 	if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
896 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
897 		    AUVIA_PLAY_BASE + VIA8233_RP_DXS_LVOL, 0);
898 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
899 		    AUVIA_PLAY_BASE + VIA8233_RP_DXS_RVOL, 0);
900 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
901 		    AUVIA_PLAY_BASE + AUVIA_RP_CONTROL,
902 		    AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
903 		    AUVIA_RPCTRL_STOP  | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
904 	} else
905 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
906 		    AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
907 
908 	return 0;
909 }
910 
911 
912 int
913 auvia_trigger_input(void *addr, void *start, void *end, int blksize,
914     void (*intr)(void *), void *arg, struct audio_params *param)
915 {
916 	struct auvia_softc *sc = addr;
917 	struct auvia_softc_chan *ch = &(sc->sc_record);
918 	struct auvia_dma *p;
919 
920 	for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
921 		;
922 
923 	if (!p)
924 		panic("auvia_trigger_input: request with bad start "
925 		    "address (%p)", start);
926 
927 	if (auvia_build_dma_ops(sc, ch, p, start, end, blksize))
928 		return 1;
929 
930 	ch->sc_intr = intr;
931 	ch->sc_arg = arg;
932 
933 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
934 	    AUVIA_RECORD_BASE + AUVIA_RP_DMAOPS_BASE,
935 	    ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
936 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
937 	    AUVIA_RECORD_BASE + AUVIA_RP_MODE, ch->sc_reg);
938 
939 	if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
940 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
941 		    AUVIA_RECORD_BASE + VIA8233_RP_DXS_LVOL, 0);
942 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
943 		    AUVIA_RECORD_BASE + VIA8233_RP_DXS_RVOL, 0);
944 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
945 		    AUVIA_RECORD_BASE + AUVIA_RP_CONTROL,
946 		    AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
947 		    AUVIA_RPCTRL_STOP  | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
948 	} else
949 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
950 		    AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
951 
952 	return 0;
953 }
954 
955 
956 int
957 auvia_intr(void *arg)
958 {
959 	struct auvia_softc *sc = arg;
960 	u_int8_t r;
961 	int i = 0;
962 
963 	r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
964 	    AUVIA_RECORD_BASE + AUVIA_RP_STAT);
965 	if (r & AUVIA_RPSTAT_INTR) {
966 		if (sc->sc_record.sc_intr)
967 			sc->sc_record.sc_intr(sc->sc_record.sc_arg);
968 
969 		/* clear interrupts */
970 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
971 		    AUVIA_RECORD_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
972 
973 		i++;
974 	}
975 	r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
976 	    AUVIA_PLAY_BASE + AUVIA_RP_STAT);
977 	if (r & AUVIA_RPSTAT_INTR) {
978 		if (sc->sc_play.sc_intr)
979 			sc->sc_play.sc_intr(sc->sc_play.sc_arg);
980 
981 		/* clear interrupts */
982 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
983 		    AUVIA_PLAY_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
984 
985 		i++;
986 	}
987 
988 	return (i? 1 : 0);
989 }
990