summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pcm/channel.c')
-rw-r--r--sys/dev/sound/pcm/channel.c276
1 files changed, 87 insertions, 189 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 4030028..da4aa14 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -36,7 +36,6 @@ MALLOC_DEFINE(M_CHANNEL, "channel", "pcm channel");
#define DMA_ALIGN_THRESHOLD 4
#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
-#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
#define CANCHANGE(c) (!(c)->buffer.dl)
#define ROUND(x) ((x) & DMA_ALIGN_MASK)
@@ -44,7 +43,6 @@ MALLOC_DEFINE(M_CHANNEL, "channel", "pcm channel");
#define DEB(x) x
*/
-static void buf_clear(snd_dbuf *b, u_int32_t fmt, int length);
static void chn_dmaupdate(pcm_channel *c);
static void chn_wrintr(pcm_channel *c);
static void chn_rdintr(pcm_channel *c);
@@ -110,14 +108,6 @@ produced on overruns.
* gets copied in or out of the real buffer. fix requires mods to isa_dma.c
* and possibly fixes to other autodma mode clients
*/
-static void
-chn_isadmabounce(pcm_channel *c)
-{
- if (ISA_DMA(&c->buffer)) {
- /* tell isa_dma to bounce data in/out */
- } else KASSERT(1, ("chn_isadmabounce called on invalid channel"));
-}
-
static int
chn_polltrigger(pcm_channel *c)
{
@@ -156,7 +146,7 @@ chn_dmadone(pcm_channel *c)
else
chn_dmaupdate(c);
if (ISA_DMA(b))
- chn_isadmabounce(c); /* sync bounce buffer */
+ sndbuf_isadmabounce(b); /* sync bounce buffer */
b->int_count++;
}
@@ -286,7 +276,7 @@ chn_wrfeed(pcm_channel *c)
b->fl -= l;
b->fp = (b->fp + l) % b->bufsize;
/* Clear the new space in the secondary buffer. */
- buf_clear(bs, bs->fmt, l);
+ sndbuf_clear(bs, l);
/* Accumulate the total bytes of the moved samples. */
lacc += l;
/* A feed to the DMA buffer is equivalent to an interrupt. */
@@ -389,7 +379,7 @@ chn_wrintr(pcm_channel *c)
chn_wrfeed(c);
else {
while (chn_wrfeed(c) > 0);
- buf_clear(b, b->fmt, b->fl);
+ sndbuf_clear(b, b->fl);
}
chn_dmawakeup(c);
if (c->flags & CHN_F_TRIGGERED) {
@@ -414,7 +404,7 @@ chn_wrintr(pcm_channel *c)
* we are near to underflow condition, so to prevent
* audio 'clicks' clear next b->fl bytes
*/
- buf_clear(b, b->fmt, b->fl);
+ sndbuf_clear(b, b->fl);
if (b->rl < DMA_ALIGN_THRESHOLD)
b->underflow = 1;
}
@@ -423,7 +413,7 @@ chn_wrintr(pcm_channel *c)
DEB(printf("underflow, flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl));
if (b->dl) { /* DMA was active */
b->underflow = 1; /* set underflow flag */
- buf_clear(b, b->fmt, b->bufsize);
+ sndbuf_clear(b, b->bufsize);
}
}
}
@@ -649,7 +639,7 @@ chn_rdfeed2nd(pcm_channel *c, struct uio *buf)
bs->rl -= w;
bs->rp = (bs->rp + w) % bs->bufsize;
/* Clear the new space in the secondary buffer. */
- buf_clear(bs, bs->fmt, l);
+ sndbuf_clear(bs, l);
/* Accumulate the total bytes of the moved samples. */
bs->total += w;
wacc += w;
@@ -844,76 +834,6 @@ chn_start(pcm_channel *c, int force)
return r;
}
-static void
-chn_dma_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 drvier can call malloc(9) by its own.
- */
-int
-chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat)
-{
- b->parent_dmat = parent_dmat;
- if (bus_dmamem_alloc(b->parent_dmat, (void **)&b->buf,
- BUS_DMA_NOWAIT, &b->dmamap)) return -1;
- if (bus_dmamap_load(b->parent_dmat, b->dmamap, b->buf,
- b->bufsize, chn_dma_setmap, b, 0)) return -1;
- return 0;
-}
-
-void
-chn_freebuf(snd_dbuf *b)
-{
- bus_dmamem_free(b->parent_dmat, b->buf, b->dmamap);
-}
-
-static void
-buf_clear(snd_dbuf *b, u_int32_t fmt, int length)
-{
- int i;
- u_int16_t data, *p;
-
- if (length == 0)
- return;
-
- if (fmt & AFMT_SIGNED)
- data = 0x00;
- else
- data = 0x80;
-
- if (fmt & AFMT_16BIT)
- data <<= 8;
- else
- data |= data << 8;
-
- if (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
chn_resetbuf(pcm_channel *c)
{
@@ -921,59 +841,8 @@ chn_resetbuf(pcm_channel *c)
snd_dbuf *bs = &c->buffer2nd;
c->blocks = 0;
- 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)
- buf_clear(b, b->fmt, b->bufsize);
-
- bs->rp = bs->fp = 0;
- bs->dl = bs->rl = 0;
- bs->fl = bs->bufsize;
- bs->prev_total = bs->total = 0;
- bs->prev_int_count = bs->int_count = 0;
- bs->underflow = 0;
- if (bs->buf && bs->bufsize > 0)
- buf_clear(bs, bs->fmt, bs->bufsize);
-}
-
-void
-buf_isadma(snd_dbuf *b, int go)
-{
- KASSERT(b, ("buf_isadma called with b == NULL"));
- KASSERT(ISA_DMA(b), ("buf_isadma called on non-ISA channel"));
-
- switch (go) {
- case PCMTRIG_START:
- 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
-buf_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, ("buf_isadmaptr called on invalid channel"));
- return -1;
+ sndbuf_reset(b);
+ sndbuf_reset(bs);
}
/*
@@ -1132,9 +1001,6 @@ chn_reset(pcm_channel *c, u_int32_t fmt)
chn_abort(c);
c->flags &= CHN_F_RESET;
CHANNEL_RESET(c->methods, c->devinfo);
- r = chn_setblocksize(c, CHN_2NDBUFBLKNUM, CHN_2NDBUFBLKSIZE);
- if (r)
- return r;
if (fmt) {
hwspd = DSP_DEFAULT_SPEED;
RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
@@ -1146,6 +1012,9 @@ chn_reset(pcm_channel *c, u_int32_t fmt)
if (r == 0)
r = chn_setvolume(c, 100, 100);
}
+ r = chn_setblocksize(c, 0, 0);
+ if (r)
+ return r;
chn_resetbuf(c);
CHANNEL_RESETDONE(c->methods, c->devinfo);
/* c->flags |= CHN_F_INIT; */
@@ -1169,6 +1038,7 @@ int
chn_init(pcm_channel *c, void *devinfo, int dir)
{
struct feeder_class *fc;
+ snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
/* Initialize the hardware and DMA buffer first. */
@@ -1191,6 +1061,8 @@ chn_init(pcm_channel *c, void *devinfo, int dir)
/* And the secondary buffer. */
bs->buf = NULL;
+ sndbuf_setfmt(b, AFMT_U8);
+ sndbuf_setfmt(bs, AFMT_U8);
bs->bufsize = 0;
return 0;
}
@@ -1202,7 +1074,7 @@ chn_kill(pcm_channel *c)
chn_trigger(c, PCMTRIG_ABORT);
while (chn_removefeeder(c) == 0);
if (CHANNEL_FREE(c->methods, c->devinfo))
- chn_freebuf(&c->buffer);
+ sndbuf_free(&c->buffer);
c->flags |= CHN_F_DEAD;
return 0;
}
@@ -1236,44 +1108,59 @@ int
chn_setspeed(pcm_channel *c, int speed)
{
pcm_feeder *f;
- int r, hwspd, delta;
+ snd_dbuf *b = &c->buffer;
+ snd_dbuf *bs = &c->buffer2nd;
+ int r, delta;
DEB(printf("want speed %d, ", speed));
if (speed <= 0)
return EINVAL;
if (CANCHANGE(c)) {
c->speed = speed;
- hwspd = speed;
- RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
- DEB(printf("try speed %d, ", hwspd));
- hwspd = CHANNEL_SETSPEED(c->methods, c->devinfo, hwspd);
- DEB(printf("got speed %d, ", hwspd));
- delta = hwspd - speed;
+ b->spd = speed;
+ bs->spd = speed;
+ RANGE(b->spd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
+ DEB(printf("try speed %d, ", b->spd));
+ b->spd = CHANNEL_SETSPEED(c->methods, c->devinfo, b->spd);
+ DEB(printf("got speed %d, ", b->spd));
+
+ delta = b->spd - bs->spd;
if (delta < 0)
delta = -delta;
+
c->feederflags &= ~(1 << FEEDER_RATE);
if (delta > 500)
c->feederflags |= 1 << FEEDER_RATE;
else
- speed = hwspd;
+ bs->spd = b->spd;
+
r = chn_buildfeeder(c);
DEB(printf("r = %d\n", r));
if (r)
return r;
+
+ r = chn_setblocksize(c, 0, 0);
+ if (r)
+ return r;
+
if (!(c->feederflags & (1 << FEEDER_RATE)))
return 0;
+
f = chn_findfeeder(c, FEEDER_RATE);
DEB(printf("feedrate = %p\n", f));
if (f == NULL)
return EINVAL;
- r = FEEDER_SET(f, FEEDRATE_SRC, speed);
- DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", speed, r));
+
+ r = FEEDER_SET(f, FEEDRATE_SRC, bs->spd);
+ DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", bs->spd, r));
if (r)
return r;
- r = FEEDER_SET(f, FEEDRATE_DST, hwspd);
- DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", hwspd, r));
+
+ r = FEEDER_SET(f, FEEDRATE_DST, b->spd);
+ DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", b->spd, r));
if (r)
return r;
+
return 0;
}
c->speed = speed;
@@ -1287,8 +1174,8 @@ chn_setformat(pcm_channel *c, u_int32_t fmt)
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
int r;
-
u_int32_t hwfmt;
+
if (CANCHANGE(c)) {
DEB(printf("want format %d\n", fmt));
c->format = fmt;
@@ -1300,8 +1187,8 @@ chn_setformat(pcm_channel *c, u_int32_t fmt)
if (r)
return r;
hwfmt = c->feeder->desc->out;
- b->fmt = hwfmt;
- bs->fmt = hwfmt;
+ sndbuf_setfmt(b, hwfmt);
+ sndbuf_setfmt(bs, hwfmt);
chn_resetbuf(c);
CHANNEL_SETFORMAT(c->methods, c->devinfo, hwfmt);
return chn_setspeed(c, c->speed);
@@ -1316,54 +1203,65 @@ chn_setblocksize(pcm_channel *c, int blkcnt, int blksz)
{
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
- int s, ss, bufsz;
+ int s, bufsz, irqhz, tmp;
- if (bs->blkcnt == blkcnt && bs->blksz == blksz)
- return 0;
- if (c->flags & CHN_F_MAPPED) {
- DEB(printf("chn_setblocksize: can't work on mapped channel"));
+ if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
return EINVAL;
- }
- c->flags &= ~CHN_F_HAS_SIZE;
-
- ss = 1;
- ss <<= (bs->fmt & AFMT_STEREO)? 1 : 0;
- ss <<= (bs->fmt & AFMT_16BIT)? 1 : 0;
- if (blksz >= 2)
+ if (blksz == 0 || blksz == -1) {
+ if (blksz == -1)
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (c->flags & CHN_F_HAS_SIZE)
+ return 0;
+ blksz = (bs->bps * bs->spd) / CHN_DEFAULT_HZ;
+ tmp = 32;
+ while (tmp <= blksz)
+ tmp <<= 1;
+ tmp >>= 1;
+ blksz = tmp;
+
+ RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
+ RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
+ } else {
+ if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
+ return EINVAL;
c->flags |= CHN_F_HAS_SIZE;
- /* let us specify blksz without setting CHN_F_HAS_SIZE */
- if (blksz < 0)
- blksz = -blksz;
- /* default to blksz = ~0.25s */
- if (blksz < 16)
- blksz = (ss * c->speed) >> 2;
- if (blksz > CHN_2NDBUFMAXSIZE / 2)
- blksz = CHN_2NDBUFMAXSIZE / 2;
- if (blkcnt < 2)
- blkcnt = 2;
-
- if (blkcnt * blksz > CHN_2NDBUFMAXSIZE)
- blkcnt = CHN_2NDBUFMAXSIZE / blksz;
+ }
+
bufsz = blkcnt * blksz;
s = spltty();
+
if (bs->buf != NULL)
free(bs->buf, M_DEVBUF);
bs->buf = malloc(bufsz, M_DEVBUF, M_WAITOK);
if (bs->buf == NULL) {
splx(s);
- DEB(printf("chn_setblocksize: out of memory."));
+ DEB(printf("chn_setblocksize: out of memory\n"));
return ENOSPC;
}
+
bs->bufsize = bufsz;
- bs->rl = bs->rp = bs->fp = 0;
- bs->fl = bs->bufsize;
- buf_clear(bs, bs->fmt, bs->bufsize);
bs->blkcnt = blkcnt;
bs->blksz = blksz;
+
+ /* adjust for different hw format/speed */
+ irqhz = (bs->bps * bs->spd) / bs->blksz;
+
+ b->blksz = (b->bps * b->spd) / irqhz;
+
+ /* round down to 2^x */
+ blksz = 32;
+ while (blksz <= b->blksz)
+ blksz <<= 1;
+ blksz >>= 1;
+
+ /* round down to fit hw buffer size */
RANGE(blksz, 16, b->bufsize / 2);
+
b->blksz = CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz);
+
+ chn_resetbuf(c);
splx(s);
return 0;
@@ -1439,7 +1337,7 @@ chn_buildfeeder(pcm_channel *c)
desc.flags = 0;
DEB(printf("find feeder type %d, ", type));
fc = feeder_getclass(&desc);
- DEB(printf("got %p\n", f));
+ DEB(printf("got %p\n", fc));
if (fc == NULL)
return EINVAL;
dst = fc->desc->in;
@@ -1454,7 +1352,7 @@ chn_buildfeeder(pcm_channel *c)
if (chn_addfeeder(c, fc, fc->desc))
return EINVAL;
src = fc->desc->out;
- DEB(printf("added feeder %p, output %x\n", f, src));
+ DEB(printf("added feeder %p, output %x\n", fc, src));
dst = 0;
flags &= ~(1 << type);
}
OpenPOWER on IntegriCloud