diff options
Diffstat (limited to 'sys/dev/sound/pcm/buffer.c')
-rw-r--r-- | sys/dev/sound/pcm/buffer.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c new file mode 100644 index 0000000..81b4037 --- /dev/null +++ b/sys/dev/sound/pcm/buffer.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <dev/sound/pcm/sound.h> + +static void +sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + snd_dbuf *b = (snd_dbuf *)arg; + + if (bootverbose) { + printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf)); + } +} + +/* + * Allocate memory for DMA buffer. If the device do not perform DMA transfer, + * the driver can call malloc(9) by its own. + */ +int +sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, int size) +{ + b->dmatag = dmatag; + b->maxsize = size; + b->bufsize = b->maxsize; + if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, &b->dmamap)) + return ENOSPC; + if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0)) + return ENOSPC; + return sndbuf_resize(b, 2, b->maxsize / 2); +} + +int +sndbuf_setup(snd_dbuf *b, void *buf, int size) +{ + bzero(b, sizeof(*b)); + b->buf = buf; + b->maxsize = size; + b->bufsize = b->maxsize; + return sndbuf_resize(b, 2, b->maxsize / 2); +} + +void +sndbuf_free(snd_dbuf *b) +{ + bus_dmamap_unload(b->dmatag, b->dmamap); + bus_dmamem_free(b->dmatag, b->buf, b->dmamap); +} + +int +sndbuf_resize(snd_dbuf *b, int blkcnt, int blksz) +{ + if (blkcnt == 0) + blkcnt = b->blkcnt; + if (blksz == 0) + blksz = b->blksz; + if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) + return EINVAL; + b->blkcnt = blkcnt; + b->blksz = blksz; + b->bufsize = blkcnt * blksz; + sndbuf_reset(b); + return 0; +} + +void +sndbuf_clear(snd_dbuf *b, int length) +{ + int i; + u_int16_t data, *p; + + if (length == 0) + return; + + if (b->fmt & AFMT_SIGNED) + data = 0x00; + else + data = 0x80; + + if (b->fmt & AFMT_16BIT) + data <<= 8; + else + data |= data << 8; + + if (b->fmt & AFMT_BIGENDIAN) + data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); + + i = b->fp; + p = (u_int16_t *)(b->buf + b->fp); + while (length > 1) { + *p++ = data; + length -= 2; + i += 2; + if (i >= b->bufsize) { + p = (u_int16_t *)b->buf; + i = 0; + } + } + if (length == 1) + *(b->buf + i) = data & 0xff; +} + +void +sndbuf_reset(snd_dbuf *b) +{ + b->rp = b->fp = 0; + b->dl = b->rl = 0; + b->fl = b->bufsize; + b->prev_total = b->total = 0; + b->prev_int_count = b->int_count = 0; + b->underflow = 0; + if (b->buf && b->bufsize > 0) + sndbuf_clear(b, b->bufsize); +} + +int +sndbuf_setfmt(snd_dbuf *b, u_int32_t fmt) +{ + b->fmt = fmt; + b->bps = 1; + b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0; + b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0; + b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0; + return 0; +} + +int +sndbuf_getbps(snd_dbuf *b) +{ + return b->bps; +} + +void * +sndbuf_getbuf(snd_dbuf *b) +{ + return b->buf; +} + +int +sndbuf_getsize(snd_dbuf *b) +{ + return b->bufsize; +} + +int +sndbuf_runsz(snd_dbuf *b) +{ + return b->dl; +} + +int +sndbuf_isadmasetup(snd_dbuf *b, struct resource *drq) +{ + /* should do isa_dma_acquire/isa_dma_release here */ + if (drq == NULL) { + b->flags &= ~SNDBUF_F_ISADMA; + b->chan = -1; + } else { + b->flags &= ~SNDBUF_F_ISADMA; + b->chan = rman_get_start(drq); + } + return 0; +} + +int +sndbuf_isadmasetdir(snd_dbuf *b, int dir) +{ + b->dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; + return 0; +} + +void +sndbuf_isadma(snd_dbuf *b, int go) +{ + KASSERT(b, ("sndbuf_isadma called with b == NULL")); + KASSERT(ISA_DMA(b), ("sndbuf_isadma called on non-ISA channel")); + + switch (go) { + case PCMTRIG_START: + /* isa_dmainit(b->chan, size); */ + isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + isa_dmastop(b->chan); + isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan); + break; + } + + DEB(printf("buf 0x%p ISA DMA %s, channel %d\n", + b, + (go == PCMTRIG_START)? "started" : "stopped", + b->chan)); +} + +int +sndbuf_isadmaptr(snd_dbuf *b) +{ + if (ISA_DMA(b)) { + int i = b->dl? isa_dmastatus(b->chan) : b->bufsize; + if (i < 0) + i = 0; + return b->bufsize - i; + } else KASSERT(1, ("sndbuf_isadmaptr called on invalid channel")); + return -1; +} + +void +sndbuf_isadmabounce(snd_dbuf *b) +{ + if (ISA_DMA(b)) { + /* tell isa_dma to bounce data in/out */ + } else + KASSERT(1, ("chn_isadmabounce called on invalid channel")); +} + |