]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/fdt/audio_soc.c
Import device-tree files from Linux 5.11
[FreeBSD/FreeBSD.git] / sys / dev / sound / fdt / audio_soc.c
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 "compatiblity"
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);