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.
This commit is contained in:
ariff 2006-11-26 12:24:06 +00:00
parent 70fe7b890e
commit 7b36f6d96b
29 changed files with 5606 additions and 2656 deletions

View File

@ -21,6 +21,19 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 7.x IS SLOW:
developers choose to disable these features on build machines
to maximize performance.
20061126:
Sound infrastructure has been updated with various fixes and
improvements. Most of the changes are pretty much transparent,
with exceptions of followings:
1) All sound driver specific sysctls (hw.snd.pcm%d.*) have been
moved to their own dev sysctl nodes, for example:
hw.snd.pcm0.vchans -> dev.pcm.0.vchans
2) /dev/dspr%d.%d has been deprecated. Each channel now has its
own chardev in the form of "dsp%d.<function>%d", where <function>
is p = playback, r = record and v = virtual, respectively. Users
are encouraged to use these devs instead of (old) "/dev/dsp%d.%d".
This does not affect those who are using "/dev/dsp".
20061122:
The following binaries have been disconnected from the build:
mount_devfs, mount_ext2fs, mount_fdescfs, mount_procfs, mount_linprocfs,

View File

@ -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) \
@ -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
@ -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);
@ -441,7 +433,7 @@ 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 {
@ -449,7 +441,7 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
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);
@ -527,40 +519,155 @@ 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);
return (ch->dma_blksz);
ch->blksz = sndbuf_getblksz(ch->buffer);
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;
}
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)));
}
}
static __inline uint32_t
atiixp_dmapos(struct atiixp_chinfo *ch)
{
struct atiixp_info *sc = ch->parent;
uint32_t reg, addr, sz, retry;
volatile uint32_t ptr;
reg = ch->dt_cur_bit;
addr = sndbuf_getbufaddr(ch->buffer);
sz = ch->blkcnt * ch->blksz;
retry = ATI_IXP_DMA_RETRY_MAX;
do {
ptr = atiixp_rd(sc, reg);
if (ptr < addr)
continue;
ptr -= addr;
if (ptr < sz) {
#if 0
#ifdef ATI_IXP_DEBUG
ch->dma_ptr = 0;
ch->dma_prevptr = 0;
if ((ptr & ~(ch->blksz - 1)) != ch->ptr) {
uint32_t delta;
delta = (sz + ptr - ch->prevptr) % sz;
#ifndef ATI_IXP_DEBUG_VERBOSE
if (delta < ch->blksz)
#endif
device_printf(sc->dev,
"PCMDIR_%s: incoherent DMA "
"prevptr=%u ptr=%u "
"ptr=%u blkcnt=%u "
"[delta=%u != blksz=%u] "
"(%s)\n",
(ch->dir == PCMDIR_PLAY) ?
"PLAY" : "REC",
ch->prevptr, ptr,
ch->ptr, ch->blkcnt,
delta, ch->blksz,
(delta < ch->blksz) ?
"OVERLAPPED!" : "Ok");
ch->ptr = ptr & ~(ch->blksz - 1);
}
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);
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
@ -569,6 +676,7 @@ 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);
@ -580,11 +688,72 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
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);
@ -594,9 +763,8 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
/* 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))
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;
@ -607,59 +775,6 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
return (0);
}
static __inline uint32_t
atiixp_dmapos(struct atiixp_chinfo *ch)
{
struct atiixp_info *sc = ch->parent;
uint32_t reg, addr, sz, retry;
volatile uint32_t ptr;
reg = ch->dma_dt_cur_bit;
addr = sndbuf_getbufaddr(ch->buffer);
sz = ch->dma_segs * ch->dma_blksz;
retry = ATI_IXP_DMA_RETRY_MAX;
do {
ptr = atiixp_rd(sc, reg);
if (ptr < addr)
continue;
ptr -= addr;
if (ptr < sz) {
#ifdef ATI_IXP_DEBUG
if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) {
uint32_t delta;
delta = (sz + ptr - ch->dma_prevptr) % sz;
#ifndef ATI_IXP_DEBUG_VERBOSE
if (delta < ch->dma_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] "
"(%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) ?
"OVERLAPPED!" : "Ok");
ch->dma_ptr = ptr & ~(ch->dma_blksz - 1);
}
ch->dma_prevptr = ptr;
#endif
return (ptr);
}
} while (--retry);
device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
(ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
return (0);
}
static int
atiixp_chan_getptr(kobj_t obj, void *data)
{
@ -668,6 +783,9 @@ atiixp_chan_getptr(kobj_t obj, void *data)
uint32_t ptr;
atiixp_lock(sc);
if (sc->polling != 0)
ptr = ch->ptr;
else
ptr = atiixp_dmapos(ch);
atiixp_unlock(sc);
@ -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
@ -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,6 +1040,13 @@ 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));
@ -891,6 +1054,7 @@ atiixp_chip_post_init(void *arg)
pcm_setstatus(sc->dev, status);
atiixp_lock(sc);
if (sc->polling == 0)
atiixp_enable_interrupts(sc);
atiixp_unlock(sc);
@ -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");
@ -998,8 +1167,7 @@ atiixp_pci_attach(device_t dev)
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,
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;
@ -1009,26 +1177,19 @@ 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
*/
device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
sc->blkcnt = sc->bufsz / i;
i = 0;
while (sc->dma_segs >> i)
while (sc->blkcnt >> 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;
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,7 +1209,7 @@ 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 *
/*maxsize*/sc->blkcnt * ATI_IXP_NCHANS *
sizeof(struct atiixp_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/NULL,
@ -1062,8 +1223,7 @@ atiixp_pci_attach(device_t dev)
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),
sc->blkcnt * ATI_IXP_NCHANS * sizeof(struct atiixp_dma_op),
atiixp_dma_cb, sc, 0))
goto bad;
@ -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,21 +1318,22 @@ 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);
if (sc->polling == 0)
atiixp_enable_interrupts(sc);
atiixp_unlock(sc);

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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) >> \

