MFC nearly everything of the soundsystem except recent commits and

most of the sysctl's (those which would introduce a new interface).
The spdif_enabled sysctl is still there (or more correctly: added
to another driver).

A rough overview what's there now:
 - new driver for ATI chips (snd_atiixp)
 - support for some Intel HDA chips in AC97 mode (no real HDA
   support!), this doesn't work with every mainboard and is
   subject to the wiring on the mainboard (no servicable parts
   inside)
 - MPSAFE and fixes for snd_als4000(4), snd_es137x(4),
   snd_via82c686(4), snd_via8233(4) and snd_ich(4)
 - 24bit and 32bit sound format support
 - feeder infrastructure (format, rate) has been reworked, new
   feeder (volume) has been introduced
 - lots of LOR and panic issues fixed
 - and a lot of small or not so small fixes I may have forgotten...

As noted in UPDATING: you may want to recompile mplayer (after booting
into the new world) to get all new features.

Tested by:	a lot of people
Requested by:	ariff
Submitted by:	ariff
This commit is contained in:
netchild 2005-12-30 19:55:55 +00:00
parent 30ca7bc8d2
commit 6020e1d27b
48 changed files with 4564 additions and 1898 deletions

View File

@ -8,6 +8,11 @@ Items affecting the ports and packages system can be found in
/usr/ports/UPDATING. Please read that file before running
portupgrade.
20051230:
A lot of fixes and new features in the soundsystem. To get all
benefits, you may want to recompile mplayer (if installed) after
booting the new world.
20051222:
Bug fixes to the trimdomain(3) function in libutil may result in
slight changes to the host names appearing in log files under

View File

@ -278,6 +278,7 @@ MAN= aac.4 \
sn.4 \
snd_ad1816.4 \
snd_als4000.4 \
snd_atiixp.4 \
snd_cmi.4 \
snd_cs4281.4 \
snd_csa.4 \

View File

@ -24,21 +24,45 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 18, 2005
.Dd December 15, 2005
.Dt SND_ES137X 4
.Os
.Sh NAME
.Nm snd_es137x
.Nd "Ensoniq AudioPCI ES137x bridge device driver"
.Sh SYNOPSIS
To compile this driver into the kernel, place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device sound"
.Cd "device snd_es137x"
.Ed
.Pp
Alternatively, to load the driver as a module at boot time, place the
following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
snd_es137x_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
bridge driver allows the generic audio driver
.Xr sound 4
to attach to the Ensoniq 137x audio cards.
.Ss Runtime Configuration
The following
.Xr sysctl 8
variable are available in addition to those available to all
.Xr sound 4
devices:
.Bl -tag -width ".Va hw.snd.pcm%d.spdif_enabled" -offset indent
.It Va hw.snd.pcm%d.spdif_enabled
Enables S/PDIF output on the primary playback channel.
This
.Xr sysctl 8
variable is available only if the device is known to support S/PDIF output.
.El
.Sh HARDWARE
The
.Nm
@ -73,9 +97,10 @@ Ensoniq AudioPCI ES1373-8
.Sh HISTORY
The
.Nm
manual page first appeared in
.Fx 5.3 .
device driver first appeared in
.Fx 4.0 .
.Sh AUTHORS
.An "Russell Cattelan" Aq cattelan@thebarn.com
.An "Cameron Grant" Aq cg@FreeBSD.org
.An "Joachim Kuebart"
.An "Jonathan Noack" Aq noackjr@alumni.rice.edu

View File

@ -24,15 +24,26 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 2, 2005
.Dd December 1, 2005
.Dt SND_VIA8233 4
.Os
.Sh NAME
.Nm snd_via8233
.Nd "VIA Technologies VT8233 bridge device driver"
.Sh SYNOPSIS
To compile this driver into the kernel, place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device sound"
.Cd "device snd_via8233"
.Ed
.Pp
Alternatively, to load the driver as a module at boot time, place the
following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
snd_via8233_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
@ -65,6 +76,9 @@ The
.Nm
device driver first appeared in
.Fx 4.7 .
.Sh AUTHORS
This manual page was written by
.An Joel Dahl Aq joel@FreeBSD.org .
.Sh BUGS
The
.Nm

View File

@ -274,6 +274,7 @@ ng_vlan_load="NO" # IEEE 802.1Q VLAN tagging netgraph node type
sound_load="NO" # Digital sound subsystem
snd_ad1816_load="NO" # ad1816
snd_als4000_load="NO" # als4000
snd_atiixp_load="NO" # atiixp
snd_cmi_load="NO" # cmi
snd_cs4281_load="NO" # cs4281
snd_csa_load="NO" # csa

View File

@ -1926,6 +1926,7 @@ device sound
# since this is unsupported at the moment...).
#
# snd_als4000: Avance Logic ALS4000 PCI.
# snd_atiixp: ATI IXP 200/300/400 PCI.
# snd_ad1816: Analog Devices AD1816 ISA PnP/non-PnP.
# snd_audiocs: Crystal Semiconductor CS4231 SBus/EBus.
# snd_cmi: CMedia CMI8338/CMI8738 PCI.
@ -1961,6 +1962,7 @@ device sound
device snd_ad1816
device snd_als4000
device snd_atiixp
#device snd_au88x0
#device snd_audiocs
device snd_cmi

View File

@ -798,6 +798,7 @@ dev/sound/isa/sb8.c optional snd_sb8 isa
dev/sound/isa/sbc.c optional snd_sbc isa
dev/sound/isa/sndbuf_dma.c optional sound isa
dev/sound/pci/als4000.c optional snd_als4000 pci
dev/sound/pci/atiixp.c optional snd_atiixp.c pci
#dev/sound/pci/au88x0.c optional snd_au88x0 pci
dev/sound/pci/cmi.c optional snd_cmi pci
dev/sound/pci/cs4281.c optional snd_cs4281 pci
@ -830,6 +831,7 @@ dev/sound/pcm/feeder.c optional sound
dev/sound/pcm/feeder_fmt.c optional sound
dev/sound/pcm/feeder_if.m optional sound
dev/sound/pcm/feeder_rate.c optional sound
dev/sound/pcm/feeder_volume.c optional sound
dev/sound/pcm/mixer.c optional sound
dev/sound/pcm/mixer_if.m optional sound
dev/sound/pcm/sndstat.c optional sound

View File

@ -54,6 +54,7 @@ MODULE_VERSION(snd_driver, 1);
MODULE_DEPEND(snd_driver, snd_ad1816, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_als4000, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_atiixp, 1, 1, 1);
/* MODULE_DEPEND(snd_driver, snd_aureal, 1, 1, 1); */
MODULE_DEPEND(snd_driver, snd_cmi, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_cs4281, 1, 1, 1);

View File

@ -138,12 +138,16 @@ ad1816_intr(void *arg)
}
/* check for capture interupt */
if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) {
ad1816_unlock(ad1816);
chn_intr(ad1816->rch.channel);
ad1816_lock(ad1816);
served |= AD1816_INTRCI; /* cp served */
}
/* check for playback interupt */
if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) {
ad1816_unlock(ad1816);
chn_intr(ad1816->pch.channel);
ad1816_lock(ad1816);
served |= AD1816_INTRPI; /* pb served */
}
if (served == 0) {

View File

@ -361,8 +361,11 @@ ess_intr(void *arg)
rirq = (src & sc->rch.hwch)? 1 : 0;
if (pirq) {
if (sc->pch.run)
if (sc->pch.run) {
ess_unlock(sc);
chn_intr(sc->pch.channel);
ess_lock(sc);
}
if (sc->pch.stopping) {
sc->pch.run = 0;
sndbuf_dma(sc->pch.buffer, PCMTRIG_STOP);
@ -375,8 +378,11 @@ ess_intr(void *arg)
}
if (rirq) {
if (sc->rch.run)
if (sc->rch.run) {
ess_unlock(sc);
chn_intr(sc->rch.channel);
ess_lock(sc);
}
if (sc->rch.stopping) {
sc->rch.run = 0;
sndbuf_dma(sc->rch.buffer, PCMTRIG_STOP);

View File

@ -92,7 +92,9 @@ static driver_intr_t mss_intr;
/* prototypes for local functions */
static int mss_detect(device_t dev, struct mss_info *mss);
#ifndef PC98
static int opti_detect(device_t dev, struct mss_info *mss);
#endif
static char *ymf_test(device_t dev, struct mss_info *mss);
static void ad_unmute(struct mss_info *mss);
@ -111,7 +113,9 @@ static void ad_leave_MCE(struct mss_info *mss);
/* OPTi-specific functions */
static void opti_write(struct mss_info *mss, u_char reg,
u_char data);
#ifndef PC98
static u_char opti_read(struct mss_info *mss, u_char reg);
#endif
static int opti_init(device_t dev, struct mss_info *mss);
/* io primitives */
@ -795,11 +799,15 @@ mss_intr(void *arg)
c &= ~served;
if (sndbuf_runsz(mss->pch.buffer) && (c & 0x10)) {
served |= 0x10;
mss_unlock(mss);
chn_intr(mss->pch.channel);
mss_lock(mss);
}
if (sndbuf_runsz(mss->rch.buffer) && (c & 0x20)) {
served |= 0x20;
mss_unlock(mss);
chn_intr(mss->rch.channel);
mss_lock(mss);
}
/* now ack the interrupt */
if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */
@ -970,6 +978,7 @@ mss_speed(struct mss_chinfo *ch, int speed)
abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i;
speed = speeds[sel];
ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel);
ad_wait_init(mss, 10000);
}
ad_leave_MCE(mss);
@ -1009,7 +1018,11 @@ mss_format(struct mss_chinfo *ch, u_int32_t format)
arg <<= 4;
ad_enter_MCE(mss);
ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg);
if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */
ad_wait_init(mss, 10000);
if (ad_read(mss, 12) & 0x40) { /* mode2? */
ad_write(mss, 28, arg); /* capture mode */
ad_wait_init(mss, 10000);
}
ad_leave_MCE(mss);
return format;
}
@ -1111,8 +1124,16 @@ opti931_intr(void *arg)
return;
}
if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) chn_intr(mss->rch.channel);
if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) chn_intr(mss->pch.channel);
if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) {
mss_unlock(mss);
chn_intr(mss->rch.channel);
mss_lock(mss);
}
if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) {
mss_unlock(mss);
chn_intr(mss->pch.channel);
mss_lock(mss);
}
opti_wr(mss, 11, ~mc11); /* ack */
if (--loops) goto again;
mss_unlock(mss);
@ -1355,6 +1376,7 @@ mss_detect(device_t dev, struct mss_info *mss)
name = "AD1848";
mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */
#ifndef PC98
if (opti_detect(dev, mss)) {
switch (mss->bd_id) {
case MD_OPTI924:
@ -1367,6 +1389,7 @@ mss_detect(device_t dev, struct mss_info *mss)
printf("Found OPTi device %s\n", name);
if (opti_init(dev, mss) == 0) goto gotit;
}
#endif
/*
* Check that the I/O address is in use.
@ -1573,6 +1596,7 @@ mss_detect(device_t dev, struct mss_info *mss)
return ENXIO;
}
#ifndef PC98
static int
opti_detect(device_t dev, struct mss_info *mss)
{
@ -1618,6 +1642,7 @@ opti_detect(device_t dev, struct mss_info *mss)
}
return 0;
}
#endif
static char *
ymf_test(device_t dev, struct mss_info *mss)
@ -2152,6 +2177,7 @@ opti_write(struct mss_info *mss, u_char reg, u_char val)
}
}
#ifndef PC98
u_char
opti_read(struct mss_info *mss, u_char reg)
{
@ -2175,6 +2201,7 @@ opti_read(struct mss_info *mss, u_char reg)
}
return -1;
}
#endif
static device_method_t pnpmss_methods[] = {
/* Device interface */

View File

@ -370,23 +370,32 @@ static int
sb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
struct sb_info *sb = mix_getdevinfo(m);
u_char recdev;
u_char recdev_l, recdev_r;
recdev = 0;
if (src & SOUND_MASK_MIC)
recdev |= 0x01; /* mono mic */
recdev_l = 0;
recdev_r = 0;
if (src & SOUND_MASK_MIC) {
recdev_l |= 0x01; /* mono mic */
recdev_r |= 0x01;
}
if (src & SOUND_MASK_CD)
recdev |= 0x06; /* l+r cd */
if (src & SOUND_MASK_CD) {
recdev_l |= 0x04; /* l cd */
recdev_r |= 0x02; /* r cd */
}
if (src & SOUND_MASK_LINE)
recdev |= 0x18; /* l+r line */
if (src & SOUND_MASK_LINE) {
recdev_l |= 0x10; /* l line */
recdev_r |= 0x08; /* r line */
}
if (src & SOUND_MASK_SYNTH)
recdev |= 0x60; /* l+r midi */
if (src & SOUND_MASK_SYNTH) {
recdev_l |= 0x40; /* l midi */
recdev_r |= 0x20; /* r midi */
}
sb_setmixer(sb, SB16_IMASK_L, recdev);
sb_setmixer(sb, SB16_IMASK_R, recdev);
sb_setmixer(sb, SB16_IMASK_L, recdev_l);
sb_setmixer(sb, SB16_IMASK_R, recdev_r);
/* Switch on/off FM tuner source */
if (src & SOUND_MASK_LINE1)
@ -494,7 +503,7 @@ static void
sb_intr(void *arg)
{
struct sb_info *sb = (struct sb_info *)arg;
int reason = 3, c;
int reason, c;
/*
* The Vibra16X has separate flags for 8 and 16 bit transfers, but
@ -570,8 +579,9 @@ sb_setup(struct sb_info *sb)
sb_reset_dsp(sb);
if (sb->bd_flags & BD_F_SB16X) {
/* full-duplex doesn't work! */
pprio = sb->pch.run? 1 : 0;
sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL);
sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2);
sb->pch.dch = pprio? 1 : 0;
sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1);
sb->rch.dch = pprio? 2 : 1;
@ -848,7 +858,7 @@ sb16_attach(device_t dev)
else
status2[0] = '\0';
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %ud %s",
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
rman_get_start(sb->drq1), status2, sb->bufsize,
PCM_KLDSTRING(snd_sb16));

View File

@ -475,11 +475,17 @@ sb_intr(void *arg)
struct sb_info *sb = (struct sb_info *)arg;
sb_lock(sb);
if (sndbuf_runsz(sb->pch.buffer) > 0)
if (sndbuf_runsz(sb->pch.buffer) > 0) {
sb_unlock(sb);
chn_intr(sb->pch.channel);
sb_lock(sb);
}
if (sndbuf_runsz(sb->rch.buffer) > 0)
if (sndbuf_runsz(sb->rch.buffer) > 0) {
sb_unlock(sb);
chn_intr(sb->rch.channel);
sb_lock(sb);
}
sb_rd(sb, DSP_DATA_AVAIL); /* int ack */
sb_unlock(sb);
@ -564,8 +570,16 @@ sb_stop(struct sb_chinfo *ch)
sb_lock(sb);
if (sb->bd_flags & BD_F_HISPEED)
sb_reset_dsp(sb);
else
else {
#if 0
/*
* NOTE: DSP_CMD_DMAEXIT_8 does not work with old
* soundblaster.
*/
sb_cmd(sb, DSP_CMD_DMAEXIT_8);
#endif
sb_reset_dsp(sb);
}
if (play)
sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */

View File

