]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/macio/aoa.c
Update to Zstandard 1.4.4
[FreeBSD/FreeBSD.git] / sys / dev / sound / macio / aoa.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2008 by Marco Trillo. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 /*
31  *      Apple Onboard Audio (AOA).
32  */
33
34 #include <sys/cdefs.h>
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/bus.h>
40 #include <sys/malloc.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <machine/dbdma.h>
44 #include <machine/resource.h>
45 #include <machine/bus.h>
46 #include <sys/rman.h>
47 #include <dev/ofw/ofw_bus.h>
48
49 #ifdef HAVE_KERNEL_OPTION_HEADERS
50 #include "opt_snd.h"
51 #endif
52
53 #include <dev/sound/pcm/sound.h>
54 #include <dev/sound/macio/aoa.h>
55
56 #include "mixer_if.h"
57
58 struct aoa_dma {
59         struct mtx               mutex;
60         struct resource         *reg;           /* DBDMA registers */
61         dbdma_channel_t         *channel;       /* DBDMA channel */
62         bus_dma_tag_t            tag;           /* bus_dma tag */
63         struct pcm_channel      *pcm;           /* PCM channel */
64         struct snd_dbuf         *buf;           /* PCM buffer */
65         u_int                    slots;         /* # of slots */
66         u_int                    slot;          /* current slot */
67         u_int                    bufsz;         /* buffer size */
68         u_int                    blksz;         /* block size */
69         int                      running;
70 };
71
72 static void
73 aoa_dma_set_program(struct aoa_dma *dma)
74 {
75         u_int32_t                addr;
76         int                      i;
77
78         addr = (u_int32_t) sndbuf_getbufaddr(dma->buf);
79         KASSERT(dma->bufsz == sndbuf_getsize(dma->buf), ("bad size"));
80
81         dma->slots = dma->bufsz / dma->blksz;
82
83         for (i = 0; i < dma->slots; ++i) {
84                 dbdma_insert_command(dma->channel, 
85                     i, /* slot */
86                     DBDMA_OUTPUT_MORE, /* command */
87                     0, /* stream */
88                     addr, /* data */
89                     dma->blksz, /* count */
90                     DBDMA_ALWAYS, /* interrupt */
91                     DBDMA_COND_TRUE, /* branch */
92                     DBDMA_NEVER, /* wait */
93                     dma->slots + 1 /* branch_slot */
94                 );
95
96                 addr += dma->blksz;
97         }
98
99         /* Branch back to beginning. */
100         dbdma_insert_branch(dma->channel, dma->slots, 0);
101
102         /* STOP command to branch when S0 is asserted. */
103         dbdma_insert_stop(dma->channel, dma->slots + 1);
104
105         /* Set S0 as the condition to branch to STOP. */
106         dbdma_set_branch_selector(dma->channel, 1 << 0, 1 << 0);
107         dbdma_set_device_status(dma->channel, 1 << 0, 0);
108
109         dbdma_sync_commands(dma->channel, BUS_DMASYNC_PREWRITE);
110 }
111
112 #define AOA_BUFFER_SIZE         65536
113
114 static struct aoa_dma * 
115 aoa_dma_create(struct aoa_softc *sc)
116 {
117         struct aoa_dma *dma;
118         bus_dma_tag_t   tag;
119         int             err;
120         device_t        self;
121
122         self = sc->sc_dev;
123         err = bus_dma_tag_create(bus_get_dma_tag(self), 
124             4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 
125             AOA_BUFFER_SIZE, 1, AOA_BUFFER_SIZE, 0, NULL, NULL, &tag);
126         if (err != 0) 
127                 return (NULL);
128
129         dma = malloc(sizeof(*dma), M_DEVBUF, M_WAITOK | M_ZERO);
130         dma->tag = tag;
131         dma->bufsz = AOA_BUFFER_SIZE;
132         dma->blksz = PAGE_SIZE; /* initial blocksize */
133         
134         mtx_init(&dma->mutex, "AOA", NULL, MTX_DEF);
135
136         sc->sc_intrp = dma;
137
138         return (dma);
139 }
140
141 static void
142 aoa_dma_delete(struct aoa_dma *dma)
143 {
144         bus_dma_tag_destroy(dma->tag);
145         mtx_destroy(&dma->mutex);
146         free(dma, M_DEVBUF);
147 }
148
149 static u_int32_t
150 aoa_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksz)
151 {
152         struct aoa_dma          *dma = data;
153         int                      err, lz;
154
155         DPRINTF(("aoa_chan_setblocksize: blocksz = %u, dma->blksz = %u\n", 
156                 blocksz, dma->blksz));
157         KASSERT(!dma->running, ("dma is running"));
158         KASSERT(blocksz > 0, ("bad blocksz"));
159
160         /* Round blocksz down to a power of two... */
161         __asm volatile ("cntlzw %0,%1" : "=r"(lz) : "r"(blocksz));
162         blocksz = 1 << (31 - lz);
163         DPRINTF(("blocksz = %u\n", blocksz));
164
165         /* ...but no more than the buffer. */
166         if (blocksz > dma->bufsz)
167                 blocksz = dma->bufsz;
168
169         err = sndbuf_resize(dma->buf, dma->bufsz / blocksz, blocksz);
170         if (err != 0) {
171                 DPRINTF(("sndbuf_resize returned %d\n", err));
172                 return (0);
173         }
174
175         if (blocksz == dma->blksz)
176                 return (dma->blksz);
177
178         /* One slot per block plus branch to 0 plus STOP. */
179         err = dbdma_resize_channel(dma->channel, 2 + dma->bufsz / blocksz);
180         if (err != 0) {
181                 DPRINTF(("dbdma_resize_channel returned %d\n", err));
182                 return (0);
183         }
184
185         /* Set the new blocksize. */
186         dma->blksz = blocksz;
187         aoa_dma_set_program(dma);
188
189         return (dma->blksz);
190 }
191
192 static int
193 aoa_chan_setformat(kobj_t obj, void *data, u_int32_t format)
194 {
195         DPRINTF(("aoa_chan_setformat: format = %u\n", format));
196
197         if (format != SND_FORMAT(AFMT_S16_BE, 2, 0))
198                 return (EINVAL);
199
200         return (0);
201 }
202
203 static u_int32_t
204 aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
205 {
206         DPRINTF(("aoa_chan_setspeed: speed = %u\n", speed));
207
208         return (44100);
209 }
210
211 static u_int32_t
212 aoa_chan_getptr(kobj_t obj, void *data)
213 {
214         struct aoa_dma   *dma = data;
215
216         if (!dma->running)
217                 return (0);
218         
219         return (dma->slot * dma->blksz);
220 }
221
222 static void *
223 aoa_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 
224         struct pcm_channel *c, int dir)
225 {
226         struct aoa_softc        *sc = devinfo;
227         struct aoa_dma          *dma;
228         int                      max_slots, err;
229
230         KASSERT(dir == PCMDIR_PLAY, ("bad dir"));
231
232         dma = aoa_dma_create(sc);
233         if (!dma)
234                 return (NULL);
235         dma->pcm = c;
236         dma->buf = b;
237         dma->reg = sc->sc_odma;
238
239         /* One slot per block, plus branch to 0 plus STOP. */
240         max_slots = 2 + dma->bufsz / dma->blksz;
241         err = dbdma_allocate_channel(dma->reg, 0, bus_get_dma_tag(sc->sc_dev),
242             max_slots, &dma->channel );
243         if (err != 0) {
244                 aoa_dma_delete(dma);
245                 return (NULL);
246         }
247
248         if (sndbuf_alloc(dma->buf, dma->tag, 0, dma->bufsz) != 0) {
249                 dbdma_free_channel(dma->channel);
250                 aoa_dma_delete(dma);
251                 return (NULL);
252         }
253
254         aoa_dma_set_program(dma);
255
256         return (dma);
257 }
258
259 static int
260 aoa_chan_trigger(kobj_t obj, void *data, int go)
261 {
262         struct aoa_dma  *dma = data;
263         int              i;
264
265         switch (go) {
266         case PCMTRIG_START:
267
268                 /* Start the DMA. */
269                 dma->running = 1;
270                 
271                 dma->slot = 0;
272                 dbdma_set_current_cmd(dma->channel, dma->slot);
273
274                 dbdma_run(dma->channel);
275
276                 return (0);
277
278         case PCMTRIG_STOP:
279         case PCMTRIG_ABORT:
280                 
281                 mtx_lock(&dma->mutex);
282
283                 dma->running = 0;
284
285                 /* Make it branch to the STOP command. */
286                 dbdma_set_device_status(dma->channel, 1 << 0, 1 << 0);
287
288                 /* XXX should wait for DBDMA_ACTIVE to clear. */
289                 DELAY(40000);
290
291                 /* Reset the DMA. */
292                 dbdma_stop(dma->channel);
293                 dbdma_set_device_status(dma->channel, 1 << 0, 0);
294
295                 for (i = 0; i < dma->slots; ++i)
296                         dbdma_clear_cmd_status(dma->channel, i);
297
298                 mtx_unlock(&dma->mutex);
299
300                 return (0);
301         }
302
303         return (0);
304 }
305
306 static int
307 aoa_chan_free(kobj_t obj, void *data)
308 {
309         struct aoa_dma  *dma = data;
310
311         sndbuf_free(dma->buf);
312         dbdma_free_channel(dma->channel);
313         aoa_dma_delete(dma);
314
315         return (0);
316 }
317
318 void 
319 aoa_interrupt(void *xsc)
320 {
321         struct aoa_softc        *sc = xsc;
322         struct aoa_dma          *dma;
323
324         if (!(dma = sc->sc_intrp) || !dma->running)
325                 return;
326
327         mtx_lock(&dma->mutex);
328
329         while (dbdma_get_cmd_status(dma->channel, dma->slot)) {
330
331                 dbdma_clear_cmd_status(dma->channel, dma->slot);
332                 dma->slot = (dma->slot + 1) % dma->slots;
333
334                 mtx_unlock(&dma->mutex);
335                 chn_intr(dma->pcm);
336                 mtx_lock(&dma->mutex);
337         }
338
339         mtx_unlock(&dma->mutex);
340 }
341
342 static u_int32_t sc_fmt[] = {
343         SND_FORMAT(AFMT_S16_BE, 2, 0),
344         0
345 };
346 static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0};
347
348 static struct pcmchan_caps *
349 aoa_chan_getcaps(kobj_t obj, void *data)
350 {
351         return (&aoa_caps);
352 }
353
354 static kobj_method_t aoa_chan_methods[] = {
355         KOBJMETHOD(channel_init,        aoa_chan_init),
356         KOBJMETHOD(channel_free,        aoa_chan_free),
357         KOBJMETHOD(channel_setformat,   aoa_chan_setformat),
358         KOBJMETHOD(channel_setspeed,    aoa_chan_setspeed),
359         KOBJMETHOD(channel_setblocksize,aoa_chan_setblocksize),
360         KOBJMETHOD(channel_trigger,     aoa_chan_trigger),
361         KOBJMETHOD(channel_getptr,      aoa_chan_getptr),
362         KOBJMETHOD(channel_getcaps,     aoa_chan_getcaps),
363         KOBJMETHOD_END
364 };
365 CHANNEL_DECLARE(aoa_chan);
366
367 int
368 aoa_attach(void *xsc)
369 {
370         char status[SND_STATUSLEN];
371         struct aoa_softc *sc;
372         device_t self;
373         int err;
374
375         sc = xsc;
376         self = sc->sc_dev;
377
378         if (pcm_register(self, sc, 1, 0))
379                 return (ENXIO);
380
381         err = pcm_getbuffersize(self, AOA_BUFFER_SIZE, AOA_BUFFER_SIZE,
382             AOA_BUFFER_SIZE);
383         DPRINTF(("pcm_getbuffersize returned %d\n", err));
384
385         pcm_addchan(self, PCMDIR_PLAY, &aoa_chan_class, sc);
386
387         snprintf(status, sizeof(status), "at %s", ofw_bus_get_name(self)); 
388         pcm_setstatus(self, status);
389
390         return (0);
391 }
392