The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/macio/aoa.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: fd695d28e9ac2bc3bfd9148b9c96402c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.