sun50i_a64_acodec.c revision 1.7
1/* $NetBSD: sun50i_a64_acodec.c,v 1.7 2018/05/16 00:12:57 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2018 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/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: sun50i_a64_acodec.c,v 1.7 2018/05/16 00:12:57 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34#include <sys/cpu.h> 35#include <sys/device.h> 36#include <sys/kmem.h> 37#include <sys/bitops.h> 38 39#include <dev/audio_dai.h> 40 41#include <dev/fdt/fdtvar.h> 42 43#define A64_PR_CFG 0x00 44#define A64_AC_PR_RST __BIT(28) 45#define A64_AC_PR_RW __BIT(24) 46#define A64_AC_PR_ADDR __BITS(20,16) 47#define A64_ACDA_PR_WDAT __BITS(15,8) 48#define A64_ACDA_PR_RDAT __BITS(7,0) 49 50#define A64_HP_CTRL 0x00 51#define A64_HPPA_EN __BIT(6) 52#define A64_HPVOL __BITS(5,0) 53#define A64_OL_MIX_CTRL 0x01 54#define A64_LMIXMUTE_LDAC __BIT(1) 55#define A64_OR_MIX_CTRL 0x02 56#define A64_RMIXMUTE_RDAC __BIT(1) 57#define A64_LINEOUT_CTRL0 0x05 58#define A64_LINEOUT_LEFT_EN __BIT(7) 59#define A64_LINEOUT_RIGHT_EN __BIT(6) 60#define A64_LINEOUT_EN (A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN) 61#define A64_LINEOUT_CTRL1 0x06 62#define A64_LINEOUT_VOL __BITS(4,0) 63#define A64_MIC1_CTRL 0x07 64#define A64_MIC1G __BITS(6,4) 65#define A64_MIC1AMPEN __BIT(3) 66#define A64_MIC1BOOST __BITS(2,0) 67#define A64_MIC2_CTRL 0x08 68#define A64_MIC2_SEL __BIT(7) 69#define A64_MIC2G __BITS(6,4) 70#define A64_MIC2AMPEN __BIT(3) 71#define A64_MIC2BOOST __BITS(2,0) 72#define A64_LINEIN_CTRL 0x09 73#define A64_LINEING __BITS(6,4) 74#define A64_MIX_DAC_CTRL 0x0a 75#define A64_DACAREN __BIT(7) 76#define A64_DACALEN __BIT(6) 77#define A64_RMIXEN __BIT(5) 78#define A64_LMIXEN __BIT(4) 79#define A64_RHPPAMUTE __BIT(3) 80#define A64_LHPPAMUTE __BIT(2) 81#define A64_RHPIS __BIT(1) 82#define A64_LHPIS __BIT(0) 83#define A64_L_ADCMIX_SRC 0x0b 84#define A64_R_ADCMIX_SRC 0x0c 85#define A64_ADCMIX_SRC_MIC1 __BIT(6) 86#define A64_ADCMIX_SRC_MIC2 __BIT(5) 87#define A64_ADCMIX_SRC_LINEIN __BIT(2) 88#define A64_ADCMIX_SRC_OMIXER __BIT(0) 89#define A64_ADC_CTRL 0x0d 90#define A64_ADCREN __BIT(7) 91#define A64_ADCLEN __BIT(6) 92#define A64_ADCG __BITS(2,0) 93#define A64_JACK_MIC_CTRL 0x1d 94#define A64_JACKDETEN __BIT(7) 95#define A64_INNERRESEN __BIT(6) 96#define A64_AUTOPLEN __BIT(1) 97 98struct a64_acodec_softc { 99 device_t sc_dev; 100 bus_space_tag_t sc_bst; 101 bus_space_handle_t sc_bsh; 102 int sc_phandle; 103 104 struct audio_dai_device sc_dai; 105 int sc_master_dev; 106}; 107 108enum a64_acodec_mixer_ctrl { 109 A64_CODEC_OUTPUT_CLASS, 110 A64_CODEC_INPUT_CLASS, 111 A64_CODEC_RECORD_CLASS, 112 113 A64_CODEC_OUTPUT_MASTER_VOLUME, 114 A64_CODEC_OUTPUT_MUTE, 115 A64_CODEC_OUTPUT_SOURCE, 116 A64_CODEC_INPUT_LINE_VOLUME, 117 A64_CODEC_INPUT_HP_VOLUME, 118 A64_CODEC_RECORD_LINE_VOLUME, 119 A64_CODEC_RECORD_MIC1_VOLUME, 120 A64_CODEC_RECORD_MIC2_VOLUME, 121 A64_CODEC_RECORD_AGC_VOLUME, 122 A64_CODEC_RECORD_SOURCE, 123 124 A64_CODEC_MIXER_CTRL_LAST 125}; 126 127#define A64_OUTPUT_SOURCE_LINE __BIT(0) 128#define A64_OUTPUT_SOURCE_HP __BIT(1) 129 130static const struct a64_acodec_mixer { 131 const char * name; 132 enum a64_acodec_mixer_ctrl mixer_class; 133 u_int reg; 134 u_int mask; 135} a64_acodec_mixers[A64_CODEC_MIXER_CTRL_LAST] = { 136 [A64_CODEC_INPUT_LINE_VOLUME] = { AudioNline, 137 A64_CODEC_INPUT_CLASS, A64_LINEOUT_CTRL1, A64_LINEOUT_VOL }, 138 [A64_CODEC_INPUT_HP_VOLUME] = { AudioNheadphone, 139 A64_CODEC_INPUT_CLASS, A64_HP_CTRL, A64_HPVOL }, 140 141 [A64_CODEC_RECORD_LINE_VOLUME] = { AudioNline, 142 A64_CODEC_RECORD_CLASS, A64_LINEIN_CTRL, A64_LINEING }, 143 [A64_CODEC_RECORD_MIC1_VOLUME] = { AudioNmicrophone, 144 A64_CODEC_RECORD_CLASS, A64_MIC1_CTRL, A64_MIC1G }, 145 [A64_CODEC_RECORD_MIC2_VOLUME] = { AudioNmicrophone "2", 146 A64_CODEC_RECORD_CLASS, A64_MIC2_CTRL, A64_MIC2G }, 147 [A64_CODEC_RECORD_AGC_VOLUME] = { AudioNagc, 148 A64_CODEC_RECORD_CLASS, A64_ADC_CTRL, A64_ADCG }, 149}; 150 151#define RD4(sc, reg) \ 152 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 153#define WR4(sc, reg, val) \ 154 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 155 156static u_int 157a64_acodec_pr_read(struct a64_acodec_softc *sc, u_int addr) 158{ 159 uint32_t val; 160 161 /* Read current value */ 162 val = RD4(sc, A64_PR_CFG); 163 164 /* De-assert reset */ 165 val |= A64_AC_PR_RST; 166 WR4(sc, A64_PR_CFG, val); 167 168 /* Read mode */ 169 val &= ~A64_AC_PR_RW; 170 WR4(sc, A64_PR_CFG, val); 171 172 /* Set address */ 173 val &= ~A64_AC_PR_ADDR; 174 val |= __SHIFTIN(addr, A64_AC_PR_ADDR); 175 WR4(sc, A64_PR_CFG, val); 176 177 /* Read data */ 178 return __SHIFTOUT(RD4(sc, A64_PR_CFG), A64_ACDA_PR_RDAT); 179} 180 181static void 182a64_acodec_pr_write(struct a64_acodec_softc *sc, u_int addr, u_int data) 183{ 184 uint32_t val; 185 186 /* Read current value */ 187 val = RD4(sc, A64_PR_CFG); 188 189 /* De-assert reset */ 190 val |= A64_AC_PR_RST; 191 WR4(sc, A64_PR_CFG, val); 192 193 /* Set address */ 194 val &= ~A64_AC_PR_ADDR; 195 val |= __SHIFTIN(addr, A64_AC_PR_ADDR); 196 WR4(sc, A64_PR_CFG, val); 197 198 /* Write data */ 199 val &= ~A64_ACDA_PR_WDAT; 200 val |= __SHIFTIN(data, A64_ACDA_PR_WDAT); 201 WR4(sc, A64_PR_CFG, val); 202 203 /* Write mode */ 204 val |= A64_AC_PR_RW; 205 WR4(sc, A64_PR_CFG, val); 206 207 /* Clear write mode */ 208 val &= ~A64_AC_PR_RW; 209 WR4(sc, A64_PR_CFG, val); 210} 211 212static void 213a64_acodec_pr_set_clear(struct a64_acodec_softc *sc, u_int addr, u_int set, u_int clr) 214{ 215 u_int old, new; 216 217 old = a64_acodec_pr_read(sc, addr); 218 new = set | (old & ~clr); 219 a64_acodec_pr_write(sc, addr, new); 220} 221 222static int 223a64_acodec_trigger_output(void *priv, void *start, void *end, int blksize, 224 void (*intr)(void *), void *intrarg, const audio_params_t *params) 225{ 226 struct a64_acodec_softc * const sc = priv; 227 228 /* Enable DAC analog l/r channels, HP PA, and output mixer */ 229 a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, 230 A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | 231 A64_RHPPAMUTE | A64_LHPPAMUTE, 0); 232 233 return 0; 234} 235 236static int 237a64_acodec_trigger_input(void *priv, void *start, void *end, int blksize, 238 void (*intr)(void *), void *intrarg, const audio_params_t *params) 239{ 240 struct a64_acodec_softc * const sc = priv; 241 242 /* Enable ADC analog l/r channels */ 243 a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, 244 A64_ADCREN | A64_ADCLEN, 0); 245 246 return 0; 247} 248 249static int 250a64_acodec_halt_output(void *priv) 251{ 252 struct a64_acodec_softc * const sc = priv; 253 254 /* Disable DAC analog l/r channels, HP PA, and output mixer */ 255 a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, 256 0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | 257 A64_RHPPAMUTE | A64_LHPPAMUTE); 258 259 return 0; 260} 261 262static int 263a64_acodec_halt_input(void *priv) 264{ 265 struct a64_acodec_softc * const sc = priv; 266 267 /* Disable ADC analog l/r channels */ 268 a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, 269 0, A64_ADCREN | A64_ADCLEN); 270 271 return 0; 272} 273 274static int 275a64_acodec_set_port(void *priv, mixer_ctrl_t *mc) 276{ 277 struct a64_acodec_softc * const sc = priv; 278 const struct a64_acodec_mixer *mix; 279 u_int val, shift; 280 int nvol, dev; 281 282 dev = mc->dev; 283 if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME) 284 dev = sc->sc_master_dev; 285 286 switch (dev) { 287 case A64_CODEC_INPUT_LINE_VOLUME: 288 case A64_CODEC_INPUT_HP_VOLUME: 289 case A64_CODEC_RECORD_LINE_VOLUME: 290 case A64_CODEC_RECORD_MIC1_VOLUME: 291 case A64_CODEC_RECORD_MIC2_VOLUME: 292 case A64_CODEC_RECORD_AGC_VOLUME: 293 mix = &a64_acodec_mixers[dev]; 294 val = a64_acodec_pr_read(sc, mix->reg); 295 shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask)); 296 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift; 297 val &= ~mix->mask; 298 val |= __SHIFTIN(nvol, mix->mask); 299 a64_acodec_pr_write(sc, mix->reg, val); 300 return 0; 301 302 case A64_CODEC_OUTPUT_MUTE: 303 if (mc->un.ord < 0 || mc->un.ord > 1) 304 return EINVAL; 305 if (mc->un.ord) { 306 a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL, 307 0, A64_LMIXMUTE_LDAC); 308 a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL, 309 0, A64_RMIXMUTE_RDAC); 310 } else { 311 a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL, 312 A64_LMIXMUTE_LDAC, 0); 313 a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL, 314 A64_RMIXMUTE_RDAC, 0); 315 } 316 return 0; 317 318 case A64_CODEC_OUTPUT_SOURCE: 319 if (mc->un.mask & A64_OUTPUT_SOURCE_LINE) 320 a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 321 A64_LINEOUT_EN, 0); 322 else 323 a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 324 0, A64_LINEOUT_EN); 325 326 if (mc->un.mask & A64_OUTPUT_SOURCE_HP) 327 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 328 A64_HPPA_EN, 0); 329 else 330 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 331 0, A64_HPPA_EN); 332 return 0; 333 334 case A64_CODEC_RECORD_SOURCE: 335 a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC, mc->un.mask); 336 a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC, mc->un.mask); 337 return 0; 338 } 339 340 return ENXIO; 341} 342 343static int 344a64_acodec_get_port(void *priv, mixer_ctrl_t *mc) 345{ 346 struct a64_acodec_softc * const sc = priv; 347 const struct a64_acodec_mixer *mix; 348 u_int val, shift; 349 int nvol, dev; 350 351 dev = mc->dev; 352 if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME) 353 dev = sc->sc_master_dev; 354 355 switch (dev) { 356 case A64_CODEC_INPUT_LINE_VOLUME: 357 case A64_CODEC_INPUT_HP_VOLUME: 358 case A64_CODEC_RECORD_LINE_VOLUME: 359 case A64_CODEC_RECORD_MIC1_VOLUME: 360 case A64_CODEC_RECORD_MIC2_VOLUME: 361 case A64_CODEC_RECORD_AGC_VOLUME: 362 mix = &a64_acodec_mixers[dev]; 363 val = a64_acodec_pr_read(sc, mix->reg); 364 shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask)); 365 nvol = __SHIFTOUT(val, mix->mask) << shift; 366 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol; 367 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol; 368 return 0; 369 370 case A64_CODEC_OUTPUT_MUTE: 371 mc->un.ord = 1; 372 if (a64_acodec_pr_read(sc, A64_OL_MIX_CTRL) & A64_LMIXMUTE_LDAC) 373 mc->un.ord = 0; 374 if (a64_acodec_pr_read(sc, A64_OR_MIX_CTRL) & A64_RMIXMUTE_RDAC) 375 mc->un.ord = 0; 376 return 0; 377 378 case A64_CODEC_OUTPUT_SOURCE: 379 mc->un.mask = 0; 380 if (a64_acodec_pr_read(sc, A64_LINEOUT_CTRL0) & A64_LINEOUT_EN) 381 mc->un.mask |= A64_OUTPUT_SOURCE_LINE; 382 if (a64_acodec_pr_read(sc, A64_HP_CTRL) & A64_HPPA_EN) 383 mc->un.mask |= A64_OUTPUT_SOURCE_HP; 384 return 0; 385 386 case A64_CODEC_RECORD_SOURCE: 387 mc->un.mask = 388 a64_acodec_pr_read(sc, A64_L_ADCMIX_SRC) | 389 a64_acodec_pr_read(sc, A64_R_ADCMIX_SRC); 390 return 0; 391 } 392 393 return ENXIO; 394} 395 396static int 397a64_acodec_query_devinfo(void *priv, mixer_devinfo_t *di) 398{ 399 struct a64_acodec_softc * const sc = priv; 400 const struct a64_acodec_mixer *mix; 401 402 switch (di->index) { 403 case A64_CODEC_OUTPUT_CLASS: 404 di->mixer_class = di->index; 405 strcpy(di->label.name, AudioCoutputs); 406 di->type = AUDIO_MIXER_CLASS; 407 di->next = di->prev = AUDIO_MIXER_LAST; 408 return 0; 409 410 case A64_CODEC_INPUT_CLASS: 411 di->mixer_class = di->index; 412 strcpy(di->label.name, AudioCinputs); 413 di->type = AUDIO_MIXER_CLASS; 414 di->next = di->prev = AUDIO_MIXER_LAST; 415 return 0; 416 417 case A64_CODEC_RECORD_CLASS: 418 di->mixer_class = di->index; 419 strcpy(di->label.name, AudioCrecord); 420 di->type = AUDIO_MIXER_CLASS; 421 di->next = di->prev = AUDIO_MIXER_LAST; 422 return 0; 423 424 case A64_CODEC_OUTPUT_MASTER_VOLUME: 425 mix = &a64_acodec_mixers[sc->sc_master_dev]; 426 di->mixer_class = A64_CODEC_OUTPUT_CLASS; 427 strcpy(di->label.name, AudioNmaster); 428 di->un.v.delta = 429 256 / (__SHIFTOUT_MASK(mix->mask) + 1); 430 di->type = AUDIO_MIXER_VALUE; 431 di->next = di->prev = AUDIO_MIXER_LAST; 432 di->un.v.num_channels = 2; 433 strcpy(di->un.v.units.name, AudioNvolume); 434 return 0; 435 436 case A64_CODEC_INPUT_LINE_VOLUME: 437 case A64_CODEC_INPUT_HP_VOLUME: 438 case A64_CODEC_RECORD_LINE_VOLUME: 439 case A64_CODEC_RECORD_MIC1_VOLUME: 440 case A64_CODEC_RECORD_MIC2_VOLUME: 441 case A64_CODEC_RECORD_AGC_VOLUME: 442 mix = &a64_acodec_mixers[di->index]; 443 di->mixer_class = mix->mixer_class; 444 strcpy(di->label.name, mix->name); 445 di->un.v.delta = 446 256 / (__SHIFTOUT_MASK(mix->mask) + 1); 447 di->type = AUDIO_MIXER_VALUE; 448 di->next = di->prev = AUDIO_MIXER_LAST; 449 di->un.v.num_channels = 2; 450 strcpy(di->un.v.units.name, AudioNvolume); 451 return 0; 452 453 case A64_CODEC_OUTPUT_MUTE: 454 di->mixer_class = A64_CODEC_OUTPUT_CLASS; 455 strcpy(di->label.name, AudioNmute); 456 di->type = AUDIO_MIXER_ENUM; 457 di->next = di->prev = AUDIO_MIXER_LAST; 458 di->un.e.num_mem = 2; 459 strcpy(di->un.e.member[0].label.name, AudioNoff); 460 di->un.e.member[0].ord = 0; 461 strcpy(di->un.e.member[1].label.name, AudioNon); 462 di->un.e.member[1].ord = 1; 463 return 0; 464 465 case A64_CODEC_OUTPUT_SOURCE: 466 di->mixer_class = A64_CODEC_OUTPUT_CLASS; 467 strcpy(di->label.name, AudioNsource); 468 di->type = AUDIO_MIXER_SET; 469 di->next = di->prev = AUDIO_MIXER_LAST; 470 di->un.s.num_mem = 2; 471 strcpy(di->un.s.member[0].label.name, AudioNline); 472 di->un.s.member[0].mask = A64_OUTPUT_SOURCE_LINE; 473 strcpy(di->un.s.member[1].label.name, AudioNheadphone); 474 di->un.s.member[1].mask = A64_OUTPUT_SOURCE_HP; 475 return 0; 476 477 case A64_CODEC_RECORD_SOURCE: 478 di->mixer_class = A64_CODEC_RECORD_CLASS; 479 strcpy(di->label.name, AudioNsource); 480 di->type = AUDIO_MIXER_SET; 481 di->next = di->prev = AUDIO_MIXER_LAST; 482 di->un.s.num_mem = 4; 483 strcpy(di->un.s.member[0].label.name, AudioNline); 484 di->un.s.member[0].mask = A64_ADCMIX_SRC_LINEIN; 485 strcpy(di->un.s.member[1].label.name, AudioNmicrophone); 486 di->un.s.member[1].mask = A64_ADCMIX_SRC_MIC1; 487 strcpy(di->un.s.member[2].label.name, AudioNmicrophone "2"); 488 di->un.s.member[2].mask = A64_ADCMIX_SRC_MIC2; 489 strcpy(di->un.s.member[3].label.name, AudioNdac); 490 di->un.s.member[3].mask = A64_ADCMIX_SRC_OMIXER; 491 return 0; 492 493 } 494 495 return ENXIO; 496} 497 498static const struct audio_hw_if a64_acodec_hw_if = { 499 .trigger_output = a64_acodec_trigger_output, 500 .trigger_input = a64_acodec_trigger_input, 501 .halt_output = a64_acodec_halt_output, 502 .halt_input = a64_acodec_halt_input, 503 .set_port = a64_acodec_set_port, 504 .get_port = a64_acodec_get_port, 505 .query_devinfo = a64_acodec_query_devinfo, 506}; 507 508static audio_dai_tag_t 509a64_acodec_dai_get_tag(device_t dev, const void *data, size_t len) 510{ 511 struct a64_acodec_softc * const sc = device_private(dev); 512 513 if (len != 4) 514 return NULL; 515 516 return &sc->sc_dai; 517} 518 519static struct fdtbus_dai_controller_func a64_acodec_dai_funcs = { 520 .get_tag = a64_acodec_dai_get_tag 521}; 522 523static int 524a64_acodec_dai_jack_detect(audio_dai_tag_t dai, u_int jack, int present) 525{ 526 struct a64_acodec_softc * const sc = audio_dai_private(dai); 527 528 switch (jack) { 529 case AUDIO_DAI_JACK_HP: 530 if (present) { 531 a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 532 0, A64_LINEOUT_EN); 533 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 534 A64_HPPA_EN, 0); 535 } else { 536 a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 537 A64_LINEOUT_EN, 0); 538 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 539 0, A64_HPPA_EN); 540 } 541 542 /* Master volume controls either HP or line out */ 543 sc->sc_master_dev = present ? 544 A64_CODEC_INPUT_HP_VOLUME : A64_CODEC_INPUT_LINE_VOLUME; 545 546 break; 547 548 case AUDIO_DAI_JACK_MIC: 549 /* XXX TODO */ 550 break; 551 } 552 553 return 0; 554} 555 556static const char * compatible[] = { 557 "allwinner,sun50i-a64-codec-analog", 558 NULL 559}; 560 561static int 562a64_acodec_match(device_t parent, cfdata_t cf, void *aux) 563{ 564 struct fdt_attach_args * const faa = aux; 565 566 return of_match_compatible(faa->faa_phandle, compatible); 567} 568 569static void 570a64_acodec_attach(device_t parent, device_t self, void *aux) 571{ 572 struct a64_acodec_softc * const sc = device_private(self); 573 struct fdt_attach_args * const faa = aux; 574 const int phandle = faa->faa_phandle; 575 bus_addr_t addr; 576 bus_size_t size; 577 578 sc->sc_dev = self; 579 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 580 aprint_error(": couldn't get registers\n"); 581 return; 582 } 583 sc->sc_bst = faa->faa_bst; 584 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 585 aprint_error(": couldn't map registers\n"); 586 return; 587 } 588 589 sc->sc_phandle = phandle; 590 591 aprint_naive("\n"); 592 aprint_normal(": A64 Audio Codec (analog part)\n"); 593 594 /* Right & Left Headphone PA enable */ 595 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 596 A64_HPPA_EN, 0); 597 598 /* Jack detect enable */ 599 sc->sc_master_dev = A64_CODEC_INPUT_HP_VOLUME; 600 a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL, 601 A64_JACKDETEN | A64_INNERRESEN | A64_AUTOPLEN, 0); 602 603 /* Unmute DAC to output mixer */ 604 a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL, 605 A64_LMIXMUTE_LDAC, 0); 606 a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL, 607 A64_RMIXMUTE_RDAC, 0); 608 609 sc->sc_dai.dai_jack_detect = a64_acodec_dai_jack_detect; 610 sc->sc_dai.dai_hw_if = &a64_acodec_hw_if; 611 sc->sc_dai.dai_dev = self; 612 sc->sc_dai.dai_priv = sc; 613 fdtbus_register_dai_controller(self, phandle, &a64_acodec_dai_funcs); 614} 615 616CFATTACH_DECL_NEW(a64_acodec, sizeof(struct a64_acodec_softc), 617 a64_acodec_match, a64_acodec_attach, NULL, NULL); 618