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