1 /* $NetBSD: sun8i_codec.c,v 1.10 2022/10/29 19:07:39 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: sun8i_codec.c,v 1.10 2022/10/29 19:07:39 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 #include <sys/gpio.h> 39 #include <sys/workqueue.h> 40 41 #include <dev/audio/audio_dai.h> 42 43 #include <dev/fdt/fdtvar.h> 44 45 #define SYSCLK_CTL 0x00c 46 #define AIF1CLK_ENA __BIT(11) 47 #define AIF1CLK_SRC __BITS(9,8) 48 #define AIF1CLK_SRC_PLL 2 49 #define SYSCLK_ENA __BIT(3) 50 #define SYSCLK_SRC __BIT(0) 51 52 #define MOD_CLK_ENA 0x010 53 #define MOD_RST_CTL 0x014 54 #define MOD_AIF1 __BIT(15) 55 #define MOD_ADC __BIT(3) 56 #define MOD_DAC __BIT(2) 57 58 #define SYS_SR_CTRL 0x018 59 #define AIF1_FS __BITS(15,12) 60 #define AIF_FS_48KHZ 8 61 62 #define AIF1CLK_CTRL 0x040 63 #define AIF1_MSTR_MOD __BIT(15) 64 #define AIF1_BCLK_INV __BIT(14) 65 #define AIF1_LRCK_INV __BIT(13) 66 #define AIF1_BCLK_DIV __BITS(12,9) 67 #define AIF1_BCLK_DIV_16 6 68 #define AIF1_LRCK_DIV __BITS(8,6) 69 #define AIF1_LRCK_DIV_16 0 70 #define AIF1_LRCK_DIV_64 2 71 #define AIF1_WORD_SIZ __BITS(5,4) 72 #define AIF1_WORD_SIZ_16 1 73 #define AIF1_DATA_FMT __BITS(3,2) 74 #define AIF1_DATA_FMT_I2S 0 75 #define AIF1_DATA_FMT_LJ 1 76 #define AIF1_DATA_FMT_RJ 2 77 #define AIF1_DATA_FMT_DSP 3 78 79 #define AIF1_DACDAT_CTRL 0x048 80 #define AIF1_DAC0L_ENA __BIT(15) 81 #define AIF1_DAC0R_ENA __BIT(14) 82 83 #define ADC_DIG_CTRL 0x100 84 #define ADC_DIG_CTRL_ENAD __BIT(15) 85 86 #define HMIC_CTRL1 0x110 87 #define HMIC_CTRL1_N __BITS(11,8) 88 #define HMIC_CTRL1_JACK_IN_IRQ_EN __BIT(4) 89 #define HMIC_CTRL1_JACK_OUT_IRQ_EN __BIT(3) 90 #define HMIC_CTRL1_MIC_DET_IRQ_EN __BIT(0) 91 92 #define HMIC_CTRL2 0x114 93 #define HMIC_CTRL2_MDATA_THRES __BITS(12,8) 94 95 #define HMIC_STS 0x118 96 #define HMIC_STS_MIC_PRESENT __BIT(6) 97 #define HMIC_STS_JACK_DET_OIRQ __BIT(4) 98 #define HMIC_STS_JACK_DET_IIRQ __BIT(3) 99 #define HMIC_STS_MIC_DET_ST __BIT(0) 100 101 #define DAC_DIG_CTRL 0x120 102 #define DAC_DIG_CTRL_ENDA __BIT(15) 103 104 #define DAC_MXR_SRC 0x130 105 #define DACL_MXR_SRC __BITS(15,12) 106 #define DACL_MXR_SRC_AIF1_DAC0L 0x8 107 #define DACR_MXR_SRC __BITS(11,8) 108 #define DACR_MXR_SRC_AIF1_DAC0R 0x8 109 110 struct sun8i_codec_softc { 111 device_t sc_dev; 112 bus_space_tag_t sc_bst; 113 bus_space_handle_t sc_bsh; 114 int sc_phandle; 115 116 struct workqueue *sc_workq; 117 struct work sc_work; 118 119 struct audio_dai_device sc_dai; 120 audio_dai_tag_t sc_codec_analog; 121 int sc_jackdet_pol; 122 123 struct fdtbus_gpio_pin *sc_pin_pa; 124 125 struct clk *sc_clk_gate; 126 struct clk *sc_clk_mod; 127 }; 128 129 #define RD4(sc, reg) \ 130 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 131 #define WR4(sc, reg, val) \ 132 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 133 134 static int 135 sun8i_codec_set_port(void *priv, mixer_ctrl_t *mc) 136 { 137 struct sun8i_codec_softc * const sc = priv; 138 139 if (!sc->sc_codec_analog) 140 return ENXIO; 141 142 return audio_dai_set_port(sc->sc_codec_analog, mc); 143 } 144 145 static int 146 sun8i_codec_get_port(void *priv, mixer_ctrl_t *mc) 147 { 148 struct sun8i_codec_softc * const sc = priv; 149 150 if (!sc->sc_codec_analog) 151 return ENXIO; 152 153 return audio_dai_get_port(sc->sc_codec_analog, mc); 154 } 155 156 static int 157 sun8i_codec_query_devinfo(void *priv, mixer_devinfo_t *di) 158 { 159 struct sun8i_codec_softc * const sc = priv; 160 161 if (!sc->sc_codec_analog) 162 return ENXIO; 163 164 return audio_dai_query_devinfo(sc->sc_codec_analog, di); 165 } 166 167 static const struct audio_hw_if sun8i_codec_hw_if = { 168 .set_port = sun8i_codec_set_port, 169 .get_port = sun8i_codec_get_port, 170 .query_devinfo = sun8i_codec_query_devinfo, 171 }; 172 173 static audio_dai_tag_t 174 sun8i_codec_dai_get_tag(device_t dev, const void *data, size_t len) 175 { 176 struct sun8i_codec_softc * const sc = device_private(dev); 177 const u_int sound_dai_cells = len / 4; 178 179 KASSERT(sound_dai_cells > 0); 180 181 /* 182 * This driver only supports AIF1 with CPU DAI at the moment. 183 * When #sound-dai-cells is 0, return this tag. When #sound-dai-cells 184 * is 1, return this tag only when the second cell contains the 185 * value 0. 186 * 187 * Update this when support for multiple interfaces is added to 188 * this driver. 189 */ 190 if (sound_dai_cells == 1) { 191 return &sc->sc_dai; 192 } 193 194 if (sound_dai_cells == 2) { 195 const u_int iface = be32dec((const u_int *)data + 1); 196 if (iface == 0) { 197 return &sc->sc_dai; 198 } 199 } 200 201 return NULL; 202 } 203 204 static struct fdtbus_dai_controller_func sun8i_codec_dai_funcs = { 205 .get_tag = sun8i_codec_dai_get_tag 206 }; 207 208 static int 209 sun8i_codec_dai_set_format(audio_dai_tag_t dai, u_int format) 210 { 211 struct sun8i_codec_softc * const sc = audio_dai_private(dai); 212 uint32_t val; 213 214 const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK); 215 const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK); 216 const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK); 217 218 val = RD4(sc, AIF1CLK_CTRL); 219 220 val &= ~AIF1_DATA_FMT; 221 switch (fmt) { 222 case AUDIO_DAI_FORMAT_I2S: 223 val |= __SHIFTIN(AIF1_DATA_FMT_I2S, AIF1_DATA_FMT); 224 break; 225 case AUDIO_DAI_FORMAT_RJ: 226 val |= __SHIFTIN(AIF1_DATA_FMT_RJ, AIF1_DATA_FMT); 227 break; 228 case AUDIO_DAI_FORMAT_LJ: 229 val |= __SHIFTIN(AIF1_DATA_FMT_LJ, AIF1_DATA_FMT); 230 break; 231 case AUDIO_DAI_FORMAT_DSPA: 232 case AUDIO_DAI_FORMAT_DSPB: 233 val |= __SHIFTIN(AIF1_DATA_FMT_DSP, AIF1_DATA_FMT); 234 break; 235 default: 236 return EINVAL; 237 } 238 239 val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV); 240 /* Codec LRCK polarity is inverted (datasheet is wrong) */ 241 if (!AUDIO_DAI_POLARITY_F(pol)) 242 val |= AIF1_LRCK_INV; 243 if (AUDIO_DAI_POLARITY_B(pol)) 244 val |= AIF1_BCLK_INV; 245 246 switch (clk) { 247 case AUDIO_DAI_CLOCK_CBM_CFM: 248 val &= ~AIF1_MSTR_MOD; /* codec is master */ 249 break; 250 case AUDIO_DAI_CLOCK_CBS_CFS: 251 val |= AIF1_MSTR_MOD; /* codec is slave */ 252 break; 253 default: 254 return EINVAL; 255 } 256 257 val &= ~AIF1_LRCK_DIV; 258 val |= __SHIFTIN(AIF1_LRCK_DIV_64, AIF1_LRCK_DIV); 259 260 val &= ~AIF1_BCLK_DIV; 261 val |= __SHIFTIN(AIF1_BCLK_DIV_16, AIF1_BCLK_DIV); 262 263 WR4(sc, AIF1CLK_CTRL, val); 264 265 return 0; 266 } 267 268 static int 269 sun8i_codec_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) 270 { 271 struct sun8i_codec_softc * const sc = audio_dai_private(dai); 272 273 if (sc->sc_codec_analog != NULL) 274 return 0; 275 276 sc->sc_codec_analog = aux; 277 278 return 0; 279 } 280 281 static void 282 sun8i_codec_set_jackdet(struct sun8i_codec_softc *sc, bool enable) 283 { 284 const uint32_t mask = 285 HMIC_CTRL1_JACK_IN_IRQ_EN | 286 HMIC_CTRL1_JACK_OUT_IRQ_EN | 287 HMIC_CTRL1_MIC_DET_IRQ_EN; 288 uint32_t val; 289 290 val = RD4(sc, HMIC_CTRL1); 291 if (enable) 292 val |= mask; 293 else 294 val &= ~mask; 295 WR4(sc, HMIC_CTRL1, val); 296 } 297 298 static int 299 sun8i_codec_intr(void *priv) 300 { 301 const uint32_t mask = 302 HMIC_STS_JACK_DET_OIRQ | 303 HMIC_STS_JACK_DET_IIRQ | 304 HMIC_STS_MIC_DET_ST; 305 struct sun8i_codec_softc * const sc = priv; 306 uint32_t val; 307 308 val = RD4(sc, HMIC_STS); 309 if (val & mask) { 310 /* Disable jack detect IRQ until work is complete */ 311 sun8i_codec_set_jackdet(sc, false); 312 313 /* Schedule pending jack detect task */ 314 workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); 315 } 316 317 return 1; 318 } 319 320 321 static void 322 sun8i_codec_thread(struct work *wk, void *priv) 323 { 324 struct sun8i_codec_softc * const sc = priv; 325 int hpdet = -1, micdet = -1; 326 uint32_t val; 327 328 val = RD4(sc, HMIC_STS); 329 330 if (sc->sc_codec_analog) { 331 if (val & HMIC_STS_JACK_DET_OIRQ) 332 hpdet = 0 ^ sc->sc_jackdet_pol; 333 else if (val & HMIC_STS_JACK_DET_IIRQ) 334 hpdet = 1 ^ sc->sc_jackdet_pol; 335 336 if (val & HMIC_STS_MIC_DET_ST) 337 micdet = !!(val & HMIC_STS_MIC_PRESENT); 338 339 if (hpdet != -1) { 340 audio_dai_jack_detect(sc->sc_codec_analog, 341 AUDIO_DAI_JACK_HP, hpdet); 342 } 343 if (micdet != -1) { 344 audio_dai_jack_detect(sc->sc_codec_analog, 345 AUDIO_DAI_JACK_MIC, micdet); 346 } 347 } 348 349 WR4(sc, HMIC_STS, val); 350 351 /* Re-enable jack detect IRQ */ 352 sun8i_codec_set_jackdet(sc, true); 353 } 354 355 static const struct device_compatible_entry compat_data[] = { 356 { .compat = "allwinner,sun8i-a33-codec" }, 357 DEVICE_COMPAT_EOL 358 }; 359 360 static int 361 sun8i_codec_match(device_t parent, cfdata_t cf, void *aux) 362 { 363 struct fdt_attach_args * const faa = aux; 364 365 return of_compatible_match(faa->faa_phandle, compat_data); 366 } 367 368 static void 369 sun8i_codec_attach(device_t parent, device_t self, void *aux) 370 { 371 struct sun8i_codec_softc * const sc = device_private(self); 372 struct fdt_attach_args * const faa = aux; 373 const int phandle = faa->faa_phandle; 374 char intrstr[128]; 375 bus_addr_t addr; 376 bus_size_t size; 377 uint32_t val; 378 void *ih; 379 380 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 381 aprint_error(": couldn't get registers\n"); 382 return; 383 } 384 385 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 386 aprint_error(": couldn't decode interrupt\n"); 387 return; 388 } 389 390 sc->sc_dev = self; 391 sc->sc_bst = faa->faa_bst; 392 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 393 aprint_error(": couldn't map registers\n"); 394 return; 395 } 396 sc->sc_jackdet_pol = 1; 397 398 sc->sc_clk_gate = fdtbus_clock_get(phandle, "bus"); 399 sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod"); 400 if (!sc->sc_clk_gate || !sc->sc_clk_mod) { 401 aprint_error(": couldn't get clocks\n"); 402 return; 403 } 404 if (clk_enable(sc->sc_clk_gate) != 0) { 405 aprint_error(": couldn't enable bus clock\n"); 406 return; 407 } 408 409 sc->sc_phandle = phandle; 410 411 aprint_naive("\n"); 412 aprint_normal(": Audio Codec\n"); 413 414 if (workqueue_create(&sc->sc_workq, "jackdet", sun8i_codec_thread, 415 sc, PRI_NONE, IPL_VM, 0) != 0) { 416 aprint_error_dev(self, "couldn't create jackdet workqueue\n"); 417 return; 418 } 419 420 /* Enable clocks */ 421 val = RD4(sc, SYSCLK_CTL); 422 val |= AIF1CLK_ENA; 423 val &= ~AIF1CLK_SRC; 424 val |= __SHIFTIN(AIF1CLK_SRC_PLL, AIF1CLK_SRC); 425 val |= SYSCLK_ENA; 426 val &= ~SYSCLK_SRC; 427 WR4(sc, SYSCLK_CTL, val); 428 WR4(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC); 429 WR4(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC); 430 431 /* Enable digital parts */ 432 WR4(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA); 433 WR4(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD); 434 435 /* Set AIF1 to 48 kHz */ 436 val = RD4(sc, SYS_SR_CTRL); 437 val &= ~AIF1_FS; 438 val |= __SHIFTIN(AIF_FS_48KHZ, AIF1_FS); 439 WR4(sc, SYS_SR_CTRL, val); 440 441 /* Set AIF1 to 16-bit */ 442 val = RD4(sc, AIF1CLK_CTRL); 443 val &= ~AIF1_WORD_SIZ; 444 val |= __SHIFTIN(AIF1_WORD_SIZ_16, AIF1_WORD_SIZ); 445 WR4(sc, AIF1CLK_CTRL, val); 446 447 /* Enable AIF1 DAC timelot 0 */ 448 val = RD4(sc, AIF1_DACDAT_CTRL); 449 val |= AIF1_DAC0L_ENA; 450 val |= AIF1_DAC0R_ENA; 451 WR4(sc, AIF1_DACDAT_CTRL, val); 452 453 /* DAC mixer source select */ 454 val = RD4(sc, DAC_MXR_SRC); 455 val &= ~DACL_MXR_SRC; 456 val |= __SHIFTIN(DACL_MXR_SRC_AIF1_DAC0L, DACL_MXR_SRC); 457 val &= ~DACR_MXR_SRC; 458 val |= __SHIFTIN(DACR_MXR_SRC_AIF1_DAC0R, DACR_MXR_SRC); 459 WR4(sc, DAC_MXR_SRC, val); 460 461 /* Enable PA power */ 462 sc->sc_pin_pa = fdtbus_gpio_acquire(phandle, "allwinner,pa-gpios", GPIO_PIN_OUTPUT); 463 if (sc->sc_pin_pa) 464 fdtbus_gpio_write(sc->sc_pin_pa, 1); 465 466 /* Enable jack detect */ 467 val = RD4(sc, HMIC_CTRL1); 468 val |= __SHIFTIN(0xff, HMIC_CTRL1_N); 469 WR4(sc, HMIC_CTRL1, val); 470 471 val = RD4(sc, HMIC_CTRL2); 472 val &= ~HMIC_CTRL2_MDATA_THRES; 473 val |= __SHIFTIN(0x17, HMIC_CTRL2_MDATA_THRES); 474 WR4(sc, HMIC_CTRL2, val); 475 476 /* Schedule initial jack detect task */ 477 workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); 478 479 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, 480 sun8i_codec_intr, sc, device_xname(self)); 481 if (ih == NULL) { 482 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 483 intrstr); 484 return; 485 } 486 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 487 488 sc->sc_dai.dai_set_format = sun8i_codec_dai_set_format; 489 sc->sc_dai.dai_add_device = sun8i_codec_dai_add_device; 490 sc->sc_dai.dai_hw_if = &sun8i_codec_hw_if; 491 sc->sc_dai.dai_dev = self; 492 sc->sc_dai.dai_priv = sc; 493 fdtbus_register_dai_controller(self, phandle, &sun8i_codec_dai_funcs); 494 } 495 496 CFATTACH_DECL_NEW(sun8i_codec, sizeof(struct sun8i_codec_softc), 497 sun8i_codec_match, sun8i_codec_attach, NULL, NULL); 498