Substantial rewrite of the SB driver to make it do full duplex on an SB16.

Because of the braindead design of the SB the input and output have to
use different precisions (8 and 16 bits).  It is possible to set the driver
to use 8 bits on both and it will the emulate 8 bits on the output by
expanding it to 16 bits.
This commit is contained in:
augustss 1997-08-29 21:41:36 +00:00
parent 42c4123e4d
commit e72ff15736
1 changed files with 313 additions and 246 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: sbdsp.c,v 1.69 1997/08/24 23:50:40 augustss Exp $ */
/* $NetBSD: sbdsp.c,v 1.70 1997/08/29 21:41:36 augustss Exp $ */
/*
* Copyright (c) 1991-1993 Regents of the University of California.
@ -171,7 +171,6 @@ static struct sbmode sbrmodes[] = {
void sbversion __P((struct sbdsp_softc *));
void sbdsp_jazz16_probe __P((struct sbdsp_softc *));
void sbdsp_set_mixer_gain __P((struct sbdsp_softc *sc, int port));
int sbdsp16_wait __P((struct sbdsp_softc *));
void sbdsp_to __P((void *));
void sbdsp_pause __P((struct sbdsp_softc *));
int sbdsp_set_timeconst __P((struct sbdsp_softc *, int));
@ -180,6 +179,8 @@ int sbdsp_set_in_ports __P((struct sbdsp_softc *, int));
void sbdsp_set_ifilter __P((void *, int));
int sbdsp_get_ifilter __P((void *));
static int sbdsp_dma_setup_input __P((struct sbdsp_softc *sc));
static int sbdsp_dma_setup_output __P((struct sbdsp_softc *sc));
static int sbdsp_adjust __P((int, int));
#ifdef AUDIO_DEBUG
@ -191,16 +192,19 @@ sb_printsc(sc)
{
int i;
printf("open %d dmachan %d/%d/%d iobase 0x%x irq %d\n",
(int)sc->sc_open, sc->dmachan, sc->sc_drq8, sc->sc_drq16,
printf("open %d dmachan %d/%d %d/%d iobase 0x%x irq %d\n",
(int)sc->sc_open, sc->sc_i.run, sc->sc_o.run,
sc->sc_drq8, sc->sc_drq16,
sc->sc_iobase, sc->sc_irq);
printf("irate %d itc %x orate %d otc %x\n",
sc->sc_irate, sc->sc_itc,
sc->sc_orate, sc->sc_otc);
sc->sc_i.rate, sc->sc_i.tc,
sc->sc_o.rate, sc->sc_o.tc);
printf("outport %u inport %u spkron %u nintr %lu\n",
sc->out_port, sc->in_port, sc->spkr_state, sc->sc_interrupts);
printf("intr %p arg %p\n",
sc->sc_intr, sc->sc_arg);
printf("intr8 %p arg8 %p\n",
sc->sc_intr8, sc->sc_arg16);
printf("intr16 %p arg16 %p\n",
sc->sc_intr8, sc->sc_arg16);
printf("gain:");
for (i = 0; i < SB_NDEVS; i++)
printf(" %u,%u", sc->gain[i][SB_LEFT], sc->gain[i][SB_RIGHT]);
@ -388,6 +392,10 @@ sbdsp_attach(sc)
printf(": dsp v%d.%02d%s\n",
SBVER_MAJOR(sc->sc_version), SBVER_MINOR(sc->sc_version),
sc->sc_model == SB_JAZZ ? ": <Jazz16>" : "");
sc->sc_fullduplex = ISSB16CLASS(sc) &&
sc->sc_drq8 != -1 && sc->sc_drq16 != -1 &&
sc->sc_drq8 != sc->sc_drq16;
}
void
@ -513,6 +521,7 @@ sbdsp_set_params(addr, setmode, usemode, play, rec)
void (*swcode) __P((void *, u_char *buf, int cnt));
int factor;
int model;
int chan;
struct audio_params *p;
int mode;
@ -527,6 +536,7 @@ sbdsp_set_params(addr, setmode, usemode, play, rec)
continue;
p = mode == AUMODE_PLAY ? play : rec;
/* Locate proper commands */
for(m = mode == AUMODE_PLAY ? sbpmodes : sbrmodes;
m->model != -1; m++) {
if (model == m->model &&
@ -632,32 +642,51 @@ sbdsp_set_params(addr, setmode, usemode, play, rec)
tc = SB_RATE_TO_TC(p->sample_rate * p->channels);
p->sample_rate = SB_TC_TO_RATE(tc) / p->channels;
}
chan = m->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
if (mode == AUMODE_PLAY) {
sc->sc_orate = rate;
sc->sc_otc = tc;
sc->sc_omodep = m;
sc->sc_obmode = bmode;
sc->sc_o.rate = rate;
sc->sc_o.tc = tc;
sc->sc_o.modep = m;
sc->sc_o.bmode = bmode;
sc->sc_o.dmachan = chan;
} else {
sc->sc_irate = rate;
sc->sc_itc = tc;
sc->sc_imodep = m;
sc->sc_ibmode = bmode;
sc->sc_i.rate = rate;
sc->sc_i.tc = tc;
sc->sc_i.modep = m;
sc->sc_i.bmode = bmode;
sc->sc_i.dmachan = chan;
}
p->sw_code = swcode;
p->factor = factor;
DPRINTF(("set_params: model=%d, mode=%d, rate=%ld, prec=%d, chan=%d, enc=%d -> tc=%02x, cmd=%02x, bmode=%02x, cmdchan=%02x, swcode=%p, factor=%d\n",
DPRINTF(("sbdsp_set_params: model=%d, mode=%d, rate=%ld, prec=%d, chan=%d, enc=%d -> tc=%02x, cmd=%02x, bmode=%02x, cmdchan=%02x, swcode=%p, factor=%d\n",
sc->sc_model, mode, p->sample_rate, p->precision, p->channels,
p->encoding, tc, m->cmd, bmode, m->cmdchan, swcode, factor));
}
/*
* XXX
* Should wait for chip to be idle.
*/
sc->sc_dmadir = SB_DMA_NONE;
sc->sc_i.run = SB_NOTRUNNING;
sc->sc_o.run = SB_NOTRUNNING;
if (sc->sc_fullduplex &&
(usemode & (AUMODE_PLAY | AUMODE_RECORD)) == (AUMODE_PLAY | AUMODE_RECORD) &&
sc->sc_i.dmachan == sc->sc_o.dmachan) {
DPRINTF(("sbdsp_commit: fd=%d, usemode=%d, idma=%d, odma=%d\n", sc->sc_fullduplex, usemode, sc->sc_i.dmachan, sc->sc_o.dmachan));
if (sc->sc_o.dmachan == sc->sc_drq8) {
/* Use 16 bit DMA for playing by expanding the samples. */
play->sw_code = linear8_to_linear16;
play->factor = 2;
sc->sc_o.modep = &sbpmodes[PLAY16];
sc->sc_o.dmachan = sc->sc_drq16;
} else {
return EINVAL;
}
}
DPRINTF(("sbdsp_set_params ichan=%d, ochan=%d\n", sc->sc_i.dmachan, sc->sc_o.dmachan));
return 0;
}
@ -858,6 +887,7 @@ sbdsp_open(addr, flags)
return ENXIO;
sc->sc_open = 1;
sc->sc_openflags = flags;
sc->sc_mintr = 0;
if (ISSBPRO(sc) &&
sbdsp_wdsp(sc, SB_DSP_RECORD_MONO) < 0) {
@ -887,7 +917,8 @@ sbdsp_close(addr)
sc->sc_open = 0;
sbdsp_spkroff(sc);
sc->spkr_state = SPKR_OFF;
sc->sc_intr = 0;
sc->sc_intr8 = 0;
sc->sc_intr16 = 0;
sc->sc_mintr = 0;
sbdsp_haltdma(sc);
@ -909,10 +940,15 @@ sbdsp_reset(sc)
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
sc->sc_intr = 0;
if (sc->sc_dmadir != SB_DMA_NONE) {
isa_dmaabort(sc->sc_isa, sc->dmachan);
sc->sc_dmadir = SB_DMA_NONE;
sc->sc_intr8 = 0;
sc->sc_intr16 = 0;
if (sc->sc_i.run != SB_NOTRUNNING) {
isa_dmaabort(sc->sc_isa, sc->sc_i.dmachan);
sc->sc_i.run = SB_NOTRUNNING;
}
if (sc->sc_o.run != SB_NOTRUNNING) {
isa_dmaabort(sc->sc_isa, sc->sc_o.dmachan);
sc->sc_o.run = SB_NOTRUNNING;
}
/*
@ -930,29 +966,9 @@ sbdsp_reset(sc)
return 0;
}
int
sbdsp16_wait(sc)
struct sbdsp_softc *sc;
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
int i;
u_char x;
for (i = SBDSP_NPOLL; --i >= 0; ) {
x = bus_space_read_1(iot, ioh, SBP_DSP_WSTAT);
delay(10);
if ((x & SB_DSP_BUSY) == 0)
continue;
return 0;
}
++sberr.wdsp;
return -1;
}
/*
* Write a byte to the dsp.
* XXX We are at the mercy of the card as we use a
* We are at the mercy of the card as we use a
* polling loop and wait until it can take the byte.
*/
int
@ -968,11 +984,11 @@ sbdsp_wdsp(sc, v)
for (i = SBDSP_NPOLL; --i >= 0; ) {
x = bus_space_read_1(iot, ioh, SBP_DSP_WSTAT);
delay(10);
if ((x & SB_DSP_BUSY) != 0)
continue;
bus_space_write_1(iot, ioh, SBP_DSP_WRITE, v);
delay(10);
return 0;
if ((x & SB_DSP_BUSY) == 0) {
bus_space_write_1(iot, ioh, SBP_DSP_WRITE, v);
delay(10);
return 0;
}
}
++sberr.wdsp;
return -1;
@ -993,11 +1009,11 @@ sbdsp_rdsp(sc)
for (i = SBDSP_NPOLL; --i >= 0; ) {
x = bus_space_read_1(iot, ioh, SBP_DSP_RSTAT);
delay(10);
if ((x & SB_DSP_READY) == 0)
continue;
x = bus_space_read_1(iot, ioh, SBP_DSP_READ);
delay(10);
return x;
if (x & SB_DSP_READY) {
x = bus_space_read_1(iot, ioh, SBP_DSP_READ);
delay(10);
return x;
}
}
++sberr.rdsp;
return -1;
@ -1166,7 +1182,7 @@ sbdsp16_set_rate(sc, cmd, rate)
struct sbdsp_softc *sc;
int cmd, rate;
{
DPRINTF(("sbdsp16_set_rate: sc=%p rate=%d\n", sc, rate));
DPRINTF(("sbdsp16_set_rate: sc=%p cmd=0x%02x rate=%d\n", sc, cmd, rate));
if (sbdsp_wdsp(sc, cmd) < 0 ||
sbdsp_wdsp(sc, rate >> 8) < 0 ||
@ -1185,17 +1201,48 @@ sbdsp_dma_init_input(addr, buf, cc)
if (sc->sc_model == SB_1)
return 0;
sc->dmaflags = DMAMODE_READ | DMAMODE_LOOP;
sc->dmaaddr = buf;
sc->dmacnt = cc;
sc->dmachan = sc->sc_imodep->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
sc->sc_i.run = SB_DMARUNNING;
DPRINTF(("sbdsp: dma start loop input addr=%p cc=%d chan=%d\n",
buf, cc, sc->dmachan));
isa_dmastart(sc->sc_isa, sc->dmachan, sc->dmaaddr,
sc->dmacnt, NULL, sc->dmaflags, BUS_DMA_NOWAIT);
buf, cc, sc->sc_i.dmachan));
isa_dmastart(sc->sc_isa, sc->sc_i.dmachan, buf,
cc, NULL, DMAMODE_READ | DMAMODE_LOOP, BUS_DMA_NOWAIT);
return 0;
}
static int
sbdsp_dma_setup_input(sc)
struct sbdsp_softc *sc;
{
int stereo = sc->sc_i.modep->channels == 2;
int filter;
/* Initialize the PCM */
if (ISSBPRO(sc)) {
if (sbdsp_wdsp(sc, sc->sc_i.modep->cmdchan) < 0)
return 0;
filter = stereo ? SBP_FILTER_OFF : sc->in_filter;
sbdsp_mix_write(sc, SBP_INFILTER,
(sbdsp_mix_read(sc, SBP_INFILTER) &
~SBP_IFILTER_MASK) | filter);
}
if (ISSB16CLASS(sc)) {
if (sbdsp16_set_rate(sc, SB_DSP16_INPUTRATE,
sc->sc_i.rate)) {
DPRINTF(("sbdsp_dma_setup_input: rate=%d set failed\n",
sc->sc_i.rate));
return 0;
}
} else {
if (sbdsp_set_timeconst(sc, sc->sc_i.tc)) {
DPRINTF(("sbdsp_dma_setup_input: tc=%d set failed\n",
sc->sc_i.rate));
return 0;
}
}
return 1;
}
int
sbdsp_dma_input(addr, p, cc, intr, arg)
void *addr;
@ -1205,89 +1252,88 @@ sbdsp_dma_input(addr, p, cc, intr, arg)
void *arg;
{
struct sbdsp_softc *sc = addr;
int loop = sc->sc_model != SB_1;
int stereo = sc->sc_imodep->channels == 2;
int filter;
#ifdef AUDIO_DEBUG
if (sbdspdebug > 1)
printf("sbdsp_dma_input: cc=%d %p (%p)\n", cc, intr, arg);
printf("sbdsp_dma_input: sc=%p buf=%p cc=%d intr=%p(%p)\n",
addr, p, cc, intr, arg);
#endif
#ifdef DIAGNOSTIC
if (sc->sc_imodep->channels == 2 && (cc & 1)) {
if (sc->sc_i.modep->channels == 2 && (cc & 1)) {
DPRINTF(("stereo record odd bytes (%d)\n", cc));
return EIO;
}
#endif
if (sc->sc_dmadir != SB_DMA_IN) {
if (ISSBPRO(sc)) {
if (sbdsp_wdsp(sc, sc->sc_imodep->cmdchan) < 0)
goto badmode;
filter = stereo ? SBP_FILTER_OFF : sc->in_filter;
sbdsp_mix_write(sc, SBP_INFILTER,
(sbdsp_mix_read(sc, SBP_INFILTER) &
~SBP_IFILTER_MASK) | filter);
if (sc->sc_i.dmachan == sc->sc_drq8) {
sc->sc_intr8 = intr;
sc->sc_arg8 = arg;
} else {
#ifdef DIAGNOSTIC
if (sc->sc_i.dmachan != sc->sc_drq16) {
printf("sbdsp_dma_input: bad chan %d\n", sc->sc_i.dmachan);
return EIO;
}
#endif
sc->sc_intr16 = intr;
sc->sc_arg16 = arg;
}
switch(sc->sc_i.run) {
case SB_NOTRUNNING:
/* Non-looping mode, not initialized */
sc->sc_i.run = SB_RUNNING;
if (!sbdsp_dma_setup_input(sc))
goto giveup;
/* fall into */
case SB_RUNNING:
/* Non-looping mode, start DMA */
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp_dma_input: dmastart buf=%p cc=%d chan=%d\n",
p, cc, sc->sc_i.dmachan);
#endif
isa_dmastart(sc->sc_isa, sc->sc_i.dmachan, p,
cc, NULL, DMAMODE_READ, BUS_DMA_NOWAIT);
/* Start PCM in non-looping mode */
if ((sc->sc_model == SB_JAZZ && sc->sc_i.dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_i.modep->precision == 16))
cc >>= 1;
--cc;
if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_input: SB1 DMA start failed\n"));
goto giveup;
}
break;
case SB_DMARUNNING:
/* Looping mode, not initialized */
sc->sc_i.run = SB_PCMRUNNING;
if (!sbdsp_dma_setup_input(sc))
goto giveup;
if ((sc->sc_model == SB_JAZZ && sc->sc_i.dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_i.modep->precision == 16))
cc >>= 1;
--cc;
/* Initialize looping PCM */
if (ISSB16CLASS(sc)) {
if (sbdsp16_set_rate(sc, SB_DSP16_INPUTRATE,
sc->sc_irate)) {
DPRINTF(("sbdsp_dma_input: rate=%d set failed\n",
sc->sc_irate));
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp16 input command cmd=0x%02x bmode=0x%02x cc=%d\n",
sc->sc_i.modep->cmd, sc->sc_i.bmode, cc);
#endif
if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0 ||
sbdsp_wdsp(sc, sc->sc_i.bmode) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_input: SB16 DMA start failed\n"));
DPRINTF(("sbdsp16 input command cmd=0x%02x bmode=0x%02x cc=%d\n",
sc->sc_i.modep->cmd, sc->sc_i.bmode, cc));
goto giveup;
}
} else {
if (sbdsp_set_timeconst(sc, sc->sc_itc)) {
DPRINTF(("sbdsp_dma_output: tc=%d set failed\n",
sc->sc_irate));
goto giveup;
}
}
sc->sc_dmadir = SB_DMA_IN;
sc->dmaflags = DMAMODE_READ;
} else {
/* If already started just return. */
if (loop)
return 0;
}
if (!loop) {
sc->dmaaddr = p;
sc->dmacnt = cc;
sc->dmachan = sc->sc_imodep->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp_dma_input: dmastart %x %p %ld %d\n",
sc->dmaflags, sc->dmaaddr, sc->dmacnt, sc->dmachan);
#endif
isa_dmastart(sc->sc_isa, sc->dmachan, sc->dmaaddr,
sc->dmacnt, NULL, sc->dmaflags, BUS_DMA_NOWAIT);
}
sc->sc_intr = intr;
sc->sc_arg = arg;
if ((sc->sc_model == SB_JAZZ && sc->dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_imodep->precision == 16))
cc >>= 1;
--cc;
if (ISSB16CLASS(sc)) {
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp16 input command %02x %02x %d\n",
sc->sc_imodep->cmd, sc->sc_ibmode, cc);
#endif
if (sbdsp_wdsp(sc, sc->sc_imodep->cmd) < 0 ||
sbdsp_wdsp(sc, sc->sc_ibmode) < 0 ||
sbdsp16_wait(sc) ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_input: SB16 DMA start failed\n"));
goto giveup;
}
} else {
if (loop) {
DPRINTF(("sbdsp_dma_input: set blocksize=%d\n", cc));
if (sbdsp_wdsp(sc, SB_DSP_BLOCKSIZE) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
@ -1295,28 +1341,21 @@ sbdsp_dma_input(addr, p, cc, intr, arg)
DPRINTF(("sbdsp_dma_input: SB2 DMA blocksize failed\n"));
goto giveup;
}
if (sbdsp_wdsp(sc, sc->sc_imodep->cmd) < 0) {
if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0) {
DPRINTF(("sbdsp_dma_input: SB2 DMA start failed\n"));
goto giveup;
}
} else {
if (sbdsp_wdsp(sc, sc->sc_imodep->cmd) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_input: SB1 DMA start failed\n"));
goto giveup;
}
}
break;
case SB_PCMRUNNING:
/* Looping mode, nothing to do */
break;
}
return 0;
giveup:
sbdsp_reset(sc);
return EIO;
badmode:
DPRINTF(("sbdsp_dma_input: can't set mode\n"));
return EIO;
}
int
@ -1329,17 +1368,48 @@ sbdsp_dma_init_output(addr, buf, cc)
if (sc->sc_model == SB_1)
return 0;
sc->dmaflags = DMAMODE_WRITE | DMAMODE_LOOP;
sc->dmaaddr = buf;
sc->dmacnt = cc;
sc->dmachan = sc->sc_omodep->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
DPRINTF(("sbdsp: dma start loop output addr=%p cc=%d chan=%d\n",
buf, cc, sc->dmachan));
isa_dmastart(sc->sc_isa, sc->dmachan, sc->dmaaddr,
sc->dmacnt, NULL, sc->dmaflags, BUS_DMA_NOWAIT);
sc->sc_o.run = SB_DMARUNNING;
DPRINTF(("sbdsp: dma start loop output buf=%p cc=%d chan=%d\n",
buf, cc, sc->sc_o.dmachan));
isa_dmastart(sc->sc_isa, sc->sc_o.dmachan, buf,
cc, NULL, DMAMODE_WRITE | DMAMODE_LOOP, BUS_DMA_NOWAIT);
return 0;
}
static int
sbdsp_dma_setup_output(sc)
struct sbdsp_softc *sc;
{
int stereo = sc->sc_o.modep->channels == 2;
int cmd;
if (ISSBPRO(sc)) {
/* make sure we re-set stereo mixer bit when we start output. */
sbdsp_mix_write(sc, SBP_STEREO,
(sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK) |
(stereo ? SBP_PLAYMODE_STEREO : SBP_PLAYMODE_MONO));
cmd = sc->sc_o.modep->cmdchan;
if (cmd && sbdsp_wdsp(sc, cmd) < 0)
return 0;
}
if (ISSB16CLASS(sc)) {
if (sbdsp16_set_rate(sc, SB_DSP16_OUTPUTRATE,
sc->sc_o.rate)) {
DPRINTF(("sbdsp_dma_setup_output: rate=%d set failed\n",
sc->sc_o.rate));
return 0;
}
} else {
if (sbdsp_set_timeconst(sc, sc->sc_o.tc)) {
DPRINTF(("sbdsp_dma_setup_output: tc=%d set failed\n",
sc->sc_o.rate));
return 0;
}
}
return 1;
}
int
sbdsp_dma_output(addr, p, cc, intr, arg)
void *addr;
@ -1349,86 +1419,80 @@ sbdsp_dma_output(addr, p, cc, intr, arg)
void *arg;
{
struct sbdsp_softc *sc = addr;
int loop = sc->sc_model != SB_1;
int stereo = sc->sc_omodep->channels == 2;
int cmd;
#ifdef AUDIO_DEBUG
if (sbdspdebug > 1)
printf("sbdsp_dma_output: cc=%d %p (%p)\n", cc, intr, arg);
printf("sbdsp_dma_output: sc=%p buf=%p cc=%d intr=%p(%p)\n", addr, p, cc, intr, arg);
#endif
#ifdef DIAGNOSTIC
if (stereo && (cc & 1)) {
if (sc->sc_o.modep->channels == 2 && (cc & 1)) {
DPRINTF(("stereo playback odd bytes (%d)\n", cc));
return EIO;
}
#endif
if (sc->sc_dmadir != SB_DMA_OUT) {
if (ISSBPRO(sc)) {
/* make sure we re-set stereo mixer bit when we start
output. */
sbdsp_mix_write(sc, SBP_STEREO,
(sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK) |
(stereo ? SBP_PLAYMODE_STEREO : SBP_PLAYMODE_MONO));
cmd = sc->sc_omodep->cmdchan;
if (cmd && sbdsp_wdsp(sc, cmd) < 0)
goto badmode;
if (sc->sc_o.dmachan == sc->sc_drq8) {
sc->sc_intr8 = intr;
sc->sc_arg8 = arg;
} else {
#ifdef DIAGNOSTIC
if (sc->sc_o.dmachan != sc->sc_drq16) {
printf("sbdsp_dma_output: bad chan %d\n", sc->sc_i.dmachan);
return EIO;
}
#endif
sc->sc_intr16 = intr;
sc->sc_arg16 = arg;
}
switch(sc->sc_o.run) {
case SB_NOTRUNNING:
/* Non-looping mode, not initialized */
sc->sc_o.run = SB_RUNNING;
if (!sbdsp_dma_setup_output(sc))
goto giveup;
/* fall into */
case SB_RUNNING:
/* Non-looping mode, initialized. Start DMA and PCM */
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp: start dma out addr=%p, cc=%d, chan=%d\n",
p, cc, sc->sc_o.dmachan);
#endif
isa_dmastart(sc->sc_isa, sc->sc_o.dmachan, p,
cc, NULL, DMAMODE_WRITE, BUS_DMA_NOWAIT);
if ((sc->sc_model == SB_JAZZ && sc->sc_o.dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_o.modep->precision == 16))
cc >>= 1;
--cc;
if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_output: SB1 DMA start failed\n"));
goto giveup;
}
break;
case SB_DMARUNNING:
/* Looping mode, not initialized */
sc->sc_o.run = SB_PCMRUNNING;
if (!sbdsp_dma_setup_output(sc))
goto giveup;
if ((sc->sc_model == SB_JAZZ && sc->sc_o.dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_o.modep->precision == 16))
cc >>= 1;
--cc;
/* Initialize looping PCM */
if (ISSB16CLASS(sc)) {
if (sbdsp16_set_rate(sc, SB_DSP16_OUTPUTRATE,
sc->sc_orate)) {
DPRINTF(("sbdsp_dma_output: rate=%d set failed\n",
sc->sc_orate));
DPRINTF(("sbdsp_dma_output: SB16 cmd=0x%02x bmode=0x%02x cc=%d\n",
sc->sc_o.modep->cmd,sc->sc_o.bmode, cc));
if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0 ||
sbdsp_wdsp(sc, sc->sc_o.bmode) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_output: SB16 DMA start failed\n"));
goto giveup;
}
} else {
if (sbdsp_set_timeconst(sc, sc->sc_otc)) {
DPRINTF(("sbdsp_dma_output: tc=%d set failed\n",
sc->sc_orate));
goto giveup;
}
}
sc->sc_dmadir = SB_DMA_OUT;
sc->dmaflags = DMAMODE_WRITE;
} else {
/* If already started just return. */
if (loop)
return 0;
}
if (!loop) {
sc->dmaaddr = p;
sc->dmacnt = cc;
sc->dmachan = sc->sc_omodep->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
#ifdef AUDIO_DEBUG
if (sbdspdebug > 2)
printf("sbdsp: start dma out flags=%x, addr=%p, cnt=%ld, chan=%d\n",
sc->dmaflags, sc->dmaaddr, sc->dmacnt, sc->dmachan);
#endif
isa_dmastart(sc->sc_isa, sc->dmachan, sc->dmaaddr,
sc->dmacnt, NULL, sc->dmaflags, BUS_DMA_NOWAIT);
}
sc->sc_intr = intr;
sc->sc_arg = arg;
if ((sc->sc_model == SB_JAZZ && sc->dmachan > 3) ||
(sc->sc_model != SB_JAZZ && sc->sc_omodep->precision == 16))
cc >>= 1;
--cc;
if (ISSB16CLASS(sc)) {
if (sbdsp_wdsp(sc, sc->sc_omodep->cmd) < 0 ||
sbdsp_wdsp(sc, sc->sc_obmode) < 0 ||
sbdsp16_wait(sc) ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_output: SB16 DMA start failed\n"));
goto giveup;
}
} else {
if (loop) {
DPRINTF(("sbdsp_dma_output: set blocksize=%d\n", cc));
if (sbdsp_wdsp(sc, SB_DSP_BLOCKSIZE) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
@ -1436,40 +1500,32 @@ sbdsp_dma_output(addr, p, cc, intr, arg)
DPRINTF(("sbdsp_dma_output: SB2 DMA blocksize failed\n"));
goto giveup;
}
if (sbdsp_wdsp(sc, sc->sc_omodep->cmd) < 0) {
if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0) {
DPRINTF(("sbdsp_dma_output: SB2 DMA start failed\n"));
goto giveup;
}
} else {
if (sbdsp_wdsp(sc, sc->sc_omodep->cmd) < 0 ||
sbdsp_wdsp(sc, cc) < 0 ||
sbdsp_wdsp(sc, cc >> 8) < 0) {
DPRINTF(("sbdsp_dma_output: SB1 DMA start failed\n"));
goto giveup;
}
}
break;
case SB_PCMRUNNING:
/* Looping mode, nothing to do */
break;
}
return 0;
giveup:
sbdsp_reset(sc);
return EIO;
badmode:
DPRINTF(("sbdsp_dma_output: can't set mode\n"));
return EIO;
}
/*
* Only the DSP unit on the sound blaster generates interrupts.
* There are three cases of interrupt: reception of a midi byte
* (when mode is enabled), completion of dma transmission, or
* completion of a dma reception. The three modes are mutually
* exclusive so we know a priori which event has occurred.
* completion of a dma reception.
*
* If there is interrupt sharing or a spurious interrupt occurs
* there is no way to distinuish this on an SB2. So if you have
* an SB2 and experience problems, buy an SB16 (it's only $25).
* there is no way to distinguish this on an SB2. So if you have
* an SB2 and experience problems, buy an SB16 (it's only $40).
*/
int
sbdsp_intr(arg)
@ -1481,14 +1537,17 @@ sbdsp_intr(arg)
#ifdef AUDIO_DEBUG
if (sbdspdebug > 1)
printf("sbdsp_intr: intr=%p\n", sc->sc_intr);
printf("sbdsp_intr: intr8=%p, intr16=%p\n",
sc->sc_intr8, sc->sc_intr16);
#endif
if (ISSB16CLASS(sc)) {
irq = sbdsp_mix_read(sc, SBP_IRQ_STATUS);
if ((irq & (SBP_IRQ_DMA8 | SBP_IRQ_DMA16)) == 0)
if ((irq & (SBP_IRQ_DMA8 | SBP_IRQ_DMA16)) == 0) {
DPRINTF(("sbdsp_intr: Spurious interrupt 0x%x\n", irq));
return 0;
}
} else {
if (!loop && !isa_dmafinished(sc->sc_isa, sc->dmachan))
if (!loop && !isa_dmafinished(sc->sc_isa, sc->sc_drq8))
return 0;
irq = SBP_IRQ_DMA8;
}
@ -1500,17 +1559,23 @@ sbdsp_intr(arg)
(*sc->sc_mintr)(sc->sc_arg, x);
} else
#endif
if (sc->sc_intr != 0) {
/* clear interrupt */
if (irq & SBP_IRQ_DMA8)
bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK8);
if (irq & SBP_IRQ_DMA16)
bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK16);
if (sc->sc_intr8 == 0 && sc->sc_intr16 == 0) {
DPRINTF(("sbdsp_intr: Unexpected interrupt 0x%x\n", irq));
/* XXX return 0;*/ /* Did not expect an interrupt */
}
/* clear interrupt */
if (irq & SBP_IRQ_DMA8) {
bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK8);
if (!loop)
isa_dmadone(sc->sc_isa, sc->dmachan);
(*sc->sc_intr)(sc->sc_arg);
} else {
return 0;
isa_dmadone(sc->sc_isa, sc->sc_drq8);
if (sc->sc_intr8)
(*sc->sc_intr8)(sc->sc_arg8);
}
if (irq & SBP_IRQ_DMA16) {
bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK16);
if (sc->sc_intr16)
(*sc->sc_intr16)(sc->sc_arg16);
}
return 1;
}
@ -2296,5 +2361,7 @@ int
sbdsp_get_props(addr)
void *addr;
{
return AUDIO_PROP_MMAP;
struct sbdsp_softc *sc = addr;
return AUDIO_PROP_MMAP |
(sc->sc_fullduplex ? AUDIO_PROP_FULLDUPLEX : 0);
}