1223534Shselasky/*	$NetBSD$	*/
2223534Shselasky
3223534Shselasky/*
4223534Shselasky * Copyright (c) 1995 Rolf Grossmann
5223534Shselasky * All rights reserved.
6223534Shselasky *
7223534Shselasky * Redistribution and use in source and binary forms, with or without
8223534Shselasky * modification, are permitted provided that the following conditions
9223534Shselasky * are met:
10223534Shselasky * 1. Redistributions of source code must retain the above copyright
11223534Shselasky *    notice, this list of conditions and the following disclaimer.
12223534Shselasky * 2. Redistributions in binary form must reproduce the above copyright
13223534Shselasky *    notice, this list of conditions and the following disclaimer in the
14223534Shselasky *    documentation and/or other materials provided with the distribution.
15223534Shselasky * 3. All advertising materials mentioning features or use of this software
16223534Shselasky *    must display the following acknowledgement:
17223534Shselasky *      This product includes software developed by Rolf Grossmann.
18223534Shselasky * 4. The name of the author may not be used to endorse or promote products
19223534Shselasky *    derived from this software without specific prior written permission
20223534Shselasky *
21223534Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22223534Shselasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23223534Shselasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24223534Shselasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25223534Shselasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26223534Shselasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27223534Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28223534Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29223534Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30223534Shselasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31223534Shselasky */
32223534Shselasky
33223534Shselasky/*
34223534Shselasky * Front-end attachment independent layer for AMD 79c30
35223534Shselasky * audio driver.  No ISDN support.
36223534Shselasky */
37223534Shselasky
38223534Shselasky#include <sys/cdefs.h>
39223534Shselasky__KERNEL_RCSID(0, "$NetBSD$");
40223534Shselasky
41223534Shselasky#include "audio.h"
42223534Shselasky#if NAUDIO > 0
43223534Shselasky
44223534Shselasky#include <sys/param.h>
45223534Shselasky#include <sys/systm.h>
46223534Shselasky#include <sys/errno.h>
47223534Shselasky#include <sys/ioctl.h>
48223534Shselasky#include <sys/device.h>
49223534Shselasky#include <sys/proc.h>
50223534Shselasky
51223534Shselasky#include <sys/bus.h>
52223534Shselasky#include <machine/autoconf.h>
53223534Shselasky#include <sys/cpu.h>
54223534Shselasky
55223534Shselasky#include <sys/audioio.h>
56223534Shselasky#include <dev/audio_if.h>
57223534Shselasky
58223534Shselasky#include <dev/ic/am7930reg.h>
59223534Shselasky#include <dev/ic/am7930var.h>
60223534Shselasky
61223534Shselasky#ifdef AUDIO_DEBUG
62223534Shselaskyint     am7930debug = 0;
63223534Shselasky#define DPRINTF(x)      if (am7930debug) printf x
64223534Shselasky#else
65223534Shselasky#define DPRINTF(x)
66223534Shselasky#endif
67223534Shselasky
68223534Shselasky
69223534Shselasky/* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
70223534Shselasky
71223534Shselasky/*
72223534Shselasky * gx, gr & stg gains.  this table must contain 256 elements with
73223534Shselasky * the 0th being "infinity" (the magic value 9008).  The remaining
74223534Shselasky * elements match sun's gain curve (but with higher resolution):
75223534Shselasky * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
76223534Shselasky */
77223534Shselaskystatic const uint16_t gx_coeff[256] = {
78223534Shselasky	0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
79223534Shselasky	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
80223534Shselasky	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
81223534Shselasky	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
82223534Shselasky	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
83223534Shselasky	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
84223534Shselasky	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
85223534Shselasky	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
86223534Shselasky	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
87223534Shselasky	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
88223534Shselasky	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
89223534Shselasky	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
90223534Shselasky	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
91223534Shselasky	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
92223534Shselasky	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
93223534Shselasky	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
94223534Shselasky	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
95223534Shselasky	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
96223534Shselasky	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
97223534Shselasky	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
98223534Shselasky	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
99223534Shselasky	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
100223534Shselasky	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
101223534Shselasky	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
102223534Shselasky	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
103223534Shselasky	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
104223534Shselasky	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
105223534Shselasky	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
106223534Shselasky	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
107223534Shselasky	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
108223534Shselasky	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
109223534Shselasky	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
110223534Shselasky};
111223534Shselasky
112223534Shselasky/*
113223534Shselasky * second stage play gain.
114223534Shselasky */
115223534Shselaskystatic const uint16_t ger_coeff[] = {
116223534Shselasky	0x431f, /* 5. dB */
117223534Shselasky	0x331f, /* 5.5 dB */
118223534Shselasky	0x40dd, /* 6. dB */
119223534Shselasky	0x11dd, /* 6.5 dB */
120223534Shselasky	0x440f, /* 7. dB */
121223534Shselasky	0x411f, /* 7.5 dB */
122223534Shselasky	0x311f, /* 8. dB */
123223534Shselasky	0x5520, /* 8.5 dB */
124223534Shselasky	0x10dd, /* 9. dB */
125223534Shselasky	0x4211, /* 9.5 dB */
126223534Shselasky	0x410f, /* 10. dB */
127223534Shselasky	0x111f, /* 10.5 dB */
128223534Shselasky	0x600b, /* 11. dB */
129223534Shselasky	0x00dd, /* 11.5 dB */
130223534Shselasky	0x4210, /* 12. dB */
131223534Shselasky	0x110f, /* 13. dB */
132223534Shselasky	0x7200, /* 14. dB */
133223534Shselasky	0x2110, /* 15. dB */
134223534Shselasky	0x2200, /* 15.9 dB */
135223534Shselasky	0x000b, /* 16.9 dB */
136223534Shselasky	0x000f  /* 18. dB */
137223534Shselasky#define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
138223534Shselasky};
139223534Shselasky
140223534Shselasky
141223534Shselasky/*
142223534Shselasky * Reset chip and set boot-time softc defaults.
143223534Shselasky */
144223534Shselaskyvoid
145223534Shselaskyam7930_init(struct am7930_softc *sc, int flag)
146223534Shselasky{
147223534Shselasky
148223534Shselasky	DPRINTF(("am7930_init()\n"));
149223534Shselasky
150223534Shselasky	/* set boot defaults */
151223534Shselasky	sc->sc_rlevel = 128;
152223534Shselasky	sc->sc_plevel = 128;
153223534Shselasky	sc->sc_mlevel = 0;
154223534Shselasky	sc->sc_out_port = AUDIOAMD_SPEAKER_VOL;
155223534Shselasky	sc->sc_mic_mute = 0;
156223534Shselasky
157223534Shselasky	/* disable sample interrupts */
158223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4, 0);
159223534Shselasky
160223534Shselasky	/* initialise voice and data, and disable interrupts */
161223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_INIT,
162223534Shselasky		AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
163223534Shselasky
164223534Shselasky	if (flag == AUDIOAMD_DMA_MODE) {
165223534Shselasky
166223534Shselasky		/* configure PP for serial (SBP) mode */
167223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_PP_PPCR1, AM7930_PPCR1_SBP);
168223534Shselasky
169223534Shselasky		/*
170223534Shselasky		 * Initialise the MUX unit - route the MAP to the PP
171223534Shselasky		 */
172223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1,
173223534Shselasky			(AM7930_MCRCHAN_BA << 4) | AM7930_MCRCHAN_BD);
174223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, AM7930_MCRCHAN_NC);
175223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3, AM7930_MCRCHAN_NC);
176223534Shselasky
177223534Shselasky	} else {
178223534Shselasky
179223534Shselasky		/*
180223534Shselasky		 * Initialize the MUX unit.  We use MCR3 to route the MAP
181223534Shselasky		 * through channel Bb.  MCR1 and MCR2 are unused.
182223534Shselasky		 * Setting the INT enable bit in MCR4 will generate an
183223534Shselasky		 * interrupt on each converted audio sample.
184223534Shselasky		 */
185223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1, 0);
186223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, 0);
187223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3,
188223534Shselasky			(AM7930_MCRCHAN_BB << 4) | AM7930_MCRCHAN_BA);
189223534Shselasky		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4,
190223534Shselasky			AM7930_MCR4_INT_ENABLE);
191223534Shselasky	}
192223534Shselasky
193223534Shselasky	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
194223534Shselasky	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
195223534Shselasky}
196223534Shselasky
197223534Shselaskyint
198223534Shselaskyam7930_open(void *addr, int flags)
199223534Shselasky{
200223534Shselasky	struct am7930_softc *sc;
201223534Shselasky
202223534Shselasky	sc = addr;
203223534Shselasky	DPRINTF(("sa_open: unit %p\n", sc));
204223534Shselasky	sc->sc_glue->onopen(sc);
205223534Shselasky	DPRINTF(("saopen: ok -> sc=%p\n",sc));
206223534Shselasky	return 0;
207223534Shselasky}
208223534Shselasky
209223534Shselaskyvoid
210223534Shselaskyam7930_close(void *addr)
211223534Shselasky{
212223534Shselasky	struct am7930_softc *sc;
213223534Shselasky
214223534Shselasky	sc = addr;
215223534Shselasky	DPRINTF(("sa_close: sc=%p\n", sc));
216223534Shselasky	sc->sc_glue->onclose(sc);
217223534Shselasky	DPRINTF(("sa_close: closed.\n"));
218223534Shselasky}
219223534Shselasky
220223534Shselasky/*
221223534Shselasky * XXX should be extended to handle a few of the more common formats.
222223534Shselasky */
223223534Shselaskyint
224223534Shselaskyam7930_set_params(void *addr, int setmode, int usemode, audio_params_t *p,
225223534Shselasky    audio_params_t *r, stream_filter_list_t *pfil, stream_filter_list_t *rfil)
226223534Shselasky{
227223534Shselasky	audio_params_t hw;
228223534Shselasky	struct am7930_softc *sc;
229223534Shselasky
230223534Shselasky	sc = addr;
231223534Shselasky	if ((usemode & AUMODE_PLAY) == AUMODE_PLAY) {
232223534Shselasky		if (p->sample_rate < 7500 || p->sample_rate > 8500 ||
233223534Shselasky			p->encoding != AUDIO_ENCODING_ULAW ||
234223534Shselasky			p->precision != 8 ||
235223534Shselasky			p->channels != 1)
236223534Shselasky				return EINVAL;
237223534Shselasky		p->sample_rate = 8000;
238223534Shselasky		if (sc->sc_glue->output_conv != NULL) {
239223534Shselasky			hw = *p;
240223534Shselasky			hw.encoding = AUDIO_ENCODING_NONE;
241223534Shselasky			hw.precision *= sc->sc_glue->factor;
242223534Shselasky			pfil->append(pfil, sc->sc_glue->output_conv, &hw);
243223534Shselasky		}
244223534Shselasky	}
245223534Shselasky	if ((usemode & AUMODE_RECORD) == AUMODE_RECORD) {
246223534Shselasky		if (r->sample_rate < 7500 || r->sample_rate > 8500 ||
247223534Shselasky			r->encoding != AUDIO_ENCODING_ULAW ||
248223534Shselasky			r->precision != 8 ||
249223534Shselasky			r->channels != 1)
250223534Shselasky				return EINVAL;
251223534Shselasky		r->sample_rate = 8000;
252223534Shselasky		if (sc->sc_glue->input_conv != NULL) {
253223534Shselasky			hw = *r;
254223534Shselasky			hw.encoding = AUDIO_ENCODING_NONE;
255223534Shselasky			hw.precision *= sc->sc_glue->factor;
256223534Shselasky			pfil->append(rfil, sc->sc_glue->input_conv, &hw);
257223534Shselasky		}
258223534Shselasky	}
259223534Shselasky
260223534Shselasky	return 0;
261223534Shselasky}
262223534Shselasky
263223534Shselaskyint
264223534Shselaskyam7930_query_encoding(void *addr, struct audio_encoding *fp)
265223534Shselasky{
266223534Shselasky	switch (fp->index) {
267223534Shselasky	case 0:
268223534Shselasky		strcpy(fp->name, AudioEmulaw);
269223534Shselasky		fp->encoding = AUDIO_ENCODING_ULAW;
270223534Shselasky		fp->precision = 8;
271223534Shselasky		fp->flags = 0;
272223534Shselasky		break;
273223534Shselasky	default:
274223534Shselasky		return EINVAL;
275223534Shselasky		    /*NOTREACHED*/
276223534Shselasky	}
277223534Shselasky	return 0;
278223534Shselasky}
279223535Shselasky
280223535Shselaskyint
281223535Shselaskyam7930_round_blocksize(void *addr, int blk,
282223535Shselasky    int mode, const audio_params_t *param)
283223534Shselasky{
284223534Shselasky	return blk;
285223534Shselasky}
286223534Shselasky
287223534Shselaskyint
288223534Shselaskyam7930_commit_settings(void *addr)
289223534Shselasky{
290223534Shselasky	struct am7930_softc *sc;
291223534Shselasky	uint16_t ger, gr, gx, stgr;
292223534Shselasky	uint8_t mmr2, mmr3;
293223534Shselasky	int level;
294223534Shselasky
295223534Shselasky	DPRINTF(("sa_commit.\n"));
296223534Shselasky	sc = addr;
297223534Shselasky	gx = gx_coeff[sc->sc_rlevel];
298223534Shselasky	stgr = gx_coeff[sc->sc_mlevel];
299223534Shselasky
300223534Shselasky	level = (sc->sc_plevel * (256 + NGER)) >> 8;
301223534Shselasky	if (level >= 256) {
302223534Shselasky		ger = ger_coeff[level - 256];
303223534Shselasky		gr = gx_coeff[255];
304223534Shselasky	} else {
305223534Shselasky		ger = ger_coeff[0];
306223534Shselasky		gr = gx_coeff[level];
307223534Shselasky	}
308223534Shselasky
309223534Shselasky	mutex_enter(&sc->sc_intr_lock);
310223534Shselasky
311223534Shselasky	mmr2 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR2);
312223534Shselasky	if (sc->sc_out_port == AUDIOAMD_SPEAKER_VOL)
313223534Shselasky		mmr2 |= AM7930_MMR2_LS;
314223534Shselasky	else
315223534Shselasky		mmr2 &= ~AM7930_MMR2_LS;
316223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR2, mmr2);
317223534Shselasky
318223534Shselasky	mmr3 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR3);
319223534Shselasky	if (sc->sc_mic_mute)
320233110Shselasky		mmr3 |= AM7930_MMR3_MUTE;
321223534Shselasky	else
322223534Shselasky		mmr3 &= ~AM7930_MMR3_MUTE;
323223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR3, mmr3);
324223534Shselasky
325223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR1,
326223534Shselasky		AM7930_MMR1_GX | AM7930_MMR1_GER |
327223534Shselasky		AM7930_MMR1_GR | AM7930_MMR1_STG);
328223534Shselasky
329223534Shselasky	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GX, gx);
330223534Shselasky	AM7930_IWRITE16(sc, AM7930_IREG_MAP_STG, stgr);
331223534Shselasky	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GR, gr);
332223534Shselasky	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GER, ger);
333223534Shselasky
334223534Shselasky	mutex_exit(&sc->sc_intr_lock);
335223534Shselasky
336223534Shselasky	return 0;
337223534Shselasky}
338223534Shselasky
339223534Shselaskyint
340223534Shselaskyam7930_halt_output(void *addr)
341223534Shselasky{
342223534Shselasky	struct am7930_softc *sc;
343223534Shselasky
344223534Shselasky	sc = addr;
345223534Shselasky	/* XXX only halt, if input is also halted ?? */
346223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_INIT,
347223534Shselasky	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
348223534Shselasky	return 0;
349223534Shselasky}
350223534Shselasky
351223534Shselaskyint
352223534Shselaskyam7930_halt_input(void *addr)
353223534Shselasky{
354223534Shselasky	struct am7930_softc *sc;
355223534Shselasky
356223534Shselasky	sc = addr;
357223534Shselasky	/* XXX only halt, if output is also halted ?? */
358223534Shselasky	AM7930_IWRITE(sc, AM7930_IREG_INIT,
359223534Shselasky	    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
360223534Shselasky	return 0;
361223534Shselasky}
362223534Shselasky
363223534Shselasky/*
364223534Shselasky * XXX chip is full-duplex, but really attach-dependent.
365223534Shselasky * For now we know of no half-duplex attachments.
366223534Shselasky */
367223534Shselaskyint
368223534Shselaskyam7930_get_props(void *addr)
369223534Shselasky{
370223534Shselasky	return AUDIO_PROP_FULLDUPLEX;
371223534Shselasky}
372223534Shselasky
373223534Shselasky/*
374223534Shselasky * Attach-dependent channel set/query
375223534Shselasky */
376223534Shselaskyint
377223534Shselaskyam7930_set_port(void *addr, mixer_ctrl_t *cp)
378223534Shselasky{
379223534Shselasky	struct am7930_softc *sc;
380223534Shselasky
381223534Shselasky	DPRINTF(("am7930_set_port: port=%d", cp->dev));
382223534Shselasky	sc = addr;
383223534Shselasky	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
384223534Shselasky		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
385223534Shselasky		cp->dev == AUDIOAMD_MIC_MUTE) {
386223534Shselasky		if (cp->type != AUDIO_MIXER_ENUM)
387			return EINVAL;
388	} else if (cp->type != AUDIO_MIXER_VALUE ||
389	    cp->un.value.num_channels != 1) {
390		return EINVAL;
391	}
392
393	switch(cp->dev) {
394	    case AUDIOAMD_MIC_VOL:
395		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
396		    break;
397	    case AUDIOAMD_SPEAKER_VOL:
398	    case AUDIOAMD_HEADPHONES_VOL:
399		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
400		    break;
401	    case AUDIOAMD_MONITOR_VOL:
402		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
403		    break;
404	    case AUDIOAMD_RECORD_SOURCE:
405		    if (cp->un.ord != AUDIOAMD_MIC_VOL)
406			    return EINVAL;
407		    break;
408	    case AUDIOAMD_MIC_MUTE:
409		    sc->sc_mic_mute = cp->un.ord;
410		    break;
411	    case AUDIOAMD_MONITOR_OUTPUT:
412		    if (cp->un.ord != AUDIOAMD_SPEAKER_VOL &&
413			cp->un.ord != AUDIOAMD_HEADPHONES_VOL)
414			    return EINVAL;
415			sc->sc_out_port = cp->un.ord;
416		    break;
417	    default:
418		    return EINVAL;
419		    /* NOTREACHED */
420	}
421	return 0;
422}
423
424int
425am7930_get_port(void *addr, mixer_ctrl_t *cp)
426{
427	struct am7930_softc *sc;
428
429	DPRINTF(("am7930_get_port: port=%d\n", cp->dev));
430	sc = addr;
431	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
432		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
433		cp->dev == AUDIOAMD_MIC_MUTE) {
434		if (cp->type != AUDIO_MIXER_ENUM)
435			return EINVAL;
436	} else if (cp->type != AUDIO_MIXER_VALUE ||
437		cp->un.value.num_channels != 1) {
438		return EINVAL;
439	}
440
441	switch(cp->dev) {
442	    case AUDIOAMD_MIC_VOL:
443		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
444		    break;
445	    case AUDIOAMD_SPEAKER_VOL:
446	    case AUDIOAMD_HEADPHONES_VOL:
447		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
448		    break;
449	    case AUDIOAMD_MONITOR_VOL:
450		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
451		    break;
452	    case AUDIOAMD_RECORD_SOURCE:
453		    cp->un.ord = AUDIOAMD_MIC_VOL;
454		    break;
455	    case AUDIOAMD_MIC_MUTE:
456		    cp->un.ord = sc->sc_mic_mute;
457		    break;
458	    case AUDIOAMD_MONITOR_OUTPUT:
459		    cp->un.ord = sc->sc_out_port;
460		    break;
461	    default:
462		    return EINVAL;
463		    /* NOTREACHED */
464	}
465	return 0;
466}
467
468
469/*
470 * Define mixer control facilities.
471 */
472int
473am7930_query_devinfo(void *addr, mixer_devinfo_t *dip)
474{
475
476	DPRINTF(("am7930_query_devinfo()\n"));
477
478	switch(dip->index) {
479	case AUDIOAMD_MIC_VOL:
480		dip->type = AUDIO_MIXER_VALUE;
481		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
482		dip->prev =  AUDIO_MIXER_LAST;
483		dip->next = AUDIOAMD_MIC_MUTE;
484		strcpy(dip->label.name, AudioNmicrophone);
485		dip->un.v.num_channels = 1;
486		strcpy(dip->un.v.units.name, AudioNvolume);
487		break;
488	case AUDIOAMD_SPEAKER_VOL:
489		dip->type = AUDIO_MIXER_VALUE;
490		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
491		dip->prev = dip->next = AUDIO_MIXER_LAST;
492		strcpy(dip->label.name, AudioNspeaker);
493		dip->un.v.num_channels = 1;
494		strcpy(dip->un.v.units.name, AudioNvolume);
495		break;
496	case AUDIOAMD_HEADPHONES_VOL:
497		dip->type = AUDIO_MIXER_VALUE;
498		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
499		dip->prev = dip->next = AUDIO_MIXER_LAST;
500		strcpy(dip->label.name, AudioNheadphone);
501		dip->un.v.num_channels = 1;
502		strcpy(dip->un.v.units.name, AudioNvolume);
503		break;
504	case AUDIOAMD_MONITOR_VOL:
505		dip->type = AUDIO_MIXER_VALUE;
506		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
507		dip->prev = dip->next = AUDIO_MIXER_LAST;
508		strcpy(dip->label.name, AudioNmonitor);
509		dip->un.v.num_channels = 1;
510		strcpy(dip->un.v.units.name, AudioNvolume);
511		break;
512	case AUDIOAMD_RECORD_SOURCE:
513		dip->type = AUDIO_MIXER_ENUM;
514		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
515		dip->next = dip->prev = AUDIO_MIXER_LAST;
516		strcpy(dip->label.name, AudioNsource);
517		dip->un.e.num_mem = 1;
518		strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
519		dip->un.e.member[0].ord = AUDIOAMD_MIC_VOL;
520		break;
521	case AUDIOAMD_MONITOR_OUTPUT:
522		dip->type = AUDIO_MIXER_ENUM;
523		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
524		dip->next = dip->prev = AUDIO_MIXER_LAST;
525		strcpy(dip->label.name, AudioNoutput);
526		dip->un.e.num_mem = 2;
527		strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
528		dip->un.e.member[0].ord = AUDIOAMD_SPEAKER_VOL;
529		strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
530		dip->un.e.member[1].ord = AUDIOAMD_HEADPHONES_VOL;
531		break;
532	case AUDIOAMD_MIC_MUTE:
533		dip->type = AUDIO_MIXER_ENUM;
534		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
535		dip->prev =  AUDIOAMD_MIC_VOL;
536		dip->next = AUDIO_MIXER_LAST;
537		strcpy(dip->label.name, AudioNmute);
538		dip->un.e.num_mem = 2;
539		strcpy(dip->un.e.member[0].label.name, AudioNoff);
540		dip->un.e.member[0].ord = 0;
541		strcpy(dip->un.e.member[1].label.name, AudioNon);
542		dip->un.e.member[1].ord = 1;
543		break;
544	case AUDIOAMD_INPUT_CLASS:
545		dip->type = AUDIO_MIXER_CLASS;
546		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
547		dip->next = dip->prev = AUDIO_MIXER_LAST;
548		strcpy(dip->label.name, AudioCinputs);
549		break;
550	case AUDIOAMD_OUTPUT_CLASS:
551		dip->type = AUDIO_MIXER_CLASS;
552		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
553		dip->next = dip->prev = AUDIO_MIXER_LAST;
554		strcpy(dip->label.name, AudioCoutputs);
555		break;
556	case AUDIOAMD_RECORD_CLASS:
557		dip->type = AUDIO_MIXER_CLASS;
558		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
559		dip->next = dip->prev = AUDIO_MIXER_LAST;
560		strcpy(dip->label.name, AudioCrecord);
561		break;
562	case AUDIOAMD_MONITOR_CLASS:
563		dip->type = AUDIO_MIXER_CLASS;
564		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
565		dip->next = dip->prev = AUDIO_MIXER_LAST;
566		strcpy(dip->label.name, AudioCmonitor);
567		break;
568	default:
569		return ENXIO;
570		/*NOTREACHED*/
571	}
572
573	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
574
575	return 0;
576}
577
578#endif	/* NAUDIO */
579