1 /*- 2 * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include "opt_platform.h" 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/clock.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/module.h> 38 #include <sys/endian.h> 39 40 #include <dev/ofw/ofw_bus.h> 41 #include <dev/ofw/ofw_bus_subr.h> 42 43 #include <dev/sound/fdt/audio_dai.h> 44 #include <dev/sound/pcm/sound.h> 45 #include "audio_dai_if.h" 46 47 #define AUDIO_BUFFER_SIZE 48000 * 4 48 49 struct audio_soc_aux_node { 50 SLIST_ENTRY(audio_soc_aux_node) link; 51 device_t dev; 52 }; 53 54 struct audio_soc_channel { 55 struct audio_soc_softc *sc; /* parent device's softc */ 56 struct pcm_channel *pcm; /* PCM channel */ 57 struct snd_dbuf *buf; /* PCM buffer */ 58 int dir; /* direction */ 59 }; 60 61 struct audio_soc_softc { 62 /* 63 * pcm_register assumes that sc is snddev_info, 64 * so this has to be first structure member for "compatibility" 65 */ 66 struct snddev_info info; 67 device_t dev; 68 char *name; 69 struct intr_config_hook init_hook; 70 device_t cpu_dev; 71 device_t codec_dev; 72 SLIST_HEAD(, audio_soc_aux_node) aux_devs; 73 unsigned int mclk_fs; 74 struct audio_soc_channel play_channel; 75 struct audio_soc_channel rec_channel; 76 /* 77 * The format is from the CPU node, for CODEC node clock roles 78 * need to be reversed. 79 */ 80 uint32_t format; 81 uint32_t link_mclk_fs; 82 }; 83 84 static struct ofw_compat_data compat_data[] = { 85 {"simple-audio-card", 1}, 86 {NULL, 0}, 87 }; 88 89 static struct { 90 const char *name; 91 unsigned int fmt; 92 } ausoc_dai_formats[] = { 93 { "i2s", AUDIO_DAI_FORMAT_I2S }, 94 { "right_j", AUDIO_DAI_FORMAT_RJ }, 95 { "left_j", AUDIO_DAI_FORMAT_LJ }, 96 { "dsp_a", AUDIO_DAI_FORMAT_DSPA }, 97 { "dsp_b", AUDIO_DAI_FORMAT_DSPB }, 98 { "ac97", AUDIO_DAI_FORMAT_AC97 }, 99 { "pdm", AUDIO_DAI_FORMAT_PDM }, 100 }; 101 102 static int audio_soc_probe(device_t dev); 103 static int audio_soc_attach(device_t dev); 104 static int audio_soc_detach(device_t dev); 105 106 /* 107 * Invert master/slave roles for CODEC side of the node 108 */ 109 static uint32_t 110 audio_soc_reverse_clocks(uint32_t format) 111 { 112 int fmt, pol, clk; 113 114 fmt = AUDIO_DAI_FORMAT_FORMAT(format); 115 pol = AUDIO_DAI_FORMAT_POLARITY(format); 116 clk = AUDIO_DAI_FORMAT_CLOCK(format); 117 118 switch (clk) { 119 case AUDIO_DAI_CLOCK_CBM_CFM: 120 clk = AUDIO_DAI_CLOCK_CBS_CFS; 121 break; 122 case AUDIO_DAI_CLOCK_CBS_CFM: 123 clk = AUDIO_DAI_CLOCK_CBM_CFS; 124 break; 125 case AUDIO_DAI_CLOCK_CBM_CFS: 126 clk = AUDIO_DAI_CLOCK_CBS_CFM; 127 break; 128 case AUDIO_DAI_CLOCK_CBS_CFS: 129 clk = AUDIO_DAI_CLOCK_CBM_CFM; 130 break; 131 } 132 133 return AUDIO_DAI_FORMAT(fmt, pol, clk); 134 } 135 136 static uint32_t 137 audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz) 138 { 139 140 return (blocksz); 141 } 142 143 static int 144 audio_soc_chan_setformat(kobj_t obj, void *data, uint32_t format) 145 { 146 147 struct audio_soc_softc *sc; 148 struct audio_soc_channel *ausoc_chan; 149 150 ausoc_chan = data; 151 sc = ausoc_chan->sc; 152 153 return AUDIO_DAI_SET_CHANFORMAT(sc->cpu_dev, format); 154 } 155 156 static uint32_t 157 audio_soc_chan_setspeed(kobj_t obj, void *data, uint32_t speed) 158 { 159 160 struct audio_soc_softc *sc; 161 struct audio_soc_channel *ausoc_chan; 162 uint32_t rate; 163 struct audio_soc_aux_node *aux_node; 164 165 ausoc_chan = data; 166 sc = ausoc_chan->sc; 167 168 if (sc->link_mclk_fs) { 169 rate = speed * sc->link_mclk_fs; 170 if (AUDIO_DAI_SET_SYSCLK(sc->cpu_dev, rate, AUDIO_DAI_CLOCK_IN)) 171 device_printf(sc->dev, "failed to set sysclk for CPU node\n"); 172 173 if (AUDIO_DAI_SET_SYSCLK(sc->codec_dev, rate, AUDIO_DAI_CLOCK_OUT)) 174 device_printf(sc->dev, "failed to set sysclk for codec node\n"); 175 176 SLIST_FOREACH(aux_node, &sc->aux_devs, link) { 177 if (AUDIO_DAI_SET_SYSCLK(aux_node->dev, rate, AUDIO_DAI_CLOCK_OUT)) 178 device_printf(sc->dev, "failed to set sysclk for aux node\n"); 179 } 180 } 181 182 /* 183 * Let CPU node determine speed 184 */ 185 speed = AUDIO_DAI_SET_CHANSPEED(sc->cpu_dev, speed); 186 AUDIO_DAI_SET_CHANSPEED(sc->codec_dev, speed); 187 SLIST_FOREACH(aux_node, &sc->aux_devs, link) { 188 AUDIO_DAI_SET_CHANSPEED(aux_node->dev, speed); 189 } 190 191 return (speed); 192 } 193 194 static uint32_t 195 audio_soc_chan_getptr(kobj_t obj, void *data) 196 { 197 struct audio_soc_softc *sc; 198 struct audio_soc_channel *ausoc_chan; 199 200 ausoc_chan = data; 201 sc = ausoc_chan->sc; 202 203 return AUDIO_DAI_GET_PTR(sc->cpu_dev, ausoc_chan->dir); 204 } 205 206 static void * 207 audio_soc_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 208 struct pcm_channel *c, int dir) 209 { 210 struct audio_soc_softc *sc; 211 struct audio_soc_channel *ausoc_chan; 212 void *buffer; 213 214 ausoc_chan = devinfo; 215 sc = ausoc_chan->sc; 216 buffer = malloc(AUDIO_BUFFER_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 217 218 if (sndbuf_setup(b, buffer, AUDIO_BUFFER_SIZE) != 0) { 219 free(buffer, M_DEVBUF); 220 return NULL; 221 } 222 223 ausoc_chan->dir = dir; 224 ausoc_chan->buf = b; 225 ausoc_chan->pcm = c; 226 227 return (devinfo); 228 } 229 230 static int 231 audio_soc_chan_trigger(kobj_t obj, void *data, int go) 232 { 233 struct audio_soc_softc *sc; 234 struct audio_soc_channel *ausoc_chan; 235 struct audio_soc_aux_node *aux_node; 236 237 ausoc_chan = (struct audio_soc_channel *)data; 238 sc = ausoc_chan->sc; 239 AUDIO_DAI_TRIGGER(sc->codec_dev, go, ausoc_chan->dir); 240 SLIST_FOREACH(aux_node, &sc->aux_devs, link) { 241 AUDIO_DAI_TRIGGER(aux_node->dev, go, ausoc_chan->dir); 242 } 243 244 return AUDIO_DAI_TRIGGER(sc->cpu_dev, go, ausoc_chan->dir); 245 } 246 247 static int 248 audio_soc_chan_free(kobj_t obj, void *data) 249 { 250 251 struct audio_soc_softc *sc; 252 struct audio_soc_channel *ausoc_chan; 253 void *buffer; 254 255 ausoc_chan = (struct audio_soc_channel *)data; 256 sc = ausoc_chan->sc; 257 258 buffer = sndbuf_getbuf(ausoc_chan->buf); 259 if (buffer) 260 free(buffer, M_DEVBUF); 261 262 return (0); 263 } 264 265 static struct pcmchan_caps * 266 audio_soc_chan_getcaps(kobj_t obj, void *data) 267 { 268 struct audio_soc_softc *sc; 269 struct audio_soc_channel *ausoc_chan; 270 271 ausoc_chan = data; 272 sc = ausoc_chan->sc; 273 274 return AUDIO_DAI_GET_CAPS(sc->cpu_dev); 275 } 276 277 static kobj_method_t audio_soc_chan_methods[] = { 278 KOBJMETHOD(channel_init, audio_soc_chan_init), 279 KOBJMETHOD(channel_free, audio_soc_chan_free), 280 KOBJMETHOD(channel_setformat, audio_soc_chan_setformat), 281 KOBJMETHOD(channel_setspeed, audio_soc_chan_setspeed), 282 KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize), 283 KOBJMETHOD(channel_trigger, audio_soc_chan_trigger), 284 KOBJMETHOD(channel_getptr, audio_soc_chan_getptr), 285 KOBJMETHOD(channel_getcaps, audio_soc_chan_getcaps), 286 KOBJMETHOD_END 287 }; 288 CHANNEL_DECLARE(audio_soc_chan); 289 290 static void 291 audio_soc_intr(void *arg) 292 { 293 struct audio_soc_softc *sc; 294 int channel_intr_required; 295 296 sc = (struct audio_soc_softc *)arg; 297 channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf); 298 if (channel_intr_required & AUDIO_DAI_PLAY_INTR) 299 chn_intr(sc->play_channel.pcm); 300 if (channel_intr_required & AUDIO_DAI_REC_INTR) 301 chn_intr(sc->rec_channel.pcm); 302 } 303 304 static int 305 audio_soc_probe(device_t dev) 306 { 307 308 if (!ofw_bus_status_okay(dev)) 309 return (ENXIO); 310 311 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { 312 device_set_desc(dev, "simple-audio-card"); 313 return (BUS_PROBE_DEFAULT); 314 } 315 316 return (ENXIO); 317 } 318 319 static void 320 audio_soc_init(void *arg) 321 { 322 struct audio_soc_softc *sc; 323 phandle_t node, child; 324 device_t daidev, auxdev; 325 uint32_t xref; 326 uint32_t *aux_devs; 327 int ncells, i; 328 struct audio_soc_aux_node *aux_node; 329 330 sc = (struct audio_soc_softc *)arg; 331 config_intrhook_disestablish(&sc->init_hook); 332 333 node = ofw_bus_get_node(sc->dev); 334 /* TODO: handle multi-link nodes */ 335 child = ofw_bus_find_child(node, "simple-audio-card,cpu"); 336 if (child == 0) { 337 device_printf(sc->dev, "cpu node is missing\n"); 338 return; 339 } 340 if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) { 341 device_printf(sc->dev, "missing sound-dai property in cpu node\n"); 342 return; 343 } 344 daidev = OF_device_from_xref(xref); 345 if (daidev == NULL) { 346 device_printf(sc->dev, "no driver attached to cpu node\n"); 347 return; 348 } 349 sc->cpu_dev = daidev; 350 351 child = ofw_bus_find_child(node, "simple-audio-card,codec"); 352 if (child == 0) { 353 device_printf(sc->dev, "codec node is missing\n"); 354 return; 355 } 356 if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) { 357 device_printf(sc->dev, "missing sound-dai property in codec node\n"); 358 return; 359 } 360 daidev = OF_device_from_xref(xref); 361 if (daidev == NULL) { 362 device_printf(sc->dev, "no driver attached to codec node\n"); 363 return; 364 } 365 sc->codec_dev = daidev; 366 367 /* Add AUX devices */ 368 aux_devs = NULL; 369 ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs), 370 (void **)&aux_devs); 371 372 for (i = 0; i < ncells; i++) { 373 auxdev = OF_device_from_xref(aux_devs[i]); 374 if (auxdev == NULL) 375 device_printf(sc->dev, "warning: no driver attached to aux node\n"); 376 aux_node = (struct audio_soc_aux_node *)malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT); 377 if (aux_node == NULL) { 378 device_printf(sc->dev, "failed to allocate aux node struct\n"); 379 return; 380 } 381 aux_node->dev = auxdev; 382 SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link); 383 } 384 385 if (aux_devs) 386 OF_prop_free(aux_devs); 387 388 if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) { 389 device_printf(sc->dev, "failed to initialize cpu node\n"); 390 return; 391 } 392 393 /* Reverse clock roles for CODEC */ 394 if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) { 395 device_printf(sc->dev, "failed to initialize codec node\n"); 396 return; 397 } 398 399 SLIST_FOREACH(aux_node, &sc->aux_devs, link) { 400 if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) { 401 device_printf(sc->dev, "failed to initialize aux node\n"); 402 return; 403 } 404 } 405 406 if (pcm_register(sc->dev, sc, 1, 1)) { 407 device_printf(sc->dev, "failed to register PCM\n"); 408 return; 409 } 410 411 pcm_getbuffersize(sc->dev, AUDIO_BUFFER_SIZE, AUDIO_BUFFER_SIZE, 412 AUDIO_BUFFER_SIZE); 413 sc->play_channel.sc = sc; 414 sc->rec_channel.sc = sc; 415 416 pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel); 417 pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel); 418 419 pcm_setstatus(sc->dev, "at EXPERIMENT"); 420 421 AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc); 422 AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev); 423 SLIST_FOREACH(aux_node, &sc->aux_devs, link) { 424 AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev); 425 } 426 } 427 428 static int 429 audio_soc_attach(device_t dev) 430 { 431 struct audio_soc_softc *sc; 432 char *name; 433 phandle_t node, cpu_child; 434 uint32_t xref; 435 int i, ret; 436 char tmp[32]; 437 unsigned int fmt, pol, clk; 438 bool frame_master, bitclock_master; 439 440 sc = device_get_softc(dev); 441 sc->dev = dev; 442 node = ofw_bus_get_node(dev); 443 444 ret = OF_getprop_alloc(node, "name", (void **)&name); 445 if (ret == -1) 446 name = "SoC audio"; 447 448 sc->name = strdup(name, M_DEVBUF); 449 device_set_desc(dev, sc->name); 450 451 if (ret != -1) 452 OF_prop_free(name); 453 454 SLIST_INIT(&sc->aux_devs); 455 456 ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp)); 457 if (ret == 0) { 458 for (i = 0; i < nitems(ausoc_dai_formats); i++) { 459 if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) { 460 fmt = ausoc_dai_formats[i].fmt; 461 break; 462 } 463 } 464 if (i == nitems(ausoc_dai_formats)) 465 return (EINVAL); 466 } else 467 fmt = AUDIO_DAI_FORMAT_I2S; 468 469 if (OF_getencprop(node, "simple-audio-card,mclk-fs", 470 &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0) 471 sc->link_mclk_fs = 0; 472 473 /* Unless specified otherwise, CPU node is the master */ 474 frame_master = bitclock_master = true; 475 476 cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu"); 477 478 if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0) 479 frame_master = cpu_child == OF_node_from_xref(xref); 480 481 if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0) 482 bitclock_master = cpu_child == OF_node_from_xref(xref); 483 484 if (frame_master) { 485 clk = bitclock_master ? 486 AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM; 487 } else { 488 clk = bitclock_master ? 489 AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS; 490 } 491 492 bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion"); 493 bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion"); 494 if (bitclock_inversion) { 495 pol = frame_inversion ? 496 AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF; 497 } else { 498 pol = frame_inversion ? 499 AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF; 500 } 501 502 sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk); 503 504 sc->init_hook.ich_func = audio_soc_init; 505 sc->init_hook.ich_arg = sc; 506 if (config_intrhook_establish(&sc->init_hook) != 0) 507 return (ENOMEM); 508 509 return (0); 510 } 511 512 static int 513 audio_soc_detach(device_t dev) 514 { 515 struct audio_soc_softc *sc; 516 struct audio_soc_aux_node *aux; 517 518 sc = device_get_softc(dev); 519 if (sc->name) 520 free(sc->name, M_DEVBUF); 521 522 while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) { 523 SLIST_REMOVE_HEAD(&sc->aux_devs, link); 524 free(aux, M_DEVBUF); 525 } 526 527 return (0); 528 } 529 530 static device_method_t audio_soc_methods[] = { 531 /* device_if methods */ 532 DEVMETHOD(device_probe, audio_soc_probe), 533 DEVMETHOD(device_attach, audio_soc_attach), 534 DEVMETHOD(device_detach, audio_soc_detach), 535 536 DEVMETHOD_END, 537 }; 538 539 static driver_t audio_soc_driver = { 540 "pcm", 541 audio_soc_methods, 542 sizeof(struct audio_soc_softc), 543 }; 544 545 DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, pcm_devclass, NULL, NULL); 546 MODULE_VERSION(audio_soc, 1); 547