1/* $OpenBSD: aplaudio.c,v 1.6 2023/01/14 23:35:09 kettenis Exp $ */ 2/* 3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 4 * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> 20#include <sys/systm.h> 21#include <sys/device.h> 22#include <sys/malloc.h> 23 24#include <machine/bus.h> 25#include <machine/fdt.h> 26 27#include <dev/ofw/openfirm.h> 28#include <dev/ofw/ofw_misc.h> 29#include <dev/ofw/fdt.h> 30 31#include <sys/audioio.h> 32#include <dev/audio_if.h> 33 34#include <arm64/dev/aplmca.h> 35 36struct aplaudio_softc { 37 struct device sc_dev; 38 39 struct dai_device *sc_dai_cpu; 40 struct dai_device *sc_dai_codec[6]; 41}; 42 43void aplaudio_set_format(struct aplaudio_softc *, uint32_t, 44 uint32_t, uint32_t); 45void aplaudio_set_tdm_slots(struct aplaudio_softc *); 46 47int aplaudio_open(void *, int); 48void aplaudio_close(void *); 49int aplaudio_set_params(void *, int, int, 50 struct audio_params *, struct audio_params *); 51void *aplaudio_allocm(void *, int, size_t, int, int); 52void aplaudio_freem(void *, void *, int); 53int aplaudio_set_port(void *, mixer_ctrl_t *); 54int aplaudio_get_port(void *, mixer_ctrl_t *); 55int aplaudio_query_devinfo(void *, mixer_devinfo_t *); 56int aplaudio_round_blocksize(void *, int); 57size_t aplaudio_round_buffersize(void *, int, size_t); 58int aplaudio_trigger_output(void *, void *, void *, int, 59 void (*)(void *), void *, struct audio_params *); 60int aplaudio_trigger_input(void *, void *, void *, int, 61 void (*)(void *), void *, struct audio_params *); 62int aplaudio_halt_output(void *); 63int aplaudio_halt_input(void *); 64 65const struct audio_hw_if aplaudio_hw_if = { 66 .open = aplaudio_open, 67 .close = aplaudio_close, 68 .set_params = aplaudio_set_params, 69 .allocm = aplaudio_allocm, 70 .freem = aplaudio_freem, 71 .set_port = aplaudio_set_port, 72 .get_port = aplaudio_get_port, 73 .query_devinfo = aplaudio_query_devinfo, 74 .round_blocksize = aplaudio_round_blocksize, 75 .round_buffersize = aplaudio_round_buffersize, 76 .trigger_output = aplaudio_trigger_output, 77 .trigger_input = aplaudio_trigger_input, 78 .halt_output = aplaudio_halt_output, 79 .halt_input = aplaudio_halt_input, 80}; 81 82int aplaudio_match(struct device *, void *, void *); 83void aplaudio_attach(struct device *, struct device *, void *); 84 85const struct cfattach aplaudio_ca = { 86 sizeof (struct aplaudio_softc), aplaudio_match, aplaudio_attach 87}; 88 89struct cfdriver aplaudio_cd = { 90 NULL, "aplaudio", DV_DULL 91}; 92 93int 94aplaudio_match(struct device *parent, void *match, void *aux) 95{ 96 struct fdt_attach_args *faa = aux; 97 98 return OF_is_compatible(faa->fa_node, "apple,macaudio"); 99} 100 101void 102aplaudio_attach(struct device *parent, struct device *self, void *aux) 103{ 104 struct aplaudio_softc *sc = (struct aplaudio_softc *)self; 105 struct fdt_attach_args *faa = aux; 106 uint32_t fmt, pol, clk; 107 uint32_t node, cpu, codec; 108 uint32_t *dais; 109 char status[32]; 110 int count = 0; 111 int i, ncells; 112 int len; 113 114 printf("\n"); 115 116 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 117 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 118 strcmp(status, "disabled") == 0) 119 continue; 120 121 cpu = OF_getnodebyname(node, "cpu"); 122 if (cpu == 0) 123 continue; 124 125 sc->sc_dai_cpu = aplmca_alloc_cluster(cpu); 126 if (sc->sc_dai_cpu == NULL) 127 continue; 128 129 codec = OF_getnodebyname(node, "codec"); 130 if (codec == 0) 131 continue; 132 133 len = OF_getproplen(codec, "sound-dai"); 134 if (len < 0) 135 continue; 136 137 dais = malloc(len, M_TEMP, M_WAITOK); 138 OF_getpropintarray(codec, "sound-dai", dais, len); 139 140 ncells = len / sizeof(uint32_t); 141 ncells = MIN(ncells, nitems(sc->sc_dai_codec)); 142 for (i = 0; i < ncells; i++) { 143 sc->sc_dai_codec[i] = dai_byphandle(dais[i]); 144 if (sc->sc_dai_codec[i] == NULL) 145 continue; 146 count++; 147 } 148 149 free(dais, M_TEMP, len); 150 151 if (count == 0) 152 continue; 153 if (count > 1) 154 aplaudio_set_tdm_slots(sc); 155 156 /* XXX Parameters are missing from the device tree? */ 157 fmt = DAI_FORMAT_LJ; 158 pol = 0; 159 clk = DAI_CLOCK_CFM | DAI_CLOCK_CBM; 160 aplaudio_set_format(sc, fmt, pol, clk); 161 162 audio_attach_mi(&aplaudio_hw_if, sc, NULL, self); 163 164 /* XXX Only attach the first set of interfaces for now. */ 165 return; 166 } 167} 168 169void 170aplaudio_set_format(struct aplaudio_softc *sc, uint32_t fmt, uint32_t pol, 171 uint32_t clk) 172{ 173 struct dai_device *dai; 174 int i; 175 176 if (sc->sc_dai_cpu->dd_set_format) 177 sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie, 178 fmt, pol, clk); 179 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 180 dai = sc->sc_dai_codec[i]; 181 if (dai == NULL) 182 continue; 183 if (dai->dd_set_format) 184 dai->dd_set_format(dai->dd_cookie, fmt, pol, clk); 185 } 186} 187 188void 189aplaudio_set_tdm_slots(struct aplaudio_softc *sc) 190{ 191 struct dai_device *dai; 192 int i; 193 194 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 195 dai = sc->sc_dai_codec[i]; 196 if (dai == NULL) 197 continue; 198 if (dai->dd_set_tdm_slot) { 199 char prefix[8]; 200 int slot = 0; 201 202 if (OF_getprop(dai->dd_node, "sound-name-prefix", 203 prefix, sizeof(prefix)) > 0) { 204 if (strncmp(prefix, "Right", 5) == 0) 205 slot = 1; 206 } 207 208 dai->dd_set_tdm_slot(dai->dd_cookie, slot); 209 } 210 } 211} 212 213int 214aplaudio_open(void *cookie, int flags) 215{ 216 struct aplaudio_softc *sc = cookie; 217 struct dai_device *dai; 218 const struct audio_hw_if *hwif; 219 int error; 220 int i; 221 222 dai = sc->sc_dai_cpu; 223 hwif = dai->dd_hw_if; 224 if (hwif->open) { 225 error = hwif->open(dai->dd_cookie, flags); 226 if (error) { 227 aplaudio_close(cookie); 228 return error; 229 } 230 } 231 232 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 233 dai = sc->sc_dai_codec[i]; 234 if (dai == NULL) 235 continue; 236 hwif = dai->dd_hw_if; 237 if (hwif->open) { 238 error = hwif->open(dai->dd_cookie, flags); 239 if (error) { 240 aplaudio_close(cookie); 241 return error; 242 } 243 } 244 } 245 246 return 0; 247} 248 249void 250aplaudio_close(void *cookie) 251{ 252 struct aplaudio_softc *sc = cookie; 253 struct dai_device *dai; 254 const struct audio_hw_if *hwif; 255 int i; 256 257 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 258 dai = sc->sc_dai_codec[i]; 259 if (dai == NULL) 260 continue; 261 hwif = dai->dd_hw_if; 262 if (hwif->close) 263 hwif->close(dai->dd_cookie); 264 } 265 266 dai = sc->sc_dai_cpu; 267 hwif = dai->dd_hw_if; 268 if (hwif->close) 269 hwif->close(dai->dd_cookie); 270} 271 272int 273aplaudio_set_params(void *cookie, int setmode, int usemode, 274 struct audio_params *play, struct audio_params *rec) 275{ 276 struct aplaudio_softc *sc = cookie; 277 struct dai_device *dai; 278 const struct audio_hw_if *hwif; 279 uint32_t rate; 280 int error; 281 int i; 282 283 dai = sc->sc_dai_cpu; 284 hwif = dai->dd_hw_if; 285 if (hwif->set_params) { 286 error = hwif->set_params(dai->dd_cookie, 287 setmode, usemode, play, rec); 288 if (error) 289 return error; 290 } 291 292 if (setmode & AUMODE_PLAY) 293 rate = play->sample_rate * play->channels * play->bps * 8; 294 else 295 rate = rec->sample_rate * rec->channels * rec->bps * 8; 296 297 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 298 dai = sc->sc_dai_codec[i]; 299 if (dai == NULL) 300 continue; 301 if (dai->dd_set_sysclk) { 302 error = dai->dd_set_sysclk(dai->dd_cookie, rate); 303 if (error) 304 return error; 305 } 306 } 307 308 dai = sc->sc_dai_cpu; 309 if (dai->dd_set_sysclk) { 310 error = dai->dd_set_sysclk(dai->dd_cookie, rate); 311 if (error) 312 return error; 313 } 314 315 return 0; 316} 317 318void * 319aplaudio_allocm(void *cookie, int direction, size_t size, int type, 320 int flags) 321{ 322 struct aplaudio_softc *sc = cookie; 323 struct dai_device *dai = sc->sc_dai_cpu; 324 const struct audio_hw_if *hwif = dai->dd_hw_if; 325 326 if (hwif->allocm) 327 return hwif->allocm(dai->dd_cookie, 328 direction, size, type, flags); 329 330 return NULL; 331} 332 333void 334aplaudio_freem(void *cookie, void *addr, int type) 335{ 336 struct aplaudio_softc *sc = cookie; 337 struct dai_device *dai = sc->sc_dai_cpu; 338 const struct audio_hw_if *hwif = dai->dd_hw_if; 339 340 if (hwif->freem) 341 hwif->freem(dai->dd_cookie, addr, type); 342} 343 344int 345aplaudio_set_port(void *cookie, mixer_ctrl_t *cp) 346{ 347 struct aplaudio_softc *sc = cookie; 348 struct dai_device *dai; 349 const struct audio_hw_if *hwif; 350 int error = ENXIO; 351 int i; 352 353 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 354 dai = sc->sc_dai_codec[i]; 355 if (dai == NULL) 356 continue; 357 hwif = dai->dd_hw_if; 358 if (hwif->set_port) 359 error = hwif->set_port(dai->dd_cookie, cp); 360 } 361 362 return error; 363} 364 365int 366aplaudio_get_port(void *cookie, mixer_ctrl_t *cp) 367{ 368 struct aplaudio_softc *sc = cookie; 369 struct dai_device *dai; 370 const struct audio_hw_if *hwif; 371 int error = ENXIO; 372 int i; 373 374 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 375 dai = sc->sc_dai_codec[i]; 376 if (dai == NULL) 377 continue; 378 hwif = dai->dd_hw_if; 379 if (hwif->get_port) 380 error = hwif->get_port(dai->dd_cookie, cp); 381 } 382 383 return error; 384} 385 386int 387aplaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip) 388{ 389 struct aplaudio_softc *sc = cookie; 390 struct dai_device *dai; 391 const struct audio_hw_if *hwif; 392 int i; 393 394 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 395 dai = sc->sc_dai_codec[i]; 396 if (dai == NULL) 397 continue; 398 hwif = dai->dd_hw_if; 399 if (hwif->query_devinfo) 400 return hwif->query_devinfo(dai->dd_cookie, dip); 401 } 402 403 return ENXIO; 404} 405 406int 407aplaudio_round_blocksize(void *cookie, int block) 408{ 409 struct aplaudio_softc *sc = cookie; 410 struct dai_device *dai = sc->sc_dai_cpu; 411 const struct audio_hw_if *hwif = dai->dd_hw_if; 412 413 if (hwif->round_blocksize) 414 return hwif->round_blocksize(dai->dd_cookie, block); 415 416 return block; 417} 418 419size_t 420aplaudio_round_buffersize(void *cookie, int direction, size_t bufsize) 421{ 422 struct aplaudio_softc *sc = cookie; 423 struct dai_device *dai = sc->sc_dai_cpu; 424 const struct audio_hw_if *hwif = dai->dd_hw_if; 425 426 if (hwif->round_buffersize) 427 return hwif->round_buffersize(dai->dd_cookie, 428 direction, bufsize); 429 430 return bufsize; 431} 432 433int 434aplaudio_trigger_output(void *cookie, void *start, void *end, int blksize, 435 void (*intr)(void *), void *arg, struct audio_params *param) 436{ 437 struct aplaudio_softc *sc = cookie; 438 struct dai_device *dai; 439 const struct audio_hw_if *hwif; 440 int error; 441 int i; 442 443 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 444 dai = sc->sc_dai_codec[i]; 445 if (dai == NULL) 446 continue; 447 hwif = dai->dd_hw_if; 448 if (hwif->trigger_output) { 449 error = hwif->trigger_output(dai->dd_cookie, 450 start, end, blksize, intr, arg, param); 451 if (error) { 452 aplaudio_halt_output(cookie); 453 return error; 454 } 455 } 456 } 457 458 dai = sc->sc_dai_cpu; 459 hwif = dai->dd_hw_if; 460 if (hwif->trigger_output) { 461 error = hwif->trigger_output(dai->dd_cookie, 462 start, end, blksize, intr, arg, param); 463 if (error) { 464 aplaudio_halt_output(cookie); 465 return error; 466 } 467 } 468 469 return 0; 470} 471 472int 473aplaudio_trigger_input(void *cookie, void *start, void *end, int blksize, 474 void (*intr)(void *), void *arg, struct audio_params *param) 475{ 476 struct aplaudio_softc *sc = cookie; 477 struct dai_device *dai; 478 const struct audio_hw_if *hwif; 479 int error; 480 int i; 481 482 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 483 dai = sc->sc_dai_codec[i]; 484 if (dai == NULL) 485 continue; 486 hwif = dai->dd_hw_if; 487 if (hwif->trigger_input) { 488 error = hwif->trigger_input(dai->dd_cookie, 489 start, end, blksize, intr, arg, param); 490 if (error) { 491 aplaudio_halt_input(cookie); 492 return error; 493 } 494 } 495 } 496 497 dai = sc->sc_dai_cpu; 498 hwif = dai->dd_hw_if; 499 if (hwif->trigger_input) { 500 error = hwif->trigger_input(dai->dd_cookie, 501 start, end, blksize, intr, arg, param); 502 if (error) { 503 aplaudio_halt_input(cookie); 504 return error; 505 } 506 } 507 508 return 0; 509} 510 511int 512aplaudio_halt_output(void *cookie) 513{ 514 struct aplaudio_softc *sc = cookie; 515 struct dai_device *dai; 516 const struct audio_hw_if *hwif; 517 int i; 518 519 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 520 dai = sc->sc_dai_codec[i]; 521 if (dai == NULL) 522 continue; 523 hwif = dai->dd_hw_if; 524 if (hwif->halt_output) 525 hwif->halt_output(dai->dd_cookie); 526 } 527 528 dai = sc->sc_dai_cpu; 529 hwif = dai->dd_hw_if; 530 if (hwif->halt_output) 531 hwif->halt_output(dai->dd_cookie); 532 533 return 0; 534} 535 536int 537aplaudio_halt_input(void *cookie) 538{ 539 struct aplaudio_softc *sc = cookie; 540 struct dai_device *dai; 541 const struct audio_hw_if *hwif; 542 int i; 543 544 for (i = 0; i < nitems(sc->sc_dai_codec); i++) { 545 dai = sc->sc_dai_codec[i]; 546 if (dai == NULL) 547 continue; 548 hwif = dai->dd_hw_if; 549 if (hwif->halt_input) 550 hwif->halt_input(dai->dd_cookie); 551 } 552 553 dai = sc->sc_dai_cpu; 554 hwif = dai->dd_hw_if; 555 if (hwif->halt_input) 556 hwif->halt_input(dai->dd_cookie); 557 558 return 0; 559} 560