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