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