1 /* $NetBSD: sun8i_codec.c,v 1.9 2021/01/27 03:10:20 thorpej 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.9 2021/01/27 03:10:20 thorpej 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 178 if (len != 4) 179 return NULL; 180 181 return &sc->sc_dai; 182 } 183 184 static struct fdtbus_dai_controller_func sun8i_codec_dai_funcs = { 185 .get_tag = sun8i_codec_dai_get_tag 186 }; 187 188 static int 189 sun8i_codec_dai_set_format(audio_dai_tag_t dai, u_int format) 190 { 191 struct sun8i_codec_softc * const sc = audio_dai_private(dai); 192 uint32_t val; 193 194 const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK); 195 const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK); 196 const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK); 197 198 val = RD4(sc, AIF1CLK_CTRL); 199 200 val &= ~AIF1_DATA_FMT; 201 switch (fmt) { 202 case AUDIO_DAI_FORMAT_I2S: 203 val |= __SHIFTIN(AIF1_DATA_FMT_I2S, AIF1_DATA_FMT); 204 break; 205 case AUDIO_DAI_FORMAT_RJ: 206 val |= __SHIFTIN(AIF1_DATA_FMT_RJ, AIF1_DATA_FMT); 207 break; 208 case AUDIO_DAI_FORMAT_LJ: 209 val |= __SHIFTIN(AIF1_DATA_FMT_LJ, AIF1_DATA_FMT); 210 break; 211 case AUDIO_DAI_FORMAT_DSPA: 212 case AUDIO_DAI_FORMAT_DSPB: 213 val |= __SHIFTIN(AIF1_DATA_FMT_DSP, AIF1_DATA_FMT); 214 break; 215 default: 216 return EINVAL; 217 } 218 219 val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV); 220 /* Codec LRCK polarity is inverted (datasheet is wrong) */ 221 if (!AUDIO_DAI_POLARITY_F(pol)) 222 val |= AIF1_LRCK_INV; 223 if (AUDIO_DAI_POLARITY_B(pol)) 224 val |= AIF1_BCLK_INV; 225 226 switch (clk) { 227 case AUDIO_DAI_CLOCK_CBM_CFM: 228 val &= ~AIF1_MSTR_MOD; /* codec is master */ 229 break; 230 case AUDIO_DAI_CLOCK_CBS_CFS: 231 val |= AIF1_MSTR_MOD; /* codec is slave */ 232 break; 233 default: 234 return EINVAL; 235 } 236 237 val &= ~AIF1_LRCK_DIV; 238 val |= __SHIFTIN(AIF1_LRCK_DIV_64, AIF1_LRCK_DIV); 239 240 val &= ~AIF1_BCLK_DIV; 241 val |= __SHIFTIN(AIF1_BCLK_DIV_16, AIF1_BCLK_DIV); 242 243 WR4(sc, AIF1CLK_CTRL, val); 244 245 return 0; 246 } 247 248 static int 249 sun8i_codec_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) 250 { 251 struct sun8i_codec_softc * const sc = audio_dai_private(dai); 252 253 if (sc->sc_codec_analog != NULL) 254 return 0; 255 256 sc->sc_codec_analog = aux; 257 258 return 0; 259 } 260 261 static void 262 sun8i_codec_set_jackdet(struct sun8i_codec_softc *sc, bool enable) 263 { 264 const uint32_t mask = 265 HMIC_CTRL1_JACK_IN_IRQ_EN | 266 HMIC_CTRL1_JACK_OUT_IRQ_EN | 267 HMIC_CTRL1_MIC_DET_IRQ_EN; 268 uint32_t val; 269 270 val = RD4(sc, HMIC_CTRL1); 271 if (enable) 272 val |= mask; 273 else 274 val &= ~mask; 275 WR4(sc, HMIC_CTRL1, val); 276 } 277 278 static int 279 sun8i_codec_intr(void *priv) 280 { 281 const uint32_t mask = 282 HMIC_STS_JACK_DET_OIRQ | 283 HMIC_STS_JACK_DET_IIRQ | 284 HMIC_STS_MIC_DET_ST; 285 struct sun8i_codec_softc * const sc = priv; 286 uint32_t val; 287 288 val = RD4(sc, HMIC_STS); 289 if (val & mask) { 290 /* Disable jack detect IRQ until work is complete */ 291 sun8i_codec_set_jackdet(sc, false); 292 293 /* Schedule pending jack detect task */ 294 workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); 295 } 296 297 return 1; 298 } 299 300 301 static void 302 sun8i_codec_thread(struct work *wk, void *priv) 303 { 304 struct sun8i_codec_softc * const sc = priv; 305 int hpdet = -1, micdet = -1; 306 uint32_t val; 307 308 val = RD4(sc, HMIC_STS); 309 310 if (sc->sc_codec_analog) { 311 if (val & HMIC_STS_JACK_DET_OIRQ) 312 hpdet = 0 ^ sc->sc_jackdet_pol; 313 else if (val & HMIC_STS_JACK_DET_IIRQ) 314 hpdet = 1 ^ sc->sc_jackdet_pol; 315 316 if (val & HMIC_STS_MIC_DET_ST) 317 micdet = !!(val & HMIC_STS_MIC_PRESENT); 318 319 if (hpdet != -1) { 320 audio_dai_jack_detect(sc->sc_codec_analog, 321 AUDIO_DAI_JACK_HP, hpdet); 322 } 323 if (micdet != -1) { 324 audio_dai_jack_detect(sc->sc_codec_analog, 325 AUDIO_DAI_JACK_MIC, micdet); 326 } 327 } 328 329 WR4(sc, HMIC_STS, val); 330 331 /* Re-enable jack detect IRQ */ 332 sun8i_codec_set_jackdet(sc, true); 333 } 334 335 static const struct device_compatible_entry compat_data[] = { 336 { .compat = "allwinner,sun8i-a33-codec" }, 337 DEVICE_COMPAT_EOL 338 }; 339 340 static int 341 sun8i_codec_match(device_t parent, cfdata_t cf, void *aux) 342 { 343 struct fdt_attach_args * const faa = aux; 344 345 return of_compatible_match(faa->faa_phandle, compat_data); 346 } 347 348 static void 349 sun8i_codec_attach(device_t parent, device_t self, void *aux) 350 { 351 struct sun8i_codec_softc * const sc = device_private(self); 352 struct fdt_attach_args * const faa = aux; 353 const int phandle = faa->faa_phandle; 354 char intrstr[128]; 355 bus_addr_t addr; 356 bus_size_t size; 357 uint32_t val; 358 void *ih; 359 360 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 361 aprint_error(": couldn't get registers\n"); 362 return; 363 } 364 365 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 366 aprint_error(": couldn't decode interrupt\n"); 367 return; 368 } 369 370 sc->sc_dev = self; 371 sc->sc_bst = faa->faa_bst; 372 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 373 aprint_error(": couldn't map registers\n"); 374 return; 375 } 376 sc->sc_jackdet_pol = 1; 377 378 sc->sc_clk_gate = fdtbus_clock_get(phandle, "bus"); 379 sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod"); 380 if (!sc->sc_clk_gate || !sc->sc_clk_mod) { 381 aprint_error(": couldn't get clocks\n"); 382 return; 383 } 384 if (clk_enable(sc->sc_clk_gate) != 0) { 385 aprint_error(": couldn't enable bus clock\n"); 386 return; 387 } 388 389 sc->sc_phandle = phandle; 390 391 aprint_naive("\n"); 392 aprint_normal(": Audio Codec\n"); 393 394 if (workqueue_create(&sc->sc_workq, "jackdet", sun8i_codec_thread, 395 sc, PRI_NONE, IPL_VM, 0) != 0) { 396 aprint_error_dev(self, "couldn't create jackdet workqueue\n"); 397 return; 398 } 399 400 /* Enable clocks */ 401 val = RD4(sc, SYSCLK_CTL); 402 val |= AIF1CLK_ENA; 403 val &= ~AIF1CLK_SRC; 404 val |= __SHIFTIN(AIF1CLK_SRC_PLL, AIF1CLK_SRC); 405 val |= SYSCLK_ENA; 406 val &= ~SYSCLK_SRC; 407 WR4(sc, SYSCLK_CTL, val); 408 WR4(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC); 409 WR4(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC); 410 411 /* Enable digital parts */ 412 WR4(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA); 413 WR4(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD); 414 415 /* Set AIF1 to 48 kHz */ 416 val = RD4(sc, SYS_SR_CTRL); 417 val &= ~AIF1_FS; 418 val |= __SHIFTIN(AIF_FS_48KHZ, AIF1_FS); 419 WR4(sc, SYS_SR_CTRL, val); 420 421 /* Set AIF1 to 16-bit */ 422 val = RD4(sc, AIF1CLK_CTRL); 423 val &= ~AIF1_WORD_SIZ; 424 val |= __SHIFTIN(AIF1_WORD_SIZ_16, AIF1_WORD_SIZ); 425 WR4(sc, AIF1CLK_CTRL, val); 426 427 /* Enable AIF1 DAC timelot 0 */ 428 val = RD4(sc, AIF1_DACDAT_CTRL); 429 val |= AIF1_DAC0L_ENA; 430 val |= AIF1_DAC0R_ENA; 431 WR4(sc, AIF1_DACDAT_CTRL, val); 432 433 /* DAC mixer source select */ 434 val = RD4(sc, DAC_MXR_SRC); 435 val &= ~DACL_MXR_SRC; 436 val |= __SHIFTIN(DACL_MXR_SRC_AIF1_DAC0L, DACL_MXR_SRC); 437 val &= ~DACR_MXR_SRC; 438 val |= __SHIFTIN(DACR_MXR_SRC_AIF1_DAC0R, DACR_MXR_SRC); 439 WR4(sc, DAC_MXR_SRC, val); 440 441 /* Enable PA power */ 442 sc->sc_pin_pa = fdtbus_gpio_acquire(phandle, "allwinner,pa-gpios", GPIO_PIN_OUTPUT); 443 if (sc->sc_pin_pa) 444 fdtbus_gpio_write(sc->sc_pin_pa, 1); 445 446 /* Enable jack detect */ 447 val = RD4(sc, HMIC_CTRL1); 448 val |= __SHIFTIN(0xff, HMIC_CTRL1_N); 449 WR4(sc, HMIC_CTRL1, val); 450 451 val = RD4(sc, HMIC_CTRL2); 452 val &= ~HMIC_CTRL2_MDATA_THRES; 453 val |= __SHIFTIN(0x17, HMIC_CTRL2_MDATA_THRES); 454 WR4(sc, HMIC_CTRL2, val); 455 456 /* Schedule initial jack detect task */ 457 workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); 458 459 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, 460 sun8i_codec_intr, sc, device_xname(self)); 461 if (ih == NULL) { 462 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 463 intrstr); 464 return; 465 } 466 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 467 468 sc->sc_dai.dai_set_format = sun8i_codec_dai_set_format; 469 sc->sc_dai.dai_add_device = sun8i_codec_dai_add_device; 470 sc->sc_dai.dai_hw_if = &sun8i_codec_hw_if; 471 sc->sc_dai.dai_dev = self; 472 sc->sc_dai.dai_priv = sc; 473 fdtbus_register_dai_controller(self, phandle, &sun8i_codec_dai_funcs); 474 } 475 476 CFATTACH_DECL_NEW(sun8i_codec, sizeof(struct sun8i_codec_softc), 477 sun8i_codec_match, sun8i_codec_attach, NULL, NULL); 478