1 /* $OpenBSD: es8316ac.c,v 1.1 2020/06/11 00:04:28 patrick Exp $ */ 2 /* $NetBSD: es8316ac.c,v 1.2 2020/01/03 01:00:08 jmcneill Exp $ */ 3 /*- 4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/device.h> 33 34 #include <machine/bus.h> 35 #include <machine/fdt.h> 36 37 #include <dev/i2c/i2cvar.h> 38 #include <dev/ofw/openfirm.h> 39 #include <dev/ofw/ofw_clock.h> 40 #include <dev/ofw/ofw_misc.h> 41 42 #include <sys/audioio.h> 43 #include <dev/audio_if.h> 44 #include <dev/midi_if.h> 45 46 #define ESCODEC_RESET_REG 0x00 47 #define RESET_ALL (0x3f << 0) 48 #define RESET_CSM_ON (1 << 7) 49 #define ESCODEC_CLKMAN1_REG 0x01 50 #define CLKMAN1_MCLK_ON (1 << 6) 51 #define CLKMAN1_BCLK_ON (1 << 5) 52 #define CLKMAN1_CLK_CP_ON (1 << 4) 53 #define CLKMAN1_CLK_DAC_ON (1 << 2) 54 #define CLKMAN1_ANACLK_DAC_ON (1 << 0) 55 #define ESCODEC_ADC_OSR_REG 0x03 56 #define ESCODEC_SD_CLK_REG 0x09 57 #define SD_CLK_MSC (1 << 7) 58 #define SD_CLK_BCLK_INV (1 << 5) 59 #define ESCODEC_SD_ADC_REG 0x0a 60 #define ESCODEC_SD_DAC_REG 0x0b 61 #define SD_FMT_LRP (1 << 5) 62 #define SD_FMT_WL_MASK (0x7 << 2) 63 #define SD_FMT_WL_16 (3 << 2) 64 #define SD_FMT_MASK (0x3 << 0) 65 #define SD_FMT_I2S (0 << 0) 66 #define ESCODEC_VMID_REG 0x0c 67 #define ESCODEC_PDN_REG 0x0d 68 #define ESCODEC_HPSEL_REG 0x13 69 #define ESCODEC_HPMIXRT_REG 0x14 70 #define HPMIXRT_LD2LHPMIX (1 << 7) 71 #define HPMIXRT_RD2RHPMIX (1 << 3) 72 #define ESCODEC_HPMIX_REG 0x15 73 #define HPMIX_LHPMIX_MUTE (1 << 5) 74 #define HPMIX_PDN_LHP_MIX (1 << 4) 75 #define HPMIX_RHPMIX_MUTE (1 << 1) 76 #define HPMIX_PDN_RHP_MIX (1 << 0) 77 #define ESCODEC_HPMIXVOL_REG 0x16 78 #define HPMIXVOL_LHPMIXVOL_MASK 0xf 79 #define HPMIXVOL_LHPMIXVOL_SHIFT 4 80 #define HPMIXVOL_RHPMIXVOL_MASK 0xf 81 #define HPMIXVOL_RHPMIXVOL_SHIFT 0 82 #define ESCODEC_HPOUTEN_REG 0x17 83 #define HPOUTEN_EN_HPL (1 << 6) 84 #define HPOUTEN_HPL_OUTEN (1 << 5) 85 #define HPOUTEN_EN_HPR (1 << 2) 86 #define HPOUTEN_HPR_OUTEN (1 << 1) 87 #define ESCODEC_HPVOL_REG 0x18 88 #define HPVOL_PDN_LICAL (1 << 7) 89 #define HPVOL_HPLVOL_MASK 0x3 90 #define HPVOL_HPLVOL_SHIFT 4 91 #define HPVOL_PDN_RICAL (1 << 3) 92 #define HPVOL_HPRVOL_MASK 0x3 93 #define HPVOL_HPRVOL_SHIFT 0 94 #define ESCODEC_HPPWR_REG 0x19 95 #define HPPWR_PDN_CPHP (1 << 2) 96 #define ESCODEC_CPPWR_REG 0x1a 97 #define CPPWR_PDN_CP (1 << 5) 98 #define ESCODEC_DACPWR_REG 0x2f 99 #define DACPWR_PDN_DAC_L (1 << 4) 100 #define DACPWR_PDN_DAC_R (1 << 0) 101 #define ESCODEC_DACCTL1_REG 0x30 102 #define DACCTL1_MUTE (1 << 5) 103 #define ESCODEC_DACVOL_L_REG 0x33 104 #define DACVOL_L_DACVOLUME_MASK 0xff 105 #define DACVOL_L_DACVOLUME_SHIFT 0 106 #define ESCODEC_DACVOL_R_REG 0x34 107 #define DACVOL_R_DACVOLUME_MASK 0xff 108 #define DACVOL_R_DACVOLUME_SHIFT 0 109 110 struct escodec_softc { 111 struct device sc_dev; 112 i2c_tag_t sc_tag; 113 i2c_addr_t sc_addr; 114 int sc_node; 115 116 struct dai_device sc_dai; 117 }; 118 119 int escodec_match(struct device *, void *, void *); 120 void escodec_attach(struct device *, struct device *, void *); 121 122 int escodec_set_format(void *, uint32_t, uint32_t, uint32_t); 123 int escodec_set_sysclk(void *, uint32_t); 124 125 void escodec_init(struct escodec_softc *); 126 void escodec_lock(struct escodec_softc *); 127 void escodec_unlock(struct escodec_softc *); 128 uint8_t escodec_read(struct escodec_softc *, uint8_t); 129 void escodec_write(struct escodec_softc *, uint8_t, uint8_t); 130 131 int escodec_set_port(void *, mixer_ctrl_t *); 132 int escodec_get_port(void *, mixer_ctrl_t *); 133 int escodec_query_devinfo(void *, mixer_devinfo_t *); 134 135 struct audio_hw_if escodec_hw_if = { 136 .set_port = escodec_set_port, 137 .get_port = escodec_get_port, 138 .query_devinfo = escodec_query_devinfo, 139 }; 140 141 struct cfattach escodec_ca = { 142 sizeof(struct escodec_softc), escodec_match, escodec_attach 143 }; 144 145 struct cfdriver escodec_cd = { 146 NULL, "escodec", DV_DULL 147 }; 148 149 int 150 escodec_match(struct device *parent, void *match, void *aux) 151 { 152 struct i2c_attach_args *ia = aux; 153 154 if (strcmp(ia->ia_name, "everest,es8316") == 0) 155 return 1; 156 157 return 0; 158 } 159 160 void 161 escodec_attach(struct device *parent, struct device *self, void *aux) 162 { 163 struct escodec_softc *sc = (struct escodec_softc *)self; 164 struct i2c_attach_args *ia = aux; 165 166 sc->sc_tag = ia->ia_tag; 167 sc->sc_addr = ia->ia_addr; 168 sc->sc_node = *(int *)ia->ia_cookie; 169 170 clock_enable(sc->sc_node, "mclk"); 171 172 printf("\n"); 173 174 escodec_init(sc); 175 176 sc->sc_dai.dd_node = sc->sc_node; 177 sc->sc_dai.dd_cookie = sc; 178 sc->sc_dai.dd_hw_if = &escodec_hw_if; 179 sc->sc_dai.dd_set_format = escodec_set_format; 180 sc->sc_dai.dd_set_sysclk = escodec_set_sysclk; 181 dai_register(&sc->sc_dai); 182 } 183 184 void 185 escodec_init(struct escodec_softc *sc) 186 { 187 uint8_t val; 188 189 escodec_lock(sc); 190 191 escodec_write(sc, ESCODEC_RESET_REG, RESET_ALL); 192 delay(5000); 193 escodec_write(sc, ESCODEC_RESET_REG, RESET_CSM_ON); 194 delay(30000); 195 196 escodec_write(sc, ESCODEC_VMID_REG, 0xff); 197 escodec_write(sc, ESCODEC_ADC_OSR_REG, 0x32); 198 199 val = escodec_read(sc, ESCODEC_SD_ADC_REG); 200 val &= ~SD_FMT_WL_MASK; 201 val |= SD_FMT_WL_16; 202 escodec_write(sc, ESCODEC_SD_ADC_REG, val); 203 204 val = escodec_read(sc, ESCODEC_SD_DAC_REG); 205 val &= ~SD_FMT_WL_MASK; 206 val |= SD_FMT_WL_16; 207 escodec_write(sc, ESCODEC_SD_DAC_REG, val); 208 209 /* Power up */ 210 escodec_write(sc, ESCODEC_PDN_REG, 0); 211 212 /* Route DAC signal to HP mixer */ 213 val = HPMIXRT_LD2LHPMIX | HPMIXRT_RD2RHPMIX; 214 escodec_write(sc, ESCODEC_HPMIXRT_REG, val); 215 216 /* Power up DAC */ 217 escodec_write(sc, ESCODEC_DACPWR_REG, 0); 218 219 /* Power up HP mixer and unmute */ 220 escodec_write(sc, ESCODEC_HPMIX_REG, 0); 221 222 /* Power up HP output driver */ 223 val = escodec_read(sc, ESCODEC_HPPWR_REG); 224 val &= ~HPPWR_PDN_CPHP; 225 escodec_write(sc, ESCODEC_HPPWR_REG, val); 226 227 /* Power up HP charge pump circuits */ 228 val = escodec_read(sc, ESCODEC_CPPWR_REG); 229 val &= ~CPPWR_PDN_CP; 230 escodec_write(sc, ESCODEC_CPPWR_REG, val); 231 232 /* Set LIN1/RIN1 as inputs for HP mixer */ 233 escodec_write(sc, ESCODEC_HPSEL_REG, 0); 234 235 /* Power up HP output driver calibration */ 236 val = escodec_read(sc, ESCODEC_HPVOL_REG); 237 val &= ~HPVOL_PDN_LICAL; 238 val &= ~HPVOL_PDN_RICAL; 239 escodec_write(sc, ESCODEC_HPVOL_REG, val); 240 241 /* Set headphone mixer to -6dB */ 242 escodec_write(sc, ESCODEC_HPMIXVOL_REG, 0x44); 243 244 /* Set charge pump headphone to -48dB */ 245 escodec_write(sc, ESCODEC_HPVOL_REG, 0x33); 246 247 /* Set DAC to 0dB */ 248 escodec_write(sc, ESCODEC_DACVOL_L_REG, 0); 249 escodec_write(sc, ESCODEC_DACVOL_R_REG, 0); 250 251 /* Enable HP output */ 252 val = HPOUTEN_EN_HPL | HPOUTEN_EN_HPR | 253 HPOUTEN_HPL_OUTEN | HPOUTEN_HPR_OUTEN; 254 escodec_write(sc, ESCODEC_HPOUTEN_REG, val); 255 256 escodec_unlock(sc); 257 } 258 259 int 260 escodec_set_format(void *cookie, uint32_t fmt, uint32_t pol, 261 uint32_t clk) 262 { 263 struct escodec_softc *sc = cookie; 264 uint8_t sd_clk, sd_fmt, val; 265 266 if (fmt != DAI_FORMAT_I2S) 267 return EINVAL; 268 269 if (clk != (DAI_CLOCK_CBS|DAI_CLOCK_CFS)) 270 return EINVAL; 271 272 switch (pol) { 273 case DAI_POLARITY_NB|DAI_POLARITY_NF: 274 sd_clk = 0; 275 sd_fmt = 0; 276 break; 277 case DAI_POLARITY_NB|DAI_POLARITY_IF: 278 sd_clk = 0; 279 sd_fmt = SD_FMT_LRP; 280 break; 281 case DAI_POLARITY_IB|DAI_POLARITY_NF: 282 sd_clk = SD_CLK_BCLK_INV; 283 sd_fmt = 0; 284 break; 285 case DAI_POLARITY_IB|DAI_POLARITY_IF: 286 sd_clk = SD_CLK_BCLK_INV; 287 sd_fmt = SD_FMT_LRP; 288 break; 289 } 290 291 escodec_lock(sc); 292 293 val = escodec_read(sc, ESCODEC_SD_CLK_REG); 294 val &= ~(SD_CLK_MSC|SD_CLK_BCLK_INV); 295 val |= sd_clk; 296 escodec_write(sc, ESCODEC_SD_CLK_REG, val); 297 298 val = escodec_read(sc, ESCODEC_SD_ADC_REG); 299 val &= ~SD_FMT_MASK; 300 val |= SD_FMT_I2S; 301 val &= ~SD_FMT_LRP; 302 val |= sd_fmt; 303 escodec_write(sc, ESCODEC_SD_ADC_REG, val); 304 305 val = escodec_read(sc, ESCODEC_SD_DAC_REG); 306 val &= ~SD_FMT_MASK; 307 val |= SD_FMT_I2S; 308 val &= ~SD_FMT_LRP; 309 val |= sd_fmt; 310 escodec_write(sc, ESCODEC_SD_DAC_REG, val); 311 312 val = escodec_read(sc, ESCODEC_CLKMAN1_REG); 313 val |= CLKMAN1_MCLK_ON; 314 val |= CLKMAN1_BCLK_ON; 315 val |= CLKMAN1_CLK_CP_ON; 316 val |= CLKMAN1_CLK_DAC_ON; 317 val |= CLKMAN1_ANACLK_DAC_ON; 318 escodec_write(sc, ESCODEC_CLKMAN1_REG, val); 319 320 escodec_unlock(sc); 321 322 return 0; 323 } 324 325 int 326 escodec_set_sysclk(void *cookie, uint32_t rate) 327 { 328 struct escodec_softc *sc = cookie; 329 int error; 330 331 error = clock_set_frequency(sc->sc_node, "mclk", rate); 332 if (error != 0) { 333 printf("%s: can't set sysclk to %u Hz\n", 334 sc->sc_dev.dv_xname, rate); 335 return error; 336 } 337 338 return 0; 339 } 340 341 void 342 escodec_lock(struct escodec_softc *sc) 343 { 344 iic_acquire_bus(sc->sc_tag, 0); 345 } 346 347 void 348 escodec_unlock(struct escodec_softc *sc) 349 { 350 iic_release_bus(sc->sc_tag, 0); 351 } 352 353 uint8_t 354 escodec_read(struct escodec_softc *sc, uint8_t reg) 355 { 356 uint8_t val; 357 358 if (iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, reg, &val, 0) != 0) 359 val = 0xff; 360 361 return val; 362 } 363 364 void 365 escodec_write(struct escodec_softc *sc, uint8_t reg, uint8_t val) 366 { 367 (void)iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, reg, val, 0); 368 } 369 370 enum escodec_mixer_ctrl { 371 ESCODEC_OUTPUT_CLASS, 372 ESCODEC_INPUT_CLASS, 373 ESCODEC_INPUT_DAC, 374 ESCODEC_INPUT_DAC_MUTE, 375 ESCODEC_INPUT_HEADPHONE, 376 ESCODEC_INPUT_MIXEROUT, 377 ESCODEC_INPUT_MIXEROUT_MUTE, 378 379 ESCODEC_MIXER_CTRL_LAST 380 }; 381 382 enum escodec_mixer_type { 383 ESCODEC_MIXER_CLASS, 384 ESCODEC_MIXER_AMPLIFIER, 385 ESCODEC_MIXER_ATTENUATOR, 386 ESCODEC_MIXER_MUTE, 387 }; 388 389 struct escodec_mixer { 390 const char * name; 391 int mixer_class; 392 int prev, next; 393 enum escodec_mixer_ctrl ctrl; 394 enum escodec_mixer_type type; 395 u_int reg[2]; 396 uint8_t mask[2]; 397 uint8_t shift[2]; 398 uint8_t maxval; 399 } escodec_mixers[ESCODEC_MIXER_CTRL_LAST] = { 400 /* 401 * Mixer classes 402 */ 403 [ESCODEC_OUTPUT_CLASS] = { 404 .name = AudioCoutputs, 405 .type = ESCODEC_MIXER_CLASS, 406 }, 407 [ESCODEC_INPUT_CLASS] = { 408 .name = AudioCinputs, 409 .type = ESCODEC_MIXER_CLASS, 410 }, 411 412 /* 413 * Stereo DAC 414 */ 415 [ESCODEC_INPUT_DAC] = { 416 .name = AudioNdac, 417 .mixer_class = ESCODEC_INPUT_CLASS, 418 .prev = AUDIO_MIXER_LAST, 419 .next = ESCODEC_INPUT_DAC_MUTE, 420 .type = ESCODEC_MIXER_ATTENUATOR, 421 .reg = { 422 [AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_DACVOL_L_REG, 423 [AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_DACVOL_R_REG, 424 }, 425 .mask = { 426 [AUDIO_MIXER_LEVEL_LEFT] = DACVOL_L_DACVOLUME_MASK, 427 [AUDIO_MIXER_LEVEL_RIGHT] = DACVOL_R_DACVOLUME_MASK, 428 }, 429 .shift = { 430 [AUDIO_MIXER_LEVEL_LEFT] = DACVOL_L_DACVOLUME_SHIFT, 431 [AUDIO_MIXER_LEVEL_RIGHT] = DACVOL_R_DACVOLUME_SHIFT, 432 }, 433 .maxval = 0xc0, 434 }, 435 [ESCODEC_INPUT_DAC_MUTE] = { 436 .name = AudioNmute, 437 .mixer_class = ESCODEC_INPUT_CLASS, 438 .prev = ESCODEC_INPUT_DAC, 439 .next = AUDIO_MIXER_LAST, 440 .type = ESCODEC_MIXER_MUTE, 441 .reg = { 442 [AUDIO_MIXER_LEVEL_MONO] = ESCODEC_DACCTL1_REG, 443 }, 444 .mask = { 445 [AUDIO_MIXER_LEVEL_MONO] = DACCTL1_MUTE, 446 } 447 }, 448 449 /* 450 * Charge Pump Headphones 451 */ 452 [ESCODEC_INPUT_HEADPHONE] = { 453 .name = AudioNheadphone, 454 .mixer_class = ESCODEC_INPUT_CLASS, 455 .prev = AUDIO_MIXER_LAST, 456 .next = AUDIO_MIXER_LAST, 457 .type = ESCODEC_MIXER_ATTENUATOR, 458 .reg = { 459 [AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_HPVOL_REG, 460 [AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_HPVOL_REG, 461 }, 462 .mask = { 463 [AUDIO_MIXER_LEVEL_LEFT] = HPVOL_HPLVOL_MASK, 464 [AUDIO_MIXER_LEVEL_RIGHT] = HPVOL_HPRVOL_MASK, 465 }, 466 .shift = { 467 [AUDIO_MIXER_LEVEL_LEFT] = HPVOL_HPLVOL_SHIFT, 468 [AUDIO_MIXER_LEVEL_RIGHT] = HPVOL_HPRVOL_SHIFT, 469 } 470 }, 471 472 /* 473 * Headphone mixer 474 */ 475 [ESCODEC_INPUT_MIXEROUT] = { 476 .name = AudioNmixerout, 477 .mixer_class = ESCODEC_INPUT_CLASS, 478 .prev = AUDIO_MIXER_LAST, 479 .next = ESCODEC_INPUT_MIXEROUT_MUTE, 480 .type = ESCODEC_MIXER_AMPLIFIER, 481 .reg = { 482 [AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_HPMIXVOL_REG, 483 [AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_HPMIXVOL_REG, 484 }, 485 .mask = { 486 [AUDIO_MIXER_LEVEL_LEFT] = HPMIXVOL_LHPMIXVOL_MASK, 487 [AUDIO_MIXER_LEVEL_RIGHT] = HPMIXVOL_RHPMIXVOL_MASK 488 }, 489 .shift = { 490 [AUDIO_MIXER_LEVEL_LEFT] = HPMIXVOL_LHPMIXVOL_SHIFT, 491 [AUDIO_MIXER_LEVEL_RIGHT] = HPMIXVOL_RHPMIXVOL_SHIFT 492 }, 493 /* 494 * Datasheet says this field goes up to 0xb, but values 495 * above 0x4 result in noisy output in practice. 496 */ 497 .maxval = 0x4, 498 }, 499 [ESCODEC_INPUT_MIXEROUT_MUTE] = { 500 .name = AudioNmute, 501 .mixer_class = ESCODEC_INPUT_CLASS, 502 .prev = ESCODEC_INPUT_MIXEROUT, 503 .next = AUDIO_MIXER_LAST, 504 .type = ESCODEC_MIXER_MUTE, 505 .reg = { 506 [AUDIO_MIXER_LEVEL_MONO] = ESCODEC_HPMIX_REG, 507 }, 508 .mask = { 509 [AUDIO_MIXER_LEVEL_MONO] = HPMIX_LHPMIX_MUTE | HPMIX_RHPMIX_MUTE, 510 } 511 }, 512 }; 513 514 struct escodec_mixer * 515 escodec_get_mixer(u_int index) 516 { 517 if (index >= ESCODEC_MIXER_CTRL_LAST) 518 return NULL; 519 520 return &escodec_mixers[index]; 521 } 522 523 int 524 escodec_set_port(void *priv, mixer_ctrl_t *mc) 525 { 526 struct escodec_softc *sc = priv; 527 const struct escodec_mixer *mix; 528 int nvol, shift, ch; 529 uint8_t val; 530 531 if ((mix = escodec_get_mixer(mc->dev)) == NULL) 532 return ENXIO; 533 534 switch (mix->type) { 535 case ESCODEC_MIXER_AMPLIFIER: 536 case ESCODEC_MIXER_ATTENUATOR: 537 escodec_lock(sc); 538 for (ch = 0; ch < 2; ch++) { 539 val = escodec_read(sc, mix->reg[ch]); 540 shift = 8 - fls(mix->mask[ch]); 541 nvol = mc->un.value.level[ch] >> shift; 542 if (mix->type == ESCODEC_MIXER_ATTENUATOR) 543 nvol = mix->mask[ch] - nvol; 544 if (mix->maxval != 0 && nvol > mix->maxval) 545 nvol = mix->maxval; 546 547 val &= ~(mix->mask[ch] << mix->shift[ch]); 548 val |= (nvol & mix->mask[ch]) << mix->shift[ch]; 549 escodec_write(sc, mix->reg[ch], val); 550 } 551 escodec_unlock(sc); 552 return 0; 553 554 case ESCODEC_MIXER_MUTE: 555 if (mc->un.ord < 0 || mc->un.ord > 1) 556 return EINVAL; 557 escodec_lock(sc); 558 val = escodec_read(sc, mix->reg[0]); 559 if (mc->un.ord) 560 val |= mix->mask[0]; 561 else 562 val &= ~mix->mask[0]; 563 escodec_write(sc, mix->reg[0], val); 564 escodec_unlock(sc); 565 return 0; 566 567 default: 568 return ENXIO; 569 } 570 } 571 572 int 573 escodec_get_port(void *priv, mixer_ctrl_t *mc) 574 { 575 struct escodec_softc *sc = priv; 576 const struct escodec_mixer *mix; 577 int nvol, shift, ch; 578 uint8_t val; 579 580 if ((mix = escodec_get_mixer(mc->dev)) == NULL) 581 return ENXIO; 582 583 switch (mix->type) { 584 case ESCODEC_MIXER_AMPLIFIER: 585 case ESCODEC_MIXER_ATTENUATOR: 586 escodec_lock(sc); 587 for (ch = 0; ch < 2; ch++) { 588 val = escodec_read(sc, mix->reg[ch]); 589 shift = 8 - fls(mix->mask[ch]); 590 nvol = (val >> mix->shift[ch]) & mix->mask[ch]; 591 if (mix->type == ESCODEC_MIXER_ATTENUATOR) 592 nvol = mix->mask[ch] - nvol; 593 nvol <<= shift; 594 mc->un.value.level[ch] = nvol; 595 } 596 escodec_unlock(sc); 597 return 0; 598 599 case ESCODEC_MIXER_MUTE: 600 escodec_lock(sc); 601 val = escodec_read(sc, mix->reg[0]); 602 mc->un.ord = (val & mix->mask[0]) != 0; 603 escodec_unlock(sc); 604 return 0; 605 606 default: 607 return ENXIO; 608 } 609 } 610 611 int 612 escodec_query_devinfo(void *priv, mixer_devinfo_t *di) 613 { 614 const struct escodec_mixer *mix; 615 616 if ((mix = escodec_get_mixer(di->index)) == NULL) 617 return ENXIO; 618 619 strlcpy(di->label.name, mix->name, sizeof(di->label.name)); 620 di->mixer_class = mix->mixer_class; 621 di->next = mix->next; 622 di->prev = mix->prev; 623 624 switch (mix->type) { 625 case ESCODEC_MIXER_CLASS: 626 di->type = AUDIO_MIXER_CLASS; 627 return 0; 628 629 case ESCODEC_MIXER_AMPLIFIER: 630 case ESCODEC_MIXER_ATTENUATOR: 631 di->type = AUDIO_MIXER_VALUE; 632 di->un.v.delta = 633 256 / (mix->mask[0] + 1); 634 di->un.v.num_channels = 2; 635 strlcpy(di->un.v.units.name, AudioNvolume, 636 sizeof(di->un.v.units.name)); 637 return 0; 638 639 case ESCODEC_MIXER_MUTE: 640 di->type = AUDIO_MIXER_ENUM; 641 di->un.e.num_mem = 2; 642 strlcpy(di->un.e.member[0].label.name, AudioNoff, 643 sizeof(di->un.e.member[0].label.name)); 644 di->un.e.member[0].ord = 0; 645 strlcpy(di->un.e.member[1].label.name, AudioNon, 646 sizeof(di->un.e.member[1].label.name)); 647 di->un.e.member[1].ord = 1; 648 return 0; 649 650 default: 651 return ENXIO; 652 } 653 } 654