* als4000 can't do 48k properly (perhaps it really can't at all!).

Set maxspeed to 44.1k instead.
* Add locking / MPSAFE
* Fix recording

Submitted by:	Ariff Abdullah <skywizard@MyBSD.org.my>
This commit is contained in:
Alexander Leidinger 2005-07-31 11:01:13 +00:00
parent 7233abab86
commit 1adf8286e7

View File

@ -75,6 +75,7 @@ struct sc_info {
struct resource *reg, *irq; struct resource *reg, *irq;
int regid, irqid; int regid, irqid;
void *ih; void *ih;
struct mtx *lock;
unsigned int bufsz; unsigned int bufsz;
struct sc_chinfo pch, rch; struct sc_chinfo pch, rch;
@ -90,7 +91,11 @@ static u_int32_t als_format[] = {
0 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 */ /* Register Utilities */
@ -199,6 +204,7 @@ alschan_init(kobj_t obj, void *devinfo,
struct sc_info *sc = devinfo; struct sc_info *sc = devinfo;
struct sc_chinfo *ch; struct sc_chinfo *ch;
snd_mtxlock(sc->lock);
if (dir == PCMDIR_PLAY) { if (dir == PCMDIR_PLAY) {
ch = &sc->pch; ch = &sc->pch;
ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS; ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
@ -214,8 +220,10 @@ alschan_init(kobj_t obj, void *devinfo,
ch->speed = DSP_DEFAULT_SPEED; ch->speed = DSP_DEFAULT_SPEED;
ch->buffer = b; ch->buffer = b;
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
snd_mtxunlock(sc->lock);
return NULL; return NULL;
} }
snd_mtxunlock(sc->lock);
return ch; return ch;
} }
@ -223,8 +231,11 @@ static int
alschan_setformat(kobj_t obj, void *data, u_int32_t format) alschan_setformat(kobj_t obj, void *data, u_int32_t format)
{ {
struct sc_chinfo *ch = data; struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
ch->format = format; ch->format = format;
snd_mtxunlock(sc->lock);
return 0; return 0;
} }
@ -234,15 +245,18 @@ alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
struct sc_chinfo *ch = data, *other; struct sc_chinfo *ch = data, *other;
struct sc_info *sc = ch->parent; struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch; other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
/* Deny request if other dma channel is active */ /* Deny request if other dma channel is active */
if (other->dma_active) { if (other->dma_active) {
ch->speed = other->speed; ch->speed = other->speed;
snd_mtxunlock(sc->lock);
return other->speed; return other->speed;
} }
ch->speed = speed; ch->speed = speed;
snd_mtxunlock(sc->lock);
return speed; return speed;
} }
@ -263,10 +277,13 @@ static int
alschan_getptr(kobj_t obj, void *data) alschan_getptr(kobj_t obj, void *data)
{ {
struct sc_chinfo *ch = data; struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
int32_t pos, sz; int32_t pos, sz;
snd_mtxlock(sc->lock);
pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff; pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
sz = sndbuf_getsize(ch->buffer); sz = sndbuf_getsize(ch->buffer);
snd_mtxunlock(sc->lock);
return (2 * sz - pos - 1) % sz; return (2 * sz - pos - 1) % sz;
} }
@ -378,7 +395,9 @@ static int
alspchan_trigger(kobj_t obj, void *data, int go) alspchan_trigger(kobj_t obj, void *data, int go)
{ {
struct sc_chinfo *ch = data; struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
switch(go) { switch(go) {
case PCMTRIG_START: case PCMTRIG_START:
als_playback_start(ch); als_playback_start(ch);
@ -387,6 +406,7 @@ alspchan_trigger(kobj_t obj, void *data, int go)
als_playback_stop(ch); als_playback_stop(ch);
break; break;
} }
snd_mtxunlock(sc->lock);
return 0; return 0;
} }
@ -468,7 +488,9 @@ static int
alsrchan_trigger(kobj_t obj, void *data, int go) alsrchan_trigger(kobj_t obj, void *data, int go)
{ {
struct sc_chinfo *ch = data; struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
snd_mtxlock(sc->lock);
switch(go) { switch(go) {
case PCMTRIG_START: case PCMTRIG_START:
als_capture_start(ch); als_capture_start(ch);
@ -477,6 +499,7 @@ alsrchan_trigger(kobj_t obj, void *data, int go)
als_capture_stop(ch); als_capture_stop(ch);
break; break;
} }
snd_mtxunlock(sc->lock);
return 0; return 0;
} }
@ -574,7 +597,7 @@ static int
alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src) alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{ {
struct sc_info *sc = mix_getdevinfo(m); struct sc_info *sc = mix_getdevinfo(m);
u_int32_t i, l, r; u_int32_t i, l, r, mask;
for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) { for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (src & (1 << i)) { if (src & (1 << i)) {
@ -583,8 +606,24 @@ alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
} }
} }
als_mix_wr(sc, SB16_IMASK_L, l); /* ALS mixer is really an SB16 mixer */
als_mix_wr(sc, SB16_IMASK_R, r);
mask = 0;
if (src & SOUND_MASK_MIC)
mask |= 0x01;
if (src & SOUND_MASK_CD)
mask |= 0x06;
if (src & SOUND_MASK_LINE)
mask |= 0x18;
if (src & SOUND_MASK_SYNTH)
mask |= 0x60;
als_mix_wr(sc, SB16_IMASK_L, l|mask);
als_mix_wr(sc, SB16_IMASK_R, r|mask);
return src; return src;
} }
@ -605,13 +644,20 @@ als_intr(void *p)
struct sc_info *sc = (struct sc_info *)p; struct sc_info *sc = (struct sc_info *)p;
u_int8_t intr, sb_status; u_int8_t intr, sb_status;
snd_mtxlock(sc->lock);
intr = als_intr_rd(sc); intr = als_intr_rd(sc);
if (intr & 0x80) if (intr & 0x80) {
snd_mtxunlock(sc->lock);
chn_intr(sc->pch.channel); chn_intr(sc->pch.channel);
snd_mtxlock(sc->lock);
}
if (intr & 0x40) if (intr & 0x40) {
snd_mtxunlock(sc->lock);
chn_intr(sc->rch.channel); chn_intr(sc->rch.channel);
snd_mtxlock(sc->lock);
}
/* ACK interrupt in PCI core */ /* ACK interrupt in PCI core */
als_intr_wr(sc, intr); als_intr_wr(sc, intr);
@ -627,6 +673,8 @@ als_intr(void *p)
als_ack_read(sc, ALS_MIDI_DATA); als_ack_read(sc, ALS_MIDI_DATA);
if (sb_status & ALS_IRQ_CR1E) if (sb_status & ALS_IRQ_CR1E)
als_ack_read(sc, ALS_CR1E_ACK_PORT); als_ack_read(sc, ALS_CR1E_ACK_PORT);
snd_mtxunlock(sc->lock);
return; return;
} }
@ -708,6 +756,10 @@ als_resource_free(device_t dev, struct sc_info *sc)
bus_dma_tag_destroy(sc->parent_dmat); bus_dma_tag_destroy(sc->parent_dmat);
sc->parent_dmat = 0; sc->parent_dmat = 0;
} }
if (sc->lock) {
snd_mtxfree(sc->lock);
sc->lock = NULL;
}
} }
static int static int
@ -730,7 +782,7 @@ als_resource_grab(device_t dev, struct sc_info *sc)
goto bad; 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)) { sc, &sc->ih)) {
device_printf(dev, "unable to setup interrupt\n"); device_printf(dev, "unable to setup interrupt\n");
goto bad; goto bad;
@ -745,8 +797,8 @@ als_resource_grab(device_t dev, struct sc_info *sc)
/*filter*/NULL, /*filterarg*/NULL, /*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sc->bufsz, /*maxsize*/sc->bufsz,
/*nsegments*/1, /*maxsegz*/0x3ffff, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, /*lockfunc*/busdma_lock_mutex, /*flags*/0, /*lockfunc*/NULL,
/*lockarg*/&Giant, &sc->parent_dmat) != 0) { /*lockarg*/NULL, &sc->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n"); device_printf(dev, "unable to create dma tag\n");
goto bad; goto bad;
} }
@ -768,6 +820,7 @@ als_pci_attach(device_t dev)
return ENXIO; return ENXIO;
} }
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
sc->dev = dev; sc->dev = dev;
data = pci_read_config(dev, PCIR_COMMAND, 2); data = pci_read_config(dev, PCIR_COMMAND, 2);
@ -851,9 +904,11 @@ als_pci_suspend(device_t dev)
{ {
struct sc_info *sc = pcm_getdevinfo(dev); struct sc_info *sc = pcm_getdevinfo(dev);
snd_mtxlock(sc->lock);
sc->pch.dma_was_active = als_playback_stop(&sc->pch); sc->pch.dma_was_active = als_playback_stop(&sc->pch);
sc->rch.dma_was_active = als_capture_stop(&sc->rch); sc->rch.dma_was_active = als_capture_stop(&sc->rch);
als_uninit(sc); als_uninit(sc);
snd_mtxunlock(sc->lock);
return 0; return 0;
} }
@ -862,13 +917,17 @@ als_pci_resume(device_t dev)
{ {
struct sc_info *sc = pcm_getdevinfo(dev); struct sc_info *sc = pcm_getdevinfo(dev);
snd_mtxlock(sc->lock);
if (als_init(sc) != 0) { if (als_init(sc) != 0) {
device_printf(dev, "unable to reinitialize the card\n"); device_printf(dev, "unable to reinitialize the card\n");
snd_mtxunlock(sc->lock);
return ENXIO; return ENXIO;
} }
if (mixer_reinit(dev) != 0) { if (mixer_reinit(dev) != 0) {
device_printf(dev, "unable to reinitialize the mixer\n"); device_printf(dev, "unable to reinitialize the mixer\n");
snd_mtxunlock(sc->lock);
return ENXIO; return ENXIO;
} }
@ -879,6 +938,8 @@ als_pci_resume(device_t dev)
if (sc->rch.dma_was_active) { if (sc->rch.dma_was_active) {
als_capture_start(&sc->rch); als_capture_start(&sc->rch);
} }
snd_mtxunlock(sc->lock);
return 0; return 0;
} }