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