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