View File

@ -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;
}
/****************************************************************************
@ -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,6 +1354,7 @@ hdac_rirb_init(struct hdac_softc *sc)
sc->rirb_rp = 0;
HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST);
if (sc->polling == 0) {
/* Setup the interrupt threshold */
HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
@ -1372,6 +1365,7 @@ hdac_rirb_init(struct hdac_softc *sc)
#else
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;
@ -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,10 +2052,51 @@ hdac_stream_stop(struct hdac_chan *ch)
HDAC_SDCTL_RUN);
HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, 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
hdac_stream_start(struct hdac_chan *ch)
@ -1929,14 +2104,52 @@ hdac_stream_start(struct hdac_chan *ch)
struct hdac_softc *sc = ch->devinfo->codec->sc;
uint32_t ctl;
if (sc->polling != 0) {
int pollticks;
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;
for (i = 0; i < blks; i++, bdle++) {
bdle->addrl = (uint32_t)addr;
bdle->addrh = (uint32_t)(addr >> 32);
bdle->len = blocksize;
bdle->ioc = 1;
addr += blocksize;
if (sc->polling != 0) {
blksz = ch->blksz * ch->blkcnt;
blkcnt = 1;
} else {
blksz = ch->blksz;
blkcnt = ch->blkcnt;
}
HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size);
HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1);
for (i = 0; i < blkcnt; i++, bdle++) {
bdle->addrl = (uint32_t)addr;
bdle->addrh = (uint32_t)(addr >> 32);
bdle->len = blksz;
bdle->ioc = 1 ^ sc->polling;
addr += blksz;
}
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));
@ -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);
__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;
sndbuf_resize(ch->b, ch->blkcnt, ch->blksz);
blksz &= ~0x7f;
if (blksz < 0x80)
blksz = 0x80;
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);
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;
sc->chan_size = pcm_getbuffersize(dev,
HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_DEFAULT);
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), "blocksize", &i) == 0 &&
i > 0) {
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_MAX);
if (resource_int_value(device_get_name(sc->dev),
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,10 +4236,15 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir)
}*/
if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
continue;
ch->io[ret++] = i;
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;
}
ch->io[ret] = -1;
ch->supp_stream_formats = fmtcap;
@ -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,9 +5051,15 @@ 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),
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));
@ -4789,7 +5067,8 @@ hdac_attach2(void *arg)
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);
}
}
/****************************************************************************

View File

@ -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

View File

@ -40,6 +40,10 @@ SND_DECLARE_FILE("$FreeBSD$");
#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,11 +1002,7 @@ 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));
pcm_setstatus(dev, status);
if (sc->fixedrate == 0) {
ich_initsys(sc);
sc->intrhook.ich_func = ich_calibrate;
@ -892,8 +1013,12 @@ ich_pci_attach(device_t dev)
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 @@ ich_pci_attach(device_t dev)
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[] = {

File diff suppressed because it is too large Load Diff

View File

@ -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,7 +914,14 @@ ac97mix_init(struct snd_mixer *m)
switch (codec->id) {
case 0x41445374: /* AD1981B */
#if 0
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;
@ -865,12 +929,15 @@ ac97mix_init(struct snd_mixer *m)
mask |= SOUND_MASK_PHONEOUT;
/* Tie ogain/phone to master volume */
if (codec->mix[SOUND_MIXER_VOLUME].enable)
mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
mix_setparentchild(m, SOUND_MIXER_VOLUME,
mask);
else {
mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
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;
}