@ -259,6 +259,7 @@ static struct isa_pnp_id sbc_ids[] = {
{0x81167316, "ESS ES1681"}, /* ESS1681 */
{0x02017316, "ESS ES1688"}, /* ESS1688 */
{0x68097316, "ESS ES1688"}, /* ESS1688 */
{0x68187316, "ESS ES1868"}, /* ESS1868 */
{0x03007316, "ESS ES1869"}, /* ESS1869 */
{0x69187316, "ESS ES1869"}, /* ESS1869 */

View File

@ -75,6 +75,7 @@ struct sc_info {
struct resource *reg, *irq;
int regid, irqid;
void *ih;
struct mtx *lock;
unsigned int bufsz;
struct sc_chinfo pch, rch;
@ -90,7 +91,11 @@ static u_int32_t als_format[] = {
0
};
static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
/*
* I don't believe this rotten soundcard can do 48k, really,
* trust me.
*/
static struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 };
/* ------------------------------------------------------------------------- */
/* Register Utilities */
@ -199,6 +204,7 @@ alschan_init(kobj_t obj, void *devinfo,
struct sc_info *sc = devinfo;
struct sc_chinfo *ch;
snd_mtxlock(sc->lock);
if (dir == PCMDIR_PLAY) {
ch = &sc->pch;
ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
@ -213,9 +219,11 @@ alschan_init(kobj_t obj, void *devinfo,
ch->format = AFMT_U8;
ch->speed = DSP_DEFAULT_SPEED;
ch->buffer = b;
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
snd_mtxunlock(sc->lock);
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0)
return NULL;
}
return ch;
}
@ -263,9 +271,12 @@ static int
alschan_getptr(kobj_t obj, void *data)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
int32_t pos, sz;
snd_mtxlock(sc->lock);
pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
snd_mtxunlock(sc->lock);
sz = sndbuf_getsize(ch->buffer);
return (2 * sz - pos - 1) % sz;
}
@ -378,7 +389,9 @@ static int
alspchan_trigger(kobj_t obj, void *data, int go)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
switch(go) {
case PCMTRIG_START:
als_playback_start(ch);
@ -387,6 +400,7 @@ alspchan_trigger(kobj_t obj, void *data, int go)
als_playback_stop(ch);
break;
}
snd_mtxunlock(sc->lock);
return 0;
}
@ -468,7 +482,9 @@ static int
alsrchan_trigger(kobj_t obj, void *data, int go)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
switch(go) {
case PCMTRIG_START:
als_capture_start(ch);
@ -477,6 +493,7 @@ alsrchan_trigger(kobj_t obj, void *data, int go)
als_capture_stop(ch);
break;
}
snd_mtxunlock(sc->lock);
return 0;
}
@ -578,8 +595,13 @@ alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (src & (1 << i)) {
l |= amt[i].iselect;
r |= amt[i].iselect << 1;
if (amt[i].iselect == 1) { /* microphone */
l |= amt[i].iselect;
r |= amt[i].iselect;
} else {
l |= amt[i].iselect;
r |= amt[i].iselect >> 1;
}
}
}
@ -605,13 +627,20 @@ als_intr(void *p)
struct sc_info *sc = (struct sc_info *)p;
u_int8_t intr, sb_status;
snd_mtxlock(sc->lock);
intr = als_intr_rd(sc);
if (intr & 0x80)
if (intr & 0x80) {
snd_mtxunlock(sc->lock);
chn_intr(sc->pch.channel);
snd_mtxlock(sc->lock);
}
if (intr & 0x40)
if (intr & 0x40) {
snd_mtxunlock(sc->lock);
chn_intr(sc->rch.channel);
snd_mtxlock(sc->lock);
}
/* ACK interrupt in PCI core */
als_intr_wr(sc, intr);
@ -627,6 +656,8 @@ als_intr(void *p)
als_ack_read(sc, ALS_MIDI_DATA);
if (sb_status & ALS_IRQ_CR1E)
als_ack_read(sc, ALS_CR1E_ACK_PORT);
snd_mtxunlock(sc->lock);
return;
}
@ -708,6 +739,10 @@ als_resource_free(device_t dev, struct sc_info *sc)
bus_dma_tag_destroy(sc->parent_dmat);
sc->parent_dmat = 0;
}
if (sc->lock) {
snd_mtxfree(sc->lock);
sc->lock = NULL;
}
}
static int
@ -730,7 +765,7 @@ als_resource_grab(device_t dev, struct sc_info *sc)
goto bad;
}
if (bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, als_intr,
if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
sc, &sc->ih)) {
device_printf(dev, "unable to setup interrupt\n");
goto bad;
@ -745,8 +780,8 @@ als_resource_grab(device_t dev, struct sc_info *sc)
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sc->bufsz,
/*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant, &sc->parent_dmat) != 0) {
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &sc->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -768,6 +803,7 @@ als_pci_attach(device_t dev)
return ENXIO;
}
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
sc->dev = dev;
data = pci_read_config(dev, PCIR_COMMAND, 2);
@ -851,9 +887,11 @@ als_pci_suspend(device_t dev)
{
struct sc_info *sc = pcm_getdevinfo(dev);
snd_mtxlock(sc->lock);
sc->pch.dma_was_active = als_playback_stop(&sc->pch);
sc->rch.dma_was_active = als_capture_stop(&sc->rch);
als_uninit(sc);
snd_mtxunlock(sc->lock);
return 0;
}
@ -862,13 +900,17 @@ als_pci_resume(device_t dev)
{
struct sc_info *sc = pcm_getdevinfo(dev);
snd_mtxlock(sc->lock);
if (als_init(sc) != 0) {
device_printf(dev, "unable to reinitialize the card\n");
snd_mtxunlock(sc->lock);
return ENXIO;
}
if (mixer_reinit(dev) != 0) {
device_printf(dev, "unable to reinitialize the mixer\n");
snd_mtxunlock(sc->lock);
return ENXIO;
}
@ -879,6 +921,8 @@ als_pci_resume(device_t dev)
if (sc->rch.dma_was_active) {
als_capture_start(&sc->rch);
}
snd_mtxunlock(sc->lock);
return 0;
}

View File

@ -876,8 +876,8 @@ cmi_attach(device_t dev)
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sc->bufsz, /*nsegments*/1,
/*maxsegz*/0x3ffff, /*flags*/0,
/*lockfunc*/busdma_lock_mutex,
/*lockfunc*/&Giant,
/*lockfunc*/NULL,
/*lockfunc*/NULL,
&sc->parent_dmat) != 0) {
device_printf(dev, "cmi_attach: Unable to create dma tag\n");
goto bad;

View File

@ -644,7 +644,7 @@ csa_initialize(sc_p scp)
/*
* Set the serial port FIFO pointer to the first sample in the FIFO.
*/
#if notdef
#ifdef notdef
csa_writeio(resp, BA0_SERBSP, 0);
#endif /* notdef */
@ -698,7 +698,7 @@ csa_initialize(sc_p scp)
* First, lets wait a short while to let things settle out a bit,
* and to prevent retrying the read too quickly.
*/
#if notdef
#ifdef notdef
DELAY(10000000L); /* clw */
#else
DELAY(1000);
@ -728,7 +728,7 @@ csa_initialize(sc_p scp)
* Power down the DAC and ADC. We will power them up (if) when we need
* them.
*/
#if notdef
#ifdef notdef
csa_writeio(resp, BA0_AC97_POWERDOWN, 0x300);
#endif /* notdef */
@ -736,7 +736,7 @@ csa_initialize(sc_p scp)
* Turn off the Processor by turning off the software clock enable flag in
* the clock control register.
*/
#if notdef
#ifdef notdef
clkcr1 = csa_readio(resp, BA0_CLKCR1) & ~CLKCR1_SWCE;
csa_writeio(resp, BA0_CLKCR1, clkcr1);
#endif /* notdef */

View File

@ -667,6 +667,14 @@ csa_init(struct csa_info *csa)
/* Crank up the power on the DAC and ADC. */
csa_setplaysamplerate(resp, 8000);
csa_setcapturesamplerate(resp, 8000);
/* Set defaults */
csa_writeio(resp, BA0_EGPIODR, EGPIODR_GPOE0);
csa_writeio(resp, BA0_EGPIOPTR, EGPIOPTR_GPPT0);
/* Power up amplifier */
csa_writeio(resp, BA0_EGPIODR, csa_readio(resp, BA0_EGPIODR) |
EGPIODR_GPOE2);
csa_writeio(resp, BA0_EGPIOPTR, csa_readio(resp, BA0_EGPIOPTR) |
EGPIOPTR_GPPT2);
return 0;
}

View File

@ -494,12 +494,12 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s,
m->buf = tmp_addr;
m->slave = s;
if (sc->audigy) {
m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_LEFT << 8 |
FXBUS_PCM_RIGHT << 16 | FXBUS_MIDI_REVERB << 24;
m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 8 |
FXBUS_PCM_LEFT << 16 | FXBUS_MIDI_REVERB << 24;
m->fxrt2 = 0x3f3f3f3f; /* No effects on second route */
} else {
m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_LEFT << 4 |
FXBUS_PCM_RIGHT << 8 | FXBUS_MIDI_REVERB << 12;
m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 4 |
FXBUS_PCM_LEFT << 8 | FXBUS_MIDI_REVERB << 12;
m->fxrt2 = 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -166,6 +166,17 @@
#define ES1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */
#define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */
/*
* S/PDIF specific
*/
/* Use ES1370_REG_CONTROL */
#define RECEN_B 0x08000000 /* Used to control mixing of analog with digital data */
#define SPDIFEN_B 0x04000000 /* Reset to switch digital output mux to "THRU" mode */
/* Use ES1370_REG_STATUS */
#define ENABLE_SPDIF 0x00040000 /* Used to enable the S/PDIF circuitry */
#define TEST_SPDIF 0x00020000 /* Used to put the S/PDIF module in "test mode" */
/*
* Sample rate converter addresses
*/

View File

@ -41,11 +41,81 @@ SND_DECLARE_FILE("$FreeBSD$");
#define ICH_DEFAULT_BUFSZ 16384
#define ICH_MAX_BUFSZ 65536
#define SIS7012ID 0x70121039 /* SiS 7012 needs special handling */
#define ICH4ID 0x24c58086 /* ICH4 needs special handling too */
#define ICH5ID 0x24d58086 /* ICH5 needs to be treated as ICH4 */
#define I6300ESBID 0x25a68086 /* 6300ESB needs to be treated as ICH4 */
#define ICH6ID 0x266e8086 /* ICH6 needs to be treated as ICH4 */
#define INTEL_VENDORID 0x8086
#define SIS_VENDORID 0x1039
#define NVIDIA_VENDORID 0x10de
#define AMD_VENDORID 0x1022
#define INTEL_82440MX 0x7195
#define INTEL_82801AA 0x2415
#define INTEL_82801AB 0x2425
#define INTEL_82801BA 0x2445
#define INTEL_82801CA 0x2485
#define INTEL_82801DB 0x24c5 /* ICH4 needs special handling */
#define INTEL_82801EB 0x24d5 /* ICH5 needs to be treated as ICH4 */
#define INTEL_6300ESB 0x25a6 /* 6300ESB needs to be treated as ICH4 */
#define INTEL_82801FB 0x266e /* ICH6 needs to be treated as ICH4 */
#define INTEL_82801GB 0x27de /* ICH7 needs to be treated as ICH4 */
#define SIS_7012 0x7012 /* SiS 7012 needs special handling */
#define NVIDIA_NFORCE 0x01b1
#define NVIDIA_NFORCE2 0x006a
#define NVIDIA_NFORCE2_400 0x008a
#define NVIDIA_NFORCE3 0x00da
#define NVIDIA_NFORCE3_250 0x00ea
#define NVIDIA_NFORCE4 0x0059
#define AMD_768 0x7445
#define AMD_8111 0x746d
#define ICH_LOCK(sc) snd_mtxlock((sc)->ich_lock)
#define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock)
#define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock)
static const struct ich_type {
uint16_t vendor;
uint16_t devid;
uint32_t options;
#define PROBE_LOW 0x01
char *name;
} ich_devs[] = {
{ INTEL_VENDORID, INTEL_82440MX, 0,
"Intel 440MX" },
{ INTEL_VENDORID, INTEL_82801AA, 0,
"Intel ICH (82801AA)" },
{ INTEL_VENDORID, INTEL_82801AB, 0,
"Intel ICH (82801AB)" },
{ INTEL_VENDORID, INTEL_82801BA, 0,
"Intel ICH2 (82801BA)" },
{ INTEL_VENDORID, INTEL_82801CA, 0,
"Intel ICH3 (82801CA)" },
{ INTEL_VENDORID, INTEL_82801DB, PROBE_LOW,
"Intel ICH4 (82801DB)" },
{ INTEL_VENDORID, INTEL_82801EB, PROBE_LOW,
"Intel ICH5 (82801EB)" },
{ INTEL_VENDORID, INTEL_6300ESB, PROBE_LOW,
"Intel 6300ESB" },
{ INTEL_VENDORID, INTEL_82801FB, PROBE_LOW,
"Intel ICH6 (82801FB)" },
{ INTEL_VENDORID, INTEL_82801GB, PROBE_LOW,
"Intel ICH7 (82801GB)" },
{ SIS_VENDORID, SIS_7012, 0,
"SiS 7012" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE, 0,
"nVidia nForce" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE2, 0,
"nVidia nForce2" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE2_400, 0,
"nVidia nForce2 400" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE3, 0,
"nVidia nForce3" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE3_250, 0,
"nVidia nForce3 250" },
{ NVIDIA_VENDORID, NVIDIA_NFORCE4, 0,
"nVidia nForce4" },
{ AMD_VENDORID, AMD_768, 0,
"AMD-768" },
{ AMD_VENDORID, AMD_8111, 0,
"AMD-8111" }
};
/* buffer descriptor */
struct ich_desc {
@ -93,6 +163,11 @@ struct sc_info {
bus_addr_t desc_addr;
struct intr_config_hook intrhook;
int use_intrhook;
uint16_t vendor;
uint16_t devid;
uint32_t flags;
#define IGNORE_PCR 0x01
struct mtx *ich_lock;
};
/* -------------------------------------------------------------------- */
@ -106,7 +181,7 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0};
/* -------------------------------------------------------------------- */
/* Hardware */
static u_int32_t
static __inline u_int32_t
ich_rd(struct sc_info *sc, int regno, int size)
{
switch (size) {
@ -121,7 +196,7 @@ ich_rd(struct sc_info *sc, int regno, int size)
}
}
static void
static __inline void
ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
{
switch (size) {
@ -149,7 +224,10 @@ ich_waitcd(void *devinfo)
data = ich_rd(sc, ICH_REG_ACC_SEMA, 1);
if ((data & 0x01) == 0)
return 0;
DELAY(1);
}
if ((sc->flags & IGNORE_PCR) != 0)
return (0);
device_printf(sc->dev, "CODEC semaphore timeout\n");
return ETIMEDOUT;
}
@ -190,15 +268,15 @@ AC97_DECLARE(ich_ac97);
static void
ich_filldtbl(struct sc_chinfo *ch)
{
struct sc_info *sc = ch->parent;
u_int32_t base;
int i;
base = sndbuf_getbufaddr(ch->buffer);
ch->blkcnt = sndbuf_getsize(ch->buffer) / ch->blksz;
if (ch->blkcnt != 2 && ch->blkcnt != 4 && ch->blkcnt != 8 && ch->blkcnt != 16 && ch->blkcnt != 32) {
ch->blkcnt = 2;
ch->blksz = sndbuf_getsize(ch->buffer) / ch->blkcnt;
}
if (ch->blksz > sc->bufsz / ch->blkcnt)
ch->blksz = sc->bufsz / ch->blkcnt;
sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz);
ch->blksz = sndbuf_getblksz(ch->buffer);
for (i = 0; i < ICH_DTBL_LENGTH; i++) {
ch->dtbl[i].buffer = base + (ch->blksz * (i % ch->blkcnt));
@ -222,7 +300,12 @@ ich_resetchan(struct sc_info *sc, int num)
return ENXIO;
ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1);
#if 1
/* This may result in no sound output on NForce 2 MBs, see PR 73987 */
DELAY(100);
#else
(void)ich_rd(sc, regbase + ICH_REG_X_CR, 1);
#endif
ich_wr(sc, regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1);
for (i = 0; i < ICH_TIMEOUT; i++) {
cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1);
@ -244,6 +327,7 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
struct sc_chinfo *ch;
unsigned int num;
ICH_LOCK(sc);
num = sc->chnum++;
ch = &sc->ch[num];
ch->num = num;
@ -283,10 +367,13 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
return NULL;
}
ICH_UNLOCK(sc);
if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0)
return NULL;
ICH_LOCK(sc);
ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4);
ICH_UNLOCK(sc);
return ch;
}
@ -304,16 +391,20 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
struct sc_info *sc = ch->parent;
if (ch->spdreg) {
int r;
int r, ac97rate;
ICH_LOCK(sc);
if (sc->ac97rate <= 32000 || sc->ac97rate >= 64000)
sc->ac97rate = 48000;
r = (speed * 48000) / sc->ac97rate;
ac97rate = sc->ac97rate;
ICH_UNLOCK(sc);
r = (speed * 48000) / ac97rate;
/*
* Cast the return value of ac97_setrate() to u_int so that
* the math don't overflow into the negative range.
*/
ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) *
sc->ac97rate) / 48000;
ac97rate) / 48000;
} else {
ch->spd = 48000;
}
@ -328,7 +419,9 @@ ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
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;
}
@ -342,12 +435,16 @@ ichchan_trigger(kobj_t obj, void *data, int go)
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_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1);
ICH_UNLOCK(sc);
break;
case PCMTRIG_ABORT:
ICH_LOCK(sc);
ich_resetchan(sc, ch->num);
ICH_UNLOCK(sc);
ch->run = 0;
break;
}
@ -361,7 +458,9 @@ ichchan_getptr(kobj_t obj, void *data)
struct sc_info *sc = ch->parent;
u_int32_t pos;
ICH_LOCK(sc);
ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt;
ICH_UNLOCK(sc);
pos = ch->civ * ch->blksz;
@ -399,6 +498,7 @@ ich_intr(void *p)
u_int32_t cbi, lbi, lvi, st, gs;
int i;
ICH_LOCK(sc);
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 */
@ -417,8 +517,11 @@ ich_intr(void *p)
st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI;
if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) {
/* block complete - update buffer */
if (ch->run)
if (ch->run) {
ICH_UNLOCK(sc);
chn_intr(ch->channel);
ICH_LOCK(sc);
}
lvi = ich_rd(sc, ch->regbase + ICH_REG_X_LVI, 1);
cbi = ch->civ % ch->blkcnt;
if (cbi == 0)
@ -439,6 +542,7 @@ ich_intr(void *p)
(sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),
st, 2);
}
ICH_UNLOCK(sc);
if (gs != 0) {
device_printf(sc->dev,
"Unhandled interrupt, gs_intr = %x\n", gs);
@ -581,11 +685,12 @@ ich_init(struct sc_info *sc)
if ((stat & ICH_GLOB_STA_PCR) == 0) {
/* ICH4/ICH5 may fail when busmastering is enabled. Continue */
if ((pci_get_devid(sc->dev) != ICH4ID) &&
(pci_get_devid(sc->dev) != ICH5ID) &&
(pci_get_devid(sc->dev) != I6300ESBID) &&
(pci_get_devid(sc->dev) != ICH6ID)) {
return ENXIO;
if (sc->vendor == INTEL_VENDORID && (
sc->devid == INTEL_82801DB || sc->devid == INTEL_82801EB ||
sc->devid == INTEL_6300ESB || sc->devid == INTEL_82801FB ||
sc->devid == INTEL_82801GB)) {
sc->flags |= IGNORE_PCR;
device_printf(sc->dev, "primary codec not ready!\n");
}
}
@ -611,104 +716,47 @@ ich_init(struct sc_info *sc)
static int
ich_pci_probe(device_t dev)
{
switch(pci_get_devid(dev)) {
case 0x71958086:
device_set_desc(dev, "Intel 443MX");
return BUS_PROBE_DEFAULT;
int i;
uint16_t devid, vendor;
case 0x24158086:
device_set_desc(dev, "Intel ICH (82801AA)");
return BUS_PROBE_DEFAULT;
case 0x24258086:
device_set_desc(dev, "Intel ICH (82801AB)");
return BUS_PROBE_DEFAULT;
case 0x24458086:
device_set_desc(dev, "Intel ICH2 (82801BA)");
return BUS_PROBE_DEFAULT;
case 0x24858086:
device_set_desc(dev, "Intel ICH3 (82801CA)");
return BUS_PROBE_DEFAULT;
case ICH4ID:
device_set_desc(dev, "Intel ICH4 (82801DB)");
return BUS_PROBE_LOW_PRIORITY;
case ICH5ID:
device_set_desc(dev, "Intel ICH5 (82801EB)");
return BUS_PROBE_LOW_PRIORITY;
case I6300ESBID:
device_set_desc(dev, "Intel 6300ESB");
return BUS_PROBE_LOW_PRIORITY;
case ICH6ID:
device_set_desc(dev, "Intel ICH6 (82801FB)");
return BUS_PROBE_LOW_PRIORITY;
case SIS7012ID:
device_set_desc(dev, "SiS 7012");
return BUS_PROBE_DEFAULT;
case 0x01b110de:
device_set_desc(dev, "nVidia nForce");
return BUS_PROBE_DEFAULT;
case 0x006a10de:
device_set_desc(dev, "nVidia nForce2");
return BUS_PROBE_DEFAULT;
case 0x008a10de:
device_set_desc(dev, "nVidia nForce2 400");
return BUS_PROBE_DEFAULT;
case 0x00da10de:
device_set_desc(dev, "nVidia nForce3");
return BUS_PROBE_DEFAULT;
case 0x00ea10de:
device_set_desc(dev, "nVidia nForce3 250");
return BUS_PROBE_DEFAULT;
case 0x005910de:
device_set_desc(dev, "nVidia nForce4");
return BUS_PROBE_DEFAULT;
case 0x74451022:
device_set_desc(dev, "AMD-768");
return BUS_PROBE_DEFAULT;
case 0x746d1022:
device_set_desc(dev, "AMD-8111");
return BUS_PROBE_DEFAULT;
default:
return ENXIO;
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
for (i = 0; i < sizeof(ich_devs)/sizeof(ich_devs[0]); i++) {
if (vendor == ich_devs[i].vendor &&
devid == ich_devs[i].devid) {
device_set_desc(dev, ich_devs[i].name);
/* allow a better driver to override us */
if ((ich_devs[i].options & PROBE_LOW) != 0)
return (BUS_PROBE_LOW_PRIORITY);
return (BUS_PROBE_DEFAULT);
}
}
return (ENXIO);
}
static int
ich_pci_attach(device_t dev)
{
u_int16_t extcaps;
uint16_t devid, vendor;
struct sc_info *sc;
char status[SND_STATUSLEN];
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) {
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(sc, sizeof(*sc));
sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
sc->dev = dev;
vendor = sc->vendor = pci_get_vendor(dev);
devid = sc->devid = pci_get_device(dev);
/*
* The SiS 7012 register set isn't quite like the standard ich.
* There really should be a general "quirks" mechanism.
*/
if (pci_get_devid(dev) == SIS7012ID) {
if (vendor == SIS_VENDORID && devid == SIS_7012) {
sc->swap_reg = 1;
sc->sample_size = 1;
} else {
@ -716,6 +764,12 @@ ich_pci_attach(device_t dev)
sc->sample_size = 2;
}
/*
* Enable bus master. On ich4/5 this may prevent the detection of
* the primary codec becoming ready in ich_init().
*/
pci_enable_busmaster(dev);
/*
* By default, ich4 has NAMBAR and NABMBAR i/o spaces as
* read-only. Need to enable "legacy support", by poking into
@ -723,22 +777,13 @@ ich_pci_attach(device_t dev)
* but doing so will mess things up here. ich4 has enough new
* features it warrants it's own driver.
*/
if (pci_get_devid(dev) == ICH4ID) {
pci_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1);
}
/*
* Enable bus master. On ich4/5 this may prevent the detection of
* the primary codec becoming ready in ich_init().
*/
pci_enable_busmaster(dev);
if (pci_get_devid(dev) == ICH5ID ||
pci_get_devid(dev) == I6300ESBID ||
pci_get_devid(dev) == ICH6ID) {
if (vendor == INTEL_VENDORID && (devid == INTEL_82801DB ||
devid == INTEL_82801EB || devid == INTEL_6300ESB ||
devid == INTEL_82801FB || devid == INTEL_82801GB)) {
sc->nambarid = PCIR_MMBAR;
sc->nabmbarid = PCIR_MBBAR;
sc->regtype = SYS_RES_MEMORY;
pci_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1);
} else {
sc->nambarid = PCIR_NAMBAR;
sc->nabmbarid = PCIR_NABMBAR;
@ -763,7 +808,7 @@ ich_pci_attach(device_t dev)
sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ);
if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
NULL, NULL, sc->bufsz, 1, 0x3ffff, 0,
busdma_lock_mutex, &Giant, &sc->dmat) != 0) {
NULL, NULL, &sc->dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -771,7 +816,7 @@ ich_pci_attach(device_t dev)
sc->irqid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, ich_intr, sc, &sc->ih)) {
if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@ -832,6 +877,8 @@ ich_pci_attach(device_t dev)
if (sc->nabmbar)
bus_release_resource(dev, sc->regtype,
sc->nabmbarid, sc->nabmbar);
if (sc->ich_lock)
snd_mtxfree(sc->ich_lock);
free(sc, M_DEVBUF);
return ENXIO;
}
@ -852,6 +899,7 @@ ich_pci_detach(device_t dev)
bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar);
bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar);
bus_dma_tag_destroy(sc->dmat);
snd_mtxfree(sc->ich_lock);
free(sc, M_DEVBUF);
return 0;
}
@ -885,12 +933,16 @@ ich_pci_suspend(device_t dev)
int i;
sc = pcm_getdevinfo(dev);
ICH_LOCK(sc);
for (i = 0 ; i < 3; i++) {
sc->ch[i].run_save = sc->ch[i].run;
if (sc->ch[i].run) {
ICH_UNLOCK(sc);
ichchan_trigger(0, &sc->ch[i], PCMTRIG_ABORT);
ICH_LOCK(sc);
}
}
ICH_UNLOCK(sc);
return 0;
}
@ -908,9 +960,11 @@ ich_pci_resume(device_t dev)
pci_enable_io(dev, SYS_RES_MEMORY);
pci_enable_busmaster(dev);
ICH_LOCK(sc);
/* Reinit audio device */
if (ich_init(sc) == -1) {
device_printf(dev, "unable to reinitialize the card\n");
ICH_UNLOCK(sc);
return ENXIO;
}
/* Reinit mixer */
@ -918,17 +972,21 @@ 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");
ICH_UNLOCK(sc);
return ENXIO;
}
/* Re-start DMA engines */
for (i = 0 ; i < 3; i++) {
struct sc_chinfo *ch = &sc->ch[i];
if (sc->ch[i].run_save) {
ICH_UNLOCK(sc);
ichchan_setblocksize(0, ch, ch->blksz);
ichchan_setspeed(0, ch, ch->spd);
ichchan_trigger(0, ch, PCMTRIG_START);
ICH_LOCK(sc);
}
}
ICH_UNLOCK(sc);
return 0;
}

