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