freebsd-nq/sys/dev/sound/pcm/buffer.c
Cameron Grant 350a5fafb1 update code dealing with snd_dbuf objects to do so using a functional interface
modify chn_setblocksize() to pick a default soft-blocksize appropriate to the
sample rate and format in use.  it will aim for a power of two size small
enough to generate block sizes of at most 20ms.  it will also set the
hard-blocksize taking into account rate/format conversions in use.

update drivers to implement setblocksize  correctly:
updated, tested: 	sb16, emu10k1, maestro, solo
updated, untested: 	ad1816, ess, mss, sb8, csa
not updated: 		ds1, es137x, fm801, neomagic, t4dwave, via82c686

i lack hardware to test: ad1816, csa, fm801, neomagic
others will be updated/tested in the next few days.
2000-12-23 03:16:13 +00:00

245 lines
5.4 KiB
C

/*
* 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"));
}