1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 5 * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca> 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/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/lock.h> 34 #include <sys/module.h> 35 #include <sys/mutex.h> 36 #include <sys/rman.h> 37 #include <sys/resource.h> 38 #include <machine/bus.h> 39 40 #include <dev/ofw/ofw_bus.h> 41 #include <dev/ofw/ofw_bus_subr.h> 42 43 #include <dev/clk/clk.h> 44 #include <dev/hwreset/hwreset.h> 45 #include <dev/regulator/regulator.h> 46 47 #include "syscon_if.h" 48 49 #include "opt_snd.h" 50 #include <dev/sound/pcm/sound.h> 51 #include <dev/sound/fdt/audio_dai.h> 52 #include "audio_dai_if.h" 53 #include "mixer_if.h" 54 55 #define A64_PR_CFG 0x00 56 #define A64_AC_PR_RST (1 << 28) 57 #define A64_AC_PR_RW (1 << 24) 58 #define A64_AC_PR_ADDR_MASK (0x1f << 16) 59 #define A64_AC_PR_ADDR(n) (((n) & 0x1f) << 16) 60 #define A64_ACDA_PR_WDAT_MASK (0xff << 8) 61 #define A64_ACDA_PR_WDAT(n) (((n) & 0xff) << 8) 62 #define A64_ACDA_PR_RDAT(n) ((n) & 0xff) 63 64 #define A64_HP_CTRL 0x00 65 #define A64_HPPA_EN (1 << 6) 66 #define A64_HPVOL_MASK 0x3f 67 #define A64_HPVOL(n) ((n) & 0x3f) 68 #define A64_OL_MIX_CTRL 0x01 69 #define A64_LMIXMUTE_LDAC (1 << 1) 70 #define A64_OR_MIX_CTRL 0x02 71 #define A64_RMIXMUTE_RDAC (1 << 1) 72 #define A64_LINEOUT_CTRL0 0x05 73 #define A64_LINEOUT_LEFT_EN (1 << 7) 74 #define A64_LINEOUT_RIGHT_EN (1 << 6) 75 #define A64_LINEOUT_EN (A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN) 76 #define A64_LINEOUT_CTRL1 0x06 77 #define A64_LINEOUT_VOL __BITS(4,0) 78 #define A64_MIC1_CTRL 0x07 79 #define A64_MIC1G __BITS(6,4) 80 #define A64_MIC1AMPEN (1 << 3) 81 #define A64_MIC1BOOST __BITS(2,0) 82 #define A64_MIC2_CTRL 0x08 83 #define A64_MIC2_SEL (1 << 7) 84 #define A64_MIC2G_MASK (7 << 4) 85 #define A64_MIC2G(n) (((n) & 7) << 4) 86 #define A64_MIC2AMPEN (1 << 3) 87 #define A64_MIC2BOOST_MASK (7 << 0) 88 #define A64_MIC2BOOST(n) (((n) & 7) << 0) 89 #define A64_LINEIN_CTRL 0x09 90 #define A64_LINEING __BITS(6,4) 91 #define A64_MIX_DAC_CTRL 0x0a 92 #define A64_DACAREN (1 << 7) 93 #define A64_DACALEN (1 << 6) 94 #define A64_RMIXEN (1 << 5) 95 #define A64_LMIXEN (1 << 4) 96 #define A64_RHPPAMUTE (1 << 3) 97 #define A64_LHPPAMUTE (1 << 2) 98 #define A64_RHPIS (1 << 1) 99 #define A64_LHPIS (1 << 0) 100 #define A64_L_ADCMIX_SRC 0x0b 101 #define A64_R_ADCMIX_SRC 0x0c 102 #define A64_ADCMIX_SRC_MIC1 (1 << 6) 103 #define A64_ADCMIX_SRC_MIC2 (1 << 5) 104 #define A64_ADCMIX_SRC_LINEIN (1 << 2) 105 #define A64_ADCMIX_SRC_OMIXER (1 << 1) 106 #define A64_ADC_CTRL 0x0d 107 #define A64_ADCREN (1 << 7) 108 #define A64_ADCLEN (1 << 6) 109 #define A64_ADCG __BITS(2,0) 110 #define A64_JACK_MIC_CTRL 0x1d 111 #define A64_JACKDETEN (1 << 7) 112 #define A64_INNERRESEN (1 << 6) 113 #define A64_HMICBIASEN (1 << 5) 114 #define A64_AUTOPLEN (1 << 1) 115 116 #define A64CODEC_MIXER_DEVS ((1 << SOUND_MIXER_VOLUME) | \ 117 (1 << SOUND_MIXER_MIC)) 118 119 static struct ofw_compat_data compat_data[] = { 120 { "allwinner,sun50i-a64-codec-analog", 1}, 121 { NULL, 0 } 122 }; 123 124 struct a64codec_softc { 125 device_t dev; 126 struct resource *res; 127 struct mtx mtx; 128 u_int regaddr; /* address for the sysctl */ 129 }; 130 131 #define A64CODEC_LOCK(sc) mtx_lock(&(sc)->mtx) 132 #define A64CODEC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 133 #define A64CODEC_READ(sc, reg) bus_read_4((sc)->res, (reg)) 134 #define A64CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 135 136 static int a64codec_probe(device_t dev); 137 static int a64codec_attach(device_t dev); 138 static int a64codec_detach(device_t dev); 139 140 static u_int 141 a64_acodec_pr_read(struct a64codec_softc *sc, u_int addr) 142 { 143 uint32_t val; 144 145 /* Read current value */ 146 val = A64CODEC_READ(sc, A64_PR_CFG); 147 148 /* De-assert reset */ 149 val |= A64_AC_PR_RST; 150 A64CODEC_WRITE(sc, A64_PR_CFG, val); 151 152 /* Read mode */ 153 val &= ~A64_AC_PR_RW; 154 A64CODEC_WRITE(sc, A64_PR_CFG, val); 155 156 /* Set address */ 157 val &= ~A64_AC_PR_ADDR_MASK; 158 val |= A64_AC_PR_ADDR(addr); 159 A64CODEC_WRITE(sc, A64_PR_CFG, val); 160 161 /* Read data */ 162 val = A64CODEC_READ(sc, A64_PR_CFG); 163 return A64_ACDA_PR_RDAT(val); 164 } 165 166 static void 167 a64_acodec_pr_write(struct a64codec_softc *sc, u_int addr, u_int data) 168 { 169 uint32_t val; 170 171 /* Read current value */ 172 val = A64CODEC_READ(sc, A64_PR_CFG); 173 174 /* De-assert reset */ 175 val |= A64_AC_PR_RST; 176 A64CODEC_WRITE(sc, A64_PR_CFG, val); 177 178 /* Set address */ 179 val &= ~A64_AC_PR_ADDR_MASK; 180 val |= A64_AC_PR_ADDR(addr); 181 A64CODEC_WRITE(sc, A64_PR_CFG, val); 182 183 /* Write data */ 184 val &= ~A64_ACDA_PR_WDAT_MASK; 185 val |= A64_ACDA_PR_WDAT(data); 186 A64CODEC_WRITE(sc, A64_PR_CFG, val); 187 188 /* Write mode */ 189 val |= A64_AC_PR_RW; 190 A64CODEC_WRITE(sc, A64_PR_CFG, val); 191 192 /* Clear write mode */ 193 val &= ~A64_AC_PR_RW; 194 A64CODEC_WRITE(sc, A64_PR_CFG, val); 195 } 196 197 static void 198 a64_acodec_pr_set_clear(struct a64codec_softc *sc, u_int addr, u_int set, u_int clr) 199 { 200 u_int old, new; 201 202 old = a64_acodec_pr_read(sc, addr); 203 new = set | (old & ~clr); 204 a64_acodec_pr_write(sc, addr, new); 205 } 206 207 static int 208 a64codec_probe(device_t dev) 209 { 210 if (!ofw_bus_status_okay(dev)) 211 return (ENXIO); 212 213 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 214 return (ENXIO); 215 216 device_set_desc(dev, "Allwinner A64 Analog Codec"); 217 return (BUS_PROBE_DEFAULT); 218 } 219 220 static int 221 a64codec_attach(device_t dev) 222 { 223 struct a64codec_softc *sc; 224 int error, rid; 225 phandle_t node; 226 regulator_t reg; 227 228 sc = device_get_softc(dev); 229 sc->dev = dev; 230 231 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 232 233 rid = 0; 234 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 235 if (!sc->res) { 236 device_printf(dev, "cannot allocate resource for device\n"); 237 error = ENXIO; 238 goto fail; 239 } 240 241 if (regulator_get_by_ofw_property(dev, 0, "cpvdd-supply", ®) == 0) { 242 error = regulator_enable(reg); 243 if (error != 0) { 244 device_printf(dev, "cannot enable PHY regulator\n"); 245 goto fail; 246 } 247 } 248 249 /* Right & Left Headphone PA enable */ 250 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 251 A64_HPPA_EN, 0); 252 253 /* Microphone BIAS enable */ 254 a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL, 255 A64_HMICBIASEN | A64_INNERRESEN, 0); 256 257 /* Unmute DAC to output mixer */ 258 a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL, 259 A64_LMIXMUTE_LDAC, 0); 260 a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL, 261 A64_RMIXMUTE_RDAC, 0); 262 263 /* For now we work only with headphones */ 264 a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 265 0, A64_LINEOUT_EN); 266 a64_acodec_pr_set_clear(sc, A64_HP_CTRL, 267 A64_HPPA_EN, 0); 268 269 u_int val = a64_acodec_pr_read(sc, A64_HP_CTRL); 270 val &= ~(0x3f); 271 val |= 0x25; 272 a64_acodec_pr_write(sc, A64_HP_CTRL, val); 273 274 a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, 275 A64_MIC2AMPEN | A64_MIC2_SEL | A64_MIC2G(0x3) | A64_MIC2BOOST(0x4), 276 A64_MIC2G_MASK | A64_MIC2BOOST_MASK); 277 278 a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC, 279 A64_ADCMIX_SRC_MIC2); 280 a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC, 281 A64_ADCMIX_SRC_MIC2); 282 283 /* Max out MIC2 gain */ 284 val = a64_acodec_pr_read(sc, A64_MIC2_CTRL); 285 val &= ~(0x7); 286 val |= (0x7); 287 val &= ~(7 << 4); 288 val |= (7 << 4); 289 a64_acodec_pr_write(sc, A64_MIC2_CTRL, val); 290 291 node = ofw_bus_get_node(dev); 292 OF_device_register_xref(OF_xref_from_node(node), dev); 293 294 return (0); 295 296 fail: 297 a64codec_detach(dev); 298 return (error); 299 } 300 301 static int 302 a64codec_detach(device_t dev) 303 { 304 struct a64codec_softc *sc; 305 306 sc = device_get_softc(dev); 307 308 if (sc->res) 309 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res); 310 mtx_destroy(&sc->mtx); 311 312 return (0); 313 } 314 315 static int 316 a64codec_mixer_init(struct snd_mixer *m) 317 { 318 319 mix_setdevs(m, A64CODEC_MIXER_DEVS); 320 321 return (0); 322 } 323 324 static int 325 a64codec_mixer_uninit(struct snd_mixer *m) 326 { 327 328 return (0); 329 } 330 331 static int 332 a64codec_mixer_reinit(struct snd_mixer *m) 333 { 334 335 return (0); 336 } 337 338 static int 339 a64codec_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 340 { 341 struct a64codec_softc *sc; 342 struct mtx *mixer_lock; 343 uint8_t do_unlock; 344 u_int val; 345 346 sc = device_get_softc(mix_getdevinfo(m)); 347 mixer_lock = mixer_get_lock(m); 348 349 if (mtx_owned(mixer_lock)) { 350 do_unlock = 0; 351 } else { 352 do_unlock = 1; 353 mtx_lock(mixer_lock); 354 } 355 356 right = left; 357 358 A64CODEC_LOCK(sc); 359 switch(dev) { 360 case SOUND_MIXER_VOLUME: 361 val = a64_acodec_pr_read(sc, A64_HP_CTRL); 362 val &= ~(A64_HPVOL_MASK); 363 val |= A64_HPVOL(left * 63 / 100); 364 a64_acodec_pr_write(sc, A64_HP_CTRL, val); 365 break; 366 367 case SOUND_MIXER_MIC: 368 val = a64_acodec_pr_read(sc, A64_MIC2_CTRL); 369 val &= ~(A64_MIC2BOOST_MASK); 370 val |= A64_MIC2BOOST(left * 7 / 100); 371 a64_acodec_pr_write(sc, A64_MIC2_CTRL, val); 372 break; 373 default: 374 break; 375 } 376 A64CODEC_UNLOCK(sc); 377 378 if (do_unlock) { 379 mtx_unlock(mixer_lock); 380 } 381 382 return (left | (right << 8)); 383 } 384 385 static unsigned 386 a64codec_mixer_setrecsrc(struct snd_mixer *m, unsigned src) 387 { 388 389 return (0); 390 } 391 392 static kobj_method_t a64codec_mixer_methods[] = { 393 KOBJMETHOD(mixer_init, a64codec_mixer_init), 394 KOBJMETHOD(mixer_uninit, a64codec_mixer_uninit), 395 KOBJMETHOD(mixer_reinit, a64codec_mixer_reinit), 396 KOBJMETHOD(mixer_set, a64codec_mixer_set), 397 KOBJMETHOD(mixer_setrecsrc, a64codec_mixer_setrecsrc), 398 KOBJMETHOD_END 399 }; 400 401 MIXER_DECLARE(a64codec_mixer); 402 403 static int 404 a64codec_dai_init(device_t dev, uint32_t format) 405 { 406 407 return (0); 408 } 409 410 static int 411 a64codec_dai_trigger(device_t dev, int go, int pcm_dir) 412 { 413 struct a64codec_softc *sc = device_get_softc(dev); 414 415 if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC)) 416 return (EINVAL); 417 418 switch (go) { 419 case PCMTRIG_START: 420 if (pcm_dir == PCMDIR_PLAY) { 421 /* Enable DAC analog l/r channels, HP PA, and output mixer */ 422 a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, 423 A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | 424 A64_RHPPAMUTE | A64_LHPPAMUTE, 0); 425 } 426 else if (pcm_dir == PCMDIR_REC) { 427 /* Enable ADC analog l/r channels */ 428 a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, 429 A64_ADCREN | A64_ADCLEN, 0); 430 } 431 break; 432 433 case PCMTRIG_STOP: 434 case PCMTRIG_ABORT: 435 if (pcm_dir == PCMDIR_PLAY) { 436 /* Disable DAC analog l/r channels, HP PA, and output mixer */ 437 a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, 438 0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | 439 A64_RHPPAMUTE | A64_LHPPAMUTE); 440 } 441 else if (pcm_dir == PCMDIR_REC) { 442 /* Disable ADC analog l/r channels */ 443 a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, 444 0, A64_ADCREN | A64_ADCLEN); 445 } 446 break; 447 } 448 449 return (0); 450 } 451 452 static int 453 a64codec_dai_setup_mixer(device_t dev, device_t pcmdev) 454 { 455 456 mixer_init(pcmdev, &a64codec_mixer_class, dev); 457 458 return (0); 459 } 460 461 static device_method_t a64codec_methods[] = { 462 /* Device interface */ 463 DEVMETHOD(device_probe, a64codec_probe), 464 DEVMETHOD(device_attach, a64codec_attach), 465 DEVMETHOD(device_detach, a64codec_detach), 466 467 DEVMETHOD(audio_dai_init, a64codec_dai_init), 468 DEVMETHOD(audio_dai_setup_mixer, a64codec_dai_setup_mixer), 469 DEVMETHOD(audio_dai_trigger, a64codec_dai_trigger), 470 471 DEVMETHOD_END 472 }; 473 474 static driver_t a64codec_driver = { 475 "a64codec", 476 a64codec_methods, 477 sizeof(struct a64codec_softc), 478 }; 479 480 DRIVER_MODULE(a64codec, simplebus, a64codec_driver, 0, 0); 481 SIMPLEBUS_PNP_INFO(compat_data); 482