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