1 /* $OpenBSD: graphaudio.c,v 1.1 2021/04/07 17:12:22 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se> 4 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 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/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_misc.h> 28 29 #include <sys/audioio.h> 30 #include <dev/audio_if.h> 31 #include <dev/midi_if.h> 32 33 struct graphaudio_softc { 34 struct device sc_dev; 35 int sc_node; 36 37 uint32_t sc_mclk_fs; 38 39 struct dai_device *sc_dai_cpu; 40 struct dai_device *sc_dai_codec; 41 }; 42 43 int graphaudio_match(struct device *, void *, void *); 44 void graphaudio_attach(struct device *, struct device *, void *); 45 void graphaudio_attach_deferred(struct device *); 46 void graphaudio_set_format(struct graphaudio_softc *, uint32_t, 47 uint32_t, uint32_t); 48 49 int graphaudio_open(void *, int); 50 void graphaudio_close(void *); 51 int graphaudio_set_params(void *, int, int, 52 struct audio_params *, struct audio_params *); 53 void *graphaudio_allocm(void *, int, size_t, int, int); 54 void graphaudio_freem(void *, void *, int); 55 int graphaudio_set_port(void *, mixer_ctrl_t *); 56 int graphaudio_get_port(void *, mixer_ctrl_t *); 57 int graphaudio_query_devinfo(void *, mixer_devinfo_t *); 58 int graphaudio_get_props(void *); 59 int graphaudio_round_blocksize(void *, int); 60 size_t graphaudio_round_buffersize(void *, int, size_t); 61 int graphaudio_trigger_output(void *, void *, void *, int, 62 void (*)(void *), void *, struct audio_params *); 63 int graphaudio_trigger_input(void *, void *, void *, int, 64 void (*)(void *), void *, struct audio_params *); 65 int graphaudio_halt_output(void *); 66 int graphaudio_halt_input(void *); 67 68 struct audio_hw_if graphaudio_hw_if = { 69 .open = graphaudio_open, 70 .close = graphaudio_close, 71 .set_params = graphaudio_set_params, 72 .allocm = graphaudio_allocm, 73 .freem = graphaudio_freem, 74 .set_port = graphaudio_set_port, 75 .get_port = graphaudio_get_port, 76 .query_devinfo = graphaudio_query_devinfo, 77 .get_props = graphaudio_get_props, 78 .round_blocksize = graphaudio_round_blocksize, 79 .round_buffersize = graphaudio_round_buffersize, 80 .trigger_output = graphaudio_trigger_output, 81 .trigger_input = graphaudio_trigger_input, 82 .halt_output = graphaudio_halt_output, 83 .halt_input = graphaudio_halt_input, 84 }; 85 86 struct cfattach graphaudio_ca = { 87 sizeof(struct graphaudio_softc), graphaudio_match, graphaudio_attach 88 }; 89 90 struct cfdriver graphaudio_cd = { 91 NULL, "graphaudio", DV_DULL 92 }; 93 94 int 95 graphaudio_match(struct device *parent, void *match, void *aux) 96 { 97 struct fdt_attach_args *faa = aux; 98 99 return OF_is_compatible(faa->fa_node, "audio-graph-card"); 100 } 101 102 void 103 graphaudio_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct graphaudio_softc *sc = (struct graphaudio_softc *)self; 106 struct fdt_attach_args *faa = aux; 107 108 printf("\n"); 109 110 sc->sc_node = faa->fa_node; 111 config_defer(self, graphaudio_attach_deferred); 112 } 113 114 void 115 graphaudio_attach_deferred(struct device *self) 116 { 117 struct graphaudio_softc *sc = (struct graphaudio_softc *)self; 118 char format[16] = { 0 }; 119 uint32_t fmt, pol, clk; 120 uint32_t dais; 121 struct device_ports *dp; 122 struct endpoint *ep, *rep; 123 124 dais = OF_getpropint(sc->sc_node, "dais", 0); 125 dp = device_ports_byphandle(dais); 126 if (dp == NULL) 127 return; 128 129 ep = endpoint_byreg(dp, -1, -1); 130 if (ep == NULL) 131 return; 132 133 rep = endpoint_remote(ep); 134 if (rep == NULL) 135 return; 136 137 sc->sc_mclk_fs = OF_getpropint(ep->ep_node, "mclk-fs", 0); 138 139 sc->sc_dai_cpu = endpoint_get_cookie(ep); 140 sc->sc_dai_codec = endpoint_get_cookie(rep); 141 142 if (sc->sc_dai_cpu == NULL || sc->sc_dai_codec == NULL) 143 return; 144 145 OF_getprop(ep->ep_node, "dai-format", format, sizeof(format)); 146 if (!strcmp(format, "i2s")) 147 fmt = DAI_FORMAT_I2S; 148 else if (!strcmp(format, "right_j")) 149 fmt = DAI_FORMAT_RJ; 150 else if (!strcmp(format, "left_j")) 151 fmt = DAI_FORMAT_LJ; 152 else if (!strcmp(format, "dsp_a")) 153 fmt = DAI_FORMAT_DSPA; 154 else if (!strcmp(format, "dsp_b")) 155 fmt = DAI_FORMAT_DSPB; 156 else if (!strcmp(format, "ac97")) 157 fmt = DAI_FORMAT_AC97; 158 else if (!strcmp(format, "pdm")) 159 fmt = DAI_FORMAT_PDM; 160 else if (!strcmp(format, "msb")) 161 fmt = DAI_FORMAT_MSB; 162 else if (!strcmp(format, "lsb")) 163 fmt = DAI_FORMAT_LSB; 164 else 165 return; 166 167 pol = 0; 168 if (OF_getproplen(ep->ep_node, "frame-inversion") == 0) 169 pol |= DAI_POLARITY_IF; 170 else 171 pol |= DAI_POLARITY_NF; 172 if (OF_getproplen(ep->ep_node, "bitclock-inversion") == 0) 173 pol |= DAI_POLARITY_IB; 174 else 175 pol |= DAI_POLARITY_NB; 176 177 clk = 0; 178 if (OF_getproplen(ep->ep_node, "frame-master") == 0) 179 clk |= DAI_CLOCK_CFM; 180 else 181 clk |= DAI_CLOCK_CFS; 182 if (OF_getproplen(ep->ep_node, "bitclock-master") == 0) 183 clk |= DAI_CLOCK_CBM; 184 else 185 clk |= DAI_CLOCK_CBS; 186 187 graphaudio_set_format(sc, fmt, pol, clk); 188 189 audio_attach_mi(&graphaudio_hw_if, sc, &sc->sc_dev); 190 } 191 192 void 193 graphaudio_set_format(struct graphaudio_softc *sc, uint32_t fmt, uint32_t pol, 194 uint32_t clk) 195 { 196 if (sc->sc_dai_cpu->dd_set_format) 197 sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie, 198 fmt, pol, clk); 199 if (sc->sc_dai_codec->dd_set_format) 200 sc->sc_dai_codec->dd_set_format(sc->sc_dai_codec->dd_cookie, 201 fmt, pol, clk); 202 } 203 204 int 205 graphaudio_open(void *cookie, int flags) 206 { 207 struct graphaudio_softc *sc = cookie; 208 struct dai_device *dai; 209 struct audio_hw_if *hwif; 210 int error; 211 212 dai = sc->sc_dai_cpu; 213 hwif = dai->dd_hw_if; 214 if (hwif->open) { 215 error = hwif->open(dai->dd_cookie, flags); 216 if (error) { 217 graphaudio_close(cookie); 218 return error; 219 } 220 } 221 222 dai = sc->sc_dai_codec; 223 hwif = dai->dd_hw_if; 224 if (hwif->open) { 225 error = hwif->open(dai->dd_cookie, flags); 226 if (error) { 227 graphaudio_close(cookie); 228 return error; 229 } 230 } 231 232 return 0; 233 } 234 235 void 236 graphaudio_close(void *cookie) 237 { 238 struct graphaudio_softc *sc = cookie; 239 struct dai_device *dai; 240 struct audio_hw_if *hwif; 241 242 dai = sc->sc_dai_codec; 243 hwif = dai->dd_hw_if; 244 if (hwif->close) 245 hwif->close(dai->dd_cookie); 246 247 dai = sc->sc_dai_cpu; 248 hwif = dai->dd_hw_if; 249 if (hwif->close) 250 hwif->close(dai->dd_cookie); 251 } 252 253 int 254 graphaudio_set_params(void *cookie, int setmode, int usemode, 255 struct audio_params *play, struct audio_params *rec) 256 { 257 struct graphaudio_softc *sc = cookie; 258 struct dai_device *dai; 259 struct audio_hw_if *hwif; 260 uint32_t rate; 261 int error; 262 263 if (sc->sc_mclk_fs) { 264 if (setmode & AUMODE_PLAY) 265 rate = play->sample_rate * sc->sc_mclk_fs; 266 else 267 rate = rec->sample_rate * sc->sc_mclk_fs; 268 269 dai = sc->sc_dai_codec; 270 if (dai->dd_set_sysclk) { 271 error = dai->dd_set_sysclk(dai->dd_cookie, rate); 272 if (error) 273 return error; 274 } 275 276 dai = sc->sc_dai_cpu; 277 if (dai->dd_set_sysclk) { 278 error = dai->dd_set_sysclk(dai->dd_cookie, rate); 279 if (error) 280 return error; 281 } 282 } 283 284 dai = sc->sc_dai_cpu; 285 hwif = dai->dd_hw_if; 286 if (hwif->set_params) { 287 error = hwif->set_params(dai->dd_cookie, 288 setmode, usemode, play, rec); 289 if (error) 290 return error; 291 } 292 293 dai = sc->sc_dai_codec; 294 hwif = dai->dd_hw_if; 295 if (hwif->set_params) { 296 error = hwif->set_params(dai->dd_cookie, 297 setmode, usemode, play, rec); 298 if (error) 299 return error; 300 } 301 302 return 0; 303 } 304 305 void * 306 graphaudio_allocm(void *cookie, int direction, size_t size, int type, 307 int flags) 308 { 309 struct graphaudio_softc *sc = cookie; 310 struct dai_device *dai = sc->sc_dai_cpu; 311 struct audio_hw_if *hwif = dai->dd_hw_if; 312 313 if (hwif->allocm) 314 return hwif->allocm(dai->dd_cookie, 315 direction, size, type, flags); 316 317 return NULL; 318 } 319 320 void 321 graphaudio_freem(void *cookie, void *addr, int type) 322 { 323 struct graphaudio_softc *sc = cookie; 324 struct dai_device *dai = sc->sc_dai_cpu; 325 struct audio_hw_if *hwif = dai->dd_hw_if; 326 327 if (hwif->freem) 328 hwif->freem(dai->dd_cookie, addr, type); 329 } 330 331 int 332 graphaudio_set_port(void *cookie, mixer_ctrl_t *cp) 333 { 334 struct graphaudio_softc *sc = cookie; 335 struct dai_device *dai = sc->sc_dai_codec; 336 struct audio_hw_if *hwif = dai->dd_hw_if; 337 338 if (hwif->set_port) 339 return hwif->set_port(dai->dd_cookie, cp); 340 341 return ENXIO; 342 } 343 344 int 345 graphaudio_get_port(void *cookie, mixer_ctrl_t *cp) 346 { 347 struct graphaudio_softc *sc = cookie; 348 struct dai_device *dai = sc->sc_dai_codec; 349 struct audio_hw_if *hwif = dai->dd_hw_if; 350 351 if (hwif->get_port) 352 return hwif->get_port(dai->dd_cookie, cp); 353 354 return ENXIO; 355 } 356 357 int 358 graphaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip) 359 { 360 struct graphaudio_softc *sc = cookie; 361 struct dai_device *dai = sc->sc_dai_codec; 362 struct audio_hw_if *hwif = dai->dd_hw_if; 363 364 if (hwif->query_devinfo) 365 return hwif->query_devinfo(dai->dd_cookie, dip); 366 367 return ENXIO; 368 } 369 370 int 371 graphaudio_get_props(void *cookie) 372 { 373 struct graphaudio_softc *sc = cookie; 374 struct dai_device *dai = sc->sc_dai_cpu; 375 struct audio_hw_if *hwif = dai->dd_hw_if; 376 377 if (hwif->get_props) 378 return hwif->get_props(dai->dd_cookie); 379 380 return 0; 381 } 382 383 int 384 graphaudio_round_blocksize(void *cookie, int block) 385 { 386 struct graphaudio_softc *sc = cookie; 387 struct dai_device *dai = sc->sc_dai_cpu; 388 struct audio_hw_if *hwif = dai->dd_hw_if; 389 390 if (hwif->round_blocksize) 391 return hwif->round_blocksize(dai->dd_cookie, block); 392 393 return block; 394 } 395 396 size_t 397 graphaudio_round_buffersize(void *cookie, int direction, size_t bufsize) 398 { 399 struct graphaudio_softc *sc = cookie; 400 struct dai_device *dai = sc->sc_dai_cpu; 401 struct audio_hw_if *hwif = dai->dd_hw_if; 402 403 if (hwif->round_buffersize) 404 return hwif->round_buffersize(dai->dd_cookie, 405 direction, bufsize); 406 407 return bufsize; 408 } 409 410 int 411 graphaudio_trigger_output(void *cookie, void *start, void *end, int blksize, 412 void (*intr)(void *), void *arg, struct audio_params *param) 413 { 414 struct graphaudio_softc *sc = cookie; 415 struct dai_device *dai; 416 struct audio_hw_if *hwif; 417 int error; 418 419 dai = sc->sc_dai_codec; 420 hwif = dai->dd_hw_if; 421 if (hwif->trigger_output) { 422 error = hwif->trigger_output(dai->dd_cookie, 423 start, end, blksize, intr, arg, param); 424 if (error) { 425 graphaudio_halt_output(cookie); 426 return error; 427 } 428 } 429 430 dai = sc->sc_dai_cpu; 431 hwif = dai->dd_hw_if; 432 if (hwif->trigger_output) { 433 error = hwif->trigger_output(dai->dd_cookie, 434 start, end, blksize, intr, arg, param); 435 if (error) { 436 graphaudio_halt_output(cookie); 437 return error; 438 } 439 } 440 441 return 0; 442 } 443 444 int 445 graphaudio_trigger_input(void *cookie, void *start, void *end, int blksize, 446 void (*intr)(void *), void *arg, struct audio_params *param) 447 { 448 struct graphaudio_softc *sc = cookie; 449 struct dai_device *dai; 450 struct audio_hw_if *hwif; 451 int error; 452 453 dai = sc->sc_dai_codec; 454 hwif = dai->dd_hw_if; 455 if (hwif->trigger_input) { 456 error = hwif->trigger_input(dai->dd_cookie, 457 start, end, blksize, intr, arg, param); 458 if (error) { 459 graphaudio_halt_input(cookie); 460 return error; 461 } 462 } 463 464 dai = sc->sc_dai_cpu; 465 hwif = dai->dd_hw_if; 466 if (hwif->trigger_input) { 467 error = hwif->trigger_input(dai->dd_cookie, 468 start, end, blksize, intr, arg, param); 469 if (error) { 470 graphaudio_halt_input(cookie); 471 return error; 472 } 473 } 474 475 return 0; 476 } 477 478 int 479 graphaudio_halt_output(void *cookie) 480 { 481 struct graphaudio_softc *sc = cookie; 482 struct dai_device *dai; 483 struct audio_hw_if *hwif; 484 485 dai = sc->sc_dai_codec; 486 hwif = dai->dd_hw_if; 487 if (hwif->halt_output) 488 hwif->halt_output(dai->dd_cookie); 489 490 dai = sc->sc_dai_cpu; 491 hwif = dai->dd_hw_if; 492 if (hwif->halt_output) 493 hwif->halt_output(dai->dd_cookie); 494 495 return 0; 496 } 497 498 int 499 graphaudio_halt_input(void *cookie) 500 { 501 struct graphaudio_softc *sc = cookie; 502 struct dai_device *dai; 503 struct audio_hw_if *hwif; 504 505 dai = sc->sc_dai_codec; 506 hwif = dai->dd_hw_if; 507 if (hwif->halt_input) 508 hwif->halt_input(dai->dd_cookie); 509 510 dai = sc->sc_dai_cpu; 511 hwif = dai->dd_hw_if; 512 if (hwif->halt_input) 513 hwif->halt_input(dai->dd_cookie); 514 515 return 0; 516 } 517