]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/a10_hdmiaudio.c
arm: allwinner: clk: Fix nm_recalc
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / a10_hdmiaudio.c
1 /*-
2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Allwinner A10/A20 HDMI Audio
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/condvar.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43
44 #include <dev/sound/pcm/sound.h>
45 #include <dev/sound/chip.h>
46
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/ofw_bus_subr.h>
49
50 #include "sunxi_dma_if.h"
51 #include "mixer_if.h"
52
53 #define DRQTYPE_HDMIAUDIO       24
54 #define DRQTYPE_SDRAM           1
55
56 #define DMA_WIDTH               32
57 #define DMA_BURST_LEN           8
58 #define DDMA_BLKSIZE            32
59 #define DDMA_WAIT_CYC           8
60
61 #define DMABUF_MIN              4096
62 #define DMABUF_DEFAULT          65536
63 #define DMABUF_MAX              131072
64
65 #define HDMI_SAMPLERATE         48000
66
67 #define TX_FIFO                 0x01c16400
68
69 static uint32_t a10hdmiaudio_fmt[] = {
70         SND_FORMAT(AFMT_S16_LE, 2, 0),
71         0
72 };
73
74 static struct pcmchan_caps a10hdmiaudio_pcaps = {
75     HDMI_SAMPLERATE, HDMI_SAMPLERATE, a10hdmiaudio_fmt, 0
76 };
77
78 struct a10hdmiaudio_info;
79
80 struct a10hdmiaudio_chinfo {
81         struct snd_dbuf         *buffer;
82         struct pcm_channel      *channel;       
83         struct a10hdmiaudio_info        *parent;
84         bus_dmamap_t            dmamap;
85         void                    *dmaaddr;
86         bus_addr_t              physaddr;
87         device_t                dmac;
88         void                    *dmachan;
89
90         int                     run;
91         uint32_t                pos;
92         uint32_t                blocksize;
93 };
94
95 struct a10hdmiaudio_info {
96         device_t                dev;
97         struct mtx              *lock;
98         bus_dma_tag_t           dmat;
99         unsigned                dmasize;
100
101         struct a10hdmiaudio_chinfo      play;
102 };
103
104 /*
105  * Mixer interface
106  */
107
108 static int
109 a10hdmiaudio_mixer_init(struct snd_mixer *m)
110 {
111         mix_setdevs(m, SOUND_MASK_PCM);
112
113         return (0);
114 }
115
116 static int
117 a10hdmiaudio_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left,
118     unsigned right)
119 {
120         return (-1);
121 }
122
123 static kobj_method_t a10hdmiaudio_mixer_methods[] = {
124         KOBJMETHOD(mixer_init,          a10hdmiaudio_mixer_init),
125         KOBJMETHOD(mixer_set,           a10hdmiaudio_mixer_set),
126         KOBJMETHOD_END
127 };
128 MIXER_DECLARE(a10hdmiaudio_mixer);
129
130
131 /*
132  * Channel interface
133  */
134
135 static void
136 a10hdmiaudio_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
137 {
138         struct a10hdmiaudio_chinfo *ch = arg;
139
140         if (error != 0)
141                 return;
142
143         ch->physaddr = segs[0].ds_addr;
144 }
145
146 static void
147 a10hdmiaudio_transfer(struct a10hdmiaudio_chinfo *ch)
148 {
149         int error;
150
151         error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan,
152             ch->physaddr + ch->pos, TX_FIFO, ch->blocksize);
153         if (error) {
154                 ch->run = 0;
155                 device_printf(ch->parent->dev, "DMA transfer failed: %d\n",
156                     error);
157         }
158 }
159
160 static void
161 a10hdmiaudio_dmaconfig(struct a10hdmiaudio_chinfo *ch)
162 {
163         struct sunxi_dma_config conf;
164
165         memset(&conf, 0, sizeof(conf));
166         conf.src_width = conf.dst_width = DMA_WIDTH;
167         conf.src_burst_len = conf.dst_burst_len = DMA_BURST_LEN;
168         conf.src_blksize = conf.dst_blksize = DDMA_BLKSIZE;
169         conf.src_wait_cyc = conf.dst_wait_cyc = DDMA_WAIT_CYC;
170         conf.src_drqtype = DRQTYPE_SDRAM;
171         conf.dst_drqtype = DRQTYPE_HDMIAUDIO;
172         conf.dst_noincr = true;
173
174         SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf);
175 }
176
177 static void
178 a10hdmiaudio_dmaintr(void *priv)
179 {
180         struct a10hdmiaudio_chinfo *ch = priv;
181         unsigned bufsize;
182
183         bufsize = sndbuf_getsize(ch->buffer);
184
185         ch->pos += ch->blocksize;
186         if (ch->pos >= bufsize)
187                 ch->pos -= bufsize;
188
189         if (ch->run) {
190                 chn_intr(ch->channel);
191                 a10hdmiaudio_transfer(ch);
192         }
193 }
194
195 static void
196 a10hdmiaudio_start(struct a10hdmiaudio_chinfo *ch)
197 {
198         ch->pos = 0;
199
200         /* Configure DMA channel */
201         a10hdmiaudio_dmaconfig(ch);
202
203         /* Start DMA transfer */
204         a10hdmiaudio_transfer(ch);
205 }
206
207 static void
208 a10hdmiaudio_stop(struct a10hdmiaudio_chinfo *ch)
209 {
210         /* Disable DMA channel */
211         SUNXI_DMA_HALT(ch->dmac, ch->dmachan);
212 }
213
214 static void *
215 a10hdmiaudio_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
216     struct pcm_channel *c, int dir)
217 {
218         struct a10hdmiaudio_info *sc = devinfo;
219         struct a10hdmiaudio_chinfo *ch = &sc->play;
220         int error;
221
222         ch->parent = sc;
223         ch->channel = c;
224         ch->buffer = b;
225
226         ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0);
227         if (ch->dmac == NULL) {
228                 device_printf(sc->dev, "cannot find DMA controller\n");
229                 return (NULL);
230         }
231         ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, true, a10hdmiaudio_dmaintr, ch);
232         if (ch->dmachan == NULL) {
233                 device_printf(sc->dev, "cannot allocate DMA channel\n");
234                 return (NULL);
235         }
236
237         error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr,
238             BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap);
239         if (error != 0) {
240                 device_printf(sc->dev, "cannot allocate channel buffer\n");
241                 return (NULL);
242         }
243         error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr,
244             sc->dmasize, a10hdmiaudio_dmamap_cb, ch, BUS_DMA_NOWAIT);
245         if (error != 0) {
246                 device_printf(sc->dev, "cannot load DMA map\n");
247                 return (NULL);
248         }
249         memset(ch->dmaaddr, 0, sc->dmasize);
250
251         if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) {
252                 device_printf(sc->dev, "cannot setup sndbuf\n");
253                 return (NULL);
254         }
255
256         return (ch);
257 }
258
259 static int
260 a10hdmiaudio_chan_free(kobj_t obj, void *data)
261 {
262         struct a10hdmiaudio_chinfo *ch = data;
263         struct a10hdmiaudio_info *sc = ch->parent;
264
265         SUNXI_DMA_FREE(ch->dmac, ch->dmachan);
266         bus_dmamap_unload(sc->dmat, ch->dmamap);
267         bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap);
268
269         return (0);
270 }
271
272 static int
273 a10hdmiaudio_chan_setformat(kobj_t obj, void *data, uint32_t format)
274 {
275         return (0);
276 }
277
278 static uint32_t
279 a10hdmiaudio_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
280 {
281         return (HDMI_SAMPLERATE);
282 }
283
284 static uint32_t
285 a10hdmiaudio_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
286 {
287         struct a10hdmiaudio_chinfo *ch = data;
288
289         ch->blocksize = blocksize & ~3;
290
291         return (ch->blocksize);
292 }
293
294 static int
295 a10hdmiaudio_chan_trigger(kobj_t obj, void *data, int go)
296 {
297         struct a10hdmiaudio_chinfo *ch = data;
298         struct a10hdmiaudio_info *sc = ch->parent;
299
300         if (!PCMTRIG_COMMON(go))
301                 return (0);
302
303         snd_mtxlock(sc->lock);
304         switch (go) {
305         case PCMTRIG_START:
306                 ch->run = 1;
307                 a10hdmiaudio_start(ch);
308                 break;
309         case PCMTRIG_STOP:
310         case PCMTRIG_ABORT:
311                 ch->run = 0;
312                 a10hdmiaudio_stop(ch);
313                 break;
314         default:
315                 break;
316         }
317         snd_mtxunlock(sc->lock);
318
319         return (0);
320 }
321
322 static uint32_t
323 a10hdmiaudio_chan_getptr(kobj_t obj, void *data)
324 {
325         struct a10hdmiaudio_chinfo *ch = data;
326
327         return (ch->pos);
328 }
329
330 static struct pcmchan_caps *
331 a10hdmiaudio_chan_getcaps(kobj_t obj, void *data)
332 {
333         return (&a10hdmiaudio_pcaps);
334 }
335
336 static kobj_method_t a10hdmiaudio_chan_methods[] = {
337         KOBJMETHOD(channel_init,                a10hdmiaudio_chan_init),
338         KOBJMETHOD(channel_free,                a10hdmiaudio_chan_free),
339         KOBJMETHOD(channel_setformat,           a10hdmiaudio_chan_setformat),
340         KOBJMETHOD(channel_setspeed,            a10hdmiaudio_chan_setspeed),
341         KOBJMETHOD(channel_setblocksize,        a10hdmiaudio_chan_setblocksize),
342         KOBJMETHOD(channel_trigger,             a10hdmiaudio_chan_trigger),
343         KOBJMETHOD(channel_getptr,              a10hdmiaudio_chan_getptr),
344         KOBJMETHOD(channel_getcaps,             a10hdmiaudio_chan_getcaps),
345         KOBJMETHOD_END
346 };
347 CHANNEL_DECLARE(a10hdmiaudio_chan);
348
349
350 /*
351  * Device interface
352  */
353
354 static int
355 a10hdmiaudio_probe(device_t dev)
356 {
357         if (!ofw_bus_status_okay(dev))
358                 return (ENXIO);
359
360         if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmiaudio"))
361                 return (ENXIO);
362
363         device_set_desc(dev, "Allwinner HDMI Audio");
364         return (BUS_PROBE_DEFAULT);
365 }
366
367 static int
368 a10hdmiaudio_attach(device_t dev)
369 {
370         struct a10hdmiaudio_info *sc;
371         char status[SND_STATUSLEN];
372         int error;
373
374         sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
375         sc->dev = dev;
376         sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10hdmiaudio softc");
377
378         sc->dmasize = pcm_getbuffersize(dev, DMABUF_MIN,
379             DMABUF_DEFAULT, DMABUF_MAX);
380         error = bus_dma_tag_create(
381             bus_get_dma_tag(dev),
382             4, sc->dmasize,             /* alignment, boundary */
383             BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
384             BUS_SPACE_MAXADDR,          /* highaddr */
385             NULL, NULL,                 /* filter, filterarg */
386             sc->dmasize, 1,             /* maxsize, nsegs */
387             sc->dmasize, 0,             /* maxsegsize, flags */
388             NULL, NULL,                 /* lockfunc, lockarg */
389             &sc->dmat);
390         if (error != 0) {
391                 device_printf(dev, "cannot create DMA tag\n");
392                 goto fail;
393         }
394
395         if (mixer_init(dev, &a10hdmiaudio_mixer_class, sc)) {
396                 device_printf(dev, "mixer_init failed\n");
397                 goto fail;
398         }
399
400         pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
401         pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
402
403         if (pcm_register(dev, sc, 1, 0)) {
404                 device_printf(dev, "pcm_register failed\n");
405                 goto fail;
406         }
407
408         pcm_addchan(dev, PCMDIR_PLAY, &a10hdmiaudio_chan_class, sc);
409
410         snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev));
411         pcm_setstatus(dev, status);
412
413         return (0);
414
415 fail:
416         snd_mtxfree(sc->lock);
417         free(sc, M_DEVBUF);
418
419         return (error);
420 }
421
422 static device_method_t a10hdmiaudio_pcm_methods[] = {
423         /* Device interface */
424         DEVMETHOD(device_probe,         a10hdmiaudio_probe),
425         DEVMETHOD(device_attach,        a10hdmiaudio_attach),
426
427         DEVMETHOD_END
428 };
429
430 static driver_t a10hdmiaudio_pcm_driver = {
431         "pcm",
432         a10hdmiaudio_pcm_methods,
433         PCM_SOFTC_SIZE,
434 };
435
436 DRIVER_MODULE(a10hdmiaudio, simplebus, a10hdmiaudio_pcm_driver, pcm_devclass, 0, 0);
437 MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
438 MODULE_VERSION(a10hdmiaudio, 1);