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