View File

@ -223,7 +223,16 @@ nm_initcd(kobj_t obj, void *devinfo)
struct sc_info *sc = (struct sc_info *)devinfo;
nm_wr(sc, 0x6c0, 0x01, 1);
#if 0
/*
* The following code-line may cause a hang for some chipsets, see
* PR 56617.
* In case of a bugreport without this line have a look at the PR and
* conditionize the code-line based upon the specific version of
* the chip.
*/
nm_wr(sc, 0x6cc, 0x87, 1);
#endif
nm_wr(sc, 0x6cc, 0x80, 1);
nm_wr(sc, 0x6cc, 0x00, 1);
return 1;

View File

@ -100,12 +100,14 @@ struct via_info {
struct ac97_info *codec;
unsigned int bufsz;
int dxs_src;
struct via_chinfo pch[NDXSCHANS + NMSGDCHANS];
struct via_chinfo rch[NWRCHANS];
struct via_dma_op *sgd_table;
u_int16_t codec_caps;
u_int16_t n_dxs_registered;
struct mtx *lock;
};
static u_int32_t via_fmt[] = {
@ -119,7 +121,90 @@ static u_int32_t via_fmt[] = {
static struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 };
static struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 };
static u_int32_t
#ifdef SND_DYNSYSCTL
static int
sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
{
struct via_info *via;
device_t dev;
uint32_t r;
int err, new_en;
dev = oidp->oid_arg1;
via = pcm_getdevinfo(dev);
snd_mtxlock(via->lock);
r = pci_read_config(dev, VIA_PCI_SPDIF, 1);
snd_mtxunlock(via->lock);
new_en = (r & VIA_SPDIF_EN) ? 1 : 0;
err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
if (err || req->newptr == NULL)
return err;
if (new_en < 0 || new_en > 1)
return EINVAL;
if (new_en)
r |= VIA_SPDIF_EN;
else
r &= ~VIA_SPDIF_EN;
snd_mtxlock(via->lock);
pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
snd_mtxunlock(via->lock);
return 0;
}
#if 0
static int
sysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS)
{
struct via_info *via;
device_t dev;
int err, val;
dev = oidp->oid_arg1;
via = pcm_getdevinfo(dev);
snd_mtxlock(via->lock);
val = via->dxs_src;
snd_mtxunlock(via->lock);
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
if (err || req->newptr == NULL)
return err;
if (val < 0 || val > 1)
return EINVAL;
snd_mtxlock(via->lock);
via->dxs_src = val;
snd_mtxunlock(via->lock);
return 0;
}
#endif
#endif /* SND_DYNSYSCTL */
static void
via_init_sysctls(device_t dev)
{
#ifdef SND_DYNSYSCTL
SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "spdif_enabled",
CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
sysctl_via8233_spdif_enable, "I",
"Enable S/PDIF output on primary playback channel");
#if 0
SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "via_dxs_src",
CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
sysctl_via8233_dxs_src, "I",
"Enable VIA DXS Sample Rate Converter");
#endif
#endif
}
static __inline u_int32_t
via_rd(struct via_info *via, int regno, int size)
{
switch (size) {
@ -134,7 +219,7 @@ via_rd(struct via_info *via, int regno, int size)
}
}
static void
static __inline void
via_wr(struct via_info *via, int regno, u_int32_t data, int size)
{
@ -253,14 +338,16 @@ via8233wr_setformat(kobj_t obj, void *data, u_int32_t format)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
u_int32_t f = WR_FORMAT_STOP_INDEX;
if (format & AFMT_STEREO)
f |= WR_FORMAT_STEREO;
if (format & AFMT_S16_LE)
f |= WR_FORMAT_16BIT;
snd_mtxlock(via->lock);
via_wr(via, VIA_WR0_FORMAT, f, 4);
snd_mtxunlock(via->lock);
return 0;
}
@ -270,9 +357,11 @@ via8233dxs_setformat(kobj_t obj, void *data, u_int32_t format)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
u_int32_t r, v;
u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
u_int32_t v = via_rd(via, r, 4);
r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
snd_mtxlock(via->lock);
v = via_rd(via, r, 4);
v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT);
if (format & AFMT_STEREO)
@ -280,6 +369,7 @@ via8233dxs_setformat(kobj_t obj, void *data, u_int32_t format)
if (format & AFMT_16BIT)
v |= VIA8233_DXS_RATEFMT_16BIT;
via_wr(via, r, v, 4);
snd_mtxunlock(via->lock);
return 0;
}
@ -301,8 +391,10 @@ via8233msgd_setformat(kobj_t obj, void *data, u_int32_t format)
s |= SLOT3(1) | SLOT4(1);
}
snd_mtxlock(via->lock);
via_wr(via, VIA_MC_SLOT_SELECT, s, 4);
via_wr(via, VIA_MC_SGD_FORMAT, v, 1);
snd_mtxunlock(via->lock);
return 0;
}
@ -316,11 +408,10 @@ via8233wr_setspeed(kobj_t obj, void *data, u_int32_t speed)
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
u_int32_t spd = 48000;
if (via->codec_caps & AC97_EXTCAP_VRA) {
spd = ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed);
}
return spd;
if (via->codec_caps & AC97_EXTCAP_VRA)
return ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed);
return 48000;
}
static int
@ -328,14 +419,17 @@ via8233dxs_setspeed(kobj_t obj, void *data, u_int32_t speed)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
u_int32_t r, v;
u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
u_int32_t v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K;
r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
snd_mtxlock(via->lock);
v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K;
/* Careful to avoid overflow (divide by 48 per vt8233c docs) */
v |= VIA8233_DXS_RATEFMT_48K * (speed / 48) / (48000 / 48);
via_wr(via, r, v, 4);
snd_mtxunlock(via->lock);
return speed;
}
@ -362,7 +456,7 @@ via8233wr_getcaps(kobj_t obj, void *data)
struct via_info *via = ch->parent;
/* Controlled by ac97 registers */
if (via->codec_caps & AC97_EXTCAP_VRA)
if (via->codec_caps & AC97_EXTCAP_VRA)
return &via_vracaps;
return &via_caps;
}
@ -370,7 +464,17 @@ via8233wr_getcaps(kobj_t obj, void *data)
static struct pcmchan_caps *
via8233dxs_getcaps(kobj_t obj, void *data)
{
/* Controlled by onboard registers */
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
/*
* Controlled by onboard registers
*
* Apparently, few boards can do DXS sample rate
* conversion.
*/
if (via->dxs_src)
return &via_vracaps;
return &via_caps;
}
@ -381,7 +485,7 @@ via8233msgd_getcaps(kobj_t obj, void *data)
struct via_info *via = ch->parent;
/* Controlled by ac97 registers */
if (via->codec_caps & AC97_EXTCAP_VRA)
if (via->codec_caps & AC97_EXTCAP_VRA)
return &via_vracaps;
return &via_caps;
}
@ -393,6 +497,7 @@ static int
via8233chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct via_chinfo *ch = data;
sndbuf_resize(ch->buffer, SEGS_PER_CHAN, blocksize);
ch->blksz = sndbuf_getblksz(ch->buffer);
return ch->blksz;
@ -403,11 +508,15 @@ via8233chan_getptr(kobj_t obj, void *data)
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
u_int32_t v, index, count;
int ptr;
u_int32_t v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
u_int32_t index = v >> 24; /* Last completed buffer */
u_int32_t count = v & 0x00ffffff; /* Bytes remaining */
int ptr = (index + 1) * ch->blksz - count;
snd_mtxlock(via->lock);
v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
snd_mtxunlock(via->lock);
index = v >> 24; /* Last completed buffer */
count = v & 0x00ffffff; /* Bytes remaining */
ptr = (index + 1) * ch->blksz - count;
ptr %= SEGS_PER_CHAN * ch->blksz; /* Wrap to available space */
return ptr;
@ -445,12 +554,17 @@ via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->dir = dir;
ch->rbase = VIA_WR_BASE(c->num);
snd_mtxlock(via->lock);
via_wr(via, ch->rbase + VIA_WR_RP_SGD_FORMAT, WR_FIFO_ENABLE, 1);
snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
return ch;
}
@ -472,13 +586,18 @@ via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
* channels. We therefore want to align first DXS channel to
* DXS3.
*/
snd_mtxlock(via->lock);
ch->rbase = VIA_DXS_BASE(NDXSCHANS - 1 - via->n_dxs_registered);
via->n_dxs_registered++;
snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
return ch;
}
@ -498,8 +617,11 @@ via8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
snd_mtxunlock(via->lock);
return ch;
}
@ -526,6 +648,7 @@ via8233chan_trigger(kobj_t obj, void* data, int go)
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
snd_mtxlock(via->lock);
switch(go) {
case PCMTRIG_START:
via_buildsgdt(ch);
@ -542,6 +665,7 @@ via8233chan_trigger(kobj_t obj, void* data, int go)
via8233chan_reset(via, ch);
break;
}
snd_mtxunlock(via->lock);
return 0;
}
@ -590,26 +714,32 @@ via_intr(void *p)
int i, stat;
/* Poll playback channels */
snd_mtxlock(via->lock);
for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
if (via->pch[i].rbase == 0)
if (via->pch[i].channel == NULL)
continue;
stat = via->pch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
snd_mtxunlock(via->lock);
chn_intr(via->pch[i].channel);
snd_mtxlock(via->lock);
}
}
/* Poll record channels */
for (i = 0; i < NWRCHANS; i++) {
if (via->rch[i].rbase == 0)
if (via->rch[i].channel == NULL)
continue;
stat = via->rch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
snd_mtxunlock(via->lock);
chn_intr(via->rch[i].channel);
snd_mtxlock(via->lock);
}
}
snd_mtxunlock(via->lock);
}
/*
@ -710,65 +840,22 @@ via_chip_init(device_t dev)
return ENXIO;
}
#ifdef SND_DYNSYSCTL
static int via8233_spdif_en;
static int
sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
{
device_t dev;
int err, new_en, r;
new_en = via8233_spdif_en;
err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
if (err || req->newptr == NULL)
return err;
if (new_en < 0 || new_en > 1)
return EINVAL;
via8233_spdif_en = new_en;
dev = oidp->oid_arg1;
r = pci_read_config(dev, VIA_PCI_SPDIF, 1) & ~VIA_SPDIF_EN;
if (new_en)
r |= VIA_SPDIF_EN;
pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
return 0;
}
#endif /* SND_DYNSYSCTL */
static void
via_init_sysctls(device_t dev)
{
#ifdef SND_DYNSYSCTL
int r;
r = pci_read_config(dev, VIA_PCI_SPDIF, 1);
via8233_spdif_en = (r & VIA_SPDIF_EN) ? 1 : 0;
SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "spdif_enabled",
CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
sysctl_via8233_spdif_enable, "I",
"Enable S/PDIF output on primary playback channel");
#endif
}
static int
via_attach(device_t dev)
{
struct via_info *via = 0;
char status[SND_STATUSLEN];
int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum;
if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_enable_busmaster(dev);
via->regid = PCIR_BAR(0);
via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid,
RF_ACTIVE);
@ -785,7 +872,7 @@ via_attach(device_t dev)
via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!via->irq ||
snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih)) {
snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@ -796,8 +883,8 @@ via_attach(device_t dev)
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant, &via->parent_dmat) != 0) {
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &via->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -813,8 +900,8 @@ via_attach(device_t dev)
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/NSEGS * sizeof(struct via_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant, &via->sgd_dmat) != 0) {
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &via->sgd_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -850,28 +937,71 @@ via_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
rman_get_start(via->reg), rman_get_start(via->irq),PCM_KLDSTRING(snd_via8233));
/* Register */
/*
* Decide whether DXS had to be disabled or not
*/
if (pci_get_revid(dev) == VIA8233_REV_ID_8233A) {
if (pcm_register(dev, via, NMSGDCHANS, 1)) goto bad;
/*
* DXS channel is disabled. Reports from multiple users
* that it plays at half-speed. Do not see this behaviour
* on available 8233C or when emulating 8233A register set
* on 8233C (either with or without ac97 VRA).
pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
*/
pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
via_dxs_disabled = 1;
} else if (resource_int_value(device_get_name(dev),
device_get_unit(dev), "via_dxs_disabled",
&via_dxs_disabled) == 0)
via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0;
else
via_dxs_disabled = 0;
if (via_dxs_disabled) {
via_dxs_chnum = 0;
via_sgd_chnum = 1;
} else {
int i;
if (pcm_register(dev, via, NMSGDCHANS + NDXSCHANS, NWRCHANS)) goto bad;
for (i = 0; i < NDXSCHANS; i++)
pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
for (i = 0; i < NWRCHANS; i++)
pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
via_init_sysctls(dev);
if (resource_int_value(device_get_name(dev),
device_get_unit(dev), "via_dxs_channels",
&via_dxs_chnum) != 0)
via_dxs_chnum = NDXSCHANS;
if (resource_int_value(device_get_name(dev),
device_get_unit(dev), "via_sgd_channels",
&via_sgd_chnum) != 0)
via_sgd_chnum = NMSGDCHANS;
}
if (via_dxs_chnum > NDXSCHANS)
via_dxs_chnum = NDXSCHANS;
else if (via_dxs_chnum < 0)
via_dxs_chnum = 0;
if (via_sgd_chnum > NMSGDCHANS)
via_sgd_chnum = NMSGDCHANS;
else if (via_sgd_chnum < 0)
via_sgd_chnum = 0;
if (via_dxs_chnum + via_sgd_chnum < 1) {
/* Minimalist ? */
via_dxs_chnum = 1;
via_sgd_chnum = 0;
}
if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev),
device_get_unit(dev), "via_dxs_src",
&via_dxs_src) == 0)
via->dxs_src = (via_dxs_src > 0) ? 1 : 0;
else
via->dxs_src = 0;
/* Register */
if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
goto bad;
for (i = 0; i < via_dxs_chnum; i++)
pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
for (i = 0; i < via_sgd_chnum; i++)
pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
for (i = 0; i < NWRCHANS; i++)
pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
if (via_dxs_chnum > 0)
via_init_sysctls(dev);
device_printf(dev, "<VIA DXS %sabled: DXS%s %d / SGD %d / REC %d>\n",
(via_dxs_chnum > 0) ? "En" : "Dis",
(via->dxs_src) ? "(SRC)" : "",
via_dxs_chnum, via_sgd_chnum, NWRCHANS);
pcm_setstatus(dev, status);
@ -884,6 +1014,7 @@ via_attach(device_t dev)
if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
if (via->lock) snd_mtxfree(via->lock);
if (via) free(via, M_DEVBUF);
return ENXIO;
}
@ -904,6 +1035,7 @@ via_detach(device_t dev)
bus_dma_tag_destroy(via->parent_dmat);
bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
bus_dma_tag_destroy(via->sgd_dmat);
snd_mtxfree(via->lock);
free(via, M_DEVBUF);
return 0;
}

