12cfe870aSOleksandr Tymoshenko /*-
22cfe870aSOleksandr Tymoshenko * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
32cfe870aSOleksandr Tymoshenko *
42cfe870aSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without
52cfe870aSOleksandr Tymoshenko * modification, are permitted provided that the following conditions
62cfe870aSOleksandr Tymoshenko * are met:
72cfe870aSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright
82cfe870aSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer.
92cfe870aSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright
102cfe870aSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the
112cfe870aSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution.
122cfe870aSOleksandr Tymoshenko *
132cfe870aSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
142cfe870aSOleksandr Tymoshenko * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
152cfe870aSOleksandr Tymoshenko * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
162cfe870aSOleksandr Tymoshenko * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
172cfe870aSOleksandr Tymoshenko * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
182cfe870aSOleksandr Tymoshenko * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
192cfe870aSOleksandr Tymoshenko * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
202cfe870aSOleksandr Tymoshenko * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
212cfe870aSOleksandr Tymoshenko * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
222cfe870aSOleksandr Tymoshenko * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
232cfe870aSOleksandr Tymoshenko *
242cfe870aSOleksandr Tymoshenko */
252cfe870aSOleksandr Tymoshenko
262cfe870aSOleksandr Tymoshenko #include <sys/cdefs.h>
272cfe870aSOleksandr Tymoshenko #include "opt_platform.h"
282cfe870aSOleksandr Tymoshenko
292cfe870aSOleksandr Tymoshenko #include <sys/param.h>
302cfe870aSOleksandr Tymoshenko #include <sys/systm.h>
312cfe870aSOleksandr Tymoshenko #include <sys/bus.h>
322cfe870aSOleksandr Tymoshenko #include <sys/clock.h>
332cfe870aSOleksandr Tymoshenko #include <sys/kernel.h>
342cfe870aSOleksandr Tymoshenko #include <sys/lock.h>
352cfe870aSOleksandr Tymoshenko #include <sys/module.h>
362cfe870aSOleksandr Tymoshenko #include <sys/endian.h>
372cfe870aSOleksandr Tymoshenko
382cfe870aSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
392cfe870aSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
402cfe870aSOleksandr Tymoshenko
412cfe870aSOleksandr Tymoshenko #include <dev/sound/fdt/audio_dai.h>
422cfe870aSOleksandr Tymoshenko #include <dev/sound/pcm/sound.h>
432cfe870aSOleksandr Tymoshenko #include "audio_dai_if.h"
442cfe870aSOleksandr Tymoshenko
452cfe870aSOleksandr Tymoshenko #define AUDIO_BUFFER_SIZE 48000 * 4
462cfe870aSOleksandr Tymoshenko
472cfe870aSOleksandr Tymoshenko struct audio_soc_aux_node {
482cfe870aSOleksandr Tymoshenko SLIST_ENTRY(audio_soc_aux_node) link;
492cfe870aSOleksandr Tymoshenko device_t dev;
502cfe870aSOleksandr Tymoshenko };
512cfe870aSOleksandr Tymoshenko
522cfe870aSOleksandr Tymoshenko struct audio_soc_channel {
532cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc; /* parent device's softc */
542cfe870aSOleksandr Tymoshenko struct pcm_channel *pcm; /* PCM channel */
552cfe870aSOleksandr Tymoshenko struct snd_dbuf *buf; /* PCM buffer */
562cfe870aSOleksandr Tymoshenko int dir; /* direction */
572cfe870aSOleksandr Tymoshenko };
582cfe870aSOleksandr Tymoshenko
592cfe870aSOleksandr Tymoshenko struct audio_soc_softc {
602cfe870aSOleksandr Tymoshenko /*
612cfe870aSOleksandr Tymoshenko * pcm_register assumes that sc is snddev_info,
6204389c85SGordon Bergling * so this has to be first structure member for "compatibility"
632cfe870aSOleksandr Tymoshenko */
642cfe870aSOleksandr Tymoshenko struct snddev_info info;
652cfe870aSOleksandr Tymoshenko device_t dev;
662cfe870aSOleksandr Tymoshenko char *name;
672cfe870aSOleksandr Tymoshenko struct intr_config_hook init_hook;
682cfe870aSOleksandr Tymoshenko device_t cpu_dev;
692cfe870aSOleksandr Tymoshenko device_t codec_dev;
702cfe870aSOleksandr Tymoshenko SLIST_HEAD(, audio_soc_aux_node) aux_devs;
712cfe870aSOleksandr Tymoshenko unsigned int mclk_fs;
722cfe870aSOleksandr Tymoshenko struct audio_soc_channel play_channel;
732cfe870aSOleksandr Tymoshenko struct audio_soc_channel rec_channel;
742cfe870aSOleksandr Tymoshenko /*
752cfe870aSOleksandr Tymoshenko * The format is from the CPU node, for CODEC node clock roles
762cfe870aSOleksandr Tymoshenko * need to be reversed.
772cfe870aSOleksandr Tymoshenko */
782cfe870aSOleksandr Tymoshenko uint32_t format;
792cfe870aSOleksandr Tymoshenko uint32_t link_mclk_fs;
802cfe870aSOleksandr Tymoshenko };
812cfe870aSOleksandr Tymoshenko
822cfe870aSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = {
832cfe870aSOleksandr Tymoshenko {"simple-audio-card", 1},
842cfe870aSOleksandr Tymoshenko {NULL, 0},
852cfe870aSOleksandr Tymoshenko };
862cfe870aSOleksandr Tymoshenko
872cfe870aSOleksandr Tymoshenko static struct {
882cfe870aSOleksandr Tymoshenko const char *name;
892cfe870aSOleksandr Tymoshenko unsigned int fmt;
902cfe870aSOleksandr Tymoshenko } ausoc_dai_formats[] = {
912cfe870aSOleksandr Tymoshenko { "i2s", AUDIO_DAI_FORMAT_I2S },
922cfe870aSOleksandr Tymoshenko { "right_j", AUDIO_DAI_FORMAT_RJ },
932cfe870aSOleksandr Tymoshenko { "left_j", AUDIO_DAI_FORMAT_LJ },
942cfe870aSOleksandr Tymoshenko { "dsp_a", AUDIO_DAI_FORMAT_DSPA },
952cfe870aSOleksandr Tymoshenko { "dsp_b", AUDIO_DAI_FORMAT_DSPB },
962cfe870aSOleksandr Tymoshenko { "ac97", AUDIO_DAI_FORMAT_AC97 },
972cfe870aSOleksandr Tymoshenko { "pdm", AUDIO_DAI_FORMAT_PDM },
982cfe870aSOleksandr Tymoshenko };
992cfe870aSOleksandr Tymoshenko
1002cfe870aSOleksandr Tymoshenko static int audio_soc_probe(device_t dev);
1012cfe870aSOleksandr Tymoshenko static int audio_soc_attach(device_t dev);
1022cfe870aSOleksandr Tymoshenko static int audio_soc_detach(device_t dev);
1032cfe870aSOleksandr Tymoshenko
1042cfe870aSOleksandr Tymoshenko /*
1052cfe870aSOleksandr Tymoshenko * Invert master/slave roles for CODEC side of the node
1062cfe870aSOleksandr Tymoshenko */
1072cfe870aSOleksandr Tymoshenko static uint32_t
audio_soc_reverse_clocks(uint32_t format)1082cfe870aSOleksandr Tymoshenko audio_soc_reverse_clocks(uint32_t format)
1092cfe870aSOleksandr Tymoshenko {
1102cfe870aSOleksandr Tymoshenko int fmt, pol, clk;
1112cfe870aSOleksandr Tymoshenko
1122cfe870aSOleksandr Tymoshenko fmt = AUDIO_DAI_FORMAT_FORMAT(format);
1132cfe870aSOleksandr Tymoshenko pol = AUDIO_DAI_FORMAT_POLARITY(format);
1142cfe870aSOleksandr Tymoshenko clk = AUDIO_DAI_FORMAT_CLOCK(format);
1152cfe870aSOleksandr Tymoshenko
1162cfe870aSOleksandr Tymoshenko switch (clk) {
1172cfe870aSOleksandr Tymoshenko case AUDIO_DAI_CLOCK_CBM_CFM:
1182cfe870aSOleksandr Tymoshenko clk = AUDIO_DAI_CLOCK_CBS_CFS;
1192cfe870aSOleksandr Tymoshenko break;
1202cfe870aSOleksandr Tymoshenko case AUDIO_DAI_CLOCK_CBS_CFM:
1212cfe870aSOleksandr Tymoshenko clk = AUDIO_DAI_CLOCK_CBM_CFS;
1222cfe870aSOleksandr Tymoshenko break;
1232cfe870aSOleksandr Tymoshenko case AUDIO_DAI_CLOCK_CBM_CFS:
1242cfe870aSOleksandr Tymoshenko clk = AUDIO_DAI_CLOCK_CBS_CFM;
1252cfe870aSOleksandr Tymoshenko break;
1262cfe870aSOleksandr Tymoshenko case AUDIO_DAI_CLOCK_CBS_CFS:
1272cfe870aSOleksandr Tymoshenko clk = AUDIO_DAI_CLOCK_CBM_CFM;
1282cfe870aSOleksandr Tymoshenko break;
1292cfe870aSOleksandr Tymoshenko }
1302cfe870aSOleksandr Tymoshenko
1312cfe870aSOleksandr Tymoshenko return AUDIO_DAI_FORMAT(fmt, pol, clk);
1322cfe870aSOleksandr Tymoshenko }
1332cfe870aSOleksandr Tymoshenko
1342cfe870aSOleksandr Tymoshenko static uint32_t
audio_soc_chan_setblocksize(kobj_t obj,void * data,uint32_t blocksz)1352cfe870aSOleksandr Tymoshenko audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz)
1362cfe870aSOleksandr Tymoshenko {
1372cfe870aSOleksandr Tymoshenko
1382cfe870aSOleksandr Tymoshenko return (blocksz);
1392cfe870aSOleksandr Tymoshenko }
1402cfe870aSOleksandr Tymoshenko
1412cfe870aSOleksandr Tymoshenko static int
audio_soc_chan_setformat(kobj_t obj,void * data,uint32_t format)1422cfe870aSOleksandr Tymoshenko audio_soc_chan_setformat(kobj_t obj, void *data, uint32_t format)
1432cfe870aSOleksandr Tymoshenko {
1442cfe870aSOleksandr Tymoshenko
1452cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
1462cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
1472cfe870aSOleksandr Tymoshenko
1482cfe870aSOleksandr Tymoshenko ausoc_chan = data;
1492cfe870aSOleksandr Tymoshenko sc = ausoc_chan->sc;
1502cfe870aSOleksandr Tymoshenko
1512cfe870aSOleksandr Tymoshenko return AUDIO_DAI_SET_CHANFORMAT(sc->cpu_dev, format);
1522cfe870aSOleksandr Tymoshenko }
1532cfe870aSOleksandr Tymoshenko
1542cfe870aSOleksandr Tymoshenko static uint32_t
audio_soc_chan_setspeed(kobj_t obj,void * data,uint32_t speed)1552cfe870aSOleksandr Tymoshenko audio_soc_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
1562cfe870aSOleksandr Tymoshenko {
1572cfe870aSOleksandr Tymoshenko
1582cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
1592cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
1602cfe870aSOleksandr Tymoshenko uint32_t rate;
1612cfe870aSOleksandr Tymoshenko struct audio_soc_aux_node *aux_node;
1622cfe870aSOleksandr Tymoshenko
1632cfe870aSOleksandr Tymoshenko ausoc_chan = data;
1642cfe870aSOleksandr Tymoshenko sc = ausoc_chan->sc;
1652cfe870aSOleksandr Tymoshenko
1662cfe870aSOleksandr Tymoshenko if (sc->link_mclk_fs) {
1672cfe870aSOleksandr Tymoshenko rate = speed * sc->link_mclk_fs;
1682cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_SET_SYSCLK(sc->cpu_dev, rate, AUDIO_DAI_CLOCK_IN))
1692cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to set sysclk for CPU node\n");
1702cfe870aSOleksandr Tymoshenko
1712cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_SET_SYSCLK(sc->codec_dev, rate, AUDIO_DAI_CLOCK_OUT))
1722cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to set sysclk for codec node\n");
1732cfe870aSOleksandr Tymoshenko
1742cfe870aSOleksandr Tymoshenko SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
1752cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_SET_SYSCLK(aux_node->dev, rate, AUDIO_DAI_CLOCK_OUT))
1762cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to set sysclk for aux node\n");
1772cfe870aSOleksandr Tymoshenko }
1782cfe870aSOleksandr Tymoshenko }
1792cfe870aSOleksandr Tymoshenko
1802cfe870aSOleksandr Tymoshenko /*
1812cfe870aSOleksandr Tymoshenko * Let CPU node determine speed
1822cfe870aSOleksandr Tymoshenko */
1832cfe870aSOleksandr Tymoshenko speed = AUDIO_DAI_SET_CHANSPEED(sc->cpu_dev, speed);
1842cfe870aSOleksandr Tymoshenko AUDIO_DAI_SET_CHANSPEED(sc->codec_dev, speed);
1852cfe870aSOleksandr Tymoshenko SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
1862cfe870aSOleksandr Tymoshenko AUDIO_DAI_SET_CHANSPEED(aux_node->dev, speed);
1872cfe870aSOleksandr Tymoshenko }
1882cfe870aSOleksandr Tymoshenko
1892cfe870aSOleksandr Tymoshenko return (speed);
1902cfe870aSOleksandr Tymoshenko }
1912cfe870aSOleksandr Tymoshenko
1922cfe870aSOleksandr Tymoshenko static uint32_t
audio_soc_chan_getptr(kobj_t obj,void * data)1932cfe870aSOleksandr Tymoshenko audio_soc_chan_getptr(kobj_t obj, void *data)
1942cfe870aSOleksandr Tymoshenko {
1952cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
1962cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
1972cfe870aSOleksandr Tymoshenko
1982cfe870aSOleksandr Tymoshenko ausoc_chan = data;
1992cfe870aSOleksandr Tymoshenko sc = ausoc_chan->sc;
2002cfe870aSOleksandr Tymoshenko
2012cfe870aSOleksandr Tymoshenko return AUDIO_DAI_GET_PTR(sc->cpu_dev, ausoc_chan->dir);
2022cfe870aSOleksandr Tymoshenko }
2032cfe870aSOleksandr Tymoshenko
2042cfe870aSOleksandr Tymoshenko static void *
audio_soc_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)2052cfe870aSOleksandr Tymoshenko audio_soc_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
2062cfe870aSOleksandr Tymoshenko struct pcm_channel *c, int dir)
2072cfe870aSOleksandr Tymoshenko {
2082cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
2092cfe870aSOleksandr Tymoshenko void *buffer;
2102cfe870aSOleksandr Tymoshenko
2112cfe870aSOleksandr Tymoshenko ausoc_chan = devinfo;
2122cfe870aSOleksandr Tymoshenko buffer = malloc(AUDIO_BUFFER_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
2132cfe870aSOleksandr Tymoshenko
2142cfe870aSOleksandr Tymoshenko if (sndbuf_setup(b, buffer, AUDIO_BUFFER_SIZE) != 0) {
2152cfe870aSOleksandr Tymoshenko free(buffer, M_DEVBUF);
2162cfe870aSOleksandr Tymoshenko return NULL;
2172cfe870aSOleksandr Tymoshenko }
2182cfe870aSOleksandr Tymoshenko
2192cfe870aSOleksandr Tymoshenko ausoc_chan->dir = dir;
2202cfe870aSOleksandr Tymoshenko ausoc_chan->buf = b;
2212cfe870aSOleksandr Tymoshenko ausoc_chan->pcm = c;
2222cfe870aSOleksandr Tymoshenko
2232cfe870aSOleksandr Tymoshenko return (devinfo);
2242cfe870aSOleksandr Tymoshenko }
2252cfe870aSOleksandr Tymoshenko
2262cfe870aSOleksandr Tymoshenko static int
audio_soc_chan_trigger(kobj_t obj,void * data,int go)2272cfe870aSOleksandr Tymoshenko audio_soc_chan_trigger(kobj_t obj, void *data, int go)
2282cfe870aSOleksandr Tymoshenko {
2292cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
2302cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
2312cfe870aSOleksandr Tymoshenko struct audio_soc_aux_node *aux_node;
2322cfe870aSOleksandr Tymoshenko
2332cfe870aSOleksandr Tymoshenko ausoc_chan = (struct audio_soc_channel *)data;
2342cfe870aSOleksandr Tymoshenko sc = ausoc_chan->sc;
2352cfe870aSOleksandr Tymoshenko AUDIO_DAI_TRIGGER(sc->codec_dev, go, ausoc_chan->dir);
2362cfe870aSOleksandr Tymoshenko SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
2372cfe870aSOleksandr Tymoshenko AUDIO_DAI_TRIGGER(aux_node->dev, go, ausoc_chan->dir);
2382cfe870aSOleksandr Tymoshenko }
2392cfe870aSOleksandr Tymoshenko
2402cfe870aSOleksandr Tymoshenko return AUDIO_DAI_TRIGGER(sc->cpu_dev, go, ausoc_chan->dir);
2412cfe870aSOleksandr Tymoshenko }
2422cfe870aSOleksandr Tymoshenko
2432cfe870aSOleksandr Tymoshenko static int
audio_soc_chan_free(kobj_t obj,void * data)2442cfe870aSOleksandr Tymoshenko audio_soc_chan_free(kobj_t obj, void *data)
2452cfe870aSOleksandr Tymoshenko {
2462cfe870aSOleksandr Tymoshenko
2472cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
2482cfe870aSOleksandr Tymoshenko void *buffer;
2492cfe870aSOleksandr Tymoshenko
2502cfe870aSOleksandr Tymoshenko ausoc_chan = (struct audio_soc_channel *)data;
2512cfe870aSOleksandr Tymoshenko
2522cfe870aSOleksandr Tymoshenko buffer = sndbuf_getbuf(ausoc_chan->buf);
2532cfe870aSOleksandr Tymoshenko if (buffer)
2542cfe870aSOleksandr Tymoshenko free(buffer, M_DEVBUF);
2552cfe870aSOleksandr Tymoshenko
2562cfe870aSOleksandr Tymoshenko return (0);
2572cfe870aSOleksandr Tymoshenko }
2582cfe870aSOleksandr Tymoshenko
2592cfe870aSOleksandr Tymoshenko static struct pcmchan_caps *
audio_soc_chan_getcaps(kobj_t obj,void * data)2602cfe870aSOleksandr Tymoshenko audio_soc_chan_getcaps(kobj_t obj, void *data)
2612cfe870aSOleksandr Tymoshenko {
2622cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
2632cfe870aSOleksandr Tymoshenko struct audio_soc_channel *ausoc_chan;
2642cfe870aSOleksandr Tymoshenko
2652cfe870aSOleksandr Tymoshenko ausoc_chan = data;
2662cfe870aSOleksandr Tymoshenko sc = ausoc_chan->sc;
2672cfe870aSOleksandr Tymoshenko
2682cfe870aSOleksandr Tymoshenko return AUDIO_DAI_GET_CAPS(sc->cpu_dev);
2692cfe870aSOleksandr Tymoshenko }
2702cfe870aSOleksandr Tymoshenko
2712cfe870aSOleksandr Tymoshenko static kobj_method_t audio_soc_chan_methods[] = {
2722cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_init, audio_soc_chan_init),
2732cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_free, audio_soc_chan_free),
2742cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_setformat, audio_soc_chan_setformat),
2752cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_setspeed, audio_soc_chan_setspeed),
2762cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize),
2772cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_trigger, audio_soc_chan_trigger),
2782cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_getptr, audio_soc_chan_getptr),
2792cfe870aSOleksandr Tymoshenko KOBJMETHOD(channel_getcaps, audio_soc_chan_getcaps),
2802cfe870aSOleksandr Tymoshenko KOBJMETHOD_END
2812cfe870aSOleksandr Tymoshenko };
2822cfe870aSOleksandr Tymoshenko CHANNEL_DECLARE(audio_soc_chan);
2832cfe870aSOleksandr Tymoshenko
2842cfe870aSOleksandr Tymoshenko static void
audio_soc_intr(void * arg)2852cfe870aSOleksandr Tymoshenko audio_soc_intr(void *arg)
2862cfe870aSOleksandr Tymoshenko {
2872cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
2882cfe870aSOleksandr Tymoshenko int channel_intr_required;
2892cfe870aSOleksandr Tymoshenko
2902cfe870aSOleksandr Tymoshenko sc = (struct audio_soc_softc *)arg;
2912cfe870aSOleksandr Tymoshenko channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf);
2922cfe870aSOleksandr Tymoshenko if (channel_intr_required & AUDIO_DAI_PLAY_INTR)
2932cfe870aSOleksandr Tymoshenko chn_intr(sc->play_channel.pcm);
2942cfe870aSOleksandr Tymoshenko if (channel_intr_required & AUDIO_DAI_REC_INTR)
2952cfe870aSOleksandr Tymoshenko chn_intr(sc->rec_channel.pcm);
2962cfe870aSOleksandr Tymoshenko }
2972cfe870aSOleksandr Tymoshenko
2982cfe870aSOleksandr Tymoshenko static int
audio_soc_probe(device_t dev)2992cfe870aSOleksandr Tymoshenko audio_soc_probe(device_t dev)
3002cfe870aSOleksandr Tymoshenko {
3012cfe870aSOleksandr Tymoshenko
3022cfe870aSOleksandr Tymoshenko if (!ofw_bus_status_okay(dev))
3032cfe870aSOleksandr Tymoshenko return (ENXIO);
3042cfe870aSOleksandr Tymoshenko
3052cfe870aSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
3062cfe870aSOleksandr Tymoshenko device_set_desc(dev, "simple-audio-card");
3072cfe870aSOleksandr Tymoshenko return (BUS_PROBE_DEFAULT);
3082cfe870aSOleksandr Tymoshenko }
3092cfe870aSOleksandr Tymoshenko
3102cfe870aSOleksandr Tymoshenko return (ENXIO);
3112cfe870aSOleksandr Tymoshenko }
3122cfe870aSOleksandr Tymoshenko
3132cfe870aSOleksandr Tymoshenko static void
audio_soc_init(void * arg)3142cfe870aSOleksandr Tymoshenko audio_soc_init(void *arg)
3152cfe870aSOleksandr Tymoshenko {
3162cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
3172cfe870aSOleksandr Tymoshenko phandle_t node, child;
3182cfe870aSOleksandr Tymoshenko device_t daidev, auxdev;
3192cfe870aSOleksandr Tymoshenko uint32_t xref;
3202cfe870aSOleksandr Tymoshenko uint32_t *aux_devs;
3212cfe870aSOleksandr Tymoshenko int ncells, i;
3222cfe870aSOleksandr Tymoshenko struct audio_soc_aux_node *aux_node;
3232cfe870aSOleksandr Tymoshenko
3242cfe870aSOleksandr Tymoshenko sc = (struct audio_soc_softc *)arg;
3252cfe870aSOleksandr Tymoshenko config_intrhook_disestablish(&sc->init_hook);
3262cfe870aSOleksandr Tymoshenko
3272cfe870aSOleksandr Tymoshenko node = ofw_bus_get_node(sc->dev);
3282cfe870aSOleksandr Tymoshenko /* TODO: handle multi-link nodes */
3292cfe870aSOleksandr Tymoshenko child = ofw_bus_find_child(node, "simple-audio-card,cpu");
3302cfe870aSOleksandr Tymoshenko if (child == 0) {
3312cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "cpu node is missing\n");
3322cfe870aSOleksandr Tymoshenko return;
3332cfe870aSOleksandr Tymoshenko }
3342cfe870aSOleksandr Tymoshenko if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
3352cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "missing sound-dai property in cpu node\n");
3362cfe870aSOleksandr Tymoshenko return;
3372cfe870aSOleksandr Tymoshenko }
3382cfe870aSOleksandr Tymoshenko daidev = OF_device_from_xref(xref);
3392cfe870aSOleksandr Tymoshenko if (daidev == NULL) {
3402cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "no driver attached to cpu node\n");
3412cfe870aSOleksandr Tymoshenko return;
3422cfe870aSOleksandr Tymoshenko }
3432cfe870aSOleksandr Tymoshenko sc->cpu_dev = daidev;
3442cfe870aSOleksandr Tymoshenko
3452cfe870aSOleksandr Tymoshenko child = ofw_bus_find_child(node, "simple-audio-card,codec");
3462cfe870aSOleksandr Tymoshenko if (child == 0) {
3472cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "codec node is missing\n");
3482cfe870aSOleksandr Tymoshenko return;
3492cfe870aSOleksandr Tymoshenko }
3502cfe870aSOleksandr Tymoshenko if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
3512cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "missing sound-dai property in codec node\n");
3522cfe870aSOleksandr Tymoshenko return;
3532cfe870aSOleksandr Tymoshenko }
3542cfe870aSOleksandr Tymoshenko daidev = OF_device_from_xref(xref);
3552cfe870aSOleksandr Tymoshenko if (daidev == NULL) {
3562cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "no driver attached to codec node\n");
3572cfe870aSOleksandr Tymoshenko return;
3582cfe870aSOleksandr Tymoshenko }
3592cfe870aSOleksandr Tymoshenko sc->codec_dev = daidev;
3602cfe870aSOleksandr Tymoshenko
3612cfe870aSOleksandr Tymoshenko /* Add AUX devices */
3622cfe870aSOleksandr Tymoshenko aux_devs = NULL;
3632cfe870aSOleksandr Tymoshenko ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs),
3642cfe870aSOleksandr Tymoshenko (void **)&aux_devs);
3652cfe870aSOleksandr Tymoshenko
3662cfe870aSOleksandr Tymoshenko for (i = 0; i < ncells; i++) {
3672cfe870aSOleksandr Tymoshenko auxdev = OF_device_from_xref(aux_devs[i]);
3682cfe870aSOleksandr Tymoshenko if (auxdev == NULL)
3692cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "warning: no driver attached to aux node\n");
3703cc01caaSChristos Margiolis aux_node = malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
3712cfe870aSOleksandr Tymoshenko if (aux_node == NULL) {
3722cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to allocate aux node struct\n");
3732cfe870aSOleksandr Tymoshenko return;
3742cfe870aSOleksandr Tymoshenko }
3752cfe870aSOleksandr Tymoshenko aux_node->dev = auxdev;
3762cfe870aSOleksandr Tymoshenko SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link);
3772cfe870aSOleksandr Tymoshenko }
3782cfe870aSOleksandr Tymoshenko
3792cfe870aSOleksandr Tymoshenko if (aux_devs)
3802cfe870aSOleksandr Tymoshenko OF_prop_free(aux_devs);
3812cfe870aSOleksandr Tymoshenko
3822cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) {
3832cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to initialize cpu node\n");
3842cfe870aSOleksandr Tymoshenko return;
3852cfe870aSOleksandr Tymoshenko }
3862cfe870aSOleksandr Tymoshenko
3872cfe870aSOleksandr Tymoshenko /* Reverse clock roles for CODEC */
3882cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) {
3892cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to initialize codec node\n");
3902cfe870aSOleksandr Tymoshenko return;
3912cfe870aSOleksandr Tymoshenko }
3922cfe870aSOleksandr Tymoshenko
3932cfe870aSOleksandr Tymoshenko SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
3942cfe870aSOleksandr Tymoshenko if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) {
3952cfe870aSOleksandr Tymoshenko device_printf(sc->dev, "failed to initialize aux node\n");
3962cfe870aSOleksandr Tymoshenko return;
3972cfe870aSOleksandr Tymoshenko }
3982cfe870aSOleksandr Tymoshenko }
3992cfe870aSOleksandr Tymoshenko
400*516a9c02SChristos Margiolis pcm_init(sc->dev, sc);
4012cfe870aSOleksandr Tymoshenko
4022cfe870aSOleksandr Tymoshenko sc->play_channel.sc = sc;
4032cfe870aSOleksandr Tymoshenko sc->rec_channel.sc = sc;
4042cfe870aSOleksandr Tymoshenko
4052cfe870aSOleksandr Tymoshenko pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
4062cfe870aSOleksandr Tymoshenko pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
4072cfe870aSOleksandr Tymoshenko
408*516a9c02SChristos Margiolis if (pcm_register(sc->dev, "at simplebus")) {
409*516a9c02SChristos Margiolis device_printf(sc->dev, "failed to register PCM\n");
410*516a9c02SChristos Margiolis return;
411*516a9c02SChristos Margiolis }
4122cfe870aSOleksandr Tymoshenko
4132cfe870aSOleksandr Tymoshenko AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
4142cfe870aSOleksandr Tymoshenko AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
4152cfe870aSOleksandr Tymoshenko SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
4162cfe870aSOleksandr Tymoshenko AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev);
4172cfe870aSOleksandr Tymoshenko }
4182cfe870aSOleksandr Tymoshenko }
4192cfe870aSOleksandr Tymoshenko
4202cfe870aSOleksandr Tymoshenko static int
audio_soc_attach(device_t dev)4212cfe870aSOleksandr Tymoshenko audio_soc_attach(device_t dev)
4222cfe870aSOleksandr Tymoshenko {
4232cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
4242cfe870aSOleksandr Tymoshenko char *name;
4252cfe870aSOleksandr Tymoshenko phandle_t node, cpu_child;
4262cfe870aSOleksandr Tymoshenko uint32_t xref;
4272cfe870aSOleksandr Tymoshenko int i, ret;
4282cfe870aSOleksandr Tymoshenko char tmp[32];
4292cfe870aSOleksandr Tymoshenko unsigned int fmt, pol, clk;
4302cfe870aSOleksandr Tymoshenko bool frame_master, bitclock_master;
4312cfe870aSOleksandr Tymoshenko
4322cfe870aSOleksandr Tymoshenko sc = device_get_softc(dev);
4332cfe870aSOleksandr Tymoshenko sc->dev = dev;
4342cfe870aSOleksandr Tymoshenko node = ofw_bus_get_node(dev);
4352cfe870aSOleksandr Tymoshenko
4362cfe870aSOleksandr Tymoshenko ret = OF_getprop_alloc(node, "name", (void **)&name);
4372cfe870aSOleksandr Tymoshenko if (ret == -1)
4382cfe870aSOleksandr Tymoshenko name = "SoC audio";
4392cfe870aSOleksandr Tymoshenko
4402cfe870aSOleksandr Tymoshenko sc->name = strdup(name, M_DEVBUF);
4412cfe870aSOleksandr Tymoshenko device_set_desc(dev, sc->name);
4422cfe870aSOleksandr Tymoshenko
4432cfe870aSOleksandr Tymoshenko if (ret != -1)
4442cfe870aSOleksandr Tymoshenko OF_prop_free(name);
4452cfe870aSOleksandr Tymoshenko
4462cfe870aSOleksandr Tymoshenko SLIST_INIT(&sc->aux_devs);
4472cfe870aSOleksandr Tymoshenko
4482cfe870aSOleksandr Tymoshenko ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp));
4492cfe870aSOleksandr Tymoshenko if (ret == 0) {
4502cfe870aSOleksandr Tymoshenko for (i = 0; i < nitems(ausoc_dai_formats); i++) {
4512cfe870aSOleksandr Tymoshenko if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) {
4522cfe870aSOleksandr Tymoshenko fmt = ausoc_dai_formats[i].fmt;
4532cfe870aSOleksandr Tymoshenko break;
4542cfe870aSOleksandr Tymoshenko }
4552cfe870aSOleksandr Tymoshenko }
4562cfe870aSOleksandr Tymoshenko if (i == nitems(ausoc_dai_formats))
4572cfe870aSOleksandr Tymoshenko return (EINVAL);
4582cfe870aSOleksandr Tymoshenko } else
4592cfe870aSOleksandr Tymoshenko fmt = AUDIO_DAI_FORMAT_I2S;
4602cfe870aSOleksandr Tymoshenko
4612cfe870aSOleksandr Tymoshenko if (OF_getencprop(node, "simple-audio-card,mclk-fs",
4622cfe870aSOleksandr Tymoshenko &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0)
4632cfe870aSOleksandr Tymoshenko sc->link_mclk_fs = 0;
4642cfe870aSOleksandr Tymoshenko
4652cfe870aSOleksandr Tymoshenko /* Unless specified otherwise, CPU node is the master */
4662cfe870aSOleksandr Tymoshenko frame_master = bitclock_master = true;
4672cfe870aSOleksandr Tymoshenko
4682cfe870aSOleksandr Tymoshenko cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu");
4692cfe870aSOleksandr Tymoshenko
4702cfe870aSOleksandr Tymoshenko if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0)
4712cfe870aSOleksandr Tymoshenko frame_master = cpu_child == OF_node_from_xref(xref);
4722cfe870aSOleksandr Tymoshenko
4732cfe870aSOleksandr Tymoshenko if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0)
4742cfe870aSOleksandr Tymoshenko bitclock_master = cpu_child == OF_node_from_xref(xref);
4752cfe870aSOleksandr Tymoshenko
4762cfe870aSOleksandr Tymoshenko if (frame_master) {
4772cfe870aSOleksandr Tymoshenko clk = bitclock_master ?
4782cfe870aSOleksandr Tymoshenko AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
4792cfe870aSOleksandr Tymoshenko } else {
4802cfe870aSOleksandr Tymoshenko clk = bitclock_master ?
4812cfe870aSOleksandr Tymoshenko AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
4822cfe870aSOleksandr Tymoshenko }
4832cfe870aSOleksandr Tymoshenko
4842cfe870aSOleksandr Tymoshenko bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion");
4852cfe870aSOleksandr Tymoshenko bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion");
4862cfe870aSOleksandr Tymoshenko if (bitclock_inversion) {
4872cfe870aSOleksandr Tymoshenko pol = frame_inversion ?
4882cfe870aSOleksandr Tymoshenko AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
4892cfe870aSOleksandr Tymoshenko } else {
4902cfe870aSOleksandr Tymoshenko pol = frame_inversion ?
4912cfe870aSOleksandr Tymoshenko AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
4922cfe870aSOleksandr Tymoshenko }
4932cfe870aSOleksandr Tymoshenko
4942cfe870aSOleksandr Tymoshenko sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk);
4952cfe870aSOleksandr Tymoshenko
4962cfe870aSOleksandr Tymoshenko sc->init_hook.ich_func = audio_soc_init;
4972cfe870aSOleksandr Tymoshenko sc->init_hook.ich_arg = sc;
4982cfe870aSOleksandr Tymoshenko if (config_intrhook_establish(&sc->init_hook) != 0)
4992cfe870aSOleksandr Tymoshenko return (ENOMEM);
5002cfe870aSOleksandr Tymoshenko
5012cfe870aSOleksandr Tymoshenko return (0);
5022cfe870aSOleksandr Tymoshenko }
5032cfe870aSOleksandr Tymoshenko
5042cfe870aSOleksandr Tymoshenko static int
audio_soc_detach(device_t dev)5052cfe870aSOleksandr Tymoshenko audio_soc_detach(device_t dev)
5062cfe870aSOleksandr Tymoshenko {
5072cfe870aSOleksandr Tymoshenko struct audio_soc_softc *sc;
5082cfe870aSOleksandr Tymoshenko struct audio_soc_aux_node *aux;
5092cfe870aSOleksandr Tymoshenko
5102cfe870aSOleksandr Tymoshenko sc = device_get_softc(dev);
5112cfe870aSOleksandr Tymoshenko if (sc->name)
5122cfe870aSOleksandr Tymoshenko free(sc->name, M_DEVBUF);
5132cfe870aSOleksandr Tymoshenko
5142cfe870aSOleksandr Tymoshenko while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) {
5152cfe870aSOleksandr Tymoshenko SLIST_REMOVE_HEAD(&sc->aux_devs, link);
5162cfe870aSOleksandr Tymoshenko free(aux, M_DEVBUF);
5172cfe870aSOleksandr Tymoshenko }
5182cfe870aSOleksandr Tymoshenko
5192cfe870aSOleksandr Tymoshenko return (0);
5202cfe870aSOleksandr Tymoshenko }
5212cfe870aSOleksandr Tymoshenko
5222cfe870aSOleksandr Tymoshenko static device_method_t audio_soc_methods[] = {
5232cfe870aSOleksandr Tymoshenko /* device_if methods */
5242cfe870aSOleksandr Tymoshenko DEVMETHOD(device_probe, audio_soc_probe),
5252cfe870aSOleksandr Tymoshenko DEVMETHOD(device_attach, audio_soc_attach),
5262cfe870aSOleksandr Tymoshenko DEVMETHOD(device_detach, audio_soc_detach),
5272cfe870aSOleksandr Tymoshenko
5282cfe870aSOleksandr Tymoshenko DEVMETHOD_END,
5292cfe870aSOleksandr Tymoshenko };
5302cfe870aSOleksandr Tymoshenko
5312cfe870aSOleksandr Tymoshenko static driver_t audio_soc_driver = {
5322cfe870aSOleksandr Tymoshenko "pcm",
5332cfe870aSOleksandr Tymoshenko audio_soc_methods,
5342cfe870aSOleksandr Tymoshenko sizeof(struct audio_soc_softc),
5352cfe870aSOleksandr Tymoshenko };
5362cfe870aSOleksandr Tymoshenko
5372287364eSJohn Baldwin DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, NULL, NULL);
5382cfe870aSOleksandr Tymoshenko MODULE_VERSION(audio_soc, 1);
539