1 /* $NetBSD: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki 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: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki 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/gpio.h> 38 39 #include <sys/audioio.h> 40 #include <dev/audio/audio_if.h> 41 #include <dev/audio/audio_dai.h> 42 43 #include <dev/fdt/fdtvar.h> 44 45 static const char *compatible[] = { "simple-audio-card", NULL }; 46 47 struct ausoc_link { 48 const char *link_name; 49 50 audio_dai_tag_t link_cpu; 51 audio_dai_tag_t link_codec; 52 audio_dai_tag_t *link_aux; 53 u_int link_naux; 54 55 u_int link_mclk_fs; 56 57 kmutex_t link_lock; 58 kmutex_t link_intr_lock; 59 }; 60 61 struct ausoc_softc { 62 device_t sc_dev; 63 int sc_phandle; 64 const char *sc_name; 65 66 struct ausoc_link *sc_link; 67 u_int sc_nlink; 68 }; 69 70 static void 71 ausoc_close(void *priv) 72 { 73 struct ausoc_link * const link = priv; 74 u_int aux; 75 76 for (aux = 0; aux < link->link_naux; aux++) 77 audio_dai_close(link->link_aux[aux]); 78 audio_dai_close(link->link_codec); 79 audio_dai_close(link->link_cpu); 80 } 81 82 static int 83 ausoc_open(void *priv, int flags) 84 { 85 struct ausoc_link * const link = priv; 86 u_int aux; 87 int error; 88 89 error = audio_dai_open(link->link_cpu, flags); 90 if (error) 91 goto failed; 92 93 error = audio_dai_open(link->link_codec, flags); 94 if (error) 95 goto failed; 96 97 for (aux = 0; aux < link->link_naux; aux++) { 98 error = audio_dai_open(link->link_aux[aux], flags); 99 if (error) 100 goto failed; 101 } 102 103 return 0; 104 105 failed: 106 ausoc_close(priv); 107 return error; 108 } 109 110 static int 111 ausoc_query_format(void *priv, audio_format_query_t *afp) 112 { 113 struct ausoc_link * const link = priv; 114 115 return audio_dai_query_format(link->link_cpu, afp); 116 } 117 118 static int 119 ausoc_set_format(void *priv, int setmode, 120 const audio_params_t *play, const audio_params_t *rec, 121 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 122 { 123 struct ausoc_link * const link = priv; 124 int error; 125 126 error = audio_dai_mi_set_format(link->link_cpu, setmode, 127 play, rec, pfil, rfil); 128 if (error) 129 return error; 130 131 return audio_dai_mi_set_format(link->link_codec, setmode, 132 play, rec, pfil, rfil); 133 } 134 135 static int 136 ausoc_set_port(void *priv, mixer_ctrl_t *mc) 137 { 138 struct ausoc_link * const link = priv; 139 140 return audio_dai_set_port(link->link_codec, mc); 141 } 142 143 static int 144 ausoc_get_port(void *priv, mixer_ctrl_t *mc) 145 { 146 struct ausoc_link * const link = priv; 147 148 return audio_dai_get_port(link->link_codec, mc); 149 } 150 151 static int 152 ausoc_query_devinfo(void *priv, mixer_devinfo_t *di) 153 { 154 struct ausoc_link * const link = priv; 155 156 return audio_dai_query_devinfo(link->link_codec, di); 157 } 158 159 static void * 160 ausoc_allocm(void *priv, int dir, size_t size) 161 { 162 struct ausoc_link * const link = priv; 163 164 return audio_dai_allocm(link->link_cpu, dir, size); 165 } 166 167 static void 168 ausoc_freem(void *priv, void *addr, size_t size) 169 { 170 struct ausoc_link * const link = priv; 171 172 return audio_dai_freem(link->link_cpu, addr, size); 173 } 174 175 static int 176 ausoc_getdev(void *priv, struct audio_device *adev) 177 { 178 struct ausoc_link * const link = priv; 179 180 /* Defaults */ 181 snprintf(adev->name, sizeof(adev->name), "%s", link->link_name); 182 snprintf(adev->version, sizeof(adev->version), ""); 183 snprintf(adev->config, sizeof(adev->config), "ausoc"); 184 185 /* Codec can override */ 186 (void)audio_dai_getdev(link->link_codec, adev); 187 188 return 0; 189 } 190 191 static int 192 ausoc_get_props(void *priv) 193 { 194 struct ausoc_link * const link = priv; 195 196 return audio_dai_get_props(link->link_cpu); 197 } 198 199 static int 200 ausoc_round_blocksize(void *priv, int bs, int mode, 201 const audio_params_t *params) 202 { 203 struct ausoc_link * const link = priv; 204 205 return audio_dai_round_blocksize(link->link_cpu, bs, mode, params); 206 } 207 208 static size_t 209 ausoc_round_buffersize(void *priv, int dir, size_t bufsize) 210 { 211 struct ausoc_link * const link = priv; 212 213 return audio_dai_round_buffersize(link->link_cpu, dir, bufsize); 214 } 215 216 static int 217 ausoc_halt_output(void *priv) 218 { 219 struct ausoc_link * const link = priv; 220 u_int n; 221 222 for (n = 0; n < link->link_naux; n++) 223 audio_dai_halt(link->link_aux[n], AUMODE_PLAY); 224 225 audio_dai_halt(link->link_codec, AUMODE_PLAY); 226 227 return audio_dai_halt(link->link_cpu, AUMODE_PLAY); 228 } 229 230 static int 231 ausoc_halt_input(void *priv) 232 { 233 struct ausoc_link * const link = priv; 234 u_int n; 235 236 for (n = 0; n < link->link_naux; n++) 237 audio_dai_halt(link->link_aux[n], AUMODE_RECORD); 238 239 audio_dai_halt(link->link_codec, AUMODE_RECORD); 240 241 return audio_dai_halt(link->link_cpu, AUMODE_RECORD); 242 } 243 244 static int 245 ausoc_trigger_output(void *priv, void *start, void *end, int blksize, 246 void (*intr)(void *), void *intrarg, const audio_params_t *params) 247 { 248 struct ausoc_link * const link = priv; 249 u_int n, rate; 250 int error; 251 252 if (link->link_mclk_fs) { 253 rate = params->sample_rate * link->link_mclk_fs; 254 error = audio_dai_set_sysclk(link->link_codec, rate, 255 AUDIO_DAI_CLOCK_IN); 256 if (error) 257 goto failed; 258 error = audio_dai_set_sysclk(link->link_cpu, rate, 259 AUDIO_DAI_CLOCK_OUT); 260 if (error) 261 goto failed; 262 } 263 264 for (n = 0; n < link->link_naux; n++) { 265 error = audio_dai_trigger(link->link_aux[n], start, end, 266 blksize, intr, intrarg, params, AUMODE_PLAY); 267 if (error) 268 goto failed; 269 } 270 error = audio_dai_trigger(link->link_codec, start, end, blksize, 271 intr, intrarg, params, AUMODE_PLAY); 272 if (error) 273 goto failed; 274 275 return audio_dai_trigger(link->link_cpu, start, end, blksize, 276 intr, intrarg, params, AUMODE_PLAY); 277 278 failed: 279 ausoc_halt_output(priv); 280 return error; 281 } 282 283 static int 284 ausoc_trigger_input(void *priv, void *start, void *end, int blksize, 285 void (*intr)(void *), void *intrarg, const audio_params_t *params) 286 { 287 struct ausoc_link * const link = priv; 288 u_int n, rate; 289 int error; 290 291 if (link->link_mclk_fs) { 292 rate = params->sample_rate * link->link_mclk_fs; 293 error = audio_dai_set_sysclk(link->link_codec, rate, 294 AUDIO_DAI_CLOCK_IN); 295 if (error) 296 goto failed; 297 error = audio_dai_set_sysclk(link->link_cpu, rate, 298 AUDIO_DAI_CLOCK_OUT); 299 if (error) 300 goto failed; 301 } 302 303 for (n = 0; n < link->link_naux; n++) { 304 error = audio_dai_trigger(link->link_aux[n], start, end, 305 blksize, intr, intrarg, params, AUMODE_RECORD); 306 if (error) 307 goto failed; 308 } 309 error = audio_dai_trigger(link->link_codec, start, end, blksize, 310 intr, intrarg, params, AUMODE_RECORD); 311 if (error) 312 goto failed; 313 314 return audio_dai_trigger(link->link_cpu, start, end, blksize, 315 intr, intrarg, params, AUMODE_RECORD); 316 317 failed: 318 ausoc_halt_input(priv); 319 return error; 320 } 321 322 static void 323 ausoc_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 324 { 325 struct ausoc_link * const link = priv; 326 327 return audio_dai_get_locks(link->link_cpu, intr, thread); 328 } 329 330 static const struct audio_hw_if ausoc_hw_if = { 331 .open = ausoc_open, 332 .close = ausoc_close, 333 .query_format = ausoc_query_format, 334 .set_format = ausoc_set_format, 335 .allocm = ausoc_allocm, 336 .freem = ausoc_freem, 337 .getdev = ausoc_getdev, 338 .set_port = ausoc_set_port, 339 .get_port = ausoc_get_port, 340 .query_devinfo = ausoc_query_devinfo, 341 .get_props = ausoc_get_props, 342 .round_blocksize = ausoc_round_blocksize, 343 .round_buffersize = ausoc_round_buffersize, 344 .trigger_output = ausoc_trigger_output, 345 .trigger_input = ausoc_trigger_input, 346 .halt_output = ausoc_halt_output, 347 .halt_input = ausoc_halt_input, 348 .get_locks = ausoc_get_locks, 349 }; 350 351 static int 352 ausoc_match(device_t parent, cfdata_t cf, void *aux) 353 { 354 struct fdt_attach_args * const faa = aux; 355 356 return of_match_compatible(faa->faa_phandle, compatible); 357 } 358 359 static struct { 360 const char *name; 361 u_int fmt; 362 } ausoc_dai_formats[] = { 363 { "i2s", AUDIO_DAI_FORMAT_I2S }, 364 { "right_j", AUDIO_DAI_FORMAT_RJ }, 365 { "left_j", AUDIO_DAI_FORMAT_LJ }, 366 { "dsp_a", AUDIO_DAI_FORMAT_DSPA }, 367 { "dsp_b", AUDIO_DAI_FORMAT_DSPB }, 368 { "ac97", AUDIO_DAI_FORMAT_AC97 }, 369 { "pdm", AUDIO_DAI_FORMAT_PDM }, 370 }; 371 372 static int 373 ausoc_link_format(struct ausoc_softc *sc, struct ausoc_link *link, int phandle, 374 int dai_phandle, bool single_link, u_int *format) 375 { 376 const char *format_prop = single_link ? 377 "simple-audio-card,format" : "format"; 378 const char *frame_master_prop = single_link ? 379 "simple-audio-card,frame-master" : "frame-master"; 380 const char *bitclock_master_prop = single_link ? 381 "simple-audio-card,bitclock-master" : "bitclock-master"; 382 const char *bitclock_inversion_prop = single_link ? 383 "simple-audio-card,bitclock-inversion" : "bitclock-inversion"; 384 const char *frame_inversion_prop = single_link ? 385 "simple-audio-card,frame-inversion" : "frame-inversion"; 386 387 u_int fmt, pol, clk; 388 const char *s; 389 u_int n; 390 391 s = fdtbus_get_string(phandle, format_prop); 392 if (s) { 393 for (n = 0; n < __arraycount(ausoc_dai_formats); n++) { 394 if (strcmp(s, ausoc_dai_formats[n].name) == 0) { 395 fmt = ausoc_dai_formats[n].fmt; 396 break; 397 } 398 } 399 if (n == __arraycount(ausoc_dai_formats)) 400 return EINVAL; 401 } else { 402 fmt = AUDIO_DAI_FORMAT_I2S; 403 } 404 405 const bool frame_master = 406 dai_phandle == fdtbus_get_phandle(phandle, frame_master_prop); 407 const bool bitclock_master = 408 dai_phandle == fdtbus_get_phandle(phandle, bitclock_master_prop); 409 if (frame_master) { 410 clk = bitclock_master ? 411 AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM; 412 } else { 413 clk = bitclock_master ? 414 AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS; 415 } 416 417 const bool bitclock_inversion = of_hasprop(phandle, bitclock_inversion_prop); 418 const bool frame_inversion = of_hasprop(phandle, frame_inversion_prop); 419 if (bitclock_inversion) { 420 pol = frame_inversion ? 421 AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF; 422 } else { 423 pol = frame_inversion ? 424 AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF; 425 } 426 427 *format = __SHIFTIN(fmt, AUDIO_DAI_FORMAT_MASK) | 428 __SHIFTIN(pol, AUDIO_DAI_POLARITY_MASK) | 429 __SHIFTIN(clk, AUDIO_DAI_CLOCK_MASK); 430 431 return 0; 432 } 433 434 static void 435 ausoc_attach_link(struct ausoc_softc *sc, struct ausoc_link *link, 436 int card_phandle, int link_phandle) 437 { 438 const bool single_link = card_phandle == link_phandle; 439 const char *cpu_prop = single_link ? 440 "simple-audio-card,cpu" : "cpu"; 441 const char *codec_prop = single_link ? 442 "simple-audio-card,codec" : "codec"; 443 const char *mclk_fs_prop = single_link ? 444 "simple-audio-card,mclk-fs" : "mclk-fs"; 445 const char *node_name = fdtbus_get_string(link_phandle, "name"); 446 u_int n, format; 447 448 const int cpu_phandle = of_find_firstchild_byname(link_phandle, cpu_prop); 449 if (cpu_phandle <= 0) { 450 aprint_error_dev(sc->sc_dev, "missing %s prop on %s node\n", 451 cpu_prop, node_name); 452 return; 453 } 454 455 link->link_cpu = fdtbus_dai_acquire(cpu_phandle, "sound-dai"); 456 if (!link->link_cpu) { 457 aprint_error_dev(sc->sc_dev, 458 "couldn't acquire cpu dai on %s node\n", node_name); 459 return; 460 } 461 462 const int codec_phandle = of_find_firstchild_byname(link_phandle, codec_prop); 463 if (codec_phandle <= 0) { 464 aprint_error_dev(sc->sc_dev, "missing %s prop on %s node\n", 465 codec_prop, node_name); 466 return; 467 } 468 469 link->link_codec = fdtbus_dai_acquire(codec_phandle, "sound-dai"); 470 if (!link->link_codec) { 471 aprint_error_dev(sc->sc_dev, 472 "couldn't acquire codec dai on %s node\n", node_name); 473 return; 474 } 475 476 for (;;) { 477 if (fdtbus_dai_acquire_index(card_phandle, 478 "simple-audio-card,aux-devs", link->link_naux) == NULL) 479 break; 480 link->link_naux++; 481 } 482 if (link->link_naux) { 483 link->link_aux = kmem_zalloc(sizeof(audio_dai_tag_t) * link->link_naux, KM_SLEEP); 484 for (n = 0; n < link->link_naux; n++) { 485 link->link_aux[n] = fdtbus_dai_acquire_index(card_phandle, 486 "simple-audio-card,aux-devs", n); 487 KASSERT(link->link_aux[n] != NULL); 488 489 /* Attach aux devices to codec */ 490 audio_dai_add_device(link->link_codec, link->link_aux[n]); 491 } 492 } 493 494 of_getprop_uint32(link_phandle, mclk_fs_prop, &link->link_mclk_fs); 495 if (ausoc_link_format(sc, link, link_phandle, codec_phandle, single_link, &format) != 0) { 496 aprint_error_dev(sc->sc_dev, "couldn't parse format properties\n"); 497 return; 498 } 499 if (audio_dai_set_format(link->link_cpu, format) != 0) { 500 aprint_error_dev(sc->sc_dev, "couldn't set cpu format\n"); 501 return; 502 } 503 if (audio_dai_set_format(link->link_codec, format) != 0) { 504 aprint_error_dev(sc->sc_dev, "couldn't set codec format\n"); 505 return; 506 } 507 508 aprint_normal_dev(sc->sc_dev, "codec: %s, cpu: %s", 509 device_xname(audio_dai_device(link->link_codec)), 510 device_xname(audio_dai_device(link->link_cpu))); 511 for (n = 0; n < link->link_naux; n++) { 512 if (n == 0) 513 aprint_normal(", aux:"); 514 aprint_normal(" %s", 515 device_xname(audio_dai_device(link->link_aux[n]))); 516 } 517 aprint_normal("\n"); 518 519 audio_attach_mi(&ausoc_hw_if, link, sc->sc_dev); 520 } 521 522 static void 523 ausoc_attach_cb(device_t self) 524 { 525 struct ausoc_softc * const sc = device_private(self); 526 const int phandle = sc->sc_phandle; 527 const char *name; 528 int child, n; 529 size_t len; 530 531 /* 532 * If the root node defines a cpu and codec, there is only one link. For 533 * cards with multiple links, there will be simple-audio-card,dai-link 534 * child nodes for each one. 535 */ 536 if (of_find_firstchild_byname(phandle, "simple-audio-card,cpu") > 0 && 537 of_find_firstchild_byname(phandle, "simple-audio-card,codec") > 0) { 538 sc->sc_nlink = 1; 539 sc->sc_link = kmem_zalloc(sizeof(*sc->sc_link), KM_SLEEP); 540 sc->sc_link[0].link_name = sc->sc_name; 541 ausoc_attach_link(sc, &sc->sc_link[0], phandle, phandle); 542 } else { 543 for (child = OF_child(phandle); child; child = OF_peer(child)) { 544 name = fdtbus_get_string(child, "name"); 545 len = strlen("simple-audio-card,dai-link"); 546 if (strncmp(name, "simple-audio-card,dai-link", len) != 0) 547 continue; 548 sc->sc_nlink++; 549 } 550 if (sc->sc_nlink == 0) 551 return; 552 sc->sc_link = kmem_zalloc(sizeof(*sc->sc_link) * sc->sc_nlink, 553 KM_SLEEP); 554 for (child = OF_child(phandle), n = 0; child; child = OF_peer(child)) { 555 name = fdtbus_get_string(child, "name"); 556 len = strlen("simple-audio-card,dai-link"); 557 if (strncmp(name, "simple-audio-card,dai-link", len) != 0) 558 continue; 559 sc->sc_link[n].link_name = sc->sc_name; 560 ausoc_attach_link(sc, &sc->sc_link[n], phandle, child); 561 n++; 562 } 563 } 564 } 565 566 static void 567 ausoc_attach(device_t parent, device_t self, void *aux) 568 { 569 struct ausoc_softc * const sc = device_private(self); 570 struct fdt_attach_args * const faa = aux; 571 const int phandle = faa->faa_phandle; 572 573 sc->sc_dev = self; 574 sc->sc_phandle = phandle; 575 sc->sc_name = fdtbus_get_string(phandle, "simple-audio-card,name"); 576 if (!sc->sc_name) 577 sc->sc_name = "SoC Audio"; 578 579 aprint_naive("\n"); 580 aprint_normal(": %s\n", sc->sc_name); 581 582 /* 583 * Defer attachment until all other drivers are ready. 584 */ 585 config_defer(self, ausoc_attach_cb); 586 } 587 588 CFATTACH_DECL_NEW(ausoc, sizeof(struct ausoc_softc), 589 ausoc_match, ausoc_attach, NULL, NULL); 590