View File

@ -86,6 +86,7 @@ struct via_info {
struct via_chinfo pch, rch;
struct via_dma_op *sgd_table;
u_int16_t codec_caps;
struct mtx *lock;
};
static u_int32_t via_fmt[] = {
@ -98,7 +99,7 @@ static u_int32_t via_fmt[] = {
static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0};
static struct pcmchan_caps via_caps = {48000, 48000, via_fmt, 0};
static u_int32_t
static __inline u_int32_t
via_rd(struct via_info *via, int regno, int size)
{
@ -115,7 +116,7 @@ via_rd(struct via_info *via, int regno, int size)
}
static void
static __inline void
via_wr(struct via_info *via, int regno, u_int32_t data, int size)
{
@ -244,6 +245,7 @@ viachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
struct via_info *via = devinfo;
struct via_chinfo *ch;
snd_mtxlock(via->lock);
if (dir == PCMDIR_PLAY) {
ch = &via->pch;
ch->base = VIA_PLAY_DMAOPS_BASE;
@ -266,9 +268,11 @@ viachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
return ch;
}
@ -286,10 +290,12 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format)
mode_set |= VIA_RPMODE_16BIT;
DEB(printf("set format: dir = %d, format=%x\n", ch->dir, format));
snd_mtxlock(via->lock);
mode = via_rd(via, ch->mode, 1);
mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO);
mode |= mode_set;
via_wr(via, ch->mode, mode, 1);
snd_mtxunlock(via->lock);
return 0;
}
@ -342,12 +348,14 @@ viachan_trigger(kobj_t obj, void *data, int go)
ado = ch->sgd_table;
DEB(printf("ado located at va=%p pa=%x\n", ado, sgd_addr));
snd_mtxlock(via->lock);
if (go == PCMTRIG_START) {
via_buildsgdt(ch);
via_wr(via, ch->base, sgd_addr, 4);
via_wr(via, ch->ctrl, VIA_RPCTRL_START, 1);
} else
via_wr(via, ch->ctrl, VIA_RPCTRL_TERMINATE, 1);
snd_mtxunlock(via->lock);
DEB(printf("viachan_trigger: go=%d\n", go));
return 0;
@ -363,11 +371,13 @@ viachan_getptr(kobj_t obj, void *data)
int ptr, base, base1, len, seg;
ado = ch->sgd_table;
snd_mtxlock(via->lock);
base1 = via_rd(via, ch->base, 4);
len = via_rd(via, ch->count, 4);
base = via_rd(via, ch->base, 4);
if (base != base1) /* Avoid race hazard */
len = via_rd(via, ch->count, 4);
snd_mtxunlock(via->lock);
DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base));
@ -417,22 +427,25 @@ static void
via_intr(void *p)
{
struct via_info *via = p;
int st;
/* DEB(printf("viachan_intr\n")); */
/* Read channel */
st = via_rd(via, VIA_PLAY_STAT, 1);
if (st & VIA_RPSTAT_INTR) {
snd_mtxlock(via->lock);
if (via_rd(via, VIA_PLAY_STAT, 1) & VIA_RPSTAT_INTR) {
via_wr(via, VIA_PLAY_STAT, VIA_RPSTAT_INTR, 1);
snd_mtxunlock(via->lock);
chn_intr(via->pch.channel);
snd_mtxlock(via->lock);
}
/* Write channel */
st = via_rd(via, VIA_RECORD_STAT, 1);
if (st & VIA_RPSTAT_INTR) {
if (via_rd(via, VIA_RECORD_STAT, 1) & VIA_RPSTAT_INTR) {
via_wr(via, VIA_RECORD_STAT, VIA_RPSTAT_INTR, 1);
snd_mtxunlock(via->lock);
chn_intr(via->rch.channel);
return;
}
snd_mtxunlock(via->lock);
}
/*
@ -468,6 +481,7 @@ via_attach(device_t dev)
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
/* Get resources */
data = pci_read_config(dev, PCIR_COMMAND, 2);
@ -521,7 +535,7 @@ via_attach(device_t dev)
via->irqid = 0;
via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!via->irq || snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih)) {
if (!via->irq || snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@ -546,8 +560,8 @@ via_attach(device_t dev)
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant, &via->parent_dmat) != 0) {
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &via->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -563,8 +577,8 @@ via_attach(device_t dev)
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/NSEGS * sizeof(struct via_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant, &via->sgd_dmat) != 0) {
/*flags*/0, /*lockfunc*/NULL,
/*lockarg*/NULL, &via->sgd_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@ -594,6 +608,7 @@ via_attach(device_t dev)
if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
if (via->lock) snd_mtxfree(via->lock);
if (via) free(via, M_DEVBUF);
return ENXIO;
}
@ -615,6 +630,7 @@ via_detach(device_t dev)
bus_dma_tag_destroy(via->parent_dmat);
bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
bus_dma_tag_destroy(via->sgd_dmat);
snd_mtxfree(via->lock);
free(via, M_DEVBUF);
return 0;
}

View File

