xref: /netbsd/sys/dev/ic/am7930.c (revision 7a9218ec)
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
63 int     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  */
78 static 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  */
116 static 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 
141 static 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 
156 static void
am7930_iwrite(struct am7930_softc * sc,int reg,uint8_t val)157 am7930_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 
164 static uint8_t
am7930_iread(struct am7930_softc * sc,int reg)165 am7930_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 
172 static void
am7930_iwrite16(struct am7930_softc * sc,int reg,uint16_t val)173 am7930_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 
181 static uint16_t __unused
am7930_iread16(struct am7930_softc * sc,int reg)182 am7930_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  */
200 void
am7930_init(struct am7930_softc * sc,int flag)201 am7930_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 
265 int
am7930_query_format(void * addr,audio_format_query_t * afp)266 am7930_query_format(void *addr, audio_format_query_t *afp)
267 {
268 
269 	return audio_query_format(&am7930_format, 1, afp);
270 }
271 
272 int
am7930_set_format(void * addr,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)273 am7930_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 
288 int
am7930_commit_settings(void * addr)289 am7930_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 
340 int
am7930_trigger_output(void * addr,void * start,void * end,int blksize,void (* intr)(void *),void * arg,const audio_params_t * params)341 am7930_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 
364 int
am7930_trigger_input(void * addr,void * start,void * end,int blksize,void (* intr)(void *),void * arg,const audio_params_t * params)365 am7930_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 
388 int
am7930_halt_output(void * addr)389 am7930_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 
403 int
am7930_halt_input(void * addr)404 am7930_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 
418 int
am7930_hwintr(void * arg)419 am7930_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 
472 void
am7930_swintr(void * cookie)473 am7930_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  */
492 int
am7930_get_props(void * addr)493 am7930_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  */
503 int
am7930_set_port(void * addr,mixer_ctrl_t * cp)504 am7930_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 
551 int
am7930_get_port(void * addr,mixer_ctrl_t * cp)552 am7930_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  */
599 int
am7930_query_devinfo(void * addr,mixer_devinfo_t * dip)600 am7930_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 
705 void
am7930_get_locks(void * addr,kmutex_t ** intr,kmutex_t ** thread)706 am7930_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