Home | History | Annotate | Line # | Download | only in ic
      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 */
    716