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 33struct 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 43int graphaudio_match(struct device *, void *, void *); 44void graphaudio_attach(struct device *, struct device *, void *); 45void graphaudio_attach_deferred(struct device *); 46void graphaudio_set_format(struct graphaudio_softc *, uint32_t, 47 uint32_t, uint32_t); 48 49int graphaudio_open(void *, int); 50void graphaudio_close(void *); 51int graphaudio_set_params(void *, int, int, 52 struct audio_params *, struct audio_params *); 53void *graphaudio_allocm(void *, int, size_t, int, int); 54void graphaudio_freem(void *, void *, int); 55int graphaudio_set_port(void *, mixer_ctrl_t *); 56int graphaudio_get_port(void *, mixer_ctrl_t *); 57int graphaudio_query_devinfo(void *, mixer_devinfo_t *); 58int graphaudio_round_blocksize(void *, int); 59size_t graphaudio_round_buffersize(void *, int, size_t); 60int graphaudio_trigger_output(void *, void *, void *, int, 61 void (*)(void *), void *, struct audio_params *); 62int graphaudio_trigger_input(void *, void *, void *, int, 63 void (*)(void *), void *, struct audio_params *); 64int graphaudio_halt_output(void *); 65int graphaudio_halt_input(void *); 66 67const 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 84const struct cfattach graphaudio_ca = { 85 sizeof(struct graphaudio_softc), graphaudio_match, graphaudio_attach 86}; 87 88struct cfdriver graphaudio_cd = { 89 NULL, "graphaudio", DV_DULL 90}; 91 92int 93graphaudio_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 100void 101graphaudio_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 112void 113graphaudio_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 190void 191graphaudio_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 202int 203graphaudio_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 233void 234graphaudio_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 251int 252graphaudio_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 303void * 304graphaudio_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 318void 319graphaudio_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 329int 330graphaudio_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 342int 343graphaudio_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 355int 356graphaudio_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 368int 369graphaudio_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 381size_t 382graphaudio_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 395int 396graphaudio_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 429int 430graphaudio_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 463int 464graphaudio_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 483int 484graphaudio_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