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