View File

@ -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);

View File

@ -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);
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

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
/*
* 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
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;
}
/*
* 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)
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); */
return NULL;
}
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;
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;
}
static u_int32_t
chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
{
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 (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_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
{
u_int32_t best;
int i, score, score2, oldscore;
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;
}
}
return best;
return chn_fmtbestfunc(fmt, fmts, 0);
}
u_int32_t
chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
{
u_int32_t best;
int i, score, score2, oldscore;
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;
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,12 +682,7 @@ 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];
tmpto[1] = 0;
to = tmpto;
} else {
best = chn_fmtbest(tmpfrom[0], to);
best = chn_fmtbest(from[0], to);
if (best != 0) {
tmpto[0] = best;
tmpto[1] = 0;
@ -433,8 +690,25 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
}
}
}
}
#define FEEDER_FMTCHAIN_MAXDEPTH 8
try = NULL;
if (to[0] != 0 && from[0] != 0 &&
to[1] == 0 && from[1] == 0) {
max = 0;
best = from[0];
c->feeder->desc->out = best;
do {
try = feeder_fmtchain(to, c->feeder, stop, max);
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;
@ -442,11 +716,9 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
c->feeder->desc->out = from[i];
try = NULL;
max = 0;
while (try == NULL && max < 8) {
do {
try = feeder_fmtchain(to, c->feeder, stop, max);
if (try == NULL)
max++;
}
} while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
if (try != NULL && max < bestmax) {
bestmax = max;
best = from[i];
@ -463,6 +735,7 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
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);

View File

@ -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,7 +72,7 @@ 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
@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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:
* ==========
*
@ -74,24 +83,18 @@ SND_DECLARE_FILE("$FreeBSD$");
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
/*
* 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))
#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
struct feed_rate_info;
#define FEEDRATE_64_MAXROLL 32
#define FEEDRATE_32_MAXROLL 16
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_min = FEEDRATE_RATEMIN;
int feeder_rate_max = FEEDRATE_RATEMAX;
int feeder_rate_round = FEEDRATE_ROUNDHZ;
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;
/*
* 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;
err = EINVAL;
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", "");
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_ratemax(SYSCTL_HANDLER_ARGS)
sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS)
{
int err, val;
val = feeder_rate_ratemax;
val = feeder_rate_max;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
if (val <= feeder_rate_ratemin || val > 0x7fffff)
err = EINVAL;
if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min)
feeder_rate_max = val;
else
feeder_rate_ratemax = val;
err = EINVAL;
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_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_scaling(SYSCTL_HANDLER_ARGS)
sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS)
{
int err, val;
val = feeder_rate_scaling;
val = feeder_rate_round;
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)
if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX)
err = EINVAL;
else
feeder_rate_scaling = val;
feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ);
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_round, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I",
"sample rate converter rounding threshold");
static int
sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS)
{
int err, val;
val = feeder_rate_buffersize;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
/*
* Don't waste too much kernel space
*/
if (val < 2 || val > 65536)
err = EINVAL;
else
feeder_rate_buffersize = val & ~1;
return err;
#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; \
}
/* 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", "");
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;
@ -241,232 +254,102 @@ feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
*gy = dst / x;
}
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:
if (info->src != info->dst)
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)
if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy)))
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,
&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:
for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) {
if (convtbl[i].format == 0)
return -1;
if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) {
info->bps = convtbl[i].bps;
info->convert = convtbl[i].convert;
break;
}
/* No way! */
if (info->gx == 0 || info->gy == 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;
}
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) {
@ -487,9 +370,6 @@ 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;
@ -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)
{
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,25 +540,60 @@ 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),
@ -829,4 +602,5 @@ static kobj_method_t feeder_rate_methods[] = {
KOBJMETHOD(feeder_feed, feed_rate),
{0, 0}
};
FEEDER_DECLARE(feeder_rate, 2, NULL);