@ -84,10 +84,8 @@ static const struct ac97mixtable_entry ac97mixtable_default[32] = {
[SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 },
[SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 },
[SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 },
#if 0
/* use igain for the mic 20dB boost */
[SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 },
#endif
[SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 },
[SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 },
[SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 },
@ -118,6 +116,11 @@ static const struct ac97_vendorid ac97vendorid[] = {
{ 0x57454300, "Winbond" },
{ 0x574d4c00, "Wolfson" },
{ 0x594d4800, "Yamaha" },
/*
* XXX This is a fluke, really! The real vendor
* should be SigmaTel, not this! This should be
* removed someday!
*/
{ 0x01408300, "Creative" },
{ 0x00000000, NULL }
};
@ -145,7 +148,9 @@ static struct ac97_codecid ac97codecid[] = {
{ 0x414c4710, 0x0f, 0, "ALC200", 0 },
{ 0x414c4740, 0x0f, 0, "ALC202", 0 },
{ 0x414c4720, 0x0f, 0, "ALC650", 0 },
{ 0x414c4752, 0x0f, 0, "ALC250", 0 },
{ 0x414c4760, 0x0f, 0, "ALC655", 0 },
{ 0x414c4770, 0x0f, 0, "ALC203", 0 },
{ 0x414c4780, 0x0f, 0, "ALC658", 0 },
{ 0x414c4790, 0x0f, 0, "ALC850", 0 },
{ 0x43525900, 0x07, 0, "CS4297", 0 },
@ -156,10 +161,14 @@ static struct ac97_codecid ac97codecid[] = {
{ 0x43525940, 0x07, 0, "CS4201", 0 },
{ 0x43525958, 0x07, 0, "CS4205", 0 },
{ 0x43525960, 0x07, 0, "CS4291A", 0 },
{ 0x434d4961, 0x00, 0, "CMI9739", 0 },
{ 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch },
{ 0x434d4941, 0x00, 0, "CMI9738", 0 },
{ 0x434d4978, 0x00, 0, "CMI9761", 0 },
{ 0x434d4982, 0x00, 0, "CMI9761", 0 },
{ 0x434d4983, 0x00, 0, "CMI9761", 0 },
{ 0x43585421, 0x00, 0, "HSD11246", 0 },
{ 0x43585428, 0x07, 0, "CX20468", 0 },
{ 0x43585430, 0x00, 0, "CX20468-21", 0 },
{ 0x44543000, 0x00, 0, "DT0398", 0 },
{ 0x454d4323, 0x00, 0, "EM28023", 0 },
{ 0x454d4328, 0x00, 0, "EM28028", 0 },
@ -192,6 +201,7 @@ static struct ac97_codecid ac97codecid[] = {
{ 0x83847658, 0x00, 0, "STAC9758/59", 0 },
{ 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */
{ 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */
{ 0x83847666, 0x00, 0, "STAC9766/67", 0 },
{ 0x53494c22, 0x00, 0, "Si3036", 0 },
{ 0x53494c23, 0x00, 0, "Si3038", 0 },
{ 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */
@ -201,6 +211,7 @@ static struct ac97_codecid ac97codecid[] = {
{ 0x54524e03, 0x07, 0, "TLV320AIC27", 0 },
{ 0x54584e20, 0x00, 0, "TLC320AD90", 0 },
{ 0x56494161, 0x00, 0, "VIA1612A", 0 },
{ 0x56494170, 0x00, 0, "VIA1617A", 0 },
{ 0x574d4c00, 0x00, 0, "WM9701A", 0 },
{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 },
{ 0x574d4c04, 0x00, 0, "WM9704Q", 0 },
@ -211,6 +222,11 @@ static struct ac97_codecid ac97codecid[] = {
{ 0x594d4800, 0x00, 0, "YMF743", 0 },
{ 0x594d4802, 0x00, 0, "YMF752", 0 },
{ 0x594d4803, 0x00, 0, "YMF753", 0 },
/*
* XXX This is a fluke, really! The real codec
* should be STAC9704, not this! This should be
* removed someday!
*/
{ 0x01408384, 0x00, 0, "EV1938", 0 },
{ 0, 0, 0, NULL, 0 }
};
@ -283,6 +299,21 @@ static char *ac97extfeature[] = {
u_int16_t
ac97_rdcd(struct ac97_info *codec, int reg)
{
if (codec->flags & AC97_F_RDCD_BUG) {
u_int16_t i[2], j = 100;
i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
while (i[0] != i[1] && j)
i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
#if 0
if (j < 100) {
device_printf(codec->dev, "%s(): Inconsistent register value at"
" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
}
#endif
return i[!(j & 1)];
}
return AC97_READ(codec->methods, codec->devinfo, reg);
}
@ -452,14 +483,16 @@ ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned
*/
snd_mtxlock(codec->lock);
if (e->mask) {
int cur = ac97_rdcd(codec, e->reg);
int cur = ac97_rdcd(codec, reg);
val |= cur & ~(mask);
}
ac97_wrcd(codec, reg, val);
snd_mtxunlock(codec->lock);
return left | (right << 8);
} else {
/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
#if 0
printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
#endif
return -1;
}
}
@ -511,6 +544,40 @@ ac97_fix_tone(struct ac97_info *codec)
}
}
static void
ac97_fix_volume(struct ac97_info *codec)
{
struct snddev_info *d = device_get_softc(codec->dev);
#if 0
/* XXX For the sake of debugging purposes */
ac97_wrcd(codec, AC97_MIX_PCM, 0);
bzero(&codec->mix[SOUND_MIXER_PCM],
sizeof(codec->mix[SOUND_MIXER_PCM]));
codec->flags |= AC97_F_SOFTVOL;
if (d)
d->flags |= SD_F_SOFTVOL;
return;
#endif
switch (codec->id) {
case 0x434d4941: /* CMI9738 */
case 0x434d4961: /* CMI9739 */
case 0x434d4978: /* CMI9761 */
case 0x434d4982: /* CMI9761 */
case 0x434d4983: /* CMI9761 */
ac97_wrcd(codec, AC97_MIX_PCM, 0);
break;
default:
return;
break;
}
bzero(&codec->mix[SOUND_MIXER_PCM],
sizeof(codec->mix[SOUND_MIXER_PCM]));
codec->flags |= AC97_F_SOFTVOL;
if (d)
d->flags |= SD_F_SOFTVOL;
}
static const char*
ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
{
@ -536,8 +603,9 @@ ac97_initmixer(struct ac97_info *codec)
const char *cname, *vname;
char desc[80];
u_int8_t model, step;
unsigned i, j, k, old;
unsigned i, j, k, bit, old;
u_int32_t id;
int reg;
snd_mtxlock(codec->lock);
codec->count = AC97_INIT(codec->methods, codec->devinfo);
@ -552,6 +620,16 @@ ac97_initmixer(struct ac97_info *codec)
ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
i = ac97_rdcd(codec, AC97_REG_RESET);
j = ac97_rdcd(codec, AC97_REG_RESET);
/*
* Let see if this codec can return consistent value.
* If not, turn on aggressive read workaround
* (STAC9704 comes in mind).
*/
if (i != j) {
codec->flags |= AC97_F_RDCD_BUG;
i = ac97_rdcd(codec, AC97_REG_RESET);
}
codec->caps = i & 0x03ff;
codec->se = (i & 0x7c00) >> 10;
@ -605,27 +683,75 @@ ac97_initmixer(struct ac97_info *codec)
}
ac97_fix_auxout(codec);
ac97_fix_tone(codec);
ac97_fix_volume(codec);
if (codec_patch)
codec_patch(codec);
for (i = 0; i < 32; i++) {
k = codec->noext? codec->mix[i].enable : 1;
if (k && (codec->mix[i].reg > 0)) {
old = ac97_rdcd(codec, codec->mix[i].reg);
ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
j = ac97_rdcd(codec, codec->mix[i].reg);
ac97_wrcd(codec, codec->mix[i].reg, old);
codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
for (k = 1; j & (1 << k); k++);
codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
reg = codec->mix[i].reg;
if (reg < 0)
reg = -reg;
if (k && reg) {
j = old = ac97_rdcd(codec, reg);
/*
* Test for mute bit (except for AC97_MIX_TONE,
* where we simply assume it as available).
*/
if (codec->mix[i].mute) {
ac97_wrcd(codec, reg, j | 0x8000);
j = ac97_rdcd(codec, reg);
} else
j |= 0x8000;
if ((j & 0x8000)) {
/*
* Test whether the control width should be
* 4, 5 or 6 bit. For 5bit register, we should
* test it whether it's really 5 or 6bit. Leave
* 4bit register alone, because sometimes an
* attempt to write past 4th bit may cause
* incorrect result especially for AC97_MIX_BEEP
* (ac97 2.3).
*/
bit = codec->mix[i].bits;
if (bit == 5)
bit++;
j = ((1 << bit) - 1) << codec->mix[i].ofs;
ac97_wrcd(codec, reg,
j | (codec->mix[i].mute ? 0x8000 : 0));
k = ac97_rdcd(codec, reg) & j;
k >>= codec->mix[i].ofs;
if (reg == AC97_MIX_TONE &&
((k & 0x0001) == 0x0000))
k >>= 1;
for (j = 0; k >> j; j++)
;
if (j != 0) {
#if 0
device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
i, k, bit, codec->mix[i].bits, j);
#endif
codec->mix[i].enable = 1;
codec->mix[i].bits = j;
} else
codec->mix[i].enable = 0;
} else
codec->mix[i].enable = 0;
ac97_wrcd(codec, reg, old);
}
/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
#if 0
printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
#endif
}
device_printf(codec->dev, "<%s>\n",
ac97_hw_desc(codec->id, vname, cname, desc));
if (bootverbose) {
if (codec->flags & AC97_F_RDCD_BUG)
device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
if (codec->flags & AC97_F_SOFTVOL)
device_printf(codec->dev, "Soft PCM volume\n");
device_printf(codec->dev, "Codec features ");
for (i = j = 0; i < 10; i++)
if (codec->caps & (1 << i))
@ -645,8 +771,16 @@ ac97_initmixer(struct ac97_info *codec)
}
}
if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
device_printf(codec->dev, "ac97 codec reports dac not ready\n");
i = 0;
while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
if (++i == 100) {
device_printf(codec->dev, "ac97 codec reports dac not ready\n");
break;
}
DELAY(1000);
}
if (bootverbose)
device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
snd_mtxunlock(codec->lock);
return 0;
}

View File

@ -81,6 +81,8 @@
#define AC97_REG_ID2 0x7e
#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)

View File

@ -46,3 +46,13 @@ void ad198x_patch(struct ac97_info* codec)
ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420);
}
void cmi9739_patch(struct ac97_info* codec)
{
/*
* Few laptops (notably ASUS W1000N) need extra register
* initialization to power up the internal speakers.
*/
ac97_wrcd(codec, AC97_REG_POWER, 0x000f);
ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000);
ac97_wrcd(codec, 0x64, 0x7110);
}

View File

@ -29,3 +29,4 @@ typedef void (*ac97_patch)(struct ac97_info*);
void ad1886_patch(struct ac97_info*);
void ad198x_patch(struct ac97_info*);
void cmi9739_patch(struct ac97_info*);

View File

@ -286,8 +286,12 @@ sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
b->fmt = fmt;
b->bps = 1;
b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0;
b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0;
if (b->fmt & AFMT_16BIT)
b->bps <<= 1;
else if (b->fmt & AFMT_24BIT)
b->bps *= 3;
else if (b->fmt & AFMT_32BIT)
b->bps <<= 2;
return 0;
}

View File

@ -107,7 +107,9 @@ chn_polltrigger(struct pcm_channel *c)
return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
} else {
amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
#if 0
lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
#endif
lim = 1;
return (amt >= lim)? 1 : 0;
}
@ -163,7 +165,7 @@ chn_sleep(struct pcm_channel *c, char *str, int timeout)
/*
* chn_dmaupdate() tracks the status of a dma transfer,
* updating pointers. It must be called at spltty().
* updating pointers.
*/
static unsigned int
@ -227,11 +229,13 @@ chn_wrfeed(struct pcm_channel *c)
unsigned int ret, amt;
CHN_LOCKASSERT(c);
/* DEB(
#if 0
DEB(
if (c->flags & CHN_F_CLOSING) {
sndbuf_dump(b, "b", 0x02);
sndbuf_dump(bs, "bs", 0x02);
}) */
})
#endif
if (c->flags & CHN_F_MAPPED)
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
@ -240,10 +244,14 @@ chn_wrfeed(struct pcm_channel *c)
KASSERT(amt <= sndbuf_getsize(bs),
("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
amt, sndbuf_getsize(bs), c->flags));
if (sndbuf_getready(bs) < amt)
ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
/*
* Possible xruns. There should be no empty space left in buffer.
*/
if (sndbuf_getfree(b) > 0)
c->xruns++;
ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
if (ret == 0 && sndbuf_getfree(b) < amt)
chn_wakeup(c);
@ -359,13 +367,17 @@ chn_rddump(struct pcm_channel *c, unsigned int cnt)
struct snd_dbuf *b = c->bufhard;
CHN_LOCKASSERT(c);
#if 0
static uint32_t kk = 0;
printf("%u: dumping %d bytes\n", ++kk, cnt);
#endif
c->xruns++;
sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
return sndbuf_dispose(b, NULL, cnt);
}
/*
* Feed new data from the read buffer. Can be called in the bottom half.
* Hence must be called at spltty.
*/
int
chn_rdfeed(struct pcm_channel *c)
@ -381,11 +393,16 @@ chn_rdfeed(struct pcm_channel *c)
sndbuf_dump(bs, "bs", 0x02);
})
#if 0
amt = sndbuf_getready(b);
if (sndbuf_getfree(bs) < amt) {
c->xruns++;
amt = sndbuf_getfree(bs);
}
#endif
amt = sndbuf_getfree(bs);
if (amt < sndbuf_getready(b))
c->xruns++;
ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
amt = sndbuf_getready(b);
@ -535,10 +552,12 @@ chn_start(struct pcm_channel *c, int force)
* fed at the first irq.
*/
if (c->direction == PCMDIR_PLAY) {
/*
* Reduce pops during playback startup.
*/
sndbuf_fillsilence(b);
if (SLIST_EMPTY(&c->children))
chn_wrfeed(c);
else
sndbuf_fillsilence(b);
}
sndbuf_setrun(b, 1);
c->xruns = 0;
@ -735,18 +754,24 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt)
r = CHANNEL_RESET(c->methods, c->devinfo);
if (fmt != 0) {
#if 0
hwspd = DSP_DEFAULT_SPEED;
/* only do this on a record channel until feederbuilder works */
if (c->direction == PCMDIR_REC)
RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
c->speed = hwspd;
#endif
hwspd = chn_getcaps(c)->minspeed;
c->speed = hwspd;
if (r == 0)
r = chn_setformat(c, fmt);
if (r == 0)
r = chn_setspeed(c, hwspd);
#if 0
if (r == 0)
r = chn_setvolume(c, 100, 100);
#endif
}
if (r == 0)
r = chn_setblocksize(c, 0, 0);
@ -886,7 +911,15 @@ chn_setvolume(struct pcm_channel *c, int left, int right)
{
CHN_LOCKASSERT(c);
/* should add a feeder for volume changing if channel returns -1 */
c->volume = (left << 8) | right;
if (left > 100)
left = 100;
if (left < 0)
left = 0;
if (right > 100)
right = 100;
if (right < 0)
right = 0;
c->volume = left | (right << 8);
return 0;
}
@ -918,7 +951,10 @@ chn_tryspeed(struct pcm_channel *c, int speed)
delta = -delta;
c->feederflags &= ~(1 << FEEDER_RATE);
if (delta > 500)
/*
* Used to be 500. It was too big!
*/
if (delta > 25)
c->feederflags |= 1 << FEEDER_RATE;
else
sndbuf_setspd(bs, sndbuf_getspd(b));
@ -951,6 +987,11 @@ chn_tryspeed(struct pcm_channel *c, int speed)
r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
out:
if (!r)
r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
sndbuf_getfmt(b));
if (!r)
sndbuf_setfmt(bs, c->format);
DEB(printf("setspeed done, r = %d\n", r));
return r;
} else
@ -1008,12 +1049,50 @@ chn_setformat(struct pcm_channel *c, u_int32_t fmt)
return r;
}
/*
* given a bufsz value, round it to a power of 2 in the min-max range
* XXX only works if min and max are powers of 2
*/
static int
round_bufsz(int bufsz, int min, int max)
{
int tmp = min * 2;
KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min));
KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max));
while (tmp <= bufsz)
tmp <<= 1;
tmp >>= 1;
if (tmp > max)
tmp = max;
return tmp;
}
/*
* set the channel's blocksize both for soft and hard buffers.
*
* blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
* that it has the same value for both bufsoft and bufhard.
* blksz == -1 computes values according to a target irq rate.
* blksz == 0 reuses previous values if available, otherwise
* behaves as for -1
*
* blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
* but should be a power of 2 for bufhard to simplify life to low
* level drivers.
* Note, for the rec channel a large blkcnt is ok,
* but for the play channel we want blksz as small as possible to keep
* the delay small, because routines in the write path always try to
* keep bufhard full.
*
* Unless we have good reason to, use the values suggested by the caller.
*/
int
chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
{
struct snd_dbuf *b = c->bufhard;
struct snd_dbuf *bs = c->bufsoft;
int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz;
int irqhz, ret, maxsz, maxsize, reqblksz;
CHN_LOCKASSERT(c);
if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
@ -1027,27 +1106,30 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
ret = 0;
DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
if (blksz == 0 || blksz == -1) {
if (blksz == -1)
if (blksz == 0 || blksz == -1) { /* let the driver choose values */
if (blksz == -1) /* delete previous values */
c->flags &= ~CHN_F_HAS_SIZE;
if (!(c->flags & CHN_F_HAS_SIZE)) {
blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
tmp = 32;
while (tmp <= blksz)
tmp <<= 1;
tmp >>= 1;
blksz = tmp;
if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
/*
* compute a base blksz according to the target irq
* rate, then round to a suitable power of 2
* in the range 16.. 2^17/2.
* Finally compute a suitable blkcnt.
*/
blksz = round_bufsz( (sndbuf_getbps(bs) *
sndbuf_getspd(bs)) / chn_targetirqrate,
16, CHN_2NDBUFMAXSIZE / 2);
blkcnt = CHN_2NDBUFMAXSIZE / blksz;
RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
} else {
} else { /* use previously defined value */
blkcnt = sndbuf_getblkcnt(bs);
blksz = sndbuf_getblksz(bs);
DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
}
} else {
/*
* use supplied values if reasonable. Note that here we
* might have blksz which is not a power of 2 if the
* ioctl() to compute it allows such values.
*/
ret = EINVAL;
if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
goto out;
@ -1056,27 +1138,29 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
}
reqblksz = blksz;
if (reqblksz < sndbuf_getbps(bs))
reqblksz = sndbuf_getbps(bs);
if (reqblksz % sndbuf_getbps(bs))
reqblksz -= reqblksz % sndbuf_getbps(bs);
/* adjust for different hw format/speed */
/*
* Now compute the approx irq rate for the given (soft) blksz,
* reduce to the acceptable range and compute a corresponding blksz
* for the hard buffer. Then set the channel's blocksize and
* corresponding hardbuf value. The number of blocks used should
* be set by the device-specific routine. In fact, even the
* call to sndbuf_setblksz() should not be here! XXX
*/
irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
RANGE(irqhz, 16, 512);
tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz;
/* round down to 2^x */
blksz = 32;
while (blksz <= tmpblksz)
blksz <<= 1;
blksz >>= 1;
/* round down to fit hw buffer size */
if (sndbuf_getmaxsize(b) > 0)
RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
else
/* virtual channels don't appear to allocate bufhard */
RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
maxsz = sndbuf_getmaxsize(b);
if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
maxsz = CHN_2NDBUFMAXSIZE;
blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
16, maxsz / 2);
/* Increase the size of bufsoft if before increasing bufhard. */
maxsize = sndbuf_getsize(b);
@ -1107,9 +1191,6 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
goto out1;
}
irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
chn_resetbuf(c);
out1:
KASSERT(sndbuf_getsize(bs) == 0 ||
@ -1119,6 +1200,24 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
blksz, maxsize, blkcnt));
out:
c->flags &= ~CHN_F_SETBLOCKSIZE;
#if 0
if (1) {
static uint32_t kk = 0;
printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
sndbuf_getbps(b),
sndbuf_getspd(b), sndbuf_getfmt(b),
sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
sndbuf_getbps(bs),
sndbuf_getspd(bs), sndbuf_getfmt(bs));
if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
sndbuf_getblksz(b) % sndbuf_getbps(b) ||
sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
sndbuf_getblksz(b) % sndbuf_getbps(b)) {
printf("%u: bps/blksz alignment screwed!\n", kk);
}
}
#endif
return ret;
}
@ -1176,7 +1275,9 @@ chn_getformats(struct pcm_channel *c)
/* report software-supported formats */
if (report_soft_formats)
fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
return fmts;
@ -1187,7 +1288,7 @@ chn_buildfeeder(struct pcm_channel *c)
{
struct feeder_class *fc;
struct pcm_feederdesc desc;
u_int32_t tmp[2], type, flags, hwfmt;
u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
int err;
CHN_LOCKASSERT(c);
@ -1208,8 +1309,13 @@ chn_buildfeeder(struct pcm_channel *c)
}
c->feeder->desc->out = c->format;
} else {
desc.type = FEEDER_MIXER;
desc.in = 0;
if (c->flags & CHN_F_HAS_VCHAN) {
desc.type = FEEDER_MIXER;
desc.in = 0;
} else {
DEB(printf("can't decide which feeder type to use!\n"));
return EOPNOTSUPP;
}
desc.out = c->format;
desc.flags = 0;
fc = feeder_getclass(&desc);
@ -1226,7 +1332,14 @@ chn_buildfeeder(struct pcm_channel *c)
return err;
}
}
c->feederflags &= ~(1 << FEEDER_VOLUME);
if (c->direction == PCMDIR_PLAY &&
!(c->flags & CHN_F_VIRTUAL) &&
c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) &&
c->parentsnddev->mixer_dev)
c->feederflags |= 1 << FEEDER_VOLUME;
flags = c->feederflags;
fmtlist = chn_getcaps(c)->fmtlist;
DEB(printf("feederflags %x\n", flags));
@ -1245,7 +1358,9 @@ chn_buildfeeder(struct pcm_channel *c)
return EOPNOTSUPP;
}
if (c->feeder->desc->out != fc->desc->in) {
if ((type == FEEDER_RATE &&
!fmtvalid(fc->desc->in, fmtlist))
|| c->feeder->desc->out != fc->desc->in) {
DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
tmp[0] = fc->desc->in;
tmp[1] = 0;
@ -1267,31 +1382,42 @@ chn_buildfeeder(struct pcm_channel *c)
}
}
if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
if (fmtvalid(c->feeder->desc->out, fmtlist)
&& !(c->direction == PCMDIR_REC &&
c->format != c->feeder->desc->out))
hwfmt = c->feeder->desc->out;
} else {
else {
if (c->direction == PCMDIR_REC) {
tmp[0] = c->format;
tmp[1] = 0;
hwfmt = chn_fmtchain(c, tmp);
} else {
#if 0
u_int32_t *x = chn_getcaps(c)->fmtlist;
printf("acceptable formats for %s:\n", c->name);
while (*x) {
printf("[0x%8x] ", *x);
x++;
}
#endif
hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
}
} else
hwfmt = chn_fmtchain(c, fmtlist);
}
if (hwfmt == 0)
if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
DEB(printf("Invalid hardware format: 0x%x\n", hwfmt));
return ENODEV;
}
sndbuf_setfmt(c->bufhard, hwfmt);
if ((flags & (1 << FEEDER_VOLUME))) {
int vol = 100 | (100 << 8);
CHN_UNLOCK(c);
/*
* XXX This is ugly! The way mixer subs being so secretive
* about its own internals force us to use this silly
* monkey trick.
*/
if (mixer_ioctl(c->parentsnddev->mixer_dev,
MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0)
device_printf(c->dev, "Soft Volume: Failed to read default value\n");
CHN_LOCK(c);
chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f);
}
return 0;
}

