FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/am7930.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */
Cache object: 16b376010129a9c8d9ad76ea552e9bd1
|