xref: /netbsd-src/sys/dev/ic/am7930.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /*	$NetBSD: am7930.c,v 1.57 2017/08/29 06:38:49 isaki Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Rolf Grossmann
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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Rolf Grossmann.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Front-end attachment independent layer for AMD 79c30
35  * audio driver.  No ISDN support.
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: am7930.c,v 1.57 2017/08/29 06:38:49 isaki Exp $");
40 
41 #include "audio.h"
42 #if NAUDIO > 0
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/errno.h>
47 #include <sys/ioctl.h>
48 #include <sys/device.h>
49 #include <sys/proc.h>
50 
51 #include <sys/bus.h>
52 #include <sys/cpu.h>
53 
54 #include <sys/audioio.h>
55 #include <dev/audio_if.h>
56 #include <dev/mulaw.h>
57 
58 #include <dev/ic/am7930reg.h>
59 #include <dev/ic/am7930var.h>
60 
61 #ifdef AUDIO_DEBUG
62 int     am7930debug = 0;
63 #define DPRINTF(x)      if (am7930debug) printf x
64 #else
65 #define DPRINTF(x)
66 #endif
67 
68 
69 /* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
70 
71 /*
72  * gx, gr & stg gains.  this table must contain 256 elements with
73  * the 0th being "infinity" (the magic value 9008).  The remaining
74  * elements match sun's gain curve (but with higher resolution):
75  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
76  */
77 static const uint16_t gx_coeff[256] = {
78 	0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
79 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
80 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
81 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
82 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
83 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
84 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
85 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
86 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
87 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
88 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
89 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
90 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
91 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
92 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
93 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
94 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
95 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
96 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
97 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
98 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
99 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
100 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
101 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
102 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
103 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
104 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
105 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
106 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
107 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
108 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
109 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
110 };
111 
112 /*
113  * second stage play gain.
114  */
115 static const uint16_t ger_coeff[] = {
116 	0x431f, /* 5. dB */
117 	0x331f, /* 5.5 dB */
118 	0x40dd, /* 6. dB */
119 	0x11dd, /* 6.5 dB */
120 	0x440f, /* 7. dB */
121 	0x411f, /* 7.5 dB */
122 	0x311f, /* 8. dB */
123 	0x5520, /* 8.5 dB */
124 	0x10dd, /* 9. dB */
125 	0x4211, /* 9.5 dB */
126 	0x410f, /* 10. dB */
127 	0x111f, /* 10.5 dB */
128 	0x600b, /* 11. dB */
129 	0x00dd, /* 11.5 dB */
130 	0x4210, /* 12. dB */
131 	0x110f, /* 13. dB */
132 	0x7200, /* 14. dB */
133 	0x2110, /* 15. dB */
134 	0x2200, /* 15.9 dB */
135 	0x000b, /* 16.9 dB */
136 	0x000f  /* 18. dB */
137 #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
138 };
139 
140 extern stream_filter_factory_t null_filter;
141 
142 /*
143  * Reset chip and set boot-time softc defaults.
144  */
145 void
146 am7930_init(struct am7930_softc *sc, int flag)
147 {
148 
149 	DPRINTF(("am7930_init()\n"));
150 
151 	/* set boot defaults */
152 	sc->sc_rlevel = 128;
153 	sc->sc_plevel = 128;
154 	sc->sc_mlevel = 0;
155 	sc->sc_out_port = AUDIOAMD_SPEAKER_VOL;
156 	sc->sc_mic_mute = 0;
157 
158 	/* disable sample interrupts */
159 	AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4, 0);
160 
161 	/* initialise voice and data, and disable interrupts */
162 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
163 		AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
164 
165 	if (flag == AUDIOAMD_DMA_MODE) {
166 
167 		/* configure PP for serial (SBP) mode */
168 		AM7930_IWRITE(sc, AM7930_IREG_PP_PPCR1, AM7930_PPCR1_SBP);
169 
170 		/*
171 		 * Initialise the MUX unit - route the MAP to the PP
172 		 */
173 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1,
174 			(AM7930_MCRCHAN_BA << 4) | AM7930_MCRCHAN_BD);
175 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, AM7930_MCRCHAN_NC);
176 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3, AM7930_MCRCHAN_NC);
177 
178 	} else {
179 
180 		/*
181 		 * Initialize the MUX unit.  We use MCR3 to route the MAP
182 		 * through channel Bb.  MCR1 and MCR2 are unused.
183 		 * Setting the INT enable bit in MCR4 will generate an
184 		 * interrupt on each converted audio sample.
185 		 */
186 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1, 0);
187 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, 0);
188 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3,
189 			(AM7930_MCRCHAN_BB << 4) | AM7930_MCRCHAN_BA);
190 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4,
191 			AM7930_MCR4_INT_ENABLE);
192 	}
193 
194 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
195 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
196 }
197 
198 int
199 am7930_open(void *addr, int flags)
200 {
201 	struct am7930_softc *sc;
202 
203 	sc = addr;
204 	DPRINTF(("sa_open: unit %p\n", sc));
205 	sc->sc_glue->onopen(sc);
206 	DPRINTF(("saopen: ok -> sc=%p\n",sc));
207 	return 0;
208 }
209 
210 void
211 am7930_close(void *addr)
212 {
213 	struct am7930_softc *sc;
214 
215 	sc = addr;
216 	DPRINTF(("sa_close: sc=%p\n", sc));
217 	sc->sc_glue->onclose(sc);
218 	DPRINTF(("sa_close: closed.\n"));
219 }
220 
221 /*
222  * XXX should be extended to handle a few of the more common formats.
223  */
224 int
225 am7930_set_params(void *addr, int setmode, int usemode, audio_params_t *p,
226     audio_params_t *r, stream_filter_list_t *pfil, stream_filter_list_t *rfil)
227 {
228 	audio_params_t hw;
229 	struct am7930_softc *sc;
230 
231 	sc = addr;
232 	if ((usemode & AUMODE_PLAY) == AUMODE_PLAY) {
233 		if (p->sample_rate < 7500 || p->sample_rate > 8500 ||
234 			(p->encoding != AUDIO_ENCODING_ULAW &&
235 			 p->encoding != AUDIO_ENCODING_SLINEAR) ||
236 			p->precision != 8 ||
237 			p->channels != 1)
238 				return EINVAL;
239 		p->sample_rate = 8000;
240 		if (sc->sc_glue->output_conv != NULL) {
241 			hw = *p;
242 			hw.encoding = AUDIO_ENCODING_NONE;
243 			hw.precision = 8;
244 			pfil->append(pfil, null_filter, &hw);
245 			hw.precision *= sc->sc_glue->factor;
246 			pfil->append(pfil, sc->sc_glue->output_conv, &hw);
247 		}
248 		if (p->encoding == AUDIO_ENCODING_SLINEAR) {
249 			hw = *p;
250 			hw.precision = 8;
251 			hw.encoding = AUDIO_ENCODING_ULAW;
252 			pfil->append(pfil, linear8_to_mulaw, &hw);
253 		}
254 
255 	}
256 	if ((usemode & AUMODE_RECORD) == AUMODE_RECORD) {
257 		if (r->sample_rate < 7500 || r->sample_rate > 8500 ||
258 			(r->encoding != AUDIO_ENCODING_ULAW &&
259 			 r->encoding != AUDIO_ENCODING_SLINEAR) ||
260 			r->precision != 8 ||
261 			r->channels != 1)
262 				return EINVAL;
263 		r->sample_rate = 8000;
264 		if (sc->sc_glue->input_conv != NULL) {
265 			hw = *r;
266 			hw.encoding = AUDIO_ENCODING_NONE;
267 			hw.precision = 8;
268 			rfil->append(rfil, null_filter, &hw);
269 			hw.precision *= sc->sc_glue->factor;
270 			rfil->append(rfil, sc->sc_glue->input_conv, &hw);
271 		}
272 	    	if (r->encoding == AUDIO_ENCODING_SLINEAR) {
273 			hw = *r;
274 			hw.precision = 8;
275 			hw.encoding = AUDIO_ENCODING_ULAW;
276 			rfil->append(rfil, mulaw_to_linear8, &hw);
277 		}
278 	}
279 
280 	return 0;
281 }
282 
283 int
284 am7930_query_encoding(void *addr, struct audio_encoding *fp)
285 {
286 	switch (fp->index) {
287 	case 0:
288 		strcpy(fp->name, AudioEmulaw);
289 		fp->encoding = AUDIO_ENCODING_ULAW;
290 		fp->precision = 8;
291 		fp->flags = 0;
292 		break;
293 	case 1:
294 		strcpy(fp->name, AudioEslinear);
295 		fp->encoding = AUDIO_ENCODING_SLINEAR;
296 		fp->precision = 8;
297 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
298 		break;
299 	default:
300 		return EINVAL;
301 		    /*NOTREACHED*/
302 	}
303 	return 0;
304 }
305 
306 int
307 am7930_round_blocksize(void *addr, int blk,
308     int mode, const audio_params_t *param)
309 {
310 	return blk;
311 }
312 
313 int
314 am7930_commit_settings(void *addr)
315 {
316 	struct am7930_softc *sc;
317 	uint16_t ger, gr, gx, stgr;
318 	uint8_t mmr2, mmr3;
319 	int level;
320 
321 	DPRINTF(("sa_commit.\n"));
322 	sc = addr;
323 	gx = gx_coeff[sc->sc_rlevel];
324 	stgr = gx_coeff[sc->sc_mlevel];
325 
326 	level = (sc->sc_plevel * (256 + NGER)) >> 8;
327 	if (level >= 256) {
328 		ger = ger_coeff[level - 256];
329 		gr = gx_coeff[255];
330 	} else {
331 		ger = ger_coeff[0];
332 		gr = gx_coeff[level];
333 	}
334 
335 	mutex_enter(&sc->sc_intr_lock);
336 
337 	mmr2 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR2);
338 	if (sc->sc_out_port == AUDIOAMD_SPEAKER_VOL)
339 		mmr2 |= AM7930_MMR2_LS;
340 	else
341 		mmr2 &= ~AM7930_MMR2_LS;
342 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR2, mmr2);
343 
344 	mmr3 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR3);
345 	if (sc->sc_mic_mute)
346 		mmr3 |= AM7930_MMR3_MUTE;
347 	else
348 		mmr3 &= ~AM7930_MMR3_MUTE;
349 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR3, mmr3);
350 
351 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR1,
352 		AM7930_MMR1_GX | AM7930_MMR1_GER |
353 		AM7930_MMR1_GR | AM7930_MMR1_STG);
354 
355 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GX, gx);
356 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_STG, stgr);
357 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GR, gr);
358 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GER, ger);
359 
360 	mutex_exit(&sc->sc_intr_lock);
361 
362 	return 0;
363 }
364 
365 int
366 am7930_halt_output(void *addr)
367 {
368 	struct am7930_softc *sc;
369 
370 	sc = addr;
371 	/* XXX only halt, if input is also halted ?? */
372 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
373 	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
374 	return 0;
375 }
376 
377 int
378 am7930_halt_input(void *addr)
379 {
380 	struct am7930_softc *sc;
381 
382 	sc = addr;
383 	/* XXX only halt, if output is also halted ?? */
384 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
385 	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
386 	return 0;
387 }
388 
389 /*
390  * XXX chip is full-duplex, but really attach-dependent.
391  * For now we know of no half-duplex attachments.
392  */
393 int
394 am7930_get_props(void *addr)
395 {
396 	return AUDIO_PROP_FULLDUPLEX;
397 }
398 
399 /*
400  * Attach-dependent channel set/query
401  */
402 int
403 am7930_set_port(void *addr, mixer_ctrl_t *cp)
404 {
405 	struct am7930_softc *sc;
406 
407 	DPRINTF(("am7930_set_port: port=%d", cp->dev));
408 	sc = addr;
409 	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
410 		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
411 		cp->dev == AUDIOAMD_MIC_MUTE) {
412 		if (cp->type != AUDIO_MIXER_ENUM)
413 			return EINVAL;
414 	} else if (cp->type != AUDIO_MIXER_VALUE ||
415 	    cp->un.value.num_channels != 1) {
416 		return EINVAL;
417 	}
418 
419 	switch(cp->dev) {
420 	    case AUDIOAMD_MIC_VOL:
421 		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
422 		    break;
423 	    case AUDIOAMD_SPEAKER_VOL:
424 	    case AUDIOAMD_HEADPHONES_VOL:
425 		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
426 		    break;
427 	    case AUDIOAMD_MONITOR_VOL:
428 		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
429 		    break;
430 	    case AUDIOAMD_RECORD_SOURCE:
431 		    if (cp->un.ord != AUDIOAMD_MIC_VOL)
432 			    return EINVAL;
433 		    break;
434 	    case AUDIOAMD_MIC_MUTE:
435 		    sc->sc_mic_mute = cp->un.ord;
436 		    break;
437 	    case AUDIOAMD_MONITOR_OUTPUT:
438 		    if (cp->un.ord != AUDIOAMD_SPEAKER_VOL &&
439 			cp->un.ord != AUDIOAMD_HEADPHONES_VOL)
440 			    return EINVAL;
441 			sc->sc_out_port = cp->un.ord;
442 		    break;
443 	    default:
444 		    return EINVAL;
445 		    /* NOTREACHED */
446 	}
447 	return 0;
448 }
449 
450 int
451 am7930_get_port(void *addr, mixer_ctrl_t *cp)
452 {
453 	struct am7930_softc *sc;
454 
455 	DPRINTF(("am7930_get_port: port=%d\n", cp->dev));
456 	sc = addr;
457 	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
458 		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
459 		cp->dev == AUDIOAMD_MIC_MUTE) {
460 		if (cp->type != AUDIO_MIXER_ENUM)
461 			return EINVAL;
462 	} else if (cp->type != AUDIO_MIXER_VALUE ||
463 		cp->un.value.num_channels != 1) {
464 		return EINVAL;
465 	}
466 
467 	switch(cp->dev) {
468 	    case AUDIOAMD_MIC_VOL:
469 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
470 		    break;
471 	    case AUDIOAMD_SPEAKER_VOL:
472 	    case AUDIOAMD_HEADPHONES_VOL:
473 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
474 		    break;
475 	    case AUDIOAMD_MONITOR_VOL:
476 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
477 		    break;
478 	    case AUDIOAMD_RECORD_SOURCE:
479 		    cp->un.ord = AUDIOAMD_MIC_VOL;
480 		    break;
481 	    case AUDIOAMD_MIC_MUTE:
482 		    cp->un.ord = sc->sc_mic_mute;
483 		    break;
484 	    case AUDIOAMD_MONITOR_OUTPUT:
485 		    cp->un.ord = sc->sc_out_port;
486 		    break;
487 	    default:
488 		    return EINVAL;
489 		    /* NOTREACHED */
490 	}
491 	return 0;
492 }
493 
494 
495 /*
496  * Define mixer control facilities.
497  */
498 int
499 am7930_query_devinfo(void *addr, mixer_devinfo_t *dip)
500 {
501 
502 	DPRINTF(("am7930_query_devinfo()\n"));
503 
504 	switch(dip->index) {
505 	case AUDIOAMD_MIC_VOL:
506 		dip->type = AUDIO_MIXER_VALUE;
507 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
508 		dip->prev =  AUDIO_MIXER_LAST;
509 		dip->next = AUDIOAMD_MIC_MUTE;
510 		strcpy(dip->label.name, AudioNmicrophone);
511 		dip->un.v.num_channels = 1;
512 		strcpy(dip->un.v.units.name, AudioNvolume);
513 		break;
514 	case AUDIOAMD_SPEAKER_VOL:
515 		dip->type = AUDIO_MIXER_VALUE;
516 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
517 		dip->prev = dip->next = AUDIO_MIXER_LAST;
518 		strcpy(dip->label.name, AudioNspeaker);
519 		dip->un.v.num_channels = 1;
520 		strcpy(dip->un.v.units.name, AudioNvolume);
521 		break;
522 	case AUDIOAMD_HEADPHONES_VOL:
523 		dip->type = AUDIO_MIXER_VALUE;
524 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
525 		dip->prev = dip->next = AUDIO_MIXER_LAST;
526 		strcpy(dip->label.name, AudioNheadphone);
527 		dip->un.v.num_channels = 1;
528 		strcpy(dip->un.v.units.name, AudioNvolume);
529 		break;
530 	case AUDIOAMD_MONITOR_VOL:
531 		dip->type = AUDIO_MIXER_VALUE;
532 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
533 		dip->prev = dip->next = AUDIO_MIXER_LAST;
534 		strcpy(dip->label.name, AudioNmonitor);
535 		dip->un.v.num_channels = 1;
536 		strcpy(dip->un.v.units.name, AudioNvolume);
537 		break;
538 	case AUDIOAMD_RECORD_SOURCE:
539 		dip->type = AUDIO_MIXER_ENUM;
540 		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
541 		dip->next = dip->prev = AUDIO_MIXER_LAST;
542 		strcpy(dip->label.name, AudioNsource);
543 		dip->un.e.num_mem = 1;
544 		strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
545 		dip->un.e.member[0].ord = AUDIOAMD_MIC_VOL;
546 		break;
547 	case AUDIOAMD_MONITOR_OUTPUT:
548 		dip->type = AUDIO_MIXER_ENUM;
549 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
550 		dip->next = dip->prev = AUDIO_MIXER_LAST;
551 		strcpy(dip->label.name, AudioNoutput);
552 		dip->un.e.num_mem = 2;
553 		strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
554 		dip->un.e.member[0].ord = AUDIOAMD_SPEAKER_VOL;
555 		strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
556 		dip->un.e.member[1].ord = AUDIOAMD_HEADPHONES_VOL;
557 		break;
558 	case AUDIOAMD_MIC_MUTE:
559 		dip->type = AUDIO_MIXER_ENUM;
560 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
561 		dip->prev =  AUDIOAMD_MIC_VOL;
562 		dip->next = AUDIO_MIXER_LAST;
563 		strcpy(dip->label.name, AudioNmute);
564 		dip->un.e.num_mem = 2;
565 		strcpy(dip->un.e.member[0].label.name, AudioNoff);
566 		dip->un.e.member[0].ord = 0;
567 		strcpy(dip->un.e.member[1].label.name, AudioNon);
568 		dip->un.e.member[1].ord = 1;
569 		break;
570 	case AUDIOAMD_INPUT_CLASS:
571 		dip->type = AUDIO_MIXER_CLASS;
572 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
573 		dip->next = dip->prev = AUDIO_MIXER_LAST;
574 		strcpy(dip->label.name, AudioCinputs);
575 		break;
576 	case AUDIOAMD_OUTPUT_CLASS:
577 		dip->type = AUDIO_MIXER_CLASS;
578 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
579 		dip->next = dip->prev = AUDIO_MIXER_LAST;
580 		strcpy(dip->label.name, AudioCoutputs);
581 		break;
582 	case AUDIOAMD_RECORD_CLASS:
583 		dip->type = AUDIO_MIXER_CLASS;
584 		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
585 		dip->next = dip->prev = AUDIO_MIXER_LAST;
586 		strcpy(dip->label.name, AudioCrecord);
587 		break;
588 	case AUDIOAMD_MONITOR_CLASS:
589 		dip->type = AUDIO_MIXER_CLASS;
590 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
591 		dip->next = dip->prev = AUDIO_MIXER_LAST;
592 		strcpy(dip->label.name, AudioCmonitor);
593 		break;
594 	default:
595 		return ENXIO;
596 		/*NOTREACHED*/
597 	}
598 
599 	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
600 
601 	return 0;
602 }
603 
604 #endif	/* NAUDIO */
605