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  * 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                 dbdma_clear_cmd_status(dma->channel, dma->slot);
  331                 dma->slot = (dma->slot + 1) % dma->slots;
  332 
  333                 mtx_unlock(&dma->mutex);
  334                 chn_intr(dma->pcm);
  335                 mtx_lock(&dma->mutex);
  336         }
  337 
  338         mtx_unlock(&dma->mutex);
  339 }
  340 
  341 static u_int32_t sc_fmt[] = {
  342         SND_FORMAT(AFMT_S16_BE, 2, 0),
  343         0
  344 };
  345 static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0};
  346 
  347 static struct pcmchan_caps *
  348 aoa_chan_getcaps(kobj_t obj, void *data)
  349 {
  350         return (&aoa_caps);
  351 }
  352 
  353 static kobj_method_t aoa_chan_methods[] = {
  354         KOBJMETHOD(channel_init,        aoa_chan_init),
  355         KOBJMETHOD(channel_free,        aoa_chan_free),
  356         KOBJMETHOD(channel_setformat,   aoa_chan_setformat),
  357         KOBJMETHOD(channel_setspeed,    aoa_chan_setspeed),
  358         KOBJMETHOD(channel_setblocksize,aoa_chan_setblocksize),
  359         KOBJMETHOD(channel_trigger,     aoa_chan_trigger),
  360         KOBJMETHOD(channel_getptr,      aoa_chan_getptr),
  361         KOBJMETHOD(channel_getcaps,     aoa_chan_getcaps),
  362         KOBJMETHOD_END
  363 };
  364 CHANNEL_DECLARE(aoa_chan);
  365 
  366 int
  367 aoa_attach(void *xsc)
  368 {
  369         char status[SND_STATUSLEN];
  370         struct aoa_softc *sc;
  371         device_t self;
  372         int err __unused;
  373 
  374         sc = xsc;
  375         self = sc->sc_dev;
  376 
  377         if (pcm_register(self, sc, 1, 0))
  378                 return (ENXIO);
  379 
  380         err = pcm_getbuffersize(self, AOA_BUFFER_SIZE, AOA_BUFFER_SIZE,
  381             AOA_BUFFER_SIZE);
  382         DPRINTF(("pcm_getbuffersize returned %d\n", err));
  383 
  384         pcm_addchan(self, PCMDIR_PLAY, &aoa_chan_class, sc);
  385 
  386         snprintf(status, sizeof(status), "at %s", ofw_bus_get_name(self)); 
  387         pcm_setstatus(self, status);
  388 
  389         return (0);
  390 }

Cache object: 70f12f9acc2516d31e00eeadc40d448d


[ 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.