1/*	$NetBSD: am7930.c,v 1.60 2020/09/12 05:19:16 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.60 2020/09/12 05:19:16 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/atomic.h>
47#include <sys/errno.h>
48#include <sys/ioctl.h>
49#include <sys/device.h>
50#include <sys/proc.h>
51
52#include <sys/bus.h>
53#include <sys/cpu.h>
54
55#include <sys/audioio.h>
56#include <dev/audio/audio_if.h>
57#include <dev/audio/mulaw.h>
58
59#include <dev/ic/am7930reg.h>
60#include <dev/ic/am7930var.h>
61
62#ifdef AUDIO_DEBUG
63int     am7930debug = 0;
64#define DPRINTF(x)      if (am7930debug) printf x
65#else
66#define DPRINTF(x)
67#endif
68
69
70/* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
71
72/*
73 * gx, gr & stg gains.  this table must contain 256 elements with
74 * the 0th being "infinity" (the magic value 9008).  The remaining
75 * elements match sun's gain curve (but with higher resolution):
76 * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
77 */
78static const uint16_t gx_coeff[256] = {
79	0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
80	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
81	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
82	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
83	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
84	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
85	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
86	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
87	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
88	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
89	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
90	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
91	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
92	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
93	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
94	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
95	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
96	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
97	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
98	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
99	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
100	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
101	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
102	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
103	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
104	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
105	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
106	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
107	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
108	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
109	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
110	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
111};
112
113/*
114 * second stage play gain.
115 */
116static const uint16_t ger_coeff[] = {
117	0x431f, /* 5. dB */
118	0x331f, /* 5.5 dB */
119	0x40dd, /* 6. dB */
120	0x11dd, /* 6.5 dB */
121	0x440f, /* 7. dB */
122	0x411f, /* 7.5 dB */
123	0x311f, /* 8. dB */
124	0x5520, /* 8.5 dB */
125	0x10dd, /* 9. dB */
126	0x4211, /* 9.5 dB */
127	0x410f, /* 10. dB */
128	0x111f, /* 10.5 dB */
129	0x600b, /* 11. dB */
130	0x00dd, /* 11.5 dB */
131	0x4210, /* 12. dB */
132	0x110f, /* 13. dB */
133	0x7200, /* 14. dB */
134	0x2110, /* 15. dB */
135	0x2200, /* 15.9 dB */
136	0x000b, /* 16.9 dB */
137	0x000f  /* 18. dB */
138#define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
139};
140
141static const struct audio_format am7930_format = {
142	.mode		= AUMODE_PLAY | AUMODE_RECORD,
143	.encoding	= AUDIO_ENCODING_ULAW,
144	.validbits	= 8,
145	.precision	= 8,
146	.channels	= 1,
147	.channel_mask	= AUFMT_MONAURAL,
148	.frequency_type	= 1,
149	.frequency	= { 8000 },
150};
151
152/*
153 * Indirect access functions.
154 */
155
156static void
157am7930_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
158{
159
160	AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
161	AM7930_DWRITE(sc, AM7930_DREG_DR, val);
162}
163
164static uint8_t
165am7930_iread(struct am7930_softc *sc, int reg)
166{
167
168	AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
169	return AM7930_DREAD(sc, AM7930_DREG_DR);
170}
171
172static void
173am7930_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
174{
175
176	AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
177	AM7930_DWRITE(sc, AM7930_DREG_DR, val);
178	AM7930_DWRITE(sc, AM7930_DREG_DR, val >> 8);
179}
180
181static uint16_t __unused
182am7930_iread16(struct am7930_softc *sc, int reg)
183{
184	uint lo, hi;
185
186	AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
187	lo = AM7930_DREAD(sc, AM7930_DREG_DR);
188	hi = AM7930_DREAD(sc, AM7930_DREG_DR);
189	return (hi << 8) | lo;
190}
191
192#define AM7930_IWRITE(sc,r,v)	am7930_iwrite(sc,r,v)
193#define AM7930_IREAD(sc,r)	am7930_iread(sc,r)
194#define AM7930_IWRITE16(sc,r,v)	am7930_iwrite16(sc,r,v)
195#define AM7930_IREAD16(sc,r)	am7930_iread16(sc,r)
196
197/*
198 * Reset chip and set boot-time softc defaults.
199 */
200void
201am7930_init(struct am7930_softc *sc, int flag)
202{
203
204	DPRINTF(("%s\n", __func__));
205
206	/* set boot defaults */
207	sc->sc_rlevel = 128;
208	sc->sc_plevel = 128;
209	sc->sc_mlevel = 0;
210	sc->sc_out_port = AUDIOAMD_SPEAKER_VOL;
211	sc->sc_mic_mute = 0;
212
213	memset(&sc->sc_p, 0, sizeof(sc->sc_p));
214	memset(&sc->sc_r, 0, sizeof(sc->sc_r));
215
216	/* disable sample interrupts */
217	AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4, 0);
218
219	/* initialise voice and data, and disable interrupts */
220	AM7930_IWRITE(sc, AM7930_IREG_INIT,
221		AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
222
223	if (flag == AUDIOAMD_DMA_MODE) {
224
225		/* configure PP for serial (SBP) mode */
226		AM7930_IWRITE(sc, AM7930_IREG_PP_PPCR1, AM7930_PPCR1_SBP);
227
228		/*
229		 * Initialise the MUX unit - route the MAP to the PP
230		 */
231		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1,
232			(AM7930_MCRCHAN_BA << 4) | AM7930_MCRCHAN_BD);
233		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, AM7930_MCRCHAN_NC);
234		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3, AM7930_MCRCHAN_NC);
235
236		mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
237	} else {
238
239		/*
240		 * Initialize the MUX unit.  We use MCR3 to route the MAP
241		 * through channel Bb.  MCR1 and MCR2 are unused.
242		 * Setting the INT enable bit in MCR4 will generate an
243		 * interrupt on each converted audio sample.
244		 */
245		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1, 0);
246		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, 0);
247		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3,
248			(AM7930_MCRCHAN_BB << 4) | AM7930_MCRCHAN_BA);
249		AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4,
250			AM7930_MCR4_INT_ENABLE);
251
252		mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SOFTSERIAL);
253	}
254
255	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
256
257	sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, &am7930_swintr, sc);
258	if (sc->sc_sicookie == NULL) {
259		aprint_error_dev(sc->sc_dev,
260		    "cannot establish software interrupt\n");
261		return;
262	}
263}
264
265int
266am7930_query_format(void *addr, audio_format_query_t *afp)
267{
268
269	return audio_query_format(&am7930_format, 1, afp);
270}
271
272int
273am7930_set_format(void *addr, int setmode,
274	const audio_params_t *play, const audio_params_t *rec,
275	audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
276{
277
278	if ((setmode & AUMODE_PLAY) != 0) {
279		pfil->codec = audio_internal_to_mulaw;
280	}
281	if ((setmode & AUMODE_RECORD) != 0) {
282		rfil->codec = audio_mulaw_to_internal;
283	}
284
285	return 0;
286}
287
288int
289am7930_commit_settings(void *addr)
290{
291	struct am7930_softc *sc;
292	uint16_t ger, gr, gx, stgr;
293	uint8_t mmr2, mmr3;
294	int level;
295
296	DPRINTF(("%s\n", __func__));
297	sc = addr;
298	gx = gx_coeff[sc->sc_rlevel];
299	stgr = gx_coeff[sc->sc_mlevel];
300
301	level = (sc->sc_plevel * (256 + NGER)) >> 8;
302	if (level >= 256) {
303		ger = ger_coeff[level - 256];
304		gr = gx_coeff[255];
305	} else {
306		ger = ger_coeff[0];
307		gr = gx_coeff[level];
308	}
309
310	mutex_enter(&sc->sc_intr_lock);
311
312	mmr2 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR2);
313	if (sc->sc_out_port == AUDIOAMD_SPEAKER_VOL)
314		mmr2 |= AM7930_MMR2_LS;
315	else
316		mmr2 &= ~AM7930_MMR2_LS;
317	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR2, mmr2);
318
319	mmr3 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR3);
320	if (sc->sc_mic_mute)
321		mmr3 |= AM7930_MMR3_MUTE;
322	else
323		mmr3 &= ~AM7930_MMR3_MUTE;
324	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR3, mmr3);
325
326	AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR1,
327		AM7930_MMR1_GX | AM7930_MMR1_GER |
328		AM7930_MMR1_GR | AM7930_MMR1_STG);
329
330	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GX, gx);
331	AM7930_IWRITE16(sc, AM7930_IREG_MAP_STG, stgr);
332	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GR, gr);
333	AM7930_IWRITE16(sc, AM7930_IREG_MAP_GER, ger);
334
335	mutex_exit(&sc->sc_intr_lock);
336
337	return 0;
338}
339
340int
341am7930_trigger_output(void *addr, void *start, void *end, int blksize,
342    void (*intr)(void *), void *arg, const audio_params_t *params)
343{
344	struct am7930_softc *sc;
345
346	DPRINTF(("%s: blksize=%d %p(%p)\n", __func__, blksize, intr, arg));
347	sc = addr;
348	sc->sc_p.intr = intr;
349	sc->sc_p.arg = arg;
350	sc->sc_p.start = start;
351	sc->sc_p.end = end;
352	sc->sc_p.blksize = blksize;
353	sc->sc_p.data = sc->sc_p.start;
354	sc->sc_p.blkend = sc->sc_p.start + sc->sc_p.blksize;
355
356	/* Start if either play or rec start. */
357	if (sc->sc_r.intr == NULL) {
358		AM7930_IWRITE(sc, AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
359		DPRINTF(("%s: started intrs.\n", __func__));
360	}
361	return 0;
362}
363
364int
365am7930_trigger_input(void *addr, void *start, void *end, int blksize,
366    void (*intr)(void *), void *arg, const audio_params_t *params)
367{
368	struct am7930_softc *sc;
369
370	DPRINTF(("%s: blksize=%d %p(%p)\n", __func__, blksize, intr, arg));
371	sc = addr;
372	sc->sc_r.intr = intr;
373	sc->sc_r.arg = arg;
374	sc->sc_r.start = start;
375	sc->sc_r.end = end;
376	sc->sc_r.blksize = blksize;
377	sc->sc_r.data = sc->sc_r.start;
378	sc->sc_r.blkend = sc->sc_r.start + sc->sc_r.blksize;
379
380	/* Start if either play or rec start. */
381	if (sc->sc_p.intr == NULL) {
382		AM7930_IWRITE(sc, AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
383		DPRINTF(("%s: started intrs.\n", __func__));
384	}
385	return 0;
386}
387
388int
389am7930_halt_output(void *addr)
390{
391	struct am7930_softc *sc;
392
393	sc = addr;
394	sc->sc_p.intr = NULL;
395	/* Halt if both of play and rec halt. */
396	if (sc->sc_r.intr == NULL) {
397		AM7930_IWRITE(sc, AM7930_IREG_INIT,
398		    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
399	}
400	return 0;
401}
402
403int
404am7930_halt_input(void *addr)
405{
406	struct am7930_softc *sc;
407
408	sc = addr;
409	sc->sc_r.intr = NULL;
410	/* Halt if both of play and rec halt. */
411	if (sc->sc_p.intr == NULL) {
412		AM7930_IWRITE(sc, AM7930_IREG_INIT,
413		    AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
414	}
415	return 0;
416}
417
418int
419am7930_hwintr(void *arg)
420{
421	struct am7930_softc *sc;
422	int k __unused;
423
424	sc = arg;
425
426	/*
427	 * This hwintr is called as pseudo-DMA.  So don't acquire intr_lock.
428	 */
429
430	/* clear interrupt */
431	k = AM7930_DREAD(sc, AM7930_DREG_IR);
432#if !defined(__vax__)
433	/* On vax, interrupt is not shared, this shouldn't happen */
434	if ((k & (AM7930_IR_DTTHRSH | AM7930_IR_DRTHRSH | AM7930_IR_DSRI |
435	    AM7930_IR_DERI | AM7930_IR_BBUFF)) == 0) {
436		return 0;
437	}
438#endif
439
440	/* receive incoming data */
441	if (sc->sc_r.intr) {
442		*sc->sc_r.data++ = AM7930_DREAD(sc, AM7930_DREG_BBRB);
443		if (sc->sc_r.data == sc->sc_r.blkend) {
444			if (sc->sc_r.blkend == sc->sc_r.end) {
445				sc->sc_r.data = sc->sc_r.start;
446				sc->sc_r.blkend = sc->sc_r.start;
447			}
448			sc->sc_r.blkend += sc->sc_r.blksize;
449			atomic_store_relaxed(&sc->sc_r.intr_pending, 1);
450			softint_schedule(sc->sc_sicookie);
451		}
452	}
453
454	/* send outgoing data */
455	if (sc->sc_p.intr) {
456		AM7930_DWRITE(sc, AM7930_DREG_BBTB, *sc->sc_p.data++);
457		if (sc->sc_p.data == sc->sc_p.blkend) {
458			if (sc->sc_p.blkend == sc->sc_p.end) {
459				sc->sc_p.data = sc->sc_p.start;
460				sc->sc_p.blkend = sc->sc_p.start;
461			}
462			sc->sc_p.blkend += sc->sc_p.blksize;
463			atomic_store_relaxed(&sc->sc_p.intr_pending, 1);
464			softint_schedule(sc->sc_sicookie);
465		}
466	}
467
468	sc->sc_intrcnt.ev_count++;
469	return 1;
470}
471
472void
473am7930_swintr(void *cookie)
474{
475	struct am7930_softc *sc = cookie;
476
477	mutex_enter(&sc->sc_intr_lock);
478	if (atomic_cas_uint(&sc->sc_r.intr_pending, 1, 0) == 1) {
479		(*sc->sc_r.intr)(sc->sc_r.arg);
480	}
481	if (atomic_cas_uint(&sc->sc_p.intr_pending, 1, 0) == 1) {
482		(*sc->sc_p.intr)(sc->sc_p.arg);
483	}
484	mutex_exit(&sc->sc_intr_lock);
485}
486
487
488/*
489 * XXX chip is full-duplex, but really attach-dependent.
490 * For now we know of no half-duplex attachments.
491 */
492int
493am7930_get_props(void *addr)
494{
495
496	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
497	    AUDIO_PROP_FULLDUPLEX;
498}
499
500/*
501 * Attach-dependent channel set/query
502 */
503int
504am7930_set_port(void *addr, mixer_ctrl_t *cp)
505{
506	struct am7930_softc *sc;
507
508	DPRINTF(("%s: port=%d\n", __func__, cp->dev));
509	sc = addr;
510	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
511		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
512		cp->dev == AUDIOAMD_MIC_MUTE) {
513		if (cp->type != AUDIO_MIXER_ENUM)
514			return EINVAL;
515	} else if (cp->type != AUDIO_MIXER_VALUE ||
516	    cp->un.value.num_channels != 1) {
517		return EINVAL;
518	}
519
520	switch(cp->dev) {
521	    case AUDIOAMD_MIC_VOL:
522		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
523		    break;
524	    case AUDIOAMD_SPEAKER_VOL:
525	    case AUDIOAMD_HEADPHONES_VOL:
526		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
527		    break;
528	    case AUDIOAMD_MONITOR_VOL:
529		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
530		    break;
531	    case AUDIOAMD_RECORD_SOURCE:
532		    if (cp->un.ord != AUDIOAMD_MIC_VOL)
533			    return EINVAL;
534		    break;
535	    case AUDIOAMD_MIC_MUTE:
536		    sc->sc_mic_mute = cp->un.ord;
537		    break;
538	    case AUDIOAMD_MONITOR_OUTPUT:
539		    if (cp->un.ord != AUDIOAMD_SPEAKER_VOL &&
540			cp->un.ord != AUDIOAMD_HEADPHONES_VOL)
541			    return EINVAL;
542			sc->sc_out_port = cp->un.ord;
543		    break;
544	    default:
545		    return EINVAL;
546		    /* NOTREACHED */
547	}
548	return 0;
549}
550
551int
552am7930_get_port(void *addr, mixer_ctrl_t *cp)
553{
554	struct am7930_softc *sc;
555
556	DPRINTF(("%s: port=%d\n", __func__, cp->dev));
557	sc = addr;
558	if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
559		cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
560		cp->dev == AUDIOAMD_MIC_MUTE) {
561		if (cp->type != AUDIO_MIXER_ENUM)
562			return EINVAL;
563	} else if (cp->type != AUDIO_MIXER_VALUE ||
564		cp->un.value.num_channels != 1) {
565		return EINVAL;
566	}
567
568	switch(cp->dev) {
569	    case AUDIOAMD_MIC_VOL:
570		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
571		    break;
572	    case AUDIOAMD_SPEAKER_VOL:
573	    case AUDIOAMD_HEADPHONES_VOL:
574		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
575		    break;
576	    case AUDIOAMD_MONITOR_VOL:
577		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
578		    break;
579	    case AUDIOAMD_RECORD_SOURCE:
580		    cp->un.ord = AUDIOAMD_MIC_VOL;
581		    break;
582	    case AUDIOAMD_MIC_MUTE:
583		    cp->un.ord = sc->sc_mic_mute;
584		    break;
585	    case AUDIOAMD_MONITOR_OUTPUT:
586		    cp->un.ord = sc->sc_out_port;
587		    break;
588	    default:
589		    return EINVAL;
590		    /* NOTREACHED */
591	}
592	return 0;
593}
594
595
596/*
597 * Define mixer control facilities.
598 */
599int
600am7930_query_devinfo(void *addr, mixer_devinfo_t *dip)
601{
602
603	DPRINTF(("%s\n", __func__));
604
605	switch(dip->index) {
606	case AUDIOAMD_MIC_VOL:
607		dip->type = AUDIO_MIXER_VALUE;
608		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
609		dip->prev =  AUDIO_MIXER_LAST;
610		dip->next = AUDIOAMD_MIC_MUTE;
611		strcpy(dip->label.name, AudioNmicrophone);
612		dip->un.v.num_channels = 1;
613		strcpy(dip->un.v.units.name, AudioNvolume);
614		break;
615	case AUDIOAMD_SPEAKER_VOL:
616		dip->type = AUDIO_MIXER_VALUE;
617		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
618		dip->prev = dip->next = AUDIO_MIXER_LAST;
619		strcpy(dip->label.name, AudioNspeaker);
620		dip->un.v.num_channels = 1;
621		strcpy(dip->un.v.units.name, AudioNvolume);
622		break;
623	case AUDIOAMD_HEADPHONES_VOL:
624		dip->type = AUDIO_MIXER_VALUE;
625		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
626		dip->prev = dip->next = AUDIO_MIXER_LAST;
627		strcpy(dip->label.name, AudioNheadphone);
628		dip->un.v.num_channels = 1;
629		strcpy(dip->un.v.units.name, AudioNvolume);
630		break;
631	case AUDIOAMD_MONITOR_VOL:
632		dip->type = AUDIO_MIXER_VALUE;
633		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
634		dip->prev = dip->next = AUDIO_MIXER_LAST;
635		strcpy(dip->label.name, AudioNmonitor);
636		dip->un.v.num_channels = 1;
637		strcpy(dip->un.v.units.name, AudioNvolume);
638		break;
639	case AUDIOAMD_RECORD_SOURCE:
640		dip->type = AUDIO_MIXER_ENUM;
641		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
642		dip->next = dip->prev = AUDIO_MIXER_LAST;
643		strcpy(dip->label.name, AudioNsource);
644		dip->un.e.num_mem = 1;
645		strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
646		dip->un.e.member[0].ord = AUDIOAMD_MIC_VOL;
647		break;
648	case AUDIOAMD_MONITOR_OUTPUT:
649		dip->type = AUDIO_MIXER_ENUM;
650		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
651		dip->next = dip->prev = AUDIO_MIXER_LAST;
652		strcpy(dip->label.name, AudioNoutput);
653		dip->un.e.num_mem = 2;
654		strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
655		dip->un.e.member[0].ord = AUDIOAMD_SPEAKER_VOL;
656		strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
657		dip->un.e.member[1].ord = AUDIOAMD_HEADPHONES_VOL;
658		break;
659	case AUDIOAMD_MIC_MUTE:
660		dip->type = AUDIO_MIXER_ENUM;
661		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
662		dip->prev =  AUDIOAMD_MIC_VOL;
663		dip->next = AUDIO_MIXER_LAST;
664		strcpy(dip->label.name, AudioNmute);
665		dip->un.e.num_mem = 2;
666		strcpy(dip->un.e.member[0].label.name, AudioNoff);
667		dip->un.e.member[0].ord = 0;
668		strcpy(dip->un.e.member[1].label.name, AudioNon);
669		dip->un.e.member[1].ord = 1;
670		break;
671	case AUDIOAMD_INPUT_CLASS:
672		dip->type = AUDIO_MIXER_CLASS;
673		dip->mixer_class = AUDIOAMD_INPUT_CLASS;
674		dip->next = dip->prev = AUDIO_MIXER_LAST;
675		strcpy(dip->label.name, AudioCinputs);
676		break;
677	case AUDIOAMD_OUTPUT_CLASS:
678		dip->type = AUDIO_MIXER_CLASS;
679		dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
680		dip->next = dip->prev = AUDIO_MIXER_LAST;
681		strcpy(dip->label.name, AudioCoutputs);
682		break;
683	case AUDIOAMD_RECORD_CLASS:
684		dip->type = AUDIO_MIXER_CLASS;
685		dip->mixer_class = AUDIOAMD_RECORD_CLASS;
686		dip->next = dip->prev = AUDIO_MIXER_LAST;
687		strcpy(dip->label.name, AudioCrecord);
688		break;
689	case AUDIOAMD_MONITOR_CLASS:
690		dip->type = AUDIO_MIXER_CLASS;
691		dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
692		dip->next = dip->prev = AUDIO_MIXER_LAST;
693		strcpy(dip->label.name, AudioCmonitor);
694		break;
695	default:
696		return ENXIO;
697		/*NOTREACHED*/
698	}
699
700	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
701
702	return 0;
703}
704
705void
706am7930_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread)
707{
708	struct am7930_softc *sc;
709
710	sc = addr;
711	*intr = &sc->sc_intr_lock;
712	*thread = &sc->sc_lock;
713}
714
715#endif	/* NAUDIO */
716