View File

@ -31,48 +31,193 @@
SND_DECLARE_FILE("$FreeBSD$");
static int
feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
int i, j, k, vol[2];
int16_t *buf;
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;
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",
__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;
static int
feed_volume_init(struct pcm_feeder *f)
{
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);
}
static struct pcm_feederdesc feeder_volume_s16_desc[] = {
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);
return 0;
}
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_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);

View File

@ -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

View File

@ -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) {

View File

@ -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 @@ pcm_setvchans(struct snddev_info *d, int newcnt)
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 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
}
#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 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
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),

View File

@ -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 @@ currently minor = (channel << 16) + (unit << 4) + dev
(((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;

View File

@ -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 },
};
static int
vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
{
/*
* to is the output buffer, tmp is the input buffer
* count is the number of 16bit samples to mix
*/
int i;
int x;
/* alias table, shorter. */
static const struct {
char *alias, *fmtstr;
} vchan_fmtstralias[] = {
{ "8", "u8" }, { "16", "s16le" },
{ "24", "s24le" }, { "32", "s32le" },
{ NULL, NULL },
};
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;
#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; \
}
if (x > 32767) {
/* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
x = 32767;
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
feed_vchan_setup(struct pcm_feeder *f)
{
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_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
feed_vchan_init(struct pcm_feeder *f)
{
/* we're going to abuse things a bit */
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(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
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;
uint32_t cnt, rcnt = 0, sz;
uint8_t *tmp;
#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
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);
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);
}
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),
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;
@ -188,7 +361,7 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
}
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;
@ -225,6 +398,15 @@ vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
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;
}
@ -251,11 +433,21 @@ 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;
}
@ -329,12 +521,13 @@ 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);
if (feeder_rate_round) {
caps = chn_getcaps(ch);
if (caps == NULL || newspd < caps->minspeed ||
newspd > caps->maxspeed) {
@ -342,13 +535,15 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
pcm_inprog(d, -1);
return EINVAL;
}
}
if (newspd != ch->speed) {
err = chn_setspeed(ch, newspd);
/*
* 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 */
@ -378,7 +664,8 @@ vchan_create(struct pcm_channel *parent)
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;
@ -424,12 +711,9 @@ vchan_create(struct pcm_channel *parent)
if (parent_caps == NULL)
err = EINVAL;
if (!err)
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
if (!err) {
fake = pcm_getfakechan(d);
if (fake != NULL) {
if (!err && fake != NULL) {
/*
* Avoid querying kernel hint, use saved value
* from fake channel.
@ -437,10 +721,40 @@ vchan_create(struct pcm_channel *parent)
CHN_UNLOCK(parent);
CHN_LOCK(fake);
speed = fake->speed;
vchanfmt = fake->format;
CHN_UNLOCK(fake);
CHN_LOCK(parent);
}
if (!err) {
if (vchanfmt == 0) {
const char *vfmt;
CHN_UNLOCK(parent);
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,11 +782,16 @@ 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;
}
}
if (feeder_rate_round) {
/*
* Limit speed based on driver caps.
* This is supposed to help fixed rate, non-VRA
@ -480,24 +801,26 @@ vchan_create(struct pcm_channel *parent)
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);
}
@ -571,9 +895,9 @@ vchan_destroy(struct pcm_channel *c)
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;
@ -607,15 +931,18 @@ vchan_initsys(device_t dev)
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", "");
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;

View File

@ -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;

View File

@ -57,7 +57,7 @@
* is created, otherwise 1.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 700025 /* Master, propagated to newvers */
#define __FreeBSD_version 700026 /* Master, propagated to newvers */
#ifndef LOCORE
#include <sys/types.h>