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