View File

@ -137,10 +137,13 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
#define CHN_F_DEAD 0x00020000
#define CHN_F_BADSETTING 0x00040000
#define CHN_F_SETBLOCKSIZE 0x00080000
#define CHN_F_HAS_VCHAN 0x00100000
#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | CHN_F_VIRTUAL)
#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \
CHN_F_HAS_VCHAN | CHN_F_VIRTUAL)
#define CHN_N_RATE 0x00000001
#define CHN_N_FORMAT 0x00000002

View File

@ -168,13 +168,11 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct pcm_channel *rdch, *wrch;
struct snddev_info *d;
intrmask_t s;
u_int32_t fmt;
int devtype;
int rdref;
int error;
s = spltty();
d = dsp_get_info(i_dev);
devtype = PCMDEV(i_dev);
@ -199,7 +197,6 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
case SND_DEV_DSPREC:
fmt = AFMT_U8;
if (mode & FWRITE) {
splx(s);
return EINVAL;
}
break;
@ -219,7 +216,6 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
/* we're a simplex device and already open, no go */
pcm_unlock(d);
splx(s);
return EBUSY;
}
@ -229,7 +225,6 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
* the opener wants; we can't handle this.
*/
pcm_unlock(d);
splx(s);
return EBUSY;
}
@ -241,25 +236,23 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
*/
if (flags & FREAD) {
/* open for read */
pcm_unlock(d);
if (devtype == SND_DEV_DSPREC)
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
else
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
if (!rdch) {
/* no channel available, exit */
pcm_unlock(d);
splx(s);
return EBUSY;
}
/* got a channel, already locked for us */
if (chn_reset(rdch, fmt)) {
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
pcm_unlock(d);
splx(s);
return ENODEV;
}
pcm_lock(d);
if (flags & O_NONBLOCK)
rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
@ -272,6 +265,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
if (flags & FWRITE) {
/* open for write */
pcm_unlock(d);
wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
error = 0;
@ -280,6 +274,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
else if (chn_reset(wrch, fmt))
error = ENODEV;
pcm_lock(d);
if (error != 0) {
if (wrch) {
/*
@ -299,7 +294,6 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
}
pcm_unlock(d);
splx(s);
return error;
}
@ -313,7 +307,6 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
i_dev->si_drv2 = wrch;
pcm_unlock(d);
splx(s);
return 0;
}
@ -322,12 +315,9 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct pcm_channel *rdch, *wrch;
struct snddev_info *d;
intrmask_t s;
int refs;
s = spltty();
d = dsp_get_info(i_dev);
pcm_lock(d);
rdch = i_dev->si_drv1;
wrch = i_dev->si_drv2;
@ -349,6 +339,8 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
*/
if ((rdch || wrch) && refs == 0) {
pcm_lock(d);
if (pcm_getfakechan(d))
pcm_getfakechan(d)->flags = 0;
@ -380,9 +372,7 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
} else
pcm_unlock(d);
splx(s);
}
return 0;
}
@ -390,10 +380,8 @@ static int
dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
{
struct pcm_channel *rdch, *wrch;
intrmask_t s;
int ret;
s = spltty();
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
KASSERT(rdch, ("dsp_read: nonexistant channel"));
@ -401,7 +389,6 @@ dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
splx(s);
return EINVAL;
}
if (!(rdch->flags & CHN_F_RUNNING))
@ -409,7 +396,6 @@ dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
ret = chn_read(rdch, buf);
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
splx(s);
return ret;
}
@ -417,10 +403,8 @@ static int
dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
{
struct pcm_channel *rdch, *wrch;
intrmask_t s;
int ret;
s = spltty();
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
KASSERT(wrch, ("dsp_write: nonexistant channel"));
@ -428,7 +412,6 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
splx(s);
return EINVAL;
}
if (!(wrch->flags & CHN_F_RUNNING))
@ -436,7 +419,6 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
ret = chn_write(wrch, buf);
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
splx(s);
return ret;
}
@ -445,7 +427,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
{
struct pcm_channel *chn, *rdch, *wrch;
struct snddev_info *d;
intrmask_t s;
int kill;
int ret = 0, *arg_i = (int *)arg, tmp;
@ -458,7 +439,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
if (IOCGROUP(cmd) == 'M')
return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td);
s = spltty();
getchns(i_dev, &rdch, &wrch, 0);
kill = 0;
@ -468,7 +448,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
kill |= 2;
if (kill == 3) {
relchns(i_dev, rdch, wrch, 0);
splx(s);
return EINVAL;
}
if (kill & 1)
@ -533,9 +512,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
{
snd_chan_param *p = (snd_chan_param *)arg;
if (cmd == AIOSFMT &&
((p->play_format != 0 && p->play_rate == 0) ||
(p->rec_format != 0 && p->rec_rate == 0))) {
ret = EINVAL;
break;
}
if (wrch) {
CHN_LOCK(wrch);
if (cmd == AIOSFMT) {
if (cmd == AIOSFMT && p->play_format != 0) {
chn_setformat(wrch, p->play_format);
chn_setspeed(wrch, p->play_rate);
}
@ -548,7 +533,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
}
if (rdch) {
CHN_LOCK(rdch);
if (cmd == AIOSFMT) {
if (cmd == AIOSFMT && p->rec_format != 0) {
chn_setformat(rdch, p->rec_format);
chn_setspeed(rdch, p->rec_rate);
}
@ -826,6 +811,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
u_int32_t fragln = (*arg_i) & 0x0000ffff;
u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
u_int32_t fragsz;
u_int32_t r_maxfrags, r_fragsz;
RANGE(fragln, 4, 16);
fragsz = 1 << fragln;
@ -841,9 +827,12 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
if (rdch) {
CHN_LOCK(rdch);
ret = chn_setblocksize(rdch, maxfrags, fragsz);
maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
fragsz = sndbuf_getblksz(rdch->bufsoft);
r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
r_fragsz = sndbuf_getblksz(rdch->bufsoft);
CHN_UNLOCK(rdch);
} else {
r_maxfrags = maxfrags;
r_fragsz = fragsz;
}
if (wrch && ret == 0) {
CHN_LOCK(wrch);
@ -851,6 +840,9 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
fragsz = sndbuf_getblksz(wrch->bufsoft);
CHN_UNLOCK(wrch);
} else { /* use whatever came from the read channel */
maxfrags = r_maxfrags;
fragsz = r_fragsz;
}
fragln = 0;
@ -887,7 +879,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
/* XXX abusive DMA update: chn_wrupdate(wrch); */
a->bytes = sndbuf_getfree(bs);
a->fragments = a->bytes / sndbuf_getblksz(bs);
a->fragstotal = sndbuf_getblkcnt(bs);
@ -904,7 +896,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
struct snd_dbuf *bs = rdch->bufsoft;
CHN_LOCK(rdch);
chn_rdupdate(rdch);
/* XXX abusive DMA update: chn_rdupdate(rdch); */
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
a->ptr = sndbuf_getreadyptr(bs);
@ -922,7 +914,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
/* XXX abusive DMA update: chn_wrupdate(wrch); */
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
a->ptr = sndbuf_getreadyptr(bs);
@ -942,7 +934,16 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SOUND_PCM_READ_BITS:
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
if (chn->format & AFMT_8BIT)
*arg_i = 8;
else if (chn->format & AFMT_16BIT)
*arg_i = 16;
else if (chn->format & AFMT_24BIT)
*arg_i = 24;
else if (chn->format & AFMT_32BIT)
*arg_i = 32;
else
ret = EINVAL;
CHN_UNLOCK(chn);
break;
@ -989,7 +990,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
/* XXX abusive DMA update: chn_wrupdate(wrch); */
*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
CHN_UNLOCK(wrch);
} else
@ -1030,7 +1031,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
break;
}
relchns(i_dev, rdch, wrch, 0);
splx(s);
return ret;
}
@ -1038,10 +1038,8 @@ static int
dsp_poll(struct cdev *i_dev, int events, struct thread *td)
{
struct pcm_channel *wrch = NULL, *rdch = NULL;
intrmask_t s;
int ret, e;
s = spltty();
ret = 0;
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
@ -1057,7 +1055,6 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td)
}
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
splx(s);
return ret;
}
@ -1065,12 +1062,10 @@ static int
dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
{
struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
intrmask_t s;
if (nprot & PROT_EXEC)
return -1;
s = spltty();
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
#if 0
/*
@ -1083,7 +1078,6 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
} else if (rdch && (nprot & PROT_READ)) {
c = rdch;
} else {
splx(s);
return -1;
}
#else
@ -1092,13 +1086,11 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
if (c == NULL) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
splx(s);
return -1;
}
if (offset >= sndbuf_getsize(c->bufsoft)) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
splx(s);
return -1;
}
@ -1108,7 +1100,6 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
*paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
splx(s);
return 0;
}
@ -1173,7 +1164,7 @@ dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
panic("Unknown devtype %d", devtype);
}
if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
*dev = pdev;
dev_ref(*dev);
return;

View File

@ -29,6 +29,10 @@
SND_DECLARE_FILE("$FreeBSD$");
static u_int32_t fk_fmt[] = {
AFMT_MU_LAW,
AFMT_STEREO | AFMT_MU_LAW,
AFMT_A_LAW,
AFMT_STEREO | AFMT_A_LAW,
AFMT_U8,
AFMT_STEREO | AFMT_U8,
AFMT_S8,
@ -41,6 +45,22 @@ static u_int32_t fk_fmt[] = {
AFMT_STEREO | AFMT_S16_BE,
AFMT_U16_BE,
AFMT_STEREO | AFMT_U16_BE,
AFMT_S24_LE,
AFMT_STEREO | AFMT_S24_LE,
AFMT_U24_LE,
AFMT_STEREO | AFMT_U24_LE,
AFMT_S24_BE,
AFMT_STEREO | AFMT_S24_BE,
AFMT_U24_BE,
AFMT_STEREO | AFMT_U24_BE,
AFMT_S32_LE,
AFMT_STEREO | AFMT_S32_LE,
AFMT_U32_LE,
AFMT_STEREO | AFMT_U32_LE,
AFMT_S32_BE,
AFMT_STEREO | AFMT_S32_BE,
AFMT_U32_BE,
AFMT_STEREO | AFMT_U32_BE,
0
};
static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0};
@ -120,6 +140,12 @@ fkchan_setup(device_t dev)
c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK);
c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK);
c->parentsnddev = d;
/*
* Fake channel is such a blessing in disguise. Using this,
* we can keep track prefered virtual channel speed without
* querying kernel hint repetitively (see vchan_create / vchan.c).
*/
c->speed = 0;
snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
return c;

View File

@ -320,7 +320,10 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
i = 0;
best = 0;
bestmax = 100;
while (from[i] != 0) {
while (from[i] != 0)
i++;
while (i > 0) {
i--;
c->feeder->desc->out = from[i];
try = NULL;
max = 0;
@ -338,7 +341,6 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
try = try->source;
feeder_destroy(del);
}
i++;
}
if (best == 0)
return 0;
@ -371,7 +373,16 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
printf("%s [%d]\n", try->class->name, try->desc->idx);
#endif
return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
if (c->direction == PCMDIR_REC) {
try = c->feeder;
while (try != NULL) {
if (try->desc->type == FEEDER_ROOT)
return try->desc->out;
try = try->source;
}
return best;
} else
return c->feeder->desc->out;
}
void

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@ struct snd_mixer {
int hwvol_muted;
int hwvol_mixer;
int hwvol_step;
device_t dev;
u_int32_t hwvol_mute_level;
u_int32_t devs;
u_int32_t recdevs;
@ -60,6 +61,7 @@ static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_LINE] = 75,
[SOUND_MIXER_MIC] = 0,
[SOUND_MIXER_CD] = 75,
[SOUND_MIXER_IGAIN] = 0,
[SOUND_MIXER_LINE1] = 75,
[SOUND_MIXER_VIDEO] = 75,
[SOUND_MIXER_RECLEV] = 0,
@ -112,6 +114,7 @@ mixer_lookup(char *devname)
static int
mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
{
struct snddev_info *d;
unsigned l, r;
int v;
@ -121,9 +124,34 @@ mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
l = min((lev & 0x00ff), 100);
r = min(((lev & 0xff00) >> 8), 100);
v = MIXER_SET(mixer, dev, l, r);
if (v < 0)
return -1;
d = device_get_softc(mixer->dev);
if (dev == SOUND_MIXER_PCM && d &&
(d->flags & SD_F_SOFTVOL)) {
struct snddev_channel *sce;
struct pcm_channel *ch;
#ifdef USING_MUTEX
int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0;
if (locked)
snd_mtxunlock(mixer->lock);
#endif
SLIST_FOREACH(sce, &d->channels, link) {
ch = sce->channel;
CHN_LOCK(ch);
if (ch->direction == PCMDIR_PLAY &&
(ch->feederflags & (1 << FEEDER_VOLUME)))
chn_setvolume(ch, l, r);
CHN_UNLOCK(ch);
}
#ifdef USING_MUTEX
if (locked)
snd_mtxlock(mixer->lock);
#endif
} else {
v = MIXER_SET(mixer, dev, l, r);
if (v < 0)
return -1;
}
mixer->level[dev] = l | (r << 8);
return 0;
@ -156,6 +184,9 @@ mixer_getrecsrc(struct snd_mixer *mixer)
void
mix_setdevs(struct snd_mixer *m, u_int32_t v)
{
struct snddev_info *d = device_get_softc(m->dev);
if (d && (d->flags & SD_F_SOFTVOL))
v |= SOUND_MASK_PCM;
m->devs = v;
}
@ -198,6 +229,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
m->type = cls->name;
m->devinfo = devinfo;
m->busy = 0;
m->dev = dev;
if (MIXER_INIT(m))
goto bad;
@ -397,16 +429,13 @@ static int
mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct snd_mixer *m;
intrmask_t s;
m = i_dev->si_drv1;
s = spltty();
snd_mtxlock(m->lock);
m->busy++;
snd_mtxunlock(m->lock);
splx(s);
return 0;
}
@ -414,21 +443,17 @@ static int
mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct snd_mixer *m;
intrmask_t s;
m = i_dev->si_drv1;
s = spltty();
snd_mtxlock(m->lock);
if (!m->busy) {
snd_mtxunlock(m->lock);
splx(s);
return EBADF;
}
m->busy--;
snd_mtxunlock(m->lock);
splx(s);
return 0;
}
@ -436,15 +461,13 @@ int
mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
{
struct snd_mixer *m;
intrmask_t s;
int ret, *arg_i = (int *)arg;
int v = -1, j = cmd & 0xff;
m = i_dev->si_drv1;
if (!m->busy)
if (mode != -1 && !m->busy)
return EBADF;
s = spltty();
snd_mtxlock(m->lock);
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
if (j == SOUND_MIXER_RECSRC)
@ -452,7 +475,6 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
else
ret = mixer_set(m, j, *arg_i);
snd_mtxunlock(m->lock);
splx(s);
return (ret == 0)? 0 : ENXIO;
}
@ -480,7 +502,6 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
return (v != -1)? 0 : ENXIO;
}
snd_mtxunlock(m->lock);
splx(s);
return ENXIO;
}
@ -495,7 +516,7 @@ mixer_clone(void *arg, struct ucred *cred, char *name, int namelen,
return;
if (strcmp(name, "mixer") == 0) {
sd = devclass_get_softc(pcm_devclass, snd_unit);
if (sd != NULL) {
if (sd != NULL && sd->mixer_dev != NULL) {
*dev = sd->mixer_dev;
dev_ref(*dev);
}

View File

@ -84,20 +84,17 @@ static int sndstat_prepare(struct sbuf *s);
static int
sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
{
intrmask_t s;
int error, verbose;
verbose = sndstat_verbose;
error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
if (error == 0 && req->newptr != NULL) {
s = spltty();
sx_xlock(&sndstat_lock);
if (verbose < 0 || verbose > 3)
error = EINVAL;
else
sndstat_verbose = verbose;
sx_xunlock(&sndstat_lock);
splx(s);
}
return error;
}
@ -107,19 +104,15 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
static int
sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
intrmask_t s;
int error;
s = spltty();
sx_xlock(&sndstat_lock);
if (sndstat_isopen) {
sx_xunlock(&sndstat_lock);
splx(s);
return EBUSY;
}
sndstat_isopen = 1;
sx_xunlock(&sndstat_lock);
splx(s);
if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
error = ENXIO;
goto out;
@ -128,11 +121,9 @@ sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
out:
if (error) {
s = spltty();
sx_xlock(&sndstat_lock);
sndstat_isopen = 0;
sx_xunlock(&sndstat_lock);
splx(s);
}
return (error);
}
@ -140,34 +131,26 @@ sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
static int
sndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
intrmask_t s;
s = spltty();
sx_xlock(&sndstat_lock);
if (!sndstat_isopen) {
sx_xunlock(&sndstat_lock);
splx(s);
return EBADF;
}
sbuf_delete(&sndstat_sbuf);
sndstat_isopen = 0;
sx_xunlock(&sndstat_lock);
splx(s);
return 0;
}
static int
sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
{
intrmask_t s;
int l, err;
s = spltty();
sx_xlock(&sndstat_lock);
if (!sndstat_isopen) {
sx_xunlock(&sndstat_lock);
splx(s);
return EBADF;
}
l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
@ -175,7 +158,6 @@ sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
sndstat_bufptr += l;
sx_xunlock(&sndstat_lock);
splx(s);
return err;
}
@ -194,10 +176,35 @@ sndstat_find(int type, int unit)
return NULL;
}
int
sndstat_acquire(void)
{
sx_xlock(&sndstat_lock);
if (sndstat_isopen) {
sx_xunlock(&sndstat_lock);
return EBUSY;
}
sndstat_isopen = 1;
sx_xunlock(&sndstat_lock);
return 0;
}
int
sndstat_release(void)
{
sx_xlock(&sndstat_lock);
if (!sndstat_isopen) {
sx_xunlock(&sndstat_lock);
return EBADF;
}
sndstat_isopen = 0;
sx_xunlock(&sndstat_lock);
return 0;
}
int
sndstat_register(device_t dev, char *str, sndstat_handler handler)
{
intrmask_t s;
struct sndstat_entry *ent;
const char *devtype;
int type, unit;
@ -228,14 +235,12 @@ sndstat_register(device_t dev, char *str, sndstat_handler handler)
ent->unit = unit;
ent->handler = handler;
s = spltty();
sx_xlock(&sndstat_lock);
SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
if (type == SS_TYPE_MODULE)
sndstat_files++;
sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
sx_xunlock(&sndstat_lock);
splx(s);
return 0;
}
@ -249,23 +254,19 @@ sndstat_registerfile(char *str)
int
sndstat_unregister(device_t dev)
{
intrmask_t s;
struct sndstat_entry *ent;
s = spltty();
sx_xlock(&sndstat_lock);
SLIST_FOREACH(ent, &sndstat_devlist, link) {
if (ent->dev == dev) {
SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
sx_xunlock(&sndstat_lock);
splx(s);
free(ent, M_DEVBUF);
return 0;
}
}
sx_xunlock(&sndstat_lock);
splx(s);
return ENXIO;
}
@ -273,24 +274,20 @@ sndstat_unregister(device_t dev)
int
sndstat_unregisterfile(char *str)
{
intrmask_t s;
struct sndstat_entry *ent;
s = spltty();
sx_xlock(&sndstat_lock);
SLIST_FOREACH(ent, &sndstat_devlist, link) {
if (ent->dev == NULL && ent->str == str) {
SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
sndstat_files--;
sx_xunlock(&sndstat_lock);
splx(s);
free(ent, M_DEVBUF);
return 0;
}
}
sx_xunlock(&sndstat_lock);
splx(s);
return ENXIO;
}
@ -353,13 +350,9 @@ sndstat_init(void)
static int
sndstat_uninit(void)
{
intrmask_t s;
s = spltty();
sx_xlock(&sndstat_lock);
if (sndstat_isopen) {
sx_xunlock(&sndstat_lock);
splx(s);
return EBUSY;
}
@ -367,18 +360,11 @@ sndstat_uninit(void)
destroy_dev(sndstat_dev);
sndstat_dev = 0;
splx(s);
sx_xunlock(&sndstat_lock);
sx_destroy(&sndstat_lock);
return 0;
}
int
sndstat_busy(void)
{
return (sndstat_isopen);
}
static void
sndstat_sysinit(void *p)
{

View File

@ -166,8 +166,6 @@ pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
struct snddev_channel *sce;
int err;
snd_mtxassert(d->lock);
/* scan for a free channel */
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
@ -189,7 +187,8 @@ pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if (!SLIST_EMPTY(&c->children)) {
if ((c->flags & CHN_F_HAS_VCHAN) &&
!SLIST_EMPTY(&c->children)) {
err = vchan_create(c);
CHN_UNLOCK(c);
if (!err)
@ -246,45 +245,64 @@ pcm_inprog(struct snddev_info *d, int delta)
static void
pcm_setmaxautovchans(struct snddev_info *d, int num)
{
struct pcm_channel *c;
struct pcm_channel *c, *ch;
struct snddev_channel *sce;
int err, done;
/*
* XXX WOAH... NEED SUPER CLEANUP!!!
* Robust, yet confusing. Understanding these will
* cause your brain spinning like a Doki Doki Dynamo.
*/
if (num > 0 && d->vchancount == 0) {
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
if ((c->direction == PCMDIR_PLAY) &&
!(c->flags & CHN_F_BUSY) &&
SLIST_EMPTY(&c->children)) {
c->flags |= CHN_F_BUSY;
err = vchan_create(c);
if (err) {
c->flags &= ~CHN_F_BUSY;
CHN_UNLOCK(c);
device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
} else
CHN_UNLOCK(c);
}
CHN_UNLOCK(c);
return;
}
CHN_UNLOCK(c);
}
return;
}
if (num == 0 && d->vchancount > 0) {
done = 0;
while (!done) {
done = 1;
/*
* XXX Keep retrying...
*/
for (done = 0; done < 1024; done++) {
ch = NULL;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
done = 0;
snd_mtxlock(d->lock);
err = vchan_destroy(c);
snd_mtxunlock(d->lock);
if (err)
device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
break; /* restart */
CHN_LOCK(c);
if (c->direction == PCMDIR_PLAY &&
!(c->flags & CHN_F_BUSY) &&
(c->flags & CHN_F_VIRTUAL)) {
ch = c;
break;
}
CHN_UNLOCK(c);
}
if (ch != NULL) {
CHN_UNLOCK(ch);
snd_mtxlock(d->lock);
err = vchan_destroy(ch);
if (err)
device_printf(d->dev, "vchan_destroy(%s) == %d\n",
ch->name, err);
snd_mtxunlock(d->lock);
} else
return;
}
return;
}
}
@ -327,7 +345,11 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
d = devclass_get_softc(pcm_devclass, i);
if (!d)
continue;
pcm_setmaxautovchans(d, v);
if (d->flags & SD_F_AUTOVCHAN) {
if (pcm_inprog(d, 1) == 1)
pcm_setmaxautovchans(d, v);
pcm_inprog(d, -1);
}
}
}
snd_maxautovchans = v;
@ -449,11 +471,37 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
if (SLIST_EMPTY(&d->channels)) {
SLIST_INSERT_HEAD(&d->channels, sce, link);
} else {
/*
* Micro optimization, channel ordering:
* hw,hw,hw,vch,vch,vch,rec
*/
after = NULL;
SLIST_FOREACH(tmp, &d->channels, link) {
after = tmp;
if (ch->flags & CHN_F_VIRTUAL) {
/* virtual channel to the end */
SLIST_FOREACH(tmp, &d->channels, link) {
if (tmp->channel->direction == PCMDIR_REC)
break;
after = tmp;
}
} else {
if (ch->direction == PCMDIR_REC) {
SLIST_FOREACH(tmp, &d->channels, link) {
after = tmp;
}
} else {
SLIST_FOREACH(tmp, &d->channels, link) {
if (tmp->channel->direction == PCMDIR_REC)
break;
if (!(tmp->channel->flags & CHN_F_VIRTUAL))
after = tmp;
}
}
}
if (after == NULL) {
SLIST_INSERT_HEAD(&d->channels, sce, link);
} else {
SLIST_INSERT_AFTER(after, sce, link);
}
SLIST_INSERT_AFTER(after, sce, link);
}
snd_mtxunlock(d->lock);
sce->dsp_devt= make_dev(&dsp_cdevsw,
@ -506,10 +554,10 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
gotit:
SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
if (ch->direction == PCMDIR_REC)
d->reccount--;
else if (ch->flags & CHN_F_VIRTUAL)
if (ch->flags & CHN_F_VIRTUAL)
d->vchancount--;
else if (ch->direction == PCMDIR_REC)
d->reccount--;
else
d->playcount--;
@ -654,7 +702,14 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
#if 0
/*
* d->flags should be cleared by the allocator of the softc.
* We cannot clear this field here because several devices set
* this flag before calling pcm_register().
*/
d->flags = 0;
#endif
d->dev = dev;
d->devinfo = devinfo;
d->devcount = 0;
@ -663,7 +718,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
d->vchancount = 0;
d->inprog = 0;
SLIST_INIT(&d->channels);
SLIST_INIT(&d->channels);
if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
@ -684,10 +738,10 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
#endif
if (numplay > 0)
if (numplay > 0) {
vchan_initsys(dev);
if (numplay == 1)
d->flags |= SD_F_AUTOVCHAN;
}
sndstat_register(dev, d->status, sndstat_prepare_pcm);
return 0;
@ -703,24 +757,25 @@ pcm_unregister(device_t dev)
struct snddev_channel *sce;
struct pcm_channel *ch;
if (sndstat_acquire() != 0) {
device_printf(dev, "unregister: sndstat busy\n");
return EBUSY;
}
snd_mtxlock(d->lock);
if (d->inprog) {
device_printf(dev, "unregister: operation in progress\n");
snd_mtxunlock(d->lock);
sndstat_release();
return EBUSY;
}
if (sndstat_busy() != 0) {
device_printf(dev, "unregister: sndstat busy\n");
snd_mtxunlock(d->lock);
return EBUSY;
}
SLIST_FOREACH(sce, &d->channels, link) {
ch = sce->channel;
if (ch->refcount > 0) {
device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
snd_mtxunlock(d->lock);
sndstat_release();
return EBUSY;
}
}
@ -728,6 +783,7 @@ pcm_unregister(device_t dev)
if (mixer_uninit(dev)) {
device_printf(dev, "unregister: mixer busy\n");
snd_mtxunlock(d->lock);
sndstat_release();
return EBUSY;
}
@ -752,9 +808,10 @@ pcm_unregister(device_t dev)
chn_kill(d->fakechan);
fkchan_kill(d->fakechan);
sndstat_unregister(dev);
snd_mtxunlock(d->lock);
snd_mtxfree(d->lock);
sndstat_unregister(dev);
sndstat_release();
return 0;
}
@ -825,9 +882,25 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
if (c->direction == PCMDIR_REC)
sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
#if 0
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),
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
sndbuf_getblkcnt(c->bufhard),
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
sndbuf_getblkcnt(c->bufsoft));
#endif
else
sbuf_printf(s, "underruns %d, ready %d",
c->xruns, sndbuf_getready(c->bufsoft));
#if 0
sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
c->xruns, sndbuf_getready(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));
#endif
sbuf_printf(s, "\n\t");
}
@ -842,7 +915,8 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
if (f->desc->type == FEEDER_RATE)
sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
f->desc->type == FEEDER_VOLUME)
sbuf_printf(s, "(0x%08x)", f->desc->out);
sbuf_printf(s, " -> ");
f = f->parent;
@ -865,26 +939,31 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
struct snddev_info *d;
struct snddev_channel *sce;
struct pcm_channel *c;
int err, newcnt, cnt, busy;
int x;
int err, newcnt, cnt;
/*
* XXX WOAH... NEED SUPER CLEANUP!!!
* Robust, yet confusing. Understanding these will
* cause your brain spinning like a Doki Doki Dynamo.
*/
d = oidp->oid_arg1;
x = pcm_inprog(d, 1);
if (x != 1) {
if (!(d->flags & SD_F_AUTOVCHAN)) {
pcm_inprog(d, -1);
return EINPROGRESS;
return EINVAL;
}
busy = 0;
cnt = 0;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
cnt++;
if (c->flags & CHN_F_BUSY)
busy++;
if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
/* Better safe than sorry */
CHN_UNLOCK(c);
return EBUSY;
}
}
CHN_UNLOCK(c);
}
@ -895,9 +974,12 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
if (err == 0 && req->newptr != NULL) {
if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
pcm_inprog(d, -1);
if (newcnt < 0 || newcnt > SND_MAXVCHANS)
return E2BIG;
if (pcm_inprog(d, 1) != 1) {
pcm_inprog(d, -1);
return EINPROGRESS;
}
if (newcnt > cnt) {
@ -936,22 +1018,15 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
if (err == 0)
cnt++;
}
if (SLIST_EMPTY(&c->children))
c->flags &= ~CHN_F_BUSY;
CHN_UNLOCK(c);
} else if (newcnt < cnt) {
if (busy > newcnt) {
printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
pcm_inprog(d, -1);
return EBUSY;
}
snd_mtxlock(d->lock);
while (err == 0 && newcnt < cnt) {
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
if (c->direction == PCMDIR_PLAY &&
(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
goto remok;
CHN_UNLOCK(c);
@ -967,8 +1042,8 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
}
snd_mtxunlock(d->lock);
}
pcm_inprog(d, -1);
}
pcm_inprog(d, -1);
return err;
}
#endif

