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:
parent
70fe7b890e
commit
7b36f6d96b
13
UPDATING
13
UPDATING
@ -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,
|
||||
|
@ -66,17 +66,11 @@
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
#define ATI_IXP_DMA_RETRY_MAX 100
|
||||
#define ATI_IXP_DMA_CHSEGS_DEFAULT 2
|
||||
|
||||
#define ATI_IXP_BUFSZ_MIN 4096
|
||||
#define ATI_IXP_BUFSZ_MAX 65536
|
||||
#define ATI_IXP_BUFSZ_DEFAULT 16384
|
||||
|
||||
#ifdef ATI_IXP_DEBUG_VERBOSE
|
||||
#undef ATI_IXP_DEBUG
|
||||
#define ATI_IXP_DEBUG 1
|
||||
#endif
|
||||
|
||||
struct atiixp_dma_op {
|
||||
volatile uint32_t addr;
|
||||
volatile uint16_t status;
|
||||
@ -92,11 +86,9 @@ struct atiixp_chinfo {
|
||||
struct atiixp_info *parent;
|
||||
struct atiixp_dma_op *sgd_table;
|
||||
bus_addr_t sgd_addr;
|
||||
uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit;
|
||||
uint32_t dma_segs, dma_blksz;
|
||||
#ifdef ATI_IXP_DEBUG
|
||||
uint32_t dma_ptr, dma_prevptr;
|
||||
#endif
|
||||
uint32_t enable_bit, flush_bit, linkptr_bit, dt_cur_bit;
|
||||
uint32_t blksz, blkcnt;
|
||||
uint32_t ptr, prevptr;
|
||||
uint32_t fmt;
|
||||
int caps_32bit, dir, active;
|
||||
};
|
||||
@ -123,10 +115,12 @@ struct atiixp_info {
|
||||
|
||||
uint32_t bufsz;
|
||||
uint32_t codec_not_ready_bits, codec_idx, codec_found;
|
||||
uint32_t dma_segs;
|
||||
uint32_t blkcnt;
|
||||
int registered_channels;
|
||||
|
||||
struct mtx *lock;
|
||||
struct callout poll_timer;
|
||||
int poll_ticks, polling;
|
||||
};
|
||||
|
||||
#define atiixp_rd(_sc, _reg) \
|
||||
@ -226,7 +220,7 @@ atiixp_enable_interrupts(struct atiixp_info *sc)
|
||||
*/
|
||||
#if 1
|
||||
value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN |
|
||||
ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
|
||||
ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
|
||||
#else
|
||||
value |= ATI_REG_IER_IN_XRUN_EN;
|
||||
value |= ATI_REG_IER_OUT_XRUN_EN;
|
||||
@ -281,8 +275,7 @@ atiixp_reset_aclink(struct atiixp_info *sc)
|
||||
/* check if the ac-link is working; reset device otherwise */
|
||||
timeout = 10;
|
||||
value = atiixp_rd(sc, ATI_REG_CMD);
|
||||
while (!(value & ATI_REG_CMD_ACLINK_ACTIVE)
|
||||
&& --timeout) {
|
||||
while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) && --timeout) {
|
||||
#if 0
|
||||
device_printf(sc->dev, "not up; resetting aclink hardware\n");
|
||||
#endif
|
||||
@ -358,7 +351,7 @@ atiixp_waitready_codec(struct atiixp_info *sc)
|
||||
|
||||
do {
|
||||
if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
|
||||
ATI_REG_PHYS_OUT_ADDR_EN) == 0)
|
||||
ATI_REG_PHYS_OUT_ADDR_EN) == 0)
|
||||
return (0);
|
||||
DELAY(1);
|
||||
} while (--timeout);
|
||||
@ -377,8 +370,7 @@ atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
|
||||
return (-1);
|
||||
|
||||
data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
|
||||
ATI_REG_PHYS_OUT_ADDR_EN |
|
||||
ATI_REG_PHYS_OUT_RW | sc->codec_idx;
|
||||
ATI_REG_PHYS_OUT_ADDR_EN | ATI_REG_PHYS_OUT_RW | sc->codec_idx;
|
||||
|
||||
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
|
||||
|
||||
@ -408,8 +400,8 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
|
||||
return (-1);
|
||||
|
||||
data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
|
||||
(((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
|
||||
ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
|
||||
(((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
|
||||
ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
|
||||
|
||||
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
|
||||
|
||||
@ -417,8 +409,8 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
|
||||
}
|
||||
|
||||
static kobj_method_t atiixp_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, atiixp_rdcd),
|
||||
KOBJMETHOD(ac97_write, atiixp_wrcd),
|
||||
KOBJMETHOD(ac97_read, atiixp_rdcd),
|
||||
KOBJMETHOD(ac97_write, atiixp_wrcd),
|
||||
{ 0, 0 }
|
||||
};
|
||||
AC97_DECLARE(atiixp_ac97);
|
||||
@ -441,15 +433,15 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR;
|
||||
ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN;
|
||||
ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH;
|
||||
ch->dma_dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
|
||||
ch->dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
|
||||
/* Native 32bit playback working properly */
|
||||
ch->caps_32bit = 1;
|
||||
} else {
|
||||
ch = &sc->rch;
|
||||
ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
|
||||
ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
|
||||
ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
|
||||
ch->flush_bit = ATI_REG_FIFO_IN_FLUSH;
|
||||
ch->dma_dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
|
||||
ch->dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
|
||||
/* XXX Native 32bit recording appear to be broken */
|
||||
ch->caps_32bit = 1;
|
||||
}
|
||||
@ -458,8 +450,8 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->dir = dir;
|
||||
ch->dma_segs = sc->dma_segs;
|
||||
ch->dma_blksz = sc->bufsz / sc->dma_segs;
|
||||
ch->blkcnt = sc->blkcnt;
|
||||
ch->blksz = sc->bufsz / ch->blkcnt;
|
||||
|
||||
atiixp_unlock(sc);
|
||||
|
||||
@ -468,9 +460,9 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
|
||||
atiixp_lock(sc);
|
||||
num = sc->registered_channels++;
|
||||
ch->sgd_table = &sc->sgd_table[num * ch->dma_segs];
|
||||
ch->sgd_addr = sc->sgd_addr +
|
||||
(num * ch->dma_segs * sizeof(struct atiixp_dma_op));
|
||||
ch->sgd_table = &sc->sgd_table[num * ch->blkcnt];
|
||||
ch->sgd_addr = sc->sgd_addr + (num * ch->blkcnt *
|
||||
sizeof(struct atiixp_dma_op));
|
||||
atiixp_disable_dma(ch);
|
||||
atiixp_unlock(sc);
|
||||
|
||||
@ -496,7 +488,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
|
||||
/* We do not have support for more than 2 channels, _yet_. */
|
||||
value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
|
||||
ATI_REG_OUT_DMA_SLOT_BIT(4);
|
||||
ATI_REG_OUT_DMA_SLOT_BIT(4);
|
||||
value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
|
||||
atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
|
||||
value = atiixp_rd(sc, ATI_REG_CMD);
|
||||
@ -527,84 +519,44 @@ atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
struct atiixp_chinfo *ch = data;
|
||||
struct atiixp_info *sc = ch->parent;
|
||||
|
||||
/* XXX Force static blocksize */
|
||||
sndbuf_resize(ch->buffer, ch->dma_segs, ch->dma_blksz);
|
||||
if ((blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
|
||||
blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
|
||||
|
||||
if (ch->dma_blksz != sndbuf_getblksz(ch->buffer)) {
|
||||
device_printf(sc->dev,
|
||||
"%s: WARNING - dma_blksz=%u != blksz=%u !!!\n",
|
||||
__func__, ch->dma_blksz, sndbuf_getblksz(ch->buffer));
|
||||
ch->dma_blksz = sndbuf_getblksz(ch->buffer);
|
||||
}
|
||||
if ((sndbuf_getblksz(ch->buffer) != blksz ||
|
||||
sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
|
||||
sndbuf_resize(ch->buffer, ch->blkcnt, blksz) != 0)
|
||||
device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
|
||||
__func__, blksz, ch->blkcnt);
|
||||
|
||||
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));
|
||||
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(ch->dma_blksz >> 2);
|
||||
ch->sgd_table[i].size = htole16(blksz >> 2);
|
||||
ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
|
||||
(((i + 1) % ch->dma_segs) *
|
||||
sizeof(struct atiixp_dma_op)));
|
||||
(((i + 1) % blkcnt) * sizeof(struct atiixp_dma_op)));
|
||||
}
|
||||
|
||||
#ifdef ATI_IXP_DEBUG
|
||||
ch->dma_ptr = 0;
|
||||
ch->dma_prevptr = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
atiixp_chan_trigger(kobj_t obj, void *data, int go)
|
||||
{
|
||||
struct atiixp_chinfo *ch = data;
|
||||
struct atiixp_info *sc = ch->parent;
|
||||
uint32_t value;
|
||||
|
||||
atiixp_lock(sc);
|
||||
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
atiixp_flush_dma(ch);
|
||||
atiixp_buildsgdt(ch);
|
||||
atiixp_wr(sc, ch->linkptr_bit, 0);
|
||||
atiixp_enable_dma(ch);
|
||||
atiixp_wr(sc, ch->linkptr_bit,
|
||||
(uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
|
||||
break;
|
||||
case PCMTRIG_STOP:
|
||||
case PCMTRIG_ABORT:
|
||||
atiixp_disable_dma(ch);
|
||||
atiixp_flush_dma(ch);
|
||||
break;
|
||||
default:
|
||||
atiixp_unlock(sc);
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update bus busy status */
|
||||
value = atiixp_rd(sc, ATI_REG_IER);
|
||||
if (atiixp_rd(sc, ATI_REG_CMD) & (
|
||||
ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN |
|
||||
ATI_REG_CMD_SPDF_OUT_EN))
|
||||
value |= ATI_REG_IER_SET_BUS_BUSY;
|
||||
else
|
||||
value &= ~ATI_REG_IER_SET_BUS_BUSY;
|
||||
atiixp_wr(sc, ATI_REG_IER, value);
|
||||
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline uint32_t
|
||||
@ -614,9 +566,9 @@ atiixp_dmapos(struct atiixp_chinfo *ch)
|
||||
uint32_t reg, addr, sz, retry;
|
||||
volatile uint32_t ptr;
|
||||
|
||||
reg = ch->dma_dt_cur_bit;
|
||||
reg = ch->dt_cur_bit;
|
||||
addr = sndbuf_getbufaddr(ch->buffer);
|
||||
sz = ch->dma_segs * ch->dma_blksz;
|
||||
sz = ch->blkcnt * ch->blksz;
|
||||
retry = ATI_IXP_DMA_RETRY_MAX;
|
||||
|
||||
do {
|
||||
@ -625,37 +577,200 @@ atiixp_dmapos(struct atiixp_chinfo *ch)
|
||||
continue;
|
||||
ptr -= addr;
|
||||
if (ptr < sz) {
|
||||
#if 0
|
||||
#ifdef ATI_IXP_DEBUG
|
||||
if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) {
|
||||
if ((ptr & ~(ch->blksz - 1)) != ch->ptr) {
|
||||
uint32_t delta;
|
||||
|
||||
delta = (sz + ptr - ch->dma_prevptr) % sz;
|
||||
delta = (sz + ptr - ch->prevptr) % sz;
|
||||
#ifndef ATI_IXP_DEBUG_VERBOSE
|
||||
if (delta < ch->dma_blksz)
|
||||
if (delta < ch->blksz)
|
||||
#endif
|
||||
device_printf(sc->dev,
|
||||
"PCMDIR_%s: incoherent DMA "
|
||||
"dma_prevptr=%u ptr=%u "
|
||||
"dma_ptr=%u dma_segs=%u "
|
||||
"[delta=%u != dma_blksz=%u] "
|
||||
"prevptr=%u ptr=%u "
|
||||
"ptr=%u blkcnt=%u "
|
||||
"[delta=%u != blksz=%u] "
|
||||
"(%s)\n",
|
||||
(ch->dir == PCMDIR_PLAY) ?
|
||||
"PLAY" : "REC",
|
||||
ch->dma_prevptr, ptr,
|
||||
ch->dma_ptr, ch->dma_segs,
|
||||
delta, ch->dma_blksz,
|
||||
(delta < ch->dma_blksz) ?
|
||||
ch->prevptr, ptr,
|
||||
ch->ptr, ch->blkcnt,
|
||||
delta, ch->blksz,
|
||||
(delta < ch->blksz) ?
|
||||
"OVERLAPPED!" : "Ok");
|
||||
ch->dma_ptr = ptr & ~(ch->dma_blksz - 1);
|
||||
ch->ptr = ptr & ~(ch->blksz - 1);
|
||||
}
|
||||
ch->dma_prevptr = ptr;
|
||||
ch->prevptr = ptr;
|
||||
#endif
|
||||
#endif
|
||||
return (ptr);
|
||||
}
|
||||
} while (--retry);
|
||||
|
||||
device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
|
||||
(ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
|
||||
(ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
atiixp_poll_channel(struct atiixp_chinfo *ch)
|
||||
{
|
||||
uint32_t sz, delta;
|
||||
volatile uint32_t ptr;
|
||||
|
||||
if (ch->active == 0)
|
||||
return (0);
|
||||
|
||||
sz = ch->blksz * ch->blkcnt;
|
||||
ptr = atiixp_dmapos(ch);
|
||||
ch->ptr = ptr;
|
||||
ptr %= sz;
|
||||
ptr &= ~(ch->blksz - 1);
|
||||
delta = (sz + ptr - ch->prevptr) % sz;
|
||||
|
||||
if (delta < ch->blksz)
|
||||
return (0);
|
||||
|
||||
ch->prevptr = ptr;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
#define atiixp_chan_active(sc) ((sc)->pch.active + (sc)->rch.active)
|
||||
|
||||
static void
|
||||
atiixp_poll_callback(void *arg)
|
||||
{
|
||||
struct atiixp_info *sc = arg;
|
||||
uint32_t trigger = 0;
|
||||
|
||||
if (sc == NULL)
|
||||
return;
|
||||
|
||||
atiixp_lock(sc);
|
||||
if (sc->polling == 0 || atiixp_chan_active(sc) == 0) {
|
||||
atiixp_unlock(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
trigger |= (atiixp_poll_channel(&sc->pch) != 0) ? 1 : 0;
|
||||
trigger |= (atiixp_poll_channel(&sc->rch) != 0) ? 2 : 0;
|
||||
|
||||
/* XXX */
|
||||
callout_reset(&sc->poll_timer, 1/*sc->poll_ticks*/,
|
||||
atiixp_poll_callback, sc);
|
||||
|
||||
atiixp_unlock(sc);
|
||||
|
||||
if (trigger & 1)
|
||||
chn_intr(sc->pch.channel);
|
||||
if (trigger & 2)
|
||||
chn_intr(sc->rch.channel);
|
||||
}
|
||||
|
||||
static int
|
||||
atiixp_chan_trigger(kobj_t obj, void *data, int go)
|
||||
{
|
||||
struct atiixp_chinfo *ch = data;
|
||||
struct atiixp_info *sc = ch->parent;
|
||||
uint32_t value;
|
||||
int pollticks;
|
||||
|
||||
atiixp_lock(sc);
|
||||
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
atiixp_flush_dma(ch);
|
||||
atiixp_buildsgdt(ch);
|
||||
atiixp_wr(sc, ch->linkptr_bit, 0);
|
||||
atiixp_enable_dma(ch);
|
||||
atiixp_wr(sc, ch->linkptr_bit,
|
||||
(uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
|
||||
if (sc->polling != 0) {
|
||||
ch->ptr = 0;
|
||||
ch->prevptr = 0;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
pollticks = hz;
|
||||
if (pollticks < 1)
|
||||
pollticks = 1;
|
||||
if (atiixp_chan_active(sc) == 0 ||
|
||||
pollticks < sc->poll_ticks) {
|
||||
if (bootverbose) {
|
||||
if (atiixp_chan_active(sc) == 0)
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks=%d\n",
|
||||
__func__, pollticks);
|
||||
else
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks %d -> %d\n",
|
||||
__func__, sc->poll_ticks,
|
||||
pollticks);
|
||||
}
|
||||
sc->poll_ticks = pollticks;
|
||||
callout_reset(&sc->poll_timer, 1,
|
||||
atiixp_poll_callback, sc);
|
||||
}
|
||||
}
|
||||
ch->active = 1;
|
||||
break;
|
||||
case PCMTRIG_STOP:
|
||||
case PCMTRIG_ABORT:
|
||||
atiixp_disable_dma(ch);
|
||||
atiixp_flush_dma(ch);
|
||||
ch->active = 0;
|
||||
if (sc->polling != 0) {
|
||||
if (atiixp_chan_active(sc) == 0) {
|
||||
callout_stop(&sc->poll_timer);
|
||||
sc->poll_ticks = 1;
|
||||
} else {
|
||||
if (sc->pch.active != 0)
|
||||
ch = &sc->pch;
|
||||
else
|
||||
ch = &sc->rch;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
pollticks = hz;
|
||||
if (pollticks < 1)
|
||||
pollticks = 1;
|
||||
if (pollticks > sc->poll_ticks) {
|
||||
if (bootverbose)
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks %d -> %d\n",
|
||||
__func__, sc->poll_ticks,
|
||||
pollticks);
|
||||
sc->poll_ticks = pollticks;
|
||||
callout_reset(&sc->poll_timer,
|
||||
1, atiixp_poll_callback,
|
||||
sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
atiixp_unlock(sc);
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update bus busy status */
|
||||
value = atiixp_rd(sc, ATI_REG_IER);
|
||||
if (atiixp_rd(sc, ATI_REG_CMD) & (ATI_REG_CMD_SEND_EN |
|
||||
ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN))
|
||||
value |= ATI_REG_IER_SET_BUS_BUSY;
|
||||
else
|
||||
value &= ~ATI_REG_IER_SET_BUS_BUSY;
|
||||
atiixp_wr(sc, ATI_REG_IER, value);
|
||||
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -668,7 +783,10 @@ atiixp_chan_getptr(kobj_t obj, void *data)
|
||||
uint32_t ptr;
|
||||
|
||||
atiixp_lock(sc);
|
||||
ptr = atiixp_dmapos(ch);
|
||||
if (sc->polling != 0)
|
||||
ptr = ch->ptr;
|
||||
else
|
||||
ptr = atiixp_dmapos(ch);
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return (ptr);
|
||||
@ -703,10 +821,14 @@ static void
|
||||
atiixp_intr(void *p)
|
||||
{
|
||||
struct atiixp_info *sc = p;
|
||||
struct atiixp_chinfo *ch;
|
||||
uint32_t status, enable, detected_codecs;
|
||||
uint32_t trigger = 0;
|
||||
|
||||
atiixp_lock(sc);
|
||||
if (sc->polling != 0) {
|
||||
atiixp_unlock(sc);
|
||||
return;
|
||||
}
|
||||
status = atiixp_rd(sc, ATI_REG_ISR);
|
||||
|
||||
if (status == 0) {
|
||||
@ -714,26 +836,10 @@ atiixp_intr(void *p)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) {
|
||||
ch = &sc->rch;
|
||||
#ifdef ATI_IXP_DEBUG
|
||||
ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
|
||||
(ch->dma_segs * ch->dma_blksz);
|
||||
#endif
|
||||
atiixp_unlock(sc);
|
||||
chn_intr(ch->channel);
|
||||
atiixp_lock(sc);
|
||||
}
|
||||
if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) {
|
||||
ch = &sc->pch;
|
||||
#ifdef ATI_IXP_DEBUG
|
||||
ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
|
||||
(ch->dma_segs * ch->dma_blksz);
|
||||
#endif
|
||||
atiixp_unlock(sc);
|
||||
chn_intr(ch->channel);
|
||||
atiixp_lock(sc);
|
||||
}
|
||||
if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.active != 0)
|
||||
trigger |= 1;
|
||||
if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.active != 0)
|
||||
trigger |= 2;
|
||||
|
||||
#if 0
|
||||
if (status & ATI_REG_ISR_IN_XRUN) {
|
||||
@ -760,6 +866,11 @@ atiixp_intr(void *p)
|
||||
/* acknowledge */
|
||||
atiixp_wr(sc, ATI_REG_ISR, status);
|
||||
atiixp_unlock(sc);
|
||||
|
||||
if (trigger & 1)
|
||||
chn_intr(sc->pch.channel);
|
||||
if (trigger & 2)
|
||||
chn_intr(sc->rch.channel);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -782,7 +893,7 @@ atiixp_chip_pre_init(struct atiixp_info *sc)
|
||||
/* clear all DMA enables (preserving rest of settings) */
|
||||
value = atiixp_rd(sc, ATI_REG_CMD);
|
||||
value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN |
|
||||
ATI_REG_CMD_SPDF_OUT_EN );
|
||||
ATI_REG_CMD_SPDF_OUT_EN );
|
||||
atiixp_wr(sc, ATI_REG_CMD, value);
|
||||
|
||||
/* reset aclink */
|
||||
@ -796,12 +907,54 @@ atiixp_chip_pre_init(struct atiixp_info *sc)
|
||||
atiixp_unlock(sc);
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct atiixp_info *sc;
|
||||
device_t dev;
|
||||
int err, val;
|
||||
|
||||
dev = oidp->oid_arg1;
|
||||
sc = pcm_getdevinfo(dev);
|
||||
if (sc == NULL)
|
||||
return (EINVAL);
|
||||
atiixp_lock(sc);
|
||||
val = sc->polling;
|
||||
atiixp_unlock(sc);
|
||||
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
|
||||
|
||||
if (err || req->newptr == NULL)
|
||||
return (err);
|
||||
if (val < 0 || val > 1)
|
||||
return (EINVAL);
|
||||
|
||||
atiixp_lock(sc);
|
||||
if (val != sc->polling) {
|
||||
if (atiixp_chan_active(sc) != 0)
|
||||
err = EBUSY;
|
||||
else if (val == 0) {
|
||||
atiixp_enable_interrupts(sc);
|
||||
sc->polling = 0;
|
||||
DELAY(1000);
|
||||
} else {
|
||||
atiixp_disable_interrupts(sc);
|
||||
sc->polling = 1;
|
||||
DELAY(1000);
|
||||
}
|
||||
}
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
atiixp_chip_post_init(void *arg)
|
||||
{
|
||||
struct atiixp_info *sc = (struct atiixp_info *)arg;
|
||||
uint32_t subdev;
|
||||
int i, timeout, found;
|
||||
int i, timeout, found, polling;
|
||||
char status[SND_STATUSLEN];
|
||||
|
||||
atiixp_lock(sc);
|
||||
@ -811,6 +964,9 @@ atiixp_chip_post_init(void *arg)
|
||||
sc->delayed_attach.ich_func = NULL;
|
||||
}
|
||||
|
||||
polling = sc->polling;
|
||||
sc->polling = 0;
|
||||
|
||||
/* wait for the interrupts to happen */
|
||||
timeout = 100;
|
||||
do {
|
||||
@ -819,6 +975,7 @@ atiixp_chip_post_init(void *arg)
|
||||
break;
|
||||
} while (--timeout);
|
||||
|
||||
sc->polling = polling;
|
||||
atiixp_disable_interrupts(sc);
|
||||
|
||||
if (timeout == 0) {
|
||||
@ -835,22 +992,19 @@ atiixp_chip_post_init(void *arg)
|
||||
* ATI IXP can have upto 3 codecs, but single codec should be
|
||||
* suffice for now.
|
||||
*/
|
||||
if (!(sc->codec_not_ready_bits &
|
||||
ATI_REG_ISR_CODEC0_NOT_READY)) {
|
||||
if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) {
|
||||
/* codec 0 present */
|
||||
sc->codec_found++;
|
||||
sc->codec_idx = 0;
|
||||
found++;
|
||||
}
|
||||
|
||||
if (!(sc->codec_not_ready_bits &
|
||||
ATI_REG_ISR_CODEC1_NOT_READY)) {
|
||||
if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) {
|
||||
/* codec 1 present */
|
||||
sc->codec_found++;
|
||||
}
|
||||
|
||||
if (!(sc->codec_not_ready_bits &
|
||||
ATI_REG_ISR_CODEC2_NOT_READY)) {
|
||||
if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) {
|
||||
/* codec 2 present */
|
||||
sc->codec_found++;
|
||||
}
|
||||
@ -865,10 +1019,12 @@ atiixp_chip_post_init(void *arg)
|
||||
if (sc->codec == NULL)
|
||||
goto postinitbad;
|
||||
|
||||
subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev);
|
||||
subdev = (pci_get_subdevice(sc->dev) << 16) |
|
||||
pci_get_subvendor(sc->dev);
|
||||
switch (subdev) {
|
||||
case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */
|
||||
ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
|
||||
ac97_setflags(sc->codec, ac97_getflags(sc->codec) |
|
||||
AC97_F_EAPD_INV);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -884,14 +1040,22 @@ atiixp_chip_post_init(void *arg)
|
||||
for (i = 0; i < ATI_IXP_NRCHAN; i++)
|
||||
pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
|
||||
sysctl_atiixp_polling, "I", "Enable polling mode");
|
||||
#endif
|
||||
|
||||
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
|
||||
rman_get_start(sc->reg), rman_get_start(sc->irq),
|
||||
PCM_KLDSTRING(snd_atiixp));
|
||||
rman_get_start(sc->reg), rman_get_start(sc->irq),
|
||||
PCM_KLDSTRING(snd_atiixp));
|
||||
|
||||
pcm_setstatus(sc->dev, status);
|
||||
|
||||
atiixp_lock(sc);
|
||||
atiixp_enable_interrupts(sc);
|
||||
if (sc->polling == 0)
|
||||
atiixp_enable_interrupts(sc);
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return;
|
||||
@ -949,7 +1113,7 @@ atiixp_pci_probe(device_t dev)
|
||||
devid = pci_get_device(dev);
|
||||
for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
|
||||
if (vendor == atiixp_hw[i].vendor &&
|
||||
devid == atiixp_hw[i].devid) {
|
||||
devid == atiixp_hw[i].devid) {
|
||||
device_set_desc(dev, atiixp_hw[i].desc);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
@ -971,18 +1135,23 @@ atiixp_pci_attach(device_t dev)
|
||||
|
||||
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
|
||||
sc->dev = dev;
|
||||
/*
|
||||
* Default DMA segments per playback / recording channel
|
||||
*/
|
||||
sc->dma_segs = ATI_IXP_DMA_CHSEGS_DEFAULT;
|
||||
|
||||
callout_init(&sc->poll_timer, CALLOUT_MPSAFE);
|
||||
sc->poll_ticks = 1;
|
||||
|
||||
if (resource_int_value(device_get_name(sc->dev),
|
||||
device_get_unit(sc->dev), "polling", &i) == 0 && i != 0)
|
||||
sc->polling = 1;
|
||||
else
|
||||
sc->polling = 0;
|
||||
|
||||
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
|
||||
pci_enable_busmaster(dev);
|
||||
|
||||
sc->regid = PCIR_BAR(0);
|
||||
sc->regtype = SYS_RES_MEMORY;
|
||||
sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid,
|
||||
RF_ACTIVE);
|
||||
sc->reg = bus_alloc_resource_any(dev, sc->regtype,
|
||||
&sc->regid, RF_ACTIVE);
|
||||
|
||||
if (!sc->reg) {
|
||||
device_printf(dev, "unable to allocate register space\n");
|
||||
@ -993,14 +1162,13 @@ atiixp_pci_attach(device_t dev)
|
||||
sc->sh = rman_get_bushandle(sc->reg);
|
||||
|
||||
sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN,
|
||||
ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
|
||||
ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
|
||||
|
||||
sc->irqid = 0;
|
||||
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
|
||||
RF_ACTIVE | RF_SHAREABLE);
|
||||
if (!sc->irq ||
|
||||
snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
|
||||
atiixp_intr, sc, &sc->ih)) {
|
||||
RF_ACTIVE | RF_SHAREABLE);
|
||||
if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
|
||||
atiixp_intr, sc, &sc->ih)) {
|
||||
device_printf(dev, "unable to map interrupt\n");
|
||||
goto bad;
|
||||
}
|
||||
@ -1008,27 +1176,20 @@ atiixp_pci_attach(device_t dev)
|
||||
/*
|
||||
* Let the user choose the best DMA segments.
|
||||
*/
|
||||
if (resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "dma_segs",
|
||||
&i) == 0) {
|
||||
if (i < ATI_IXP_DMA_CHSEGS_MIN)
|
||||
i = ATI_IXP_DMA_CHSEGS_MIN;
|
||||
if (i > ATI_IXP_DMA_CHSEGS_MAX)
|
||||
i = ATI_IXP_DMA_CHSEGS_MAX;
|
||||
sc->dma_segs = i;
|
||||
}
|
||||
if (resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
|
||||
sc->blkcnt = sc->bufsz / i;
|
||||
i = 0;
|
||||
while (sc->blkcnt >> i)
|
||||
i++;
|
||||
sc->blkcnt = 1 << (i - 1);
|
||||
if (sc->blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
|
||||
sc->blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
|
||||
else if (sc->blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
|
||||
sc->blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
|
||||
|
||||
/*
|
||||
* round the value to the nearest ^2
|
||||
*/
|
||||
i = 0;
|
||||
while (sc->dma_segs >> i)
|
||||
i++;
|
||||
sc->dma_segs = 1 << (i - 1);
|
||||
if (sc->dma_segs < ATI_IXP_DMA_CHSEGS_MIN)
|
||||
sc->dma_segs = ATI_IXP_DMA_CHSEGS_MIN;
|
||||
else if (sc->dma_segs > ATI_IXP_DMA_CHSEGS_MAX)
|
||||
sc->dma_segs = ATI_IXP_DMA_CHSEGS_MAX;
|
||||
} else
|
||||
sc->blkcnt = ATI_IXP_DMA_CHSEGS;
|
||||
|
||||
/*
|
||||
* DMA tag for scatter-gather buffers and link pointers
|
||||
@ -1048,8 +1209,8 @@ atiixp_pci_attach(device_t dev)
|
||||
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
|
||||
/*highaddr*/BUS_SPACE_MAXADDR,
|
||||
/*filter*/NULL, /*filterarg*/NULL,
|
||||
/*maxsize*/sc->dma_segs * ATI_IXP_NCHANS *
|
||||
sizeof(struct atiixp_dma_op),
|
||||
/*maxsize*/sc->blkcnt * ATI_IXP_NCHANS *
|
||||
sizeof(struct atiixp_dma_op),
|
||||
/*nsegments*/1, /*maxsegz*/0x3ffff,
|
||||
/*flags*/0, /*lockfunc*/NULL,
|
||||
/*lockarg*/NULL, &sc->sgd_dmat) != 0) {
|
||||
@ -1058,13 +1219,12 @@ atiixp_pci_attach(device_t dev)
|
||||
}
|
||||
|
||||
if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
|
||||
BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
|
||||
BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
|
||||
goto bad;
|
||||
|
||||
if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
|
||||
sc->dma_segs * ATI_IXP_NCHANS *
|
||||
sizeof(struct atiixp_dma_op),
|
||||
atiixp_dma_cb, sc, 0))
|
||||
sc->blkcnt * ATI_IXP_NCHANS * sizeof(struct atiixp_dma_op),
|
||||
atiixp_dma_cb, sc, 0))
|
||||
goto bad;
|
||||
|
||||
|
||||
@ -1073,7 +1233,7 @@ atiixp_pci_attach(device_t dev)
|
||||
sc->delayed_attach.ich_func = atiixp_chip_post_init;
|
||||
sc->delayed_attach.ich_arg = sc;
|
||||
if (cold == 0 ||
|
||||
config_intrhook_establish(&sc->delayed_attach) != 0) {
|
||||
config_intrhook_establish(&sc->delayed_attach) != 0) {
|
||||
sc->delayed_attach.ich_func = NULL;
|
||||
atiixp_chip_post_init(sc);
|
||||
}
|
||||
@ -1116,15 +1276,12 @@ atiixp_pci_suspend(device_t dev)
|
||||
/* quickly disable interrupts and save channels active state */
|
||||
atiixp_lock(sc);
|
||||
atiixp_disable_interrupts(sc);
|
||||
value = atiixp_rd(sc, ATI_REG_CMD);
|
||||
sc->pch.active = (value & ATI_REG_CMD_SEND_EN) ? 1 : 0;
|
||||
sc->rch.active = (value & ATI_REG_CMD_RECEIVE_EN) ? 1 : 0;
|
||||
atiixp_unlock(sc);
|
||||
|
||||
/* stop everything */
|
||||
if (sc->pch.channel && sc->pch.active)
|
||||
if (sc->pch.active != 0)
|
||||
atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP);
|
||||
if (sc->rch.channel && sc->rch.active)
|
||||
if (sc->rch.active != 0)
|
||||
atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP);
|
||||
|
||||
/* power down aclink and pci bus */
|
||||
@ -1161,22 +1318,23 @@ atiixp_pci_resume(device_t dev)
|
||||
* Resume channel activities. Reset channel format regardless
|
||||
* of its previous state.
|
||||
*/
|
||||
if (sc->pch.channel) {
|
||||
if (sc->pch.fmt)
|
||||
if (sc->pch.channel != NULL) {
|
||||
if (sc->pch.fmt != 0)
|
||||
atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt);
|
||||
if (sc->pch.active)
|
||||
if (sc->pch.active != 0)
|
||||
atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START);
|
||||
}
|
||||
if (sc->rch.channel) {
|
||||
if (sc->rch.fmt)
|
||||
if (sc->rch.channel != NULL) {
|
||||
if (sc->rch.fmt != 0)
|
||||
atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt);
|
||||
if (sc->rch.active)
|
||||
if (sc->rch.active != 0)
|
||||
atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START);
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
atiixp_lock(sc);
|
||||
atiixp_enable_interrupts(sc);
|
||||
if (sc->polling == 0)
|
||||
atiixp_enable_interrupts(sc);
|
||||
atiixp_unlock(sc);
|
||||
|
||||
return (0);
|
||||
|
@ -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
@ -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) >> \
|
||||
|
@ -44,7 +44,7 @@
|
||||
* http://people.freebsd.org/~ariff/HDA/parser.rb . This crude
|
||||
* ruby parser take the verbose dmesg dump as its input. Refer to
|
||||
* http://www.microsoft.com/whdc/device/audio/default.mspx for various
|
||||
* interesting documents, especiall UAA (Universal Audio Architecture).
|
||||
* interesting documents, especially UAA (Universal Audio Architecture).
|
||||
* 4) Possible vendor specific support.
|
||||
* (snd_hda_intel, snd_hda_ati, etc..)
|
||||
*
|
||||
@ -80,7 +80,7 @@
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
#define HDA_DRV_TEST_REV "20061017_0033"
|
||||
#define HDA_DRV_TEST_REV "20061111_0034"
|
||||
#define HDA_WIDGET_PARSER_REV 1
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
@ -197,6 +197,11 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6)
|
||||
#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff)
|
||||
|
||||
/* Lenovo */
|
||||
#define LNV_VENDORID 0x17aa
|
||||
#define LNV_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LNV, 0x2066)
|
||||
#define LNV_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LNV, 0xffff)
|
||||
|
||||
|
||||
/* Misc constants.. */
|
||||
#define HDA_AMP_MUTE_DEFAULT (0xffffffff)
|
||||
@ -512,6 +517,9 @@ static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *,
|
||||
static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *);
|
||||
static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t);
|
||||
|
||||
static int hdac_rirb_flush(struct hdac_softc *sc);
|
||||
static int hdac_unsolq_flush(struct hdac_softc *sc);
|
||||
|
||||
#define hdac_command(a1, a2, a3) \
|
||||
hdac_command_sendone_internal(a1, a2, a3)
|
||||
|
||||
@ -788,7 +796,7 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
|
||||
{
|
||||
/* XXX to be removed */
|
||||
@ -797,7 +805,7 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
|
||||
#endif
|
||||
|
||||
if (ch->blkcnt == 0)
|
||||
return;
|
||||
return (0);
|
||||
|
||||
/* XXX to be removed */
|
||||
#ifdef HDAC_INTR_EXTRA
|
||||
@ -822,16 +830,13 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
|
||||
#ifdef HDAC_INTR_EXTRA
|
||||
if (res & HDAC_SDSTS_BCIS) {
|
||||
#endif
|
||||
ch->prevptr = ch->ptr;
|
||||
ch->ptr += sndbuf_getblksz(ch->b);
|
||||
ch->ptr %= sndbuf_getsize(ch->b);
|
||||
hdac_unlock(sc);
|
||||
chn_intr(ch->c);
|
||||
hdac_lock(sc);
|
||||
return (1);
|
||||
/* XXX to be removed */
|
||||
#ifdef HDAC_INTR_EXTRA
|
||||
}
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -845,14 +850,16 @@ hdac_intr_handler(void *context)
|
||||
struct hdac_softc *sc;
|
||||
uint32_t intsts;
|
||||
uint8_t rirbsts;
|
||||
uint8_t rirbwp;
|
||||
struct hdac_rirb *rirb_base, *rirb;
|
||||
nid_t ucad;
|
||||
uint32_t utag;
|
||||
struct hdac_rirb *rirb_base;
|
||||
uint32_t trigger = 0;
|
||||
|
||||
sc = (struct hdac_softc *)context;
|
||||
|
||||
hdac_lock(sc);
|
||||
if (sc->polling != 0) {
|
||||
hdac_unlock(sc);
|
||||
return;
|
||||
}
|
||||
/* Do we have anything to do? */
|
||||
intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS);
|
||||
if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) {
|
||||
@ -866,26 +873,9 @@ hdac_intr_handler(void *context)
|
||||
rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
|
||||
/* Get as many responses that we can */
|
||||
while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) {
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
|
||||
rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
|
||||
bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
|
||||
BUS_DMASYNC_POSTREAD);
|
||||
while (sc->rirb_rp != rirbwp) {
|
||||
sc->rirb_rp++;
|
||||
sc->rirb_rp %= sc->rirb_size;
|
||||
rirb = &rirb_base[sc->rirb_rp];
|
||||
if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
|
||||
ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
|
||||
utag = rirb->response >> 26;
|
||||
if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
|
||||
sc->codecs[ucad] != NULL) {
|
||||
sc->unsolq[sc->unsolq_wp++] =
|
||||
(ucad << 16) |
|
||||
(utag & 0xffff);
|
||||
sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
HDAC_WRITE_1(&sc->mem,
|
||||
HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
|
||||
hdac_rirb_flush(sc);
|
||||
rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
|
||||
}
|
||||
/* XXX to be removed */
|
||||
@ -894,29 +884,29 @@ hdac_intr_handler(void *context)
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS);
|
||||
#endif
|
||||
}
|
||||
|
||||
hdac_unsolq_flush(sc);
|
||||
|
||||
if (intsts & HDAC_INTSTS_SIS_MASK) {
|
||||
if (intsts & (1 << sc->num_iss))
|
||||
hdac_stream_intr(sc, &sc->play);
|
||||
if (intsts & (1 << 0))
|
||||
hdac_stream_intr(sc, &sc->rec);
|
||||
if ((intsts & (1 << sc->num_iss)) &&
|
||||
hdac_stream_intr(sc, &sc->play) != 0)
|
||||
trigger |= 1;
|
||||
if ((intsts & (1 << 0)) &&
|
||||
hdac_stream_intr(sc, &sc->rec) != 0)
|
||||
trigger |= 2;
|
||||
/* XXX to be removed */
|
||||
#ifdef HDAC_INTR_EXTRA
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts &
|
||||
HDAC_INTSTS_SIS_MASK);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
|
||||
sc->unsolq_st = HDAC_UNSOLQ_BUSY;
|
||||
while (sc->unsolq_rp != sc->unsolq_wp) {
|
||||
ucad = sc->unsolq[sc->unsolq_rp] >> 16;
|
||||
utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
|
||||
sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
|
||||
hdac_unsolicited_handler(sc->codecs[ucad], utag);
|
||||
}
|
||||
sc->unsolq_st = HDAC_UNSOLQ_READY;
|
||||
}
|
||||
|
||||
hdac_unlock(sc);
|
||||
|
||||
if (trigger & 1)
|
||||
chn_intr(sc->play.c);
|
||||
if (trigger & 2)
|
||||
chn_intr(sc->rec.c);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1216,6 +1206,7 @@ hdac_mem_free(struct hdac_softc *sc)
|
||||
if (mem->mem_res != NULL)
|
||||
bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid,
|
||||
mem->mem_res);
|
||||
mem->mem_res = NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1239,7 +1230,7 @@ hdac_irq_alloc(struct hdac_softc *sc)
|
||||
goto fail;
|
||||
}
|
||||
result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE,
|
||||
hdac_intr_handler, sc, &irq->irq_handle);
|
||||
hdac_intr_handler, sc, &irq->irq_handle);
|
||||
if (result != 0) {
|
||||
device_printf(sc->dev,
|
||||
"%s: Unable to setup interrupt handler (%x)\n",
|
||||
@ -1250,9 +1241,8 @@ hdac_irq_alloc(struct hdac_softc *sc)
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
if (irq->irq_res != NULL)
|
||||
bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
|
||||
irq->irq_res);
|
||||
hdac_irq_free(sc);
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
@ -1267,11 +1257,13 @@ hdac_irq_free(struct hdac_softc *sc)
|
||||
struct hdac_irq *irq;
|
||||
|
||||
irq = &sc->irq;
|
||||
if (irq->irq_handle != NULL)
|
||||
if (irq->irq_res != NULL && irq->irq_handle != NULL)
|
||||
bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle);
|
||||
if (irq->irq_res != NULL)
|
||||
bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
|
||||
irq->irq_res);
|
||||
irq->irq_handle = NULL;
|
||||
irq->irq_res = NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1362,16 +1354,18 @@ hdac_rirb_init(struct hdac_softc *sc)
|
||||
sc->rirb_rp = 0;
|
||||
HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST);
|
||||
|
||||
/* Setup the interrupt threshold */
|
||||
HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
|
||||
if (sc->polling == 0) {
|
||||
/* Setup the interrupt threshold */
|
||||
HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
|
||||
|
||||
/* Enable Overrun and response received reporting */
|
||||
/* Enable Overrun and response received reporting */
|
||||
#if 0
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
|
||||
HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
|
||||
HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
|
||||
#else
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that the Host CPU cache doesn't contain any dirty
|
||||
@ -1439,6 +1433,8 @@ hdac_scan_codecs(struct hdac_softc *sc)
|
||||
"Unable to allocate memory for codec\n");
|
||||
continue;
|
||||
}
|
||||
codec->commands = NULL;
|
||||
codec->responses_received = 0;
|
||||
codec->verbs_sent = 0;
|
||||
codec->sc = sc;
|
||||
codec->cad = i;
|
||||
@ -1688,14 +1684,14 @@ hdac_widget_pin_parse(struct hdac_widget *w)
|
||||
w->wclass.pin.config = config;
|
||||
|
||||
pincap = hdac_command(sc,
|
||||
HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
|
||||
HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
|
||||
w->wclass.pin.cap = pincap;
|
||||
|
||||
w->wclass.pin.ctrl = hdac_command(sc,
|
||||
HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
|
||||
~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
|
||||
HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
|
||||
HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE);
|
||||
HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
|
||||
~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
|
||||
HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
|
||||
HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE);
|
||||
|
||||
if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
|
||||
w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE;
|
||||
@ -1907,6 +1903,144 @@ hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid)
|
||||
return (&devinfo->widget[nid - devinfo->startnode]);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
hda_poll_channel(struct hdac_chan *ch)
|
||||
{
|
||||
uint32_t sz, delta;
|
||||
volatile uint32_t ptr;
|
||||
|
||||
if (ch->active == 0)
|
||||
return (0);
|
||||
|
||||
sz = ch->blksz * ch->blkcnt;
|
||||
ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, ch->off + HDAC_SDLPIB);
|
||||
ch->ptr = ptr;
|
||||
ptr %= sz;
|
||||
ptr &= ~(ch->blksz - 1);
|
||||
delta = (sz + ptr - ch->prevptr) % sz;
|
||||
|
||||
if (delta < ch->blksz)
|
||||
return (0);
|
||||
|
||||
ch->prevptr = ptr;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
#define hda_chan_active(sc) ((sc)->play.active + (sc)->rec.active)
|
||||
|
||||
static void
|
||||
hda_poll_callback(void *arg)
|
||||
{
|
||||
struct hdac_softc *sc = arg;
|
||||
uint32_t trigger = 0;
|
||||
|
||||
if (sc == NULL)
|
||||
return;
|
||||
|
||||
hdac_lock(sc);
|
||||
if (sc->polling == 0 || hda_chan_active(sc) == 0) {
|
||||
hdac_unlock(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0;
|
||||
trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0;
|
||||
|
||||
/* XXX */
|
||||
callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/,
|
||||
hda_poll_callback, sc);
|
||||
|
||||
hdac_unlock(sc);
|
||||
|
||||
if (trigger & 1)
|
||||
chn_intr(sc->play.c);
|
||||
if (trigger & 2)
|
||||
chn_intr(sc->rec.c);
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_rirb_flush(struct hdac_softc *sc)
|
||||
{
|
||||
struct hdac_rirb *rirb_base, *rirb;
|
||||
struct hdac_codec *codec;
|
||||
struct hdac_command_list *commands;
|
||||
nid_t cad;
|
||||
uint32_t resp;
|
||||
uint8_t rirbwp;
|
||||
int ret = 0;
|
||||
|
||||
rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr;
|
||||
rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
|
||||
bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
|
||||
BUS_DMASYNC_POSTREAD);
|
||||
|
||||
while (sc->rirb_rp != rirbwp) {
|
||||
sc->rirb_rp++;
|
||||
sc->rirb_rp %= sc->rirb_size;
|
||||
rirb = &rirb_base[sc->rirb_rp];
|
||||
cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
|
||||
if (cad < 0 || cad >= HDAC_CODEC_MAX ||
|
||||
sc->codecs[cad] == NULL)
|
||||
continue;
|
||||
resp = rirb->response;
|
||||
codec = sc->codecs[cad];
|
||||
commands = codec->commands;
|
||||
if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
|
||||
sc->unsolq[sc->unsolq_wp++] = (cad << 16) |
|
||||
((resp >> 26) & 0xffff);
|
||||
sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
|
||||
} else if (commands != NULL && commands->num_commands > 0 &&
|
||||
codec->responses_received < commands->num_commands)
|
||||
commands->responses[codec->responses_received++] =
|
||||
resp;
|
||||
ret++;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_unsolq_flush(struct hdac_softc *sc)
|
||||
{
|
||||
nid_t cad;
|
||||
uint32_t tag;
|
||||
int ret = 0;
|
||||
|
||||
if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
|
||||
sc->unsolq_st = HDAC_UNSOLQ_BUSY;
|
||||
while (sc->unsolq_rp != sc->unsolq_wp) {
|
||||
cad = sc->unsolq[sc->unsolq_rp] >> 16;
|
||||
tag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
|
||||
sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
|
||||
hdac_unsolicited_handler(sc->codecs[cad], tag);
|
||||
ret++;
|
||||
}
|
||||
sc->unsolq_st = HDAC_UNSOLQ_READY;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
hdac_poll_callback(void *arg)
|
||||
{
|
||||
struct hdac_softc *sc = arg;
|
||||
if (sc == NULL)
|
||||
return;
|
||||
|
||||
hdac_lock(sc);
|
||||
if (sc->polling == 0) {
|
||||
hdac_unlock(sc);
|
||||
return;
|
||||
}
|
||||
hdac_rirb_flush(sc);
|
||||
hdac_unsolq_flush(sc);
|
||||
callout_reset(&sc->poll_hdac, max(hz >> 2, 1),
|
||||
hdac_poll_callback, sc);
|
||||
hdac_unlock(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
hdac_stream_stop(struct hdac_chan *ch)
|
||||
{
|
||||
@ -1918,9 +2052,50 @@ hdac_stream_stop(struct hdac_chan *ch)
|
||||
HDAC_SDCTL_RUN);
|
||||
HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
|
||||
|
||||
ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
|
||||
ctl &= ~(1 << (ch->off >> 5));
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
|
||||
ch->active = 0;
|
||||
|
||||
if (sc->polling != 0) {
|
||||
int pollticks;
|
||||
|
||||
if (hda_chan_active(sc) == 0) {
|
||||
callout_stop(&sc->poll_hda);
|
||||
sc->poll_ticks = 1;
|
||||
} else {
|
||||
if (sc->play.active != 0)
|
||||
ch = &sc->play;
|
||||
else
|
||||
ch = &sc->rec;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->b) *
|
||||
sndbuf_getspd(ch->b));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
pollticks = hz;
|
||||
if (pollticks < 1) {
|
||||
HDA_BOOTVERBOSE(
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks=%d < 1 !\n",
|
||||
__func__, pollticks);
|
||||
);
|
||||
pollticks = 1;
|
||||
}
|
||||
if (pollticks > sc->poll_ticks) {
|
||||
HDA_BOOTVERBOSE(
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks %d -> %d\n",
|
||||
__func__, sc->poll_ticks,
|
||||
pollticks);
|
||||
);
|
||||
sc->poll_ticks = pollticks;
|
||||
callout_reset(&sc->poll_hda, 1,
|
||||
hda_poll_callback, sc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
|
||||
ctl &= ~(1 << (ch->off >> 5));
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1929,14 +2104,52 @@ hdac_stream_start(struct hdac_chan *ch)
|
||||
struct hdac_softc *sc = ch->devinfo->codec->sc;
|
||||
uint32_t ctl;
|
||||
|
||||
ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
|
||||
ctl |= 1 << (ch->off >> 5);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
|
||||
if (sc->polling != 0) {
|
||||
int pollticks;
|
||||
|
||||
ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
|
||||
ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
|
||||
HDAC_SDCTL_RUN;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
pollticks = hz;
|
||||
if (pollticks < 1) {
|
||||
HDA_BOOTVERBOSE(
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks=%d < 1 !\n",
|
||||
__func__, pollticks);
|
||||
);
|
||||
pollticks = 1;
|
||||
}
|
||||
if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) {
|
||||
HDA_BOOTVERBOSE(
|
||||
if (hda_chan_active(sc) == 0) {
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks=%d\n",
|
||||
__func__, pollticks);
|
||||
} else {
|
||||
device_printf(sc->dev,
|
||||
"%s: pollticks %d -> %d\n",
|
||||
__func__, sc->poll_ticks,
|
||||
pollticks);
|
||||
}
|
||||
);
|
||||
sc->poll_ticks = pollticks;
|
||||
callout_reset(&sc->poll_hda, 1, hda_poll_callback,
|
||||
sc);
|
||||
}
|
||||
ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
|
||||
ctl |= HDAC_SDCTL_RUN;
|
||||
} else {
|
||||
ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
|
||||
ctl |= 1 << (ch->off >> 5);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
|
||||
ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
|
||||
ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
|
||||
HDAC_SDCTL_RUN;
|
||||
}
|
||||
HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
|
||||
|
||||
ch->active = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1988,28 +2201,32 @@ static void
|
||||
hdac_bdl_setup(struct hdac_chan *ch)
|
||||
{
|
||||
struct hdac_softc *sc = ch->devinfo->codec->sc;
|
||||
uint64_t addr;
|
||||
int blks, size, blocksize;
|
||||
struct hdac_bdle *bdle;
|
||||
uint64_t addr;
|
||||
uint32_t blksz, blkcnt;
|
||||
int i;
|
||||
|
||||
addr = (uint64_t)sndbuf_getbufaddr(ch->b);
|
||||
size = sndbuf_getsize(ch->b);
|
||||
blocksize = sndbuf_getblksz(ch->b);
|
||||
blks = size / blocksize;
|
||||
bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr;
|
||||
bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr;
|
||||
|
||||
for (i = 0; i < blks; i++, bdle++) {
|
||||
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));
|
||||
@ -2046,7 +2263,7 @@ hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid,
|
||||
v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
|
||||
(lmute << 7) | left;
|
||||
hdac_command(sc,
|
||||
HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
|
||||
HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
|
||||
v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
|
||||
(rmute << 7) | right;
|
||||
} else
|
||||
@ -2142,14 +2359,12 @@ hdac_command_send_internal(struct hdac_softc *sc,
|
||||
struct hdac_codec *codec;
|
||||
int corbrp;
|
||||
uint32_t *corb;
|
||||
uint8_t rirbwp;
|
||||
int timeout;
|
||||
int retry = 10;
|
||||
struct hdac_rirb *rirb_base, *rirb;
|
||||
nid_t ucad;
|
||||
uint32_t utag;
|
||||
struct hdac_rirb *rirb_base;
|
||||
|
||||
if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL)
|
||||
if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL ||
|
||||
commands->num_commands < 1)
|
||||
return;
|
||||
|
||||
codec = sc->codecs[cad];
|
||||
@ -2180,55 +2395,22 @@ hdac_command_send_internal(struct hdac_softc *sc,
|
||||
}
|
||||
|
||||
timeout = 1000;
|
||||
do {
|
||||
rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
|
||||
bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
|
||||
BUS_DMASYNC_POSTREAD);
|
||||
if (sc->rirb_rp != rirbwp) {
|
||||
do {
|
||||
sc->rirb_rp++;
|
||||
sc->rirb_rp %= sc->rirb_size;
|
||||
rirb = &rirb_base[sc->rirb_rp];
|
||||
if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
|
||||
ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
|
||||
utag = rirb->response >> 26;
|
||||
if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
|
||||
sc->codecs[ucad] != NULL) {
|
||||
sc->unsolq[sc->unsolq_wp++] =
|
||||
(ucad << 16) |
|
||||
(utag & 0xffff);
|
||||
sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
|
||||
}
|
||||
} else if (codec->responses_received < commands->num_commands)
|
||||
codec->commands->responses[codec->responses_received++] =
|
||||
rirb->response;
|
||||
} while (sc->rirb_rp != rirbwp);
|
||||
break;
|
||||
}
|
||||
while (hdac_rirb_flush(sc) == 0 && --timeout)
|
||||
DELAY(10);
|
||||
} while (--timeout);
|
||||
} while ((codec->verbs_sent != commands->num_commands ||
|
||||
codec->responses_received != commands->num_commands) &&
|
||||
--retry);
|
||||
codec->responses_received != commands->num_commands) && --retry);
|
||||
|
||||
if (retry == 0)
|
||||
device_printf(sc->dev,
|
||||
"%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
|
||||
__func__, commands->num_commands,
|
||||
codec->verbs_sent, codec->responses_received);
|
||||
"%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
|
||||
__func__, commands->num_commands, codec->verbs_sent,
|
||||
codec->responses_received);
|
||||
|
||||
codec->commands = NULL;
|
||||
codec->responses_received = 0;
|
||||
codec->verbs_sent = 0;
|
||||
|
||||
if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
|
||||
sc->unsolq_st = HDAC_UNSOLQ_BUSY;
|
||||
while (sc->unsolq_rp != sc->unsolq_wp) {
|
||||
ucad = sc->unsolq[sc->unsolq_rp] >> 16;
|
||||
utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
|
||||
sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
|
||||
hdac_unsolicited_handler(sc->codecs[ucad], utag);
|
||||
}
|
||||
sc->unsolq_st = HDAC_UNSOLQ_READY;
|
||||
}
|
||||
hdac_unsolq_flush(sc);
|
||||
}
|
||||
|
||||
|
||||
@ -2352,16 +2534,18 @@ static int
|
||||
hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct hdac_chan *ch = data;
|
||||
uint32_t spd = 0;
|
||||
uint32_t spd = 0, threshold;
|
||||
int i;
|
||||
|
||||
for (i = 0; ch->pcmrates[i] != 0; i++) {
|
||||
spd = ch->pcmrates[i];
|
||||
if (spd >= speed)
|
||||
threshold = spd + ((ch->pcmrates[i + 1] != 0) ?
|
||||
((ch->pcmrates[i + 1] - spd) >> 1) : 0);
|
||||
if (speed < threshold)
|
||||
break;
|
||||
}
|
||||
|
||||
if (spd == 0)
|
||||
if (spd == 0) /* impossible */
|
||||
ch->spd = 48000;
|
||||
else
|
||||
ch->spd = spd;
|
||||
@ -2416,11 +2600,25 @@ hdac_stream_setup(struct hdac_chan *ch)
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
|
||||
hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
{
|
||||
struct hdac_chan *ch = data;
|
||||
struct hdac_softc *sc = ch->devinfo->codec->sc;
|
||||
|
||||
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);
|
||||
ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
|
||||
if (sc->polling != 0)
|
||||
ptr = ch->ptr;
|
||||
else
|
||||
ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
|
||||
hdac_unlock(sc);
|
||||
|
||||
sz = sndbuf_getsize(ch->b);
|
||||
ptr %= sz;
|
||||
|
||||
if (ch->dir == PCMDIR_REC) {
|
||||
delta = ptr % sndbuf_getblksz(ch->b);
|
||||
if (delta != 0) {
|
||||
ptr -= delta;
|
||||
if (ptr < delta)
|
||||
ptr = sz - delta;
|
||||
else
|
||||
ptr -= delta;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Round to available space and force 128 bytes aligment.
|
||||
*/
|
||||
ptr %= ch->blksz * ch->blkcnt;
|
||||
ptr &= ~0x7f;
|
||||
|
||||
return (ptr);
|
||||
}
|
||||
@ -2872,11 +3064,24 @@ hdac_attach(device_t dev)
|
||||
sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16;
|
||||
sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff;
|
||||
|
||||
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,9 +4236,14 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir)
|
||||
}*/
|
||||
if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
|
||||
continue;
|
||||
if (ret == 0) {
|
||||
fmtcap = w->param.supp_stream_formats;
|
||||
pcmcap = w->param.supp_pcm_size_rate;
|
||||
} else {
|
||||
fmtcap &= w->param.supp_stream_formats;
|
||||
pcmcap &= w->param.supp_pcm_size_rate;
|
||||
}
|
||||
ch->io[ret++] = i;
|
||||
fmtcap &= w->param.supp_stream_formats;
|
||||
pcmcap &= w->param.supp_pcm_size_rate;
|
||||
}
|
||||
ch->io[ret] = -1;
|
||||
|
||||
@ -4498,6 +4707,8 @@ hdac_release_resources(struct hdac_softc *sc)
|
||||
return;
|
||||
|
||||
hdac_lock(sc);
|
||||
if (sc->polling != 0)
|
||||
callout_stop(&sc->poll_hdac);
|
||||
hdac_reset(sc);
|
||||
hdac_unlock(sc);
|
||||
snd_mtxfree(sc->lock);
|
||||
@ -4597,6 +4808,65 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hdac_polling(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct hdac_softc *sc;
|
||||
struct hdac_devinfo *devinfo;
|
||||
device_t dev;
|
||||
uint32_t ctl;
|
||||
int err, val;
|
||||
|
||||
dev = oidp->oid_arg1;
|
||||
devinfo = pcm_getdevinfo(dev);
|
||||
if (devinfo == NULL || devinfo->codec == NULL ||
|
||||
devinfo->codec->sc == NULL)
|
||||
return (EINVAL);
|
||||
sc = devinfo->codec->sc;
|
||||
hdac_lock(sc);
|
||||
val = sc->polling;
|
||||
hdac_unlock(sc);
|
||||
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
|
||||
|
||||
if (err || req->newptr == NULL)
|
||||
return (err);
|
||||
if (val < 0 || val > 1)
|
||||
return (EINVAL);
|
||||
|
||||
hdac_lock(sc);
|
||||
if (val != sc->polling) {
|
||||
if (hda_chan_active(sc) != 0)
|
||||
err = EBUSY;
|
||||
else if (val == 0) {
|
||||
callout_stop(&sc->poll_hdac);
|
||||
HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT,
|
||||
sc->rirb_size / 2);
|
||||
ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL);
|
||||
ctl |= HDAC_RIRBCTL_RINTCTL;
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL,
|
||||
HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
|
||||
sc->polling = 0;
|
||||
DELAY(1000);
|
||||
} else {
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, 0);
|
||||
HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, 0);
|
||||
ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL);
|
||||
ctl &= ~HDAC_RIRBCTL_RINTCTL;
|
||||
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl);
|
||||
callout_reset(&sc->poll_hdac, 1, hdac_poll_callback,
|
||||
sc);
|
||||
sc->polling = 1;
|
||||
DELAY(1000);
|
||||
}
|
||||
}
|
||||
hdac_unlock(sc);
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
hdac_attach2(void *arg)
|
||||
{
|
||||
@ -4642,7 +4912,9 @@ hdac_attach2(void *arg)
|
||||
device_printf(sc->dev,
|
||||
"HDA_DEBUG: Enabling controller interrupt...\n");
|
||||
);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
|
||||
if (sc->polling == 0)
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL,
|
||||
HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
|
||||
HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) |
|
||||
HDAC_GCTL_UNSOL);
|
||||
|
||||
@ -4779,17 +5051,24 @@ hdac_attach2(void *arg)
|
||||
for (i = 0; i < rcnt; i++)
|
||||
pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo);
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
|
||||
sysctl_hdac_polling, "I", "Enable polling mode");
|
||||
#endif
|
||||
|
||||
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]",
|
||||
rman_get_start(sc->mem.mem_res),
|
||||
rman_get_start(sc->irq.irq_res),
|
||||
PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV);
|
||||
rman_get_start(sc->mem.mem_res), rman_get_start(sc->irq.irq_res),
|
||||
PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV);
|
||||
pcm_setstatus(sc->dev, status);
|
||||
device_printf(sc->dev, "<HDA Codec: %s>\n", hdac_codec_name(devinfo));
|
||||
HDA_BOOTVERBOSE(
|
||||
device_printf(sc->dev, "<HDA Codec ID: 0x%08x>\n",
|
||||
hdac_codec_id(devinfo));
|
||||
);
|
||||
device_printf(sc->dev, "<HDA Driver Revision: %s>\n", HDA_DRV_TEST_REV);
|
||||
device_printf(sc->dev, "<HDA Driver Revision: %s>\n",
|
||||
HDA_DRV_TEST_REV);
|
||||
|
||||
HDA_BOOTVERBOSE(
|
||||
if (devinfo->function.audio.quirks != 0) {
|
||||
@ -4844,6 +5123,12 @@ hdac_attach2(void *arg)
|
||||
device_printf(sc->dev, "+--------------------------------------+\n");
|
||||
hdac_dump_pcmchannels(sc, pcnt, rcnt);
|
||||
);
|
||||
|
||||
if (sc->polling != 0) {
|
||||
hdac_lock(sc);
|
||||
callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc);
|
||||
hdac_unlock(sc);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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
|
||||
|
@ -36,10 +36,14 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */
|
||||
#define ICH_DTBL_LENGTH 32
|
||||
#define ICH_DEFAULT_BUFSZ 16384
|
||||
#define ICH_MAX_BUFSZ 65536
|
||||
#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */
|
||||
#define ICH_DTBL_LENGTH 32
|
||||
#define ICH_DEFAULT_BUFSZ 16384
|
||||
#define ICH_MAX_BUFSZ 65536
|
||||
#define ICH_MIN_BUFSZ 4096
|
||||
#define ICH_DEFAULT_BLKCNT 2
|
||||
#define ICH_MAX_BLKCNT 32
|
||||
#define ICH_MIN_BLKCNT 2
|
||||
|
||||
#define INTEL_VENDORID 0x8086
|
||||
#define SIS_VENDORID 0x1039
|
||||
@ -71,6 +75,14 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock)
|
||||
#define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock)
|
||||
|
||||
#if 0
|
||||
#define ICH_DEBUG(stmt) do { \
|
||||
stmt \
|
||||
} while(0)
|
||||
#else
|
||||
#define ICH_DEBUG(stmt)
|
||||
#endif
|
||||
|
||||
static const struct ich_type {
|
||||
uint16_t vendor;
|
||||
uint16_t devid;
|
||||
@ -122,19 +134,19 @@ static const struct ich_type {
|
||||
|
||||
/* buffer descriptor */
|
||||
struct ich_desc {
|
||||
volatile u_int32_t buffer;
|
||||
volatile u_int32_t length;
|
||||
volatile uint32_t buffer;
|
||||
volatile uint32_t length;
|
||||
};
|
||||
|
||||
struct sc_info;
|
||||
|
||||
/* channel registers */
|
||||
struct sc_chinfo {
|
||||
u_int32_t num:8, run:1, run_save:1;
|
||||
u_int32_t blksz, blkcnt, spd;
|
||||
u_int32_t regbase, spdreg;
|
||||
u_int32_t imask;
|
||||
u_int32_t civ;
|
||||
uint32_t num:8, run:1, run_save:1;
|
||||
uint32_t blksz, blkcnt, spd;
|
||||
uint32_t regbase, spdreg;
|
||||
uint32_t imask;
|
||||
uint32_t civ;
|
||||
|
||||
struct snd_dbuf *buffer;
|
||||
struct pcm_channel *channel;
|
||||
@ -148,8 +160,8 @@ struct sc_chinfo {
|
||||
struct sc_info {
|
||||
device_t dev;
|
||||
int hasvra, hasvrm, hasmic;
|
||||
unsigned int chnum, bufsz;
|
||||
int sample_size, swap_reg;
|
||||
unsigned int chnum, bufsz, blkcnt;
|
||||
int sample_size, swap_reg, fixedrate;
|
||||
|
||||
struct resource *nambar, *nabmbar, *irq;
|
||||
int regtype, nambarid, nabmbarid, irqid;
|
||||
@ -161,7 +173,7 @@ struct sc_info {
|
||||
|
||||
struct ac97_info *codec;
|
||||
struct sc_chinfo ch[3];
|
||||
int ac97rate;
|
||||
int ac97rate, calibrated;
|
||||
struct ich_desc *dtbl;
|
||||
bus_addr_t desc_addr;
|
||||
struct intr_config_hook intrhook;
|
||||
@ -175,7 +187,7 @@ struct sc_info {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static u_int32_t ich_fmt[] = {
|
||||
static uint32_t ich_fmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
0
|
||||
};
|
||||
@ -184,23 +196,23 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Hardware */
|
||||
static __inline u_int32_t
|
||||
static __inline uint32_t
|
||||
ich_rd(struct sc_info *sc, int regno, int size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno);
|
||||
return (bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno));
|
||||
case 2:
|
||||
return bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno);
|
||||
return (bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno));
|
||||
case 4:
|
||||
return bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno);
|
||||
return (bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno));
|
||||
default:
|
||||
return 0xffffffff;
|
||||
return (0xffffffff);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline void
|
||||
ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
|
||||
ich_wr(struct sc_info *sc, int regno, uint32_t data, int size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
@ -219,20 +231,20 @@ ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
|
||||
static int
|
||||
ich_waitcd(void *devinfo)
|
||||
{
|
||||
int i;
|
||||
u_int32_t data;
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
uint32_t data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ICH_TIMEOUT; i++) {
|
||||
data = ich_rd(sc, ICH_REG_ACC_SEMA, 1);
|
||||
if ((data & 0x01) == 0)
|
||||
return 0;
|
||||
return (0);
|
||||
DELAY(1);
|
||||
}
|
||||
if ((sc->flags & IGNORE_PCR) != 0)
|
||||
return (0);
|
||||
device_printf(sc->dev, "CODEC semaphore timeout\n");
|
||||
return ETIMEDOUT;
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -243,11 +255,11 @@ ich_rdcd(kobj_t obj, void *devinfo, int regno)
|
||||
regno &= 0xff;
|
||||
ich_waitcd(sc);
|
||||
|
||||
return bus_space_read_2(sc->nambart, sc->nambarh, regno);
|
||||
return (bus_space_read_2(sc->nambart, sc->nambarh, regno));
|
||||
}
|
||||
|
||||
static int
|
||||
ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
|
||||
ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
|
||||
@ -255,7 +267,7 @@ ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
|
||||
ich_waitcd(sc);
|
||||
bus_space_write_2(sc->nambart, sc->nambarh, regno, data);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static kobj_method_t ich_ac97_methods[] = {
|
||||
@ -272,13 +284,17 @@ static void
|
||||
ich_filldtbl(struct sc_chinfo *ch)
|
||||
{
|
||||
struct sc_info *sc = ch->parent;
|
||||
u_int32_t base;
|
||||
uint32_t base;
|
||||
int i;
|
||||
|
||||
base = sndbuf_getbufaddr(ch->buffer);
|
||||
if (ch->blksz > sc->bufsz / ch->blkcnt)
|
||||
ch->blksz = sc->bufsz / ch->blkcnt;
|
||||
sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz);
|
||||
if ((ch->blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer))
|
||||
ch->blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt;
|
||||
if ((sndbuf_getblksz(ch->buffer) != ch->blksz ||
|
||||
sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) &&
|
||||
sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz) != 0)
|
||||
device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
|
||||
__func__, ch->blksz, ch->blkcnt);
|
||||
ch->blksz = sndbuf_getblksz(ch->buffer);
|
||||
|
||||
for (i = 0; i < ICH_DTBL_LENGTH; i++) {
|
||||
@ -300,7 +316,7 @@ ich_resetchan(struct sc_info *sc, int num)
|
||||
else if (num == 2)
|
||||
regbase = ICH_REG_MC_BASE;
|
||||
else
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
|
||||
ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1);
|
||||
#if 1
|
||||
@ -313,11 +329,11 @@ ich_resetchan(struct sc_info *sc, int num)
|
||||
for (i = 0; i < ICH_TIMEOUT; i++) {
|
||||
cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1);
|
||||
if (cr == 0)
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
device_printf(sc->dev, "cannot reset channel %d\n", num);
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@ -341,58 +357,78 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
|
||||
ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH);
|
||||
ch->desc_addr = sc->desc_addr + (ch->num * ICH_DTBL_LENGTH) *
|
||||
sizeof(struct ich_desc);
|
||||
ch->blkcnt = 2;
|
||||
ch->blkcnt = sc->blkcnt;
|
||||
ch->blksz = sc->bufsz / ch->blkcnt;
|
||||
|
||||
switch(ch->num) {
|
||||
case 0: /* play */
|
||||
KASSERT(dir == PCMDIR_PLAY, ("wrong direction"));
|
||||
ch->regbase = ICH_REG_PO_BASE;
|
||||
ch->spdreg = sc->hasvra? AC97_REGEXT_FDACRATE : 0;
|
||||
ch->spdreg = (sc->hasvra) ? AC97_REGEXT_FDACRATE : 0;
|
||||
ch->imask = ICH_GLOB_STA_POINT;
|
||||
break;
|
||||
|
||||
case 1: /* record */
|
||||
KASSERT(dir == PCMDIR_REC, ("wrong direction"));
|
||||
ch->regbase = ICH_REG_PI_BASE;
|
||||
ch->spdreg = sc->hasvra? AC97_REGEXT_LADCRATE : 0;
|
||||
ch->spdreg = (sc->hasvra) ? AC97_REGEXT_LADCRATE : 0;
|
||||
ch->imask = ICH_GLOB_STA_PIINT;
|
||||
break;
|
||||
|
||||
case 2: /* mic */
|
||||
KASSERT(dir == PCMDIR_REC, ("wrong direction"));
|
||||
ch->regbase = ICH_REG_MC_BASE;
|
||||
ch->spdreg = sc->hasvrm? AC97_REGEXT_MADCRATE : 0;
|
||||
ch->spdreg = (sc->hasvrm) ? AC97_REGEXT_MADCRATE : 0;
|
||||
ch->imask = ICH_GLOB_STA_MINT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (sc->fixedrate != 0)
|
||||
ch->spdreg = 0;
|
||||
|
||||
ICH_UNLOCK(sc);
|
||||
if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0)
|
||||
return NULL;
|
||||
return (NULL);
|
||||
|
||||
ICH_LOCK(sc);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
|
||||
ICH_UNLOCK(sc);
|
||||
|
||||
return ch;
|
||||
return (ch);
|
||||
}
|
||||
|
||||
static int
|
||||
ichchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
ichchan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
{
|
||||
return 0;
|
||||
|
||||
ICH_DEBUG(
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
ichchan_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
|
||||
ICH_DEBUG(
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
if (ch->spdreg) {
|
||||
int r, ac97rate;
|
||||
|
||||
@ -403,30 +439,37 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
ICH_UNLOCK(sc);
|
||||
r = (speed * 48000) / ac97rate;
|
||||
/*
|
||||
* Cast the return value of ac97_setrate() to u_int so that
|
||||
* Cast the return value of ac97_setrate() to uint64 so that
|
||||
* the math don't overflow into the negative range.
|
||||
*/
|
||||
ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) *
|
||||
ch->spd = ((uint64_t)ac97_setrate(sc->codec, ch->spdreg, r) *
|
||||
ac97rate) / 48000;
|
||||
} else {
|
||||
ch->spd = 48000;
|
||||
}
|
||||
return ch->spd;
|
||||
return (ch->spd);
|
||||
}
|
||||
|
||||
static int
|
||||
ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
|
||||
ICH_DEBUG(
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
ch->blksz = blocksize;
|
||||
ich_filldtbl(ch);
|
||||
ICH_LOCK(sc);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1);
|
||||
ICH_UNLOCK(sc);
|
||||
|
||||
return ch->blksz;
|
||||
return (ch->blksz);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -435,11 +478,18 @@ ichchan_trigger(kobj_t obj, void *data, int go)
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
|
||||
ICH_DEBUG(
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
ch->run = 1;
|
||||
ICH_LOCK(sc);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1);
|
||||
ICH_UNLOCK(sc);
|
||||
break;
|
||||
@ -451,7 +501,7 @@ ichchan_trigger(kobj_t obj, void *data, int go)
|
||||
ch->run = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -459,7 +509,14 @@ ichchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
u_int32_t pos;
|
||||
uint32_t pos;
|
||||
|
||||
ICH_DEBUG(
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
ICH_LOCK(sc);
|
||||
ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt;
|
||||
@ -467,7 +524,7 @@ ichchan_getptr(kobj_t obj, void *data)
|
||||
|
||||
pos = ch->civ * ch->blksz;
|
||||
|
||||
return pos;
|
||||
return (pos);
|
||||
}
|
||||
|
||||
static struct pcmchan_caps *
|
||||
@ -475,7 +532,16 @@ ichchan_getcaps(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
|
||||
return ch->spdreg? &ich_vrcaps : &ich_caps;
|
||||
ICH_DEBUG(
|
||||
struct sc_info *sc = ch->parent;
|
||||
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(ch->parent->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
return ((ch->spdreg) ? &ich_vrcaps : &ich_caps);
|
||||
}
|
||||
|
||||
static kobj_method_t ichchan_methods[] = {
|
||||
@ -498,10 +564,18 @@ ich_intr(void *p)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)p;
|
||||
struct sc_chinfo *ch;
|
||||
u_int32_t cbi, lbi, lvi, st, gs;
|
||||
uint32_t cbi, lbi, lvi, st, gs;
|
||||
int i;
|
||||
|
||||
ICH_LOCK(sc);
|
||||
|
||||
ICH_DEBUG(
|
||||
if (sc->calibrated == 0)
|
||||
device_printf(sc->dev,
|
||||
"WARNING: %s() called before calibration!\n",
|
||||
__func__);
|
||||
);
|
||||
|
||||
gs = ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_IMASK;
|
||||
if (gs & (ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES)) {
|
||||
/* Clear resume interrupt(s) - nothing doing with them */
|
||||
@ -515,7 +589,7 @@ ich_intr(void *p)
|
||||
continue;
|
||||
gs &= ~ch->imask;
|
||||
st = ich_rd(sc, ch->regbase +
|
||||
(sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),
|
||||
((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR),
|
||||
2);
|
||||
st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI;
|
||||
if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) {
|
||||
@ -542,7 +616,7 @@ ich_intr(void *p)
|
||||
}
|
||||
/* clear status bit */
|
||||
ich_wr(sc, ch->regbase +
|
||||
(sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),
|
||||
((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR),
|
||||
st, 2);
|
||||
}
|
||||
ICH_UNLOCK(sc);
|
||||
@ -564,13 +638,26 @@ ich_initsys(struct sc_info* sc)
|
||||
/* XXX: this should move to a device specific sysctl "dev.pcm.X.yyy"
|
||||
via device_get_sysctl_*() as discussed on multimedia@ in msg-id
|
||||
<861wujij2q.fsf@xps.des.no> */
|
||||
SYSCTL_ADD_INT(snd_sysctl_tree(sc->dev),
|
||||
SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)),
|
||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
|
||||
OID_AUTO, "ac97rate", CTLFLAG_RW,
|
||||
&sc->ac97rate, 48000,
|
||||
"AC97 link rate (default = 48000)");
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
ich_setstatus(struct sc_info *sc)
|
||||
{
|
||||
char status[SND_STATUSLEN];
|
||||
|
||||
snprintf(status, SND_STATUSLEN,
|
||||
"at io 0x%lx, 0x%lx irq %ld bufsz %u %s",
|
||||
rman_get_start(sc->nambar), rman_get_start(sc->nabmbar),
|
||||
rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich));
|
||||
|
||||
pcm_setstatus(sc->dev, status);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@ -578,16 +665,17 @@ ich_initsys(struct sc_info* sc)
|
||||
* function of the ac97 codec initialization code (to be investigated).
|
||||
*/
|
||||
|
||||
static
|
||||
void ich_calibrate(void *arg)
|
||||
static void
|
||||
ich_calibrate(void *arg)
|
||||
{
|
||||
struct sc_info *sc;
|
||||
struct sc_chinfo *ch;
|
||||
struct timeval t1, t2;
|
||||
u_int8_t ociv, nciv;
|
||||
u_int32_t wait_us, actual_48k_rate, bytes;
|
||||
uint8_t ociv, nciv;
|
||||
uint32_t wait_us, actual_48k_rate, oblkcnt;
|
||||
|
||||
sc = (struct sc_info *)arg;
|
||||
ICH_LOCK(sc);
|
||||
ch = &sc->ch[1];
|
||||
|
||||
if (sc->use_intrhook)
|
||||
@ -602,8 +690,13 @@ void ich_calibrate(void *arg)
|
||||
|
||||
KASSERT(ch->regbase == ICH_REG_PI_BASE, ("wrong direction"));
|
||||
|
||||
bytes = sndbuf_getsize(ch->buffer) / 2;
|
||||
ichchan_setblocksize(0, ch, bytes);
|
||||
oblkcnt = ch->blkcnt;
|
||||
ch->blkcnt = 2;
|
||||
sc->calibrated = 1;
|
||||
ICH_UNLOCK(sc);
|
||||
ichchan_setblocksize(0, ch, sndbuf_getmaxsize(ch->buffer) >> 1);
|
||||
ICH_LOCK(sc);
|
||||
sc->calibrated = 0;
|
||||
|
||||
/*
|
||||
* our data format is stereo, 16 bit so each sample is 4 bytes.
|
||||
@ -620,20 +713,19 @@ void ich_calibrate(void *arg)
|
||||
/* prepare */
|
||||
ociv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1);
|
||||
nciv = ociv;
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4);
|
||||
|
||||
/* start */
|
||||
microtime(&t1);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM, 1);
|
||||
|
||||
/* wait */
|
||||
while (nciv == ociv) {
|
||||
do {
|
||||
microtime(&t2);
|
||||
if (t2.tv_sec - t1.tv_sec > 1)
|
||||
break;
|
||||
nciv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1);
|
||||
}
|
||||
microtime(&t2);
|
||||
} while (nciv == ociv);
|
||||
|
||||
/* stop */
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_CR, 0, 1);
|
||||
@ -641,16 +733,20 @@ void ich_calibrate(void *arg)
|
||||
/* reset */
|
||||
DELAY(100);
|
||||
ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1);
|
||||
ch->blkcnt = oblkcnt;
|
||||
|
||||
/* turn time delta into us */
|
||||
wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec;
|
||||
|
||||
if (nciv == ociv) {
|
||||
device_printf(sc->dev, "ac97 link rate calibration timed out after %d us\n", wait_us);
|
||||
sc->calibrated = 1;
|
||||
ICH_UNLOCK(sc);
|
||||
ich_setstatus(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
actual_48k_rate = (bytes * 250000) / wait_us;
|
||||
actual_48k_rate = ((uint64_t)ch->blksz * 250000) / wait_us;
|
||||
|
||||
if (actual_48k_rate < 47500 || actual_48k_rate > 48500) {
|
||||
sc->ac97rate = actual_48k_rate;
|
||||
@ -664,6 +760,10 @@ void ich_calibrate(void *arg)
|
||||
printf(", will use %d Hz", sc->ac97rate);
|
||||
printf("\n");
|
||||
}
|
||||
sc->calibrated = 1;
|
||||
ICH_UNLOCK(sc);
|
||||
|
||||
ich_setstatus(sc);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -682,7 +782,7 @@ ich_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
||||
static int
|
||||
ich_init(struct sc_info *sc)
|
||||
{
|
||||
u_int32_t stat;
|
||||
uint32_t stat;
|
||||
|
||||
ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4);
|
||||
DELAY(600000);
|
||||
@ -706,11 +806,11 @@ ich_init(struct sc_info *sc)
|
||||
#endif
|
||||
|
||||
if (ich_resetchan(sc, 0) || ich_resetchan(sc, 1))
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
if (sc->hasmic && ich_resetchan(sc, 2))
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -738,14 +838,14 @@ static int
|
||||
ich_pci_attach(device_t dev)
|
||||
{
|
||||
uint32_t subdev;
|
||||
u_int16_t extcaps;
|
||||
uint16_t extcaps;
|
||||
uint16_t devid, vendor;
|
||||
struct sc_info *sc;
|
||||
char status[SND_STATUSLEN];
|
||||
int i;
|
||||
|
||||
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
|
||||
device_printf(dev, "cannot allocate softc\n");
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
|
||||
@ -807,7 +907,30 @@ ich_pci_attach(device_t dev)
|
||||
sc->nabmbart = rman_get_bustag(sc->nabmbar);
|
||||
sc->nabmbarh = rman_get_bushandle(sc->nabmbar);
|
||||
|
||||
sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ);
|
||||
sc->bufsz = pcm_getbuffersize(dev,
|
||||
ICH_MIN_BUFSZ, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ);
|
||||
|
||||
if (resource_int_value(device_get_name(sc->dev),
|
||||
device_get_unit(sc->dev), "blocksize", &i) == 0 && i > 0) {
|
||||
sc->blkcnt = sc->bufsz / i;
|
||||
i = 0;
|
||||
while (sc->blkcnt >> i)
|
||||
i++;
|
||||
sc->blkcnt = 1 << (i - 1);
|
||||
if (sc->blkcnt < ICH_MIN_BLKCNT)
|
||||
sc->blkcnt = ICH_MIN_BLKCNT;
|
||||
else if (sc->blkcnt > ICH_MAX_BLKCNT)
|
||||
sc->blkcnt = ICH_MAX_BLKCNT;
|
||||
} else
|
||||
sc->blkcnt = ICH_DEFAULT_BLKCNT;
|
||||
|
||||
if (resource_int_value(device_get_name(sc->dev),
|
||||
device_get_unit(sc->dev), "fixedrate", &i) == 0 &&
|
||||
i != 0)
|
||||
sc->fixedrate = 1;
|
||||
else
|
||||
sc->fixedrate = 0;
|
||||
|
||||
if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
|
||||
NULL, NULL, sc->bufsz, 1, 0x3ffff, 0,
|
||||
NULL, NULL, &sc->dmat) != 0) {
|
||||
@ -854,6 +977,8 @@ ich_pci_attach(device_t dev)
|
||||
case 0x81c0104d: /* Sony VAIO type T */
|
||||
case 0x81c5104d: /* Sony VAIO VGN B1VP/B1XP */
|
||||
case 0x3089103c: /* Compaq Presario B3800 */
|
||||
case 0x82131033: /* NEC VersaPro VJ10F/BH */
|
||||
case 0x82be1033: /* NEC VersaPro VJ12F/CH */
|
||||
ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
|
||||
break;
|
||||
default:
|
||||
@ -869,7 +994,7 @@ ich_pci_attach(device_t dev)
|
||||
sc->hasmic = ac97_getcaps(sc->codec) & AC97_CAP_MICCHANNEL;
|
||||
ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm);
|
||||
|
||||
if (pcm_register(dev, sc, 1, sc->hasmic? 2 : 1))
|
||||
if (pcm_register(dev, sc, 1, (sc->hasmic) ? 2 : 1))
|
||||
goto bad;
|
||||
|
||||
pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); /* play */
|
||||
@ -877,23 +1002,23 @@ ich_pci_attach(device_t dev)
|
||||
if (sc->hasmic)
|
||||
pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record mic */
|
||||
|
||||
snprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld bufsz %u %s",
|
||||
rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich));
|
||||
if (sc->fixedrate == 0) {
|
||||
ich_initsys(sc);
|
||||
|
||||
pcm_setstatus(dev, status);
|
||||
|
||||
ich_initsys(sc);
|
||||
|
||||
sc->intrhook.ich_func = ich_calibrate;
|
||||
sc->intrhook.ich_arg = sc;
|
||||
sc->use_intrhook = 1;
|
||||
if (config_intrhook_establish(&sc->intrhook) != 0) {
|
||||
device_printf(dev, "Cannot establish calibration hook, will calibrate now\n");
|
||||
sc->use_intrhook = 0;
|
||||
ich_calibrate(sc);
|
||||
sc->intrhook.ich_func = ich_calibrate;
|
||||
sc->intrhook.ich_arg = sc;
|
||||
sc->use_intrhook = 1;
|
||||
if (config_intrhook_establish(&sc->intrhook) != 0) {
|
||||
device_printf(dev, "Cannot establish calibration hook, will calibrate now\n");
|
||||
sc->use_intrhook = 0;
|
||||
ich_calibrate(sc);
|
||||
}
|
||||
} else {
|
||||
sc->calibrated = 1;
|
||||
ich_setstatus(sc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
|
||||
bad:
|
||||
if (sc->codec)
|
||||
@ -915,7 +1040,7 @@ 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
@ -28,6 +28,8 @@
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pcm/ac97_patch.h>
|
||||
|
||||
#include <dev/pci/pcivar.h>
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
@ -791,7 +793,7 @@ struct ac97_info *
|
||||
ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
|
||||
{
|
||||
struct ac97_info *codec;
|
||||
int eapd_inv;
|
||||
int eapdinv;
|
||||
|
||||
codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO);
|
||||
if (codec == NULL)
|
||||
@ -811,8 +813,8 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
|
||||
codec->devinfo = devinfo;
|
||||
codec->flags = 0;
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"ac97_eapd_inv", &eapd_inv) == 0) {
|
||||
if (eapd_inv != 0)
|
||||
"eapdinv", &eapdinv) == 0) {
|
||||
if (eapdinv != 0)
|
||||
codec->flags |= AC97_F_EAPD_INV;
|
||||
}
|
||||
return codec;
|
||||
@ -842,11 +844,66 @@ ac97_getflags(struct ac97_info *codec)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct ac97_info *codec;
|
||||
int ea, inv, err = 0;
|
||||
u_int16_t val;
|
||||
|
||||
codec = oidp->oid_arg1;
|
||||
if (codec == NULL || codec->id == 0 || codec->lock == NULL)
|
||||
return EINVAL;
|
||||
snd_mtxlock(codec->lock);
|
||||
val = ac97_rdcd(codec, AC97_REG_POWER);
|
||||
inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
|
||||
ea = (val >> 15) ^ inv;
|
||||
snd_mtxunlock(codec->lock);
|
||||
err = sysctl_handle_int(oidp, &ea, sizeof(ea), req);
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (ea != 0 && ea != 1)
|
||||
return EINVAL;
|
||||
if (ea != ((val >> 15) ^ inv)) {
|
||||
snd_mtxlock(codec->lock);
|
||||
ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
|
||||
snd_mtxunlock(codec->lock);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ac97_init_sysctl(struct ac97_info *codec)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
u_int16_t orig, val;
|
||||
|
||||
if (codec == NULL || codec->dev == NULL)
|
||||
return;
|
||||
snd_mtxlock(codec->lock);
|
||||
orig = ac97_rdcd(codec, AC97_REG_POWER);
|
||||
ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
|
||||
val = ac97_rdcd(codec, AC97_REG_POWER);
|
||||
ac97_wrcd(codec, AC97_REG_POWER, orig);
|
||||
snd_mtxunlock(codec->lock);
|
||||
if ((val & 0x8000) == (orig & 0x8000))
|
||||
return;
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
|
||||
OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
|
||||
codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
|
||||
"I", "AC97 External Amplifier");
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
ac97mix_init(struct snd_mixer *m)
|
||||
{
|
||||
struct ac97_info *codec = mix_getdevinfo(m);
|
||||
struct snddev_info *d;
|
||||
u_int32_t subvendor;
|
||||
u_int32_t i, mask;
|
||||
|
||||
if (codec == NULL)
|
||||
@ -857,20 +914,30 @@ ac97mix_init(struct snd_mixer *m)
|
||||
|
||||
switch (codec->id) {
|
||||
case 0x41445374: /* AD1981B */
|
||||
#if 0
|
||||
mask = 0;
|
||||
if (codec->mix[SOUND_MIXER_OGAIN].enable)
|
||||
mask |= SOUND_MASK_OGAIN;
|
||||
if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
|
||||
mask |= SOUND_MASK_PHONEOUT;
|
||||
/* Tie ogain/phone to master volume */
|
||||
if (codec->mix[SOUND_MIXER_VOLUME].enable)
|
||||
mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
|
||||
else {
|
||||
mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
|
||||
mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
|
||||
subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
|
||||
subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
|
||||
0x0000ffff;
|
||||
/* IBM Thinkcentre */
|
||||
if (subvendor == 0x02d91014) {
|
||||
/* Enable headphone jack sensing */
|
||||
ac97_wrcd(codec, 0x72, ac97_rdcd(codec, 0x72) |
|
||||
0x0800);
|
||||
mask = 0;
|
||||
if (codec->mix[SOUND_MIXER_OGAIN].enable)
|
||||
mask |= SOUND_MASK_OGAIN;
|
||||
if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
|
||||
mask |= SOUND_MASK_PHONEOUT;
|
||||
/* Tie ogain/phone to master volume */
|
||||
if (codec->mix[SOUND_MIXER_VOLUME].enable)
|
||||
mix_setparentchild(m, SOUND_MIXER_VOLUME,
|
||||
mask);
|
||||
else {
|
||||
mix_setparentchild(m, SOUND_MIXER_VOLUME,
|
||||
mask);
|
||||
mix_setrealdev(m, SOUND_MIXER_VOLUME,
|
||||
SOUND_MIXER_NONE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 0x434d4941: /* CMI9738 */
|
||||
case 0x434d4961: /* CMI9739 */
|
||||
@ -906,6 +973,9 @@ ac97mix_init(struct snd_mixer *m)
|
||||
for (i = 0; i < 32; i++)
|
||||
mask |= codec->mix[i].recidx? 1 << i : 0;
|
||||
mix_setrecdevs(m, mask);
|
||||
|
||||
ac97_init_sysctl(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -975,5 +1045,3 @@ ac97_getmixerclass(void)
|
||||
{
|
||||
return &ac97mixer_class;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -254,10 +254,7 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
|
||||
if (length > b->bufsize)
|
||||
length = b->bufsize;
|
||||
|
||||
if (b->fmt & AFMT_SIGNED)
|
||||
data = 0x00;
|
||||
else
|
||||
data = 0x80;
|
||||
data = sndbuf_zerodata(b->fmt);
|
||||
|
||||
i = sndbuf_getfreeptr(b);
|
||||
p = sndbuf_getbuf(b);
|
||||
@ -278,18 +275,8 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
|
||||
void
|
||||
sndbuf_fillsilence(struct snd_dbuf *b)
|
||||
{
|
||||
int i;
|
||||
u_char data, *p;
|
||||
|
||||
if (b->fmt & AFMT_SIGNED)
|
||||
data = 0x00;
|
||||
else
|
||||
data = 0x80;
|
||||
|
||||
i = 0;
|
||||
p = sndbuf_getbuf(b);
|
||||
while (i < b->bufsize)
|
||||
p[i++] = data;
|
||||
if (b->bufsize > 0)
|
||||
memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
|
||||
b->rp = 0;
|
||||
b->rl = b->bufsize;
|
||||
}
|
||||
@ -543,6 +530,52 @@ sndbuf_updateprevtotal(struct snd_dbuf *b)
|
||||
b->prev_total = b->total;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
snd_xbytes(unsigned int v, unsigned int from, unsigned int to)
|
||||
{
|
||||
unsigned int w, x, y;
|
||||
|
||||
if (from == to)
|
||||
return v;
|
||||
|
||||
if (from == 0 || to == 0 || v == 0)
|
||||
return 0;
|
||||
|
||||
x = from;
|
||||
y = to;
|
||||
while (y != 0) {
|
||||
w = x % y;
|
||||
x = y;
|
||||
y = w;
|
||||
}
|
||||
from /= x;
|
||||
to /= x;
|
||||
|
||||
return (unsigned int)(((u_int64_t)v * to) / from);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
|
||||
{
|
||||
if (from == NULL || to == NULL || v == 0)
|
||||
return 0;
|
||||
|
||||
return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from),
|
||||
sndbuf_getbps(to) * sndbuf_getspd(to));
|
||||
}
|
||||
|
||||
u_int8_t
|
||||
sndbuf_zerodata(u_int32_t fmt)
|
||||
{
|
||||
if (fmt & AFMT_SIGNED)
|
||||
return (0x00);
|
||||
else if (fmt & AFMT_MU_LAW)
|
||||
return (0x7f);
|
||||
else if (fmt & AFMT_A_LAW)
|
||||
return (0x55);
|
||||
return (0x80);
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
|
||||
/**
|
||||
@ -624,15 +657,20 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
|
||||
int
|
||||
sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
KASSERT(count > 0, ("can't feed 0 bytes"));
|
||||
|
||||
if (sndbuf_getfree(to) < count)
|
||||
return EINVAL;
|
||||
|
||||
count = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
|
||||
if (count)
|
||||
sndbuf_acquire(to, to->tmpbuf, count);
|
||||
/* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
|
||||
do {
|
||||
cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
|
||||
if (cnt)
|
||||
sndbuf_acquire(to, to->tmpbuf, cnt);
|
||||
/* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
|
||||
count -= cnt;
|
||||
} while (count && cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -682,12 +720,8 @@ sndbuf_clearshadow(struct snd_dbuf *b)
|
||||
KASSERT(b != NULL, ("b is a null pointer"));
|
||||
KASSERT(b->sl >= 0, ("illegal shadow length"));
|
||||
|
||||
if ((b->shadbuf != NULL) && (b->sl > 0)) {
|
||||
if (b->fmt & AFMT_SIGNED)
|
||||
memset(b->shadbuf, 0x00, b->sl);
|
||||
else
|
||||
memset(b->shadbuf, 0x80, b->sl);
|
||||
}
|
||||
if ((b->shadbuf != NULL) && (b->sl > 0))
|
||||
memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl);
|
||||
}
|
||||
|
||||
#ifdef OSSV4_EXPERIMENT
|
||||
|
@ -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
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,98 +311,113 @@ 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;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
if (fmt & AFMT_A_LAW)
|
||||
return 10;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* 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)
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
|
||||
chn_fmtscore(u_int32_t fmt)
|
||||
{
|
||||
u_int32_t best;
|
||||
int i, score, score2, oldscore;
|
||||
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)
|
||||
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 (oldscore == 0 || (score2 == score) ||
|
||||
(score2 > oldscore && score2 < score) ||
|
||||
(score2 < oldscore && score2 > score) ||
|
||||
(oldscore < score && score2 > oldscore)) {
|
||||
best = fmts[i];
|
||||
oldscore = score2;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
@ -359,22 +426,37 @@ chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
|
||||
return best;
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
return chn_fmtbestfunc(fmt, fmts, 0);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
return chn_fmtbestfunc(fmt, fmts, 1);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
u_int32_t best1, best2;
|
||||
int score, score1, score2;
|
||||
u_int32_t score, score1, score2;
|
||||
|
||||
if (fmtvalid(fmt, fmts))
|
||||
return fmt;
|
||||
|
||||
best1 = chn_fmtbeststereo(fmt, fmts);
|
||||
best2 = chn_fmtbestbit(fmt, fmts);
|
||||
|
||||
if (best1 != 0 && best2 != 0) {
|
||||
if (best1 != 0 && best2 != 0 && best1 != best2) {
|
||||
if (fmt & AFMT_STEREO)
|
||||
return best1;
|
||||
else {
|
||||
score = chn_fmtscore(fmt);
|
||||
score1 = chn_fmtscore(best1);
|
||||
score2 = chn_fmtscore(best2);
|
||||
score = score_val(chn_fmtscore(fmt));
|
||||
score1 = score_val(chn_fmtscore(best1));
|
||||
score2 = score_val(chn_fmtscore(best2));
|
||||
if (score1 == score2 || score1 == score)
|
||||
return best1;
|
||||
else if (score2 == score)
|
||||
@ -389,6 +471,184 @@ chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
|
||||
return best2;
|
||||
}
|
||||
|
||||
static struct pcm_feeder *
|
||||
feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
|
||||
{
|
||||
struct feedertab_entry *fte, *ftebest;
|
||||
struct pcm_feeder *try, *ret;
|
||||
uint32_t fl, qout, qsrc, qdst;
|
||||
int qtype;
|
||||
|
||||
if (to == NULL || to[0] == 0)
|
||||
return NULL;
|
||||
|
||||
DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out));
|
||||
if (fmtvalid(source->desc->out, to)) {
|
||||
DEB(printf("got it\n"));
|
||||
return source;
|
||||
}
|
||||
|
||||
if (maxdepth < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* WARNING: THIS IS _NOT_ FOR THE FAINT HEART
|
||||
* Disclaimer: I don't expect anybody could understand this
|
||||
* without deep logical and mathematical analysis
|
||||
* involving various unnamed probability theorem.
|
||||
*
|
||||
* This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm
|
||||
* is **extremely** difficult to digest especially when applied to
|
||||
* large sets / numbers of random chains (feeders), each with
|
||||
* unique characteristic providing different sets of in/out format.
|
||||
*
|
||||
* Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic:
|
||||
* 1) Format chains
|
||||
* 1.1 "8bit to any, not to 8bit"
|
||||
* 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be]
|
||||
* 1.1.2 sign can be changed, e.g: u8 -> s16[le|be]
|
||||
* 1.1.3 endian can be changed, e.g: u8 -> u16[le|be]
|
||||
* 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be]
|
||||
* 1.2 "Any to 8bit, not from 8bit"
|
||||
* 1.2.1 sign can remain consistent, e.g: s16le -> s8
|
||||
* 1.2.2 sign can be changed, e.g: s16le -> u8
|
||||
* 1.2.3 source endian can be anything e.g: s16[le|be] -> s8
|
||||
* 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8
|
||||
* 1.3 "Any to any where BOTH input and output either 8bit or non-8bit"
|
||||
* 1.3.1 endian MUST remain consistent
|
||||
* 1.3.2 sign CAN be changed
|
||||
* 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding
|
||||
* 16bit to 24bit .
|
||||
* 2) Channel chains (mono <-> stereo)
|
||||
* 2.1 Both endian and sign MUST remain consistent
|
||||
* 3) Endian chains (big endian <-> little endian)
|
||||
* 3.1 Channels and sign MUST remain consistent
|
||||
* 4) Sign chains (signed <-> unsigned)
|
||||
* 4.1 Channels and endian MUST remain consistent
|
||||
*
|
||||
* .. and the mother of all chaining rules:
|
||||
*
|
||||
* Rules 0: Source and destination MUST not contain multiple selections.
|
||||
* (qtype != FMT_Q_MULTI)
|
||||
*
|
||||
* First of all, our caller ( chn_fmtchain() ) will reduce the possible
|
||||
* multiple from/to formats to a single best format using chn_fmtbest().
|
||||
* Then, using chn_fmtscore(), we determine the chaining characteristic.
|
||||
* Our main goal is to narrow it down until it reach FMT_Q_EQ chaining
|
||||
* type while still adhering above chaining rules.
|
||||
*
|
||||
* The need for this complicated chaining procedures is inevitable,
|
||||
* since currently we have more than 200 different types of FEEDER_FMT
|
||||
* doing various unique format conversion. Without this (the old way),
|
||||
* it is possible to generate broken chain since it doesn't do any
|
||||
* sanity checking to ensure that the output format is "properly aligned"
|
||||
* with the direction of conversion (quality up/down/equal).
|
||||
*
|
||||
* Conversion: s24le to s32le
|
||||
* Possible chain: 1) s24le -> s32le (correct, optimized)
|
||||
* 2) s24le -> s16le -> s32le
|
||||
* (since we have feeder_24to16 and feeder_16to32)
|
||||
* +-- obviously broken!
|
||||
*
|
||||
* Using scoring mechanisme, this will ensure that the chaining
|
||||
* process do the right thing, or at least, give the best chain
|
||||
* possible without causing quality (the 'Q') degradation.
|
||||
*/
|
||||
|
||||
qdst = chn_fmtscore(to[0]);
|
||||
qsrc = chn_fmtscore(source->desc->out);
|
||||
|
||||
#define score_q(s1) score_val(s1)
|
||||
#define score_8bit(s1) ((s1) & 0x700)
|
||||
#define score_non8bit(s1) (!score_8bit(s1))
|
||||
#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \
|
||||
(score_8bit(s2) && score_non8bit(s1)))
|
||||
|
||||
#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2))
|
||||
#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2))
|
||||
#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2))
|
||||
#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \
|
||||
0x2 : 0x0))
|
||||
#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2)
|
||||
#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \
|
||||
((score_cheq(s1, s2) && \
|
||||
score_endianeq(s1, s2)) ? \
|
||||
0x1 : 0x0) | \
|
||||
((score_cheq(s1, s2) && \
|
||||
score_signeq(s1, s2)) ? \
|
||||
0x2 : 0x0))
|
||||
|
||||
/* Determine chaining direction and set matching flag */
|
||||
fl = 0x3fff;
|
||||
if (to[1] != 0) {
|
||||
qtype = FMT_Q_MULTI;
|
||||
printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__);
|
||||
} else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) {
|
||||
qtype = FMT_Q_DOWN;
|
||||
fl = FMT_Q_DOWN_FLAGS(qsrc, qdst);
|
||||
} else if (FMT_CHAIN_Q_UP(qsrc, qdst)) {
|
||||
qtype = FMT_Q_UP;
|
||||
fl = FMT_Q_UP_FLAGS(qsrc, qdst);
|
||||
} else {
|
||||
qtype = FMT_Q_EQ;
|
||||
fl = FMT_Q_EQ_FLAGS(qsrc, qdst);
|
||||
}
|
||||
|
||||
ftebest = NULL;
|
||||
|
||||
SLIST_FOREACH(fte, &feedertab, link) {
|
||||
if (fte->desc == NULL)
|
||||
continue;
|
||||
if (fte->desc->type != FEEDER_FMT)
|
||||
continue;
|
||||
qout = chn_fmtscore(fte->desc->out);
|
||||
#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI)
|
||||
#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl)))
|
||||
#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \
|
||||
score_q(s3) >= score_q(s1) && \
|
||||
score_q(s3) <= score_q(s2))
|
||||
#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \
|
||||
score_q(s3) <= score_q(s1) && \
|
||||
score_q(s3) >= score_q(s2))
|
||||
#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \
|
||||
score_q(s1) == score_q(s2))
|
||||
if (fte->desc->in == source->desc->out &&
|
||||
(FMT_Q_MULTI_VALIDATE(qtype) ||
|
||||
(FMT_Q_FL_MATCH(fl, qout, qdst) &&
|
||||
(FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) ||
|
||||
FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) ||
|
||||
FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) {
|
||||
try = feeder_create(fte->feederclass, fte->desc);
|
||||
if (try) {
|
||||
try->source = source;
|
||||
ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
|
||||
if (ret != NULL)
|
||||
return ret;
|
||||
feeder_destroy(try);
|
||||
}
|
||||
} else if (fte->desc->in == source->desc->out) {
|
||||
/* XXX quality must be considered! */
|
||||
if (ftebest == NULL)
|
||||
ftebest = fte;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftebest != NULL) {
|
||||
try = feeder_create(ftebest->feederclass, ftebest->desc);
|
||||
if (try) {
|
||||
try->source = source;
|
||||
ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
|
||||
if (ret != NULL)
|
||||
return ret;
|
||||
feeder_destroy(try);
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("giving up %s...\n", source->class->name); */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
|
||||
{
|
||||
@ -401,13 +661,15 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
|
||||
KASSERT(to != NULL, ("to == NULL"));
|
||||
KASSERT(to[0] != 0, ("to[0] == 0"));
|
||||
|
||||
if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0)
|
||||
return 0;
|
||||
|
||||
stop = c->feeder;
|
||||
best = 0;
|
||||
|
||||
if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
|
||||
from = chn_getcaps(c)->fmtlist;
|
||||
if (fmtvalid(to[0], from))
|
||||
from = to;
|
||||
else {
|
||||
if (from[1] != 0) {
|
||||
best = chn_fmtbest(to[0], from);
|
||||
if (best != 0) {
|
||||
tmpfrom[0] = best;
|
||||
@ -420,49 +682,60 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
|
||||
tmpfrom[1] = 0;
|
||||
from = tmpfrom;
|
||||
if (to[1] != 0) {
|
||||
if (fmtvalid(tmpfrom[0], to)) {
|
||||
tmpto[0] = tmpfrom[0];
|
||||
best = chn_fmtbest(from[0], to);
|
||||
if (best != 0) {
|
||||
tmpto[0] = best;
|
||||
tmpto[1] = 0;
|
||||
to = tmpto;
|
||||
} else {
|
||||
best = chn_fmtbest(tmpfrom[0], to);
|
||||
if (best != 0) {
|
||||
tmpto[0] = best;
|
||||
tmpto[1] = 0;
|
||||
to = tmpto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
best = 0;
|
||||
bestmax = 100;
|
||||
while (from[i] != 0) {
|
||||
c->feeder->desc->out = from[i];
|
||||
try = NULL;
|
||||
max = 0;
|
||||
while (try == NULL && max < 8) {
|
||||
try = feeder_fmtchain(to, c->feeder, stop, max);
|
||||
if (try == NULL)
|
||||
max++;
|
||||
}
|
||||
if (try != NULL && max < bestmax) {
|
||||
bestmax = max;
|
||||
best = from[i];
|
||||
}
|
||||
while (try != NULL && try != stop) {
|
||||
del = try;
|
||||
try = try->source;
|
||||
feeder_destroy(del);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (best == 0)
|
||||
return 0;
|
||||
#define FEEDER_FMTCHAIN_MAXDEPTH 8
|
||||
|
||||
c->feeder->desc->out = best;
|
||||
try = feeder_fmtchain(to, c->feeder, stop, bestmax);
|
||||
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;
|
||||
while (from[i] != 0) {
|
||||
c->feeder->desc->out = from[i];
|
||||
try = NULL;
|
||||
max = 0;
|
||||
do {
|
||||
try = feeder_fmtchain(to, c->feeder, stop, max);
|
||||
} while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
|
||||
if (try != NULL && max < bestmax) {
|
||||
bestmax = max;
|
||||
best = from[i];
|
||||
}
|
||||
while (try != NULL && try != stop) {
|
||||
del = try;
|
||||
try = try->source;
|
||||
feeder_destroy(del);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (best == 0)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -53,7 +53,7 @@ struct pcm_feeder {
|
||||
void feeder_register(void *p);
|
||||
struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc);
|
||||
|
||||
int chn_fmtscore(u_int32_t fmt);
|
||||
u_int32_t chn_fmtscore(u_int32_t fmt);
|
||||
u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts);
|
||||
@ -72,11 +72,11 @@ static struct feeder_class feeder ## _class = { \
|
||||
.desc = feeder ## _desc, \
|
||||
.data = pdata, \
|
||||
}; \
|
||||
SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class);
|
||||
SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class);
|
||||
|
||||
#define FEEDER_ROOT 1
|
||||
#define FEEDER_FMT 2
|
||||
#define FEEDER_MIXER 3
|
||||
#define FEEDER_MIXER 3
|
||||
#define FEEDER_RATE 4
|
||||
#define FEEDER_FILTER 5
|
||||
#define FEEDER_VOLUME 6
|
||||
@ -85,4 +85,27 @@ SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _cl
|
||||
#define FEEDRATE_SRC 1
|
||||
#define FEEDRATE_DST 2
|
||||
|
||||
#define FEEDRATE_RATEMIN 1
|
||||
#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */
|
||||
|
||||
#define FEEDRATE_MIN 1
|
||||
#define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */
|
||||
|
||||
#define FEEDRATE_ROUNDHZ 25
|
||||
#define FEEDRATE_ROUNDHZ_MIN 0
|
||||
#define FEEDRATE_ROUNDHZ_MAX 500
|
||||
|
||||
/*
|
||||
* Default buffer size for feeder processing.
|
||||
*
|
||||
* Big = less sndbuf_feed(), more memory usage.
|
||||
* Small = aggresive sndbuf_feed() (perhaps too much), less memory usage.
|
||||
*/
|
||||
#define FEEDBUFSZ 16384
|
||||
#define FEEDBUFSZ_MIN 2048
|
||||
#define FEEDBUFSZ_MAX 131072
|
||||
|
||||
extern int feeder_rate_min;
|
||||
extern int feeder_rate_max;
|
||||
extern int feeder_rate_round;
|
||||
extern int feeder_buffersize;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
|
||||
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;
|
||||
#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; \
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_volume_s16_desc[] = {
|
||||
{FEEDER_VOLUME, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
|
||||
FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be)
|
||||
/* unsigned */
|
||||
FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be)
|
||||
|
||||
static int
|
||||
feed_volume_setup(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_volume_info *info = f->data;
|
||||
static const struct {
|
||||
uint32_t format; /* pcm / audio format */
|
||||
uint32_t bps; /* bytes-per-sample, regardless of
|
||||
total channels */
|
||||
feed_volume_filter filter;
|
||||
} voltbl[] = {
|
||||
{ AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne },
|
||||
{ AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le },
|
||||
{ AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le },
|
||||
{ AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le },
|
||||
{ AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be },
|
||||
{ AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be },
|
||||
{ AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be },
|
||||
/* unsigned */
|
||||
{ AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne },
|
||||
{ AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le },
|
||||
{ AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le },
|
||||
{ AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le },
|
||||
{ AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be },
|
||||
{ AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be },
|
||||
{ AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be },
|
||||
{ 0, 0, NULL },
|
||||
};
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < sizeof(voltbl) / sizeof(*voltbl); i++) {
|
||||
if (voltbl[i].format == 0)
|
||||
return -1;
|
||||
if ((f->desc->out & ~AFMT_STEREO) == voltbl[i].format) {
|
||||
info->bps = voltbl[i].bps;
|
||||
info->filter = voltbl[i].filter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* For now, this is mandatory! */
|
||||
info->channels = 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
feed_volume_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 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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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; \
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (x > 32767) {
|
||||
/* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
|
||||
x = 32767;
|
||||
}
|
||||
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);
|
||||
CHN_LOCK(ch);
|
||||
if (ch->flags & CHN_F_TRIGGERED) {
|
||||
if (ch->flags & CHN_F_MAPPED)
|
||||
sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
|
||||
cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
|
||||
vchan_mix_s16(dst, tmp, cnt >> 1);
|
||||
cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, ch->bufsoft);
|
||||
cnt -= cnt % sz;
|
||||
cnt = info->mix(info, b, tmp, cnt);
|
||||
if (cnt > rcnt)
|
||||
rcnt = cnt;
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
|
||||
return rcnt & ~1;
|
||||
if (++c->feedcount == 0)
|
||||
c->feedcount = 2;
|
||||
|
||||
return rcnt;
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
|
||||
static struct pcm_feederdesc feeder_vchan_desc[] = {
|
||||
{FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
|
||||
{FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
|
||||
{0},
|
||||
{FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
|
||||
/* unsigned */
|
||||
{FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
|
||||
{FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
|
||||
{FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
|
||||
{FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
static kobj_method_t feeder_vchan_s16_methods[] = {
|
||||
KOBJMETHOD(feeder_feed, feed_vchan_s16),
|
||||
{ 0, 0 }
|
||||
static kobj_method_t feeder_vchan_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_vchan_init),
|
||||
KOBJMETHOD(feeder_free, feed_vchan_free),
|
||||
KOBJMETHOD(feeder_feed, feed_vchan),
|
||||
{0, 0}
|
||||
};
|
||||
FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
|
||||
FEEDER_DECLARE(feeder_vchan, 2, NULL);
|
||||
|
||||
/************************************************************/
|
||||
|
||||
@ -165,7 +338,7 @@ vchan_free(kobj_t obj, void *data)
|
||||
}
|
||||
|
||||
static int
|
||||
vchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
vchan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
@ -180,15 +353,15 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
ch->bps *= 3;
|
||||
else if (ch->fmt & AFMT_32BIT)
|
||||
ch->bps <<= 2;
|
||||
CHN_UNLOCK(channel);
|
||||
CHN_UNLOCK(channel);
|
||||
chn_notify(parent, CHN_N_FORMAT);
|
||||
CHN_LOCK(channel);
|
||||
CHN_LOCK(channel);
|
||||
sndbuf_setfmt(channel->bufsoft, format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
@ -204,7 +377,7 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
}
|
||||
|
||||
static int
|
||||
vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *channel = ch->channel;
|
||||
@ -213,18 +386,27 @@ vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
int prate, crate;
|
||||
|
||||
ch->blksz = blocksize;
|
||||
/* CHN_UNLOCK(channel); */
|
||||
/* CHN_UNLOCK(channel); */
|
||||
sndbuf_setblksz(channel->bufhard, blocksize);
|
||||
chn_notify(parent, CHN_N_BLOCKSIZE);
|
||||
CHN_LOCK(parent);
|
||||
/* CHN_LOCK(channel); */
|
||||
CHN_LOCK(parent);
|
||||
/* CHN_LOCK(channel); */
|
||||
|
||||
crate = ch->spd * ch->bps;
|
||||
prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
|
||||
blocksize = sndbuf_getblksz(parent->bufsoft);
|
||||
CHN_UNLOCK(parent);
|
||||
CHN_UNLOCK(parent);
|
||||
blocksize *= prate;
|
||||
blocksize /= crate;
|
||||
blocksize += ch->bps;
|
||||
prate = 0;
|
||||
while (blocksize >> prate)
|
||||
prate++;
|
||||
blocksize = 1 << (prate - 1);
|
||||
blocksize -= blocksize % ch->bps;
|
||||
/* XXX screwed !@#$ */
|
||||
if (blocksize < ch->bps)
|
||||
blocksize = 4096 - (4096 % ch->bps);
|
||||
|
||||
return blocksize;
|
||||
}
|
||||
@ -240,9 +422,9 @@ vchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
|
||||
ch->run = (go == PCMTRIG_START)? 1 : 0;
|
||||
CHN_UNLOCK(channel);
|
||||
CHN_UNLOCK(channel);
|
||||
chn_notify(parent, CHN_N_TRIGGER);
|
||||
CHN_LOCK(channel);
|
||||
CHN_LOCK(channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -251,24 +433,34 @@ static struct pcmchan_caps *
|
||||
vchan_getcaps(kobj_t obj, void *data)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
uint32_t fmt;
|
||||
|
||||
ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
|
||||
ch->caps.maxspeed = ch->caps.minspeed;
|
||||
ch->caps.fmtlist = vchan_fmt;
|
||||
ch->caps.caps = 0;
|
||||
ch->fmts[1] = 0;
|
||||
fmt = sndbuf_getfmt(ch->parent->bufsoft);
|
||||
if (fmt != vchan_valid_format(fmt)) {
|
||||
device_printf(ch->parent->dev,
|
||||
"%s: WARNING: invalid vchan format! (0x%08x)\n",
|
||||
__func__, fmt);
|
||||
fmt = VCHAN_DEFAULT_AFMT;
|
||||
}
|
||||
ch->fmts[0] = fmt;
|
||||
ch->caps.fmtlist = ch->fmts;
|
||||
|
||||
return &ch->caps;
|
||||
}
|
||||
|
||||
static kobj_method_t vchan_methods[] = {
|
||||
KOBJMETHOD(channel_init, vchan_init),
|
||||
KOBJMETHOD(channel_free, vchan_free),
|
||||
KOBJMETHOD(channel_setformat, vchan_setformat),
|
||||
KOBJMETHOD(channel_setspeed, vchan_setspeed),
|
||||
KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
|
||||
KOBJMETHOD(channel_trigger, vchan_trigger),
|
||||
KOBJMETHOD(channel_getcaps, vchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD(channel_init, vchan_init),
|
||||
KOBJMETHOD(channel_free, vchan_free),
|
||||
KOBJMETHOD(channel_setformat, vchan_setformat),
|
||||
KOBJMETHOD(channel_setspeed, vchan_setspeed),
|
||||
KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
|
||||
KOBJMETHOD(channel_trigger, vchan_trigger),
|
||||
KOBJMETHOD(channel_getcaps, vchan_getcaps),
|
||||
{0, 0}
|
||||
};
|
||||
CHANNEL_DECLARE(vchan);
|
||||
|
||||
@ -280,7 +472,7 @@ static int
|
||||
sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_channel *sce;
|
||||
struct snddev_channel *sce;
|
||||
struct pcm_channel *c, *ch = NULL, *fake;
|
||||
struct pcmchan_caps *caps;
|
||||
int err = 0;
|
||||
@ -329,18 +521,20 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (newspd < 1 || newspd < feeder_rate_ratemin ||
|
||||
newspd > feeder_rate_ratemax) {
|
||||
if (newspd < 1 || newspd < feeder_rate_min ||
|
||||
newspd > feeder_rate_max) {
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
CHN_LOCK(ch);
|
||||
caps = chn_getcaps(ch);
|
||||
if (caps == NULL || newspd < caps->minspeed ||
|
||||
newspd > caps->maxspeed) {
|
||||
CHN_UNLOCK(ch);
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
if (feeder_rate_round) {
|
||||
caps = chn_getcaps(ch);
|
||||
if (caps == NULL || newspd < caps->minspeed ||
|
||||
newspd > caps->maxspeed) {
|
||||
CHN_UNLOCK(ch);
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (newspd != ch->speed) {
|
||||
err = chn_setspeed(ch, newspd);
|
||||
@ -348,7 +542,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||
* Try to avoid FEEDER_RATE on parent channel if the
|
||||
* requested value is not supported by the hardware.
|
||||
*/
|
||||
if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
|
||||
if (!err && feeder_rate_round &&
|
||||
(ch->feederflags & (1 << FEEDER_RATE))) {
|
||||
newspd = sndbuf_getspd(ch->bufhard);
|
||||
err = chn_setspeed(ch, newspd);
|
||||
}
|
||||
@ -367,6 +562,97 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||
pcm_inprog(d, -1);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_channel *sce;
|
||||
struct pcm_channel *c, *ch = NULL, *fake;
|
||||
uint32_t newfmt, spd;
|
||||
char fmtstr[AFMTSTR_MAXSZ];
|
||||
int err = 0, i;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
|
||||
return EINVAL;
|
||||
if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
return EINPROGRESS;
|
||||
}
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == PCMDIR_PLAY) {
|
||||
if (c->flags & CHN_F_VIRTUAL) {
|
||||
/* Sanity check */
|
||||
if (ch != NULL && ch != c->parentchannel) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
if (req->newptr != NULL &&
|
||||
(c->flags & CHN_F_BUSY)) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
return EBUSY;
|
||||
}
|
||||
} else if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
/* No way!! */
|
||||
if (ch != NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
ch = c;
|
||||
if (ch->format != afmt2afmtstr(vchan_supported_fmts,
|
||||
ch->format, fmtstr, sizeof(fmtstr),
|
||||
AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
|
||||
strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr));
|
||||
}
|
||||
}
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
if (ch == NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
|
||||
if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
|
||||
strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
newfmt = vchan_valid_strformat(fmtstr);
|
||||
if (newfmt == 0) {
|
||||
pcm_inprog(d, -1);
|
||||
return EINVAL;
|
||||
}
|
||||
CHN_LOCK(ch);
|
||||
if (newfmt != ch->format) {
|
||||
/* Get channel speed, before chn_reset() screw it. */
|
||||
spd = ch->speed;
|
||||
err = chn_reset(ch, newfmt);
|
||||
if (err == 0)
|
||||
err = chn_setspeed(ch, spd);
|
||||
CHN_UNLOCK(ch);
|
||||
if (err == 0) {
|
||||
fake = pcm_getfakechan(d);
|
||||
if (fake != NULL) {
|
||||
CHN_LOCK(fake);
|
||||
fake->format = newfmt;
|
||||
CHN_UNLOCK(fake);
|
||||
}
|
||||
}
|
||||
} else
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
pcm_inprog(d, -1);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* virtual channel interface */
|
||||
@ -374,11 +660,12 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||
int
|
||||
vchan_create(struct pcm_channel *parent)
|
||||
{
|
||||
struct snddev_info *d = parent->parentsnddev;
|
||||
struct snddev_info *d = parent->parentsnddev;
|
||||
struct pcmchan_children *pce;
|
||||
struct pcm_channel *child, *fake;
|
||||
struct pcmchan_caps *parent_caps;
|
||||
int err, first, speed = 0;
|
||||
uint32_t vchanfmt = 0;
|
||||
int err, first, speed = 0, r;
|
||||
|
||||
if (!(parent->flags & CHN_F_BUSY))
|
||||
return EBUSY;
|
||||
@ -388,7 +675,7 @@ vchan_create(struct pcm_channel *parent)
|
||||
|
||||
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (!pce) {
|
||||
CHN_LOCK(parent);
|
||||
CHN_LOCK(parent);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
@ -396,7 +683,7 @@ vchan_create(struct pcm_channel *parent)
|
||||
child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
|
||||
if (!child) {
|
||||
free(pce, M_DEVBUF);
|
||||
CHN_LOCK(parent);
|
||||
CHN_LOCK(parent);
|
||||
return ENODEV;
|
||||
}
|
||||
pce->channel = child;
|
||||
@ -413,7 +700,7 @@ vchan_create(struct pcm_channel *parent)
|
||||
return err;
|
||||
}
|
||||
|
||||
CHN_LOCK(parent);
|
||||
CHN_LOCK(parent);
|
||||
/* add us to our parent channel's children */
|
||||
first = SLIST_EMPTY(&parent->children);
|
||||
SLIST_INSERT_HEAD(&parent->children, pce, link);
|
||||
@ -424,23 +711,50 @@ vchan_create(struct pcm_channel *parent)
|
||||
if (parent_caps == NULL)
|
||||
err = EINVAL;
|
||||
|
||||
if (!err)
|
||||
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
|
||||
fake = pcm_getfakechan(d);
|
||||
|
||||
if (!err && fake != NULL) {
|
||||
/*
|
||||
* Avoid querying kernel hint, use saved value
|
||||
* from fake channel.
|
||||
*/
|
||||
CHN_UNLOCK(parent);
|
||||
CHN_LOCK(fake);
|
||||
speed = fake->speed;
|
||||
vchanfmt = fake->format;
|
||||
CHN_UNLOCK(fake);
|
||||
CHN_LOCK(parent);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
fake = pcm_getfakechan(d);
|
||||
if (fake != NULL) {
|
||||
/*
|
||||
* Avoid querying kernel hint, use saved value
|
||||
* from fake channel.
|
||||
*/
|
||||
CHN_UNLOCK(parent);
|
||||
CHN_LOCK(fake);
|
||||
speed = fake->speed;
|
||||
CHN_UNLOCK(fake);
|
||||
CHN_LOCK(parent);
|
||||
}
|
||||
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,38 +782,45 @@ vchan_create(struct pcm_channel *parent)
|
||||
break;
|
||||
default:
|
||||
speed = VCHAN_DEFAULT_SPEED;
|
||||
if (speed > parent_caps->maxspeed)
|
||||
speed = parent_caps->maxspeed;
|
||||
break;
|
||||
}
|
||||
if (speed < parent_caps->minspeed)
|
||||
speed = parent_caps->minspeed;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Limit speed based on driver caps.
|
||||
* This is supposed to help fixed rate, non-VRA
|
||||
* AC97 cards, but.. (see below)
|
||||
*/
|
||||
if (speed < parent_caps->minspeed)
|
||||
speed = parent_caps->minspeed;
|
||||
if (speed > parent_caps->maxspeed)
|
||||
speed = parent_caps->maxspeed;
|
||||
if (feeder_rate_round) {
|
||||
/*
|
||||
* Limit speed based on driver caps.
|
||||
* This is supposed to help fixed rate, non-VRA
|
||||
* AC97 cards, but.. (see below)
|
||||
*/
|
||||
if (speed < parent_caps->minspeed)
|
||||
speed = parent_caps->minspeed;
|
||||
if (speed > parent_caps->maxspeed)
|
||||
speed = parent_caps->maxspeed;
|
||||
}
|
||||
|
||||
/*
|
||||
* We still need to limit the speed between
|
||||
* feeder_rate_ratemin <-> feeder_rate_ratemax. This is
|
||||
* feeder_rate_min <-> feeder_rate_max. This is
|
||||
* just an escape goat if all of the above failed
|
||||
* miserably.
|
||||
*/
|
||||
if (speed < feeder_rate_ratemin)
|
||||
speed = feeder_rate_ratemin;
|
||||
if (speed > feeder_rate_ratemax)
|
||||
speed = feeder_rate_ratemax;
|
||||
if (speed < feeder_rate_min)
|
||||
speed = feeder_rate_min;
|
||||
if (speed > feeder_rate_max)
|
||||
speed = feeder_rate_max;
|
||||
|
||||
err = chn_setspeed(parent, speed);
|
||||
/*
|
||||
* Try to avoid FEEDER_RATE on parent channel if the
|
||||
* requested value is not supported by the hardware.
|
||||
*/
|
||||
if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
|
||||
if (!err && feeder_rate_round &&
|
||||
(parent->feederflags & (1 << FEEDER_RATE))) {
|
||||
speed = sndbuf_getspd(parent->bufhard);
|
||||
err = chn_setspeed(parent, speed);
|
||||
}
|
||||
@ -509,6 +832,7 @@ vchan_create(struct pcm_channel *parent)
|
||||
CHN_UNLOCK(parent);
|
||||
CHN_LOCK(fake);
|
||||
fake->speed = speed;
|
||||
fake->format = vchanfmt;
|
||||
CHN_UNLOCK(fake);
|
||||
CHN_LOCK(parent);
|
||||
}
|
||||
@ -533,7 +857,7 @@ int
|
||||
vchan_destroy(struct pcm_channel *c)
|
||||
{
|
||||
struct pcm_channel *parent = c->parentchannel;
|
||||
struct snddev_info *d = parent->parentsnddev;
|
||||
struct snddev_info *d = parent->parentsnddev;
|
||||
struct pcmchan_children *pce;
|
||||
struct snddev_channel *sce;
|
||||
uint32_t spd;
|
||||
@ -571,9 +895,9 @@ 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;
|
||||
@ -606,16 +930,19 @@ vchan_initsys(device_t dev)
|
||||
#ifdef SND_DYNSYSCTL
|
||||
struct snddev_info *d;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
/* XXX: the user should be able to set this with a control tool, the
|
||||
sysadmin needs a sysctl so set a max value for "vchan" and min+max
|
||||
values for "vchanrate" to limit what an user can do */
|
||||
SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
|
||||
OID_AUTO, "_vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_hw_snd_vchans, "I", "");
|
||||
SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
|
||||
OID_AUTO, "_vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_hw_snd_vchanrate, "I", "");
|
||||
d = device_get_softc(dev);
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_hw_snd_vchanformat, "A", "virtual channel format");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user