summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
committerariff <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
commit7b36f6d96b710844a6d2abd90a94bc169f72a5d1 (patch)
treebe8f05cc6b2e41975dd084107c4db8978e7c9734 /sys/dev/sound
parent70fe7b890e66532cef253d3bad928093268a24fd (diff)
downloadFreeBSD-src-7b36f6d96b710844a6d2abd90a94bc169f72a5d1.zip
FreeBSD-src-7b36f6d96b710844a6d2abd90a94bc169f72a5d1.tar.gz
Welcome to Once-a-year Sound Mega-Commit. Enjoy numerous updates and fixes
in every sense. General ------- - Multichannel safe, endian safe, format safe * Large part of critical pcm filters such as vchan.c, feeder_rate.c, feeder_volume.c, feeder_fmt.c and feeder.c has been rewritten so that using them does not cause the pcm data to be converted to 16bit little endian. * Macrosses for accessing pcm data safely are defined within sound.h in the form of PCM_READ_* / PCM_WRITE_* * Currently, most of them are probably limited for mono/stereo handling, but the future addition of true multichannel will be much easier. - Low latency operation * Well, this require lot more works to do not just within sound driver, but we're heading towards right direction. Buffer/block sizing within channel.c is rewritten to calculate precise allocation for various combination of sample/data/rate size. As a result, applying correct SNDCTL_DSP_POLICY value will achive expected latency behaviour simmilar to what commercial 4front driver do. * Signal handling fix. ctrl+c of "cat /dev/zero > /dev/dsp" does not result long delay. * Eliminate sound truncation if the sound data is too small. DIY: 1) Download / extract http://people.freebsd.org/~ariff/lowlatency/shortfiles.tar.gz 2) Do a comparison between "cat state*.au > /dev/dsp" and "for x in state*.au ; do cat $x > /dev/dsp ; done" - there should be no "perceivable" differences. Double close for PR kern/31445. CAVEAT: Low latency come with (unbearable) price especially for poorly written applications. Applications that trying to act smarter by requesting (wrong) blocksize/blockcount will suffer the most. Fixup samples/patches can be found at: http://people.freebsd.org/~ariff/ports/ - Switch minimum/maximum sampling rate limit to "1" and "2016000" (48k * 42) due to closer compatibility with 4front driver. Discussed with: marcus@ (long time ago?) - All driver specific sysctls in the form of "hw.snd.pcm%d.*" have been moved to their own dev sysctl nodes, notably: hw.snd.pcm%d.vchans -> dev.pcm.%d.vchans Bump __FreeBSD_version. Driver specific --------------- - Ditto for sysctls. - snd_atiixp, snd_es137x, snd_via8233, snd_hda * Numerous cleanups and fixes. * _EXPERIMENTAL_ polling mode support using simple callout_* mechanisme. This was intended for pure debugging and latency measurement, but proven good enough in few unexpected and rare cases (such as problematic shared IRQ with GIANT devices - USB). Polling can be enabled/disabled through dev.pcm.0.polling. Disabled by default. - snd_ich * Fix possible overflow during speed calibration. Delay final initialization (pcm_setstatus) after calibration finished. PR: kern/100169 Tested by: Kevin Overman <oberman@es.net> * Inverted EAPD for few Nec VersaPro. PR: kern/104715 Submitted by: KAWATA Masahiko <kawata@mta.biglobe.ne.jp> Thanks to various people, notably Joel Dahl, Yuriy Tsibizov, Kevin Oberman, those at #freebsd-azalia @ freenode and others for testing. Joel Dahl will do the manpage update.
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/pci/atiixp.c552
-rw-r--r--sys/dev/sound/pci/cmi.c6
-rw-r--r--sys/dev/sound/pci/es137x.c997
-rw-r--r--sys/dev/sound/pci/hda/hda_reg.h48
-rw-r--r--sys/dev/sound/pci/hda/hdac.c627
-rw-r--r--sys/dev/sound/pci/hda/hdac_private.h11
-rw-r--r--sys/dev/sound/pci/ich.c331
-rw-r--r--sys/dev/sound/pci/via8233.c835
-rw-r--r--sys/dev/sound/pcm/ac97.c104
-rw-r--r--sys/dev/sound/pcm/ac97.h2
-rw-r--r--sys/dev/sound/pcm/buffer.c86
-rw-r--r--sys/dev/sound/pcm/buffer.h3
-rw-r--r--sys/dev/sound/pcm/channel.c1032
-rw-r--r--sys/dev/sound/pcm/channel.h40
-rw-r--r--sys/dev/sound/pcm/dsp.c33
-rw-r--r--sys/dev/sound/pcm/fake.c3
-rw-r--r--sys/dev/sound/pcm/feeder.c584
-rw-r--r--sys/dev/sound/pcm/feeder.h29
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c826
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c866
-rw-r--r--sys/dev/sound/pcm/feeder_volume.c211
-rw-r--r--sys/dev/sound/pcm/mixer.c8
-rw-r--r--sys/dev/sound/pcm/sndstat.c21
-rw-r--r--sys/dev/sound/pcm/sound.c88
-rw-r--r--sys/dev/sound/pcm/sound.h287
-rw-r--r--sys/dev/sound/pcm/vchan.c589
-rw-r--r--sys/dev/sound/usb/uaudio.c2
27 files changed, 5579 insertions, 2642 deletions
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index 33f0f07..bd7249c 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -66,17 +66,11 @@
SND_DECLARE_FILE("$FreeBSD$");
#define ATI_IXP_DMA_RETRY_MAX 100
-#define ATI_IXP_DMA_CHSEGS_DEFAULT 2
#define ATI_IXP_BUFSZ_MIN 4096
#define ATI_IXP_BUFSZ_MAX 65536
#define ATI_IXP_BUFSZ_DEFAULT 16384
-#ifdef ATI_IXP_DEBUG_VERBOSE
-#undef ATI_IXP_DEBUG
-#define ATI_IXP_DEBUG 1
-#endif
-
struct atiixp_dma_op {
volatile uint32_t addr;
volatile uint16_t status;
@@ -92,11 +86,9 @@ struct atiixp_chinfo {
struct atiixp_info *parent;
struct atiixp_dma_op *sgd_table;
bus_addr_t sgd_addr;
- uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit;
- uint32_t dma_segs, dma_blksz;
-#ifdef ATI_IXP_DEBUG
- uint32_t dma_ptr, dma_prevptr;
-#endif
+ uint32_t enable_bit, flush_bit, linkptr_bit, dt_cur_bit;
+ uint32_t blksz, blkcnt;
+ uint32_t ptr, prevptr;
uint32_t fmt;
int caps_32bit, dir, active;
};
@@ -123,10 +115,12 @@ struct atiixp_info {
uint32_t bufsz;
uint32_t codec_not_ready_bits, codec_idx, codec_found;
- uint32_t dma_segs;
+ uint32_t blkcnt;
int registered_channels;
struct mtx *lock;
+ struct callout poll_timer;
+ int poll_ticks, polling;
};
#define atiixp_rd(_sc, _reg) \
@@ -226,7 +220,7 @@ atiixp_enable_interrupts(struct atiixp_info *sc)
*/
#if 1
value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN |
- ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
+ ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
#else
value |= ATI_REG_IER_IN_XRUN_EN;
value |= ATI_REG_IER_OUT_XRUN_EN;
@@ -281,8 +275,7 @@ atiixp_reset_aclink(struct atiixp_info *sc)
/* check if the ac-link is working; reset device otherwise */
timeout = 10;
value = atiixp_rd(sc, ATI_REG_CMD);
- while (!(value & ATI_REG_CMD_ACLINK_ACTIVE)
- && --timeout) {
+ while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) && --timeout) {
#if 0
device_printf(sc->dev, "not up; resetting aclink hardware\n");
#endif
@@ -358,7 +351,7 @@ atiixp_waitready_codec(struct atiixp_info *sc)
do {
if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
- ATI_REG_PHYS_OUT_ADDR_EN) == 0)
+ ATI_REG_PHYS_OUT_ADDR_EN) == 0)
return (0);
DELAY(1);
} while (--timeout);
@@ -377,8 +370,7 @@ atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
return (-1);
data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
- ATI_REG_PHYS_OUT_ADDR_EN |
- ATI_REG_PHYS_OUT_RW | sc->codec_idx;
+ ATI_REG_PHYS_OUT_ADDR_EN | ATI_REG_PHYS_OUT_RW | sc->codec_idx;
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
@@ -408,8 +400,8 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
return (-1);
data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
- (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
- ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
+ (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
@@ -417,8 +409,8 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
}
static kobj_method_t atiixp_ac97_methods[] = {
- KOBJMETHOD(ac97_read, atiixp_rdcd),
- KOBJMETHOD(ac97_write, atiixp_wrcd),
+ KOBJMETHOD(ac97_read, atiixp_rdcd),
+ KOBJMETHOD(ac97_write, atiixp_wrcd),
{ 0, 0 }
};
AC97_DECLARE(atiixp_ac97);
@@ -441,15 +433,15 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR;
ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN;
ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH;
- ch->dma_dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
+ ch->dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
/* Native 32bit playback working properly */
ch->caps_32bit = 1;
} else {
ch = &sc->rch;
ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
- ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
+ ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
ch->flush_bit = ATI_REG_FIFO_IN_FLUSH;
- ch->dma_dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
+ ch->dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
/* XXX Native 32bit recording appear to be broken */
ch->caps_32bit = 1;
}
@@ -458,8 +450,8 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->parent = sc;
ch->channel = c;
ch->dir = dir;
- ch->dma_segs = sc->dma_segs;
- ch->dma_blksz = sc->bufsz / sc->dma_segs;
+ ch->blkcnt = sc->blkcnt;
+ ch->blksz = sc->bufsz / ch->blkcnt;
atiixp_unlock(sc);
@@ -468,9 +460,9 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
atiixp_lock(sc);
num = sc->registered_channels++;
- ch->sgd_table = &sc->sgd_table[num * ch->dma_segs];
- ch->sgd_addr = sc->sgd_addr +
- (num * ch->dma_segs * sizeof(struct atiixp_dma_op));
+ ch->sgd_table = &sc->sgd_table[num * ch->blkcnt];
+ ch->sgd_addr = sc->sgd_addr + (num * ch->blkcnt *
+ sizeof(struct atiixp_dma_op));
atiixp_disable_dma(ch);
atiixp_unlock(sc);
@@ -496,7 +488,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
/* We do not have support for more than 2 channels, _yet_. */
value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
- ATI_REG_OUT_DMA_SLOT_BIT(4);
+ ATI_REG_OUT_DMA_SLOT_BIT(4);
value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
value = atiixp_rd(sc, ATI_REG_CMD);
@@ -527,84 +519,44 @@ atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
struct atiixp_chinfo *ch = data;
struct atiixp_info *sc = ch->parent;
- /* XXX Force static blocksize */
- sndbuf_resize(ch->buffer, ch->dma_segs, ch->dma_blksz);
+ if ((blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
+ blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
- if (ch->dma_blksz != sndbuf_getblksz(ch->buffer)) {
- device_printf(sc->dev,
- "%s: WARNING - dma_blksz=%u != blksz=%u !!!\n",
- __func__, ch->dma_blksz, sndbuf_getblksz(ch->buffer));
- ch->dma_blksz = sndbuf_getblksz(ch->buffer);
- }
+ if ((sndbuf_getblksz(ch->buffer) != blksz ||
+ sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
+ sndbuf_resize(ch->buffer, ch->blkcnt, blksz) != 0)
+ device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
+ __func__, blksz, ch->blkcnt);
+
+ ch->blksz = sndbuf_getblksz(ch->buffer);
- return (ch->dma_blksz);
+ return (ch->blksz);
}
static void
atiixp_buildsgdt(struct atiixp_chinfo *ch)
{
- uint32_t addr;
+ struct atiixp_info *sc = ch->parent;
+ uint32_t addr, blksz, blkcnt;
int i;
addr = sndbuf_getbufaddr(ch->buffer);
- for (i = 0; i < ch->dma_segs; i++) {
- ch->sgd_table[i].addr = htole32(addr + (i * ch->dma_blksz));
- ch->sgd_table[i].status = htole16(0);
- ch->sgd_table[i].size = htole16(ch->dma_blksz >> 2);
- ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
- (((i + 1) % ch->dma_segs) *
- sizeof(struct atiixp_dma_op)));
+ if (sc->polling != 0) {
+ blksz = ch->blksz * ch->blkcnt;
+ blkcnt = 1;
+ } else {
+ blksz = ch->blksz;
+ blkcnt = ch->blkcnt;
}
-#ifdef ATI_IXP_DEBUG
- ch->dma_ptr = 0;
- ch->dma_prevptr = 0;
-#endif
-}
-
-static int
-atiixp_chan_trigger(kobj_t obj, void *data, int go)
-{
- struct atiixp_chinfo *ch = data;
- struct atiixp_info *sc = ch->parent;
- uint32_t value;
-
- atiixp_lock(sc);
-
- switch (go) {
- case PCMTRIG_START:
- atiixp_flush_dma(ch);
- atiixp_buildsgdt(ch);
- atiixp_wr(sc, ch->linkptr_bit, 0);
- atiixp_enable_dma(ch);
- atiixp_wr(sc, ch->linkptr_bit,
- (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
- break;
- case PCMTRIG_STOP:
- case PCMTRIG_ABORT:
- atiixp_disable_dma(ch);
- atiixp_flush_dma(ch);
- break;
- default:
- atiixp_unlock(sc);
- return (0);
- break;
+ for (i = 0; i < blkcnt; i++) {
+ ch->sgd_table[i].addr = htole32(addr + (i * blksz));
+ ch->sgd_table[i].status = htole16(0);
+ ch->sgd_table[i].size = htole16(blksz >> 2);
+ ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
+ (((i + 1) % blkcnt) * sizeof(struct atiixp_dma_op)));
}
-
- /* Update bus busy status */
- value = atiixp_rd(sc, ATI_REG_IER);
- if (atiixp_rd(sc, ATI_REG_CMD) & (
- ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN |
- ATI_REG_CMD_SPDF_OUT_EN))
- value |= ATI_REG_IER_SET_BUS_BUSY;
- else
- value &= ~ATI_REG_IER_SET_BUS_BUSY;
- atiixp_wr(sc, ATI_REG_IER, value);
-
- atiixp_unlock(sc);
-
- return (0);
}
static __inline uint32_t
@@ -614,9 +566,9 @@ atiixp_dmapos(struct atiixp_chinfo *ch)
uint32_t reg, addr, sz, retry;
volatile uint32_t ptr;
- reg = ch->dma_dt_cur_bit;
+ reg = ch->dt_cur_bit;
addr = sndbuf_getbufaddr(ch->buffer);
- sz = ch->dma_segs * ch->dma_blksz;
+ sz = ch->blkcnt * ch->blksz;
retry = ATI_IXP_DMA_RETRY_MAX;
do {
@@ -625,37 +577,200 @@ atiixp_dmapos(struct atiixp_chinfo *ch)
continue;
ptr -= addr;
if (ptr < sz) {
+#if 0
#ifdef ATI_IXP_DEBUG
- if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) {
+ if ((ptr & ~(ch->blksz - 1)) != ch->ptr) {
uint32_t delta;
- delta = (sz + ptr - ch->dma_prevptr) % sz;
+ delta = (sz + ptr - ch->prevptr) % sz;
#ifndef ATI_IXP_DEBUG_VERBOSE
- if (delta < ch->dma_blksz)
+ if (delta < ch->blksz)
#endif
device_printf(sc->dev,
"PCMDIR_%s: incoherent DMA "
- "dma_prevptr=%u ptr=%u "
- "dma_ptr=%u dma_segs=%u "
- "[delta=%u != dma_blksz=%u] "
+ "prevptr=%u ptr=%u "
+ "ptr=%u blkcnt=%u "
+ "[delta=%u != blksz=%u] "
"(%s)\n",
(ch->dir == PCMDIR_PLAY) ?
"PLAY" : "REC",
- ch->dma_prevptr, ptr,
- ch->dma_ptr, ch->dma_segs,
- delta, ch->dma_blksz,
- (delta < ch->dma_blksz) ?
+ ch->prevptr, ptr,
+ ch->ptr, ch->blkcnt,
+ delta, ch->blksz,
+ (delta < ch->blksz) ?
"OVERLAPPED!" : "Ok");
- ch->dma_ptr = ptr & ~(ch->dma_blksz - 1);
+ ch->ptr = ptr & ~(ch->blksz - 1);
}
- ch->dma_prevptr = ptr;
+ ch->prevptr = ptr;
+#endif
#endif
return (ptr);
}
} while (--retry);
device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
- (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
+ (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
+
+ return (0);
+}
+
+static __inline int
+atiixp_poll_channel(struct atiixp_chinfo *ch)
+{
+ uint32_t sz, delta;
+ volatile uint32_t ptr;
+
+ if (ch->active == 0)
+ return (0);
+
+ sz = ch->blksz * ch->blkcnt;
+ ptr = atiixp_dmapos(ch);
+ ch->ptr = ptr;
+ ptr %= sz;
+ ptr &= ~(ch->blksz - 1);
+ delta = (sz + ptr - ch->prevptr) % sz;
+
+ if (delta < ch->blksz)
+ return (0);
+
+ ch->prevptr = ptr;
+
+ return (1);
+}
+
+#define atiixp_chan_active(sc) ((sc)->pch.active + (sc)->rch.active)
+
+static void
+atiixp_poll_callback(void *arg)
+{
+ struct atiixp_info *sc = arg;
+ uint32_t trigger = 0;
+
+ if (sc == NULL)
+ return;
+
+ atiixp_lock(sc);
+ if (sc->polling == 0 || atiixp_chan_active(sc) == 0) {
+ atiixp_unlock(sc);
+ return;
+ }
+
+ trigger |= (atiixp_poll_channel(&sc->pch) != 0) ? 1 : 0;
+ trigger |= (atiixp_poll_channel(&sc->rch) != 0) ? 2 : 0;
+
+ /* XXX */
+ callout_reset(&sc->poll_timer, 1/*sc->poll_ticks*/,
+ atiixp_poll_callback, sc);
+
+ atiixp_unlock(sc);
+
+ if (trigger & 1)
+ chn_intr(sc->pch.channel);
+ if (trigger & 2)
+ chn_intr(sc->rch.channel);
+}
+
+static int
+atiixp_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct atiixp_chinfo *ch = data;
+ struct atiixp_info *sc = ch->parent;
+ uint32_t value;
+ int pollticks;
+
+ atiixp_lock(sc);
+
+ switch (go) {
+ case PCMTRIG_START:
+ atiixp_flush_dma(ch);
+ atiixp_buildsgdt(ch);
+ atiixp_wr(sc, ch->linkptr_bit, 0);
+ atiixp_enable_dma(ch);
+ atiixp_wr(sc, ch->linkptr_bit,
+ (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
+ if (sc->polling != 0) {
+ ch->ptr = 0;
+ ch->prevptr = 0;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->buffer) *
+ sndbuf_getspd(ch->buffer));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1)
+ pollticks = 1;
+ if (atiixp_chan_active(sc) == 0 ||
+ pollticks < sc->poll_ticks) {
+ if (bootverbose) {
+ if (atiixp_chan_active(sc) == 0)
+ device_printf(sc->dev,
+ "%s: pollticks=%d\n",
+ __func__, pollticks);
+ else
+ device_printf(sc->dev,
+ "%s: pollticks %d -> %d\n",
+ __func__, sc->poll_ticks,
+ pollticks);
+ }
+ sc->poll_ticks = pollticks;
+ callout_reset(&sc->poll_timer, 1,
+ atiixp_poll_callback, sc);
+ }
+ }
+ ch->active = 1;
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ atiixp_disable_dma(ch);
+ atiixp_flush_dma(ch);
+ ch->active = 0;
+ if (sc->polling != 0) {
+ if (atiixp_chan_active(sc) == 0) {
+ callout_stop(&sc->poll_timer);
+ sc->poll_ticks = 1;
+ } else {
+ if (sc->pch.active != 0)
+ ch = &sc->pch;
+ else
+ ch = &sc->rch;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->buffer) *
+ sndbuf_getspd(ch->buffer));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1)
+ pollticks = 1;
+ if (pollticks > sc->poll_ticks) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "%s: pollticks %d -> %d\n",
+ __func__, sc->poll_ticks,
+ pollticks);
+ sc->poll_ticks = pollticks;
+ callout_reset(&sc->poll_timer,
+ 1, atiixp_poll_callback,
+ sc);
+ }
+ }
+ }
+ break;
+ default:
+ atiixp_unlock(sc);
+ return (0);
+ break;
+ }
+
+ /* Update bus busy status */
+ value = atiixp_rd(sc, ATI_REG_IER);
+ if (atiixp_rd(sc, ATI_REG_CMD) & (ATI_REG_CMD_SEND_EN |
+ ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN))
+ value |= ATI_REG_IER_SET_BUS_BUSY;
+ else
+ value &= ~ATI_REG_IER_SET_BUS_BUSY;
+ atiixp_wr(sc, ATI_REG_IER, value);
+
+ atiixp_unlock(sc);
return (0);
}
@@ -668,7 +783,10 @@ atiixp_chan_getptr(kobj_t obj, void *data)
uint32_t ptr;
atiixp_lock(sc);
- ptr = atiixp_dmapos(ch);
+ if (sc->polling != 0)
+ ptr = ch->ptr;
+ else
+ ptr = atiixp_dmapos(ch);
atiixp_unlock(sc);
return (ptr);
@@ -703,10 +821,14 @@ static void
atiixp_intr(void *p)
{
struct atiixp_info *sc = p;
- struct atiixp_chinfo *ch;
uint32_t status, enable, detected_codecs;
+ uint32_t trigger = 0;
atiixp_lock(sc);
+ if (sc->polling != 0) {
+ atiixp_unlock(sc);
+ return;
+ }
status = atiixp_rd(sc, ATI_REG_ISR);
if (status == 0) {
@@ -714,26 +836,10 @@ atiixp_intr(void *p)
return;
}
- if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) {
- ch = &sc->rch;
-#ifdef ATI_IXP_DEBUG
- ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
- (ch->dma_segs * ch->dma_blksz);
-#endif
- atiixp_unlock(sc);
- chn_intr(ch->channel);
- atiixp_lock(sc);
- }
- if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) {
- ch = &sc->pch;
-#ifdef ATI_IXP_DEBUG
- ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
- (ch->dma_segs * ch->dma_blksz);
-#endif
- atiixp_unlock(sc);
- chn_intr(ch->channel);
- atiixp_lock(sc);
- }
+ if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.active != 0)
+ trigger |= 1;
+ if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.active != 0)
+ trigger |= 2;
#if 0
if (status & ATI_REG_ISR_IN_XRUN) {
@@ -760,6 +866,11 @@ atiixp_intr(void *p)
/* acknowledge */
atiixp_wr(sc, ATI_REG_ISR, status);
atiixp_unlock(sc);
+
+ if (trigger & 1)
+ chn_intr(sc->pch.channel);
+ if (trigger & 2)
+ chn_intr(sc->rch.channel);
}
static void
@@ -782,7 +893,7 @@ atiixp_chip_pre_init(struct atiixp_info *sc)
/* clear all DMA enables (preserving rest of settings) */
value = atiixp_rd(sc, ATI_REG_CMD);
value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN |
- ATI_REG_CMD_SPDF_OUT_EN );
+ ATI_REG_CMD_SPDF_OUT_EN );
atiixp_wr(sc, ATI_REG_CMD, value);
/* reset aclink */
@@ -796,12 +907,54 @@ atiixp_chip_pre_init(struct atiixp_info *sc)
atiixp_unlock(sc);
}
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
+{
+ struct atiixp_info *sc;
+ device_t dev;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ sc = pcm_getdevinfo(dev);
+ if (sc == NULL)
+ return (EINVAL);
+ atiixp_lock(sc);
+ val = sc->polling;
+ atiixp_unlock(sc);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return (err);
+ if (val < 0 || val > 1)
+ return (EINVAL);
+
+ atiixp_lock(sc);
+ if (val != sc->polling) {
+ if (atiixp_chan_active(sc) != 0)
+ err = EBUSY;
+ else if (val == 0) {
+ atiixp_enable_interrupts(sc);
+ sc->polling = 0;
+ DELAY(1000);
+ } else {
+ atiixp_disable_interrupts(sc);
+ sc->polling = 1;
+ DELAY(1000);
+ }
+ }
+ atiixp_unlock(sc);
+
+ return (err);
+}
+#endif
+
static void
atiixp_chip_post_init(void *arg)
{
struct atiixp_info *sc = (struct atiixp_info *)arg;
uint32_t subdev;
- int i, timeout, found;
+ int i, timeout, found, polling;
char status[SND_STATUSLEN];
atiixp_lock(sc);
@@ -811,6 +964,9 @@ atiixp_chip_post_init(void *arg)
sc->delayed_attach.ich_func = NULL;
}
+ polling = sc->polling;
+ sc->polling = 0;
+
/* wait for the interrupts to happen */
timeout = 100;
do {
@@ -819,6 +975,7 @@ atiixp_chip_post_init(void *arg)
break;
} while (--timeout);
+ sc->polling = polling;
atiixp_disable_interrupts(sc);
if (timeout == 0) {
@@ -835,22 +992,19 @@ atiixp_chip_post_init(void *arg)
* ATI IXP can have upto 3 codecs, but single codec should be
* suffice for now.
*/
- if (!(sc->codec_not_ready_bits &
- ATI_REG_ISR_CODEC0_NOT_READY)) {
+ if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) {
/* codec 0 present */
sc->codec_found++;
sc->codec_idx = 0;
found++;
}
- if (!(sc->codec_not_ready_bits &
- ATI_REG_ISR_CODEC1_NOT_READY)) {
+ if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) {
/* codec 1 present */
sc->codec_found++;
}
- if (!(sc->codec_not_ready_bits &
- ATI_REG_ISR_CODEC2_NOT_READY)) {
+ if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) {
/* codec 2 present */
sc->codec_found++;
}
@@ -865,10 +1019,12 @@ atiixp_chip_post_init(void *arg)
if (sc->codec == NULL)
goto postinitbad;
- subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev);
+ subdev = (pci_get_subdevice(sc->dev) << 16) |
+ pci_get_subvendor(sc->dev);
switch (subdev) {
case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */
- ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
+ ac97_setflags(sc->codec, ac97_getflags(sc->codec) |
+ AC97_F_EAPD_INV);
break;
default:
break;
@@ -884,14 +1040,22 @@ atiixp_chip_post_init(void *arg)
for (i = 0; i < ATI_IXP_NRCHAN; i++)
pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
+#ifdef SND_DYNSYSCTL
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
+ "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
+ sysctl_atiixp_polling, "I", "Enable polling mode");
+#endif
+
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
- rman_get_start(sc->reg), rman_get_start(sc->irq),
- PCM_KLDSTRING(snd_atiixp));
+ rman_get_start(sc->reg), rman_get_start(sc->irq),
+ PCM_KLDSTRING(snd_atiixp));
pcm_setstatus(sc->dev, status);
atiixp_lock(sc);
- atiixp_enable_interrupts(sc);
+ if (sc->polling == 0)
+ atiixp_enable_interrupts(sc);
atiixp_unlock(sc);
return;
@@ -949,7 +1113,7 @@ atiixp_pci_probe(device_t dev)
devid = pci_get_device(dev);
for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
if (vendor == atiixp_hw[i].vendor &&
- devid == atiixp_hw[i].devid) {
+ devid == atiixp_hw[i].devid) {
device_set_desc(dev, atiixp_hw[i].desc);
return (BUS_PROBE_DEFAULT);
}
@@ -971,18 +1135,23 @@ atiixp_pci_attach(device_t dev)
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
sc->dev = dev;
- /*
- * Default DMA segments per playback / recording channel
- */
- sc->dma_segs = ATI_IXP_DMA_CHSEGS_DEFAULT;
+
+ callout_init(&sc->poll_timer, CALLOUT_MPSAFE);
+ sc->poll_ticks = 1;
+
+ if (resource_int_value(device_get_name(sc->dev),
+ device_get_unit(sc->dev), "polling", &i) == 0 && i != 0)
+ sc->polling = 1;
+ else
+ sc->polling = 0;
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_enable_busmaster(dev);
sc->regid = PCIR_BAR(0);
sc->regtype = SYS_RES_MEMORY;
- sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid,
- RF_ACTIVE);
+ sc->reg = bus_alloc_resource_any(dev, sc->regtype,
+ &sc->regid, RF_ACTIVE);
if (!sc->reg) {
device_printf(dev, "unable to allocate register space\n");
@@ -993,14 +1162,13 @@ atiixp_pci_attach(device_t dev)
sc->sh = rman_get_bushandle(sc->reg);
sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN,
- ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
+ ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
sc->irqid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
- RF_ACTIVE | RF_SHAREABLE);
- if (!sc->irq ||
- snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
- atiixp_intr, sc, &sc->ih)) {
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
+ atiixp_intr, sc, &sc->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@@ -1008,27 +1176,20 @@ atiixp_pci_attach(device_t dev)
/*
* Let the user choose the best DMA segments.
*/
- if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "dma_segs",
- &i) == 0) {
- if (i < ATI_IXP_DMA_CHSEGS_MIN)
- i = ATI_IXP_DMA_CHSEGS_MIN;
- if (i > ATI_IXP_DMA_CHSEGS_MAX)
- i = ATI_IXP_DMA_CHSEGS_MAX;
- sc->dma_segs = i;
- }
-
- /*
- * round the value to the nearest ^2
- */
- i = 0;
- while (sc->dma_segs >> i)
- i++;
- sc->dma_segs = 1 << (i - 1);
- if (sc->dma_segs < ATI_IXP_DMA_CHSEGS_MIN)
- sc->dma_segs = ATI_IXP_DMA_CHSEGS_MIN;
- else if (sc->dma_segs > ATI_IXP_DMA_CHSEGS_MAX)
- sc->dma_segs = ATI_IXP_DMA_CHSEGS_MAX;
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
+ sc->blkcnt = sc->bufsz / i;
+ i = 0;
+ while (sc->blkcnt >> i)
+ i++;
+ sc->blkcnt = 1 << (i - 1);
+ if (sc->blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
+ sc->blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
+ else if (sc->blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
+ sc->blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
+
+ } else
+ sc->blkcnt = ATI_IXP_DMA_CHSEGS;
/*
* DMA tag for scatter-gather buffers and link pointers
@@ -1048,8 +1209,8 @@ atiixp_pci_attach(device_t dev)
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/sc->dma_segs * ATI_IXP_NCHANS *
- sizeof(struct atiixp_dma_op),
+ /*maxsize*/sc->blkcnt * ATI_IXP_NCHANS *
+ sizeof(struct atiixp_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &sc->sgd_dmat) != 0) {
@@ -1058,13 +1219,12 @@ atiixp_pci_attach(device_t dev)
}
if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
- BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
+ BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
goto bad;
if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
- sc->dma_segs * ATI_IXP_NCHANS *
- sizeof(struct atiixp_dma_op),
- atiixp_dma_cb, sc, 0))
+ sc->blkcnt * ATI_IXP_NCHANS * sizeof(struct atiixp_dma_op),
+ atiixp_dma_cb, sc, 0))
goto bad;
@@ -1073,7 +1233,7 @@ atiixp_pci_attach(device_t dev)
sc->delayed_attach.ich_func = atiixp_chip_post_init;
sc->delayed_attach.ich_arg = sc;
if (cold == 0 ||
- config_intrhook_establish(&sc->delayed_attach) != 0) {
+ config_intrhook_establish(&sc->delayed_attach) != 0) {
sc->delayed_attach.ich_func = NULL;
atiixp_chip_post_init(sc);
}
@@ -1116,15 +1276,12 @@ atiixp_pci_suspend(device_t dev)
/* quickly disable interrupts and save channels active state */
atiixp_lock(sc);
atiixp_disable_interrupts(sc);
- value = atiixp_rd(sc, ATI_REG_CMD);
- sc->pch.active = (value & ATI_REG_CMD_SEND_EN) ? 1 : 0;
- sc->rch.active = (value & ATI_REG_CMD_RECEIVE_EN) ? 1 : 0;
atiixp_unlock(sc);
/* stop everything */
- if (sc->pch.channel && sc->pch.active)
+ if (sc->pch.active != 0)
atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP);
- if (sc->rch.channel && sc->rch.active)
+ if (sc->rch.active != 0)
atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP);
/* power down aclink and pci bus */
@@ -1161,22 +1318,23 @@ atiixp_pci_resume(device_t dev)
* Resume channel activities. Reset channel format regardless
* of its previous state.
*/
- if (sc->pch.channel) {
- if (sc->pch.fmt)
+ if (sc->pch.channel != NULL) {
+ if (sc->pch.fmt != 0)
atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt);
- if (sc->pch.active)
+ if (sc->pch.active != 0)
atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START);
}
- if (sc->rch.channel) {
- if (sc->rch.fmt)
+ if (sc->rch.channel != NULL) {
+ if (sc->rch.fmt != 0)
atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt);
- if (sc->rch.active)
+ if (sc->rch.active != 0)
atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START);
}
/* enable interrupts */
atiixp_lock(sc);
- atiixp_enable_interrupts(sc);
+ if (sc->polling == 0)
+ atiixp_enable_interrupts(sc);
atiixp_unlock(sc);
return (0);
diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c
index 131650d..83d9cad 100644
--- a/sys/dev/sound/pci/cmi.c
+++ b/sys/dev/sound/pci/cmi.c
@@ -746,9 +746,9 @@ cmi_initsys(struct sc_info* sc)
to a device specific sysctl "dev.pcm.X.yyy" via
device_get_sysctl_*() as discussed on multimedia@ in msg-id
<861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_INT(snd_sysctl_tree(sc->dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)),
- OID_AUTO, "_spdif_enabled", CTLFLAG_RW,
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
+ OID_AUTO, "spdif_enabled", CTLFLAG_RW,
&sc->spdif_enabled, 0,
"enable SPDIF output at 44.1 kHz and above");
#endif /* SND_DYNSYSCTL */
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index 0851650..e5aa86e 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -105,7 +105,9 @@ struct es_chinfo {
struct snd_dbuf *buffer;
struct pcmchan_caps caps;
int dir, num, index;
- u_int32_t fmt, blksz, bufsz;
+ uint32_t fmt, blksz, blkcnt, bufsz;
+ uint32_t ptr, prevptr;
+ int active;
};
/*
@@ -181,7 +183,7 @@ struct es_info {
device_t dev;
int num;
- unsigned int bufsz;
+ unsigned int bufsz, blkcnt;
/* Contents of board's registers */
uint32_t ctrl;
@@ -189,6 +191,8 @@ struct es_info {
uint32_t escfg;
struct es_chinfo ch[ES_NCHANS];
struct mtx *lock;
+ struct callout poll_timer;
+ int poll_ticks, polling;
};
#define ES_LOCK(sc) snd_mtxlock((sc)->lock)
@@ -198,14 +202,15 @@ struct es_info {
/* prototypes */
static void es_intr(void *);
static uint32_t es1371_wait_src_ready(struct es_info *);
-static void es1371_src_write(struct es_info *, u_short, unsigned short);
-static u_int es1371_adc_rate(struct es_info *, u_int, int);
-static u_int es1371_dac_rate(struct es_info *, u_int, int);
+static void es1371_src_write(struct es_info *,
+ unsigned short, unsigned short);
+static unsigned int es1371_adc_rate(struct es_info *, unsigned int, int);
+static unsigned int es1371_dac_rate(struct es_info *, unsigned int, int);
static int es1371_init(struct es_info *);
static int es1370_init(struct es_info *);
-static int es1370_wrcodec(struct es_info *, u_char, u_char);
+static int es1370_wrcodec(struct es_info *, unsigned char, unsigned char);
-static u_int32_t es_fmt[] = {
+static uint32_t es_fmt[] = {
AFMT_U8,
AFMT_STEREO | AFMT_U8,
AFMT_S16_LE,
@@ -234,23 +239,23 @@ static const struct {
[SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 }
};
-static __inline u_int32_t
+static __inline uint32_t
es_rd(struct es_info *es, int regno, int size)
{
switch (size) {
case 1:
- return bus_space_read_1(es->st, es->sh, regno);
+ return (bus_space_read_1(es->st, es->sh, regno));
case 2:
- return bus_space_read_2(es->st, es->sh, regno);
+ return (bus_space_read_2(es->st, es->sh, regno));
case 4:
- return bus_space_read_4(es->st, es->sh, regno);
+ return (bus_space_read_4(es->st, es->sh, regno));
default:
- return 0xFFFFFFFF;
+ return (0xFFFFFFFF);
}
}
static __inline void
-es_wr(struct es_info *es, int regno, u_int32_t data, int size)
+es_wr(struct es_info *es, int regno, uint32_t data, int size)
{
switch (size) {
@@ -274,12 +279,14 @@ es1370_mixinit(struct snd_mixer *m)
{
struct es_info *es;
int i;
- u_int32_t v;
+ uint32_t v;
es = mix_getdevinfo(m);
v = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (mixtable[i].avail) v |= (1 << i);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (mixtable[i].avail)
+ v |= (1 << i);
+ }
/*
* Each DAC1/2 for ES1370 can be controlled independently
* DAC1 = controlled by synth
@@ -291,12 +298,14 @@ es1370_mixinit(struct snd_mixer *m)
v &= ~(1 << SOUND_MIXER_SYNTH);
mix_setdevs(m, v);
v = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (mixtable[i].recmask) v |= (1 << i);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (mixtable[i].recmask)
+ v |= (1 << i);
+ }
if (ES_SINGLE_PCM_MIX(es->escfg)) /* ditto */
v &= ~(1 << SOUND_MIXER_SYNTH);
mix_setrecdevs(m, v);
- return 0;
+ return (0);
}
static int
@@ -305,38 +314,38 @@ es1370_mixset(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
struct es_info *es;
int l, r, rl, rr, set_dac1;
- if (!mixtable[dev].avail) return -1;
+ if (!mixtable[dev].avail)
+ return (-1);
l = left;
- r = mixtable[dev].stereo? right : l;
- if (mixtable[dev].left == 0xf) {
- rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
- } else {
- rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
- }
+ r = (mixtable[dev].stereo) ? right : l;
+ if (mixtable[dev].left == 0xf)
+ rl = (l < 2) ? 0x80 : 7 - (l - 2) / 14;
+ else
+ rl = (l < 10) ? 0x80 : 15 - (l - 10) / 6;
es = mix_getdevinfo(m);
ES_LOCK(es);
if (dev == SOUND_MIXER_PCM && (ES_SINGLE_PCM_MIX(es->escfg)) &&
- ES_DAC1_ENABLED(es->escfg)) {
+ ES_DAC1_ENABLED(es->escfg))
set_dac1 = 1;
- } else {
+ else
set_dac1 = 0;
- }
if (mixtable[dev].stereo) {
- rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
+ rr = (r < 10) ? 0x80 : 15 - (r - 10) / 6;
es1370_wrcodec(es, mixtable[dev].right, rr);
if (set_dac1 && mixtable[SOUND_MIXER_SYNTH].stereo)
- es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].right, rr);
+ es1370_wrcodec(es,
+ mixtable[SOUND_MIXER_SYNTH].right, rr);
}
es1370_wrcodec(es, mixtable[dev].left, rl);
if (set_dac1)
es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].left, rl);
ES_UNLOCK(es);
- return l | (r << 8);
+ return (l | (r << 8));
}
static int
-es1370_mixsetrecsrc(struct snd_mixer *m, u_int32_t src)
+es1370_mixsetrecsrc(struct snd_mixer *m, uint32_t src)
{
struct es_info *es;
int i, j = 0;
@@ -349,9 +358,8 @@ es1370_mixsetrecsrc(struct snd_mixer *m, u_int32_t src)
ES_LOCK(es);
if ((src & (1 << SOUND_MIXER_PCM)) && ES_SINGLE_PCM_MIX(es->escfg) &&
- ES_DAC1_ENABLED(es->escfg)) {
+ ES_DAC1_ENABLED(es->escfg))
j |= mixtable[SOUND_MIXER_SYNTH].recmask;
- }
es1370_wrcodec(es, CODEC_LIMIX1, j & 0x55);
es1370_wrcodec(es, CODEC_RIMIX1, j & 0xaa);
es1370_wrcodec(es, CODEC_LIMIX2, (j >> 8) & 0x17);
@@ -360,13 +368,13 @@ es1370_mixsetrecsrc(struct snd_mixer *m, u_int32_t src)
es1370_wrcodec(es, CODEC_OMIX2, 0x3f);
ES_UNLOCK(es);
- return src;
+ return (src);
}
static kobj_method_t es1370_mixer_methods[] = {
- KOBJMETHOD(mixer_init, es1370_mixinit),
- KOBJMETHOD(mixer_set, es1370_mixset),
- KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc),
+ KOBJMETHOD(mixer_init, es1370_mixinit),
+ KOBJMETHOD(mixer_set, es1370_mixset),
+ KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc),
{ 0, 0 }
};
MIXER_DECLARE(es1370_mixer);
@@ -374,30 +382,31 @@ MIXER_DECLARE(es1370_mixer);
/* -------------------------------------------------------------------- */
static int
-es1370_wrcodec(struct es_info *es, u_char i, u_char data)
+es1370_wrcodec(struct es_info *es, unsigned char i, unsigned char data)
{
- u_int t;
+ unsigned int t;
ES_LOCK_ASSERT(es);
for (t = 0; t < 0x1000; t++) {
if ((es_rd(es, ES1370_REG_STATUS, 4) &
- STAT_CSTAT) == 0) {
+ STAT_CSTAT) == 0) {
es_wr(es, ES1370_REG_CODEC,
- ((u_short)i << CODEC_INDEX_SHIFT) | data, 2);
- return 0;
+ ((unsigned short)i << CODEC_INDEX_SHIFT) | data, 2);
+ return (0);
}
DELAY(1);
}
device_printf(es->dev, "%s: timed out\n", __func__);
- return -1;
+ return (-1);
}
/* -------------------------------------------------------------------- */
/* channel interface */
static void *
-eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
{
struct es_info *es = devinfo;
struct es_chinfo *ch;
@@ -408,26 +417,26 @@ eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c
if (dir == PCMDIR_PLAY) {
index = ES_GP(es->escfg);
es->escfg = ES_SET_GP(es->escfg, index + 1);
- if (index == 0) {
+ if (index == 0)
index = ES_DAC_FIRST(es->escfg);
- } else if (index == 1) {
+ else if (index == 1)
index = ES_DAC_SECOND(es->escfg);
- } else {
- device_printf(es->dev, "Invalid ES_GP index: %d\n", index);
+ else {
+ device_printf(es->dev,
+ "Invalid ES_GP index: %d\n", index);
ES_UNLOCK(es);
- return NULL;
+ return (NULL);
}
if (!(index == ES_DAC1 || index == ES_DAC2)) {
- device_printf(es->dev, "Unknown DAC: %d\n",
- index + 1);
+ device_printf(es->dev, "Unknown DAC: %d\n", index + 1);
ES_UNLOCK(es);
- return NULL;
+ return (NULL);
}
if (es->ch[index].channel != NULL) {
device_printf(es->dev, "DAC%d already initialized!\n",
- index + 1);
+ index + 1);
ES_UNLOCK(es);
- return NULL;
+ return (NULL);
}
} else
index = ES_ADC;
@@ -443,7 +452,7 @@ eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c
} else {
uint32_t fixed_rate = ES_FIXED_RATE(es->escfg);
if (!(fixed_rate < es_caps.minspeed ||
- fixed_rate > es_caps.maxspeed)) {
+ fixed_rate > es_caps.maxspeed)) {
ch->caps.maxspeed = fixed_rate;
ch->caps.minspeed = fixed_rate;
}
@@ -453,33 +462,42 @@ eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c
ch->channel = c;
ch->buffer = b;
ch->bufsz = es->bufsz;
- ch->blksz = ch->bufsz / 2;
+ ch->blkcnt = es->blkcnt;
+ ch->blksz = ch->bufsz / ch->blkcnt;
ch->dir = dir;
ES_UNLOCK(es);
if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0)
- return NULL;
+ return (NULL);
ES_LOCK(es);
if (dir == PCMDIR_PLAY) {
if (ch->index == ES_DAC1) {
- es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMEADR >> 8, 1);
- es_wr(es, ES1370_REG_DAC1_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
- es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
+ es_wr(es, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC1_FRAMEADR >> 8, 1);
+ es_wr(es, ES1370_REG_DAC1_FRAMEADR & 0xff,
+ sndbuf_getbufaddr(ch->buffer), 4);
+ es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
} else {
- es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8, 1);
- es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
- es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
+ es_wr(es, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMEADR >> 8, 1);
+ es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff,
+ sndbuf_getbufaddr(ch->buffer), 4);
+ es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
}
} else {
es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8, 1);
- es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
- es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
+ es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff,
+ sndbuf_getbufaddr(ch->buffer), 4);
+ es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
}
ES_UNLOCK(es);
- return ch;
+ return (ch);
}
static int
-eschan_setformat(kobj_t obj, void *data, u_int32_t format)
+eschan_setformat(kobj_t obj, void *data, uint32_t format)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
@@ -488,38 +506,44 @@ eschan_setformat(kobj_t obj, void *data, u_int32_t format)
if (ch->dir == PCMDIR_PLAY) {
if (ch->index == ES_DAC1) {
es->sctrl &= ~SCTRL_P1FMT;
- if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P1SEB;
- if (format & AFMT_STEREO) es->sctrl |= SCTRL_P1SMB;
+ if (format & AFMT_S16_LE)
+ es->sctrl |= SCTRL_P1SEB;
+ if (format & AFMT_STEREO)
+ es->sctrl |= SCTRL_P1SMB;
} else {
es->sctrl &= ~SCTRL_P2FMT;
- if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
- if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
+ if (format & AFMT_S16_LE)
+ es->sctrl |= SCTRL_P2SEB;
+ if (format & AFMT_STEREO)
+ es->sctrl |= SCTRL_P2SMB;
}
} else {
es->sctrl &= ~SCTRL_R1FMT;
- if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
- if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
+ if (format & AFMT_S16_LE)
+ es->sctrl |= SCTRL_R1SEB;
+ if (format & AFMT_STEREO)
+ es->sctrl |= SCTRL_R1SMB;
}
es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
ES_UNLOCK(es);
ch->fmt = format;
- return 0;
+ return (0);
}
static int
-eschan1370_setspeed(kobj_t obj, void *data, u_int32_t speed)
+eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
+ ES_LOCK(es);
/* Fixed rate , do nothing. */
if (ch->caps.minspeed == ch->caps.maxspeed)
- return ch->caps.maxspeed;
+ return (ch->caps.maxspeed);
if (speed < ch->caps.minspeed)
speed = ch->caps.minspeed;
if (speed > ch->caps.maxspeed)
speed = ch->caps.maxspeed;
- ES_LOCK(es);
if (ch->index == ES_DAC1) {
/*
* DAC1 does not support continuous rate settings.
@@ -546,11 +570,11 @@ eschan1370_setspeed(kobj_t obj, void *data, u_int32_t speed)
}
es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
ES_UNLOCK(es);
- return speed;
+ return (speed);
}
static int
-eschan1371_setspeed(kobj_t obj, void *data, u_int32_t speed)
+eschan1371_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
@@ -563,33 +587,107 @@ eschan1371_setspeed(kobj_t obj, void *data, u_int32_t speed)
else
i = es1371_adc_rate(es, speed, ch->index); /* record */
ES_UNLOCK(es);
- delta = (speed > i) ? speed - i : i - speed;
+ delta = (speed > i) ? (speed - i) : (i - speed);
if (delta < 2)
- return speed;
- return i;
+ return (speed);
+ return (i);
}
static int
-eschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+eschan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
{
- struct es_info *es;
struct es_chinfo *ch = data;
- uint32_t oblksz, obufsz;
- int error;
-
- oblksz = ch->blksz;
- obufsz = ch->bufsz;
- ch->blksz = blocksize;
- ch->bufsz = ch->blksz * 2;
- error = sndbuf_resize(ch->buffer, 2, ch->blksz);
- if (error != 0) {
- ch->blksz = oblksz;
- ch->bufsz = obufsz;
- es = ch->parent;
- device_printf(es->dev, "unable to set block size, blksz = %d, "
- "error = %d", blocksize, error);
+ struct es_info *es = ch->parent;
+
+ blksz &= ~0x3f;
+ if (blksz < 0x40)
+ blksz = 0x40;
+
+ if ((blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
+ blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
+
+ if ((sndbuf_getblksz(ch->buffer) != blksz ||
+ sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
+ sndbuf_resize(ch->buffer, ch->blkcnt, blksz) != 0)
+ device_printf(es->dev, "%s: failed blksz=%u blkcnt=%u\n",
+ __func__, blksz, ch->blkcnt);
+
+ ch->bufsz = sndbuf_getsize(ch->buffer);
+ ch->blksz = sndbuf_getblksz(ch->buffer);
+
+ return (ch->blksz);
+}
+
+#define es_chan_active(es) ((es)->ch[ES_DAC1].active + \
+ (es)->ch[ES_DAC2].active + \
+ (es)->ch[ES_ADC].active)
+
+static __inline int
+es_poll_channel(struct es_chinfo *ch)
+{
+ struct es_info *es;
+ uint32_t sz, delta;
+ uint32_t reg, ptr;
+
+ if (ch == NULL || ch->channel == NULL || ch->active == 0)
+ return (0);
+
+ es = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ if (ch->index == ES_DAC1)
+ reg = ES1370_REG_DAC1_FRAMECNT;
+ else
+ reg = ES1370_REG_DAC2_FRAMECNT;
+ } else
+ reg = ES1370_REG_ADC_FRAMECNT;
+ sz = ch->blksz * ch->blkcnt;
+ es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4);
+ ptr = es_rd(es, reg & 0x000000ff, 4) >> 16;
+ ptr <<= 2;
+ ch->ptr = ptr;
+ ptr %= sz;
+ ptr &= ~(ch->blksz - 1);
+ delta = (sz + ptr - ch->prevptr) % sz;
+
+ if (delta < ch->blksz)
+ return (0);
+
+ ch->prevptr = ptr;
+
+ return (1);
+}
+
+static void
+es_poll_callback(void *arg)
+{
+ struct es_info *es = arg;
+ uint32_t trigger = 0;
+ int i;
+
+ if (es == NULL)
+ return;
+
+ ES_LOCK(es);
+ if (es->polling == 0 || es_chan_active(es) == 0) {
+ ES_UNLOCK(es);
+ return;
+ }
+
+ for (i = 0; i < ES_NCHANS; i++) {
+ if (es_poll_channel(&es->ch[i]) != 0)
+ trigger |= 1 << i;
+ }
+
+ /* XXX */
+ callout_reset(&es->poll_timer, 1/*es->poll_ticks*/,
+ es_poll_callback, es);
+
+ ES_UNLOCK(es);
+
+ for (i = 0; i < ES_NCHANS; i++) {
+ if (trigger & (1 << i))
+ chn_intr(es->ch[i].channel);
}
- return ch->blksz;
}
static int
@@ -600,50 +698,94 @@ eschan_trigger(kobj_t obj, void *data, int go)
uint32_t cnt, b = 0;
if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
- return 0;
+ return (0);
+ ES_LOCK(es);
cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1;
if (ch->fmt & AFMT_16BIT)
b |= 0x02;
if (ch->fmt & AFMT_STEREO)
b |= 0x01;
- ES_LOCK(es);
if (ch->dir == PCMDIR_PLAY) {
if (go == PCMTRIG_START) {
if (ch->index == ES_DAC1) {
es->ctrl |= CTRL_DAC1_EN;
- es->sctrl &= ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD);
- es->sctrl |= SCTRL_P1INTEN | b;
+ es->sctrl &= ~(SCTRL_P1LOOPSEL |
+ SCTRL_P1PAUSE | SCTRL_P1SCTRLD);
+ if (es->polling == 0)
+ es->sctrl |= SCTRL_P1INTEN;
+ else
+ es->sctrl &= ~SCTRL_P1INTEN;
+ es->sctrl |= b;
es_wr(es, ES1370_REG_DAC1_SCOUNT, cnt, 4);
/* start at beginning of buffer */
- es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMECNT >> 8, 4);
- es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
+ es_wr(es, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC1_FRAMECNT >> 8, 4);
+ es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
} else {
es->ctrl |= CTRL_DAC2_EN;
- es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN);
- es->sctrl |= SCTRL_P2INTEN | (b << 2) |
- (((b & 2) ? : 1) << SCTRL_SH_P2ENDINC);
+ es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
+ SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
+ SCTRL_P2DACSEN);
+ if (es->polling == 0)
+ es->sctrl |= SCTRL_P2INTEN;
+ else
+ es->sctrl &= ~SCTRL_P2INTEN;
+ es->sctrl |= (b << 2) |
+ ((((b >> 1) & 1) + 1) << SCTRL_SH_P2ENDINC);
es_wr(es, ES1370_REG_DAC2_SCOUNT, cnt, 4);
/* start at beginning of buffer */
- es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8, 4);
- es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
+ es_wr(es, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMECNT >> 8, 4);
+ es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
}
- } else es->ctrl &= ~(ch->index == ES_DAC1 ? CTRL_DAC1_EN : CTRL_DAC2_EN);
+ } else
+ es->ctrl &= ~((ch->index == ES_DAC1) ?
+ CTRL_DAC1_EN : CTRL_DAC2_EN);
} else {
if (go == PCMTRIG_START) {
es->ctrl |= CTRL_ADC_EN;
es->sctrl &= ~SCTRL_R1LOOPSEL;
- es->sctrl |= SCTRL_R1INTEN | (b << 4);
+ if (es->polling == 0)
+ es->sctrl |= SCTRL_R1INTEN;
+ else
+ es->sctrl &= ~SCTRL_R1INTEN;
+ es->sctrl |= b << 4;
es_wr(es, ES1370_REG_ADC_SCOUNT, cnt, 4);
/* start at beginning of buffer */
- es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8, 4);
- es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
- } else es->ctrl &= ~CTRL_ADC_EN;
+ es_wr(es, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMECNT >> 8, 4);
+ es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (ch->bufsz >> 2) - 1, 4);
+ } else
+ es->ctrl &= ~CTRL_ADC_EN;
}
es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
+ if (go == PCMTRIG_START) {
+ if (es->polling != 0) {
+ ch->ptr = 0;
+ ch->prevptr = 0;
+ if (es_chan_active(es) == 0) {
+ es->poll_ticks = 1;
+ callout_reset(&es->poll_timer, 1,
+ es_poll_callback, es);
+ }
+ }
+ ch->active = 1;
+ } else {
+ ch->active = 0;
+ if (es->polling != 0) {
+ if (es_chan_active(es) == 0) {
+ callout_stop(&es->poll_timer);
+ es->poll_ticks = 1;
+ }
+ }
+ }
ES_UNLOCK(es);
- return 0;
+ return (0);
}
static int
@@ -651,21 +793,29 @@ eschan_getptr(kobj_t obj, void *data)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
- u_int32_t reg, cnt;
+ uint32_t reg, cnt;
- if (ch->dir == PCMDIR_PLAY) {
- if (ch->index == ES_DAC1)
- reg = ES1370_REG_DAC1_FRAMECNT;
- else
- reg = ES1370_REG_DAC2_FRAMECNT;
- } else
- reg = ES1370_REG_ADC_FRAMECNT;
ES_LOCK(es);
- es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4);
- cnt = es_rd(es, reg & 0x000000ff, 4) >> 16;
+ if (es->polling != 0)
+ cnt = ch->ptr;
+ else {
+ if (ch->dir == PCMDIR_PLAY) {
+ if (ch->index == ES_DAC1)
+ reg = ES1370_REG_DAC1_FRAMECNT;
+ else
+ reg = ES1370_REG_DAC2_FRAMECNT;
+ } else
+ reg = ES1370_REG_ADC_FRAMECNT;
+ es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4);
+ cnt = es_rd(es, reg & 0x000000ff, 4) >> 16;
+ /* cnt is longwords */
+ cnt <<= 2;
+ }
ES_UNLOCK(es);
- /* cnt is longwords */
- return cnt << 2;
+
+ cnt &= ~0x3f;
+
+ return (cnt);
}
static struct pcmchan_caps *
@@ -673,29 +823,29 @@ eschan_getcaps(kobj_t obj, void *data)
{
struct es_chinfo *ch = data;
- return &ch->caps;
+ return (&ch->caps);
}
static kobj_method_t eschan1370_methods[] = {
- KOBJMETHOD(channel_init, eschan_init),
- KOBJMETHOD(channel_setformat, eschan_setformat),
- KOBJMETHOD(channel_setspeed, eschan1370_setspeed),
- KOBJMETHOD(channel_setblocksize, eschan_setblocksize),
- KOBJMETHOD(channel_trigger, eschan_trigger),
- KOBJMETHOD(channel_getptr, eschan_getptr),
- KOBJMETHOD(channel_getcaps, eschan_getcaps),
+ KOBJMETHOD(channel_init, eschan_init),
+ KOBJMETHOD(channel_setformat, eschan_setformat),
+ KOBJMETHOD(channel_setspeed, eschan1370_setspeed),
+ KOBJMETHOD(channel_setblocksize, eschan_setblocksize),
+ KOBJMETHOD(channel_trigger, eschan_trigger),
+ KOBJMETHOD(channel_getptr, eschan_getptr),
+ KOBJMETHOD(channel_getcaps, eschan_getcaps),
{ 0, 0 }
};
CHANNEL_DECLARE(eschan1370);
static kobj_method_t eschan1371_methods[] = {
- KOBJMETHOD(channel_init, eschan_init),
- KOBJMETHOD(channel_setformat, eschan_setformat),
- KOBJMETHOD(channel_setspeed, eschan1371_setspeed),
- KOBJMETHOD(channel_setblocksize, eschan_setblocksize),
- KOBJMETHOD(channel_trigger, eschan_trigger),
- KOBJMETHOD(channel_getptr, eschan_getptr),
- KOBJMETHOD(channel_getcaps, eschan_getcaps),
+ KOBJMETHOD(channel_init, eschan_init),
+ KOBJMETHOD(channel_setformat, eschan_setformat),
+ KOBJMETHOD(channel_setspeed, eschan1371_setspeed),
+ KOBJMETHOD(channel_setblocksize, eschan_setblocksize),
+ KOBJMETHOD(channel_trigger, eschan_trigger),
+ KOBJMETHOD(channel_getptr, eschan_getptr),
+ KOBJMETHOD(channel_getcaps, eschan_getcaps),
{ 0, 0 }
};
CHANNEL_DECLARE(eschan1371);
@@ -709,6 +859,10 @@ es_intr(void *p)
uint32_t intsrc, sctrl;
ES_LOCK(es);
+ if (es->polling != 0) {
+ ES_UNLOCK(es);
+ return;
+ }
intsrc = es_rd(es, ES1370_REG_STATUS, 4);
if ((intsrc & STAT_INTR) == 0) {
ES_UNLOCK(es);
@@ -716,17 +870,23 @@ es_intr(void *p)
}
sctrl = es->sctrl;
- if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
- if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
- if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
+ if (intsrc & STAT_ADC)
+ sctrl &= ~SCTRL_R1INTEN;
+ if (intsrc & STAT_DAC1)
+ sctrl &= ~SCTRL_P1INTEN;
+ if (intsrc & STAT_DAC2)
+ sctrl &= ~SCTRL_P2INTEN;
es_wr(es, ES1370_REG_SERIAL_CONTROL, sctrl, 4);
es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
ES_UNLOCK(es);
- if (intsrc & STAT_ADC) chn_intr(es->ch[ES_ADC].channel);
- if (intsrc & STAT_DAC1) chn_intr(es->ch[ES_DAC1].channel);
- if (intsrc & STAT_DAC2) chn_intr(es->ch[ES_DAC2].channel);
+ if (intsrc & STAT_ADC)
+ chn_intr(es->ch[ES_ADC].channel);
+ if (intsrc & STAT_DAC1)
+ chn_intr(es->ch[ES_DAC1].channel);
+ if (intsrc & STAT_DAC2)
+ chn_intr(es->ch[ES_DAC2].channel);
}
/* ES1370 specific */
@@ -738,7 +898,7 @@ es1370_init(struct es_info *es)
/* ES1370 default to fixed rate operation */
if (resource_int_value(device_get_name(es->dev),
- device_get_unit(es->dev), "fixed_rate", &r) == 0) {
+ device_get_unit(es->dev), "fixed_rate", &r) == 0) {
fixed_rate = r;
if (fixed_rate) {
if (fixed_rate < es_caps.minspeed)
@@ -750,8 +910,8 @@ es1370_init(struct es_info *es)
fixed_rate = es_caps.maxspeed;
if (resource_int_value(device_get_name(es->dev),
- device_get_unit(es->dev), "single_pcm_mixer", &r) == 0)
- single_pcm = (r) ? 1 : 0;
+ device_get_unit(es->dev), "single_pcm_mixer", &r) == 0)
+ single_pcm = (r != 0) ? 1 : 0;
else
single_pcm = 1;
@@ -760,34 +920,38 @@ es1370_init(struct es_info *es)
single_pcm = 1;
/* This is ES1370 */
es->escfg = ES_SET_IS_ES1370(es->escfg, 1);
- if (fixed_rate) {
+ if (fixed_rate)
es->escfg = ES_SET_FIXED_RATE(es->escfg, fixed_rate);
- } else {
+ else {
es->escfg = ES_SET_FIXED_RATE(es->escfg, 0);
fixed_rate = DSP_DEFAULT_SPEED;
}
- if (single_pcm) {
+ if (single_pcm)
es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1);
- } else {
+ else
es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0);
- }
es->ctrl = CTRL_CDC_EN | CTRL_JYSTK_EN | CTRL_SERR_DIS |
- (DAC2_SRTODIV(fixed_rate) << CTRL_SH_PCLKDIV);
+ (DAC2_SRTODIV(fixed_rate) << CTRL_SH_PCLKDIV);
es->ctrl |= 3 << CTRL_SH_WTSRSEL;
es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
es->sctrl = 0;
es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
- es1370_wrcodec(es, CODEC_RES_PD, 3);/* No RST, PD */
- es1370_wrcodec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
- * {LR,B}CLK2 and run off the LRCLK2
- * PLL; program DAC_SYNC=0! */
- es1370_wrcodec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
- es1370_wrcodec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
+ /* No RST, PD */
+ es1370_wrcodec(es, CODEC_RES_PD, 3);
+ /*
+ * CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL;
+ * program DAC_SYNC=0!
+ */
+ es1370_wrcodec(es, CODEC_CSEL, 0);
+ /* Recording source is mixer */
+ es1370_wrcodec(es, CODEC_ADSEL, 0);
+ /* MIC amp is 0db */
+ es1370_wrcodec(es, CODEC_MGAIN, 0);
ES_UNLOCK(es);
- return 0;
+ return (0);
}
/* ES1371 specific */
@@ -805,7 +969,8 @@ es1371_init(struct es_info *es)
cssr = 0;
devid = pci_get_devid(es->dev);
revid = pci_get_revid(es->dev);
- subdev = (pci_get_subdevice(es->dev) << 16) | pci_get_subvendor(es->dev);
+ subdev = (pci_get_subdevice(es->dev) << 16) |
+ pci_get_subvendor(es->dev);
/*
* Joyport blacklist. Either we're facing with broken hardware
* or because this hardware need special (unknown) initialization
@@ -846,20 +1011,21 @@ es1371_init(struct es_info *es)
es_wr(es, ES1371_REG_SMPRATE, ES1371_DIS_SRC, 4);
for (idx = 0; idx < 0x80; idx++)
es1371_src_write(es, idx, 0);
- es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
+ es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
- es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
+ es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
- es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12);
- es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12);
- es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12);
- es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
- es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12);
- es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
- es1371_adc_rate(es, 22050, ES_ADC);
- es1371_dac_rate(es, 22050, ES_DAC1);
- es1371_dac_rate(es, 22050, ES_DAC2);
- /* WARNING:
+ es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
+ es1371_adc_rate(es, 22050, ES_ADC);
+ es1371_dac_rate(es, 22050, ES_DAC1);
+ es1371_dac_rate(es, 22050, ES_DAC2);
+ /*
+ * WARNING:
* enabling the sample rate converter without properly programming
* its parameters causes the chip to lock up (the SRC busy bit will
* be stuck high, and I've found no way to rectify this other than
@@ -878,7 +1044,7 @@ es1371_init(struct es_info *es)
/* -------------------------------------------------------------------- */
static int
-es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data)
+es1371_wrcd(kobj_t obj, void *s, int addr, uint32_t data)
{
uint32_t t, x, orig;
struct es_info *es = (struct es_info*)s;
@@ -889,27 +1055,29 @@ es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data)
/* save the current state for later */
x = orig = es_rd(es, ES1371_REG_SMPRATE, 4);
/* enable SRC state data in SRC mux */
- es_wr(es, ES1371_REG_SMPRATE,
- (x &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) |
- 0x00010000, 4);
+ es_wr(es, ES1371_REG_SMPRATE, (x & (ES1371_DIS_SRC | ES1371_DIS_P1 |
+ ES1371_DIS_P2 | ES1371_DIS_R1)) | 0x00010000, 4);
/* busy wait */
- for (t = 0; t < 0x1000; t++)
- if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000)
+ for (t = 0; t < 0x1000; t++) {
+ if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) ==
+ 0x00000000)
break;
+ }
/* wait for a SAFE time to write addr/data and then do it, dammit */
- for (t = 0; t < 0x1000; t++)
- if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000)
+ for (t = 0; t < 0x1000; t++) {
+ if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) ==
+ 0x00010000)
break;
+ }
- es_wr(es, ES1371_REG_CODEC,
- ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
- ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), 4);
+ es_wr(es, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) &
+ CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) &
+ CODEC_PODAT_MASK), 4);
/* restore SRC reg */
es1371_wait_src_ready(s);
es_wr(es, ES1371_REG_SMPRATE, orig, 4);
- return 0;
+ return (0);
}
static int
@@ -918,83 +1086,88 @@ es1371_rdcd(kobj_t obj, void *s, int addr)
uint32_t t, x, orig;
struct es_info *es = (struct es_info *)s;
- for (t = 0; t < 0x1000; t++)
+ for (t = 0; t < 0x1000; t++) {
if (!(x = es_rd(es, ES1371_REG_CODEC, 4) & CODEC_WIP))
break;
+ }
/* save the current state for later */
x = orig = es_rd(es, ES1371_REG_SMPRATE, 4);
/* enable SRC state data in SRC mux */
- es_wr(es, ES1371_REG_SMPRATE,
- (x &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) |
- 0x00010000, 4);
+ es_wr(es, ES1371_REG_SMPRATE, (x & (ES1371_DIS_SRC | ES1371_DIS_P1 |
+ ES1371_DIS_P2 | ES1371_DIS_R1)) | 0x00010000, 4);
/* busy wait */
- for (t = 0; t < 0x1000; t++)
- if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000)
+ for (t = 0; t < 0x1000; t++) {
+ if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) ==
+ 0x00000000)
break;
+ }
/* wait for a SAFE time to write addr/data and then do it, dammit */
- for (t = 0; t < 0x1000; t++)
- if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000)
+ for (t = 0; t < 0x1000; t++) {
+ if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) ==
+ 0x00010000)
break;
+ }
- es_wr(es, ES1371_REG_CODEC,
- ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
- CODEC_PORD, 4);
+ es_wr(es, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) &
+ CODEC_POADD_MASK) | CODEC_PORD, 4);
/* restore SRC reg */
es1371_wait_src_ready(s);
es_wr(es, ES1371_REG_SMPRATE, orig, 4);
/* now wait for the stinkin' data (RDY) */
- for (t = 0; t < 0x1000; t++)
+ for (t = 0; t < 0x1000; t++) {
if ((x = es_rd(es, ES1371_REG_CODEC, 4)) & CODEC_RDY)
break;
+ }
return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
}
static kobj_method_t es1371_ac97_methods[] = {
- KOBJMETHOD(ac97_read, es1371_rdcd),
- KOBJMETHOD(ac97_write, es1371_wrcd),
+ KOBJMETHOD(ac97_read, es1371_rdcd),
+ KOBJMETHOD(ac97_write, es1371_wrcd),
{ 0, 0 }
};
AC97_DECLARE(es1371_ac97);
/* -------------------------------------------------------------------- */
-static u_int
-es1371_src_read(struct es_info *es, u_short reg)
+static unsigned int
+es1371_src_read(struct es_info *es, unsigned short reg)
{
uint32_t r;
- r = es1371_wait_src_ready(es) &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
+ r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 |
+ ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg);
es_wr(es, ES1371_REG_SMPRATE, r, 4);
- return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es));
+ return (ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es)));
}
static void
-es1371_src_write(struct es_info *es, u_short reg, u_short data)
+es1371_src_write(struct es_info *es, unsigned short reg, unsigned short data)
{
uint32_t r;
- r = es1371_wait_src_ready(es) &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
+ r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 |
+ ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data);
es_wr(es, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE, 4);
}
-static u_int
-es1371_adc_rate(struct es_info *es, u_int rate, int set)
+static unsigned int
+es1371_adc_rate(struct es_info *es, unsigned int rate, int set)
{
- u_int n, truncm, freq, result;
+ unsigned int n, truncm, freq, result;
ES_LOCK_ASSERT(es);
- if (rate > 48000) rate = 48000;
- if (rate < 4000) rate = 4000;
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 4000)
+ rate = 4000;
n = rate / 3000;
if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
n--;
@@ -1003,46 +1176,54 @@ es1371_adc_rate(struct es_info *es, u_int rate, int set)
result = (48000UL << 15) / (freq / n);
if (set) {
if (rate >= 24000) {
- if (truncm > 239) truncm = 239;
+ if (truncm > 239)
+ truncm = 239;
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
- (((239 - truncm) >> 1) << 9) | (n << 4));
+ (((239 - truncm) >> 1) << 9) | (n << 4));
} else {
- if (truncm > 119) truncm = 119;
+ if (truncm > 119)
+ truncm = 119;
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
- 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+ 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
}
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
- (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) &
- 0x00ff) | ((freq >> 5) & 0xfc00));
- es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) &
+ 0x00ff) | ((freq >> 5) & 0xfc00));
+ es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC,
+ freq & 0x7fff);
es1371_src_write(es, ES_SMPREG_VOL_ADC, n << 8);
es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, n << 8);
}
- return result;
+ return (result);
}
-static u_int
-es1371_dac_rate(struct es_info *es, u_int rate, int set)
+static unsigned int
+es1371_dac_rate(struct es_info *es, unsigned int rate, int set)
{
- u_int freq, r, result, dac, dis;
+ unsigned int freq, r, result, dac, dis;
ES_LOCK_ASSERT(es);
- if (rate > 48000) rate = 48000;
- if (rate < 4000) rate = 4000;
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 4000)
+ rate = 4000;
freq = ((rate << 15) + 1500) / 3000;
result = (freq * 3000) >> 15;
-
+
dac = (set == ES_DAC1) ? ES_SMPREG_DAC1 : ES_SMPREG_DAC2;
dis = (set == ES_DAC1) ? ES1371_DIS_P2 : ES1371_DIS_P1;
- r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
+ r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 |
+ ES1371_DIS_P2 | ES1371_DIS_R1));
es_wr(es, ES1371_REG_SMPRATE, r, 4);
es1371_src_write(es, dac + ES_SMPREG_INT_REGS,
- (es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00));
+ (es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) |
+ ((freq >> 5) & 0xfc00));
es1371_src_write(es, dac + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
- r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | dis | ES1371_DIS_R1));
+ r = (es1371_wait_src_ready(es) &
+ (ES1371_DIS_SRC | dis | ES1371_DIS_R1));
es_wr(es, ES1371_REG_SMPRATE, r, 4);
- return result;
+ return (result);
}
static uint32_t
@@ -1051,13 +1232,14 @@ es1371_wait_src_ready(struct es_info *es)
uint32_t t, r;
for (t = 0; t < 0x1000; t++) {
- if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) & ES1371_SRC_RAM_BUSY))
- return r;
+ if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) &
+ ES1371_SRC_RAM_BUSY))
+ return (r);
DELAY(1);
}
device_printf(es->dev, "%s: timed out 0x%x [0x%x]\n", __func__,
ES1371_REG_SMPRATE, r);
- return 0;
+ return (0);
}
/* -------------------------------------------------------------------- */
@@ -1072,78 +1254,76 @@ es_pci_probe(device_t dev)
switch(pci_get_devid(dev)) {
case ES1370_PCI_ID:
device_set_desc(dev, "AudioPCI ES1370");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371_PCI_ID:
switch(pci_get_revid(dev)) {
case ES1371REV_ES1371_A:
device_set_desc(dev, "AudioPCI ES1371-A");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371REV_ES1371_B:
device_set_desc(dev, "AudioPCI ES1371-B");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371REV_ES1373_A:
device_set_desc(dev, "AudioPCI ES1373-A");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371REV_ES1373_B:
device_set_desc(dev, "AudioPCI ES1373-B");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371REV_ES1373_8:
device_set_desc(dev, "AudioPCI ES1373-8");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case ES1371REV_CT5880_A:
device_set_desc(dev, "Creative CT5880-A");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
default:
device_set_desc(dev, "AudioPCI ES1371-?");
- device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev));
- return BUS_PROBE_DEFAULT;
+ device_printf(dev,
+ "unknown revision %d -- please report to "
+ "freebsd-multimedia@freebsd.org\n",
+ pci_get_revid(dev));
+ return (BUS_PROBE_DEFAULT);
}
-
case ES1371_PCI_ID2:
device_set_desc(dev, "Strange AudioPCI ES1371-? (vid=3274)");
- device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev));
- return BUS_PROBE_DEFAULT;
-
+ device_printf(dev,
+ "unknown revision %d -- please report to "
+ "freebsd-multimedia@freebsd.org\n", pci_get_revid(dev));
+ return (BUS_PROBE_DEFAULT);
case CT4730_PCI_ID:
switch(pci_get_revid(dev)) {
case CT4730REV_CT4730_A:
- device_set_desc(dev, "Creative SB AudioPCI CT4730/EV1938");
- return BUS_PROBE_DEFAULT;
+ device_set_desc(dev,
+ "Creative SB AudioPCI CT4730/EV1938");
+ return (BUS_PROBE_DEFAULT);
default:
device_set_desc(dev, "Creative SB AudioPCI CT4730-?");
- device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev));
- return BUS_PROBE_DEFAULT;
+ device_printf(dev,
+ "unknown revision %d -- please report to "
+ "freebsd-multimedia@freebsd.org\n",
+ pci_get_revid(dev));
+ return (BUS_PROBE_DEFAULT);
}
-
case CT5880_PCI_ID:
switch(pci_get_revid(dev)) {
case CT5880REV_CT5880_C:
device_set_desc(dev, "Creative CT5880-C");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case CT5880REV_CT5880_D:
device_set_desc(dev, "Creative CT5880-D");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
case CT5880REV_CT5880_E:
device_set_desc(dev, "Creative CT5880-E");
- return BUS_PROBE_DEFAULT;
-
+ return (BUS_PROBE_DEFAULT);
default:
device_set_desc(dev, "Creative CT5880-?");
- device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev));
- return BUS_PROBE_DEFAULT;
+ device_printf(dev,
+ "unknown revision %d -- please report to "
+ "freebsd-multimedia@freebsd.org\n",
+ pci_get_revid(dev));
+ return (BUS_PROBE_DEFAULT);
}
-
default:
- return ENXIO;
+ return (ENXIO);
}
}
@@ -1200,7 +1380,7 @@ sysctl_es137x_latency_timer(SYSCTL_HANDLER_ARGS)
val = pci_read_config(dev, PCIR_LATTIMER, 1);
ES_UNLOCK(es);
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
-
+
if (err || req->newptr == NULL)
return (err);
if (val > 255)
@@ -1229,7 +1409,7 @@ sysctl_es137x_fixed_rate(SYSCTL_HANDLER_ARGS)
val = 0;
ES_UNLOCK(es);
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
-
+
if (err || req->newptr == NULL)
return (err);
if (val != 0 && (val < es_caps.minspeed || val > es_caps.maxspeed))
@@ -1284,7 +1464,7 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
val = set;
ES_UNLOCK(es);
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
-
+
if (err || req->newptr == NULL)
return (err);
if (!(val == 0 || val == 1))
@@ -1294,11 +1474,11 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
i_dev = d->mixer_dev;
if (mixer_ioctl(i_dev, 0, (caddr_t)&recsrc, 0, NULL) != EBADF)
return (EBUSY);
- err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM),
- (caddr_t)&level, -1, NULL);
+ err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&level,
+ -1, NULL);
if (!err)
err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_RECSRC),
- (caddr_t)&recsrc, -1, NULL);
+ (caddr_t)&recsrc, -1, NULL);
if (err)
return (err);
if (level < 0)
@@ -1309,27 +1489,26 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
ES_UNLOCK(es);
return (EBUSY);
}
- if (val) {
+ if (val)
es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1);
- } else {
+ else
es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0);
- }
ES_UNLOCK(es);
m = i_dev->si_drv1;
if (!val) {
mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) |
- (1 << SOUND_MIXER_SYNTH));
+ (1 << SOUND_MIXER_SYNTH));
mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) |
- (1 << SOUND_MIXER_SYNTH));
+ (1 << SOUND_MIXER_SYNTH));
err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH),
- (caddr_t)&level, -1, NULL);
+ (caddr_t)&level, -1, NULL);
} else {
err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH),
- (caddr_t)&level, -1, NULL);
+ (caddr_t)&level, -1, NULL);
mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) &
- ~(1 << SOUND_MIXER_SYNTH));
+ ~(1 << SOUND_MIXER_SYNTH));
mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) &
- ~(1 << SOUND_MIXER_SYNTH));
+ ~(1 << SOUND_MIXER_SYNTH));
}
if (!err) {
level = recsrc;
@@ -1338,11 +1517,47 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
else if (recsrc & (1 << SOUND_MIXER_SYNTH))
recsrc |= 1 << SOUND_MIXER_PCM;
if (level != recsrc)
- err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_RECSRC),
- (caddr_t)&recsrc, -1, NULL);
+ err = mixer_ioctl(i_dev,
+ MIXER_WRITE(SOUND_MIXER_RECSRC),
+ (caddr_t)&recsrc, -1, NULL);
}
return (err);
}
+
+static int
+sysctl_es_polling(SYSCTL_HANDLER_ARGS)
+{
+ struct es_info *es;
+ device_t dev;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ es = pcm_getdevinfo(dev);
+ if (es == NULL)
+ return (EINVAL);
+ ES_LOCK(es);
+ val = es->polling;
+ ES_UNLOCK(es);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return (err);
+ if (val < 0 || val > 1)
+ return (EINVAL);
+
+ ES_LOCK(es);
+ if (val != es->polling) {
+ if (es_chan_active(es) != 0)
+ err = EBUSY;
+ else if (val == 0)
+ es->polling = 0;
+ else
+ es->polling = 1;
+ }
+ ES_UNLOCK(es);
+
+ return (err);
+}
#endif /* SND_DYNSYSCTL */
static void
@@ -1365,66 +1580,69 @@ es_init_sysctls(device_t dev)
to a device specific sysctl "dev.pcm.X.yyy" via
device_get_sysctl_*() as discussed on multimedia@ in msg-id
<861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_spdif_enabled",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_es137x_spdif_enable, "I",
- "Enable S/PDIF output on primary playback channel");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "spdif_enabled", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es137x_spdif_enable, "I",
+ "Enable S/PDIF output on primary playback channel");
} else if (devid == ES1370_PCI_ID) {
/*
* Enable fixed rate sysctl if both DAC2 / ADC enabled.
*/
- if (es->ch[ES_DAC2].channel != NULL && es->ch[ES_ADC].channel != NULL) {
+ if (es->ch[ES_DAC2].channel != NULL &&
+ es->ch[ES_ADC].channel != NULL) {
/* XXX: an user should be able to set this with a control tool,
if not done before 7.0-RELEASE, this needs to be converted
to a device specific sysctl "dev.pcm.X.yyy" via
device_get_sysctl_*() as discussed on multimedia@ in msg-id
<861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_fixed_rate",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_es137x_fixed_rate, "I",
- "Enable fixed rate playback/recording");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "fixed_rate", CTLTYPE_INT | CTLFLAG_RW,
+ dev, sizeof(dev), sysctl_es137x_fixed_rate, "I",
+ "Enable fixed rate playback/recording");
}
/*
* Enable single pcm mixer sysctl if both DAC1/2 enabled.
*/
- if (es->ch[ES_DAC1].channel != NULL && es->ch[ES_DAC2].channel != NULL) {
+ if (es->ch[ES_DAC1].channel != NULL &&
+ es->ch[ES_DAC2].channel != NULL) {
/* XXX: an user should be able to set this with a control tool,
if not done before 7.0-RELEASE, this needs to be converted
to a device specific sysctl "dev.pcm.X.yyy" via
device_get_sysctl_*() as discussed on multimedia@ in msg-id
<861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_single_pcm_mixer",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_es137x_single_pcm_mixer, "I",
- "Single PCM mixer controller for both DAC1/DAC2");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "single_pcm_mixer",
+ CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es137x_single_pcm_mixer, "I",
+ "Single PCM mixer controller for both DAC1/DAC2");
}
}
- if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "latency_timer", &r) == 0 &&
- !(r < 0 || r > 255))
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "latency_timer", &r) == 0 && !(r < 0 || r > 255))
pci_write_config(dev, PCIR_LATTIMER, r, 1);
/* XXX: this needs to be converted to a device specific sysctl
"dev.pcm.X.yyy" via device_get_sysctl_*() as discussed on
multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "latency_timer",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_es137x_latency_timer, "I",
- "PCI Latency Timer configuration");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "latency_timer", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es137x_latency_timer, "I",
+ "PCI Latency Timer configuration");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es_polling, "I",
+ "Enable polling mode");
#endif /* SND_DYNSYSCTL */
}
static int
es_pci_attach(device_t dev)
{
- u_int32_t data;
+ uint32_t data;
struct es_info *es = NULL;
int mapped, i, numplay, dac_cfg;
char status[SND_STATUSLEN];
@@ -1434,7 +1652,7 @@ es_pci_attach(device_t dev)
if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
- return ENXIO;
+ return (ENXIO);
}
es->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
es->dev = dev;
@@ -1450,7 +1668,7 @@ es_pci_attach(device_t dev)
es->regid = MEM_MAP_REG;
es->regtype = SYS_RES_MEMORY;
es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid,
- RF_ACTIVE);
+ RF_ACTIVE);
if (es->reg)
mapped++;
}
@@ -1458,7 +1676,7 @@ es_pci_attach(device_t dev)
es->regid = PCIR_BAR(0);
es->regtype = SYS_RES_IOPORT;
es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid,
- RF_ACTIVE);
+ RF_ACTIVE);
if (es->reg)
mapped++;
}
@@ -1469,35 +1687,61 @@ es_pci_attach(device_t dev)
es->st = rman_get_bustag(es->reg);
es->sh = rman_get_bushandle(es->reg);
- es->bufsz = pcm_getbuffersize(dev, 4096, ES_DEFAULT_BUFSZ, 65536);
+ callout_init(&es->poll_timer, CALLOUT_MPSAFE);
+ es->poll_ticks = 1;
if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "dac", &dac_cfg) == 0) {
+ device_get_unit(dev), "polling", &i) == 0 && i != 0)
+ es->polling = 1;
+ else
+ es->polling = 0;
+
+ es->bufsz = pcm_getbuffersize(dev, 4096, ES_DEFAULT_BUFSZ, 65536);
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
+ i &= ~0x3f;
+ if (i < 0x40)
+ i = 0x40;
+ es->blkcnt = es->bufsz / i;
+ i = 0;
+ while (es->blkcnt >> i)
+ i++;
+ es->blkcnt = 1 << (i - 1);
+ if (es->blkcnt < 2)
+ es->blkcnt = 2;
+ else if (es->blkcnt > 256)
+ es->blkcnt = 256;
+
+ } else
+ es->blkcnt = 2;
+
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "dac", &dac_cfg) == 0) {
if (dac_cfg < 0 || dac_cfg > 3)
dac_cfg = ES_DEFAULT_DAC_CFG;
} else
dac_cfg = ES_DEFAULT_DAC_CFG;
switch (dac_cfg) {
- case 0: /* Enable all DAC: DAC1, DAC2 */
- numplay = 2;
- es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1);
- es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC2);
- break;
- case 1: /* Only DAC1 */
- numplay = 1;
- es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1);
- break;
- case 3: /* Enable all DAC / swap position: DAC2, DAC1 */
- numplay = 2;
- es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2);
- es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC1);
- break;
- case 2: /* Only DAC2 */
- default:
- numplay = 1;
- es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2);
- break;
+ case 0: /* Enable all DAC: DAC1, DAC2 */
+ numplay = 2;
+ es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1);
+ es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC2);
+ break;
+ case 1: /* Only DAC1 */
+ numplay = 1;
+ es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1);
+ break;
+ case 3: /* Enable all DAC / swap position: DAC2, DAC1 */
+ numplay = 2;
+ es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2);
+ es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC1);
+ break;
+ case 2: /* Only DAC2 */
+ default:
+ numplay = 1;
+ es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2);
+ break;
}
es->escfg = ES_SET_NUMPLAY(es->escfg, numplay);
es->escfg = ES_SET_NUMREC(es->escfg, 1);
@@ -1526,9 +1770,8 @@ es_pci_attach(device_t dev)
* This is a special case for es1370 only, where the
* speed of both ADC and DAC2 locked together.
*/
- if (!ES_DAC2_ENABLED(es->escfg)) {
+ if (!ES_DAC2_ENABLED(es->escfg))
es->escfg = ES_SET_FIXED_RATE(es->escfg, 0);
- }
if (mixer_init(dev, &es1370_mixer_class, es))
goto bad;
ct = &eschan1370_class;
@@ -1540,8 +1783,9 @@ es_pci_attach(device_t dev)
es->irqid = 0;
es->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &es->irqid,
- RF_ACTIVE | RF_SHAREABLE);
- if (!es->irq || snd_setup_intr(dev, es->irq, INTR_MPSAFE, es_intr, es, &es->ih)) {
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!es->irq || snd_setup_intr(dev, es->irq, INTR_MPSAFE, es_intr,
+ es, &es->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@@ -1558,8 +1802,9 @@ es_pci_attach(device_t dev)
}
snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s",
- (es->regtype == SYS_RES_IOPORT)? "io" : "memory",
- rman_get_start(es->reg), rman_get_start(es->irq),PCM_KLDSTRING(snd_es137x));
+ (es->regtype == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(es->reg), rman_get_start(es->irq),
+ PCM_KLDSTRING(snd_es137x));
if (pcm_register(dev, es, numplay, 1))
goto bad;
@@ -1569,25 +1814,30 @@ es_pci_attach(device_t dev)
es_init_sysctls(dev);
pcm_setstatus(dev, status);
es->escfg = ES_SET_GP(es->escfg, 0);
- if (numplay == 1) {
+ if (numplay == 1)
device_printf(dev, "<Playback: DAC%d / Record: ADC>\n",
- ES_DAC_FIRST(es->escfg) + 1);
- } else if (numplay == 2) {
+ ES_DAC_FIRST(es->escfg) + 1);
+ else if (numplay == 2)
device_printf(dev, "<Playback: DAC%d,DAC%d / Record: ADC>\n",
- ES_DAC_FIRST(es->escfg) + 1,
- ES_DAC_SECOND(es->escfg) + 1);
- }
- return 0;
-
- bad:
- if (es->parent_dmat) bus_dma_tag_destroy(es->parent_dmat);
- if (es->ih) bus_teardown_intr(dev, es->irq, es->ih);
- if (es->irq) bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq);
- if (codec) ac97_destroy(codec);
- if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg);
- if (es->lock) snd_mtxfree(es->lock);
- if (es) free(es, M_DEVBUF);
- return ENXIO;
+ ES_DAC_FIRST(es->escfg) + 1, ES_DAC_SECOND(es->escfg) + 1);
+ return (0);
+
+bad:
+ if (es->parent_dmat)
+ bus_dma_tag_destroy(es->parent_dmat);
+ if (es->ih)
+ bus_teardown_intr(dev, es->irq, es->ih);
+ if (es->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq);
+ if (codec)
+ ac97_destroy(codec);
+ if (es->reg)
+ bus_release_resource(dev, es->regtype, es->regid, es->reg);
+ if (es->lock)
+ snd_mtxfree(es->lock);
+ if (es)
+ free(es, M_DEVBUF);
+ return (ENXIO);
}
static int
@@ -1597,7 +1847,8 @@ es_pci_detach(device_t dev)
struct es_info *es;
r = pcm_unregister(dev);
- if (r) return r;
+ if (r)
+ return (r);
es = pcm_getdevinfo(dev);
bus_teardown_intr(dev, es->irq, es->ih);
@@ -1607,7 +1858,7 @@ es_pci_detach(device_t dev)
snd_mtxfree(es->lock);
free(es, M_DEVBUF);
- return 0;
+ return (0);
}
static device_method_t es_methods[] = {
diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h
index 0bf1456..7965daf 100644
--- a/sys/dev/sound/pci/hda/hda_reg.h
+++ b/sys/dev/sound/pci/hda/hda_reg.h
@@ -870,30 +870,30 @@
#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17
#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000
#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000800
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 11
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000400
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 10
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000200
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 9
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000100
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 8
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000080
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 7
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000040
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 6
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000020
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 5
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000010
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 4
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000008
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 3
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x000000004
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 2
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x000000002
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 1
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x000000001
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 0
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000001
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 0
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000002
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 1
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000004
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 2
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000008
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 3
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000010
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 4
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000020
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 5
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000040
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 6
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000080
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 7
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000100
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 8
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x00000200
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 9
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x00000400
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 10
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x00000800
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 11
#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \
(((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \
diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c
index 0889e4d..649cb73 100644
--- a/sys/dev/sound/pci/hda/hdac.c
+++ b/sys/dev/sound/pci/hda/hdac.c
@@ -44,7 +44,7 @@
* http://people.freebsd.org/~ariff/HDA/parser.rb . This crude
* ruby parser take the verbose dmesg dump as its input. Refer to
* http://www.microsoft.com/whdc/device/audio/default.mspx for various
- * interesting documents, especiall UAA (Universal Audio Architecture).
+ * interesting documents, especially UAA (Universal Audio Architecture).
* 4) Possible vendor specific support.
* (snd_hda_intel, snd_hda_ati, etc..)
*
@@ -80,7 +80,7 @@
#include "mixer_if.h"
-#define HDA_DRV_TEST_REV "20061017_0033"
+#define HDA_DRV_TEST_REV "20061111_0034"
#define HDA_WIDGET_PARSER_REV 1
SND_DECLARE_FILE("$FreeBSD$");
@@ -197,6 +197,11 @@ SND_DECLARE_FILE("$FreeBSD$");
#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6)
#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff)
+/* Lenovo */
+#define LNV_VENDORID 0x17aa
+#define LNV_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LNV, 0x2066)
+#define LNV_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LNV, 0xffff)
+
/* Misc constants.. */
#define HDA_AMP_MUTE_DEFAULT (0xffffffff)
@@ -512,6 +517,9 @@ static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *,
static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *);
static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t);
+static int hdac_rirb_flush(struct hdac_softc *sc);
+static int hdac_unsolq_flush(struct hdac_softc *sc);
+
#define hdac_command(a1, a2, a3) \
hdac_command_sendone_internal(a1, a2, a3)
@@ -788,7 +796,7 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag)
}
}
-static void
+static int
hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
{
/* XXX to be removed */
@@ -797,7 +805,7 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
#endif
if (ch->blkcnt == 0)
- return;
+ return (0);
/* XXX to be removed */
#ifdef HDAC_INTR_EXTRA
@@ -822,16 +830,13 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
#ifdef HDAC_INTR_EXTRA
if (res & HDAC_SDSTS_BCIS) {
#endif
- ch->prevptr = ch->ptr;
- ch->ptr += sndbuf_getblksz(ch->b);
- ch->ptr %= sndbuf_getsize(ch->b);
- hdac_unlock(sc);
- chn_intr(ch->c);
- hdac_lock(sc);
+ return (1);
/* XXX to be removed */
#ifdef HDAC_INTR_EXTRA
}
#endif
+
+ return (0);
}
/****************************************************************************
@@ -845,14 +850,16 @@ hdac_intr_handler(void *context)
struct hdac_softc *sc;
uint32_t intsts;
uint8_t rirbsts;
- uint8_t rirbwp;
- struct hdac_rirb *rirb_base, *rirb;
- nid_t ucad;
- uint32_t utag;
+ struct hdac_rirb *rirb_base;
+ uint32_t trigger = 0;
sc = (struct hdac_softc *)context;
hdac_lock(sc);
+ if (sc->polling != 0) {
+ hdac_unlock(sc);
+ return;
+ }
/* Do we have anything to do? */
intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS);
if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) {
@@ -866,26 +873,9 @@ hdac_intr_handler(void *context)
rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
/* Get as many responses that we can */
while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) {
- HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
- rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
- bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
- BUS_DMASYNC_POSTREAD);
- while (sc->rirb_rp != rirbwp) {
- sc->rirb_rp++;
- sc->rirb_rp %= sc->rirb_size;
- rirb = &rirb_base[sc->rirb_rp];
- if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
- ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
- utag = rirb->response >> 26;
- if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
- sc->codecs[ucad] != NULL) {
- sc->unsolq[sc->unsolq_wp++] =
- (ucad << 16) |
- (utag & 0xffff);
- sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
- }
- }
- }
+ HDAC_WRITE_1(&sc->mem,
+ HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
+ hdac_rirb_flush(sc);
rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
}
/* XXX to be removed */
@@ -894,29 +884,29 @@ hdac_intr_handler(void *context)
HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS);
#endif
}
+
+ hdac_unsolq_flush(sc);
+
if (intsts & HDAC_INTSTS_SIS_MASK) {
- if (intsts & (1 << sc->num_iss))
- hdac_stream_intr(sc, &sc->play);
- if (intsts & (1 << 0))
- hdac_stream_intr(sc, &sc->rec);
+ if ((intsts & (1 << sc->num_iss)) &&
+ hdac_stream_intr(sc, &sc->play) != 0)
+ trigger |= 1;
+ if ((intsts & (1 << 0)) &&
+ hdac_stream_intr(sc, &sc->rec) != 0)
+ trigger |= 2;
/* XXX to be removed */
#ifdef HDAC_INTR_EXTRA
- HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK);
+ HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts &
+ HDAC_INTSTS_SIS_MASK);
#endif
}
- if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
- sc->unsolq_st = HDAC_UNSOLQ_BUSY;
- while (sc->unsolq_rp != sc->unsolq_wp) {
- ucad = sc->unsolq[sc->unsolq_rp] >> 16;
- utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
- sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
- hdac_unsolicited_handler(sc->codecs[ucad], utag);
- }
- sc->unsolq_st = HDAC_UNSOLQ_READY;
- }
-
hdac_unlock(sc);
+
+ if (trigger & 1)
+ chn_intr(sc->play.c);
+ if (trigger & 2)
+ chn_intr(sc->rec.c);
}
/****************************************************************************
@@ -1216,6 +1206,7 @@ hdac_mem_free(struct hdac_softc *sc)
if (mem->mem_res != NULL)
bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid,
mem->mem_res);
+ mem->mem_res = NULL;
}
/****************************************************************************
@@ -1239,7 +1230,7 @@ hdac_irq_alloc(struct hdac_softc *sc)
goto fail;
}
result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE,
- hdac_intr_handler, sc, &irq->irq_handle);
+ hdac_intr_handler, sc, &irq->irq_handle);
if (result != 0) {
device_printf(sc->dev,
"%s: Unable to setup interrupt handler (%x)\n",
@@ -1250,9 +1241,8 @@ hdac_irq_alloc(struct hdac_softc *sc)
return (0);
fail:
- if (irq->irq_res != NULL)
- bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
- irq->irq_res);
+ hdac_irq_free(sc);
+
return (ENXIO);
}
@@ -1267,11 +1257,13 @@ hdac_irq_free(struct hdac_softc *sc)
struct hdac_irq *irq;
irq = &sc->irq;
- if (irq->irq_handle != NULL)
+ if (irq->irq_res != NULL && irq->irq_handle != NULL)
bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle);
if (irq->irq_res != NULL)
bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
irq->irq_res);
+ irq->irq_handle = NULL;
+ irq->irq_res = NULL;
}
/****************************************************************************
@@ -1362,16 +1354,18 @@ hdac_rirb_init(struct hdac_softc *sc)
sc->rirb_rp = 0;
HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST);
- /* Setup the interrupt threshold */
- HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
+ if (sc->polling == 0) {
+ /* Setup the interrupt threshold */
+ HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
- /* Enable Overrun and response received reporting */
+ /* Enable Overrun and response received reporting */
#if 0
- HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
- HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
+ HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
+ HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
#else
- HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
+ HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
#endif
+ }
/*
* Make sure that the Host CPU cache doesn't contain any dirty
@@ -1439,6 +1433,8 @@ hdac_scan_codecs(struct hdac_softc *sc)
"Unable to allocate memory for codec\n");
continue;
}
+ codec->commands = NULL;
+ codec->responses_received = 0;
codec->verbs_sent = 0;
codec->sc = sc;
codec->cad = i;
@@ -1688,14 +1684,14 @@ hdac_widget_pin_parse(struct hdac_widget *w)
w->wclass.pin.config = config;
pincap = hdac_command(sc,
- HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
+ HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
w->wclass.pin.cap = pincap;
w->wclass.pin.ctrl = hdac_command(sc,
- HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
- ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
- HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
- HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE);
+ HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
+ ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
+ HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
+ HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE);
if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE;
@@ -1907,6 +1903,144 @@ hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid)
return (&devinfo->widget[nid - devinfo->startnode]);
}
+static __inline int
+hda_poll_channel(struct hdac_chan *ch)
+{
+ uint32_t sz, delta;
+ volatile uint32_t ptr;
+
+ if (ch->active == 0)
+ return (0);
+
+ sz = ch->blksz * ch->blkcnt;
+ ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, ch->off + HDAC_SDLPIB);
+ ch->ptr = ptr;
+ ptr %= sz;
+ ptr &= ~(ch->blksz - 1);
+ delta = (sz + ptr - ch->prevptr) % sz;
+
+ if (delta < ch->blksz)
+ return (0);
+
+ ch->prevptr = ptr;
+
+ return (1);
+}
+
+#define hda_chan_active(sc) ((sc)->play.active + (sc)->rec.active)
+
+static void
+hda_poll_callback(void *arg)
+{
+ struct hdac_softc *sc = arg;
+ uint32_t trigger = 0;
+
+ if (sc == NULL)
+ return;
+
+ hdac_lock(sc);
+ if (sc->polling == 0 || hda_chan_active(sc) == 0) {
+ hdac_unlock(sc);
+ return;
+ }
+
+ trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0;
+ trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0;
+
+ /* XXX */
+ callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/,
+ hda_poll_callback, sc);
+
+ hdac_unlock(sc);
+
+ if (trigger & 1)
+ chn_intr(sc->play.c);
+ if (trigger & 2)
+ chn_intr(sc->rec.c);
+}
+
+static int
+hdac_rirb_flush(struct hdac_softc *sc)
+{
+ struct hdac_rirb *rirb_base, *rirb;
+ struct hdac_codec *codec;
+ struct hdac_command_list *commands;
+ nid_t cad;
+ uint32_t resp;
+ uint8_t rirbwp;
+ int ret = 0;
+
+ rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr;
+ rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
+ bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
+ BUS_DMASYNC_POSTREAD);
+
+ while (sc->rirb_rp != rirbwp) {
+ sc->rirb_rp++;
+ sc->rirb_rp %= sc->rirb_size;
+ rirb = &rirb_base[sc->rirb_rp];
+ cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
+ if (cad < 0 || cad >= HDAC_CODEC_MAX ||
+ sc->codecs[cad] == NULL)
+ continue;
+ resp = rirb->response;
+ codec = sc->codecs[cad];
+ commands = codec->commands;
+ if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
+ sc->unsolq[sc->unsolq_wp++] = (cad << 16) |
+ ((resp >> 26) & 0xffff);
+ sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
+ } else if (commands != NULL && commands->num_commands > 0 &&
+ codec->responses_received < commands->num_commands)
+ commands->responses[codec->responses_received++] =
+ resp;
+ ret++;
+ }
+
+ return (ret);
+}
+
+static int
+hdac_unsolq_flush(struct hdac_softc *sc)
+{
+ nid_t cad;
+ uint32_t tag;
+ int ret = 0;
+
+ if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
+ sc->unsolq_st = HDAC_UNSOLQ_BUSY;
+ while (sc->unsolq_rp != sc->unsolq_wp) {
+ cad = sc->unsolq[sc->unsolq_rp] >> 16;
+ tag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
+ sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
+ hdac_unsolicited_handler(sc->codecs[cad], tag);
+ ret++;
+ }
+ sc->unsolq_st = HDAC_UNSOLQ_READY;
+ }
+
+ return (ret);
+}
+
+static void
+hdac_poll_callback(void *arg)
+{
+ struct hdac_softc *sc = arg;
+ if (sc == NULL)
+ return;
+
+ hdac_lock(sc);
+ if (sc->polling == 0) {
+ hdac_unlock(sc);
+ return;
+ }
+ hdac_rirb_flush(sc);
+ hdac_unsolq_flush(sc);
+ callout_reset(&sc->poll_hdac, max(hz >> 2, 1),
+ hdac_poll_callback, sc);
+ hdac_unlock(sc);
+}
+
static void
hdac_stream_stop(struct hdac_chan *ch)
{
@@ -1918,9 +2052,50 @@ hdac_stream_stop(struct hdac_chan *ch)
HDAC_SDCTL_RUN);
HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
- ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
- ctl &= ~(1 << (ch->off >> 5));
- HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+ ch->active = 0;
+
+ if (sc->polling != 0) {
+ int pollticks;
+
+ if (hda_chan_active(sc) == 0) {
+ callout_stop(&sc->poll_hda);
+ sc->poll_ticks = 1;
+ } else {
+ if (sc->play.active != 0)
+ ch = &sc->play;
+ else
+ ch = &sc->rec;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->b) *
+ sndbuf_getspd(ch->b));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1) {
+ HDA_BOOTVERBOSE(
+ device_printf(sc->dev,
+ "%s: pollticks=%d < 1 !\n",
+ __func__, pollticks);
+ );
+ pollticks = 1;
+ }
+ if (pollticks > sc->poll_ticks) {
+ HDA_BOOTVERBOSE(
+ device_printf(sc->dev,
+ "%s: pollticks %d -> %d\n",
+ __func__, sc->poll_ticks,
+ pollticks);
+ );
+ sc->poll_ticks = pollticks;
+ callout_reset(&sc->poll_hda, 1,
+ hda_poll_callback, sc);
+ }
+ }
+ } else {
+ ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
+ ctl &= ~(1 << (ch->off >> 5));
+ HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+ }
}
static void
@@ -1929,14 +2104,52 @@ hdac_stream_start(struct hdac_chan *ch)
struct hdac_softc *sc = ch->devinfo->codec->sc;
uint32_t ctl;
- ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
- ctl |= 1 << (ch->off >> 5);
- HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+ if (sc->polling != 0) {
+ int pollticks;
- ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
- ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
- HDAC_SDCTL_RUN;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1) {
+ HDA_BOOTVERBOSE(
+ device_printf(sc->dev,
+ "%s: pollticks=%d < 1 !\n",
+ __func__, pollticks);
+ );
+ pollticks = 1;
+ }
+ if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) {
+ HDA_BOOTVERBOSE(
+ if (hda_chan_active(sc) == 0) {
+ device_printf(sc->dev,
+ "%s: pollticks=%d\n",
+ __func__, pollticks);
+ } else {
+ device_printf(sc->dev,
+ "%s: pollticks %d -> %d\n",
+ __func__, sc->poll_ticks,
+ pollticks);
+ }
+ );
+ sc->poll_ticks = pollticks;
+ callout_reset(&sc->poll_hda, 1, hda_poll_callback,
+ sc);
+ }
+ ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
+ ctl |= HDAC_SDCTL_RUN;
+ } else {
+ ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
+ ctl |= 1 << (ch->off >> 5);
+ HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+ ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
+ ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
+ HDAC_SDCTL_RUN;
+ }
HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
+
+ ch->active = 1;
}
static void
@@ -1988,28 +2201,32 @@ static void
hdac_bdl_setup(struct hdac_chan *ch)
{
struct hdac_softc *sc = ch->devinfo->codec->sc;
- uint64_t addr;
- int blks, size, blocksize;
struct hdac_bdle *bdle;
+ uint64_t addr;
+ uint32_t blksz, blkcnt;
int i;
addr = (uint64_t)sndbuf_getbufaddr(ch->b);
- size = sndbuf_getsize(ch->b);
- blocksize = sndbuf_getblksz(ch->b);
- blks = size / blocksize;
- bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr;
+ bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr;
- for (i = 0; i < blks; i++, bdle++) {
+ if (sc->polling != 0) {
+ blksz = ch->blksz * ch->blkcnt;
+ blkcnt = 1;
+ } else {
+ blksz = ch->blksz;
+ blkcnt = ch->blkcnt;
+ }
+
+ for (i = 0; i < blkcnt; i++, bdle++) {
bdle->addrl = (uint32_t)addr;
bdle->addrh = (uint32_t)(addr >> 32);
- bdle->len = blocksize;
- bdle->ioc = 1;
-
- addr += blocksize;
+ bdle->len = blksz;
+ bdle->ioc = 1 ^ sc->polling;
+ addr += blksz;
}
- HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size);
- HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1);
+ HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt);
+ HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1);
addr = ch->bdl_dma.dma_paddr;
HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr);
HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32));
@@ -2046,7 +2263,7 @@ hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid,
v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
(lmute << 7) | left;
hdac_command(sc,
- HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
+ HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
(rmute << 7) | right;
} else
@@ -2142,14 +2359,12 @@ hdac_command_send_internal(struct hdac_softc *sc,
struct hdac_codec *codec;
int corbrp;
uint32_t *corb;
- uint8_t rirbwp;
int timeout;
int retry = 10;
- struct hdac_rirb *rirb_base, *rirb;
- nid_t ucad;
- uint32_t utag;
+ struct hdac_rirb *rirb_base;
- if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL)
+ if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL ||
+ commands->num_commands < 1)
return;
codec = sc->codecs[cad];
@@ -2180,55 +2395,22 @@ hdac_command_send_internal(struct hdac_softc *sc,
}
timeout = 1000;
- do {
- rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
- bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
- BUS_DMASYNC_POSTREAD);
- if (sc->rirb_rp != rirbwp) {
- do {
- sc->rirb_rp++;
- sc->rirb_rp %= sc->rirb_size;
- rirb = &rirb_base[sc->rirb_rp];
- if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
- ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
- utag = rirb->response >> 26;
- if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
- sc->codecs[ucad] != NULL) {
- sc->unsolq[sc->unsolq_wp++] =
- (ucad << 16) |
- (utag & 0xffff);
- sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
- }
- } else if (codec->responses_received < commands->num_commands)
- codec->commands->responses[codec->responses_received++] =
- rirb->response;
- } while (sc->rirb_rp != rirbwp);
- break;
- }
+ while (hdac_rirb_flush(sc) == 0 && --timeout)
DELAY(10);
- } while (--timeout);
} while ((codec->verbs_sent != commands->num_commands ||
- codec->responses_received != commands->num_commands) &&
- --retry);
+ codec->responses_received != commands->num_commands) && --retry);
if (retry == 0)
device_printf(sc->dev,
- "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
- __func__, commands->num_commands,
- codec->verbs_sent, codec->responses_received);
+ "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
+ __func__, commands->num_commands, codec->verbs_sent,
+ codec->responses_received);
+ codec->commands = NULL;
+ codec->responses_received = 0;
codec->verbs_sent = 0;
- if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
- sc->unsolq_st = HDAC_UNSOLQ_BUSY;
- while (sc->unsolq_rp != sc->unsolq_wp) {
- ucad = sc->unsolq[sc->unsolq_rp] >> 16;
- utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
- sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
- hdac_unsolicited_handler(sc->codecs[ucad], utag);
- }
- sc->unsolq_st = HDAC_UNSOLQ_READY;
- }
+ hdac_unsolq_flush(sc);
}
@@ -2352,16 +2534,18 @@ static int
hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct hdac_chan *ch = data;
- uint32_t spd = 0;
+ uint32_t spd = 0, threshold;
int i;
for (i = 0; ch->pcmrates[i] != 0; i++) {
spd = ch->pcmrates[i];
- if (spd >= speed)
+ threshold = spd + ((ch->pcmrates[i + 1] != 0) ?
+ ((ch->pcmrates[i + 1] - spd) >> 1) : 0);
+ if (speed < threshold)
break;
}
- if (spd == 0)
+ if (spd == 0) /* impossible */
ch->spd = 48000;
else
ch->spd = spd;
@@ -2416,11 +2600,25 @@ hdac_stream_setup(struct hdac_chan *ch)
}
static int
-hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
{
struct hdac_chan *ch = data;
+ struct hdac_softc *sc = ch->devinfo->codec->sc;
+
+ blksz &= ~0x7f;
+ if (blksz < 0x80)
+ blksz = 0x80;
- sndbuf_resize(ch->b, ch->blkcnt, ch->blksz);
+ if ((blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->b))
+ blksz = sndbuf_getmaxsize(ch->b) / ch->blkcnt;
+
+ if ((sndbuf_getblksz(ch->b) != blksz ||
+ sndbuf_getblkcnt(ch->b) != ch->blkcnt) &&
+ sndbuf_resize(ch->b, ch->blkcnt, blksz) != 0)
+ device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
+ __func__, blksz, ch->blkcnt);
+
+ ch->blksz = sndbuf_getblksz(ch->b);
return (ch->blksz);
}
@@ -2480,26 +2678,20 @@ hdac_channel_getptr(kobj_t obj, void *data)
{
struct hdac_chan *ch = data;
struct hdac_softc *sc = ch->devinfo->codec->sc;
- int sz, delta;
uint32_t ptr;
hdac_lock(sc);
- ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
+ if (sc->polling != 0)
+ ptr = ch->ptr;
+ else
+ ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
hdac_unlock(sc);
- sz = sndbuf_getsize(ch->b);
- ptr %= sz;
-
- if (ch->dir == PCMDIR_REC) {
- delta = ptr % sndbuf_getblksz(ch->b);
- if (delta != 0) {
- ptr -= delta;
- if (ptr < delta)
- ptr = sz - delta;
- else
- ptr -= delta;
- }
- }
+ /*
+ * Round to available space and force 128 bytes aligment.
+ */
+ ptr %= ch->blksz * ch->blkcnt;
+ ptr &= ~0x7f;
return (ptr);
}
@@ -2872,11 +3064,24 @@ hdac_attach(device_t dev)
sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16;
sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff;
+ callout_init(&sc->poll_hda, CALLOUT_MPSAFE);
+ callout_init(&sc->poll_hdac, CALLOUT_MPSAFE);
+
+ sc->poll_ticks = 1;
+ if (resource_int_value(device_get_name(sc->dev),
+ device_get_unit(sc->dev), "polling", &i) == 0 && i != 0)
+ sc->polling = 1;
+ else
+ sc->polling = 0;
+
sc->chan_size = pcm_getbuffersize(dev,
- HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_DEFAULT);
+ HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX);
+
if (resource_int_value(device_get_name(sc->dev),
- device_get_unit(sc->dev), "blocksize", &i) == 0 &&
- i > 0) {
+ device_get_unit(sc->dev), "blocksize", &i) == 0 && i > 0) {
+ i &= ~0x7f;
+ if (i < 0x80)
+ i = 0x80;
sc->chan_blkcnt = sc->chan_size / i;
i = 0;
while (sc->chan_blkcnt >> i)
@@ -3227,15 +3432,12 @@ static const struct {
uint32_t set, unset;
} hdac_quirks[] = {
/*
- * XXX Fixed rate quirk. Other than 48000
- * sounds pretty much like train wreck.
- *
* XXX Force stereo quirk. Monoural recording / playback
* on few codecs (especially ALC880) seems broken or
* perhaps unsupported.
*/
{ HDA_MATCH_ALL, HDA_MATCH_ALL,
- HDA_QUIRK_FIXEDRATE | HDA_QUIRK_FORCESTEREO, 0 },
+ HDA_QUIRK_FORCESTEREO, 0 },
{ ACER_ALL_SUBVENDOR, HDA_MATCH_ALL,
HDA_QUIRK_GPIO1, 0 },
{ ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880,
@@ -3244,6 +3446,8 @@ static const struct {
HDA_QUIRK_EAPDINV, 0 },
{ ASUS_A8JC_SUBVENDOR, HDA_CODEC_AD1986A,
HDA_QUIRK_EAPDINV, 0 },
+ { LNV_3KN100_SUBVENDOR, HDA_CODEC_AD1986A,
+ HDA_QUIRK_EAPDINV, 0 },
{ HDA_MATCH_ALL, HDA_CODEC_CXVENICE,
0, HDA_QUIRK_FORCESTEREO },
{ HDA_MATCH_ALL, HDA_CODEC_STACXXXX,
@@ -3550,8 +3754,8 @@ hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo,
}
w->ctlflags |= fl;
return (fl);
- } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX
- && HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) &&
+ } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
+ HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) &&
(w->pflags & HDA_ADC_PATH)) {
conndev = w->wclass.pin.config &
HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
@@ -4032,9 +4236,14 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir)
}*/
if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
continue;
+ if (ret == 0) {
+ fmtcap = w->param.supp_stream_formats;
+ pcmcap = w->param.supp_pcm_size_rate;
+ } else {
+ fmtcap &= w->param.supp_stream_formats;
+ pcmcap &= w->param.supp_pcm_size_rate;
+ }
ch->io[ret++] = i;
- fmtcap &= w->param.supp_stream_formats;
- pcmcap &= w->param.supp_pcm_size_rate;
}
ch->io[ret] = -1;
@@ -4498,6 +4707,8 @@ hdac_release_resources(struct hdac_softc *sc)
return;
hdac_lock(sc);
+ if (sc->polling != 0)
+ callout_stop(&sc->poll_hdac);
hdac_reset(sc);
hdac_unlock(sc);
snd_mtxfree(sc->lock);
@@ -4597,6 +4808,65 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off)
}
}
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hdac_polling(SYSCTL_HANDLER_ARGS)
+{
+ struct hdac_softc *sc;
+ struct hdac_devinfo *devinfo;
+ device_t dev;
+ uint32_t ctl;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ devinfo = pcm_getdevinfo(dev);
+ if (devinfo == NULL || devinfo->codec == NULL ||
+ devinfo->codec->sc == NULL)
+ return (EINVAL);
+ sc = devinfo->codec->sc;
+ hdac_lock(sc);
+ val = sc->polling;
+ hdac_unlock(sc);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return (err);
+ if (val < 0 || val > 1)
+ return (EINVAL);
+
+ hdac_lock(sc);
+ if (val != sc->polling) {
+ if (hda_chan_active(sc) != 0)
+ err = EBUSY;
+ else if (val == 0) {
+ callout_stop(&sc->poll_hdac);
+ HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT,
+ sc->rirb_size / 2);
+ ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL);
+ ctl |= HDAC_RIRBCTL_RINTCTL;
+ HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl);
+ HDAC_WRITE_4(&sc->mem, HDAC_INTCTL,
+ HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
+ sc->polling = 0;
+ DELAY(1000);
+ } else {
+ HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, 0);
+ HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, 0);
+ ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL);
+ ctl &= ~HDAC_RIRBCTL_RINTCTL;
+ HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl);
+ callout_reset(&sc->poll_hdac, 1, hdac_poll_callback,
+ sc);
+ sc->polling = 1;
+ DELAY(1000);
+ }
+ }
+ hdac_unlock(sc);
+
+ return (err);
+}
+#endif
+
static void
hdac_attach2(void *arg)
{
@@ -4642,7 +4912,9 @@ hdac_attach2(void *arg)
device_printf(sc->dev,
"HDA_DEBUG: Enabling controller interrupt...\n");
);
- HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
+ if (sc->polling == 0)
+ HDAC_WRITE_4(&sc->mem, HDAC_INTCTL,
+ HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) |
HDAC_GCTL_UNSOL);
@@ -4779,17 +5051,24 @@ hdac_attach2(void *arg)
for (i = 0; i < rcnt; i++)
pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo);
+#ifdef SND_DYNSYSCTL
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
+ "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
+ sysctl_hdac_polling, "I", "Enable polling mode");
+#endif
+
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]",
- rman_get_start(sc->mem.mem_res),
- rman_get_start(sc->irq.irq_res),
- PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV);
+ rman_get_start(sc->mem.mem_res), rman_get_start(sc->irq.irq_res),
+ PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV);
pcm_setstatus(sc->dev, status);
device_printf(sc->dev, "<HDA Codec: %s>\n", hdac_codec_name(devinfo));
HDA_BOOTVERBOSE(
device_printf(sc->dev, "<HDA Codec ID: 0x%08x>\n",
hdac_codec_id(devinfo));
);
- device_printf(sc->dev, "<HDA Driver Revision: %s>\n", HDA_DRV_TEST_REV);
+ device_printf(sc->dev, "<HDA Driver Revision: %s>\n",
+ HDA_DRV_TEST_REV);
HDA_BOOTVERBOSE(
if (devinfo->function.audio.quirks != 0) {
@@ -4844,6 +5123,12 @@ hdac_attach2(void *arg)
device_printf(sc->dev, "+--------------------------------------+\n");
hdac_dump_pcmchannels(sc, pcnt, rcnt);
);
+
+ if (sc->polling != 0) {
+ hdac_lock(sc);
+ callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc);
+ hdac_unlock(sc);
+ }
}
/****************************************************************************
diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h
index a4752ab..eca6cd4 100644
--- a/sys/dev/sound/pci/hda/hdac_private.h
+++ b/sys/dev/sound/pci/hda/hdac_private.h
@@ -266,7 +266,8 @@ struct hdac_chan {
struct hdac_dma bdl_dma;
uint32_t spd, fmt, fmtlist[8], pcmrates[16];
uint32_t supp_stream_formats, supp_pcm_size_rate;
- int ptr, prevptr, blkcnt, blksz;
+ uint32_t ptr, prevptr, blkcnt, blksz;
+ int active;
int dir;
int off;
int sid;
@@ -310,6 +311,14 @@ struct hdac_softc {
int chan_size;
int chan_blkcnt;
+ /*
+ * Polling
+ */
+ int polling;
+ int poll_ticks;
+ struct callout poll_hda;
+ struct callout poll_hdac;
+
#define HDAC_UNSOLQ_MAX 64
#define HDAC_UNSOLQ_READY 0
#define HDAC_UNSOLQ_BUSY 1
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c
index f3401e8..7a99948 100644
--- a/sys/dev/sound/pci/ich.c
+++ b/sys/dev/sound/pci/ich.c
@@ -36,10 +36,14 @@ SND_DECLARE_FILE("$FreeBSD$");
/* -------------------------------------------------------------------- */
-#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */
-#define ICH_DTBL_LENGTH 32
-#define ICH_DEFAULT_BUFSZ 16384
-#define ICH_MAX_BUFSZ 65536
+#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */
+#define ICH_DTBL_LENGTH 32
+#define ICH_DEFAULT_BUFSZ 16384
+#define ICH_MAX_BUFSZ 65536
+#define ICH_MIN_BUFSZ 4096
+#define ICH_DEFAULT_BLKCNT 2
+#define ICH_MAX_BLKCNT 32
+#define ICH_MIN_BLKCNT 2
#define INTEL_VENDORID 0x8086
#define SIS_VENDORID 0x1039
@@ -71,6 +75,14 @@ SND_DECLARE_FILE("$FreeBSD$");
#define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock)
#define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock)
+#if 0
+#define ICH_DEBUG(stmt) do { \
+ stmt \
+} while(0)
+#else
+#define ICH_DEBUG(stmt)
+#endif
+
static const struct ich_type {
uint16_t vendor;
uint16_t devid;
@@ -122,19 +134,19 @@ static const struct ich_type {
/* buffer descriptor */
struct ich_desc {
- volatile u_int32_t buffer;
- volatile u_int32_t length;
+ volatile uint32_t buffer;
+ volatile uint32_t length;
};
struct sc_info;
/* channel registers */
struct sc_chinfo {
- u_int32_t num:8, run:1, run_save:1;
- u_int32_t blksz, blkcnt, spd;
- u_int32_t regbase, spdreg;
- u_int32_t imask;
- u_int32_t civ;
+ uint32_t num:8, run:1, run_save:1;
+ uint32_t blksz, blkcnt, spd;
+ uint32_t regbase, spdreg;
+ uint32_t imask;
+ uint32_t civ;
struct snd_dbuf *buffer;
struct pcm_channel *channel;
@@ -148,8 +160,8 @@ struct sc_chinfo {
struct sc_info {
device_t dev;
int hasvra, hasvrm, hasmic;
- unsigned int chnum, bufsz;
- int sample_size, swap_reg;
+ unsigned int chnum, bufsz, blkcnt;
+ int sample_size, swap_reg, fixedrate;
struct resource *nambar, *nabmbar, *irq;
int regtype, nambarid, nabmbarid, irqid;
@@ -161,7 +173,7 @@ struct sc_info {
struct ac97_info *codec;
struct sc_chinfo ch[3];
- int ac97rate;
+ int ac97rate, calibrated;
struct ich_desc *dtbl;
bus_addr_t desc_addr;
struct intr_config_hook intrhook;
@@ -175,7 +187,7 @@ struct sc_info {
/* -------------------------------------------------------------------- */
-static u_int32_t ich_fmt[] = {
+static uint32_t ich_fmt[] = {
AFMT_STEREO | AFMT_S16_LE,
0
};
@@ -184,23 +196,23 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0};
/* -------------------------------------------------------------------- */
/* Hardware */
-static __inline u_int32_t
+static __inline uint32_t
ich_rd(struct sc_info *sc, int regno, int size)
{
switch (size) {
case 1:
- return bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno);
+ return (bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno));
case 2:
- return bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno);
+ return (bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno));
case 4:
- return bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno);
+ return (bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno));
default:
- return 0xffffffff;
+ return (0xffffffff);
}
}
static __inline void
-ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
+ich_wr(struct sc_info *sc, int regno, uint32_t data, int size)
{
switch (size) {
case 1:
@@ -219,20 +231,20 @@ ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
static int
ich_waitcd(void *devinfo)
{
- int i;
- u_int32_t data;
struct sc_info *sc = (struct sc_info *)devinfo;
+ uint32_t data;
+ int i;
for (i = 0; i < ICH_TIMEOUT; i++) {
data = ich_rd(sc, ICH_REG_ACC_SEMA, 1);
if ((data & 0x01) == 0)
- return 0;
+ return (0);
DELAY(1);
}
if ((sc->flags & IGNORE_PCR) != 0)
return (0);
device_printf(sc->dev, "CODEC semaphore timeout\n");
- return ETIMEDOUT;
+ return (ETIMEDOUT);
}
static int
@@ -243,11 +255,11 @@ ich_rdcd(kobj_t obj, void *devinfo, int regno)
regno &= 0xff;
ich_waitcd(sc);
- return bus_space_read_2(sc->nambart, sc->nambarh, regno);
+ return (bus_space_read_2(sc->nambart, sc->nambarh, regno));
}
static int
-ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
+ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data)
{
struct sc_info *sc = (struct sc_info *)devinfo;
@@ -255,7 +267,7 @@ ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
ich_waitcd(sc);
bus_space_write_2(sc->nambart, sc->nambarh, regno, data);
- return 0;
+ return (0);
}
static kobj_method_t ich_ac97_methods[] = {
@@ -272,13 +284,17 @@ static void
ich_filldtbl(struct sc_chinfo *ch)
{
struct sc_info *sc = ch->parent;
- u_int32_t base;
+ uint32_t base;
int i;
base = sndbuf_getbufaddr(ch->buffer);
- if (ch->blksz > sc->bufsz / ch->blkcnt)
- ch->blksz = sc->bufsz / ch->blkcnt;
- sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz);
+ if ((ch->blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
+ ch->blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
+ if ((sndbuf_getblksz(ch->buffer) != ch->blksz ||
+ sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
+ sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz) != 0)
+ device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
+ __func__, ch->blksz, ch->blkcnt);
ch->blksz = sndbuf_getblksz(ch->buffer);
for (i = 0; i < ICH_DTBL_LENGTH; i++) {
@@ -300,7 +316,7 @@ ich_resetchan(struct sc_info *sc, int num)
else if (num == 2)
regbase = ICH_REG_MC_BASE;
else
- return ENXIO;
+ return (ENXIO);
ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1);
#if 1
@@ -313,11 +329,11 @@ ich_resetchan(struct sc_info *sc, int num)
for (i = 0; i < ICH_TIMEOUT; i++) {
cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1);
if (cr == 0)
- return 0;
+ return (0);
}
device_printf(sc->dev, "cannot reset channel %d\n", num);
- return ENXIO;
+ return (ENXIO);
}
/* -------------------------------------------------------------------- */
@@ -341,58 +357,78 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH);
ch->desc_addr = sc->desc_addr + (ch->num * ICH_DTBL_LENGTH) *
sizeof(struct ich_desc);
- ch->blkcnt = 2;
+ ch->blkcnt = sc->blkcnt;
ch->blksz = sc->bufsz / ch->blkcnt;
switch(ch->num) {
case 0: /* play */
KASSERT(dir == PCMDIR_PLAY, ("wrong direction"));
ch->regbase = ICH_REG_PO_BASE;
- ch->spdreg = sc->hasvra? AC97_REGEXT_FDACRATE : 0;
+ ch->spdreg = (sc->hasvra) ? AC97_REGEXT_FDACRATE : 0;
ch->imask = ICH_GLOB_STA_POINT;
break;
case 1: /* record */
KASSERT(dir == PCMDIR_REC, ("wrong direction"));
ch->regbase = ICH_REG_PI_BASE;
- ch->spdreg = sc->hasvra? AC97_REGEXT_LADCRATE : 0;
+ ch->spdreg = (sc->hasvra) ? AC97_REGEXT_LADCRATE : 0;
ch->imask = ICH_GLOB_STA_PIINT;
break;
case 2: /* mic */
KASSERT(dir == PCMDIR_REC, ("wrong direction"));
ch->regbase = ICH_REG_MC_BASE;
- ch->spdreg = sc->hasvrm? AC97_REGEXT_MADCRATE : 0;
+ ch->spdreg = (sc->hasvrm) ? AC97_REGEXT_MADCRATE : 0;
ch->imask = ICH_GLOB_STA_MINT;
break;
default:
- return NULL;
+ return (NULL);
}
+ if (sc->fixedrate != 0)
+ ch->spdreg = 0;
+
ICH_UNLOCK(sc);
if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0)
- return NULL;
+ return (NULL);
ICH_LOCK(sc);
- ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
+ ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
ICH_UNLOCK(sc);
- return ch;
+ return (ch);
}
static int
-ichchan_setformat(kobj_t obj, void *data, u_int32_t format)
+ichchan_setformat(kobj_t obj, void *data, uint32_t format)
{
- return 0;
+
+ ICH_DEBUG(
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
+ return (0);
}
static int
-ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+ichchan_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
+ ICH_DEBUG(
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
if (ch->spdreg) {
int r, ac97rate;
@@ -403,30 +439,37 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
ICH_UNLOCK(sc);
r = (speed * 48000) / ac97rate;
/*
- * Cast the return value of ac97_setrate() to u_int so that
+ * Cast the return value of ac97_setrate() to uint64 so that
* the math don't overflow into the negative range.
*/
- ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) *
+ ch->spd = ((uint64_t)ac97_setrate(sc->codec, ch->spdreg, r) *
ac97rate) / 48000;
} else {
ch->spd = 48000;
}
- return ch->spd;
+ return (ch->spd);
}
static int
-ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
+ ICH_DEBUG(
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
ch->blksz = blocksize;
ich_filldtbl(ch);
ICH_LOCK(sc);
ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1);
ICH_UNLOCK(sc);
- return ch->blksz;
+ return (ch->blksz);
}
static int
@@ -435,11 +478,18 @@ ichchan_trigger(kobj_t obj, void *data, int go)
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
+ ICH_DEBUG(
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
switch (go) {
case PCMTRIG_START:
ch->run = 1;
ICH_LOCK(sc);
- ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
+ ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1);
ICH_UNLOCK(sc);
break;
@@ -451,7 +501,7 @@ ichchan_trigger(kobj_t obj, void *data, int go)
ch->run = 0;
break;
}
- return 0;
+ return (0);
}
static int
@@ -459,7 +509,14 @@ ichchan_getptr(kobj_t obj, void *data)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
- u_int32_t pos;
+ uint32_t pos;
+
+ ICH_DEBUG(
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
ICH_LOCK(sc);
ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt;
@@ -467,7 +524,7 @@ ichchan_getptr(kobj_t obj, void *data)
pos = ch->civ * ch->blksz;
- return pos;
+ return (pos);
}
static struct pcmchan_caps *
@@ -475,7 +532,16 @@ ichchan_getcaps(kobj_t obj, void *data)
{
struct sc_chinfo *ch = data;
- return ch->spdreg? &ich_vrcaps : &ich_caps;
+ ICH_DEBUG(
+ struct sc_info *sc = ch->parent;
+
+ if (sc->calibrated == 0)
+ device_printf(ch->parent->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
+ return ((ch->spdreg) ? &ich_vrcaps : &ich_caps);
}
static kobj_method_t ichchan_methods[] = {
@@ -498,10 +564,18 @@ ich_intr(void *p)
{
struct sc_info *sc = (struct sc_info *)p;
struct sc_chinfo *ch;
- u_int32_t cbi, lbi, lvi, st, gs;
+ uint32_t cbi, lbi, lvi, st, gs;
int i;
ICH_LOCK(sc);
+
+ ICH_DEBUG(
+ if (sc->calibrated == 0)
+ device_printf(sc->dev,
+ "WARNING: %s() called before calibration!\n",
+ __func__);
+ );
+
gs = ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_IMASK;
if (gs & (ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES)) {
/* Clear resume interrupt(s) - nothing doing with them */
@@ -515,7 +589,7 @@ ich_intr(void *p)
continue;
gs &= ~ch->imask;
st = ich_rd(sc, ch->regbase +
- (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),
+ ((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR),
2);
st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI;
if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) {
@@ -542,7 +616,7 @@ ich_intr(void *p)
}
/* clear status bit */
ich_wr(sc, ch->regbase +
- (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),
+ ((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR),
st, 2);
}
ICH_UNLOCK(sc);
@@ -564,13 +638,26 @@ ich_initsys(struct sc_info* sc)
/* XXX: this should move to a device specific sysctl "dev.pcm.X.yyy"
via device_get_sysctl_*() as discussed on multimedia@ in msg-id
<861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_INT(snd_sysctl_tree(sc->dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)),
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
OID_AUTO, "ac97rate", CTLFLAG_RW,
&sc->ac97rate, 48000,
"AC97 link rate (default = 48000)");
#endif /* SND_DYNSYSCTL */
- return 0;
+ return (0);
+}
+
+static void
+ich_setstatus(struct sc_info *sc)
+{
+ char status[SND_STATUSLEN];
+
+ snprintf(status, SND_STATUSLEN,
+ "at io 0x%lx, 0x%lx irq %ld bufsz %u %s",
+ rman_get_start(sc->nambar), rman_get_start(sc->nabmbar),
+ rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich));
+
+ pcm_setstatus(sc->dev, status);
}
/* -------------------------------------------------------------------- */
@@ -578,16 +665,17 @@ ich_initsys(struct sc_info* sc)
* function of the ac97 codec initialization code (to be investigated).
*/
-static
-void ich_calibrate(void *arg)
+static void
+ich_calibrate(void *arg)
{
struct sc_info *sc;
struct sc_chinfo *ch;
struct timeval t1, t2;
- u_int8_t ociv, nciv;
- u_int32_t wait_us, actual_48k_rate, bytes;
+ uint8_t ociv, nciv;
+ uint32_t wait_us, actual_48k_rate, oblkcnt;
sc = (struct sc_info *)arg;
+ ICH_LOCK(sc);
ch = &sc->ch[1];
if (sc->use_intrhook)
@@ -602,8 +690,13 @@ void ich_calibrate(void *arg)
KASSERT(ch->regbase == ICH_REG_PI_BASE, ("wrong direction"));
- bytes = sndbuf_getsize(ch->buffer) / 2;
- ichchan_setblocksize(0, ch, bytes);
+ oblkcnt = ch->blkcnt;
+ ch->blkcnt = 2;
+ sc->calibrated = 1;
+ ICH_UNLOCK(sc);
+ ichchan_setblocksize(0, ch, sndbuf_getmaxsize(ch->buffer) >> 1);
+ ICH_LOCK(sc);
+ sc->calibrated = 0;
/*
* our data format is stereo, 16 bit so each sample is 4 bytes.
@@ -620,20 +713,19 @@ void ich_calibrate(void *arg)
/* prepare */
ociv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1);
nciv = ociv;
- ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
+ ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
/* start */
microtime(&t1);
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM, 1);
/* wait */
- while (nciv == ociv) {
+ do {
microtime(&t2);
if (t2.tv_sec - t1.tv_sec > 1)
break;
nciv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1);
- }
- microtime(&t2);
+ } while (nciv == ociv);
/* stop */
ich_wr(sc, ch->regbase + ICH_REG_X_CR, 0, 1);
@@ -641,16 +733,20 @@ void ich_calibrate(void *arg)
/* reset */
DELAY(100);
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1);
+ ch->blkcnt = oblkcnt;
/* turn time delta into us */
wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec;
if (nciv == ociv) {
device_printf(sc->dev, "ac97 link rate calibration timed out after %d us\n", wait_us);
+ sc->calibrated = 1;
+ ICH_UNLOCK(sc);
+ ich_setstatus(sc);
return;
}
- actual_48k_rate = (bytes * 250000) / wait_us;
+ actual_48k_rate = ((uint64_t)ch->blksz * 250000) / wait_us;
if (actual_48k_rate < 47500 || actual_48k_rate > 48500) {
sc->ac97rate = actual_48k_rate;
@@ -664,6 +760,10 @@ void ich_calibrate(void *arg)
printf(", will use %d Hz", sc->ac97rate);
printf("\n");
}
+ sc->calibrated = 1;
+ ICH_UNLOCK(sc);
+
+ ich_setstatus(sc);
return;
}
@@ -682,7 +782,7 @@ ich_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
static int
ich_init(struct sc_info *sc)
{
- u_int32_t stat;
+ uint32_t stat;
ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4);
DELAY(600000);
@@ -706,11 +806,11 @@ ich_init(struct sc_info *sc)
#endif
if (ich_resetchan(sc, 0) || ich_resetchan(sc, 1))
- return ENXIO;
+ return (ENXIO);
if (sc->hasmic && ich_resetchan(sc, 2))
- return ENXIO;
+ return (ENXIO);
- return 0;
+ return (0);
}
static int
@@ -738,14 +838,14 @@ static int
ich_pci_attach(device_t dev)
{
uint32_t subdev;
- u_int16_t extcaps;
+ uint16_t extcaps;
uint16_t devid, vendor;
struct sc_info *sc;
- char status[SND_STATUSLEN];
+ int i;
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
- return ENXIO;
+ return (ENXIO);
}
sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
@@ -807,7 +907,30 @@ ich_pci_attach(device_t dev)
sc->nabmbart = rman_get_bustag(sc->nabmbar);
sc->nabmbarh = rman_get_bushandle(sc->nabmbar);
- sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ);
+ sc->bufsz = pcm_getbuffersize(dev,
+ ICH_MIN_BUFSZ, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ);
+
+ if (resource_int_value(device_get_name(sc->dev),
+ device_get_unit(sc->dev), "blocksize", &i) == 0 && i > 0) {
+ sc->blkcnt = sc->bufsz / i;
+ i = 0;
+ while (sc->blkcnt >> i)
+ i++;
+ sc->blkcnt = 1 << (i - 1);
+ if (sc->blkcnt < ICH_MIN_BLKCNT)
+ sc->blkcnt = ICH_MIN_BLKCNT;
+ else if (sc->blkcnt > ICH_MAX_BLKCNT)
+ sc->blkcnt = ICH_MAX_BLKCNT;
+ } else
+ sc->blkcnt = ICH_DEFAULT_BLKCNT;
+
+ if (resource_int_value(device_get_name(sc->dev),
+ device_get_unit(sc->dev), "fixedrate", &i) == 0 &&
+ i != 0)
+ sc->fixedrate = 1;
+ else
+ sc->fixedrate = 0;
+
if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
NULL, NULL, sc->bufsz, 1, 0x3ffff, 0,
NULL, NULL, &sc->dmat) != 0) {
@@ -854,6 +977,8 @@ ich_pci_attach(device_t dev)
case 0x81c0104d: /* Sony VAIO type T */
case 0x81c5104d: /* Sony VAIO VGN B1VP/B1XP */
case 0x3089103c: /* Compaq Presario B3800 */
+ case 0x82131033: /* NEC VersaPro VJ10F/BH */
+ case 0x82be1033: /* NEC VersaPro VJ12F/CH */
ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
break;
default:
@@ -869,7 +994,7 @@ ich_pci_attach(device_t dev)
sc->hasmic = ac97_getcaps(sc->codec) & AC97_CAP_MICCHANNEL;
ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm);
- if (pcm_register(dev, sc, 1, sc->hasmic? 2 : 1))
+ if (pcm_register(dev, sc, 1, (sc->hasmic) ? 2 : 1))
goto bad;
pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); /* play */
@@ -877,23 +1002,23 @@ ich_pci_attach(device_t dev)
if (sc->hasmic)
pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record mic */
- snprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld bufsz %u %s",
- rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich));
+ if (sc->fixedrate == 0) {
+ ich_initsys(sc);
- pcm_setstatus(dev, status);
-
- ich_initsys(sc);
-
- sc->intrhook.ich_func = ich_calibrate;
- sc->intrhook.ich_arg = sc;
- sc->use_intrhook = 1;
- if (config_intrhook_establish(&sc->intrhook) != 0) {
- device_printf(dev, "Cannot establish calibration hook, will calibrate now\n");
- sc->use_intrhook = 0;
- ich_calibrate(sc);
+ sc->intrhook.ich_func = ich_calibrate;
+ sc->intrhook.ich_arg = sc;
+ sc->use_intrhook = 1;
+ if (config_intrhook_establish(&sc->intrhook) != 0) {
+ device_printf(dev, "Cannot establish calibration hook, will calibrate now\n");
+ sc->use_intrhook = 0;
+ ich_calibrate(sc);
+ }
+ } else {
+ sc->calibrated = 1;
+ ich_setstatus(sc);
}
- return 0;
+ return (0);
bad:
if (sc->codec)
@@ -915,7 +1040,7 @@ bad:
if (sc->ich_lock)
snd_mtxfree(sc->ich_lock);
free(sc, M_DEVBUF);
- return ENXIO;
+ return (ENXIO);
}
static int
@@ -926,7 +1051,7 @@ ich_pci_detach(device_t dev)
r = pcm_unregister(dev);
if (r)
- return r;
+ return (r);
sc = pcm_getdevinfo(dev);
bus_teardown_intr(dev, sc->irq, sc->ih);
@@ -937,7 +1062,7 @@ ich_pci_detach(device_t dev)
bus_dma_tag_destroy(sc->dmat);
snd_mtxfree(sc->ich_lock);
free(sc, M_DEVBUF);
- return 0;
+ return (0);
}
static void
@@ -979,7 +1104,7 @@ ich_pci_suspend(device_t dev)
}
}
ICH_UNLOCK(sc);
- return 0;
+ return (0);
}
static int
@@ -1001,7 +1126,7 @@ ich_pci_resume(device_t dev)
if (ich_init(sc) == -1) {
device_printf(dev, "unable to reinitialize the card\n");
ICH_UNLOCK(sc);
- return ENXIO;
+ return (ENXIO);
}
/* Reinit mixer */
ich_pci_codec_reset(sc);
@@ -1009,7 +1134,7 @@ ich_pci_resume(device_t dev)
ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm);
if (mixer_reinit(dev) == -1) {
device_printf(dev, "unable to reinitialize the mixer\n");
- return ENXIO;
+ return (ENXIO);
}
/* Re-start DMA engines */
for (i = 0 ; i < 3; i++) {
@@ -1020,7 +1145,7 @@ ich_pci_resume(device_t dev)
ichchan_trigger(0, ch, PCMTRIG_START);
}
}
- return 0;
+ return (0);
}
static device_method_t ich_methods[] = {
diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c
index d353b51..c1f0578 100644
--- a/sys/dev/sound/pci/via8233.c
+++ b/sys/dev/sound/pci/via8233.c
@@ -32,7 +32,7 @@
* Grzybowski Rafal, Russell Davies, Mark Handley, Daniel O'Connor for
* comments, machine time, testing patches, and patience. VIA for
* providing specs. ALSA for helpful comments and some register poke
- * ordering.
+ * ordering.
*/
#include <dev/sound/pcm/sound.h>
@@ -62,13 +62,16 @@ SND_DECLARE_FILE("$FreeBSD$");
#define NWRCHANS 1 /* No of write channels */
#define NCHANS (NWRCHANS + NDXSCHANS + NMSGDCHANS)
#define NSEGS NCHANS * SEGS_PER_CHAN /* Segments in SGD table */
+#define VIA_SEGS_MIN 2
+#define VIA_SEGS_MAX 128
+#define VIA_SEGS_DEFAULT 2
#define VIA_DEFAULT_BUFSZ 0x1000
/* we rely on this struct being packed to 64 bits */
struct via_dma_op {
- volatile u_int32_t ptr;
- volatile u_int32_t flags;
+ volatile uint32_t ptr;
+ volatile uint32_t flags;
#define VIA_DMAOP_EOL 0x80000000
#define VIA_DMAOP_FLAG 0x40000000
#define VIA_DMAOP_STOP 0x20000000
@@ -83,8 +86,9 @@ struct via_chinfo {
struct snd_dbuf *buffer;
struct via_dma_op *sgd_table;
bus_addr_t sgd_addr;
- int dir, blksz;
- int rbase;
+ int dir, rbase, active;
+ unsigned int blksz, blkcnt;
+ unsigned int ptr, prevptr;
};
struct via_info {
@@ -100,18 +104,20 @@ struct via_info {
void *ih;
struct ac97_info *codec;
- unsigned int bufsz;
+ unsigned int bufsz, blkcnt;
int dxs_src, dma_eol_wake;
struct via_chinfo pch[NDXSCHANS + NMSGDCHANS];
struct via_chinfo rch[NWRCHANS];
struct via_dma_op *sgd_table;
- u_int16_t codec_caps;
- u_int16_t n_dxs_registered;
+ uint16_t codec_caps;
+ uint16_t n_dxs_registered;
struct mtx *lock;
+ struct callout poll_timer;
+ int poll_ticks, polling;
};
-static u_int32_t via_fmt[] = {
+static uint32_t via_fmt[] = {
AFMT_U8,
AFMT_STEREO | AFMT_U8,
AFMT_S16_LE,
@@ -122,6 +128,23 @@ static u_int32_t via_fmt[] = {
static struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 };
static struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 };
+static __inline int
+via_chan_active(struct via_info *via)
+{
+ int i, ret = 0;
+
+ if (via == NULL)
+ return (0);
+
+ for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++)
+ ret += via->pch[i].active;
+
+ for (i = 0; i < NWRCHANS; i++)
+ ret += via->rch[i].active;
+
+ return (ret);
+}
+
#ifdef SND_DYNSYSCTL
static int
sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
@@ -140,9 +163,9 @@ sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
if (err || req->newptr == NULL)
- return err;
+ return (err);
if (new_en < 0 || new_en > 1)
- return EINVAL;
+ return (EINVAL);
if (new_en)
r |= VIA_SPDIF_EN;
@@ -152,7 +175,7 @@ sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
}
static int
@@ -170,15 +193,50 @@ sysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS)
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
if (err || req->newptr == NULL)
- return err;
+ return (err);
if (val < 0 || val > 1)
- return EINVAL;
+ return (EINVAL);
snd_mtxlock(via->lock);
via->dxs_src = val;
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
+}
+
+static int
+sysctl_via_polling(SYSCTL_HANDLER_ARGS)
+{
+ struct via_info *via;
+ device_t dev;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ via = pcm_getdevinfo(dev);
+ if (via == NULL)
+ return (EINVAL);
+ snd_mtxlock(via->lock);
+ val = via->polling;
+ snd_mtxunlock(via->lock);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return (err);
+ if (val < 0 || val > 1)
+ return (EINVAL);
+
+ snd_mtxlock(via->lock);
+ if (val != via->polling) {
+ if (via_chan_active(via) != 0)
+ err = EBUSY;
+ else if (val == 0)
+ via->polling = 0;
+ else
+ via->polling = 1;
+ }
+ snd_mtxunlock(via->lock);
+
+ return (err);
}
#endif /* SND_DYNSYSCTL */
@@ -190,38 +248,41 @@ via_init_sysctls(device_t dev)
if not done before 7.0-RELEASE, this needs to be converted to
a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*()
as discussed on multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_spdif_enabled",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_via8233_spdif_enable, "I",
- "Enable S/PDIF output on primary playback channel");
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_via_dxs_src",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_via8233_dxs_src, "I",
- "Enable VIA DXS Sample Rate Converter");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "spdif_enabled", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_via8233_spdif_enable, "I",
+ "Enable S/PDIF output on primary playback channel");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "dxs_src", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_via8233_dxs_src, "I",
+ "Enable VIA DXS Sample Rate Converter");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_via_polling, "I",
+ "Enable polling mode");
#endif
}
-static __inline u_int32_t
+static __inline uint32_t
via_rd(struct via_info *via, int regno, int size)
{
switch (size) {
case 1:
- return bus_space_read_1(via->st, via->sh, regno);
+ return (bus_space_read_1(via->st, via->sh, regno));
case 2:
- return bus_space_read_2(via->st, via->sh, regno);
+ return (bus_space_read_2(via->st, via->sh, regno));
case 4:
- return bus_space_read_4(via->st, via->sh, regno);
+ return (bus_space_read_4(via->st, via->sh, regno));
default:
- return 0xFFFFFFFF;
+ return (0xFFFFFFFF);
}
}
static __inline void
-via_wr(struct via_info *via, int regno, u_int32_t data, int size)
+via_wr(struct via_info *via, int regno, uint32_t data, int size)
{
switch (size) {
@@ -248,11 +309,11 @@ via_waitready_codec(struct via_info *via)
/* poll until codec not busy */
for (i = 0; i < 1000; i++) {
if ((via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_BUSY) == 0)
- return 0;
+ return (0);
DELAY(1);
}
printf("via: codec busy\n");
- return 1;
+ return (1);
}
static int
@@ -263,25 +324,26 @@ via_waitvalid_codec(struct via_info *via)
/* poll until codec valid */
for (i = 0; i < 1000; i++) {
if (via_rd(via, VIA_AC97_CONTROL, 4) & VIA_AC97_CODEC00_VALID)
- return 0;
+ return (0);
DELAY(1);
}
printf("via: codec invalid\n");
- return 1;
+ return (1);
}
static int
-via_write_codec(kobj_t obj, void *addr, int reg, u_int32_t val)
+via_write_codec(kobj_t obj, void *addr, int reg, uint32_t val)
{
struct via_info *via = addr;
- if (via_waitready_codec(via)) return -1;
+ if (via_waitready_codec(via))
+ return (-1);
- via_wr(via, VIA_AC97_CONTROL,
+ via_wr(via, VIA_AC97_CONTROL,
VIA_AC97_CODEC00_VALID | VIA_AC97_INDEX(reg) |
VIA_AC97_DATA(val), 4);
- return 0;
+ return (0);
}
static int
@@ -290,23 +352,23 @@ via_read_codec(kobj_t obj, void *addr, int reg)
struct via_info *via = addr;
if (via_waitready_codec(via))
- return -1;
+ return (-1);
- via_wr(via, VIA_AC97_CONTROL, VIA_AC97_CODEC00_VALID |
- VIA_AC97_READ | VIA_AC97_INDEX(reg), 4);
+ via_wr(via, VIA_AC97_CONTROL, VIA_AC97_CODEC00_VALID |
+ VIA_AC97_READ | VIA_AC97_INDEX(reg), 4);
if (via_waitready_codec(via))
- return -1;
+ return (-1);
if (via_waitvalid_codec(via))
- return -1;
+ return (-1);
- return via_rd(via, VIA_AC97_CONTROL, 2);
+ return (via_rd(via, VIA_AC97_CONTROL, 2));
}
static kobj_method_t via_ac97_methods[] = {
- KOBJMETHOD(ac97_read, via_read_codec),
- KOBJMETHOD(ac97_write, via_write_codec),
+ KOBJMETHOD(ac97_read, via_read_codec),
+ KOBJMETHOD(ac97_write, via_write_codec),
{ 0, 0 }
};
AC97_DECLARE(via_ac97);
@@ -316,31 +378,30 @@ AC97_DECLARE(via_ac97);
static int
via_buildsgdt(struct via_chinfo *ch)
{
- u_int32_t phys_addr, flag;
- int i, seg_size;
+ uint32_t phys_addr, flag;
+ int i;
- seg_size = sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN;
phys_addr = sndbuf_getbufaddr(ch->buffer);
- for (i = 0; i < SEGS_PER_CHAN; i++) {
- flag = (i == SEGS_PER_CHAN - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
- ch->sgd_table[i].ptr = phys_addr + (i * seg_size);
- ch->sgd_table[i].flags = flag | seg_size;
+ for (i = 0; i < ch->blkcnt; i++) {
+ flag = (i == ch->blkcnt - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
+ ch->sgd_table[i].ptr = phys_addr + (i * ch->blksz);
+ ch->sgd_table[i].flags = flag | ch->blksz;
}
- return 0;
+ return (0);
}
/* -------------------------------------------------------------------- */
/* Format setting functions */
static int
-via8233wr_setformat(kobj_t obj, void *data, u_int32_t format)
+via8233wr_setformat(kobj_t obj, void *data, uint32_t format)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t f = WR_FORMAT_STOP_INDEX;
+ uint32_t f = WR_FORMAT_STOP_INDEX;
if (format & AFMT_STEREO)
f |= WR_FORMAT_STEREO;
@@ -350,15 +411,15 @@ via8233wr_setformat(kobj_t obj, void *data, u_int32_t format)
via_wr(via, VIA_WR0_FORMAT, f, 4);
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
}
static int
-via8233dxs_setformat(kobj_t obj, void *data, u_int32_t format)
+via8233dxs_setformat(kobj_t obj, void *data, uint32_t format)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t r, v;
+ uint32_t r, v;
r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
snd_mtxlock(via->lock);
@@ -367,22 +428,22 @@ via8233dxs_setformat(kobj_t obj, void *data, u_int32_t format)
v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT);
if (format & AFMT_STEREO)
v |= VIA8233_DXS_RATEFMT_STEREO;
- if (format & AFMT_16BIT)
+ if (format & AFMT_16BIT)
v |= VIA8233_DXS_RATEFMT_16BIT;
via_wr(via, r, v, 4);
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
}
static int
-via8233msgd_setformat(kobj_t obj, void *data, u_int32_t format)
+via8233msgd_setformat(kobj_t obj, void *data, uint32_t format)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t s = 0xff000000;
- u_int8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT;
+ uint32_t s = 0xff000000;
+ uint8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT;
if (format & AFMT_STEREO) {
v |= MC_SGD_CHANNELS(2);
@@ -397,30 +458,30 @@ via8233msgd_setformat(kobj_t obj, void *data, u_int32_t format)
via_wr(via, VIA_MC_SGD_FORMAT, v, 1);
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
}
/* -------------------------------------------------------------------- */
/* Speed setting functions */
static int
-via8233wr_setspeed(kobj_t obj, void *data, u_int32_t speed)
+via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
if (via->codec_caps & AC97_EXTCAP_VRA)
- return ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed);
+ return (ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed));
- return 48000;
+ return (48000);
}
static int
-via8233dxs_setspeed(kobj_t obj, void *data, u_int32_t speed)
+via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t r, v;
+ uint32_t r, v;
r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
snd_mtxlock(via->lock);
@@ -432,19 +493,19 @@ via8233dxs_setspeed(kobj_t obj, void *data, u_int32_t speed)
via_wr(via, r, v, 4);
snd_mtxunlock(via->lock);
- return speed;
+ return (speed);
}
static int
-via8233msgd_setspeed(kobj_t obj, void *data, u_int32_t speed)
+via8233msgd_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
if (via->codec_caps & AC97_EXTCAP_VRA)
- return ac97_setrate(via->codec, AC97_REGEXT_FDACRATE, speed);
+ return (ac97_setrate(via->codec, AC97_REGEXT_FDACRATE, speed));
- return 48000;
+ return (48000);
}
/* -------------------------------------------------------------------- */
@@ -458,8 +519,8 @@ via8233wr_getcaps(kobj_t obj, void *data)
/* Controlled by ac97 registers */
if (via->codec_caps & AC97_EXTCAP_VRA)
- return &via_vracaps;
- return &via_caps;
+ return (&via_vracaps);
+ return (&via_caps);
}
static struct pcmchan_caps *
@@ -475,8 +536,8 @@ via8233dxs_getcaps(kobj_t obj, void *data)
* conversion.
*/
if (via->dxs_src)
- return &via_vracaps;
- return &via_caps;
+ return (&via_vracaps);
+ return (&via_caps);
}
static struct pcmchan_caps *
@@ -487,21 +548,30 @@ via8233msgd_getcaps(kobj_t obj, void *data)
/* Controlled by ac97 registers */
if (via->codec_caps & AC97_EXTCAP_VRA)
- return &via_vracaps;
- return &via_caps;
+ return (&via_vracaps);
+ return (&via_caps);
}
/* -------------------------------------------------------------------- */
/* Common functions */
static int
-via8233chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
{
struct via_chinfo *ch = data;
- sndbuf_resize(ch->buffer, SEGS_PER_CHAN, blocksize);
+ if ((blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
+ blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
+
+ if ((sndbuf_getblksz(ch->buffer) != blksz ||
+ sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
+ sndbuf_resize(ch->buffer, ch->blkcnt, blksz) != 0)
+ printf("via: %s: failed blksz=%u blkcnt=%u\n",
+ __func__, blksz, ch->blkcnt);
+
ch->blksz = sndbuf_getblksz(ch->buffer);
- return ch->blksz;
+
+ return (ch->blksz);
}
static int
@@ -509,18 +579,23 @@ via8233chan_getptr(kobj_t obj, void *data)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t v, index, count;
+ uint32_t v, index, count;
int ptr;
snd_mtxlock(via->lock);
- v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
- snd_mtxunlock(via->lock);
- index = v >> 24; /* Last completed buffer */
- count = v & 0x00ffffff; /* Bytes remaining */
- ptr = (index + 1) * ch->blksz - count;
- ptr %= SEGS_PER_CHAN * ch->blksz; /* Wrap to available space */
+ if (via->polling != 0) {
+ ptr = ch->ptr;
+ snd_mtxunlock(via->lock);
+ } else {
+ v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
+ snd_mtxunlock(via->lock);
+ index = v >> 24; /* Last completed buffer */
+ count = v & 0x00ffffff; /* Bytes remaining */
+ ptr = (index + 1) * ch->blksz - count;
+ ptr %= ch->blkcnt * ch->blksz; /* Wrap to available space */
+ }
- return ptr;
+ return (ptr);
}
static void
@@ -528,8 +603,8 @@ via8233chan_reset(struct via_info *via, struct via_chinfo *ch)
{
via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1);
via_wr(via, ch->rbase + VIA_RP_CONTROL, 0x00, 1);
- via_wr(via, ch->rbase + VIA_RP_STATUS,
- SGD_STATUS_EOL | SGD_STATUS_FLAG, 1);
+ via_wr(via, ch->rbase + VIA_RP_STATUS,
+ SGD_STATUS_EOL | SGD_STATUS_FLAG, 1);
}
/* -------------------------------------------------------------------- */
@@ -538,13 +613,14 @@ via8233chan_reset(struct via_info *via, struct via_chinfo *ch)
static void
via8233chan_sgdinit(struct via_info *via, struct via_chinfo *ch, int chnum)
{
- ch->sgd_table = &via->sgd_table[chnum * SEGS_PER_CHAN];
- ch->sgd_addr = via->sgd_addr + chnum * SEGS_PER_CHAN * sizeof(struct via_dma_op);
+ ch->sgd_table = &via->sgd_table[chnum * via->blkcnt];
+ ch->sgd_addr = via->sgd_addr + chnum * via->blkcnt *
+ sizeof(struct via_dma_op);
}
static void*
via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
- struct pcm_channel *c, int dir)
+ struct pcm_channel *c, int dir)
{
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->rch[c->num];
@@ -553,6 +629,7 @@ via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
+ ch->blkcnt = via->blkcnt;
ch->rbase = VIA_WR_BASE(c->num);
snd_mtxlock(via->lock);
@@ -560,19 +637,19 @@ via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
- return NULL;
+ return (NULL);
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
- return ch;
+ return (ch);
}
static void*
via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
- struct pcm_channel *c, int dir)
+ struct pcm_channel *c, int dir)
{
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->pch[c->num];
@@ -581,6 +658,7 @@ via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
+ ch->blkcnt = via->blkcnt;
/*
* All cards apparently support DXS3, but not other DXS
@@ -593,19 +671,19 @@ via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
- return NULL;
+ return (NULL);
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
- return ch;
+ return (ch);
}
static void*
via8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
- struct pcm_channel *c, int dir)
+ struct pcm_channel *c, int dir)
{
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->pch[c->num];
@@ -615,16 +693,17 @@ via8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->buffer = b;
ch->dir = dir;
ch->rbase = VIA_MC_SGD_STATUS;
+ ch->blkcnt = via->blkcnt;
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
- return NULL;
+ return (NULL);
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
- return ch;
+ return (ch);
}
static void
@@ -635,7 +714,8 @@ via8233chan_mute(struct via_info *via, struct via_chinfo *ch, int muted)
muted = (muted) ? VIA8233_DXS_MUTE : 0;
via_wr(via, ch->rbase + VIA8233_RP_DXS_LVOL, muted, 1);
via_wr(via, ch->rbase + VIA8233_RP_DXS_RVOL, muted, 1);
- r = via_rd(via, ch->rbase + VIA8233_RP_DXS_LVOL, 1) & VIA8233_DXS_MUTE;
+ r = via_rd(via, ch->rbase + VIA8233_RP_DXS_LVOL, 1) &
+ VIA8233_DXS_MUTE;
if (r != muted) {
printf("via: failed to set dxs volume "
"(dxs base 0x%02x).\n", ch->rbase);
@@ -643,11 +723,125 @@ via8233chan_mute(struct via_info *via, struct via_chinfo *ch, int muted)
}
}
+static __inline int
+via_poll_channel(struct via_chinfo *ch)
+{
+ struct via_info *via;
+ uint32_t sz, delta;
+ uint32_t v, index, count;
+ int ptr;
+
+ if (ch == NULL || ch->channel == NULL || ch->active == 0)
+ return (0);
+
+ via = ch->parent;
+ sz = ch->blksz * ch->blkcnt;
+ v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
+ index = v >> 24;
+ count = v & 0x00ffffff;
+ ptr = ((index + 1) * ch->blksz) - count;
+ ptr %= sz;
+ ptr &= ~(ch->blksz - 1);
+ ch->ptr = ptr;
+ delta = (sz + ptr - ch->prevptr) % sz;
+
+ if (delta < ch->blksz)
+ return (0);
+
+ ch->prevptr = ptr;
+
+ return (1);
+}
+
+static void
+via_poll_callback(void *arg)
+{
+ struct via_info *via = arg;
+ uint32_t ptrigger = 0, rtrigger = 0;
+ int i;
+
+ if (via == NULL)
+ return;
+
+ snd_mtxlock(via->lock);
+ if (via->polling == 0 || via_chan_active(via) == 0) {
+ snd_mtxunlock(via->lock);
+ return;
+ }
+
+ for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++)
+ ptrigger |= (via_poll_channel(&via->pch[i]) != 0) ?
+ (1 << i) : 0;
+
+ for (i = 0; i < NWRCHANS; i++)
+ rtrigger |= (via_poll_channel(&via->rch[i]) != 0) ?
+ (1 << i) : 0;
+
+ /* XXX */
+ callout_reset(&via->poll_timer, 1/*via->poll_ticks*/,
+ via_poll_callback, via);
+
+ snd_mtxunlock(via->lock);
+
+ for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
+ if (ptrigger & (1 << i))
+ chn_intr(via->pch[i].channel);
+ }
+ for (i = 0; i < NWRCHANS; i++) {
+ if (rtrigger & (1 << i))
+ chn_intr(via->rch[i].channel);
+ }
+}
+
+static int
+via_poll_ticks(struct via_info *via)
+{
+ struct via_chinfo *ch;
+ int i;
+ int ret = hz;
+ int pollticks;
+
+ for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
+ ch = &via->pch[i];
+ if (ch->channel == NULL || ch->active == 0)
+ continue;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->buffer) *
+ sndbuf_getspd(ch->buffer));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1)
+ pollticks = 1;
+ if (pollticks < ret)
+ ret = pollticks;
+ }
+
+ for (i = 0; i < NWRCHANS; i++) {
+ ch = &via->rch[i];
+ if (ch->channel == NULL || ch->active == 0)
+ continue;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->buffer) *
+ sndbuf_getspd(ch->buffer));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1)
+ pollticks = 1;
+ if (pollticks < ret)
+ ret = pollticks;
+ }
+
+ return (ret);
+}
+
static int
via8233chan_trigger(kobj_t obj, void* data, int go)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
+ int pollticks;
snd_mtxlock(via->lock);
switch(go) {
@@ -655,53 +849,105 @@ via8233chan_trigger(kobj_t obj, void* data, int go)
via_buildsgdt(ch);
via8233chan_mute(via, ch, 0);
via_wr(via, ch->rbase + VIA_RP_TABLE_PTR, ch->sgd_addr, 4);
+ if (via->polling != 0) {
+ ch->ptr = 0;
+ ch->prevptr = 0;
+ pollticks = ((uint64_t)hz * ch->blksz) /
+ ((uint64_t)sndbuf_getbps(ch->buffer) *
+ sndbuf_getspd(ch->buffer));
+ pollticks >>= 2;
+ if (pollticks > hz)
+ pollticks = hz;
+ if (pollticks < 1)
+ pollticks = 1;
+ if (via_chan_active(via) == 0 ||
+ pollticks < via->poll_ticks) {
+ if (bootverbose) {
+ if (via_chan_active(via) == 0)
+ printf("%s: pollticks=%d\n",
+ __func__, pollticks);
+ else
+ printf("%s: "
+ "pollticks %d -> %d\n",
+ __func__, via->poll_ticks,
+ pollticks);
+ }
+ via->poll_ticks = pollticks;
+ callout_reset(&via->poll_timer, 1,
+ via_poll_callback, via);
+ }
+ }
via_wr(via, ch->rbase + VIA_RP_CONTROL,
- SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
- SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1);
+ SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
+ ((via->polling == 0) ?
+ (SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG) : 0), 1);
+ ch->active = 1;
break;
case PCMTRIG_STOP:
case PCMTRIG_ABORT:
via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1);
via8233chan_mute(via, ch, 1);
via8233chan_reset(via, ch);
+ ch->active = 0;
+ if (via->polling != 0) {
+ if (via_chan_active(via) == 0) {
+ callout_stop(&via->poll_timer);
+ via->poll_ticks = 1;
+ } else {
+ pollticks = via_poll_ticks(via);
+ if (pollticks > via->poll_ticks) {
+ if (bootverbose)
+ printf("%s: pollticks "
+ "%d -> %d\n",
+ __func__, via->poll_ticks,
+ pollticks);
+ via->poll_ticks = pollticks;
+ callout_reset(&via->poll_timer,
+ 1, via_poll_callback,
+ via);
+ }
+ }
+ }
+ break;
+ default:
break;
}
snd_mtxunlock(via->lock);
- return 0;
+ return (0);
}
static kobj_method_t via8233wr_methods[] = {
- KOBJMETHOD(channel_init, via8233wr_init),
- KOBJMETHOD(channel_setformat, via8233wr_setformat),
- KOBJMETHOD(channel_setspeed, via8233wr_setspeed),
- KOBJMETHOD(channel_getcaps, via8233wr_getcaps),
- KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
- KOBJMETHOD(channel_trigger, via8233chan_trigger),
- KOBJMETHOD(channel_getptr, via8233chan_getptr),
+ KOBJMETHOD(channel_init, via8233wr_init),
+ KOBJMETHOD(channel_setformat, via8233wr_setformat),
+ KOBJMETHOD(channel_setspeed, via8233wr_setspeed),
+ KOBJMETHOD(channel_getcaps, via8233wr_getcaps),
+ KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
+ KOBJMETHOD(channel_trigger, via8233chan_trigger),
+ KOBJMETHOD(channel_getptr, via8233chan_getptr),
{ 0, 0 }
};
CHANNEL_DECLARE(via8233wr);
static kobj_method_t via8233dxs_methods[] = {
- KOBJMETHOD(channel_init, via8233dxs_init),
- KOBJMETHOD(channel_setformat, via8233dxs_setformat),
- KOBJMETHOD(channel_setspeed, via8233dxs_setspeed),
- KOBJMETHOD(channel_getcaps, via8233dxs_getcaps),
- KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
- KOBJMETHOD(channel_trigger, via8233chan_trigger),
- KOBJMETHOD(channel_getptr, via8233chan_getptr),
+ KOBJMETHOD(channel_init, via8233dxs_init),
+ KOBJMETHOD(channel_setformat, via8233dxs_setformat),
+ KOBJMETHOD(channel_setspeed, via8233dxs_setspeed),
+ KOBJMETHOD(channel_getcaps, via8233dxs_getcaps),
+ KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
+ KOBJMETHOD(channel_trigger, via8233chan_trigger),
+ KOBJMETHOD(channel_getptr, via8233chan_getptr),
{ 0, 0 }
};
CHANNEL_DECLARE(via8233dxs);
static kobj_method_t via8233msgd_methods[] = {
- KOBJMETHOD(channel_init, via8233msgd_init),
- KOBJMETHOD(channel_setformat, via8233msgd_setformat),
- KOBJMETHOD(channel_setspeed, via8233msgd_setspeed),
- KOBJMETHOD(channel_getcaps, via8233msgd_getcaps),
- KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
- KOBJMETHOD(channel_trigger, via8233chan_trigger),
- KOBJMETHOD(channel_getptr, via8233chan_getptr),
+ KOBJMETHOD(channel_init, via8233msgd_init),
+ KOBJMETHOD(channel_setformat, via8233msgd_setformat),
+ KOBJMETHOD(channel_setspeed, via8233msgd_setspeed),
+ KOBJMETHOD(channel_getcaps, via8233msgd_getcaps),
+ KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize),
+ KOBJMETHOD(channel_trigger, via8233chan_trigger),
+ KOBJMETHOD(channel_getptr, via8233chan_getptr),
{ 0, 0 }
};
CHANNEL_DECLARE(via8233msgd);
@@ -712,55 +958,56 @@ static void
via_intr(void *p)
{
struct via_info *via = p;
+ uint32_t ptrigger = 0, rtrigger = 0;
int i, reg, stat;
- /* Poll playback channels */
snd_mtxlock(via->lock);
+ if (via->polling != 0) {
+ snd_mtxunlock(via->lock);
+ return;
+ }
+ /* Poll playback channels */
for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
- if (via->pch[i].channel == NULL)
+ if (via->pch[i].channel == NULL || via->pch[i].active == 0)
continue;
reg = via->pch[i].rbase + VIA_RP_STATUS;
stat = via_rd(via, reg, 1);
if (stat & SGD_STATUS_INTR) {
if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
- !(stat & SGD_STATUS_ACTIVE))) {
- via_wr(via,
- via->pch[i].rbase + VIA_RP_CONTROL,
- SGD_CONTROL_START |
- SGD_CONTROL_AUTOSTART |
- SGD_CONTROL_I_EOL |
- SGD_CONTROL_I_FLAG, 1);
- }
+ !(stat & SGD_STATUS_ACTIVE)))
+ via_wr(via, via->pch[i].rbase + VIA_RP_CONTROL,
+ SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
+ SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1);
via_wr(via, reg, stat, 1);
- snd_mtxunlock(via->lock);
- chn_intr(via->pch[i].channel);
- snd_mtxlock(via->lock);
+ ptrigger |= 1 << i;
}
}
-
/* Poll record channels */
for (i = 0; i < NWRCHANS; i++) {
- if (via->rch[i].channel == NULL)
+ if (via->rch[i].channel == NULL || via->rch[i].active == 0)
continue;
reg = via->rch[i].rbase + VIA_RP_STATUS;
stat = via_rd(via, reg, 1);
if (stat & SGD_STATUS_INTR) {
if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
- !(stat & SGD_STATUS_ACTIVE))) {
- via_wr(via,
- via->rch[i].rbase + VIA_RP_CONTROL,
- SGD_CONTROL_START |
- SGD_CONTROL_AUTOSTART |
- SGD_CONTROL_I_EOL |
- SGD_CONTROL_I_FLAG, 1);
- }
+ !(stat & SGD_STATUS_ACTIVE)))
+ via_wr(via, via->rch[i].rbase + VIA_RP_CONTROL,
+ SGD_CONTROL_START | SGD_CONTROL_AUTOSTART |
+ SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1);
via_wr(via, reg, stat, 1);
- snd_mtxunlock(via->lock);
- chn_intr(via->rch[i].channel);
- snd_mtxlock(via->lock);
+ rtrigger |= 1 << i;
}
}
snd_mtxunlock(via->lock);
+
+ for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
+ if (ptrigger & (1 << i))
+ chn_intr(via->pch[i].channel);
+ }
+ for (i = 0; i < NWRCHANS; i++) {
+ if (rtrigger & (1 << i))
+ chn_intr(via->rch[i].channel);
+ }
}
/*
@@ -772,33 +1019,33 @@ via_probe(device_t dev)
switch(pci_get_devid(dev)) {
case VIA8233_PCI_ID:
switch(pci_get_revid(dev)) {
- case VIA8233_REV_ID_8233PRE:
+ case VIA8233_REV_ID_8233PRE:
device_set_desc(dev, "VIA VT8233 (pre)");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8233C:
device_set_desc(dev, "VIA VT8233C");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8233:
device_set_desc(dev, "VIA VT8233");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8233A:
device_set_desc(dev, "VIA VT8233A");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8235:
device_set_desc(dev, "VIA VT8235");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8237:
device_set_desc(dev, "VIA VT8237");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
case VIA8233_REV_ID_8251:
device_set_desc(dev, "VIA VT8251");
- return BUS_PROBE_DEFAULT;
+ return (BUS_PROBE_DEFAULT);
default:
device_set_desc(dev, "VIA VT8233X"); /* Unknown */
- return BUS_PROBE_DEFAULT;
- }
+ return (BUS_PROBE_DEFAULT);
+ }
}
- return ENXIO;
+ return (ENXIO);
}
static void
@@ -811,7 +1058,7 @@ dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
static int
via_chip_init(device_t dev)
{
- u_int32_t data, cnt;
+ uint32_t data, cnt;
/* Wake up and reset AC97 if necessary */
data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1);
@@ -819,32 +1066,32 @@ via_chip_init(device_t dev)
if ((data & VIA_PCI_ACLINK_C00_READY) == 0) {
/* Cold reset per ac97r2.3 spec (page 95) */
/* Assert low */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN, 1);
/* Wait T_rst_low */
- DELAY(100);
+ DELAY(100);
/* Assert high */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST, 1);
/* Wait T_rst2clk */
DELAY(5);
/* Assert low */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN, 1);
} else {
/* Warm reset */
/* Force no sync */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN, 1);
DELAY(100);
/* Sync */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_SYNC, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_SYNC, 1);
/* Wait T_sync_high */
DELAY(5);
/* Force no sync */
- pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
- VIA_PCI_ACLINK_EN, 1);
+ pci_write_config(dev, VIA_PCI_ACLINK_CTRL,
+ VIA_PCI_ACLINK_EN, 1);
/* Wait T_sync2clk */
DELAY(5);
}
@@ -855,13 +1102,12 @@ via_chip_init(device_t dev)
/* Wait for codec to become ready (largest reported delay 310ms) */
for (cnt = 0; cnt < 2000; cnt++) {
data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1);
- if (data & VIA_PCI_ACLINK_C00_READY) {
- return 0;
- }
+ if (data & VIA_PCI_ACLINK_C00_READY)
+ return (0);
DELAY(5000);
}
device_printf(dev, "primary codec not ready (cnt = 0x%02x)\n", cnt);
- return ENXIO;
+ return (ENXIO);
}
static int
@@ -870,14 +1116,24 @@ via_attach(device_t dev)
struct via_info *via = 0;
char status[SND_STATUSLEN];
int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum;
+ int nsegs;
uint32_t revid;
if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
- return ENXIO;
+ return (ENXIO);
}
via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
+ callout_init(&via->poll_timer, CALLOUT_MPSAFE);
+ via->poll_ticks = 1;
+
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "polling", &i) == 0 && i != 0)
+ via->polling = 1;
+ else
+ via->polling = 0;
+
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_enable_busmaster(dev);
@@ -891,76 +1147,31 @@ via_attach(device_t dev)
via->st = rman_get_bustag(via->reg);
via->sh = rman_get_bushandle(via->reg);
- via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536);
-
via->irqid = 0;
via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
- RF_ACTIVE | RF_SHAREABLE);
- if (!via->irq ||
- snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) {
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!via->irq ||
+ snd_setup_intr(dev, via->irq, INTR_MPSAFE,
+ via_intr, via, &via->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
- /* DMA tag for buffers */
- if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
- /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
- /*highaddr*/BUS_SPACE_MAXADDR,
- /*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/NULL,
- /*lockarg*/NULL, &via->parent_dmat) != 0) {
- device_printf(dev, "unable to create dma tag\n");
- goto bad;
- }
-
- /*
- * DMA tag for SGD table. The 686 uses scatter/gather DMA and
- * requires a list in memory of work to do. We need only 16 bytes
- * for this list, and it is wasteful to allocate 16K.
- */
- if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
- /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
- /*highaddr*/BUS_SPACE_MAXADDR,
- /*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/NSEGS * sizeof(struct via_dma_op),
- /*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/NULL,
- /*lockarg*/NULL, &via->sgd_dmat) != 0) {
- device_printf(dev, "unable to create dma tag\n");
- goto bad;
- }
-
- if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table,
- BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1)
- goto bad;
- if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table,
- NSEGS * sizeof(struct via_dma_op), dma_cb, via, 0))
- goto bad;
-
- if (via_chip_init(dev))
- goto bad;
-
- via->codec = AC97_CREATE(dev, via, via_ac97);
- if (!via->codec)
- goto bad;
-
- mixer_init(dev, ac97_getmixerclass(), via->codec);
-
- via->codec_caps = ac97_getextcaps(via->codec);
-
- /* Try to set VRA without generating an error, VRM not reqrd yet */
- if (via->codec_caps &
- (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM | AC97_EXTCAP_DRA)) {
- u_int16_t ext = ac97_getextmode(via->codec);
- ext |= (via->codec_caps &
- (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM));
- ext &= ~AC97_EXTCAP_DRA;
- ac97_setextmode(via->codec, ext);
- }
-
- snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
- rman_get_start(via->reg), rman_get_start(via->irq),PCM_KLDSTRING(snd_via8233));
+ via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536);
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
+ via->blkcnt = via->bufsz / i;
+ i = 0;
+ while (via->blkcnt >> i)
+ i++;
+ via->blkcnt = 1 << (i - 1);
+ if (via->blkcnt < VIA_SEGS_MIN)
+ via->blkcnt = VIA_SEGS_MIN;
+ else if (via->blkcnt > VIA_SEGS_MAX)
+ via->blkcnt = VIA_SEGS_MAX;
+
+ } else
+ via->blkcnt = VIA_SEGS_DEFAULT;
revid = pci_get_revid(dev);
@@ -985,8 +1196,8 @@ via_attach(device_t dev)
*/
via_dxs_disabled = 1;
} else if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "via_dxs_disabled",
- &via_dxs_disabled) == 0)
+ device_get_unit(dev), "via_dxs_disabled",
+ &via_dxs_disabled) == 0)
via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0;
else
via_dxs_disabled = 0;
@@ -996,12 +1207,12 @@ via_attach(device_t dev)
via_sgd_chnum = 1;
} else {
if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "via_dxs_channels",
- &via_dxs_chnum) != 0)
+ device_get_unit(dev), "via_dxs_channels",
+ &via_dxs_chnum) != 0)
via_dxs_chnum = NDXSCHANS;
if (resource_int_value(device_get_name(dev),
- device_get_unit(dev), "via_sgd_channels",
- &via_sgd_chnum) != 0)
+ device_get_unit(dev), "via_sgd_channels",
+ &via_sgd_chnum) != 0)
via_sgd_chnum = NMSGDCHANS;
}
if (via_dxs_chnum > NDXSCHANS)
@@ -1018,11 +1229,74 @@ via_attach(device_t dev)
via_sgd_chnum = 0;
}
if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev),
- device_get_unit(dev), "via_dxs_src",
- &via_dxs_src) == 0)
+ device_get_unit(dev), "via_dxs_src", &via_dxs_src) == 0)
via->dxs_src = (via_dxs_src > 0) ? 1 : 0;
else
via->dxs_src = 0;
+
+ nsegs = (via_dxs_chnum + via_sgd_chnum) * via->blkcnt;
+
+ /* DMA tag for buffers */
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ /*
+ * DMA tag for SGD table. The 686 uses scatter/gather DMA and
+ * requires a list in memory of work to do. We need only 16 bytes
+ * for this list, and it is wasteful to allocate 16K.
+ */
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/nsegs * sizeof(struct via_dma_op),
+ /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->sgd_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table,
+ BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1)
+ goto bad;
+ if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table,
+ nsegs * sizeof(struct via_dma_op), dma_cb, via, 0))
+ goto bad;
+
+ if (via_chip_init(dev))
+ goto bad;
+
+ via->codec = AC97_CREATE(dev, via, via_ac97);
+ if (!via->codec)
+ goto bad;
+
+ mixer_init(dev, ac97_getmixerclass(), via->codec);
+
+ via->codec_caps = ac97_getextcaps(via->codec);
+
+ /* Try to set VRA without generating an error, VRM not reqrd yet */
+ if (via->codec_caps &
+ (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM | AC97_EXTCAP_DRA)) {
+ uint16_t ext = ac97_getextmode(via->codec);
+ ext |= (via->codec_caps &
+ (AC97_EXTCAP_VRA | AC97_EXTCAP_VRM));
+ ext &= ~AC97_EXTCAP_DRA;
+ ac97_setextmode(via->codec, ext);
+ }
+
+ snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
+ rman_get_start(via->reg), rman_get_start(via->irq),
+ PCM_KLDSTRING(snd_via8233));
+
/* Register */
if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
goto bad;
@@ -1035,24 +1309,32 @@ via_attach(device_t dev)
if (via_dxs_chnum > 0)
via_init_sysctls(dev);
device_printf(dev, "<VIA DXS %sabled: DXS%s %d / SGD %d / REC %d>\n",
- (via_dxs_chnum > 0) ? "En" : "Dis",
- (via->dxs_src) ? "(SRC)" : "",
- via_dxs_chnum, via_sgd_chnum, NWRCHANS);
+ (via_dxs_chnum > 0) ? "En" : "Dis", (via->dxs_src) ? "(SRC)" : "",
+ via_dxs_chnum, via_sgd_chnum, NWRCHANS);
pcm_setstatus(dev, status);
- return 0;
+ return (0);
bad:
- if (via->codec) ac97_destroy(via->codec);
- if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
- if (via->ih) bus_teardown_intr(dev, via->irq, via->ih);
- if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
- if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
- if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
- if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
- if (via->lock) snd_mtxfree(via->lock);
- if (via) free(via, M_DEVBUF);
- return ENXIO;
+ if (via->codec)
+ ac97_destroy(via->codec);
+ if (via->reg)
+ bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
+ if (via->ih)
+ bus_teardown_intr(dev, via->irq, via->ih);
+ if (via->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq);
+ if (via->parent_dmat)
+ bus_dma_tag_destroy(via->parent_dmat);
+ if (via->sgd_dmamap)
+ bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
+ if (via->sgd_dmat)
+ bus_dma_tag_destroy(via->sgd_dmat);
+ if (via->lock)
+ snd_mtxfree(via->lock);
+ if (via)
+ free(via, M_DEVBUF);
+ return (ENXIO);
}
static int
@@ -1062,7 +1344,8 @@ via_detach(device_t dev)
struct via_info *via = 0;
r = pcm_unregister(dev);
- if (r) return r;
+ if (r)
+ return (r);
via = pcm_getdevinfo(dev);
bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);
@@ -1073,7 +1356,7 @@ via_detach(device_t dev)
bus_dma_tag_destroy(via->sgd_dmat);
snd_mtxfree(via->lock);
free(via, M_DEVBUF);
- return 0;
+ return (0);
}
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
index 9b3380b..46ceef0 100644
--- a/sys/dev/sound/pcm/ac97.c
+++ b/sys/dev/sound/pcm/ac97.c
@@ -28,6 +28,8 @@
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pcm/ac97_patch.h>
+#include <dev/pci/pcivar.h>
+
#include "mixer_if.h"
SND_DECLARE_FILE("$FreeBSD$");
@@ -791,7 +793,7 @@ struct ac97_info *
ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
{
struct ac97_info *codec;
- int eapd_inv;
+ int eapdinv;
codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO);
if (codec == NULL)
@@ -811,8 +813,8 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
codec->devinfo = devinfo;
codec->flags = 0;
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
- "ac97_eapd_inv", &eapd_inv) == 0) {
- if (eapd_inv != 0)
+ "eapdinv", &eapdinv) == 0) {
+ if (eapdinv != 0)
codec->flags |= AC97_F_EAPD_INV;
}
return codec;
@@ -842,11 +844,66 @@ ac97_getflags(struct ac97_info *codec)
/* -------------------------------------------------------------------- */
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
+{
+ struct ac97_info *codec;
+ int ea, inv, err = 0;
+ u_int16_t val;
+
+ codec = oidp->oid_arg1;
+ if (codec == NULL || codec->id == 0 || codec->lock == NULL)
+ return EINVAL;
+ snd_mtxlock(codec->lock);
+ val = ac97_rdcd(codec, AC97_REG_POWER);
+ inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
+ ea = (val >> 15) ^ inv;
+ snd_mtxunlock(codec->lock);
+ err = sysctl_handle_int(oidp, &ea, sizeof(ea), req);
+ if (err == 0 && req->newptr != NULL) {
+ if (ea != 0 && ea != 1)
+ return EINVAL;
+ if (ea != ((val >> 15) ^ inv)) {
+ snd_mtxlock(codec->lock);
+ ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
+ snd_mtxunlock(codec->lock);
+ }
+ }
+ return err;
+}
+#endif
+
+static void
+ac97_init_sysctl(struct ac97_info *codec)
+{
+#ifdef SND_DYNSYSCTL
+ u_int16_t orig, val;
+
+ if (codec == NULL || codec->dev == NULL)
+ return;
+ snd_mtxlock(codec->lock);
+ orig = ac97_rdcd(codec, AC97_REG_POWER);
+ ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
+ val = ac97_rdcd(codec, AC97_REG_POWER);
+ ac97_wrcd(codec, AC97_REG_POWER, orig);
+ snd_mtxunlock(codec->lock);
+ if ((val & 0x8000) == (orig & 0x8000))
+ return;
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
+ OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
+ codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
+ "I", "AC97 External Amplifier");
+#endif
+}
+
static int
ac97mix_init(struct snd_mixer *m)
{
struct ac97_info *codec = mix_getdevinfo(m);
struct snddev_info *d;
+ u_int32_t subvendor;
u_int32_t i, mask;
if (codec == NULL)
@@ -857,20 +914,30 @@ ac97mix_init(struct snd_mixer *m)
switch (codec->id) {
case 0x41445374: /* AD1981B */
-#if 0
- mask = 0;
- if (codec->mix[SOUND_MIXER_OGAIN].enable)
- mask |= SOUND_MASK_OGAIN;
- if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
- mask |= SOUND_MASK_PHONEOUT;
- /* Tie ogain/phone to master volume */
- if (codec->mix[SOUND_MIXER_VOLUME].enable)
- mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
- else {
- mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
- mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
+ subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
+ subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
+ 0x0000ffff;
+ /* IBM Thinkcentre */
+ if (subvendor == 0x02d91014) {
+ /* Enable headphone jack sensing */
+ ac97_wrcd(codec, 0x72, ac97_rdcd(codec, 0x72) |
+ 0x0800);
+ mask = 0;
+ if (codec->mix[SOUND_MIXER_OGAIN].enable)
+ mask |= SOUND_MASK_OGAIN;
+ if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
+ mask |= SOUND_MASK_PHONEOUT;
+ /* Tie ogain/phone to master volume */
+ if (codec->mix[SOUND_MIXER_VOLUME].enable)
+ mix_setparentchild(m, SOUND_MIXER_VOLUME,
+ mask);
+ else {
+ mix_setparentchild(m, SOUND_MIXER_VOLUME,
+ mask);
+ mix_setrealdev(m, SOUND_MIXER_VOLUME,
+ SOUND_MIXER_NONE);
+ }
}
-#endif
break;
case 0x434d4941: /* CMI9738 */
case 0x434d4961: /* CMI9739 */
@@ -906,6 +973,9 @@ ac97mix_init(struct snd_mixer *m)
for (i = 0; i < 32; i++)
mask |= codec->mix[i].recidx? 1 << i : 0;
mix_setrecdevs(m, mask);
+
+ ac97_init_sysctl(codec);
+
return 0;
}
@@ -975,5 +1045,3 @@ ac97_getmixerclass(void)
{
return &ac97mixer_class;
}
-
-
diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h
index 8266fb5..f73dc76 100644
--- a/sys/dev/sound/pcm/ac97.h
+++ b/sys/dev/sound/pcm/ac97.h
@@ -82,7 +82,6 @@
#define AC97_F_EAPD_INV 0x00000001
#define AC97_F_RDCD_BUG 0x00000002
-#define AC97_F_SOFTVOL 0x00000004
#define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
#define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class)
@@ -105,4 +104,3 @@ u_int16_t ac97_getcaps(struct ac97_info *codec);
u_int16_t ac97_rdcd(struct ac97_info *codec, int reg);
void ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val);
-
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
index 11c1bfb..e12765e 100644
--- a/sys/dev/sound/pcm/buffer.c
+++ b/sys/dev/sound/pcm/buffer.c
@@ -254,10 +254,7 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
if (length > b->bufsize)
length = b->bufsize;
- if (b->fmt & AFMT_SIGNED)
- data = 0x00;
- else
- data = 0x80;
+ data = sndbuf_zerodata(b->fmt);
i = sndbuf_getfreeptr(b);
p = sndbuf_getbuf(b);
@@ -278,18 +275,8 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
void
sndbuf_fillsilence(struct snd_dbuf *b)
{
- int i;
- u_char data, *p;
-
- if (b->fmt & AFMT_SIGNED)
- data = 0x00;
- else
- data = 0x80;
-
- i = 0;
- p = sndbuf_getbuf(b);
- while (i < b->bufsize)
- p[i++] = data;
+ if (b->bufsize > 0)
+ memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
b->rp = 0;
b->rl = b->bufsize;
}
@@ -543,6 +530,52 @@ sndbuf_updateprevtotal(struct snd_dbuf *b)
b->prev_total = b->total;
}
+unsigned int
+snd_xbytes(unsigned int v, unsigned int from, unsigned int to)
+{
+ unsigned int w, x, y;
+
+ if (from == to)
+ return v;
+
+ if (from == 0 || to == 0 || v == 0)
+ return 0;
+
+ x = from;
+ y = to;
+ while (y != 0) {
+ w = x % y;
+ x = y;
+ y = w;
+ }
+ from /= x;
+ to /= x;
+
+ return (unsigned int)(((u_int64_t)v * to) / from);
+}
+
+unsigned int
+sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
+{
+ if (from == NULL || to == NULL || v == 0)
+ return 0;
+
+ return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from),
+ sndbuf_getbps(to) * sndbuf_getspd(to));
+}
+
+u_int8_t
+sndbuf_zerodata(u_int32_t fmt)
+{
+ if (fmt & AFMT_SIGNED)
+ return (0x00);
+ else if (fmt & AFMT_MU_LAW)
+ return (0x7f);
+ else if (fmt & AFMT_A_LAW)
+ return (0x55);
+ return (0x80);
+}
+
/************************************************************/
/**
@@ -624,15 +657,20 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
int
sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
{
+ unsigned int cnt;
+
KASSERT(count > 0, ("can't feed 0 bytes"));
if (sndbuf_getfree(to) < count)
return EINVAL;
- count = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
- if (count)
- sndbuf_acquire(to, to->tmpbuf, count);
- /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
+ do {
+ cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
+ if (cnt)
+ sndbuf_acquire(to, to->tmpbuf, cnt);
+ /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
+ count -= cnt;
+ } while (count && cnt);
return 0;
}
@@ -682,12 +720,8 @@ sndbuf_clearshadow(struct snd_dbuf *b)
KASSERT(b != NULL, ("b is a null pointer"));
KASSERT(b->sl >= 0, ("illegal shadow length"));
- if ((b->shadbuf != NULL) && (b->sl > 0)) {
- if (b->fmt & AFMT_SIGNED)
- memset(b->shadbuf, 0x00, b->sl);
- else
- memset(b->shadbuf, 0x80, b->sl);
- }
+ if ((b->shadbuf != NULL) && (b->sl > 0))
+ memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl);
}
#ifdef OSSV4_EXPERIMENT
diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h
index 07724be..67e08a7 100644
--- a/sys/dev/sound/pcm/buffer.h
+++ b/sys/dev/sound/pcm/buffer.h
@@ -107,6 +107,9 @@ unsigned int sndbuf_getreadyptr(struct snd_dbuf *b);
unsigned int sndbuf_getblocks(struct snd_dbuf *b);
unsigned int sndbuf_getprevblocks(struct snd_dbuf *b);
unsigned int sndbuf_gettotal(struct snd_dbuf *b);
+unsigned int snd_xbytes(unsigned int v, unsigned int from, unsigned int to);
+unsigned int sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to);
+u_int8_t sndbuf_zerodata(u_int32_t fmt);
void sndbuf_updateprevtotal(struct snd_dbuf *b);
int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count);
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 43ef365..d88f0e2 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -41,32 +41,60 @@ SND_DECLARE_FILE("$FreeBSD$");
#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
+#define BUF_PARENT(c, b) \
+ (((c) != NULL && (c)->parentchannel != NULL && \
+ (c)->parentchannel->bufhard != NULL) ? \
+ (c)->parentchannel->bufhard : (b))
+
/*
#define DEB(x) x
*/
-static int chn_targetirqrate = 32;
-TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
+int report_soft_formats = 1;
+SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
+ &report_soft_formats, 1, "report software-emulated formats");
+
+int chn_latency = CHN_LATENCY_DEFAULT;
+TUNABLE_INT("hw.snd.latency", &chn_latency);
static int
-sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = chn_targetirqrate;
+ val = chn_latency;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val < 16 || val > 512)
+ if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX)
err = EINVAL;
else
- chn_targetirqrate = val;
+ chn_latency = val;
return err;
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
-static int report_soft_formats = 1;
-SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
- &report_soft_formats, 1, "report software-emulated formats");
+SYSCTL_PROC(_hw_snd, OID_AUTO, latency, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_latency, "I",
+ "buffering latency (0=low ... 10=high)");
+
+int chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
+TUNABLE_INT("hw.snd.latency_profile", &chn_latency_profile);
+
+static int
+sysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = chn_latency_profile;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX)
+ err = EINVAL;
+ else
+ chn_latency_profile = val;
+
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_latency_profile, "I",
+ "buffering latency profile (0=aggresive 1=safe)");
/**
* @brief Channel sync group lock
@@ -173,7 +201,7 @@ chn_wakeup(struct pcm_channel *c)
}
}
- wakeup(bs);
+ wakeup_one(bs);
}
static int
@@ -220,13 +248,23 @@ chn_dmaupdate(struct pcm_channel *c)
if (c->direction == PCMDIR_PLAY) {
amt = MIN(delta, sndbuf_getready(b));
+ amt -= amt % sndbuf_getbps(b);
if (amt > 0)
sndbuf_dispose(b, NULL, amt);
} else {
amt = MIN(delta, sndbuf_getfree(b));
+ amt -= amt % sndbuf_getbps(b);
if (amt > 0)
sndbuf_acquire(b, NULL, amt);
}
+ if (snd_verbose > 2 && (c->flags & CHN_F_TRIGGERED) && delta == 0) {
+ device_printf(c->dev, "WARNING: PCMDIR_%s DMA completion "
+ "too fast/slow ! hwptr=%u, old=%u "
+ "delta=%u amt=%u ready=%u free=%u\n",
+ (c->direction == PCMDIR_PLAY) ? "PLAY" : "REC",
+ hwptr, old, delta, amt,
+ sndbuf_getready(b), sndbuf_getfree(b));
+ }
return delta;
}
@@ -270,9 +308,11 @@ chn_wrfeed(struct pcm_channel *c)
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
amt = sndbuf_getfree(b);
- KASSERT(amt <= sndbuf_getsize(bs),
- ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
- amt, sndbuf_getsize(bs), c->flags));
+ DEB(if (amt > sndbuf_getsize(bs) &&
+ sndbuf_getbps(bs) >= sndbuf_getbps(b)) {
+ printf("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
+ amt, sndbuf_getsize(bs), c->flags);
+ });
ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
/*
@@ -313,10 +353,10 @@ chn_wrintr(struct pcm_channel *c)
int
chn_write(struct pcm_channel *c, struct uio *buf)
{
- int ret, timeout, newsize, count, sz;
+ int ret, newsize, count, sz;
struct snd_dbuf *bs = c->bufsoft;
void *off;
- int t, x,togo,p;
+ int t, x, togo, p;
CHN_LOCKASSERT(c);
/*
@@ -356,14 +396,13 @@ chn_write(struct pcm_channel *c, struct uio *buf)
*/
ret = EAGAIN;
} else {
- timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
- if (timeout < 1)
- timeout = 1;
- timeout = 1;
- ret = chn_sleep(c, "pcmwr", timeout);
+ ret = chn_sleep(c, "pcmwr", c->timeout);
if (ret == EWOULDBLOCK) {
- count -= timeout;
+ count -= c->timeout;
ret = 0;
+ } else if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
+ return ret;
} else if (ret == 0)
count = hz;
}
@@ -378,7 +417,7 @@ chn_write(struct pcm_channel *c, struct uio *buf)
* unlock-uiomove-lock sequence.
*/
togo = sz;
- while (ret == 0 && togo> 0) {
+ while (ret == 0 && togo > 0) {
p = sndbuf_getfreeptr(bs);
t = MIN(togo, sndbuf_getsize(bs) - p);
off = sndbuf_getbufofs(bs, p);
@@ -389,7 +428,7 @@ chn_write(struct pcm_channel *c, struct uio *buf)
x = sndbuf_acquire(bs, NULL, t);
}
ret = 0;
- if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
+ if (!(c->flags & CHN_F_TRIGGERED))
chn_start(c, 0);
}
}
@@ -411,7 +450,7 @@ chn_rddump(struct pcm_channel *c, unsigned int cnt)
CHN_LOCKASSERT(c);
#if 0
- static uint32_t kk = 0;
+ static u_int32_t kk = 0;
printf("%u: dumping %d bytes\n", ++kk, cnt);
#endif
c->xruns++;
@@ -501,10 +540,10 @@ chn_rdintr(struct pcm_channel *c)
int
chn_read(struct pcm_channel *c, struct uio *buf)
{
- int ret, timeout, sz, count;
- struct snd_dbuf *bs = c->bufsoft;
+ int ret, sz, count;
+ struct snd_dbuf *bs = c->bufsoft;
void *off;
- int t, x,togo,p;
+ int t, x, togo, p;
CHN_LOCKASSERT(c);
if (!(c->flags & CHN_F_TRIGGERED))
@@ -522,7 +561,7 @@ chn_read(struct pcm_channel *c, struct uio *buf)
* unlock-uiomove-lock sequence.
*/
togo = sz;
- while (ret == 0 && togo> 0) {
+ while (ret == 0 && togo > 0) {
p = sndbuf_getreadyptr(bs);
t = MIN(togo, sndbuf_getsize(bs) - p);
off = sndbuf_getbufofs(bs, p);
@@ -534,20 +573,18 @@ chn_read(struct pcm_channel *c, struct uio *buf)
}
ret = 0;
} else {
- if (c->flags & CHN_F_NBIO) {
+ if (c->flags & CHN_F_NBIO)
ret = EWOULDBLOCK;
- } else {
- timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
- if (timeout < 1)
- timeout = 1;
- ret = chn_sleep(c, "pcmrd", timeout);
+ else {
+ ret = chn_sleep(c, "pcmrd", c->timeout);
if (ret == EWOULDBLOCK) {
- count -= timeout;
+ count -= c->timeout;
ret = 0;
- } else {
+ } else if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
+ return ret;
+ } else
count = hz;
- }
-
}
}
}
@@ -584,27 +621,49 @@ chn_start(struct pcm_channel *c, int force)
if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
return EINVAL;
- i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
- j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
- if (force || (i >= j)) {
- c->flags |= CHN_F_TRIGGERED;
- /*
- * if we're starting because a vchan started, don't feed any data
- * or it becomes impossible to start vchans synchronised with the
- * first one. the hardbuf should be empty so we top it up with
- * silence to give it something to chew. the real data will be
- * fed at the first irq.
- */
- if (c->direction == PCMDIR_PLAY) {
- /*
- * Reduce pops during playback startup.
- */
- sndbuf_fillsilence(b);
- if (SLIST_EMPTY(&c->children))
- chn_wrfeed(c);
+ if (force) {
+ i = 1;
+ j = 0;
+ } else {
+ if (c->direction == PCMDIR_REC) {
+ i = sndbuf_getfree(bs);
+ j = sndbuf_getready(b);
+ } else {
+ struct snd_dbuf *pb;
+
+ i = sndbuf_getready(bs);
+
+ pb = BUF_PARENT(c, b);
+ j = min(sndbuf_xbytes(sndbuf_getsize(pb), pb, bs),
+ sndbuf_getsize(bs));
}
+ if (snd_verbose > 3 && SLIST_EMPTY(&c->children))
+ printf("%s: PCMDIR_%s (%s) threshold i=%d j=%d\n",
+ __func__,
+ (c->direction == PCMDIR_PLAY) ? "PLAY" : "REC",
+ (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ i, j);
+ }
+
+ if (i >= j) {
+ c->flags |= CHN_F_TRIGGERED;
sndbuf_setrun(b, 1);
+ c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0;
+ c->interrupts = 0;
c->xruns = 0;
+ if (c->direction == PCMDIR_PLAY && c->parentchannel == NULL) {
+ chn_wrfeed(c);
+ if (snd_verbose > 3)
+ printf("%s: %s starting! (%s) (ready=%d "
+ "force=%d i=%d j=%d intrtimeout=%u)\n",
+ __func__,
+ (c->flags & CHN_F_HAS_VCHAN) ?
+ "VCHAN" : "HW",
+ (c->flags & CHN_F_CLOSING) ? "closing" :
+ "running",
+ sndbuf_getready(b),
+ force, i, j, c->timeout);
+ }
chn_trigger(c, PCMTRIG_START);
return 0;
}
@@ -631,34 +690,91 @@ chn_resetbuf(struct pcm_channel *c)
int
chn_sync(struct pcm_channel *c, int threshold)
{
- u_long rdy;
- int ret;
- struct snd_dbuf *bs = c->bufsoft;
+ int ret, count, hcount, minflush, resid, residp;
+ struct snd_dbuf *b, *bs;
CHN_LOCKASSERT(c);
+ if (c->flags & (CHN_F_DEAD | CHN_F_ABORTING))
+ return 0;
+
+ if (c->direction != PCMDIR_PLAY)
+ return EINVAL;
+
+ bs = c->bufsoft;
+
/* if we haven't yet started and nothing is buffered, else start*/
if (!(c->flags & CHN_F_TRIGGERED)) {
if (sndbuf_getready(bs) > 0) {
ret = chn_start(c, 1);
if (ret)
return ret;
- } else {
+ } else
return 0;
- }
}
- for (;;) {
- rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
- if (rdy <= threshold) {
- ret = chn_sleep(c, "pcmsyn", 1);
- if (ret == ERESTART || ret == EINTR) {
- DEB(printf("chn_sync: tsleep returns %d\n", ret));
- return -1;
- }
- } else
+ b = BUF_PARENT(c, c->bufhard);
+
+ threshold += sndbuf_getready(b);
+ minflush = sndbuf_xbytes(threshold, b, bs);
+ minflush -= minflush % sndbuf_getbps(bs);
+
+ if (minflush > 0) {
+ threshold = min(minflush, sndbuf_getfree(bs));
+ sndbuf_clear(bs, threshold);
+ sndbuf_acquire(bs, NULL, threshold);
+ minflush -= threshold;
+ }
+
+ resid = sndbuf_getready(bs);
+ residp = resid;
+ count = sndbuf_xbytes(minflush + resid, bs, b) / sndbuf_getblksz(b);
+ hcount = count;
+ ret = 0;
+
+ while (count > 0 && resid > 0) {
+ ret = chn_sleep(c, "pcmsyn", c->timeout);
+ if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
break;
- }
+ }
+ if (ret == 0 || ret == EWOULDBLOCK) {
+ resid = sndbuf_getready(bs);
+ if (resid == residp) {
+ --count;
+ if (snd_verbose > 3)
+ printf("%s: [stalled] timeout=%d "
+ "count=%d hcount=%d "
+ "resid=%d minflush=%d\n",
+ __func__, c->timeout, count,
+ hcount, resid, minflush);
+ } else if (resid < residp && count < hcount) {
+ ++count;
+ if (snd_verbose > 3)
+ printf("%s: [resume] timeout=%d "
+ "count=%d hcount=%d "
+ "resid=%d minflush=%d\n",
+ __func__, c->timeout, count,
+ hcount, resid, minflush);
+ }
+ if (minflush > 0 && sndbuf_getfree(bs) > 0) {
+ threshold = min(minflush,
+ sndbuf_getfree(bs));
+ sndbuf_clear(bs, threshold);
+ sndbuf_acquire(bs, NULL, threshold);
+ resid = sndbuf_getready(bs);
+ minflush -= threshold;
+ }
+ residp = resid;
+ }
+ }
+
+ if (snd_verbose > 3)
+ printf("%s: timeout=%d count=%d hcount=%d resid=%d residp=%d "
+ "minflush=%d ret=%d\n",
+ __func__, c->timeout, count, hcount, resid, residp,
+ minflush, ret);
+
return 0;
}
@@ -724,48 +840,14 @@ chn_abort(struct pcm_channel *c)
int
chn_flush(struct pcm_channel *c)
{
- int ret, count, resid, resid_p;
struct snd_dbuf *b = c->bufhard;
- struct snd_dbuf *bs = c->bufsoft;
CHN_LOCKASSERT(c);
KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
- /* if we haven't yet started and nothing is buffered, else start*/
- if (!(c->flags & CHN_F_TRIGGERED)) {
- if (sndbuf_getready(bs) > 0) {
- ret = chn_start(c, 1);
- if (ret)
- return ret;
- } else {
- return 0;
- }
- }
-
c->flags |= CHN_F_CLOSING;
- resid = sndbuf_getready(bs) + sndbuf_getready(b);
- resid_p = resid;
- count = 10;
- ret = 0;
- while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
- /* still pending output data. */
- ret = chn_sleep(c, "pcmflu", hz / 10);
- if (ret == EWOULDBLOCK)
- ret = 0;
- if (ret == 0) {
- resid = sndbuf_getready(bs) + sndbuf_getready(b);
- if (resid == resid_p)
- count--;
- if (resid > resid_p)
- DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
- resid_p = resid;
- }
- }
- if (count == 0)
- DEB(printf("chn_flush: timeout, hw %d, sw %d\n",
- sndbuf_getready(b), sndbuf_getready(bs)));
-
+ chn_sync(c, 0);
c->flags &= ~CHN_F_TRIGGERED;
/* kill the channel */
chn_trigger(c, PCMTRIG_ABORT);
@@ -786,14 +868,141 @@ fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
return 0;
}
+static struct afmtstr_table default_afmtstr_table[] = {
+ { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW },
+ { "u8", AFMT_U8 }, { "s8", AFMT_S8 },
+ { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
+ { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
+ { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
+ { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
+ { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
+ { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
+ { NULL, 0 },
+};
+
+int
+afmtstr_swap_sign(char *s)
+{
+ if (s == NULL || strlen(s) < 2) /* full length of "s8" */
+ return 0;
+ if (*s == 's')
+ *s = 'u';
+ else if (*s == 'u')
+ *s = 's';
+ else
+ return 0;
+ return 1;
+}
+
+int
+afmtstr_swap_endian(char *s)
+{
+ if (s == NULL || strlen(s) < 5) /* full length of "s16le" */
+ return 0;
+ if (s[3] == 'l')
+ s[3] = 'b';
+ else if (s[3] == 'b')
+ s[3] = 'l';
+ else
+ return 0;
+ return 1;
+}
+
+u_int32_t
+afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo)
+{
+ size_t fsz, sz;
+
+ sz = (s == NULL) ? 0 : strlen(s);
+
+ if (sz > 1) {
+
+ if (tbl == NULL)
+ tbl = default_afmtstr_table;
+
+ for (; tbl->fmtstr != NULL; tbl++) {
+ fsz = strlen(tbl->fmtstr);
+ if (sz < fsz)
+ continue;
+ if (strncmp(s, tbl->fmtstr, fsz) != 0)
+ continue;
+ if (fsz == sz)
+ return tbl->format |
+ ((stereo) ? AFMT_STEREO : 0);
+ if ((sz - fsz) < 2 || s[fsz] != ':')
+ break;
+ /*
+ * For now, just handle mono/stereo.
+ */
+ if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' ||
+ s[fsz + 1] == '1')) ||
+ strcmp(s + fsz + 1, "mono") == 0)
+ return tbl->format;
+ if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' ||
+ s[fsz + 1] == '2')) ||
+ strcmp(s + fsz + 1, "stereo") == 0)
+ return tbl->format | AFMT_STEREO;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+u_int32_t
+afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst,
+ size_t len, int type, int stereo)
+{
+ u_int32_t fmt = 0;
+ char *fmtstr = NULL, *tag = "";
+
+ if (tbl == NULL)
+ tbl = default_afmtstr_table;
+
+ for (; tbl->format != 0; tbl++) {
+ if (tbl->format == 0)
+ break;
+ if ((afmt & ~AFMT_STEREO) != tbl->format)
+ continue;
+ fmt = afmt;
+ fmtstr = tbl->fmtstr;
+ break;
+ }
+
+ if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) {
+ strlcpy(dst, fmtstr, len);
+ switch (type) {
+ case AFMTSTR_SIMPLE:
+ tag = (fmt & AFMT_STEREO) ? ":s" : ":m";
+ break;
+ case AFMTSTR_NUM:
+ tag = (fmt & AFMT_STEREO) ? ":2" : ":1";
+ break;
+ case AFMTSTR_FULL:
+ tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono";
+ break;
+ case AFMTSTR_NONE:
+ default:
+ break;
+ }
+ if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \
+ (!stereo && (fmt & AFMT_STEREO))))
+ strlcat(dst, tag, len);
+ }
+
+ return fmt;
+}
+
int
chn_reset(struct pcm_channel *c, u_int32_t fmt)
{
int hwspd, r;
CHN_LOCKASSERT(c);
+ c->feedcount = 0;
c->flags &= CHN_F_RESET;
c->interrupts = 0;
+ c->timeout = 1;
c->xruns = 0;
r = CHANNEL_RESET(c->methods, c->devinfo);
@@ -818,7 +1027,7 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt)
#endif
}
if (r == 0)
- r = chn_setblocksize(c, 0, 0);
+ r = chn_setlatency(c, chn_latency);
if (r == 0) {
chn_resetbuf(c);
r = CHANNEL_RESETDONE(c->methods, c->devinfo);
@@ -839,6 +1048,8 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
bs = NULL;
c->devinfo = NULL;
c->feeder = NULL;
+ c->latency = -1;
+ c->timeout = 1;
ret = ENOMEM;
b = sndbuf_create(c->dev, c->name, "primary", c);
@@ -981,6 +1192,355 @@ chn_setvolume(struct pcm_channel *c, int left, int right)
return 0;
}
+static u_int32_t
+round_pow2(u_int32_t v)
+{
+ u_int32_t ret;
+
+ if (v < 2)
+ v = 2;
+ ret = 0;
+ while (v >> ret)
+ ret++;
+ ret = 1 << (ret - 1);
+ while (ret < v)
+ ret <<= 1;
+ return ret;
+}
+
+/*
+ * 4Front call it DSP Policy, while we call it "Latency Profile". The idea
+ * is to keep 2nd buffer short so that it doesn't cause long queue during
+ * buffer transfer.
+ *
+ * Latency reference table for 48khz stereo 16bit: (PLAY)
+ *
+ * +---------+------------+-----------+------------+
+ * | Latency | Blockcount | Blocksize | Buffersize |
+ * +---------+------------+-----------+------------+
+ * | 0 | 2 | 64 | 128 |
+ * +---------+------------+-----------+------------+
+ * | 1 | 4 | 128 | 512 |
+ * +---------+------------+-----------+------------+
+ * | 2 | 8 | 512 | 4096 |
+ * +---------+------------+-----------+------------+
+ * | 3 | 16 | 512 | 8192 |
+ * +---------+------------+-----------+------------+
+ * | 4 | 32 | 512 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 5 | 32 | 1024 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 6 | 16 | 2048 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 7 | 8 | 4096 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 8 | 4 | 8192 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 9 | 2 | 16384 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 10 | 2 | 32768 | 65536 |
+ * +---------+------------+-----------+------------+
+ *
+ * Recording need a different reference table. All we care is
+ * gobbling up everything within reasonable buffering threshold.
+ *
+ * Latency reference table for 48khz stereo 16bit: (REC)
+ *
+ * +---------+------------+-----------+------------+
+ * | Latency | Blockcount | Blocksize | Buffersize |
+ * +---------+------------+-----------+------------+
+ * | 0 | 512 | 32 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 1 | 256 | 64 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 2 | 128 | 128 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 3 | 64 | 256 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 4 | 32 | 512 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 5 | 32 | 1024 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 6 | 16 | 2048 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 7 | 8 | 4096 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 8 | 4 | 8192 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 9 | 2 | 16384 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 10 | 2 | 32768 | 65536 |
+ * +---------+------------+-----------+------------+
+ *
+ * Calculations for other data rate are entirely based on these reference
+ * tables. For normal operation, Latency 5 seems give the best, well
+ * balanced performance for typical workload. Anything below 5 will
+ * eat up CPU to keep up with increasing context switches because of
+ * shorter buffer space and usually require the application to handle it
+ * aggresively through possibly real time programming technique.
+ *
+ */
+#define CHN_LATENCY_PBLKCNT_REF \
+ {{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}, \
+ {1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}}
+#define CHN_LATENCY_PBUFSZ_REF \
+ {{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16}, \
+ {11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}}
+
+#define CHN_LATENCY_RBLKCNT_REF \
+ {{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}, \
+ {9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}}
+#define CHN_LATENCY_RBUFSZ_REF \
+ {{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16}, \
+ {15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}}
+
+#define CHN_LATENCY_DATA_REF 192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */
+
+static int
+chn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
+ u_int32_t max, int *rblksz, int *rblkcnt)
+{
+ u_int32_t bufsz;
+ int blksz, blkcnt;
+ static int pblkcnts[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_PBLKCNT_REF;
+ static int pbufszs[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_PBUFSZ_REF;
+ static int rblkcnts[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_RBLKCNT_REF;
+ static int rbufszs[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_RBUFSZ_REF;
+
+ if (CHN_LATENCY_MIN != 0 || CHN_LATENCY_MAX != 10 ||
+ latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX ||
+ bps < 1 || datarate < 1 ||
+ !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) {
+ if (rblksz != NULL)
+ *rblksz = CHN_2NDBUFMAXSIZE >> 1;
+ if (rblkcnt != NULL)
+ *rblkcnt = 2;
+ printf("%s: FAILED dir=%d latency=%d bps=%d "
+ "datarate=%u max=%u\n",
+ __func__, dir, latency, bps, datarate, max);
+ return CHN_2NDBUFMAXSIZE;
+ }
+
+ if (dir == PCMDIR_PLAY) {
+ blkcnt = pblkcnts[chn_latency_profile][latency];
+ bufsz = pbufszs[chn_latency_profile][latency];
+ } else {
+ blkcnt = rblkcnts[chn_latency_profile][latency];
+ bufsz = rbufszs[chn_latency_profile][latency];
+ }
+ bufsz = snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF, datarate);
+ if (bufsz > max)
+ bufsz = max;
+ if (bufsz < 32)
+ bufsz = 32;
+ blksz = bufsz >> blkcnt;
+ blksz -= blksz % bps;
+ while (blksz < 16 || blksz < bps)
+ blksz += bps;
+ while ((blksz << blkcnt) > bufsz && blkcnt > 1)
+ blkcnt--;
+ if (rblksz != NULL)
+ *rblksz = blksz;
+ if (rblkcnt != NULL)
+ *rblkcnt = 1 << blkcnt;
+
+ return blksz << blkcnt;
+}
+
+/*
+ * Note that there is no strict requirement to align blksz to the
+ * nearest ^2, except for hardware CHANNEL_SETBLOCKSIZE. If the application
+ * trying to act smarter and requesting for specific blksz/blkcnt, so be it.
+ */
+static int
+chn_resizebuf(struct pcm_channel *c, int latency,
+ int blkcnt, int blksz)
+{
+ struct snd_dbuf *b, *bs, *pb;
+ int sblksz, sblkcnt, hblksz, limit = 1;
+ int ret;
+
+ CHN_LOCKASSERT(c);
+
+ if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED) ||
+ !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC))
+ return EINVAL;
+
+ if (latency == -1) {
+ c->latency = -1;
+ latency = chn_latency;
+ } else if (latency == -2) {
+ latency = c->latency;
+ if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
+ latency = chn_latency;
+ } else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
+ return EINVAL;
+ else {
+ c->latency = latency;
+ limit = 0;
+ }
+
+ bs = c->bufsoft;
+ b = c->bufhard;
+
+ if (!(blksz == 0 || blkcnt == -1) &&
+ (blksz < 16 || blksz < sndbuf_getbps(bs) || blkcnt < 2 ||
+ (blksz * blkcnt) > CHN_2NDBUFMAXSIZE))
+ return EINVAL;
+
+ chn_calclatency(c->direction, latency, sndbuf_getbps(bs),
+ sndbuf_getbps(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE,
+ &sblksz, &sblkcnt);
+
+ if (blksz == 0 || blkcnt == -1) {
+ if (blkcnt == -1)
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (c->flags & CHN_F_HAS_SIZE) {
+ blksz = sndbuf_getblksz(bs);
+ blkcnt = sndbuf_getblkcnt(bs);
+ }
+ } else
+ c->flags |= CHN_F_HAS_SIZE;
+
+ if (c->flags & CHN_F_HAS_SIZE) {
+ /*
+ * The application has requested their own blksz/blkcnt.
+ * Just obey with it, and let them toast alone. We can
+ * clamp it to the nearest latency profile, but that would
+ * defeat the purpose of having custom control. The least
+ * we can do is round it to the nearest ^2 and align it.
+ */
+ sblksz = round_pow2(blksz);
+ sblksz -= sblksz % sndbuf_getbps(bs);
+ sblkcnt = blkcnt;
+ while (sblksz < 16 || sblksz < sndbuf_getbps(bs))
+ sblksz += sndbuf_getbps(bs);
+ if (snd_verbose > 3 && !(blksz == 0 || blkcnt == -1))
+ printf("%s: requested blksz=%d blkcnt=%d -> %d/%d\n",
+ __func__, blksz, blkcnt, sblksz, sblkcnt);
+ limit = 0;
+ }
+
+ if (c->parentchannel != NULL) {
+ pb = BUF_PARENT(c, NULL);
+ CHN_UNLOCK(c);
+ chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
+ CHN_LOCK(c);
+ limit = (limit != 0 && pb != NULL) ?
+ sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
+ c->timeout = c->parentchannel->timeout;
+ } else {
+ if (c->flags & CHN_F_HAS_SIZE) {
+ hblksz = sndbuf_xbytes(sblksz, bs, b);
+ if (snd_verbose > 3)
+ printf("%s: sblksz=%d -> hblksz=%d\n",
+ __func__, sblksz, hblksz);
+ } else
+ chn_calclatency(c->direction, latency,
+ sndbuf_getbps(b),
+ sndbuf_getbps(b) * sndbuf_getspd(b),
+ CHN_2NDBUFMAXSIZE, &hblksz, NULL);
+
+ hblksz = round_pow2(hblksz);
+ if ((hblksz << 1) > sndbuf_getmaxsize(b))
+ hblksz = sndbuf_getmaxsize(b) >> 1;
+ hblksz -= hblksz % sndbuf_getbps(b);
+ while (hblksz < 16 || hblksz < sndbuf_getbps(b))
+ hblksz += sndbuf_getbps(b);
+
+#if 0
+ hblksz = sndbuf_getmaxsize(b) >> 1;
+#endif
+ CHN_UNLOCK(c);
+ sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods,
+ c->devinfo, hblksz));
+ CHN_LOCK(c);
+
+ if (!SLIST_EMPTY(&c->children)) {
+ /*
+ * Virtual channels underneath. Set the biggest
+ * possible value for their mixing space.
+ */
+ sblksz = CHN_2NDBUFMAXSIZE >> 1;
+ sblksz -= sblksz % sndbuf_getbps(bs);
+ sblkcnt = 2;
+ limit = 0;
+ } else if (limit != 0)
+ limit = sndbuf_xbytes(sndbuf_getsize(b), b, bs);
+
+ /*
+ * Interrupt timeout
+ */
+ c->timeout = ((u_int64_t)hz * sndbuf_getblksz(b)) /
+ ((u_int64_t)sndbuf_getspd(b) * sndbuf_getbps(b));
+ if (c->timeout < 1)
+ c->timeout = 1;
+ c->timeout <<= 1;
+ }
+
+ if (limit > CHN_2NDBUFMAXSIZE)
+ limit = CHN_2NDBUFMAXSIZE;
+
+ hblksz = sblksz;
+ while ((sblksz * sblkcnt) < limit) {
+ sblksz += hblksz;
+ if ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) {
+ sblksz -= hblksz;
+ break;
+ }
+ }
+
+ if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz ||
+ sndbuf_getsize(bs) != (sblkcnt * sblksz)) {
+ ret = sndbuf_remalloc(bs, sblkcnt, sblksz);
+ if (ret != 0) {
+ printf("%s: Failed: %d %d\n", __func__,
+ sblkcnt, sblksz);
+ return ret;
+ }
+ }
+
+ /*
+ * OSSv4 docs: "By default OSS will set the low water level equal
+ * to the fragment size which is optimal in most cases."
+ */
+ c->lw = sndbuf_getblksz(bs);
+ chn_resetbuf(c);
+
+ if (snd_verbose > 3)
+ printf("%s: PCMDIR_%s (%s) timeout=%u "
+ "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n",
+ __func__, (c->direction == PCMDIR_REC) ? "REC" : "PLAY",
+ (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ c->timeout,
+ sndbuf_getsize(b), sndbuf_getblksz(b),
+ sndbuf_getblkcnt(b),
+ sndbuf_getsize(bs), sndbuf_getblksz(bs),
+ sndbuf_getblkcnt(bs), limit);
+
+ return 0;
+}
+
+int
+chn_setlatency(struct pcm_channel *c, int latency)
+{
+ CHN_LOCKASSERT(c);
+ /* Destroy blksz/blkcnt, enforce latency profile. */
+ return chn_resizebuf(c, latency, -1, 0);
+}
+
+int
+chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
+{
+ CHN_LOCKASSERT(c);
+ /* Destroy latency profile, enforce blksz/blkcnt */
+ return chn_resizebuf(c, -1, blkcnt, blksz);
+}
+
static int
chn_tryspeed(struct pcm_channel *c, int speed)
{
@@ -1012,7 +1572,7 @@ chn_tryspeed(struct pcm_channel *c, int speed)
/*
* Used to be 500. It was too big!
*/
- if (delta > 25)
+ if (delta > feeder_rate_round)
c->feederflags |= 1 << FEEDER_RATE;
else
sndbuf_setspd(bs, sndbuf_getspd(b));
@@ -1022,10 +1582,6 @@ chn_tryspeed(struct pcm_channel *c, int speed)
if (r)
goto out;
- r = chn_setblocksize(c, 0, 0);
- if (r)
- goto out;
-
if (!(c->feederflags & (1 << FEEDER_RATE)))
goto out;
@@ -1050,6 +1606,8 @@ out:
sndbuf_getfmt(b));
if (!r)
sndbuf_setfmt(bs, c->format);
+ if (!r)
+ r = chn_resizebuf(c, -2, 0, 0);
DEB(printf("setspeed done, r = %d\n", r));
return r;
} else
@@ -1063,7 +1621,9 @@ chn_setspeed(struct pcm_channel *c, int speed)
r = chn_tryspeed(c, speed);
if (r) {
- DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
+ if (snd_verbose > 3)
+ printf("Failed to set speed %d falling back to %d\n",
+ speed, oldspeed);
r = chn_tryspeed(c, oldspeed);
}
return r;
@@ -1101,190 +1661,14 @@ chn_setformat(struct pcm_channel *c, u_int32_t fmt)
r = chn_tryformat(c, fmt);
if (r) {
- DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
+ if (snd_verbose > 3)
+ printf("Format change 0x%08x failed, reverting to 0x%08x\n",
+ fmt, oldfmt);
chn_tryformat(c, oldfmt);
}
return r;
}
-/*
- * given a bufsz value, round it to a power of 2 in the min-max range
- * XXX only works if min and max are powers of 2
- */
-static int
-round_bufsz(int bufsz, int min, int max)
-{
- int tmp = min * 2;
-
- KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min));
- KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max));
- while (tmp <= bufsz)
- tmp <<= 1;
- tmp >>= 1;
- if (tmp > max)
- tmp = max;
- return tmp;
-}
-
-/*
- * set the channel's blocksize both for soft and hard buffers.
- *
- * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
- * that it has the same value for both bufsoft and bufhard.
- * blksz == -1 computes values according to a target irq rate.
- * blksz == 0 reuses previous values if available, otherwise
- * behaves as for -1
- *
- * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
- * but should be a power of 2 for bufhard to simplify life to low
- * level drivers.
- * Note, for the rec channel a large blkcnt is ok,
- * but for the play channel we want blksz as small as possible to keep
- * the delay small, because routines in the write path always try to
- * keep bufhard full.
- *
- * Unless we have good reason to, use the values suggested by the caller.
- */
-int
-chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
-{
- struct snd_dbuf *b = c->bufhard;
- struct snd_dbuf *bs = c->bufsoft;
- int irqhz, ret, maxsz, maxsize, reqblksz;
-
- CHN_LOCKASSERT(c);
- if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
- KASSERT(sndbuf_getsize(bs) == 0 ||
- sndbuf_getsize(bs) >= sndbuf_getsize(b),
- ("%s(%s): bufsoft size %d < bufhard size %d", __func__,
- c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
- return EINVAL;
- }
- c->flags |= CHN_F_SETBLOCKSIZE;
-
- ret = 0;
- DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
- if (blksz == 0 || blksz == -1) { /* let the driver choose values */
- if (blksz == -1) /* delete previous values */
- c->flags &= ~CHN_F_HAS_SIZE;
- if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
- /*
- * compute a base blksz according to the target irq
- * rate, then round to a suitable power of 2
- * in the range 16.. 2^17/2.
- * Finally compute a suitable blkcnt.
- */
- blksz = round_bufsz( (sndbuf_getbps(bs) *
- sndbuf_getspd(bs)) / chn_targetirqrate,
- 16, CHN_2NDBUFMAXSIZE / 2);
- blkcnt = CHN_2NDBUFMAXSIZE / blksz;
- } else { /* use previously defined value */
- blkcnt = sndbuf_getblkcnt(bs);
- blksz = sndbuf_getblksz(bs);
- }
- } else {
- /*
- * use supplied values if reasonable. Note that here we
- * might have blksz which is not a power of 2 if the
- * ioctl() to compute it allows such values.
- */
- ret = EINVAL;
- if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
- goto out;
- ret = 0;
- c->flags |= CHN_F_HAS_SIZE;
- }
-
- reqblksz = blksz;
- if (reqblksz < sndbuf_getbps(bs))
- reqblksz = sndbuf_getbps(bs);
- if (reqblksz % sndbuf_getbps(bs))
- reqblksz -= reqblksz % sndbuf_getbps(bs);
-
- /* adjust for different hw format/speed */
- /*
- * Now compute the approx irq rate for the given (soft) blksz,
- * reduce to the acceptable range and compute a corresponding blksz
- * for the hard buffer. Then set the channel's blocksize and
- * corresponding hardbuf value. The number of blocks used should
- * be set by the device-specific routine. In fact, even the
- * call to sndbuf_setblksz() should not be here! XXX
- */
-
- irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
- RANGE(irqhz, 16, 512);
-
- maxsz = sndbuf_getmaxsize(b);
- if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
- maxsz = CHN_2NDBUFMAXSIZE;
- blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
- 16, maxsz / 2);
-
- /* Increase the size of bufsoft if before increasing bufhard. */
- maxsize = sndbuf_getsize(b);
- if (sndbuf_getsize(bs) > maxsize)
- maxsize = sndbuf_getsize(bs);
- if (reqblksz * blkcnt > maxsize)
- maxsize = reqblksz * blkcnt;
- if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
- ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
- if (ret)
- goto out1;
- }
-
- CHN_UNLOCK(c);
- sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
- CHN_LOCK(c);
-
- /* Decrease the size of bufsoft after decreasing bufhard. */
- maxsize = sndbuf_getsize(b);
- if (reqblksz * blkcnt > maxsize)
- maxsize = reqblksz * blkcnt;
- if (maxsize > sndbuf_getsize(bs))
- printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
- c->name, sndbuf_getsize(bs), maxsize);
- if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
- ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
- if (ret)
- goto out1;
- }
-
- /*
- * OSSv4 docs: "By default OSS will set the low water level equal
- * to the fragment size which is optimal in most cases."
- */
- c->lw = sndbuf_getblksz(bs);
-
- chn_resetbuf(c);
-out1:
- KASSERT(sndbuf_getsize(bs) == 0 ||
- sndbuf_getsize(bs) >= sndbuf_getsize(b),
- ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
- __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
- blksz, maxsize, blkcnt));
-out:
- c->flags &= ~CHN_F_SETBLOCKSIZE;
-#if 0
- if (1) {
- static uint32_t kk = 0;
- printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
- sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
- sndbuf_getbps(b),
- sndbuf_getspd(b), sndbuf_getfmt(b),
- sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
- sndbuf_getbps(bs),
- sndbuf_getspd(bs), sndbuf_getfmt(bs));
- if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
- sndbuf_getblksz(b) % sndbuf_getbps(b) ||
- sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
- sndbuf_getblksz(b) % sndbuf_getbps(b)) {
- printf("%u: bps/blksz alignment screwed!\n", kk);
- }
- }
-#endif
- return ret;
-}
-
int
chn_trigger(struct pcm_channel *c, int go)
{
@@ -1372,6 +1756,7 @@ chn_buildfeeder(struct pcm_channel *c)
struct pcm_feederdesc desc;
u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
int err;
+ char fmtstr[AFMTSTR_MAXSZ];
CHN_LOCKASSERT(c);
while (chn_removefeeder(c) == 0);
@@ -1393,7 +1778,7 @@ chn_buildfeeder(struct pcm_channel *c)
} else {
if (c->flags & CHN_F_HAS_VCHAN) {
desc.type = FEEDER_MIXER;
- desc.in = 0;
+ desc.in = c->format;
} else {
DEB(printf("can't decide which feeder type to use!\n"));
return EOPNOTSUPP;
@@ -1432,7 +1817,42 @@ chn_buildfeeder(struct pcm_channel *c)
desc.out = 0;
desc.flags = 0;
DEB(printf("find feeder type %d, ", type));
- fc = feeder_getclass(&desc);
+ if (type == FEEDER_VOLUME || type == FEEDER_RATE) {
+ if (c->feeder->desc->out & AFMT_32BIT)
+ strlcpy(fmtstr,"s32le", sizeof(fmtstr));
+ else if (c->feeder->desc->out & AFMT_24BIT)
+ strlcpy(fmtstr, "s24le", sizeof(fmtstr));
+ else {
+ /*
+ * 8bit doesn't provide enough headroom
+ * for proper processing without
+ * creating too much noises. Force to
+ * 16bit instead.
+ */
+ strlcpy(fmtstr, "s16le", sizeof(fmtstr));
+ }
+ if (!(c->feeder->desc->out & AFMT_8BIT) &&
+ c->feeder->desc->out & AFMT_BIGENDIAN)
+ afmtstr_swap_endian(fmtstr);
+ if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) &&
+ !(c->feeder->desc->out & AFMT_SIGNED))
+ afmtstr_swap_sign(fmtstr);
+ desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN);
+ if (desc.in == 0)
+ desc.in = AFMT_S16_LE;
+ /* feeder_volume need stereo processing */
+ if (type == FEEDER_VOLUME ||
+ c->feeder->desc->out & AFMT_STEREO)
+ desc.in |= AFMT_STEREO;
+ desc.out = desc.in;
+ fc = feeder_getclass(&desc);
+ if (fc != NULL && fc->desc != NULL)
+ desc.flags = fc->desc->flags;
+ } else {
+ fc = feeder_getclass(&desc);
+ if (fc != NULL && fc->desc != NULL)
+ desc = *fc->desc;
+ }
DEB(printf("got %p\n", fc));
if (fc == NULL) {
DEB(printf("can't find required feeder type %d\n", type));
@@ -1441,7 +1861,7 @@ chn_buildfeeder(struct pcm_channel *c)
}
DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in));
- tmp[0] = fc->desc->in;
+ tmp[0] = desc.in;
tmp[1] = 0;
if (chn_fmtchain(c, tmp) == 0) {
DEB(printf("failed\n"));
@@ -1450,7 +1870,7 @@ chn_buildfeeder(struct pcm_channel *c)
}
DEB(printf("ok\n"));
- err = chn_addfeeder(c, fc, fc->desc);
+ err = chn_addfeeder(c, fc, &desc);
if (err) {
DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err));
@@ -1475,7 +1895,7 @@ chn_buildfeeder(struct pcm_channel *c)
sndbuf_setfmt(c->bufhard, hwfmt);
if ((flags & (1 << FEEDER_VOLUME))) {
- uint32_t parent = SOUND_MIXER_NONE;
+ u_int32_t parent = SOUND_MIXER_NONE;
int vol, left, right;
vol = 100 | (100 << 8);
@@ -1553,20 +1973,10 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
*/
}
if (flags & CHN_N_BLOCKSIZE) {
- int blksz;
/*
- * scan the children, find the lowest blocksize and use that
- * for the hard blocksize
+ * Set to default latency profile
*/
- blksz = sndbuf_getmaxsize(c->bufhard) / 2;
- SLIST_FOREACH(pce, &c->children, link) {
- child = pce->channel;
- CHN_LOCK(child);
- if (sndbuf_getblksz(child->bufhard) < blksz)
- blksz = sndbuf_getblksz(child->bufhard);
- CHN_UNLOCK(child);
- }
- chn_setblocksize(c, 2, blksz);
+ chn_setlatency(c, chn_latency);
}
if (flags & CHN_N_TRIGGER) {
int nrun;
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index d6426f7..3d4f537 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -79,6 +79,7 @@ struct pcm_channel {
u_int32_t align;
int volume;
+ int latency;
u_int32_t speed;
u_int32_t format;
u_int32_t flags;
@@ -86,7 +87,8 @@ struct pcm_channel {
u_int32_t blocks;
int direction;
- unsigned int interrupts, xruns;
+ unsigned int interrupts, xruns, feedcount;
+ unsigned int timeout;
struct snd_dbuf *bufhard, *bufsoft;
struct snddev_info *parentsnddev;
struct pcm_channel *parentchannel;
@@ -142,6 +144,7 @@ int chn_setvolume(struct pcm_channel *c, int left, int right);
int chn_setspeed(struct pcm_channel *c, int speed);
int chn_setformat(struct pcm_channel *c, u_int32_t fmt);
int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz);
+int chn_setlatency(struct pcm_channel *c, int latency);
int chn_trigger(struct pcm_channel *c, int go);
int chn_getptr(struct pcm_channel *c);
struct pcmchan_caps *chn_getcaps(struct pcm_channel *c);
@@ -181,6 +184,30 @@ int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
+#define AFMTSTR_NONE 0 /* "s16le" */
+#define AFMTSTR_SIMPLE 1 /* "s16le:s" */
+#define AFMTSTR_NUM 2 /* "s16le:2" */
+#define AFMTSTR_FULL 3 /* "s16le:stereo" */
+
+#define AFMTSTR_MAXSZ 13 /* include null terminator */
+
+#define AFMTSTR_MONO_RETURN 0
+#define AFMTSTR_STEREO_RETURN 1
+
+struct afmtstr_table {
+ char *fmtstr;
+ u_int32_t format;
+};
+
+int afmtstr_swap_sign(char *);
+int afmtstr_swap_endian(char *);
+u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int);
+u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int);
+
+extern int chn_latency;
+extern int chn_latency_profile;
+extern int report_soft_formats;
+
#define PCMDIR_VIRTUAL 2
#define PCMDIR_PLAY 1
#define PCMDIR_REC -1
@@ -218,6 +245,17 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
#define CHN_N_BLOCKSIZE 0x00000008
#define CHN_N_TRIGGER 0x00000010
+#define CHN_LATENCY_MIN 0
+#define CHN_LATENCY_MAX 10
+#define CHN_LATENCY_DEFAULT 5
+#define CHN_POLICY_MIN CHN_LATENCY_MIN
+#define CHN_POLICY_MAX CHN_LATENCY_MAX
+#define CHN_POLICY_DEFAULT CHN_LATENCY_DEFAULT
+
+#define CHN_LATENCY_PROFILE_MIN 0
+#define CHN_LATENCY_PROFILE_MAX 1
+#define CHN_LATENCY_PROFILE_DEFAULT CHN_LATENCY_PROFILE_MAX
+
/*
* This should be large enough to hold all pcm data between
* tsleeps in chn_{read,write} at the highest sample rate.
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 56f50a7..940c3fd 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -215,10 +215,12 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
fmt = 0;
break;
- case SND_DEV_DSPREC:
+ case SND_DEV_DSPHW:
+ /*
+ * HW *specific* access without channel numbering confusion
+ * caused by "first come first served" by dsp_clone().
+ */
fmt = AFMT_U8;
- if (flags & FWRITE)
- return EINVAL;
chnum = PCMCHAN(i_dev);
break;
@@ -768,7 +770,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
/* chn_sync may sleep */
if (wrch) {
CHN_LOCK(wrch);
- chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
+ chn_sync(wrch, 0);
CHN_UNLOCK(wrch);
}
break;
@@ -1085,12 +1087,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SNDCTL_DSP_GETODELAY:
if (wrch) {
- struct snd_dbuf *b = wrch->bufhard;
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
/* XXX abusive DMA update: chn_wrupdate(wrch); */
- *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
+ *arg_i = sndbuf_getready(bs);
CHN_UNLOCK(wrch);
} else
ret = EINVAL;
@@ -2131,31 +2132,29 @@ dsp_oss_syncstart(int sg_id)
static int
dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
{
- int fragln, fragsz, maxfrags, ret;
+ int ret;
+
+ if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
+ return EIO;
/* Default: success */
ret = 0;
- /* Scale policy [0..10] to fragment size [2^4..2^16]. */
- fragln = policy;
- RANGE(fragln, 0, 10);
- fragln += 4;
- fragsz = 1 << fragln;
-
- maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
-
if (rdch) {
CHN_LOCK(rdch);
- ret = chn_setblocksize(rdch, maxfrags, fragsz);
+ ret = chn_setlatency(rdch, policy);
CHN_UNLOCK(rdch);
}
if (wrch && ret == 0) {
CHN_LOCK(wrch);
- ret = chn_setblocksize(wrch, maxfrags, fragsz);
+ ret = chn_setlatency(wrch, policy);
CHN_UNLOCK(wrch);
}
+ if (ret)
+ ret = EIO;
+
return ret;
}
diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c
index 7143902..0c94319 100644
--- a/sys/dev/sound/pcm/fake.c
+++ b/sys/dev/sound/pcm/fake.c
@@ -142,10 +142,11 @@ fkchan_setup(device_t dev)
c->parentsnddev = d;
/*
* Fake channel is such a blessing in disguise. Using this,
- * we can keep track prefered virtual channel speed without
+ * we can keep track prefered virtual channel speed / format without
* querying kernel hint repetitively (see vchan_create / vchan.c).
*/
c->speed = 0;
+ c->format = 0;
snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
return c;
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
index b70bd58..4f0de4f 100644
--- a/sys/dev/sound/pcm/feeder.c
+++ b/sys/dev/sound/pcm/feeder.c
@@ -35,6 +35,11 @@ MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
#define MAXFEEDERS 256
#undef FEEDER_DEBUG
+int feeder_buffersize = FEEDBUFSZ;
+TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize);
+SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD,
+ &feeder_buffersize, FEEDBUFSZ, "feeder buffer size");
+
struct feedertab_entry {
SLIST_ENTRY(feedertab_entry) link;
struct feeder_class *feederclass;
@@ -72,6 +77,53 @@ feeder_register(void *p)
SLIST_INSERT_HEAD(&feedertab, fte, link);
feedercnt++;
+ /* initialize global variables */
+
+ if (snd_verbose < 0 || snd_verbose > 3)
+ snd_verbose = 1;
+
+ if (snd_unit < 0 || snd_unit > PCMMAXDEV)
+ snd_unit = 0;
+
+ if (snd_maxautovchans < 0 ||
+ snd_maxautovchans > SND_MAXVCHANS)
+ snd_maxautovchans = 0;
+
+ if (chn_latency < CHN_LATENCY_MIN ||
+ chn_latency > CHN_LATENCY_MAX)
+ chn_latency = CHN_LATENCY_DEFAULT;
+
+ if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
+ chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
+ chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
+
+ if (feeder_buffersize < FEEDBUFSZ_MIN ||
+ feeder_buffersize > FEEDBUFSZ_MAX)
+ feeder_buffersize = FEEDBUFSZ;
+
+ if (feeder_rate_min < FEEDRATE_MIN ||
+ feeder_rate_max < FEEDRATE_MIN ||
+ feeder_rate_min > FEEDRATE_MAX ||
+ feeder_rate_max > FEEDRATE_MAX ||
+ !(feeder_rate_min < feeder_rate_max)) {
+ feeder_rate_min = FEEDRATE_RATEMIN;
+ feeder_rate_max = FEEDRATE_RATEMAX;
+ }
+
+ if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
+ feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
+ feeder_rate_round = FEEDRATE_ROUNDHZ;
+
+ if (bootverbose)
+ printf("%s: snd_unit=%d snd_maxautovchans=%d "
+ "latency=%d feeder_buffersize=%d "
+ "feeder_rate_min=%d feeder_rate_max=%d "
+ "feeder_rate_round=%d\n",
+ __func__, snd_unit, snd_maxautovchans,
+ chn_latency, feeder_buffersize,
+ feeder_rate_min, feeder_rate_max,
+ feeder_rate_round);
+
/* we've got our root feeder so don't veto pcm loading anymore */
pcm_veto_load = 0;
@@ -259,122 +311,152 @@ chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
return 1;
}
-static struct pcm_feeder *
-feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
-{
- struct feedertab_entry *fte;
- struct pcm_feeder *try, *ret;
-
- DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out));
- if (fmtvalid(source->desc->out, to)) {
- DEB(printf("got it\n"));
- return source;
- }
-
- if (maxdepth < 0)
- return NULL;
-
- SLIST_FOREACH(fte, &feedertab, link) {
- if (fte->desc == NULL)
- continue;
- if (fte->desc->type != FEEDER_FMT)
- continue;
- if (fte->desc->in == source->desc->out) {
- try = feeder_create(fte->feederclass, fte->desc);
- if (try) {
- try->source = source;
- ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
- if (ret != NULL)
- return ret;
- feeder_destroy(try);
- }
- }
- }
- /* printf("giving up %s...\n", source->class->name); */
+/*
+ * See feeder_fmtchain() for the mumbo-jumbo ridiculous explaination
+ * of what the heck is this FMT_Q_*
+ */
+#define FMT_Q_UP 1
+#define FMT_Q_DOWN 2
+#define FMT_Q_EQ 3
+#define FMT_Q_MULTI 4
- return NULL;
-}
+/*
+ * 14bit format scoring
+ * --------------------
+ *
+ * 13 12 11 10 9 8 2 1 0 offset
+ * +---+---+---+---+---+---+-------------+---+---+
+ * | X | X | X | X | X | X | X X X X X X | X | X |
+ * +---+---+---+---+---+---+-------------+---+---+
+ * | | | | | | | | |
+ * | | | | | | | | +--> signed?
+ * | | | | | | | |
+ * | | | | | | | +------> bigendian?
+ * | | | | | | |
+ * | | | | | | +---------------> total channels
+ * | | | | | |
+ * | | | | | +------------------------> AFMT_A_LAW
+ * | | | | |
+ * | | | | +----------------------------> AFMT_MU_LAW
+ * | | | |
+ * | | | +--------------------------------> AFMT_8BIT
+ * | | |
+ * | | +------------------------------------> AFMT_16BIT
+ * | |
+ * | +----------------------------------------> AFMT_24BIT
+ * |
+ * +--------------------------------------------> AFMT_32BIT
+ */
+#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1))
+#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2))
+#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc))
+#define score_val(s1) ((s1) & 0x3f00)
+#define score_cse(s1) ((s1) & 0x7f)
-int
+u_int32_t
chn_fmtscore(u_int32_t fmt)
{
- if (fmt & AFMT_32BIT)
- return 60;
- if (fmt & AFMT_24BIT)
- return 50;
- if (fmt & AFMT_16BIT)
- return 40;
- if (fmt & (AFMT_U8|AFMT_S8))
- return 30;
- if (fmt & AFMT_MU_LAW)
- return 20;
+ u_int32_t ret;
+
+ ret = 0;
+ if (fmt & AFMT_SIGNED)
+ ret |= 1 << 0;
+ if (fmt & AFMT_BIGENDIAN)
+ ret |= 1 << 1;
+ if (fmt & AFMT_STEREO)
+ ret |= (2 & 0x3f) << 2;
+ else
+ ret |= (1 & 0x3f) << 2;
if (fmt & AFMT_A_LAW)
- return 10;
- return 0;
+ ret |= 1 << 8;
+ else if (fmt & AFMT_MU_LAW)
+ ret |= 1 << 9;
+ else if (fmt & AFMT_8BIT)
+ ret |= 1 << 10;
+ else if (fmt & AFMT_16BIT)
+ ret |= 1 << 11;
+ else if (fmt & AFMT_24BIT)
+ ret |= 1 << 12;
+ else if (fmt & AFMT_32BIT)
+ ret |= 1 << 13;
+
+ return ret;
}
-u_int32_t
-chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
+static u_int32_t
+chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
{
- u_int32_t best;
- int i, score, score2, oldscore;
+ u_int32_t best, score, score2, oldscore;
+ int i;
+
+ if (fmt == 0 || fmts == NULL || fmts[0] == 0)
+ return 0;
+
+ if (fmtvalid(fmt, fmts))
+ return fmt;
best = 0;
score = chn_fmtscore(fmt);
oldscore = 0;
for (i = 0; fmts[i] != 0; i++) {
score2 = chn_fmtscore(fmts[i]);
- if (oldscore == 0 || (score2 == score) ||
- (score2 > oldscore && score2 < score) ||
- (score2 < oldscore && score2 > score) ||
- (oldscore < score && score2 > oldscore)) {
- best = fmts[i];
- oldscore = score2;
+ if (cheq && !score_cheq(score, score2))
+ continue;
+ if (oldscore == 0 ||
+ (score_val(score2) == score_val(score)) ||
+ (score_val(score2) == score_val(oldscore)) ||
+ (score_val(score2) > score_val(oldscore) &&
+ score_val(score2) < score_val(score)) ||
+ (score_val(score2) < score_val(oldscore) &&
+ score_val(score2) > score_val(score)) ||
+ (score_val(oldscore) < score_val(score) &&
+ score_val(score2) > score_val(oldscore))) {
+ if (score_val(oldscore) != score_val(score2) ||
+ score_cse(score) == score_cse(score2) ||
+ ((score_cse(oldscore) != score_cse(score) &&
+ !score_endianeq(score, oldscore) &&
+ (score_endianeq(score, score2) ||
+ (!score_signeq(score, oldscore) &&
+ score_signeq(score, score2)))))) {
+ best = fmts[i];
+ oldscore = score2;
+ }
}
}
return best;
}
u_int32_t
-chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
+chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
{
- u_int32_t best;
- int i, score, score2, oldscore;
+ return chn_fmtbestfunc(fmt, fmts, 0);
+}
- best = 0;
- score = chn_fmtscore(fmt);
- oldscore = 0;
- for (i = 0; fmts[i] != 0; i++) {
- if ((fmt & AFMT_STEREO) == (fmts[i] & AFMT_STEREO)) {
- score2 = chn_fmtscore(fmts[i]);
- if (oldscore == 0 || (score2 == score) ||
- (score2 > oldscore && score2 < score) ||
- (score2 < oldscore && score2 > score) ||
- (oldscore < score && score2 > oldscore)) {
- best = fmts[i];
- oldscore = score2;
- }
- }
- }
- return best;
+u_int32_t
+chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
+{
+ return chn_fmtbestfunc(fmt, fmts, 1);
}
u_int32_t
chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
{
u_int32_t best1, best2;
- int score, score1, score2;
+ u_int32_t score, score1, score2;
+
+ if (fmtvalid(fmt, fmts))
+ return fmt;
best1 = chn_fmtbeststereo(fmt, fmts);
best2 = chn_fmtbestbit(fmt, fmts);
- if (best1 != 0 && best2 != 0) {
+ if (best1 != 0 && best2 != 0 && best1 != best2) {
if (fmt & AFMT_STEREO)
return best1;
else {
- score = chn_fmtscore(fmt);
- score1 = chn_fmtscore(best1);
- score2 = chn_fmtscore(best2);
+ score = score_val(chn_fmtscore(fmt));
+ score1 = score_val(chn_fmtscore(best1));
+ score2 = score_val(chn_fmtscore(best2));
if (score1 == score2 || score1 == score)
return best1;
else if (score2 == score)
@@ -389,6 +471,184 @@ chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
return best2;
}
+static struct pcm_feeder *
+feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
+{
+ struct feedertab_entry *fte, *ftebest;
+ struct pcm_feeder *try, *ret;
+ uint32_t fl, qout, qsrc, qdst;
+ int qtype;
+
+ if (to == NULL || to[0] == 0)
+ return NULL;
+
+ DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out));
+ if (fmtvalid(source->desc->out, to)) {
+ DEB(printf("got it\n"));
+ return source;
+ }
+
+ if (maxdepth < 0)
+ return NULL;
+
+ /*
+ * WARNING: THIS IS _NOT_ FOR THE FAINT HEART
+ * Disclaimer: I don't expect anybody could understand this
+ * without deep logical and mathematical analysis
+ * involving various unnamed probability theorem.
+ *
+ * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm
+ * is **extremely** difficult to digest especially when applied to
+ * large sets / numbers of random chains (feeders), each with
+ * unique characteristic providing different sets of in/out format.
+ *
+ * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic:
+ * 1) Format chains
+ * 1.1 "8bit to any, not to 8bit"
+ * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be]
+ * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be]
+ * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be]
+ * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be]
+ * 1.2 "Any to 8bit, not from 8bit"
+ * 1.2.1 sign can remain consistent, e.g: s16le -> s8
+ * 1.2.2 sign can be changed, e.g: s16le -> u8
+ * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8
+ * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8
+ * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit"
+ * 1.3.1 endian MUST remain consistent
+ * 1.3.2 sign CAN be changed
+ * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding
+ * 16bit to 24bit .
+ * 2) Channel chains (mono <-> stereo)
+ * 2.1 Both endian and sign MUST remain consistent
+ * 3) Endian chains (big endian <-> little endian)
+ * 3.1 Channels and sign MUST remain consistent
+ * 4) Sign chains (signed <-> unsigned)
+ * 4.1 Channels and endian MUST remain consistent
+ *
+ * .. and the mother of all chaining rules:
+ *
+ * Rules 0: Source and destination MUST not contain multiple selections.
+ * (qtype != FMT_Q_MULTI)
+ *
+ * First of all, our caller ( chn_fmtchain() ) will reduce the possible
+ * multiple from/to formats to a single best format using chn_fmtbest().
+ * Then, using chn_fmtscore(), we determine the chaining characteristic.
+ * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining
+ * type while still adhering above chaining rules.
+ *
+ * The need for this complicated chaining procedures is inevitable,
+ * since currently we have more than 200 different types of FEEDER_FMT
+ * doing various unique format conversion. Without this (the old way),
+ * it is possible to generate broken chain since it doesn't do any
+ * sanity checking to ensure that the output format is "properly aligned"
+ * with the direction of conversion (quality up/down/equal).
+ *
+ * Conversion: s24le to s32le
+ * Possible chain: 1) s24le -> s32le (correct, optimized)
+ * 2) s24le -> s16le -> s32le
+ * (since we have feeder_24to16 and feeder_16to32)
+ * +-- obviously broken!
+ *
+ * Using scoring mechanisme, this will ensure that the chaining
+ * process do the right thing, or at least, give the best chain
+ * possible without causing quality (the 'Q') degradation.
+ */
+
+ qdst = chn_fmtscore(to[0]);
+ qsrc = chn_fmtscore(source->desc->out);
+
+#define score_q(s1) score_val(s1)
+#define score_8bit(s1) ((s1) & 0x700)
+#define score_non8bit(s1) (!score_8bit(s1))
+#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \
+ (score_8bit(s2) && score_non8bit(s1)))
+
+#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2))
+#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2))
+#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2))
+#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \
+ 0x2 : 0x0))
+#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2)
+#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \
+ ((score_cheq(s1, s2) && \
+ score_endianeq(s1, s2)) ? \
+ 0x1 : 0x0) | \
+ ((score_cheq(s1, s2) && \
+ score_signeq(s1, s2)) ? \
+ 0x2 : 0x0))
+
+ /* Determine chaining direction and set matching flag */
+ fl = 0x3fff;
+ if (to[1] != 0) {
+ qtype = FMT_Q_MULTI;
+ printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__);
+ } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) {
+ qtype = FMT_Q_DOWN;
+ fl = FMT_Q_DOWN_FLAGS(qsrc, qdst);
+ } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) {
+ qtype = FMT_Q_UP;
+ fl = FMT_Q_UP_FLAGS(qsrc, qdst);
+ } else {
+ qtype = FMT_Q_EQ;
+ fl = FMT_Q_EQ_FLAGS(qsrc, qdst);
+ }
+
+ ftebest = NULL;
+
+ SLIST_FOREACH(fte, &feedertab, link) {
+ if (fte->desc == NULL)
+ continue;
+ if (fte->desc->type != FEEDER_FMT)
+ continue;
+ qout = chn_fmtscore(fte->desc->out);
+#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI)
+#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl)))
+#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \
+ score_q(s3) >= score_q(s1) && \
+ score_q(s3) <= score_q(s2))
+#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \
+ score_q(s3) <= score_q(s1) && \
+ score_q(s3) >= score_q(s2))
+#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \
+ score_q(s1) == score_q(s2))
+ if (fte->desc->in == source->desc->out &&
+ (FMT_Q_MULTI_VALIDATE(qtype) ||
+ (FMT_Q_FL_MATCH(fl, qout, qdst) &&
+ (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) ||
+ FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) ||
+ FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) {
+ try = feeder_create(fte->feederclass, fte->desc);
+ if (try) {
+ try->source = source;
+ ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
+ if (ret != NULL)
+ return ret;
+ feeder_destroy(try);
+ }
+ } else if (fte->desc->in == source->desc->out) {
+ /* XXX quality must be considered! */
+ if (ftebest == NULL)
+ ftebest = fte;
+ }
+ }
+
+ if (ftebest != NULL) {
+ try = feeder_create(ftebest->feederclass, ftebest->desc);
+ if (try) {
+ try->source = source;
+ ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
+ if (ret != NULL)
+ return ret;
+ feeder_destroy(try);
+ }
+ }
+
+ /* printf("giving up %s...\n", source->class->name); */
+
+ return NULL;
+}
+
u_int32_t
chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
{
@@ -401,13 +661,15 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
KASSERT(to != NULL, ("to == NULL"));
KASSERT(to[0] != 0, ("to[0] == 0"));
+ if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0)
+ return 0;
+
stop = c->feeder;
+ best = 0;
if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
from = chn_getcaps(c)->fmtlist;
- if (fmtvalid(to[0], from))
- from = to;
- else {
+ if (from[1] != 0) {
best = chn_fmtbest(to[0], from);
if (best != 0) {
tmpfrom[0] = best;
@@ -420,49 +682,60 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
tmpfrom[1] = 0;
from = tmpfrom;
if (to[1] != 0) {
- if (fmtvalid(tmpfrom[0], to)) {
- tmpto[0] = tmpfrom[0];
+ best = chn_fmtbest(from[0], to);
+ if (best != 0) {
+ tmpto[0] = best;
tmpto[1] = 0;
to = tmpto;
- } else {
- best = chn_fmtbest(tmpfrom[0], to);
- if (best != 0) {
- tmpto[0] = best;
- tmpto[1] = 0;
- to = tmpto;
- }
}
}
}
- i = 0;
- best = 0;
- bestmax = 100;
- while (from[i] != 0) {
- c->feeder->desc->out = from[i];
- try = NULL;
+#define FEEDER_FMTCHAIN_MAXDEPTH 8
+
+ try = NULL;
+
+ if (to[0] != 0 && from[0] != 0 &&
+ to[1] == 0 && from[1] == 0) {
max = 0;
- while (try == NULL && max < 8) {
+ best = from[0];
+ c->feeder->desc->out = best;
+ do {
try = feeder_fmtchain(to, c->feeder, stop, max);
- if (try == NULL)
- max++;
- }
- if (try != NULL && max < bestmax) {
- bestmax = max;
- best = from[i];
- }
- while (try != NULL && try != stop) {
- del = try;
- try = try->source;
- feeder_destroy(del);
+ DEB(if (try != NULL) {
+ printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n",
+ __func__, from[0], to[0], max)
+ });
+ } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
+ } else {
+ printf("%s: Using the old-way format chaining!\n", __func__);
+ i = 0;
+ best = 0;
+ bestmax = 100;
+ while (from[i] != 0) {
+ c->feeder->desc->out = from[i];
+ try = NULL;
+ max = 0;
+ do {
+ try = feeder_fmtchain(to, c->feeder, stop, max);
+ } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
+ if (try != NULL && max < bestmax) {
+ bestmax = max;
+ best = from[i];
+ }
+ while (try != NULL && try != stop) {
+ del = try;
+ try = try->source;
+ feeder_destroy(del);
+ }
+ i++;
}
- i++;
- }
- if (best == 0)
- return 0;
+ if (best == 0)
+ return 0;
- c->feeder->desc->out = best;
- try = feeder_fmtchain(to, c->feeder, stop, bestmax);
+ c->feeder->desc->out = best;
+ try = feeder_fmtchain(to, c->feeder, stop, bestmax);
+ }
if (try == NULL)
return 0;
@@ -521,28 +794,78 @@ static int
feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
{
struct snd_dbuf *src = source;
- int l;
- u_int8_t x;
+ int l, offset;
KASSERT(count > 0, ("feed_root: count == 0"));
/* count &= ~((1 << ch->align) - 1); */
KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
+ if (++ch->feedcount == 0)
+ ch->feedcount = 2;
+
l = min(count, sndbuf_getready(src));
- sndbuf_dispose(src, buffer, l);
/* When recording only return as much data as available */
- if (ch->direction == PCMDIR_REC)
+ if (ch->direction == PCMDIR_REC) {
+ sndbuf_dispose(src, buffer, l);
return l;
+ }
-/*
- if (l < count)
- printf("appending %d bytes\n", count - l);
-*/
- x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
- while (l < count)
- buffer[l++] = x;
+ offset = count - l;
+
+ if (offset > 0) {
+ if (snd_verbose > 3)
+ printf("%s: (%s) %spending %d bytes "
+ "(count=%d l=%d feed=%d)\n",
+ __func__,
+ (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ (ch->feedcount == 1) ? "pre" : "ap",
+ offset, count, l, ch->feedcount);
+
+ if (ch->feedcount == 1) {
+ if (offset > 0)
+ memset(buffer,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (l > 0)
+ sndbuf_dispose(src, buffer + offset, l);
+ else
+ ch->feedcount--;
+ } else {
+ if (l > 0)
+ sndbuf_dispose(src, buffer, l);
+ if (offset > 0) {
+#if 1
+ memset(buffer + l,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (!(ch->flags & CHN_F_CLOSING))
+ ch->xruns++;
+#else
+ if (l < 1 || (ch->flags & CHN_F_CLOSING)) {
+ memset(buffer + l,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (!(ch->flags & CHN_F_CLOSING))
+ ch->xruns++;
+ } else {
+ int cp, tgt;
+
+ tgt = l;
+ while (offset > 0) {
+ cp = min(l, offset);
+ memcpy(buffer + tgt, buffer, cp);
+ offset -= cp;
+ tgt += cp;
+ }
+ ch->xruns++;
+ }
+#endif
+ }
+ }
+ } else if (l > 0)
+ sndbuf_dispose(src, buffer, l);
return count;
}
@@ -561,8 +884,3 @@ static struct feeder_class feeder_root_class = {
};
SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
-
-
-
-
-
diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h
index 545fd24..a3985c0 100644
--- a/sys/dev/sound/pcm/feeder.h
+++ b/sys/dev/sound/pcm/feeder.h
@@ -53,7 +53,7 @@ struct pcm_feeder {
void feeder_register(void *p);
struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc);
-int chn_fmtscore(u_int32_t fmt);
+u_int32_t chn_fmtscore(u_int32_t fmt);
u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts);
u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts);
@@ -72,11 +72,11 @@ static struct feeder_class feeder ## _class = { \
.desc = feeder ## _desc, \
.data = pdata, \
}; \
-SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class);
+SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class);
#define FEEDER_ROOT 1
#define FEEDER_FMT 2
-#define FEEDER_MIXER 3
+#define FEEDER_MIXER 3
#define FEEDER_RATE 4
#define FEEDER_FILTER 5
#define FEEDER_VOLUME 6
@@ -85,4 +85,27 @@ SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _cl
#define FEEDRATE_SRC 1
#define FEEDRATE_DST 2
+#define FEEDRATE_RATEMIN 1
+#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */
+#define FEEDRATE_MIN 1
+#define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */
+
+#define FEEDRATE_ROUNDHZ 25
+#define FEEDRATE_ROUNDHZ_MIN 0
+#define FEEDRATE_ROUNDHZ_MAX 500
+
+/*
+ * Default buffer size for feeder processing.
+ *
+ * Big = less sndbuf_feed(), more memory usage.
+ * Small = aggresive sndbuf_feed() (perhaps too much), less memory usage.
+ */
+#define FEEDBUFSZ 16384
+#define FEEDBUFSZ_MIN 2048
+#define FEEDBUFSZ_MAX 131072
+
+extern int feeder_rate_min;
+extern int feeder_rate_max;
+extern int feeder_rate_round;
+extern int feeder_buffersize;
diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c
index 17cd030..0ccc7b9 100644
--- a/sys/dev/sound/pcm/feeder_fmt.c
+++ b/sys/dev/sound/pcm/feeder_fmt.c
@@ -43,12 +43,13 @@ SND_DECLARE_FILE("$FreeBSD$");
MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder");
-#define FEEDBUFSZ 8192
-#define FEEDBUF24SZ 8190
-
#define FMT_TRACE(x...) /* printf(x) */
#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */
-#define FMT_ALIGNBYTE(x) /* x */
+
+struct feed_fmt_info {
+ int bps, bufsz;
+ uint8_t *buffer;
+};
/*
* Sign inverted ulaw/alaw -> 8 table
@@ -194,33 +195,48 @@ static uint8_t u8_to_alaw_tbl[] = {
};
static int
-feed_table_u8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_table_8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int j, k = FEEDER_FEED(f->source, c, b, count, source);
+ int j, sign, k;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 1)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count, source);
j = k;
+ sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80;
while (j > 0) {
j--;
- b[j] = tbl[b[j]] ^ 0x80;
+ b[j] = tbl[b[j]] ^ sign;
}
return k;
}
static int
-feed_table_s16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_table_16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, sign, k;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
i = k;
k <<= 1;
j = k;
- while (i > 0) {
- b[--j] = tbl[b[--i]];
- b[--j] = 0;
+ sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80;
+ if (f->desc->out & AFMT_BIGENDIAN) {
+ while (i > 0) {
+ b[--j] = 0;
+ b[--j] = tbl[b[--i]] ^ sign;
+ }
+ } else {
+ while (i > 0) {
+ b[--j] = tbl[b[--i]] ^ sign;
+ b[--j] = 0;
+ }
}
return k;
}
@@ -229,188 +245,286 @@ static int
feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int j, k = FEEDER_FEED(f->source, c, b, count, source);
+ int j, sign, k;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 1)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count, source);
j = k;
+ sign = (f->desc->in & AFMT_SIGNED) ? 0x80 : 0x00;
while (j > 0) {
j--;
- b[j] = tbl[b[j]];
+ b[j] = tbl[b[j] ^ sign];
}
return k;
}
-static struct pcm_feederdesc feeder_ulawtou8_desc[] = {
+static struct pcm_feederdesc feeder_ulawto8_desc[] = {
{FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
{FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_ulawtou8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table_u8),
+static kobj_method_t feeder_ulawto8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_8),
{0, 0}
};
-FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_s8_tbl);
+FEEDER_DECLARE(feeder_ulawto8, 0, ulaw_to_s8_tbl);
-static struct pcm_feederdesc feeder_alawtou8_desc[] = {
+static struct pcm_feederdesc feeder_alawto8_desc[] = {
{FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0},
{FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_alawtou8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table_u8),
+static kobj_method_t feeder_alawto8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_8),
{0, 0}
};
-FEEDER_DECLARE(feeder_alawtou8, 0, alaw_to_s8_tbl);
+FEEDER_DECLARE(feeder_alawto8, 0, alaw_to_s8_tbl);
-static struct pcm_feederdesc feeder_ulawtos16le_desc[] = {
+static struct pcm_feederdesc feeder_ulawto16_desc[] = {
{FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_ulawtos16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table_s16le),
+static kobj_method_t feeder_ulawto16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_16),
{0, 0}
};
-FEEDER_DECLARE(feeder_ulawtos16le, 0, ulaw_to_s8_tbl);
+FEEDER_DECLARE(feeder_ulawto16, 0, ulaw_to_s8_tbl);
-static struct pcm_feederdesc feeder_alawtos16le_desc[] = {
+static struct pcm_feederdesc feeder_alawto16_desc[] = {
{FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_alawtos16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table_s16le),
+static kobj_method_t feeder_alawto16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_16),
{0, 0}
};
-FEEDER_DECLARE(feeder_alawtos16le, 0, alaw_to_s8_tbl);
+FEEDER_DECLARE(feeder_alawto16, 0, alaw_to_s8_tbl);
-static struct pcm_feederdesc feeder_u8toulaw_desc[] = {
+static struct pcm_feederdesc feeder_8toulaw_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0},
{FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_MU_LAW, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_u8toulaw_methods[] = {
+static kobj_method_t feeder_8toulaw_methods[] = {
KOBJMETHOD(feeder_feed, feed_table_xlaw),
{0, 0}
};
-FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw_tbl);
+FEEDER_DECLARE(feeder_8toulaw, 0, u8_to_ulaw_tbl);
-static struct pcm_feederdesc feeder_u8toalaw_desc[] = {
+static struct pcm_feederdesc feeder_8toalaw_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0},
{FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_A_LAW, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_u8toalaw_methods[] = {
+static kobj_method_t feeder_8toalaw_methods[] = {
KOBJMETHOD(feeder_feed, feed_table_xlaw),
{0, 0}
};
-FEEDER_DECLARE(feeder_u8toalaw, 0, u8_to_alaw_tbl);
+FEEDER_DECLARE(feeder_8toalaw, 0, u8_to_alaw_tbl);
/*
- * Conversion rules:-
- * 1. BE -> LE
- * 2. if fmt == u8 , u8 -> s8 (economical)
- * 3. Xle -> 16le
- * 4. if fmt != u8 && fmt == u16le , u16le -> s16le
- * 4. s16le mono -> s16le stereo
- *
* All conversion done in byte level to preserve endianess.
*/
static int
-feed_common_init(struct pcm_feeder *f)
+feed_fmt_init(struct pcm_feeder *f)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT|M_ZERO);
- if (f->data == NULL)
+ struct feed_fmt_info *info;
+
+ info = malloc(sizeof(*info), M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return ENOMEM;
+
+ info->bps = 1;
+ info->bps <<= (f->desc->in & AFMT_STEREO) ? 1 : 0;
+ if (f->desc->in & AFMT_16BIT)
+ info->bps <<= 1;
+ else if (f->desc->in & AFMT_24BIT)
+ info->bps *= 3;
+ else if (f->desc->in & AFMT_32BIT)
+ info->bps <<= 2;
+ info->bufsz = feeder_buffersize - (feeder_buffersize % info->bps);
+
+ info->buffer = malloc(info->bufsz, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ if (info->buffer == NULL) {
+ free(info, M_FMTFEEDER);
return ENOMEM;
+ }
+
+ f->data = info;
+
return 0;
}
static int
-feed_common_free(struct pcm_feeder *f)
+feed_fmt_free(struct pcm_feeder *f)
{
- if (f->data)
- free(f->data, M_FMTFEEDER);
+ struct feed_fmt_info *info = f->data;
+
+ if (info) {
+ if (info->buffer)
+ free(info->buffer, M_FMTFEEDER);
+ free(info, M_FMTFEEDER);
+ }
f->data = NULL;
return 0;
}
+#define swap_sign(fdr) \
+ (((((fdr)->desc->in & AFMT_SIGNED) && \
+ !((fdr)->desc->out & AFMT_SIGNED)) || \
+ (!((fdr)->desc->in & AFMT_SIGNED) && \
+ ((fdr)->desc->out & AFMT_SIGNED))) ? 0x80 : 0x00)
+
/*
* Bit conversion
*/
static int
-feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_8to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, k;
+ int sign;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
i = k;
k <<= 1;
j = k;
- while (i > 0) {
- b[--j] = b[--i];
- b[--j] = 0;
+ sign = swap_sign(f);
+ if (f->desc->out & AFMT_BIGENDIAN) {
+ while (i > 0) {
+ b[--j] = 0;
+ b[--j] = b[--i] ^ sign;
+ }
+ } else {
+ while (i > 0) {
+ b[--j] = b[--i] ^ sign;
+ b[--j] = 0;
+ }
}
return k;
}
-static struct pcm_feederdesc feeder_8to16le_desc[] = {
+static struct pcm_feederdesc feeder_8to16_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0},
{FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U8, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U8, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U8, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_8to16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_8to16le),
+static kobj_method_t feeder_8to16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_8to16),
{0, 0}
};
-FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+FEEDER_DECLARE(feeder_8to16, 0, NULL);
static int
-feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_16to8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
+ if (count < 1)
+ return 0;
k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k;
j = k >> 1;
- while (i > 0) {
- b[--j] = src[--i];
- i--;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 1) {
+ i--;
+ b[--j] = src[--i] ^ sign;
+ }
+ } else {
+ while (i > 1) {
+ b[--j] = src[--i] ^ sign;
+ i--;
+ }
}
return k >> 1;
}
-static struct pcm_feederdesc feeder_16leto8_desc[] = {
+static struct pcm_feederdesc feeder_16to8_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0},
{FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0},
{FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_16leto8_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_16leto8),
+static kobj_method_t feeder_16to8_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_16to8),
{0, 0}
};
-FEEDER_DECLARE(feeder_16leto8, 0, NULL);
+FEEDER_DECLARE(feeder_16to8, 0, NULL);
static int
-feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_16to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k;
+ int i, j, k, sign;
+ if (count < 3)
+ return 0;
k = (count / 3) << 1;
k = FEEDER_FEED(f->source, c, b, k, source);
if (k < 2) {
@@ -419,146 +533,370 @@ feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
return 0;
}
FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k;
j = (k >> 1) * 3;
k = j;
- while (i > 0) {
- b[--j] = b[--i];
- b[--j] = b[--i];
- b[--j] = 0;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 1) {
+ b[--j] = 0;
+ b[--j] = b[--i];
+ b[--j] = b[--i] ^ sign;
+ }
+ } else {
+ while (i > 1) {
+ b[--j] = b[--i] ^ sign;
+ b[--j] = b[--i];
+ b[--j] = 0;
+ }
}
return k;
}
-static struct pcm_feederdesc feeder_16leto24le_desc[] = {
+static struct pcm_feederdesc feeder_16to24_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0},
{FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0},
{FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_16leto24le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_16leto24le),
+static kobj_method_t feeder_16to24_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_16to24),
{0, 0}
};
-FEEDER_DECLARE(feeder_16leto24le, 0, NULL);
+FEEDER_DECLARE(feeder_16to24, 0, NULL);
static int
-feed_24leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_24to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
- k = (count * 3) >> 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source);
+ if (count < 2)
+ return 0;
+ k = (count >> 1) * 3;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k -= k % 3);
+ k -= k % 3;
i = (k / 3) << 1;
j = i;
- while (i > 0) {
- b[--i] = src[--k];
- b[--i] = src[--k];
- k--;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 1) {
+ k--;
+ b[--i] = src[--k];
+ b[--i] = src[--k] ^ sign;
+ }
+ } else {
+ while (i > 1) {
+ b[--i] = src[--k] ^ sign;
+ b[--i] = src[--k];
+ k--;
+ }
}
return j;
}
-static struct pcm_feederdesc feeder_24leto16le_desc[] = {
+static struct pcm_feederdesc feeder_24to16_desc[] = {
{FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0},
{FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_24leto16le_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_24leto16le),
+static kobj_method_t feeder_24to16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_24to16),
{0, 0}
};
-FEEDER_DECLARE(feeder_24leto16le, 1, NULL);
+FEEDER_DECLARE(feeder_24to16, 1, NULL);
static int
-feed_16leto32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_16to32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, sign, k;
+
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 1, source);
if (k < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k;
j = k << 1;
k = j;
- while (i > 0) {
- b[--j] = b[--i];
- b[--j] = b[--i];
- b[--j] = 0;
- b[--j] = 0;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 1) {
+ b[--j] = 0;
+ b[--j] = 0;
+ b[--j] = b[--i];
+ b[--j] = b[--i] ^ sign;
+ }
+ } else {
+ while (i > 1) {
+ b[--j] = b[--i] ^ sign;
+ b[--j] = b[--i];
+ b[--j] = 0;
+ b[--j] = 0;
+ }
}
return k;
}
-static struct pcm_feederdesc feeder_16leto32le_desc[] = {
+static struct pcm_feederdesc feeder_16to32_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0},
{FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0},
{FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_16leto32le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_16leto32le),
+static kobj_method_t feeder_16to32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_16to32),
{0, 0}
};
-FEEDER_DECLARE(feeder_16leto32le, 0, NULL);
+FEEDER_DECLARE(feeder_16to32, 0, NULL);
static int
-feed_32leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_32to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 2)
+ return 0;
+ k = (count & ~1) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~3);
+ k &= ~3;
i = k;
k >>= 1;
j = k;
- while (i > 0) {
- b[--j] = src[--i];
- b[--j] = src[--i];
- i -= 2;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 3) {
+ i -= 2;
+ b[--j] = src[--i];
+ b[--j] = src[--i] ^ sign;
+ }
+ } else {
+ while (i > 3) {
+ b[--j] = src[--i] ^ sign;
+ b[--j] = src[--i];
+ i -= 2;
+ }
}
return k;
}
-static struct pcm_feederdesc feeder_32leto16le_desc[] = {
+static struct pcm_feederdesc feeder_32to16_desc[] = {
{FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0},
{FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_32to16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_32to16),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_32to16, 1, NULL);
+
+static int
+feed_24to32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j, k, sign;
+
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count >> 2) * 3, source);
+ if (k < 3) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__);
+ k -= k % 3;
+ i = k;
+ k = (k / 3) << 2;
+ j = k;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 2) {
+ b[--j] = 0;
+ b[--j] = b[--i];
+ b[--j] = b[--i];
+ b[--j] = b[--i] ^ sign;
+ }
+ } else {
+ while (i > 2) {
+ b[--j] = b[--i] ^ sign;
+ b[--j] = b[--i];
+ b[--j] = b[--i];
+ b[--j] = 0;
+ }
+ }
+ return k;
+}
+static struct pcm_feederdesc feeder_24to32_desc[] = {
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_32leto16le_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_32leto16le),
+static kobj_method_t feeder_24to32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_24to32),
{0, 0}
};
-FEEDER_DECLARE(feeder_32leto16le, 1, NULL);
+FEEDER_DECLARE(feeder_24to32, 1, NULL);
+
+static int
+feed_32to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
+
+ if (count < 3)
+ return 0;
+ k = (count / 3) << 2;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
+ if (k < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__);
+ k &= ~3;
+ i = k;
+ k = (k >> 2) * 3;
+ j = k;
+ sign = swap_sign(f);
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 3) {
+ --i;
+ b[--j] = src[--i];
+ b[--j] = src[--i];
+ b[--j] = src[--i] ^ sign;
+ }
+ } else {
+ while (i > 3) {
+ b[--j] = src[--i] ^ sign;
+ b[--j] = src[--i];
+ b[--j] = src[--i];
+ --i;
+ }
+ }
+ return k;
+}
+static struct pcm_feederdesc feeder_32to24_desc[] = {
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_32to24_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_32to24),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_32to24, 1, NULL);
/*
* Bit conversion end
*/
@@ -570,8 +908,11 @@ static int
feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, k;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
i = k;
j = k << 1;
while (i > 0) {
@@ -583,6 +924,8 @@ feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
static struct pcm_feederdesc feeder_monotostereo8_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_U8|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S8, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_MU_LAW|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_A_LAW|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
static kobj_method_t feeder_monotostereo8_methods[] = {
@@ -595,19 +938,22 @@ static int
feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, k;
uint8_t l, m;
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 1, source);
if (k < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k;
j = k << 1;
- while (i > 0) {
+ while (i > 1) {
l = b[--i];
m = b[--i];
b[--j] = l;
@@ -634,19 +980,22 @@ static int
feed_monotostereo24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, k;
uint8_t l, m, n;
+ if (count < 6)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, ((count / 6) * 6) >> 1, source);
if (k < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k -= k % 3);
+ k -= k % 3;
i = k;
j = k << 1;
- while (i > 0) {
+ while (i > 2) {
l = b[--i];
m = b[--i];
n = b[--i];
@@ -676,19 +1025,22 @@ static int
feed_monotostereo32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ int i, j, k;
uint8_t l, m, n, o;
+ if (count < 8)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~7) >> 1, source);
if (k < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~3);
+ k &= ~3;
i = k;
j = k << 1;
- while (i > 0) {
+ while (i > 3) {
l = b[--i];
m = b[--i];
n = b[--i];
@@ -727,18 +1079,21 @@ static int
feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
+ if (count < 1)
+ return 0;
k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k >> 1;
j = i;
while (i > 0) {
@@ -750,11 +1105,13 @@ feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
static struct pcm_feederdesc feeder_stereotomono8_desc[] = {
{FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U8, 0},
{FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_MU_LAW, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_A_LAW, 0},
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono8_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono8),
{0, 0}
};
@@ -764,21 +1121,24 @@ static int
feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 2)
+ return 0;
+ k = (count & ~1) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 3, "%s: Bytes not 16bit (stereo) aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~3);
+ k &= ~3;
i = k >> 1;
j = i;
- while (i > 0) {
+ while (i > 1) {
k -= 2;
b[--i] = src[--k];
b[--i] = src[--k];
@@ -793,8 +1153,8 @@ static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono16_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono16),
{0, 0}
};
@@ -804,21 +1164,24 @@ static int
feed_stereotomono24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source);
+ if (count < 3)
+ return 0;
+ k = ((count / 3) * 3) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 6) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k % 6, "%s: Bytes not 24bit (stereo) aligned.\n", __func__);
- FMT_ALIGNBYTE(k -= k % 6);
+ k -= k % 6;
i = k >> 1;
j = i;
- while (i > 0) {
+ while (i > 2) {
k -= 3;
b[--i] = src[--k];
b[--i] = src[--k];
@@ -834,8 +1197,8 @@ static struct pcm_feederdesc feeder_stereotomono24_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono24_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono24),
{0, 0}
};
@@ -845,21 +1208,24 @@ static int
feed_stereotomono32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 4)
+ return 0;
+ k = (count & ~3) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 8) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
return 0;
}
FMT_TEST(k & 7, "%s: Bytes not 32bit (stereo) aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~7);
+ k &= ~7;
i = k >> 1;
j = i;
- while (i > 0) {
+ while (i > 3) {
k -= 4;
b[--i] = src[--k];
b[--i] = src[--k];
@@ -876,8 +1242,8 @@ static struct pcm_feederdesc feeder_stereotomono32_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono32_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono32),
{0, 0}
};
@@ -893,8 +1259,11 @@ static int
feed_sign8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
+ if (count < 1)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count, source);
i = j;
while (i > 0)
b[--i] ^= 0x80;
@@ -914,103 +1283,145 @@ static kobj_method_t feeder_sign8_methods[] = {
FEEDER_DECLARE(feeder_sign8, 0, NULL);
static int
-feed_sign16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_sign16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
+ if (count < 2)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~1, source);
if (j < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j &= ~1);
+ j &= ~1;
i = j;
- while (i > 0) {
- b[--i] ^= 0x80;
- i--;
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 1) {
+ i--;
+ b[--i] ^= 0x80;
+ }
+ } else {
+ while (i > 1) {
+ b[--i] ^= 0x80;
+ i--;
+ }
}
return j;
}
-static struct pcm_feederdesc feeder_sign16le_desc[] = {
+static struct pcm_feederdesc feeder_sign16_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0},
{FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0},
{FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_sign16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign16le),
+static kobj_method_t feeder_sign16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign16),
{0, 0}
};
-FEEDER_DECLARE(feeder_sign16le, 0, NULL);
+FEEDER_DECLARE(feeder_sign16, 0, NULL);
static int
-feed_sign24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_sign24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
+ if (count < 3)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source);
if (j < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j -= j % 3);
+ j -= j % 3;
i = j;
- while (i > 0) {
- b[--i] ^= 0x80;
- i -= 2;
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 2) {
+ i -= 2;
+ b[--i] ^= 0x80;
+ }
+ } else {
+ while (i > 2) {
+ b[--i] ^= 0x80;
+ i -= 2;
+ }
}
return j;
}
-static struct pcm_feederdesc feeder_sign24le_desc[] = {
+static struct pcm_feederdesc feeder_sign24_desc[] = {
{FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0},
{FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0},
{FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_sign24le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign24le),
+static kobj_method_t feeder_sign24_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign24),
{0, 0}
};
-FEEDER_DECLARE(feeder_sign24le, 0, NULL);
+FEEDER_DECLARE(feeder_sign24, 0, NULL);
static int
-feed_sign32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+feed_sign32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
+ if (count < 4)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~3, source);
if (j < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j &= ~3);
+ j &= ~3;
i = j;
- while (i > 0) {
- b[--i] ^= 0x80;
- i -= 3;
+ if (f->desc->in & AFMT_BIGENDIAN) {
+ while (i > 3) {
+ i -= 3;
+ b[--i] ^= 0x80;
+ }
+ } else {
+ while (i > 3) {
+ b[--i] ^= 0x80;
+ i -= 3;
+ }
}
return j;
}
-static struct pcm_feederdesc feeder_sign32le_desc[] = {
+static struct pcm_feederdesc feeder_sign32_desc[] = {
{FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0},
{FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0},
{FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_sign32le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign32le),
+static kobj_method_t feeder_sign32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign32),
{0, 0}
};
-FEEDER_DECLARE(feeder_sign32le, 0, NULL);
+FEEDER_DECLARE(feeder_sign32, 0, NULL);
/*
* Sign conversion end.
*/
@@ -1022,18 +1433,21 @@ static int
feed_endian16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
uint8_t v;
+ if (count < 2)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~1, source);
if (j < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j &= ~1);
+ j &= ~1;
i = j;
- while (i > 0) {
+ while (i > 1) {
v = b[--i];
b[i] = b[i - 1];
b[--i] = v;
@@ -1061,18 +1475,21 @@ static int
feed_endian24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
uint8_t v;
+ if (count < 3)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source);
if (j < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j -= j % 3);
+ j -= j % 3;
i = j;
- while (i > 0) {
+ while (i > 2) {
v = b[--i];
b[i] = b[i - 2];
b[i -= 2] = v;
@@ -1100,18 +1517,21 @@ static int
feed_endian32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
- int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j;
uint8_t l, m;
+ if (count < 4)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~3, source);
if (j < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
return 0;
}
FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__);
- FMT_ALIGNBYTE(j &= ~3);
+ j &= ~3;
i = j;
- while (i > 0) {
+ while (i > 3) {
l = b[--i];
m = b[--i];
b[i] = b[i - 1];
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index b4496d1..a3fe99d 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -25,6 +25,15 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * 2006-02-21:
+ * ==========
+ *
+ * Major cleanup and overhaul to remove much redundant codes.
+ * Highlights:
+ * 1) Support for signed / unsigned 16, 24 and 32 bit,
+ * big / little endian,
+ * 2) Unlimited channels.
+ *
* 2005-06-11:
* ==========
*
@@ -68,30 +77,24 @@
SND_DECLARE_FILE("$FreeBSD$");
-#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
-#define RATE_TEST(x, y) /* if (!(x)) printf y */
-#define RATE_TRACE(x...) /* printf(x) */
+#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
+#define RATE_TEST(x, y) /* if (!(x)) printf y */
+#define RATE_TRACE(x...) /* printf(x) */
MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
-#define FEEDBUFSZ 8192
-#define ROUNDHZ 25
-#define RATEMIN 4000
-/* 8000 * 138 or 11025 * 100 . This is insane, indeed! */
-#define RATEMAX 1102500
-#define MINGAIN 92
-#define MAXGAIN 96
-
-#define FEEDRATE_CONVERT_64 0
-#define FEEDRATE_CONVERT_SCALE64 1
-#define FEEDRATE_CONVERT_SCALE32 2
-#define FEEDRATE_CONVERT_PLAIN 3
-#define FEEDRATE_CONVERT_FIXED 4
-#define FEEDRATE_CONVERT_OPTIMAL 5
-#define FEEDRATE_CONVERT_WORST 6
-
-#define FEEDRATE_64_MAXROLL 32
-#define FEEDRATE_32_MAXROLL 16
+/*
+ * Don't overflow 32bit integer, since everything is done
+ * within 32bit arithmetic.
+ */
+#define RATE_FACTOR_MIN 1
+#define RATE_FACTOR_MAX PCM_S24_MAX
+#define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \
+ (val) > RATE_FACTOR_MAX))
+
+struct feed_rate_info;
+
+typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, uint8_t *, uint32_t);
struct feed_rate_info {
uint32_t src, dst; /* rounded source / destination rates */
@@ -99,138 +102,148 @@ struct feed_rate_info {
uint32_t gx, gy; /* interpolation / decimation ratio */
uint32_t alpha; /* interpolation distance */
uint32_t pos, bpos; /* current sample / buffer positions */
- uint32_t bufsz; /* total buffer size */
+ uint32_t bufsz; /* total buffer size limit */
+ uint32_t bufsz_init; /* allocated buffer size */
+ uint32_t channels; /* total channels */
+ uint32_t bps; /* bytes-per-sample */
uint32_t stray; /* stray bytes */
- int32_t scale, roll; /* scale / roll factor */
- int16_t *buffer;
- uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t);
+ uint8_t *buffer;
+ feed_rate_converter convert;
};
-static uint32_t
-feed_convert_64(struct feed_rate_info *, int16_t *, uint32_t);
-static uint32_t
-feed_convert_scale64(struct feed_rate_info *, int16_t *, uint32_t);
-static uint32_t
-feed_convert_scale32(struct feed_rate_info *, int16_t *, uint32_t);
-static uint32_t
-feed_convert_plain(struct feed_rate_info *, int16_t *, uint32_t);
-
-int feeder_rate_ratemin = RATEMIN;
-int feeder_rate_ratemax = RATEMAX;
-/*
- * See 'Feeder Scaling Type' below..
- */
-static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL;
-static int feeder_rate_buffersize = FEEDBUFSZ & ~1;
+int feeder_rate_min = FEEDRATE_RATEMIN;
+int feeder_rate_max = FEEDRATE_RATEMAX;
+int feeder_rate_round = FEEDRATE_ROUNDHZ;
-/*
- * sysctls.. I love sysctls..
- */
-TUNABLE_INT("hw.snd.feeder_rate_ratemin", &feeder_rate_ratemin);
-TUNABLE_INT("hw.snd.feeder_rate_ratemax", &feeder_rate_ratemin);
-TUNABLE_INT("hw.snd.feeder_rate_scaling", &feeder_rate_scaling);
-TUNABLE_INT("hw.snd.feeder_rate_buffersize", &feeder_rate_buffersize);
+TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min);
+TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max);
+TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round);
static int
-sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = feeder_rate_ratemin;
+ val = feeder_rate_min;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val < 1 || val >= feeder_rate_ratemax)
- err = EINVAL;
+ if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max)
+ feeder_rate_min = val;
else
- feeder_rate_ratemin = val;
- return err;
-}
-SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemin, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", "");
-
-static int
-sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS)
-{
- int err, val;
-
- val = feeder_rate_ratemax;
- err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val <= feeder_rate_ratemin || val > 0x7fffff)
err = EINVAL;
- else
- feeder_rate_ratemax = val;
return err;
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemax, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", "");
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I",
+ "minimum allowable rate");
static int
-sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = feeder_rate_scaling;
+ val = feeder_rate_max;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- /*
- * Feeder Scaling Type
- * ===================
- *
- * 1. Plain 64bit (high precision)
- * 2. 64bit scaling (high precision, CPU friendly, but can
- * cause gain up/down).
- * 3. 32bit scaling (somehow can cause hz roundup, gain
- * up/down).
- * 4. Plain copy (default if src == dst. Except if src == dst,
- * this is the worst / silly conversion method!).
- *
- * Sysctl options:-
- *
- * 0 - Plain 64bit - no fallback.
- * 1 - 64bit scaling - no fallback.
- * 2 - 32bit scaling - no fallback.
- * 3 - Plain copy - no fallback.
- * 4 - Fixed rate. Means that, choose optimal conversion method
- * without causing hz roundup.
- * 32bit scaling (as long as hz roundup does not occur),
- * 64bit scaling, Plain 64bit.
- * 5 - Optimal / CPU friendly (DEFAULT).
- * 32bit scaling, 64bit scaling, Plain 64bit
- * 6 - Optimal to worst, no 64bit arithmetic involved.
- * 32bit scaling, Plain copy.
- */
- if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST)
- err = EINVAL;
+ if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min)
+ feeder_rate_max = val;
else
- feeder_rate_scaling = val;
+ err = EINVAL;
return err;
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", "");
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_max, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I",
+ "maximum allowable rate");
static int
-sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = feeder_rate_buffersize;
+ val = feeder_rate_round;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- /*
- * Don't waste too much kernel space
- */
- if (val < 2 || val > 65536)
+ if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX)
err = EINVAL;
else
- feeder_rate_buffersize = val & ~1;
+ feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ);
return err;
}
-/* XXX: this should be settable by an user via a control tool, the sysadmin
- needs a max and min sysctl to limit what an user can do */
-SYSCTL_PROC(_hw_snd, OID_AUTO, _feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", "");
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I",
+ "sample rate converter rounding threshold");
+
+#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
+static uint32_t \
+feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \
+ uint8_t *dst, uint32_t max) \
+{ \
+ uint32_t ret, smpsz, bps, ch, pos, bpos, gx, gy, alpha, distance; \
+ int32_t x, y; \
+ int i; \
+ uint8_t *src, *sx, *sy; \
+ \
+ ret = 0; \
+ alpha = info->alpha; \
+ gx = info->gx; \
+ gy = info->gy; \
+ pos = info->pos; \
+ bpos = info->bpos; \
+ src = info->buffer + pos; \
+ ch = info->channels; \
+ bps = info->bps; \
+ smpsz = bps * ch; \
+ for (;;) { \
+ if (alpha < gx) { \
+ alpha += gy; \
+ pos += smpsz; \
+ if (pos == bpos) \
+ break; \
+ src += smpsz; \
+ } else { \
+ alpha -= gx; \
+ distance = (alpha << PCM_FXSHIFT) / gy; \
+ sx = src - smpsz; \
+ sy = src; \
+ i = ch; \
+ do { \
+ x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \
+ y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \
+ x = (((RATE_INTCAST)x * distance) + \
+ ((RATE_INTCAST)y * ((1 << PCM_FXSHIFT) - \
+ distance))) >> PCM_FXSHIFT; \
+ PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \
+ dst += bps; \
+ sx += bps; \
+ sy += bps; \
+ ret += bps; \
+ } while (--i); \
+ if (ret == max) \
+ break; \
+ } \
+ } \
+ info->alpha = alpha; \
+ info->pos = pos; \
+ return ret; \
+}
+
+FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne)
+FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le)
+FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le)
+FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le)
+FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be)
+FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be)
+FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be)
+/* unsigned */
+FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne)
+FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le)
+FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le)
+FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le)
+FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be)
+FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be)
+FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be)
static void
-feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
+feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy)
{
- uint32_t w, src = x, dst = y;
+ uint32_t w, x = src, y = dst;
while (y != 0) {
w = x % y;
@@ -242,242 +255,112 @@ feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
}
static void
-feed_scale_roll(uint32_t dst, int32_t *scale, int32_t *roll, int32_t max)
-{
- int64_t k, tscale;
- int32_t j, troll;
-
- *scale = *roll = -1;
- for (j = MAXGAIN; j >= MINGAIN; j -= 3) {
- for (troll = 0; troll < max; troll++) {
- tscale = (1 << troll) / dst;
- k = (tscale * dst * 100) >> troll;
- if (k > j && k <= 100) {
- *scale = tscale;
- *roll = troll;
- return;
- }
- }
- }
-}
-
-static int
-feed_get_best_coef(uint32_t *src, uint32_t *dst, uint32_t *gx, uint32_t *gy,
- int32_t *scale, int32_t *roll)
-{
- uint32_t tsrc, tdst, sscale, dscale;
- int32_t tscale, troll;
- int i, j, hzmin, hzmax;
-
- *scale = *roll = -1;
- for (i = 0; i < 2; i++) {
- hzmin = (ROUNDHZ * i) + 1;
- hzmax = hzmin + ROUNDHZ;
- for (j = hzmin; j < hzmax; j++) {
- tsrc = *src - (*src % j);
- tdst = *dst;
- if (tsrc < 1 || tdst < 1)
- goto coef_failed;
- feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
- feed_scale_roll(dscale, &tscale, &troll,
- FEEDRATE_32_MAXROLL);
- if (tscale != -1 && troll != -1) {
- *src = tsrc;
- *gx = sscale;
- *gy = dscale;
- *scale = tscale;
- *roll = troll;
- return j;
- }
- }
- for (j = hzmin; j < hzmax; j++) {
- tsrc = *src - (*src % j);
- tdst = *dst - (*dst % j);
- if (tsrc < 1 || tdst < 1)
- goto coef_failed;
- feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
- feed_scale_roll(dscale, &tscale, &troll,
- FEEDRATE_32_MAXROLL);
- if (tscale != -1 && troll != -1) {
- *src = tsrc;
- *dst = tdst;
- *gx = sscale;
- *gy = dscale;
- *scale = tscale;
- *roll = troll;
- return j;
- }
- }
- for (j = hzmin; j < hzmax; j++) {
- tsrc = *src;
- tdst = *dst - (*dst % j);
- if (tsrc < 1 || tdst < 1)
- goto coef_failed;
- feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
- feed_scale_roll(dscale, &tscale, &troll,
- FEEDRATE_32_MAXROLL);
- if (tscale != -1 && troll != -1) {
- *src = tsrc;
- *dst = tdst;
- *gx = sscale;
- *gy = dscale;
- *scale = tscale;
- *roll = troll;
- return j;
- }
- }
- }
-coef_failed:
- feed_speed_ratio(*src, *dst, gx, gy);
- feed_scale_roll(*gy, scale, roll, FEEDRATE_32_MAXROLL);
- return 0;
-}
-
-static void
feed_rate_reset(struct feed_rate_info *info)
{
- info->scale = -1;
- info->roll = -1;
- info->src = info->rsrc;
- info->dst = info->rdst;
- info->gx = 0;
- info->gy = 0;
+ info->src = info->rsrc - (info->rsrc %
+ ((feeder_rate_round > 0) ? feeder_rate_round : 1));
+ info->dst = info->rdst - (info->rdst %
+ ((feeder_rate_round > 0) ? feeder_rate_round : 1));
+ info->gx = 1;
+ info->gy = 1;
+ info->alpha = 0;
+ info->channels = 2;
+ info->bps = 2;
+ info->convert = NULL;
+ info->bufsz = info->bufsz_init;
+ info->pos = 4;
+ info->bpos = 8;
+ info->stray = 0;
}
static int
feed_rate_setup(struct pcm_feeder *f)
{
struct feed_rate_info *info = f->data;
- int r = 0;
+ static const struct {
+ uint32_t format; /* pcm / audio format */
+ uint32_t bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_rate_converter convert;
+ } convtbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_convert_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_convert_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be },
+ { 0, 0, NULL },
+ };
+ uint32_t i;
- info->pos = 2;
- info->bpos = 4;
- info->alpha = 0;
- info->stray = 0;
feed_rate_reset(info);
- if (info->src == info->dst) {
- /*
- * No conversion ever needed. Just do plain copy.
- */
- info->convert = feed_convert_plain;
- info->gx = 1;
- info->gy = 1;
- } else {
- switch (feeder_rate_scaling) {
- case FEEDRATE_CONVERT_64:
- feed_speed_ratio(info->src, info->dst,
- &info->gx, &info->gy);
- info->convert = feed_convert_64;
- break;
- case FEEDRATE_CONVERT_SCALE64:
- feed_speed_ratio(info->src, info->dst,
- &info->gx, &info->gy);
- feed_scale_roll(info->gy, &info->scale,
- &info->roll, FEEDRATE_64_MAXROLL);
- if (info->scale == -1 || info->roll == -1)
- return -1;
- info->convert = feed_convert_scale64;
- break;
- case FEEDRATE_CONVERT_SCALE32:
- r = feed_get_best_coef(&info->src, &info->dst,
- &info->gx, &info->gy, &info->scale,
- &info->roll);
- if (r == 0)
- return -1;
- info->convert = feed_convert_scale32;
- break;
- case FEEDRATE_CONVERT_PLAIN:
- feed_speed_ratio(info->src, info->dst,
+
+ if (info->src != info->dst)
+ feed_speed_ratio(info->src, info->dst,
&info->gx, &info->gy);
- info->convert = feed_convert_plain;
- break;
- case FEEDRATE_CONVERT_FIXED:
- r = feed_get_best_coef(&info->src, &info->dst,
- &info->gx, &info->gy, &info->scale,
- &info->roll);
- if (r != 0 && info->src == info->rsrc &&
- info->dst == info->rdst)
- info->convert = feed_convert_scale32;
- else {
- /* Fallback */
- feed_rate_reset(info);
- feed_speed_ratio(info->src, info->dst,
- &info->gx, &info->gy);
- feed_scale_roll(info->gy, &info->scale,
- &info->roll, FEEDRATE_64_MAXROLL);
- if (info->scale != -1 && info->roll != -1)
- info->convert = feed_convert_scale64;
- else
- info->convert = feed_convert_64;
- }
- break;
- case FEEDRATE_CONVERT_OPTIMAL:
- r = feed_get_best_coef(&info->src, &info->dst,
- &info->gx, &info->gy, &info->scale,
- &info->roll);
- if (r != 0)
- info->convert = feed_convert_scale32;
- else {
- /* Fallback */
- feed_rate_reset(info);
- feed_speed_ratio(info->src, info->dst,
- &info->gx, &info->gy);
- feed_scale_roll(info->gy, &info->scale,
- &info->roll, FEEDRATE_64_MAXROLL);
- if (info->scale != -1 && info->roll != -1)
- info->convert = feed_convert_scale64;
- else
- info->convert = feed_convert_64;
- }
- break;
- case FEEDRATE_CONVERT_WORST:
- r = feed_get_best_coef(&info->src, &info->dst,
- &info->gx, &info->gy, &info->scale,
- &info->roll);
- if (r != 0)
- info->convert = feed_convert_scale32;
- else {
- /* Fallback */
- feed_rate_reset(info);
- feed_speed_ratio(info->src, info->dst,
- &info->gx, &info->gy);
- info->convert = feed_convert_plain;
- }
- break;
- default:
- return -1;
- break;
- }
- /* No way! */
- if (info->gx == 0 || info->gy == 0)
+
+ if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy)))
+ return -1;
+
+ for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) {
+ if (convtbl[i].format == 0)
return -1;
- /*
- * No need to interpolate/decimate, just do plain copy.
- * This probably caused by Hz roundup.
- */
- if (info->gx == info->gy)
- info->convert = feed_convert_plain;
+ if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) {
+ info->bps = convtbl[i].bps;
+ info->convert = convtbl[i].convert;
+ break;
+ }
}
+
+ /*
+ * No need to interpolate/decimate, just do plain copy.
+ */
+ if (info->gx == info->gy)
+ info->convert = NULL;
+
+ info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
+ info->pos = info->bps * info->channels;
+ info->bpos = info->pos << 1;
+ info->bufsz -= info->bufsz % info->pos;
+
+ memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos);
+
+ RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , "
+ "format=0x%08x, channels=%u, bufsz=%u\n",
+ __func__, info->src, info->rsrc, info->dst, info->rdst,
+ info->gx, info->gy,
+ f->desc->out, info->channels,
+ info->bufsz - info->pos);
+
return 0;
}
static int
-feed_rate_set(struct pcm_feeder *f, int what, int value)
+feed_rate_set(struct pcm_feeder *f, int what, int32_t value)
{
struct feed_rate_info *info = f->data;
- if (value < feeder_rate_ratemin || value > feeder_rate_ratemax)
+ if (value < feeder_rate_min || value > feeder_rate_max)
return -1;
-
+
switch (what) {
- case FEEDRATE_SRC:
- info->rsrc = value;
- break;
- case FEEDRATE_DST:
- info->rdst = value;
- break;
- default:
- return -1;
+ case FEEDRATE_SRC:
+ info->rsrc = value;
+ break;
+ case FEEDRATE_DST:
+ info->rdst = value;
+ break;
+ default:
+ return -1;
}
return feed_rate_setup(f);
}
@@ -487,16 +370,13 @@ feed_rate_get(struct pcm_feeder *f, int what)
{
struct feed_rate_info *info = f->data;
- /*
- * Return *real* src/dst rate.
- */
switch (what) {
- case FEEDRATE_SRC:
- return info->rsrc;
- case FEEDRATE_DST:
- return info->rdst;
- default:
- return -1;
+ case FEEDRATE_SRC:
+ return info->rsrc;
+ case FEEDRATE_DST:
+ return info->rdst;
+ default:
+ return -1;
}
return -1;
}
@@ -506,14 +386,17 @@ feed_rate_init(struct pcm_feeder *f)
{
struct feed_rate_info *info;
+ if (f->desc->out != f->desc->in)
+ return EINVAL;
+
info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info == NULL)
return ENOMEM;
/*
* bufsz = sample from last cycle + conversion space
*/
- info->bufsz = 2 + feeder_rate_buffersize;
- info->buffer = malloc(sizeof(*info->buffer) * info->bufsz,
+ info->bufsz_init = 8 + feeder_buffersize;
+ info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init,
M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info->buffer == NULL) {
free(info, M_RATEFEEDER);
@@ -539,233 +422,88 @@ feed_rate_free(struct pcm_feeder *f)
return 0;
}
-static uint32_t
-feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max)
-{
- int64_t x, alpha, distance;
- uint32_t ret;
- int32_t pos, bpos, gx, gy;
- int16_t *src;
- /*
- * Plain, straight forward 64bit arith. No bit-magic applied here.
- */
- ret = 0;
- alpha = info->alpha;
- gx = info->gx;
- gy = info->gy;
- pos = info->pos;
- bpos = info->bpos;
- src = info->buffer;
- for (;;) {
- if (alpha < gx) {
- alpha += gy;
- pos += 2;
- if (pos == bpos)
- break;
- } else {
- alpha -= gx;
- distance = gy - alpha;
- x = (alpha * src[pos - 2]) + (distance * src[pos]);
- dst[ret++] = x / gy;
- x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
- dst[ret++] = x / gy;
- if (ret == max)
- break;
- }
- }
- info->alpha = alpha;
- info->pos = pos;
- return ret;
-}
-
-static uint32_t
-feed_convert_scale64(struct feed_rate_info *info, int16_t *dst, uint32_t max)
-{
- int64_t x, alpha, distance;
- uint32_t ret;
- int32_t pos, bpos, gx, gy, roll;
- int16_t *src;
- /*
- * 64bit scaling.
- */
- ret = 0;
- roll = info->roll;
- alpha = info->alpha * info->scale;
- gx = info->gx * info->scale;
- gy = info->gy * info->scale;
- pos = info->pos;
- bpos = info->bpos;
- src = info->buffer;
- for (;;) {
- if (alpha < gx) {
- alpha += gy;
- pos += 2;
- if (pos == bpos)
- break;
- } else {
- alpha -= gx;
- distance = gy - alpha;
- x = (alpha * src[pos - 2]) + (distance * src[pos]);
- dst[ret++] = x >> roll;
- x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
- dst[ret++] = x >> roll;
- if (ret == max)
- break;
- }
- }
- info->alpha = alpha / info->scale;
- info->pos = pos;
- return ret;
-}
-
-static uint32_t
-feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max)
-{
- uint32_t ret;
- int32_t x, pos, bpos, gx, gy, alpha, roll, distance;
- int16_t *src;
- /*
- * 32bit scaling.
- */
- ret = 0;
- roll = info->roll;
- alpha = info->alpha * info->scale;
- gx = info->gx * info->scale;
- gy = info->gy * info->scale;
- pos = info->pos;
- bpos = info->bpos;
- src = info->buffer;
- for (;;) {
- if (alpha < gx) {
- alpha += gy;
- pos += 2;
- if (pos == bpos)
- break;
- } else {
- alpha -= gx;
- distance = gy - alpha;
- x = (alpha * src[pos - 2]) + (distance * src[pos]);
- dst[ret++] = x >> roll;
- x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
- dst[ret++] = x >> roll;
- if (ret == max)
- break;
- }
- }
- info->alpha = alpha / info->scale;
- info->pos = pos;
- return ret;
-}
-
-static uint32_t
-feed_convert_plain(struct feed_rate_info *info, int16_t *dst, uint32_t max)
-{
- uint32_t ret;
- int32_t pos, bpos, gx, gy, alpha;
- int16_t *src;
- /*
- * Plain copy.
- */
- ret = 0;
- gx = info->gx;
- gy = info->gy;
- alpha = info->alpha;
- pos = info->pos;
- bpos = info->bpos;
- src = info->buffer;
- for (;;) {
- if (alpha < gx) {
- alpha += gy;
- pos += 2;
- if (pos == bpos)
- break;
- } else {
- alpha -= gx;
- dst[ret++] = src[pos];
- dst[ret++] = src[pos + 1];
- if (ret == max)
- break;
- }
- }
- info->pos = pos;
- info->alpha = alpha;
- return ret;
-}
-
-static int32_t
+static int
feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
- uint32_t count, void *source)
+ uint32_t count, void *source)
{
struct feed_rate_info *info = f->data;
- uint32_t i;
+ uint32_t i, smpsz;
int32_t fetch, slot;
- int16_t *dst = (int16_t *)b;
+
+ if (info->convert == NULL)
+ return FEEDER_FEED(f->source, c, b, count, source);
+
/*
* This loop has been optimized to generalize both up / down
* sampling without causing missing samples or excessive buffer
- * feeding.
+ * feeding. The tricky part is to calculate *precise* (slot) value
+ * needed for the entire conversion space since we are bound to
+ * return and fill up the buffer according to the requested 'count'.
+ * Too much feeding will cause the extra buffer stay within temporary
+ * circular buffer forever and always manifest itself as a truncated
+ * sound during end of playback / recording. Too few, and we end up
+ * with possible underruns and waste of cpu cycles.
+ *
+ * 'Stray' management exist to combat with possible unaligned
+ * buffering by the caller.
*/
- RATE_TEST(count >= 4 && (count & 3) == 0,
- ("%s: Count size not byte integral (%d)\n", __func__, count));
- if (count < 4)
+ smpsz = info->bps * info->channels;
+ RATE_TEST(count >= smpsz && (count % smpsz) == 0,
+ ("%s: Count size not sample integral (%d)\n", __func__, count));
+ if (count < smpsz)
return 0;
- count >>= 1;
- count &= ~1;
- slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1;
- RATE_TEST((slot & 1) == 0, ("%s: Slot count not sample integral (%d)\n",
- __func__, slot));
+ count -= count % smpsz;
/*
- * Optimize buffer feeding aggressively to ensure calculated slot
- * can be fitted nicely into available buffer free space, hence
- * avoiding multiple feeding.
+ * This slot count formula will stay here for the next million years
+ * to come. This is the key of our circular buffering precision.
*/
+ slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / info->gy) * smpsz;
+ RATE_TEST((slot % smpsz) == 0, ("%s: Slot count not sample integral (%d)\n",
+ __func__, slot));
RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n",
__func__,info->stray));
- if (info->pos != 2 && info->bpos - info->pos == 2 &&
+ if (info->pos != smpsz && info->bpos - info->pos == smpsz &&
info->bpos + slot > info->bufsz) {
/*
* Copy last unit sample and its previous to
* beginning of buffer.
*/
- info->buffer[0] = info->buffer[info->pos - 2];
- info->buffer[1] = info->buffer[info->pos - 1];
- info->buffer[2] = info->buffer[info->pos];
- info->buffer[3] = info->buffer[info->pos + 1];
- info->pos = 2;
- info->bpos = 4;
+ bcopy(info->buffer + info->pos - smpsz, info->buffer,
+ sizeof(*info->buffer) * (smpsz << 1));
+ info->pos = smpsz;
+ info->bpos = smpsz << 1;
}
RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n",
__func__, slot));
i = 0;
for (;;) {
for (;;) {
- fetch = (info->bufsz - info->bpos) << 1;
+ fetch = info->bufsz - info->bpos;
fetch -= info->stray;
RATE_ASSERT(fetch >= 0,
("%s: [1] Buffer overrun: %d > %d\n",
__func__, info->bpos, info->bufsz));
- if ((slot << 1) < fetch)
- fetch = slot << 1;
+ if (slot < fetch)
+ fetch = slot;
if (fetch > 0) {
- RATE_ASSERT(((info->bpos << 1) - info->stray) >= 0 &&
- ((info->bpos << 1) - info->stray) < (info->bufsz << 1),
+ RATE_ASSERT((int32_t)(info->bpos - info->stray) >= 0 &&
+ (info->bpos - info->stray) < info->bufsz,
("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", __func__,
- info->bufsz << 1, (info->bpos << 1) - info->stray));
+ info->bufsz, info->bpos - info->stray));
fetch = FEEDER_FEED(f->source, c,
- (uint8_t *)(info->buffer) + (info->bpos << 1) - info->stray,
+ info->buffer + info->bpos - info->stray,
fetch, source);
info->stray = 0;
if (fetch == 0)
break;
- RATE_TEST((fetch & 3) == 0,
- ("%s: Fetch size not byte integral (%d)\n",
+ RATE_TEST((fetch % smpsz) == 0,
+ ("%s: Fetch size not sample integral (%d)\n",
__func__, fetch));
- info->stray += fetch & 3;
+ info->stray += fetch % smpsz;
RATE_TEST(info->stray == 0,
("%s: Stray bytes detected (%d)\n",
__func__, info->stray));
- fetch >>= 1;
- fetch &= ~1;
+ fetch -= fetch % smpsz;
info->bpos += fetch;
slot -= fetch;
RATE_ASSERT(slot >= 0,
@@ -779,7 +517,7 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
break;
}
if (info->pos == info->bpos) {
- RATE_TEST(info->pos == 2,
+ RATE_TEST(info->pos == smpsz,
("%s: EOF while in progress\n", __func__));
break;
}
@@ -788,10 +526,10 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
info->pos, info->bpos));
RATE_ASSERT(info->pos < info->bpos,
("%s: Zero buffer!\n", __func__));
- RATE_ASSERT(((info->bpos - info->pos) & 1) == 0,
+ RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0,
("%s: Buffer not sample integral (%d)\n",
__func__, info->bpos - info->pos));
- i += info->convert(info, dst + i, count - i);
+ i += info->convert(info, b + i, count - i);
RATE_ASSERT(info->pos <= info->bpos,
("%s: [3] Buffer overrun: %d > %d\n",
__func__, info->pos, info->bpos));
@@ -802,31 +540,67 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
* interpolate using it.
*/
RATE_TEST(info->stray == 0, ("%s: [2] Stray bytes: %u\n", __func__, info->stray));
- info->buffer[0] = info->buffer[info->pos - 2];
- info->buffer[1] = info->buffer[info->pos - 1];
- info->bpos = 2;
- info->pos = 2;
+ bcopy(info->buffer + info->pos - smpsz, info->buffer,
+ sizeof(*info->buffer) * smpsz);
+ info->bpos = smpsz;
+ info->pos = smpsz;
}
if (i == count)
break;
}
-#if 0
- RATE_TEST(count == i, ("Expect: %u , Got: %u\n", count << 1, i << 1));
-#endif
+
+ RATE_TEST((slot == 0 && count == i) ||
+ (slot > 0 && count > i &&
+ info->pos == info->bpos && info->pos == smpsz),
+ ("%s: Inconsistent slot/count! "
+ "Count Expect: %u , Got: %u, Slot Left: %d\n",
+ __func__, count, i, slot));
+
RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, info->stray));
- return i << 1;
+
+ return i;
}
static struct pcm_feederdesc feeder_rate_desc[] = {
+ {FEEDER_RATE, AFMT_S8, AFMT_S8, 0},
+ {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0},
+ {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0},
+ {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0},
+ {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0},
+ {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0},
+ {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0},
+ {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
{FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
+ /* unsigned */
+ {FEEDER_RATE, AFMT_U8, AFMT_U8, 0},
+ {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0},
+ {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0},
+ {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0},
+ {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0},
+ {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0},
+ {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0},
+ {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
+ {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
{0, 0, 0, 0},
};
+
static kobj_method_t feeder_rate_methods[] = {
- KOBJMETHOD(feeder_init, feed_rate_init),
- KOBJMETHOD(feeder_free, feed_rate_free),
- KOBJMETHOD(feeder_set, feed_rate_set),
- KOBJMETHOD(feeder_get, feed_rate_get),
- KOBJMETHOD(feeder_feed, feed_rate),
+ KOBJMETHOD(feeder_init, feed_rate_init),
+ KOBJMETHOD(feeder_free, feed_rate_free),
+ KOBJMETHOD(feeder_set, feed_rate_set),
+ KOBJMETHOD(feeder_get, feed_rate_get),
+ KOBJMETHOD(feeder_feed, feed_rate),
{0, 0}
};
+
FEEDER_DECLARE(feeder_rate, 2, NULL);
diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c
index bb46ea0..544194c 100644
--- a/sys/dev/sound/pcm/feeder_volume.c
+++ b/sys/dev/sound/pcm/feeder_volume.c
@@ -31,48 +31,193 @@
SND_DECLARE_FILE("$FreeBSD$");
+MALLOC_DEFINE(M_VOLUMEFEEDER, "volumefeed", "pcm volume feeder");
+
+#define FVOL_TRACE(x...) /* device_printf(c->dev, x) */
+#define FVOL_TEST(x, y...) /* if (x) FVOL_TRACE(y) */
+
+#define FVOL_RESOLUTION 6 /* 6bit volume resolution */
+#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / 100)
+#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f)
+#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8)
+#define FVOL_MAX (1 << FVOL_RESOLUTION)
+#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION)
+
+struct feed_volume_info;
+
+typedef uint32_t (*feed_volume_filter)(struct feed_volume_info *,
+ uint8_t *, int *, uint32_t);
+
+struct feed_volume_info {
+ uint32_t bps, channels;
+ feed_volume_filter filter;
+};
+
+#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
+static uint32_t \
+feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(struct feed_volume_info *info, \
+ uint8_t *b, int *vol, uint32_t count) \
+{ \
+ uint32_t bps; \
+ int32_t j; \
+ int i; \
+ \
+ bps = info->bps; \
+ i = count; \
+ b += i; \
+ while (i > 0) { \
+ b -= bps; \
+ i -= bps; \
+ j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \
+ j = FVOL_CALC((VOL_INTCAST)j, vol[(i / bps) & 1]); \
+ PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \
+ } \
+ return count; \
+}
+
+FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne)
+FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le)
+FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le)
+FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le)
+FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be)
+FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be)
+FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be)
+/* unsigned */
+FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne)
+FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le)
+FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le)
+FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le)
+FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be)
+FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be)
+FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be)
+
+static int
+feed_volume_setup(struct pcm_feeder *f)
+{
+ struct feed_volume_info *info = f->data;
+ static const struct {
+ uint32_t format; /* pcm / audio format */
+ uint32_t bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_volume_filter filter;
+ } voltbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be },
+ { 0, 0, NULL },
+ };
+ uint32_t i;
+
+ for (i = 0; i < sizeof(voltbl) / sizeof(*voltbl); i++) {
+ if (voltbl[i].format == 0)
+ return -1;
+ if ((f->desc->out & ~AFMT_STEREO) == voltbl[i].format) {
+ info->bps = voltbl[i].bps;
+ info->filter = voltbl[i].filter;
+ break;
+ }
+ }
+
+ /* For now, this is mandatory! */
+ info->channels = 2;
+
+ return 0;
+}
+
static int
-feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
- uint32_t count, void *source)
+feed_volume_init(struct pcm_feeder *f)
{
- int i, j, k, vol[2];
- int16_t *buf;
+ struct feed_volume_info *info;
+
+ if (f->desc->in != f->desc->out)
+ return EINVAL;
+
+ /* Mandatory */
+ if (!(f->desc->out & AFMT_STEREO))
+ return EINVAL;
+
+ info = malloc(sizeof(*info), M_VOLUMEFEEDER, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return ENOMEM;
+ f->data = info;
+ return feed_volume_setup(f);
+}
- k = FEEDER_FEED(f->source, c, b, count & ~1, source);
- if (k < 2) {
-#if 0
- device_printf(c->dev, "%s: Not enough data (Got: %d bytes)\n",
+static int
+feed_volume_free(struct pcm_feeder *f)
+{
+ struct feed_volume_info *info = f->data;
+
+ if (info)
+ free(info, M_VOLUMEFEEDER);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ struct feed_volume_info *info = f->data;
+ uint32_t k, smpsz;
+ int vol[2];
+
+ vol[0] = FVOL_LEFT(c->volume);
+ vol[1] = FVOL_RIGHT(c->volume);
+
+ if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX)
+ return FEEDER_FEED(f->source, c, b, count, source);
+
+ smpsz = info->bps * info->channels;
+ if (count < smpsz)
+ return 0;
+ count -= count % smpsz;
+ k = FEEDER_FEED(f->source, c, b, count, source);
+ if (k < smpsz) {
+ FVOL_TRACE("%s: Not enough data (Got: %u bytes)\n",
__func__, k);
-#endif
return 0;
}
-#if 0
- if (k & 1)
- device_printf(c->dev, "%s: Bytes not 16bit aligned.\n", __func__);
-#endif
- k &= ~1;
- i = k >> 1;
- buf = (int16_t *)b;
- vol[0] = c->volume & 0x7f;
- vol[1] = (c->volume >> 8) & 0x7f;
- while (i > 0) {
- i--;
- j = (vol[i & 1] * buf[i]) / 100;
- if (j > 32767)
- j = 32767;
- if (j < -32768)
- j = -32768;
- buf[i] = j;
- }
- return k;
+ FVOL_TEST(k % smpsz, "%s: Bytes not %dbit (stereo) aligned.\n",
+ __func__, info->bps << 3);
+ k -= k % smpsz;
+ return info->filter(info, b, vol, k);
}
-static struct pcm_feederdesc feeder_volume_s16_desc[] = {
- {FEEDER_VOLUME, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+static struct pcm_feederdesc feeder_volume_desc[] = {
+ {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
+ /* unsigned */
+ {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
+ {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
{0, 0, 0, 0},
};
-static kobj_method_t feeder_volume_s16_methods[] = {
- KOBJMETHOD(feeder_feed, feed_volume_s16),
+static kobj_method_t feeder_volume_methods[] = {
+ KOBJMETHOD(feeder_init, feed_volume_init),
+ KOBJMETHOD(feeder_free, feed_volume_free),
+ KOBJMETHOD(feeder_feed, feed_volume),
{0, 0}
};
-FEEDER_DECLARE(feeder_volume_s16, 2, NULL);
+FEEDER_DECLARE(feeder_volume, 2, NULL);
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index d0f6946..3deded7 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -631,7 +631,7 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
m = oidp->oid_arg1;
snd_mtxlock(m->lock);
- strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
+ strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
snd_mtxunlock(m->lock);
error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
snd_mtxlock(m->lock);
@@ -663,9 +663,11 @@ mixer_hwvol_init(device_t dev)
m->hwvol_mixer = SOUND_MIXER_VOLUME;
m->hwvol_step = 5;
#ifdef SND_DYNSYSCTL
- SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
sysctl_hw_snd_hwvol_mixer, "A", "");
#endif
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index 5b07ece..32188ff 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -72,11 +72,11 @@ static int sndstat_files = 0;
static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
-static int sndstat_verbose = 1;
+int snd_verbose = 1;
#ifdef USING_MUTEX
-TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
+TUNABLE_INT("hw.snd.verbose", &snd_verbose);
#else
-TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
+TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose);
#endif
static int sndstat_prepare(struct sbuf *s);
@@ -86,20 +86,20 @@ sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
{
int error, verbose;
- verbose = sndstat_verbose;
+ verbose = snd_verbose;
error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
if (error == 0 && req->newptr != NULL) {
sx_xlock(&sndstat_lock);
- if (verbose < 0 || verbose > 3)
+ if (verbose < 0 || verbose > 4)
error = EINVAL;
else
- sndstat_verbose = verbose;
+ snd_verbose = verbose;
sx_xunlock(&sndstat_lock);
}
return error;
}
SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_sndverbose, "I", "");
+ 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level");
static int
sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
@@ -300,7 +300,8 @@ sndstat_prepare(struct sbuf *s)
struct sndstat_entry *ent;
int i, j;
- sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
+ sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit)\n",
+ (unsigned int)sizeof(intpcm_t) << 3);
if (SLIST_EMPTY(&sndstat_devlist)) {
sbuf_printf(s, "No devices installed.\n");
sbuf_finish(s);
@@ -318,14 +319,14 @@ sndstat_prepare(struct sbuf *s)
sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
sbuf_printf(s, " %s", ent->str);
if (ent->handler)
- ent->handler(s, ent->dev, sndstat_verbose);
+ ent->handler(s, ent->dev, snd_verbose);
else
sbuf_printf(s, " [no handler]");
sbuf_printf(s, "\n");
}
}
- if (sndstat_verbose >= 3 && sndstat_files > 0) {
+ if (snd_verbose >= 3 && sndstat_files > 0) {
sbuf_printf(s, "\nFile Versions:\n");
SLIST_FOREACH(ent, &sndstat_devlist, link) {
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 5ab8343..719bd93 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -26,6 +26,7 @@
*/
#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pcm/vchan.h>
#include <dev/sound/pcm/dsp.h>
#include <sys/limits.h>
@@ -59,22 +60,6 @@ struct unrhdr *pcmsg_unrhdr = NULL;
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
-struct sysctl_ctx_list *
-snd_sysctl_tree(device_t dev)
-{
- struct snddev_info *d = device_get_softc(dev);
-
- return &d->sysctl_tree;
-}
-
-struct sysctl_oid *
-snd_sysctl_tree_top(device_t dev)
-{
- struct snddev_info *d = device_get_softc(dev);
-
- return d->sysctl_tree_top;
-}
-
void *
snd_mtxcreate(const char *desc, const char *type)
{
@@ -176,6 +161,11 @@ pcm_setvchans(struct snddev_info *d, int newcnt)
pcm_inprog(d, 1);
+ if (d->playcount < 1) {
+ err = ENODEV;
+ goto setvchans_out;
+ }
+
if (!(d->flags & SD_F_AUTOVCHAN)) {
err = EINVAL;
goto setvchans_out;
@@ -238,7 +228,7 @@ addok:
ORPHAN_CDEVT(sce->dsp_devt) &&
ORPHAN_CDEVT(sce->dspW_devt) &&
ORPHAN_CDEVT(sce->audio_devt) &&
- ORPHAN_CDEVT(sce->dspr_devt))
+ ORPHAN_CDEVT(sce->dspHW_devt))
goto remok;
/*
* Either we're busy, or our cdev
@@ -392,7 +382,7 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
}
/* XXX: do we need a way to let the user change the default unit? */
SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "");
+ 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
#endif
static int
@@ -419,7 +409,7 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
return (error);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
+ 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
struct pcm_channel *
pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
@@ -556,6 +546,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
unsigned rdevcount;
int device = device_get_unit(d->dev);
size_t namelen;
+ char dtype;
/*
* Note it's confusing nomenclature.
@@ -647,11 +638,20 @@ retry_chan_num_search_out:
}
#endif
+ if (ch->flags & CHN_F_VIRTUAL)
+ dtype = 'v';
+ else if (ch->direction == PCMDIR_PLAY)
+ dtype = 'p';
+ else if (ch->direction == PCMDIR_REC)
+ dtype = 'r';
+ else
+ dtype = 'u'; /* we're screwed */
+
namelen = strlen(ch->name);
- if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
+ if ((CHN_NAMELEN - namelen) > 11) { /* ":dspXX.TYYY" */
snprintf(ch->name + namelen,
- CHN_NAMELEN - namelen, ":dsp%d.%d",
- device, sce->chan_num);
+ CHN_NAMELEN - namelen, ":dsp%d.%c%d",
+ device, dtype, ch->num);
}
snd_mtxunlock(d->lock);
@@ -673,11 +673,11 @@ retry_chan_num_search_out:
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
device, sce->chan_num);
- if (ch->direction == PCMDIR_REC)
- sce->dspr_devt = make_dev(&dsp_cdevsw,
- PCMMKMINOR(device, SND_DEV_DSPREC,
- sce->chan_num), UID_ROOT, GID_WHEEL,
- 0666, "dspr%d.%d", device, sce->chan_num);
+ /* Except this. */
+ sce->dspHW_devt = make_dev(&dsp_cdevsw,
+ PCMMKMINOR(device, SND_DEV_DSPHW, sce->chan_num),
+ UID_ROOT, GID_WHEEL, 0666, "dsp%d.%c%d",
+ device, dtype, ch->num);
return 0;
}
@@ -770,7 +770,7 @@ pcm_setstatus(device_t dev, char *str)
struct snddev_info *d = device_get_softc(dev);
snd_mtxlock(d->lock);
- strncpy(d->status, str, SND_STATUSLEN);
+ strlcpy(d->status, str, SND_STATUSLEN);
snd_mtxunlock(d->lock);
if (snd_maxautovchans > 0)
pcm_setvchans(d, 1);
@@ -869,18 +869,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
chn_init(d->fakechan, NULL, 0, 0);
#ifdef SND_DYNSYSCTL
- sysctl_ctx_init(&d->sysctl_tree);
- d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
- SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
- device_get_nameunit(dev), CTLFLAG_RD, 0, "");
- if (d->sysctl_tree_top == NULL) {
- sysctl_ctx_free(&d->sysctl_tree);
- goto no;
- }
/* XXX: an user should be able to set this with a control tool, the
sysadmin then needs min+max sysctls for this */
- SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
#endif
if (numplay > 0) {
d->flags |= SD_F_AUTOVCHAN;
@@ -889,9 +882,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
sndstat_register(dev, d->status, sndstat_prepare_pcm);
return 0;
-no:
- snd_mtxfree(d->lock);
- return ENXIO;
}
int
@@ -945,9 +935,9 @@ pcm_unregister(device_t dev)
destroy_dev(sce->audio_devt);
sce->audio_devt = NULL;
}
- if (sce->dspr_devt) {
- destroy_dev(sce->dspr_devt);
- sce->dspr_devt = NULL;
+ if (sce->dspHW_devt) {
+ destroy_dev(sce->dspHW_devt);
+ sce->dspHW_devt = NULL;
}
d->devcount--;
ch = sce->channel;
@@ -967,9 +957,11 @@ pcm_unregister(device_t dev)
}
#ifdef SND_DYNSYSCTL
+#if 0
d->sysctl_tree_top = NULL;
sysctl_ctx_free(&d->sysctl_tree);
#endif
+#endif
#if 0
SLIST_FOREACH(sce, &d->channels, link) {
@@ -1068,15 +1060,15 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
sbuf_printf(s, "interrupts %d, ", c->interrupts);
if (c->direction == PCMDIR_REC)
- sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
+ sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
sndbuf_getblkcnt(c->bufhard),
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
sndbuf_getblkcnt(c->bufsoft));
else
- sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, sndbuf_getready(c->bufsoft),
+ sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
sndbuf_getblkcnt(c->bufhard),
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index eeb5c20..b62b1a5 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -58,6 +58,7 @@
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/rman.h>
+#include <sys/limits.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/sbuf.h>
@@ -148,6 +149,281 @@ nomenclature:
(((var)<(low))? (low) : ((var)>(high))? (high) : (var))
#define DSP_BUFFSIZE (8192)
+/*
+ * Macros for reading/writing PCM sample / int values from bytes array.
+ * Since every process is done using signed integer (and to make our life
+ * less miserable), unsigned sample will be converted to its signed
+ * counterpart and restored during writing back. To avoid overflow,
+ * we truncate 32bit (and only 32bit) samples down to 24bit (see below
+ * for the reason), unless PCM_USE_64BIT_ARITH is defined.
+ */
+
+/*
+ * Automatically turn on 64bit arithmetic on suitable archs
+ * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing.
+ */
+#if LONG_BIT >= 64
+#undef PCM_USE_64BIT_ARITH
+#define PCM_USE_64BIT_ARITH 1
+#else
+#if 0
+#undef PCM_USE_64BIT_ARITH
+#define PCM_USE_64BIT_ARITH 1
+#endif
+#endif
+
+#ifdef PCM_USE_64BIT_ARITH
+typedef int64_t intpcm_t;
+#else
+typedef int32_t intpcm_t;
+#endif
+
+/* 32bit fixed point shift */
+#define PCM_FXSHIFT 8
+
+#define PCM_S8_MAX 0x7f
+#define PCM_S8_MIN -0x80
+#define PCM_S16_MAX 0x7fff
+#define PCM_S16_MIN -0x8000
+#define PCM_S24_MAX 0x7fffff
+#define PCM_S24_MIN -0x800000
+#ifdef PCM_USE_64BIT_ARITH
+#if LONG_BIT >= 64
+#define PCM_S32_MAX 0x7fffffffL
+#define PCM_S32_MIN -0x80000000L
+#else
+#define PCM_S32_MAX 0x7fffffffLL
+#define PCM_S32_MIN -0x80000000LL
+#endif
+#else
+#define PCM_S32_MAX 0x7fffffff
+#define PCM_S32_MIN (-0x7fffffff - 1)
+#endif
+
+/* Bytes-per-sample definition */
+#define PCM_8_BPS 1
+#define PCM_16_BPS 2
+#define PCM_24_BPS 3
+#define PCM_32_BPS 4
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define PCM_READ_S16_LE(b8) *((int16_t *)(b8))
+#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8))
+#define PCM_READ_S16_BE(b8) \
+ ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8))
+#define _PCM_READ_S32_BE(b8) \
+ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \
+ ((int8_t)((b8)[0])) << 24))
+
+#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val)
+#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val)
+#define PCM_WRITE_S16_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[1] = val; \
+ b8[0] = val >> 8; \
+ } while(0)
+#define _PCM_WRITE_S32_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[3] = val; \
+ b8[2] = val >> 8; \
+ b8[1] = val >> 16; \
+ b8[0] = val >> 24; \
+ } while(0)
+
+#define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
+#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
+#define PCM_READ_U16_BE(b8) \
+ ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8))
+#define _PCM_READ_U32_BE(b8) \
+ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \
+ ((int8_t)((b8)[0] ^ 0x80)) << 24))
+
+#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000
+#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000
+#define PCM_WRITE_U16_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[1] = val; \
+ b8[0] = (val >> 8) ^ 0x80; \
+ } while(0)
+#define _PCM_WRITE_U32_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[3] = val; \
+ b8[2] = val >> 8; \
+ b8[1] = val >> 16; \
+ b8[0] = (val >> 24) ^ 0x80; \
+ } while(0)
+#else /* !LITTLE_ENDIAN */
+#define PCM_READ_S16_LE(b8) \
+ ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8))
+#define _PCM_READ_S32_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \
+ ((int8_t)((b8)[3])) << 24))
+#define PCM_READ_S16_BE(b8) *((int16_t *)(b8))
+#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8))
+
+#define PCM_WRITE_S16_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ } while(0)
+#define _PCM_WRITE_S32_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ b8[3] = val >> 24; \
+ } while(0)
+#define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val)
+#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val)
+
+#define PCM_READ_U16_LE(b8) \
+ ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8))
+#define _PCM_READ_U32_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \
+ ((int8_t)((b8)[3] ^ 0x80)) << 24))
+#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
+#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
+
+#define PCM_WRITE_U16_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = (val >> 8) ^ 0x80; \
+ } while(0)
+#define _PCM_WRITE_U32_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ b8[3] = (val >> 24) ^ 0x80; \
+ } while(0)
+#define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000
+#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000
+#endif
+
+#define PCM_READ_S24_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16))
+#define PCM_READ_S24_BE(b8) \
+ ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16))
+
+#define PCM_WRITE_S24_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ } while(0)
+#define PCM_WRITE_S24_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[2] = val; \
+ b8[1] = val >> 8; \
+ b8[0] = val >> 16; \
+ } while(0)
+
+#define PCM_READ_U24_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | \
+ ((int8_t)((b8)[2] ^ 0x80)) << 16))
+#define PCM_READ_U24_BE(b8) \
+ ((int32_t)((b8)[2] | (b8)[1] << 8 | \
+ ((int8_t)((b8)[0] ^ 0x80)) << 16))
+
+#define PCM_WRITE_U24_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = (val >> 16) ^ 0x80; \
+ } while(0)
+#define PCM_WRITE_U24_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[2] = val; \
+ b8[1] = val >> 8; \
+ b8[0] = (val >> 16) ^ 0x80; \
+ } while(0)
+
+#ifdef PCM_USE_64BIT_ARITH
+#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8)
+#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8)
+#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
+#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
+
+#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8)
+#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8)
+#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
+#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
+#else /* !PCM_USE_64BIT_ARITH */
+/*
+ * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight:
+ * Dynamic range for:
+ * 1) Human =~ 140db
+ * 2) 16bit = 96db (close enough)
+ * 3) 24bit = 144db (perfect)
+ * 4) 32bit = 196db (way too much)
+ * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db
+ * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit
+ * is pretty much sufficient for our signed integer processing.
+ */
+#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT)
+#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT)
+#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT)
+#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT)
+
+#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT)
+#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT)
+#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT)
+#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT)
+#endif
+
+/*
+ * 8bit sample is pretty much useless since it doesn't provide
+ * sufficient dynamic range throughout our filtering process.
+ * For the sake of completeness, declare it anyway.
+ */
+#define PCM_READ_S8(b8) *((int8_t *)(b8))
+#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8)
+#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80))
+#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8)
+
+#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val)
+#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val)
+#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80
+#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val)
+
+#define PCM_CLAMP_S8(val) \
+ (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \
+ (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val)))
+#define PCM_CLAMP_S16(val) \
+ (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \
+ (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val)))
+#define PCM_CLAMP_S24(val) \
+ (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \
+ (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val)))
+
+#ifdef PCM_USE_64BIT_ARITH
+#define PCM_CLAMP_S32(val) \
+ (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \
+ (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val)))
+#else
+#define PCM_CLAMP_S32(val) \
+ (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \
+ (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \
+ ((val) << PCM_FXSHIFT)))
+#endif
+
+#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val)
+#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val)
+#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val)
+#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val)
+
/* make figuring out what a format is easier. got AFMT_STEREO already */
#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE)
#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
@@ -185,7 +461,7 @@ int fkchan_kill(struct pcm_channel *c);
#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
#define SND_DEV_NORESET 10
-#define SND_DEV_DSPREC 11 /* recording channels */
+#define SND_DEV_DSPHW 11 /* specific channel request */
#define DSP_DEFAULT_SPEED 8000
@@ -194,6 +470,8 @@ int fkchan_kill(struct pcm_channel *c);
extern int pcm_veto_load;
extern int snd_unit;
+extern int snd_maxautovchans;
+extern int snd_verbose;
extern devclass_t pcm_devclass;
extern struct unrhdr *pcmsg_unrhdr;
@@ -210,9 +488,6 @@ extern struct unrhdr *pcmsg_unrhdr;
SYSCTL_DECL(_hw_snd);
-struct sysctl_ctx_list *snd_sysctl_tree(device_t dev);
-struct sysctl_oid *snd_sysctl_tree_top(device_t dev);
-
struct pcm_channel *pcm_getfakechan(struct snddev_info *d);
int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum);
int pcm_chnrelease(struct pcm_channel *c);
@@ -287,7 +562,7 @@ struct snddev_channel {
struct cdev *dsp_devt;
struct cdev *dspW_devt;
struct cdev *audio_devt;
- struct cdev *dspr_devt;
+ struct cdev *dspHW_devt;
};
struct snddev_info {
@@ -300,8 +575,6 @@ struct snddev_info {
void *devinfo;
device_t dev;
char status[SND_STATUSLEN];
- struct sysctl_ctx_list sysctl_tree;
- struct sysctl_oid *sysctl_tree_top;
struct mtx *lock;
struct cdev *mixer_dev;
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index b3fbd25..b210dca 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -1,5 +1,6 @@
/*-
- * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
+ * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
+ * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,6 +23,9 @@
* 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.
+ *
+ * Almost entirely rewritten to add multi-format/channels mixing support.
+ *
*/
#include <dev/sound/pcm/sound.h>
@@ -30,74 +34,210 @@
SND_DECLARE_FILE("$FreeBSD$");
+MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
+
/*
- * Default speed
+ * Default speed / format
*/
#define VCHAN_DEFAULT_SPEED 48000
+#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO)
+#define VCHAN_DEFAULT_STRFMT "s16le"
-extern int feeder_rate_ratemin;
-extern int feeder_rate_ratemax;
+struct feed_vchan_info;
+
+typedef uint32_t (*feed_vchan_mixer)(struct feed_vchan_info *,
+ uint8_t *, uint8_t *, uint32_t);
+
+struct feed_vchan_info {
+ uint32_t bps, channels, zero_sample;
+ feed_vchan_mixer mix;
+};
struct vchinfo {
- u_int32_t spd, fmt, blksz, bps, run;
+ uint32_t spd, fmt, fmts[2], blksz, bps, run;
struct pcm_channel *channel, *parent;
struct pcmchan_caps caps;
};
-static u_int32_t vchan_fmt[] = {
- AFMT_STEREO | AFMT_S16_LE,
- 0
+/* support everything (mono / stereo), except a-law / mu-law */
+static struct afmtstr_table vchan_supported_fmts[] = {
+ { "u8", AFMT_U8 }, { "s8", AFMT_S8 },
+ { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
+ { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
+ { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
+ { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
+ { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
+ { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
+ { NULL, 0 },
+};
+
+/* alias table, shorter. */
+static const struct {
+ char *alias, *fmtstr;
+} vchan_fmtstralias[] = {
+ { "8", "u8" }, { "16", "s16le" },
+ { "24", "s24le" }, { "32", "s32le" },
+ { NULL, NULL },
};
+#define vchan_valid_format(fmt) \
+ afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \
+ AFMTSTR_STEREO_RETURN)
+#define vchan_valid_strformat(strfmt) \
+ afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN);
+
+/*
+ * Need specialized WRITE macros since 32bit might involved saturation
+ * if calculation is done within 32bit arithmetic.
+ */
+#define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val)
+#define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val)
+#define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val)
+#define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
+#define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val)
+#define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val)
+#define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
+#define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val)
+#define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val)
+#define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val)
+#define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
+#define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val)
+#define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val)
+#define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
+
+#define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
+static uint32_t \
+feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(struct feed_vchan_info *info, \
+ uint8_t *to, uint8_t *tmp, uint32_t count) \
+{ \
+ uint32_t bps; \
+ int32_t x, y; \
+ VCHAN_INTCAST z; \
+ int i; \
+ \
+ bps = info->bps; \
+ i = count; \
+ tmp += i; \
+ to += i; \
+ while (i > 0) { \
+ tmp -= bps; \
+ to -= bps; \
+ i -= bps; \
+ x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \
+ y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \
+ z = (VCHAN_INTCAST)x + y; \
+ x = PCM_CLAMP_##SIGN##FMTBIT(z); \
+ VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \
+ } \
+ return count; \
+}
+
+FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
+FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
+FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
+FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
+FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
+FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
+FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
+/* unsigned */
+FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
+FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
+FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
+FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
+FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
+FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
+FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
+
static int
-vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
+feed_vchan_setup(struct pcm_feeder *f)
{
- /*
- * to is the output buffer, tmp is the input buffer
- * count is the number of 16bit samples to mix
- */
- int i;
- int x;
-
- for(i = 0; i < count; i++) {
- x = to[i];
- x += tmp[i];
- if (x < -32768) {
- /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
- x = -32768;
- }
- if (x > 32767) {
- /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
- x = 32767;
+ struct feed_vchan_info *info = f->data;
+ static const struct {
+ uint32_t format; /* pcm / audio format */
+ uint32_t bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_vchan_mixer mix;
+ } vchan_mix_tbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
+ { 0, 0, NULL },
+ };
+ uint32_t i;
+
+ for (i = 0; i < sizeof(vchan_mix_tbl) / sizeof(*vchan_mix_tbl); i++) {
+ if (vchan_mix_tbl[i].format == 0)
+ return -1;
+ if ((f->desc->out & ~AFMT_STEREO) == vchan_mix_tbl[i].format) {
+ info->bps = vchan_mix_tbl[i].bps;
+ info->mix = vchan_mix_tbl[i].mix;
+ break;
}
- to[i] = x & 0x0000ffff;
}
+
+ info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
+ info->zero_sample = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80;
+
+ return 0;
+}
+
+static int
+feed_vchan_init(struct pcm_feeder *f)
+{
+ struct feed_vchan_info *info;
+
+ if (f->desc->out != f->desc->in)
+ return EINVAL;
+
+ info = malloc(sizeof(*info), M_VCHANFEEDER, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return ENOMEM;
+ f->data = info;
+ return feed_vchan_setup(f);
+}
+
+static int
+feed_vchan_free(struct pcm_feeder *f)
+{
+ struct feed_vchan_info *info = f->data;
+
+ if (info)
+ free(info, M_VCHANFEEDER);
+ f->data = NULL;
return 0;
}
static int
-feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- /* we're going to abuse things a bit */
+ struct feed_vchan_info *info = f->data;
struct snd_dbuf *src = source;
struct pcmchan_children *cce;
struct pcm_channel *ch;
- uint32_t sz;
- int16_t *tmp, *dst;
- unsigned int cnt, rcnt = 0;
-
- #if 0
- if (sndbuf_getsize(src) < count)
- panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
- c->name, sndbuf_getsize(src), count, c->flags);
- #endif
+ uint32_t cnt, rcnt = 0, sz;
+ uint8_t *tmp;
+
sz = sndbuf_getsize(src);
if (sz < count)
count = sz;
- count &= ~1;
- if (count < 2)
+
+ sz = info->bps * info->channels;
+ count -= count % sz;
+ if (count < sz)
return 0;
- bzero(b, count);
/*
* we are going to use our source as a temporary buffer since it's
@@ -105,35 +245,68 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
* list of children and calling vchan_mix_* to mix count bytes from each
* into our destination buffer, b
*/
- dst = (int16_t *)b;
- tmp = (int16_t *)sndbuf_getbuf(src);
- bzero(tmp, count);
+ tmp = sndbuf_getbuf(src);
+ memset(b, info->zero_sample, count);
SLIST_FOREACH(cce, &c->children, link) {
ch = cce->channel;
- CHN_LOCK(ch);
+ CHN_LOCK(ch);
if (ch->flags & CHN_F_TRIGGERED) {
if (ch->flags & CHN_F_MAPPED)
sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
- cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
- vchan_mix_s16(dst, tmp, cnt >> 1);
+ cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, ch->bufsoft);
+ cnt -= cnt % sz;
+ cnt = info->mix(info, b, tmp, cnt);
if (cnt > rcnt)
rcnt = cnt;
}
- CHN_UNLOCK(ch);
+ CHN_UNLOCK(ch);
}
- return rcnt & ~1;
+ if (++c->feedcount == 0)
+ c->feedcount = 2;
+
+ return rcnt;
}
-static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
+static struct pcm_feederdesc feeder_vchan_desc[] = {
+ {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
+ {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
+ {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
+ {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
+ {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
+ {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
+ {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
+ {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
- {0},
+ {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
+ /* unsigned */
+ {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
+ {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
+ {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
+ {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
+ {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
+ {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
+ {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
+ {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
+ {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_vchan_s16_methods[] = {
- KOBJMETHOD(feeder_feed, feed_vchan_s16),
- { 0, 0 }
+static kobj_method_t feeder_vchan_methods[] = {
+ KOBJMETHOD(feeder_init, feed_vchan_init),
+ KOBJMETHOD(feeder_free, feed_vchan_free),
+ KOBJMETHOD(feeder_feed, feed_vchan),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
+FEEDER_DECLARE(feeder_vchan, 2, NULL);
/************************************************************/
@@ -165,7 +338,7 @@ vchan_free(kobj_t obj, void *data)
}
static int
-vchan_setformat(kobj_t obj, void *data, u_int32_t format)
+vchan_setformat(kobj_t obj, void *data, uint32_t format)
{
struct vchinfo *ch = data;
struct pcm_channel *parent = ch->parent;
@@ -180,15 +353,15 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
ch->bps *= 3;
else if (ch->fmt & AFMT_32BIT)
ch->bps <<= 2;
- CHN_UNLOCK(channel);
+ CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_FORMAT);
- CHN_LOCK(channel);
+ CHN_LOCK(channel);
sndbuf_setfmt(channel->bufsoft, format);
return 0;
}
static int
-vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
{
struct vchinfo *ch = data;
struct pcm_channel *parent = ch->parent;
@@ -204,7 +377,7 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
}
static int
-vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
{
struct vchinfo *ch = data;
struct pcm_channel *channel = ch->channel;
@@ -213,18 +386,27 @@ vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
int prate, crate;
ch->blksz = blocksize;
- /* CHN_UNLOCK(channel); */
+ /* CHN_UNLOCK(channel); */
sndbuf_setblksz(channel->bufhard, blocksize);
chn_notify(parent, CHN_N_BLOCKSIZE);
- CHN_LOCK(parent);
- /* CHN_LOCK(channel); */
+ CHN_LOCK(parent);
+ /* CHN_LOCK(channel); */
crate = ch->spd * ch->bps;
prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
blocksize = sndbuf_getblksz(parent->bufsoft);
- CHN_UNLOCK(parent);
+ CHN_UNLOCK(parent);
blocksize *= prate;
blocksize /= crate;
+ blocksize += ch->bps;
+ prate = 0;
+ while (blocksize >> prate)
+ prate++;
+ blocksize = 1 << (prate - 1);
+ blocksize -= blocksize % ch->bps;
+ /* XXX screwed !@#$ */
+ if (blocksize < ch->bps)
+ blocksize = 4096 - (4096 % ch->bps);
return blocksize;
}
@@ -240,9 +422,9 @@ vchan_trigger(kobj_t obj, void *data, int go)
return 0;
ch->run = (go == PCMTRIG_START)? 1 : 0;
- CHN_UNLOCK(channel);
+ CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_TRIGGER);
- CHN_LOCK(channel);
+ CHN_LOCK(channel);
return 0;
}
@@ -251,24 +433,34 @@ static struct pcmchan_caps *
vchan_getcaps(kobj_t obj, void *data)
{
struct vchinfo *ch = data;
+ uint32_t fmt;
ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
ch->caps.maxspeed = ch->caps.minspeed;
- ch->caps.fmtlist = vchan_fmt;
ch->caps.caps = 0;
+ ch->fmts[1] = 0;
+ fmt = sndbuf_getfmt(ch->parent->bufsoft);
+ if (fmt != vchan_valid_format(fmt)) {
+ device_printf(ch->parent->dev,
+ "%s: WARNING: invalid vchan format! (0x%08x)\n",
+ __func__, fmt);
+ fmt = VCHAN_DEFAULT_AFMT;
+ }
+ ch->fmts[0] = fmt;
+ ch->caps.fmtlist = ch->fmts;
return &ch->caps;
}
static kobj_method_t vchan_methods[] = {
- KOBJMETHOD(channel_init, vchan_init),
- KOBJMETHOD(channel_free, vchan_free),
- KOBJMETHOD(channel_setformat, vchan_setformat),
- KOBJMETHOD(channel_setspeed, vchan_setspeed),
- KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
- KOBJMETHOD(channel_trigger, vchan_trigger),
- KOBJMETHOD(channel_getcaps, vchan_getcaps),
- { 0, 0 }
+ KOBJMETHOD(channel_init, vchan_init),
+ KOBJMETHOD(channel_free, vchan_free),
+ KOBJMETHOD(channel_setformat, vchan_setformat),
+ KOBJMETHOD(channel_setspeed, vchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
+ KOBJMETHOD(channel_trigger, vchan_trigger),
+ KOBJMETHOD(channel_getcaps, vchan_getcaps),
+ {0, 0}
};
CHANNEL_DECLARE(vchan);
@@ -280,7 +472,7 @@ static int
sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
- struct snddev_channel *sce;
+ struct snddev_channel *sce;
struct pcm_channel *c, *ch = NULL, *fake;
struct pcmchan_caps *caps;
int err = 0;
@@ -329,18 +521,20 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
}
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
if (err == 0 && req->newptr != NULL) {
- if (newspd < 1 || newspd < feeder_rate_ratemin ||
- newspd > feeder_rate_ratemax) {
+ if (newspd < 1 || newspd < feeder_rate_min ||
+ newspd > feeder_rate_max) {
pcm_inprog(d, -1);
return EINVAL;
}
CHN_LOCK(ch);
- caps = chn_getcaps(ch);
- if (caps == NULL || newspd < caps->minspeed ||
- newspd > caps->maxspeed) {
- CHN_UNLOCK(ch);
- pcm_inprog(d, -1);
- return EINVAL;
+ if (feeder_rate_round) {
+ caps = chn_getcaps(ch);
+ if (caps == NULL || newspd < caps->minspeed ||
+ newspd > caps->maxspeed) {
+ CHN_UNLOCK(ch);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
}
if (newspd != ch->speed) {
err = chn_setspeed(ch, newspd);
@@ -348,7 +542,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
* Try to avoid FEEDER_RATE on parent channel if the
* requested value is not supported by the hardware.
*/
- if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
+ if (!err && feeder_rate_round &&
+ (ch->feederflags & (1 << FEEDER_RATE))) {
newspd = sndbuf_getspd(ch->bufhard);
err = chn_setspeed(ch, newspd);
}
@@ -367,6 +562,97 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
pcm_inprog(d, -1);
return err;
}
+
+static int
+sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ struct snddev_channel *sce;
+ struct pcm_channel *c, *ch = NULL, *fake;
+ uint32_t newfmt, spd;
+ char fmtstr[AFMTSTR_MAXSZ];
+ int err = 0, i;
+
+ d = oidp->oid_arg1;
+ if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
+ return EINVAL;
+ if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
+ }
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY) {
+ if (c->flags & CHN_F_VIRTUAL) {
+ /* Sanity check */
+ if (ch != NULL && ch != c->parentchannel) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ if (req->newptr != NULL &&
+ (c->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EBUSY;
+ }
+ } else if (c->flags & CHN_F_HAS_VCHAN) {
+ /* No way!! */
+ if (ch != NULL) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ ch = c;
+ if (ch->format != afmt2afmtstr(vchan_supported_fmts,
+ ch->format, fmtstr, sizeof(fmtstr),
+ AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
+ strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr));
+ }
+ }
+ }
+ CHN_UNLOCK(c);
+ }
+ if (ch == NULL) {
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
+ if (err == 0 && req->newptr != NULL) {
+ for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
+ if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
+ strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr));
+ break;
+ }
+ }
+ newfmt = vchan_valid_strformat(fmtstr);
+ if (newfmt == 0) {
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ CHN_LOCK(ch);
+ if (newfmt != ch->format) {
+ /* Get channel speed, before chn_reset() screw it. */
+ spd = ch->speed;
+ err = chn_reset(ch, newfmt);
+ if (err == 0)
+ err = chn_setspeed(ch, spd);
+ CHN_UNLOCK(ch);
+ if (err == 0) {
+ fake = pcm_getfakechan(d);
+ if (fake != NULL) {
+ CHN_LOCK(fake);
+ fake->format = newfmt;
+ CHN_UNLOCK(fake);
+ }
+ }
+ } else
+ CHN_UNLOCK(ch);
+ }
+ pcm_inprog(d, -1);
+ return err;
+}
#endif
/* virtual channel interface */
@@ -374,11 +660,12 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
int
vchan_create(struct pcm_channel *parent)
{
- struct snddev_info *d = parent->parentsnddev;
+ struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct pcm_channel *child, *fake;
struct pcmchan_caps *parent_caps;
- int err, first, speed = 0;
+ uint32_t vchanfmt = 0;
+ int err, first, speed = 0, r;
if (!(parent->flags & CHN_F_BUSY))
return EBUSY;
@@ -388,7 +675,7 @@ vchan_create(struct pcm_channel *parent)
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
if (!pce) {
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
return ENOMEM;
}
@@ -396,7 +683,7 @@ vchan_create(struct pcm_channel *parent)
child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
if (!child) {
free(pce, M_DEVBUF);
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
return ENODEV;
}
pce->channel = child;
@@ -413,7 +700,7 @@ vchan_create(struct pcm_channel *parent)
return err;
}
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
/* add us to our parent channel's children */
first = SLIST_EMPTY(&parent->children);
SLIST_INSERT_HEAD(&parent->children, pce, link);
@@ -424,23 +711,50 @@ vchan_create(struct pcm_channel *parent)
if (parent_caps == NULL)
err = EINVAL;
- if (!err)
- err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
+ fake = pcm_getfakechan(d);
+
+ if (!err && fake != NULL) {
+ /*
+ * Avoid querying kernel hint, use saved value
+ * from fake channel.
+ */
+ CHN_UNLOCK(parent);
+ CHN_LOCK(fake);
+ speed = fake->speed;
+ vchanfmt = fake->format;
+ CHN_UNLOCK(fake);
+ CHN_LOCK(parent);
+ }
if (!err) {
- fake = pcm_getfakechan(d);
- if (fake != NULL) {
- /*
- * Avoid querying kernel hint, use saved value
- * from fake channel.
- */
+ if (vchanfmt == 0) {
+ const char *vfmt;
+
CHN_UNLOCK(parent);
- CHN_LOCK(fake);
- speed = fake->speed;
- CHN_UNLOCK(fake);
+ r = resource_string_value(device_get_name(parent->dev),
+ device_get_unit(parent->dev),
+ "vchanformat", &vfmt);
CHN_LOCK(parent);
+ if (r != 0)
+ vfmt = NULL;
+ if (vfmt != NULL) {
+ vchanfmt = vchan_valid_strformat(vfmt);
+ for (r = 0; vchanfmt == 0 &&
+ vchan_fmtstralias[r].alias != NULL;
+ r++) {
+ if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
+ vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
+ break;
+ }
+ }
+ }
+ if (vchanfmt == 0)
+ vchanfmt = VCHAN_DEFAULT_AFMT;
}
+ err = chn_reset(parent, vchanfmt);
+ }
+ if (!err) {
/*
* This is very sad. Few soundcards advertised as being
* able to do (insanely) higher/lower speed, but in
@@ -448,7 +762,6 @@ vchan_create(struct pcm_channel *parent)
* to set sane value via kernel hints or sysctl.
*/
if (speed < 1) {
- int r;
CHN_UNLOCK(parent);
r = resource_int_value(device_get_name(parent->dev),
device_get_unit(parent->dev),
@@ -456,6 +769,9 @@ vchan_create(struct pcm_channel *parent)
CHN_LOCK(parent);
if (r != 0) {
/*
+ * No saved value from fake channel,
+ * no hint, NOTHING.
+ *
* Workaround for sb16 running
* poorly at 45k / 49k.
*/
@@ -466,38 +782,45 @@ vchan_create(struct pcm_channel *parent)
break;
default:
speed = VCHAN_DEFAULT_SPEED;
+ if (speed > parent_caps->maxspeed)
+ speed = parent_caps->maxspeed;
break;
}
+ if (speed < parent_caps->minspeed)
+ speed = parent_caps->minspeed;
}
}
- /*
- * Limit speed based on driver caps.
- * This is supposed to help fixed rate, non-VRA
- * AC97 cards, but.. (see below)
- */
- if (speed < parent_caps->minspeed)
- speed = parent_caps->minspeed;
- if (speed > parent_caps->maxspeed)
- speed = parent_caps->maxspeed;
+ if (feeder_rate_round) {
+ /*
+ * Limit speed based on driver caps.
+ * This is supposed to help fixed rate, non-VRA
+ * AC97 cards, but.. (see below)
+ */
+ if (speed < parent_caps->minspeed)
+ speed = parent_caps->minspeed;
+ if (speed > parent_caps->maxspeed)
+ speed = parent_caps->maxspeed;
+ }
/*
* We still need to limit the speed between
- * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
+ * feeder_rate_min <-> feeder_rate_max. This is
* just an escape goat if all of the above failed
* miserably.
*/
- if (speed < feeder_rate_ratemin)
- speed = feeder_rate_ratemin;
- if (speed > feeder_rate_ratemax)
- speed = feeder_rate_ratemax;
+ if (speed < feeder_rate_min)
+ speed = feeder_rate_min;
+ if (speed > feeder_rate_max)
+ speed = feeder_rate_max;
err = chn_setspeed(parent, speed);
/*
* Try to avoid FEEDER_RATE on parent channel if the
* requested value is not supported by the hardware.
*/
- if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
+ if (!err && feeder_rate_round &&
+ (parent->feederflags & (1 << FEEDER_RATE))) {
speed = sndbuf_getspd(parent->bufhard);
err = chn_setspeed(parent, speed);
}
@@ -509,6 +832,7 @@ vchan_create(struct pcm_channel *parent)
CHN_UNLOCK(parent);
CHN_LOCK(fake);
fake->speed = speed;
+ fake->format = vchanfmt;
CHN_UNLOCK(fake);
CHN_LOCK(parent);
}
@@ -533,7 +857,7 @@ int
vchan_destroy(struct pcm_channel *c)
{
struct pcm_channel *parent = c->parentchannel;
- struct snddev_info *d = parent->parentsnddev;
+ struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct snddev_channel *sce;
uint32_t spd;
@@ -571,9 +895,9 @@ gotch:
destroy_dev(sce->audio_devt);
sce->audio_devt = NULL;
}
- if (sce->dspr_devt) {
- destroy_dev(sce->dspr_devt);
- sce->dspr_devt = NULL;
+ if (sce->dspHW_devt) {
+ destroy_dev(sce->dspHW_devt);
+ sce->dspHW_devt = NULL;
}
d->devcount--;
break;
@@ -606,16 +930,19 @@ vchan_initsys(device_t dev)
#ifdef SND_DYNSYSCTL
struct snddev_info *d;
- d = device_get_softc(dev);
- /* XXX: the user should be able to set this with a control tool, the
- sysadmin needs a sysctl so set a max value for "vchan" and min+max
- values for "vchanrate" to limit what an user can do */
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
- sysctl_hw_snd_vchans, "I", "");
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
- sysctl_hw_snd_vchanrate, "I", "");
+ d = device_get_softc(dev);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchanformat, "A", "virtual channel format");
#endif
return 0;
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index 9e4b7f1..29117f2 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -2517,7 +2517,7 @@ uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
break;
default:
mi->type = AUDIO_MIXER_VALUE;
- strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
+ strlcpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
mi->un.v.num_channels = mc->nchan;
mi->un.v.delta = mc->delta;
break;
OpenPOWER on IntegriCloud