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