View File

@ -130,7 +130,8 @@ currently minor = (channel << 16) + (unit << 4) + dev
#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
#define SD_F_SIMPLEX 0x00000001
#define SD_F_AUTOVCHAN 0x00000002
#define SD_F_AUTOVCHAN 0x00000002
#define SD_F_SOFTVOL 0x00000004
#define SD_F_PRIO_RD 0x10000000
#define SD_F_PRIO_WR 0x20000000
#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
@ -144,10 +145,13 @@ currently minor = (channel << 16) + (unit << 4) + dev
/* 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)
#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
#define AFMT_8BIT (AFMT_U8 | AFMT_S8)
#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
#define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8)
#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \
AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \
AFMT_S16_BE | AFMT_U16_BE)
struct pcm_channel *fkchan_setup(device_t dev);
int fkchan_kill(struct pcm_channel *c);
@ -235,11 +239,12 @@ void snd_mtxassert(void *m);
int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS);
typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose);
int sndstat_acquire(void);
int sndstat_release(void);
int sndstat_register(device_t dev, char *str, sndstat_handler handler);
int sndstat_registerfile(char *str);
int sndstat_unregister(device_t dev);
int sndstat_unregisterfile(char *str);
int sndstat_busy(void);
#define SND_DECLARE_FILE(version) \
_SND_DECLARE_FILE(__LINE__, version)

View File

@ -30,6 +30,14 @@
SND_DECLARE_FILE("$FreeBSD$");
/*
* Default speed
*/
#define VCHAN_DEFAULT_SPEED 44100
extern int feeder_rate_ratemin;
extern int feeder_rate_ratemax;
struct vchinfo {
u_int32_t spd, fmt, blksz, bps, run;
struct pcm_channel *channel, *parent;
@ -74,13 +82,21 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
struct snd_dbuf *src = source;
struct pcmchan_children *cce;
struct pcm_channel *ch;
uint32_t sz;
int16_t *tmp, *dst;
unsigned int cnt;
unsigned int cnt, rcnt = 0;
#if 0
if (sndbuf_getsize(src) < count)
panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
c->name, sndbuf_getsize(src), count, c->flags);
#endif
sz = sndbuf_getsize(src);
if (sz < count)
count = sz;
count &= ~1;
if (count < 2)
return 0;
bzero(b, count);
/*
@ -99,12 +115,14 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
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 / 2);
vchan_mix_s16(dst, tmp, cnt >> 1);
if (cnt > rcnt)
rcnt = cnt;
}
CHN_UNLOCK(ch);
}
return count;
return rcnt & ~1;
}
static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
@ -127,6 +145,8 @@ vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c,
KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
if (!ch)
return NULL;
ch->parent = parent;
ch->channel = c;
ch->fmt = AFMT_U8;
@ -154,11 +174,16 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
ch->fmt = format;
ch->bps = 1;
ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0;
if (ch->fmt & AFMT_16BIT)
ch->bps <<= 1;
else if (ch->fmt & AFMT_24BIT)
ch->bps *= 3;
else if (ch->fmt & AFMT_32BIT)
ch->bps <<= 2;
CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_FORMAT);
CHN_LOCK(channel);
sndbuf_setfmt(channel->bufsoft, format);
return 0;
}
@ -170,9 +195,11 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
struct pcm_channel *channel = ch->channel;
ch->spd = speed;
CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_RATE);
CHN_LOCK(channel);
CHN_UNLOCK(channel);
CHN_LOCK(parent);
speed = sndbuf_getspd(parent->bufsoft);
CHN_UNLOCK(parent);
CHN_LOCK(channel);
return speed;
}
@ -180,19 +207,21 @@ static int
vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct vchinfo *ch = data;
struct pcm_channel *channel = ch->channel;
struct pcm_channel *parent = ch->parent;
/* struct pcm_channel *channel = ch->channel; */
int prate, crate;
ch->blksz = blocksize;
/* CHN_UNLOCK(channel); */
sndbuf_setblksz(channel->bufhard, blocksize);
chn_notify(parent, CHN_N_BLOCKSIZE);
CHN_LOCK(parent);
/* CHN_LOCK(channel); */
crate = ch->spd * ch->bps;
prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
blocksize = sndbuf_getblksz(parent->bufhard);
prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
blocksize = sndbuf_getblksz(parent->bufsoft);
CHN_UNLOCK(parent);
blocksize *= prate;
blocksize /= crate;
@ -223,7 +252,7 @@ vchan_getcaps(kobj_t obj, void *data)
{
struct vchinfo *ch = data;
ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard);
ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
ch->caps.maxspeed = ch->caps.minspeed;
ch->caps.fmtlist = vchan_fmt;
ch->caps.caps = 0;
@ -243,6 +272,91 @@ static kobj_method_t vchan_methods[] = {
};
CHANNEL_DECLARE(vchan);
#if 0
/*
* On the fly vchan rate settings
*/
#ifdef SND_DYNSYSCTL
static int
sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
struct snddev_channel *sce;
struct pcm_channel *c, *ch = NULL, *fake;
struct pcmchan_caps *caps;
int err = 0;
int newspd = 0;
d = oidp->oid_arg1;
if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
return EINVAL;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if (c->direction == PCMDIR_PLAY) {
if (c->flags & CHN_F_VIRTUAL) {
if (req->newptr != NULL &&
(c->flags & CHN_F_BUSY)) {
CHN_UNLOCK(c);
return EBUSY;
}
if (ch == NULL)
ch = c->parentchannel;
}
}
CHN_UNLOCK(c);
}
if (ch != NULL) {
CHN_LOCK(ch);
newspd = ch->speed;
CHN_UNLOCK(ch);
}
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
if (err == 0 && req->newptr != NULL) {
if (ch == NULL || newspd < 1 ||
newspd < feeder_rate_ratemin ||
newspd > feeder_rate_ratemax)
return EINVAL;
if (pcm_inprog(d, 1) != 1) {
pcm_inprog(d, -1);
return EINPROGRESS;
}
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 (newspd != ch->speed) {
err = chn_setspeed(ch, newspd);
/*
* Try to avoid FEEDER_RATE on parent channel if the
* requested value is not supported by the hardware.
*/
if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
newspd = sndbuf_getspd(ch->bufhard);
err = chn_setspeed(ch, newspd);
}
CHN_UNLOCK(ch);
if (err == 0) {
fake = pcm_getfakechan(d);
if (fake != NULL) {
CHN_LOCK(fake);
fake->speed = newspd;
CHN_UNLOCK(fake);
}
}
} else
CHN_UNLOCK(ch);
pcm_inprog(d, -1);
}
return err;
}
#endif
#endif
/* virtual channel interface */
int
@ -250,11 +364,15 @@ vchan_create(struct pcm_channel *parent)
{
struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct pcm_channel *child;
struct pcm_channel *child, *fake;
struct pcmchan_caps *parent_caps;
int err, first, speed;
int err, first, speed = 0;
CHN_UNLOCK(parent);
if (!(parent->flags & CHN_F_BUSY))
return EBUSY;
CHN_UNLOCK(parent);
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
if (!pce) {
@ -269,16 +387,7 @@ vchan_create(struct pcm_channel *parent)
CHN_LOCK(parent);
return ENODEV;
}
CHN_LOCK(parent);
if (!(parent->flags & CHN_F_BUSY))
return EBUSY;
first = SLIST_EMPTY(&parent->children);
/* add us to our parent channel's children */
pce->channel = child;
SLIST_INSERT_HEAD(&parent->children, pce, link);
CHN_UNLOCK(parent);
/* add us to our grandparent's channel list */
/*
@ -288,33 +397,111 @@ vchan_create(struct pcm_channel *parent)
if (err) {
pcm_chn_destroy(child);
free(pce, M_DEVBUF);
CHN_LOCK(parent);
return err;
}
CHN_LOCK(parent);
/* XXX gross ugly hack, murder death kill */
if (first && !err) {
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
if (err)
printf("chn_reset: %d\n", err);
speed = 44100;
/* add us to our parent channel's children */
first = SLIST_EMPTY(&parent->children);
SLIST_INSERT_HEAD(&parent->children, pce, link);
parent->flags |= CHN_F_HAS_VCHAN;
if (first) {
parent_caps = chn_getcaps(parent);
if (parent_caps != NULL) {
if (parent_caps == NULL)
err = EINVAL;
if (!err)
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
if (!err) {
fake = pcm_getfakechan(d);
if (fake != NULL) {
/*
* 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);
}
/*
* This is very sad. Few soundcards advertised as being
* able to do (insanely) higher/lower speed, but in
* reality, they simply can't. At least, we give user chance
* 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),
"vchanrate", &speed);
CHN_LOCK(parent);
if (r != 0)
speed = VCHAN_DEFAULT_SPEED;
}
/*
* Limit speed based on driver caps.
* This is supposed to help fixed rate, non-VRA
* AC97 cards.
* 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
* 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;
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))) {
speed = sndbuf_getspd(parent->bufhard);
err = chn_setspeed(parent, speed);
}
if (!err && fake != NULL) {
/*
* Save new value to fake channel.
*/
CHN_UNLOCK(parent);
CHN_LOCK(fake);
fake->speed = speed;
CHN_UNLOCK(fake);
CHN_LOCK(parent);
}
}
if (err) {
SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
parent->flags &= ~CHN_F_HAS_VCHAN;
CHN_UNLOCK(parent);
free(pce, M_DEVBUF);
pcm_chn_remove(d, child);
pcm_chn_destroy(child);
CHN_LOCK(parent);
return err;
}
err = chn_setspeed(parent, speed);
if (err)
printf("chn_setspeed: %d\n", err);
}
return err;
return 0;
}
int
@ -361,17 +548,27 @@ vchan_destroy(struct pcm_channel *c)
free(pce, M_DEVBUF);
last = SLIST_EMPTY(&parent->children);
if (last)
if (last) {
parent->flags &= ~CHN_F_BUSY;
parent->flags &= ~CHN_F_HAS_VCHAN;
}
/* remove us from our grandparent's channel list */
err = pcm_chn_remove(d, c);
if (err)
return err;
CHN_UNLOCK(parent);
/* destroy ourselves */
err = pcm_chn_destroy(c);
if (!err)
err = pcm_chn_destroy(c);
#if 0
if (!err && last) {
CHN_LOCK(parent);
chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
chn_setspeed(parent, chn_getcaps(parent)->minspeed);
CHN_UNLOCK(parent);
}
#endif
return err;
}
@ -386,9 +583,12 @@ vchan_initsys(device_t dev)
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", "");
#if 0
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", "");
#endif
#endif
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ int uaudio_halt_in_dma(device_t dev);
#endif
void uaudio_chan_set_param(device_t, u_char *, u_char *);
void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir);
void uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir);
int uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir);
void uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir);
int uaudio_chan_getptr(device_t dev, int);
void uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
@ -49,5 +49,5 @@ void uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src);
u_int32_t uaudio_query_mix_info(device_t dev);
u_int32_t uaudio_query_recsrc_info(device_t dev);
void uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt);
unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt);
void uaudio_sndstat_register(device_t dev);

