1/* $OpenBSD: es8316ac.c,v 1.3 2022/04/06 18:59:28 naddy 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 110struct 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 119int escodec_match(struct device *, void *, void *); 120void escodec_attach(struct device *, struct device *, void *); 121 122int escodec_set_format(void *, uint32_t, uint32_t, uint32_t); 123int escodec_set_sysclk(void *, uint32_t); 124 125void escodec_init(struct escodec_softc *); 126void escodec_lock(struct escodec_softc *); 127void escodec_unlock(struct escodec_softc *); 128uint8_t escodec_read(struct escodec_softc *, uint8_t); 129void escodec_write(struct escodec_softc *, uint8_t, uint8_t); 130 131int escodec_set_port(void *, mixer_ctrl_t *); 132int escodec_get_port(void *, mixer_ctrl_t *); 133int escodec_query_devinfo(void *, mixer_devinfo_t *); 134 135const 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 141const struct cfattach escodec_ca = { 142 sizeof(struct escodec_softc), escodec_match, escodec_attach 143}; 144 145struct cfdriver escodec_cd = { 146 NULL, "escodec", DV_DULL 147}; 148 149int 150escodec_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 160void 161escodec_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 184void 185escodec_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 259int 260escodec_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 325int 326escodec_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 341void 342escodec_lock(struct escodec_softc *sc) 343{ 344 iic_acquire_bus(sc->sc_tag, 0); 345} 346 347void 348escodec_unlock(struct escodec_softc *sc) 349{ 350 iic_release_bus(sc->sc_tag, 0); 351} 352 353uint8_t 354escodec_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 364void 365escodec_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 370enum 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 382enum escodec_mixer_type { 383 ESCODEC_MIXER_CLASS, 384 ESCODEC_MIXER_AMPLIFIER, 385 ESCODEC_MIXER_ATTENUATOR, 386 ESCODEC_MIXER_MUTE, 387}; 388 389struct 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 514struct escodec_mixer * 515escodec_get_mixer(u_int index) 516{ 517 if (index >= ESCODEC_MIXER_CTRL_LAST) 518 return NULL; 519 520 return &escodec_mixers[index]; 521} 522 523int 524escodec_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 572int 573escodec_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 611int 612escodec_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