xref: /netbsd-src/sys/dev/ic/am7930.c (revision 9616dacfef448e70e3fbbd865bddf60d54b656c5)
1 /*	$NetBSD: am7930.c,v 1.53 2016/12/28 10:04:53 nat 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.53 2016/12/28 10:04:53 nat 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 
141 /*
142  * Reset chip and set boot-time softc defaults.
143  */
144 void
145 am7930_init(struct am7930_softc *sc, int flag)
146 {
147 
148 	DPRINTF(("am7930_init()\n"));
149 
150 	/* set boot defaults */
151 	sc->sc_rlevel = 128;
152 	sc->sc_plevel = 128;
153 	sc->sc_mlevel = 0;
154 	sc->sc_out_port = AUDIOAMD_SPEAKER_VOL;
155 	sc->sc_mic_mute = 0;
156 
157 	/* disable sample interrupts */
158 	AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4, 0);
159 
160 	/* initialise voice and data, and disable interrupts */
161 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
162 		AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
163 
164 	if (flag == AUDIOAMD_DMA_MODE) {
165 
166 		/* configure PP for serial (SBP) mode */
167 		AM7930_IWRITE(sc, AM7930_IREG_PP_PPCR1, AM7930_PPCR1_SBP);
168 
169 		/*
170 		 * Initialise the MUX unit - route the MAP to the PP
171 		 */
172 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1,
173 			(AM7930_MCRCHAN_BA << 4) | AM7930_MCRCHAN_BD);
174 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, AM7930_MCRCHAN_NC);
175 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3, AM7930_MCRCHAN_NC);
176 
177 	} else {
178 
179 		/*
180 		 * Initialize the MUX unit.  We use MCR3 to route the MAP
181 		 * through channel Bb.  MCR1 and MCR2 are unused.
182 		 * Setting the INT enable bit in MCR4 will generate an
183 		 * interrupt on each converted audio sample.
184 		 */
185 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1, 0);
186 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, 0);
187 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3,
188 			(AM7930_MCRCHAN_BB << 4) | AM7930_MCRCHAN_BA);
189 		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4,
190 			AM7930_MCR4_INT_ENABLE);
191 	}
192 
193 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
194 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
195 }
196 
197 int
198 am7930_open(void *addr, int flags)
199 {
200 	struct am7930_softc *sc;
201 
202 	sc = addr;
203 	DPRINTF(("sa_open: unit %p\n", sc));
204 	sc->sc_glue->onopen(sc);
205 	DPRINTF(("saopen: ok -> sc=%p\n",sc));
206 	return 0;
207 }
208 
209 void
210 am7930_close(void *addr)
211 {
212 	struct am7930_softc *sc;
213 
214 	sc = addr;
215 	DPRINTF(("sa_close: sc=%p\n", sc));
216 	sc->sc_glue->onclose(sc);
217 	DPRINTF(("sa_close: closed.\n"));
218 }
219 
220 /*
221  * XXX should be extended to handle a few of the more common formats.
222  */
223 int
224 am7930_set_params(void *addr, int setmode, int usemode, audio_params_t *p,
225     audio_params_t *r, stream_filter_list_t *pfil, stream_filter_list_t *rfil)
226 {
227 	audio_params_t hw;
228 	struct am7930_softc *sc;
229 
230 	sc = addr;
231 	if ((usemode & AUMODE_PLAY) == AUMODE_PLAY) {
232 		if (p->sample_rate < 7500 || p->sample_rate > 8500 ||
233 			(p->encoding != AUDIO_ENCODING_ULAW &&
234 			 p->encoding != AUDIO_ENCODING_SLINEAR) ||
235 			p->precision != 8 ||
236 			p->channels != 1)
237 				return EINVAL;
238 		p->sample_rate = 8000;
239 		if (sc->sc_glue->output_conv != NULL) {
240 			hw = *p;
241 			hw.encoding = AUDIO_ENCODING_NONE;
242 			hw.precision *= sc->sc_glue->factor;
243 			pfil->append(pfil, sc->sc_glue->output_conv, &hw);
244 		}
245 	}
246 	if ((usemode & AUMODE_RECORD) == AUMODE_RECORD) {
247 		if (r->sample_rate < 7500 || r->sample_rate > 8500 ||
248 			(r->encoding != AUDIO_ENCODING_ULAW &&
249 			 r->encoding != AUDIO_ENCODING_SLINEAR) ||
250 			r->precision != 8 ||
251 			r->channels != 1)
252 				return EINVAL;
253 		r->sample_rate = 8000;
254 		if (sc->sc_glue->input_conv != NULL) {
255 			hw = *r;
256 			hw.encoding = AUDIO_ENCODING_NONE;
257 			hw.precision *= sc->sc_glue->factor;
258 			pfil->append(rfil, sc->sc_glue->input_conv, &hw);
259 		}
260 	}
261 
262 	if (p->encoding == AUDIO_ENCODING_SLINEAR ||
263 	    r->encoding == AUDIO_ENCODING_SLINEAR) {
264 		hw.encoding = AUDIO_ENCODING_ULAW;
265 		pfil->req_size = rfil->req_size = 0;
266 		pfil->append(rfil, mulaw_to_linear8, &hw);
267 		rfil->append(pfil, linear8_to_mulaw, &hw);
268 	}
269 
270 	return 0;
271 }
272 
273 int
274 am7930_query_encoding(void *addr, struct audio_encoding *fp)
275 {
276 	switch (fp->index) {
277 	case 0:
278 		strcpy(fp->name, AudioEmulaw);
279 		fp->encoding = AUDIO_ENCODING_ULAW;
280 		fp->precision = 8;
281 		fp->flags = 0;
282 		break;
283 	case 1:
284 		strcpy(fp->name, AudioEslinear);
285 		fp->encoding = AUDIO_ENCODING_SLINEAR;
286 		fp->precision = 8;
287 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
288 		break;
289 	default:
290 		return EINVAL;
291 		    /*NOTREACHED*/
292 	}
293 	return 0;
294 }
295 
296 int
297 am7930_round_blocksize(void *addr, int blk,
298     int mode, const audio_params_t *param)
299 {
300 	return blk;
301 }
302 
303 int
304 am7930_commit_settings(void *addr)
305 {
306 	struct am7930_softc *sc;
307 	uint16_t ger, gr, gx, stgr;
308 	uint8_t mmr2, mmr3;
309 	int level;
310 
311 	DPRINTF(("sa_commit.\n"));
312 	sc = addr;
313 	gx = gx_coeff[sc->sc_rlevel];
314 	stgr = gx_coeff[sc->sc_mlevel];
315 
316 	level = (sc->sc_plevel * (256 + NGER)) >> 8;
317 	if (level >= 256) {
318 		ger = ger_coeff[level - 256];
319 		gr = gx_coeff[255];
320 	} else {
321 		ger = ger_coeff[0];
322 		gr = gx_coeff[level];
323 	}
324 
325 	mutex_enter(&sc->sc_intr_lock);
326 
327 	mmr2 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR2);
328 	if (sc->sc_out_port == AUDIOAMD_SPEAKER_VOL)
329 		mmr2 |= AM7930_MMR2_LS;
330 	else
331 		mmr2 &= ~AM7930_MMR2_LS;
332 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR2, mmr2);
333 
334 	mmr3 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR3);
335 	if (sc->sc_mic_mute)
336 		mmr3 |= AM7930_MMR3_MUTE;
337 	else
338 		mmr3 &= ~AM7930_MMR3_MUTE;
339 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR3, mmr3);
340 
341 	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR1,
342 		AM7930_MMR1_GX | AM7930_MMR1_GER |
343 		AM7930_MMR1_GR | AM7930_MMR1_STG);
344 
345 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GX, gx);
346 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_STG, stgr);
347 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GR, gr);
348 	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GER, ger);
349 
350 	mutex_exit(&sc->sc_intr_lock);
351 
352 	return 0;
353 }
354 
355 int
356 am7930_halt_output(void *addr)
357 {
358 	struct am7930_softc *sc;
359 
360 	sc = addr;
361 	/* XXX only halt, if input is also halted ?? */
362 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
363 	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
364 	return 0;
365 }
366 
367 int
368 am7930_halt_input(void *addr)
369 {
370 	struct am7930_softc *sc;
371 
372 	sc = addr;
373 	/* XXX only halt, if output is also halted ?? */
374 	AM7930_IWRITE(sc, AM7930_IREG_INIT,
375 	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
376 	return 0;
377 }
378 
379 /*
380  * XXX chip is full-duplex, but really attach-dependent.
381  * For now we know of no half-duplex attachments.
382  */
383 int
384 am7930_get_props(void *addr)
385 {
386 	return AUDIO_PROP_FULLDUPLEX;
387 }
388 
389 /*
390  * Attach-dependent channel set/query
391  */
392 int
393 am7930_set_port(void *addr, mixer_ctrl_t *cp)
394 {
395 	struct am7930_softc *sc;
396 
397 	DPRINTF(("am7930_set_port: port=%d", cp->dev));
398 	sc = addr;
399 	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
400 		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
401 		cp->dev == AUDIOAMD_MIC_MUTE) {
402 		if (cp->type != AUDIO_MIXER_ENUM)
403 			return EINVAL;
404 	} else if (cp->type != AUDIO_MIXER_VALUE ||
405 	    cp->un.value.num_channels != 1) {
406 		return EINVAL;
407 	}
408 
409 	switch(cp->dev) {
410 	    case AUDIOAMD_MIC_VOL:
411 		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
412 		    break;
413 	    case AUDIOAMD_SPEAKER_VOL:
414 	    case AUDIOAMD_HEADPHONES_VOL:
415 		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
416 		    break;
417 	    case AUDIOAMD_MONITOR_VOL:
418 		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
419 		    break;
420 	    case AUDIOAMD_RECORD_SOURCE:
421 		    if (cp->un.ord != AUDIOAMD_MIC_VOL)
422 			    return EINVAL;
423 		    break;
424 	    case AUDIOAMD_MIC_MUTE:
425 		    sc->sc_mic_mute = cp->un.ord;
426 		    break;
427 	    case AUDIOAMD_MONITOR_OUTPUT:
428 		    if (cp->un.ord != AUDIOAMD_SPEAKER_VOL &&
429 			cp->un.ord != AUDIOAMD_HEADPHONES_VOL)
430 			    return EINVAL;
431 			sc->sc_out_port = cp->un.ord;
432 		    break;
433 	    default:
434 		    return EINVAL;
435 		    /* NOTREACHED */
436 	}
437 	return 0;
438 }
439 
440 int
441 am7930_get_port(void *addr, mixer_ctrl_t *cp)
442 {
443 	struct am7930_softc *sc;
444 
445 	DPRINTF(("am7930_get_port: port=%d\n", cp->dev));
446 	sc = addr;
447 	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
448 		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
449 		cp->dev == AUDIOAMD_MIC_MUTE) {
450 		if (cp->type != AUDIO_MIXER_ENUM)
451 			return EINVAL;
452 	} else if (cp->type != AUDIO_MIXER_VALUE ||
453 		cp->un.value.num_channels != 1) {
454 		return EINVAL;
455 	}
456 
457 	switch(cp->dev) {
458 	    case AUDIOAMD_MIC_VOL:
459 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
460 		    break;
461 	    case AUDIOAMD_SPEAKER_VOL:
462 	    case AUDIOAMD_HEADPHONES_VOL:
463 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
464 		    break;
465 	    case AUDIOAMD_MONITOR_VOL:
466 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
467 		    break;
468 	    case AUDIOAMD_RECORD_SOURCE:
469 		    cp->un.ord = AUDIOAMD_MIC_VOL;
470 		    break;
471 	    case AUDIOAMD_MIC_MUTE:
472 		    cp->un.ord = sc->sc_mic_mute;
473 		    break;
474 	    case AUDIOAMD_MONITOR_OUTPUT:
475 		    cp->un.ord = sc->sc_out_port;
476 		    break;
477 	    default:
478 		    return EINVAL;
479 		    /* NOTREACHED */
480 	}
481 	return 0;
482 }
483 
484 
485 /*
486  * Define mixer control facilities.
487  */
488 int
489 am7930_query_devinfo(void *addr, mixer_devinfo_t *dip)
490 {
491 
492 	DPRINTF(("am7930_query_devinfo()\n"));
493 
494 	switch(dip->index) {
495 	case AUDIOAMD_MIC_VOL:
496 		dip->type = AUDIO_MIXER_VALUE;
497 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
498 		dip->prev =  AUDIO_MIXER_LAST;
499 		dip->next = AUDIOAMD_MIC_MUTE;
500 		strcpy(dip->label.name, AudioNmicrophone);
501 		dip->un.v.num_channels = 1;
502 		strcpy(dip->un.v.units.name, AudioNvolume);
503 		break;
504 	case AUDIOAMD_SPEAKER_VOL:
505 		dip->type = AUDIO_MIXER_VALUE;
506 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
507 		dip->prev = dip->next = AUDIO_MIXER_LAST;
508 		strcpy(dip->label.name, AudioNspeaker);
509 		dip->un.v.num_channels = 1;
510 		strcpy(dip->un.v.units.name, AudioNvolume);
511 		break;
512 	case AUDIOAMD_HEADPHONES_VOL:
513 		dip->type = AUDIO_MIXER_VALUE;
514 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
515 		dip->prev = dip->next = AUDIO_MIXER_LAST;
516 		strcpy(dip->label.name, AudioNheadphone);
517 		dip->un.v.num_channels = 1;
518 		strcpy(dip->un.v.units.name, AudioNvolume);
519 		break;
520 	case AUDIOAMD_MONITOR_VOL:
521 		dip->type = AUDIO_MIXER_VALUE;
522 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
523 		dip->prev = dip->next = AUDIO_MIXER_LAST;
524 		strcpy(dip->label.name, AudioNmonitor);
525 		dip->un.v.num_channels = 1;
526 		strcpy(dip->un.v.units.name, AudioNvolume);
527 		break;
528 	case AUDIOAMD_RECORD_SOURCE:
529 		dip->type = AUDIO_MIXER_ENUM;
530 		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
531 		dip->next = dip->prev = AUDIO_MIXER_LAST;
532 		strcpy(dip->label.name, AudioNsource);
533 		dip->un.e.num_mem = 1;
534 		strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
535 		dip->un.e.member[0].ord = AUDIOAMD_MIC_VOL;
536 		break;
537 	case AUDIOAMD_MONITOR_OUTPUT:
538 		dip->type = AUDIO_MIXER_ENUM;
539 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
540 		dip->next = dip->prev = AUDIO_MIXER_LAST;
541 		strcpy(dip->label.name, AudioNoutput);
542 		dip->un.e.num_mem = 2;
543 		strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
544 		dip->un.e.member[0].ord = AUDIOAMD_SPEAKER_VOL;
545 		strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
546 		dip->un.e.member[1].ord = AUDIOAMD_HEADPHONES_VOL;
547 		break;
548 	case AUDIOAMD_MIC_MUTE:
549 		dip->type = AUDIO_MIXER_ENUM;
550 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
551 		dip->prev =  AUDIOAMD_MIC_VOL;
552 		dip->next = AUDIO_MIXER_LAST;
553 		strcpy(dip->label.name, AudioNmute);
554 		dip->un.e.num_mem = 2;
555 		strcpy(dip->un.e.member[0].label.name, AudioNoff);
556 		dip->un.e.member[0].ord = 0;
557 		strcpy(dip->un.e.member[1].label.name, AudioNon);
558 		dip->un.e.member[1].ord = 1;
559 		break;
560 	case AUDIOAMD_INPUT_CLASS:
561 		dip->type = AUDIO_MIXER_CLASS;
562 		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
563 		dip->next = dip->prev = AUDIO_MIXER_LAST;
564 		strcpy(dip->label.name, AudioCinputs);
565 		break;
566 	case AUDIOAMD_OUTPUT_CLASS:
567 		dip->type = AUDIO_MIXER_CLASS;
568 		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
569 		dip->next = dip->prev = AUDIO_MIXER_LAST;
570 		strcpy(dip->label.name, AudioCoutputs);
571 		break;
572 	case AUDIOAMD_RECORD_CLASS:
573 		dip->type = AUDIO_MIXER_CLASS;
574 		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
575 		dip->next = dip->prev = AUDIO_MIXER_LAST;
576 		strcpy(dip->label.name, AudioCrecord);
577 		break;
578 	case AUDIOAMD_MONITOR_CLASS:
579 		dip->type = AUDIO_MIXER_CLASS;
580 		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
581 		dip->next = dip->prev = AUDIO_MIXER_LAST;
582 		strcpy(dip->label.name, AudioCmonitor);
583 		break;
584 	default:
585 		return ENXIO;
586 		/*NOTREACHED*/
587 	}
588 
589 	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
590 
591 	return 0;
592 }
593 
594 #endif	/* NAUDIO */
595