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