View File

@ -49,16 +49,13 @@ struct ua_info {
device_t sc_dev;
u_int32_t bufsz;
struct ua_chinfo pch, rch;
#define FORMAT_NUM 32
u_int32_t ua_playfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
struct pcmchan_caps ua_playcaps;
struct pcmchan_caps ua_reccaps;
};
static u_int32_t ua_playfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
static struct pcmchan_caps ua_playcaps = {8000, 48000, ua_playfmt, 0};
static u_int32_t ua_recfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
static struct pcmchan_caps ua_reccaps = {8000, 48000, ua_recfmt, 0};
#define UAUDIO_DEFAULT_BUFSZ 16*1024
/************************************************************/
@ -76,23 +73,9 @@ ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *
ch->dir = dir;
pa_dev = device_get_parent(sc->sc_dev);
/* Create ua_playfmt[] & ua_recfmt[] */
uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt);
if (dir == PCMDIR_PLAY) {
if (ua_playfmt[0] == 0) {
printf("play channel supported format list invalid\n");
return NULL;
}
} else {
if (ua_recfmt[0] == 0) {
printf("record channel supported format list invalid\n");
return NULL;
}
}
ch->buf = malloc(sc->bufsz, M_DEVBUF, M_NOWAIT);
if (ch->buf == NULL)
if (ch->buf == NULL)
return NULL;
if (sndbuf_setup(b, ch->buf, sc->bufsz) != 0) {
free(ch->buf, M_DEVBUF);
@ -133,6 +116,9 @@ ua_chan_setformat(kobj_t obj, void *data, u_int32_t format)
struct ua_chinfo *ch = data;
/*
* At this point, no need to query as we shouldn't select an unsorted format
*/
ua = ch->parent;
pa_dev = device_get_parent(ua->sc_dev);
uaudio_chan_set_param_format(pa_dev, format, ch->dir);
@ -144,15 +130,15 @@ ua_chan_setformat(kobj_t obj, void *data, u_int32_t format)
static int
ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
{
struct ua_chinfo *ch;
device_t pa_dev;
struct ua_info *ua;
int bestspeed;
struct ua_chinfo *ch = data;
ch->spd = speed;
ch = data;
pa_dev = device_get_parent(ch->parent->sc_dev);
ua = ch->parent;
pa_dev = device_get_parent(ua->sc_dev);
uaudio_chan_set_param_speed(pa_dev, speed, ch->dir);
if ((bestspeed = uaudio_chan_set_param_speed(pa_dev, speed, ch->dir)))
ch->spd = bestspeed;
return ch->spd;
}
@ -224,9 +210,10 @@ ua_chan_getptr(kobj_t obj, void *data)
static struct pcmchan_caps *
ua_chan_getcaps(kobj_t obj, void *data)
{
struct ua_chinfo *ch = data;
struct ua_chinfo *ch;
return (ch->dir == PCMDIR_PLAY) ? &ua_playcaps : & ua_reccaps;
ch = data;
return (ch->dir == PCMDIR_PLAY) ? &(ch->parent->ua_playcaps) : &(ch->parent->ua_reccaps);
}
static kobj_method_t ua_chan_methods[] = {
@ -249,11 +236,20 @@ ua_mixer_init(struct snd_mixer *m)
{
u_int32_t mask;
device_t pa_dev;
struct snddev_info *d;
struct ua_info *ua = mix_getdevinfo(m);
pa_dev = device_get_parent(ua->sc_dev);
d = device_get_softc(ua->sc_dev);
mask = uaudio_query_mix_info(pa_dev);
if (d && !(mask & SOUND_MIXER_PCM)) {
/*
* Emulate missing pcm mixer controller
* through FEEDER_VOLUME
*/
d->flags |= SD_F_SOFTVOL;
}
mix_setdevs(m, mask);
mask = uaudio_query_recsrc_info(pa_dev);
@ -318,42 +314,63 @@ ua_attach(device_t dev)
{
struct ua_info *ua;
char status[SND_STATUSLEN];
device_t pa_dev;
u_int32_t nplay, nrec;
int i;
ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_NOWAIT);
if (!ua)
ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_ZERO | M_NOWAIT);
if (ua == NULL)
return ENXIO;
bzero(ua, sizeof *ua);
ua->sc_dev = dev;
pa_dev = device_get_parent(dev);
ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536);
if (bootverbose)
device_printf(dev, "using a default buffer size of %jd\n", (intmax_t)ua->bufsz);
if (mixer_init(dev, &ua_mixer_class, ua)) {
return(ENXIO);
goto bad;
}
snprintf(status, SND_STATUSLEN, "at addr ?");
snprintf(status, SND_STATUSLEN, "at ? %s", PCM_KLDSTRING(snd_uaudio));
ua->ua_playcaps.fmtlist = ua->ua_playfmt;
ua->ua_reccaps.fmtlist = ua->ua_recfmt;
nplay = uaudio_query_formats(pa_dev, PCMDIR_PLAY, FORMAT_NUM * 2, &ua->ua_playcaps);
nrec = uaudio_query_formats(pa_dev, PCMDIR_REC, FORMAT_NUM * 2, &ua->ua_reccaps);
if (nplay > 1)
nplay = 1;
if (nrec > 1)
nrec = 1;
#ifndef NO_RECORDING
if (pcm_register(dev, ua, 1, 1)) {
if (pcm_register(dev, ua, nplay, nrec)) {
#else
if (pcm_register(dev, ua, 1, 0)) {
if (pcm_register(dev, ua, nplay, 0)) {
#endif
return(ENXIO);
goto bad;
}
sndstat_unregister(dev);
uaudio_sndstat_register(dev);
pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
for (i = 0; i < nplay; i++) {
pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
}
#ifndef NO_RECORDING
pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
for (i = 0; i < nrec; i++) {
pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
}
#endif
pcm_setstatus(dev, status);
return 0;
bad: free(ua, M_DEVBUF);
return ENXIO;
}
static int

View File

@ -1,9 +1,9 @@
# $FreeBSD$
.if ${MACHINE_ARCH} == "sparc64"
SUBDIR = audiocs
SUBDIR = audiocs es137x
.else
SUBDIR = als4000 ad1816 cmi cs4281 csa ds1 emu10k1 es137x ess
SUBDIR = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 es137x ess
SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo
SUBDIR += t4dwave via8233 via82c686 vibes
SUBDIR += driver uaudio

View File

@ -8,7 +8,7 @@ SRCS= device_if.h bus_if.h isa_if.h pci_if.h opt_isa.h
SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h
SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c
SRCS+= ac97.c ac97_patch.c buffer.c channel.c dsp.c
SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c
SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c
SRCS+= mixer.c sndstat.c sound.c vchan.c
EXPORT_SYMS= YES # XXX evaluate

View File

@ -180,6 +180,10 @@ struct snd_size {
#define AFMT_S32_BE 0x00002000 /* Big endian signed 32-bit */
#define AFMT_U32_LE 0x00004000 /* Little endian unsigned 32-bit */
#define AFMT_U32_BE 0x00008000 /* Big endian unsigned 32-bit */
#define AFMT_S24_LE 0x00010000 /* Little endian signed 24-bit */
#define AFMT_S24_BE 0x00020000 /* Big endian signed 24-bit */
#define AFMT_U24_LE 0x00040000 /* Little endian unsigned 24-bit */
#define AFMT_U24_BE 0x00080000 /* Big endian unsigned 24-bit */
#define AFMT_STEREO 0x10000000 /* can do/want stereo */