summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjmg <jmg@FreeBSD.org>1997-10-31 12:24:28 +0000
committerjmg <jmg@FreeBSD.org>1997-10-31 12:24:28 +0000
commit01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2 (patch)
treee9c2dd7a7137068541454b8d30fef97a895a6541
parent21bec5640c25cac4bb8e9ae59bd0290bc84a07ad (diff)
downloadFreeBSD-src-01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2.zip
FreeBSD-src-01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2.tar.gz
This updates Luigi's sound code to the basic code in snd971023...
changes: o rip the old select from his distribution to prevent extra pollution o the code now uses audio dma, helps reduce clicks o improved card support, should work in full duplex on sb16 cards o add better voxware ioctl support pointed out by Joao Carlos Mendes Luis <jonny@coppe.ufrj.br> o remove an unused file that I included for more complete history o and MANY other changes I have personally tested this code with a CS4237 based card and an AWE32 (non-PnP). Both cards worked fine in 8bit and 16bit mode.
-rw-r--r--sys/dev/pcm/isa/mss.c436
-rw-r--r--sys/dev/pcm/isa/mss.h109
-rw-r--r--sys/dev/pcm/isa/sb.c159
-rw-r--r--sys/dev/pcm/isa/sb.h45
-rw-r--r--sys/dev/sound/isa/mss.c436
-rw-r--r--sys/dev/sound/isa/mss.h109
-rw-r--r--sys/dev/sound/isa/sb.c159
-rw-r--r--sys/dev/sound/isa/sb.h45
-rw-r--r--sys/dev/sound/isa/sb16.c159
-rw-r--r--sys/dev/sound/isa/sb8.c159
-rw-r--r--sys/i386/isa/snd/README100
-rw-r--r--sys/i386/isa/snd/ad1848.c436
-rw-r--r--sys/i386/isa/snd/dmabuf.c773
-rw-r--r--sys/i386/isa/snd/dmabuf_auto.c802
-rw-r--r--sys/i386/isa/snd/mss.h109
-rw-r--r--sys/i386/isa/snd/sb_dsp.c159
-rw-r--r--sys/i386/isa/snd/sbcard.h45
-rw-r--r--sys/i386/isa/snd/sound.c260
-rw-r--r--sys/i386/isa/snd/sound.h91
19 files changed, 2173 insertions, 2418 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c
index 1188d32..0cd2dfd 100644
--- a/sys/dev/pcm/isa/mss.c
+++ b/sys/dev/pcm/isa/mss.c
@@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
- * Modified by Luigi Rizzo (luigi@iet.unipi.it)
- *
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248
- *
- * CS4231, used in the GUS MAX and some other cards;
- * AD1845, CS4231A (CS4231-like)
- * CS4232 (CS4231+SB and MPU, PnP)
- * CS4236 (upgrade of the CS4232, has a better mixer)
- * OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- *
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
- *
- * The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
+ * but we do not use these modes.
*/
} ;
/*
- * this is the probe routine. Note, it is not necessary to
+ * mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
+ dev->id_alive = 8 ; /* number of io ports */
+ /* should be already set but just in case... */
+
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
+ dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
+ if (d->dma1 != d->dma2)
+ d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
- ask_init(d);
+ ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
+ * With the exception of INIT, all other callbacks are invoked
+ * with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
- u_long s;
u_char m;
int retry, wr, cnt;
@@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
- /* fallthrough */
- case SND_CB_RESTART :
- s = spltty();
- cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
+ cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
- DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
+ DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
- splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
- s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
-
- /* disable DMA by clearing count registers. */
+#if 1
+ /*
+ * try to disable DMA by clearing count registers. Not sure it
+ * is needed, and it might cause false interrupts when the
+ * DMA is re-enabled later.
+ */
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
- splx(s);
break;
+#endif
}
return 0 ;
}
@@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
+ * The correct way to operate for a device with multiple internal
+ * interrupt sources is to loop on the status register and ack
+ * interrupts until all interrupts are served and none are reported. At
+ * this point the IRQ line to the ISA IRQ controller should go low
+ * and be raised at the next interrupt.
+ *
+ * Since the ISA IRQ controller is sent EOI _before_ passing control
+ * to the isr, it might happen that we serve an interrupt early, in
+ * which case the status register at the next interrupt should just
+ * say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
- u_char i11, c;
- u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
+ u_char c, served = 0;
+ int i;
- i11 = ad_read(d, 11);
- reason = inb(io_Status(d));
+ DEB(printf("mss_intr\n"));
+ ad_read(d, 11); /* fake read of status bits */
- if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
- return;
- /* get exact reason */
- c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
- if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
- dsp_wrintr(d);
- c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
- if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
- dsp_rdintr(d);
- /* XXX check this on the 4236... */
- if (d->dma1 == d->dma2)
- outb(io_Status(d), 0); /* Clear interrupt status */
- else
- ad_write(d, 24, ~c); /* ack selectively */
+ /*
+ * loop until there are interrupts, but no more than 10 times.
+ */
+ for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
+ /* get exact reason for full-duplex boards */
+ c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
+ c &= ~served ;
+ if ( d->dbuf_out.dl && (c & 0x10) ) {
+ served |= 0x10 ;
+ dsp_wrintr(d);
+ }
+ if ( d->dbuf_in.dl && (c & 0x20) ) {
+ served |= 0x20 ;
+ dsp_rdintr(d);
+ }
+ /*
+ * now ack the interrupt
+ */
+ if (d->dma1 == d->dma2)
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ else
+ ad_write(d, 24, ~c); /* ack selectively */
+ }
}
/*
* the opti931 seems to miss interrupts when working in full
- * duplex. god know why...
+ * duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
- int w_miss=0, r_miss=0;
- static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning CD interrupt\n");
+ printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning MPU interrupt\n");
+ printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
- if (w_miss || r_miss ) {
- misses++;
- if ( (misses & 0xff) == 1 )
- printf("opti931: %d missed irq (now %s%s)\n",
- misses, w_miss?"play ":"",
- r_miss?"capture ":"");
- }
- if (loops==10)
+ if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- if (w_miss) mc11 |= 4 ;
- if (r_miss) mc11 |= 8 ;
- if (mc11==0)
- return;
+ }
+ return;
}
- if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
+ if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
- r_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
- r_miss = 1 ;
-
- if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
- if (isa_dmastatus1(d->dma1) != 0)
- printf("opti931_intr: wr dma %d\n",
- isa_dmastatus1(d->dma1));
+ if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
- w_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
- w_miss = 1 ;
-
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
+
+static void
+gus_write(int io_base, u_char reg, u_char value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 5, value);
+}
+
+static void
+gus_writew(int io_base, u_char reg, u_short value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 4, value);
+}
+
+static u_char
+gus_read(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inb(io_base+5);
+}
+
+static u_short
+gus_readw(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inw(io_base+4);
+}
+
+
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
- outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
-
- outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
- *
- * fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
- DDB(printf("nbits = 0 for dev %d\n", dev) );
+ DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
- DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
+ DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
- val = ad_read(d, regoffs);
+ old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
+ DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
+ dev, regoffs, old, val));
}
return 0; /* success */
}
@@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
- d->mix_rec_devs = MODE1_REC_DEVICES;
+ d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
+ /*
+ * some device-specific things, mostly mute the mic to
+ * the output mixer so as to avoid hisses. In many cases this
+ * is the default after reset, this code is here mostly as a
+ * reminder that this might be necessary on other boards.
+ */
+ switch(d->bd_id) {
+ case MD_OPTI931:
+ ad_write(d, 20, 0x88);
+ ad_write(d, 21, 0x88);
+ break;
+ case MD_GUSPNP:
+ /* this is only necessary in mode 3 ... */
+ ad_write(d, 22, 0x88);
+ ad_write(d, 23, 0x88);
+ }
}
/*
@@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
- *
- * fixed lr970724
*/
static int
@@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DDB(printf("mss_detect error, busy still set (0x%02x)\n",
+ DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
- return 0;
+ return 0;
}
ad_write(d, 0, 0x45);
@@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DDB(printf("mss_detect error - step H(%x)\n", tmp1));
+ DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DDB(printf("mss_detect() - step I\n"));
+ DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
- case 0x03: /* CS4236 on Intel PR440FX motherboard */
+ case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
- DDB(printf("mss_detect() - Detected %s\n", name));
+ DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
+#if 0
+ if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
+ printf("warning, cannot do ulaw rec + play on the GUS\n");
+ r = 0 ; /* move to U8 */
+ }
+#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
+ /*
+ * not sure if this is really needed...
+ */
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
-static char * cs4236_probe(u_long csn, u_long vend_id);
-static void cs4236_attach(u_long csn, u_long vend_id, char *name,
+static char * cs423x_probe(u_long csn, u_long vend_id);
+static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
-static struct pnp_device cs4236 = {
- "cs423x",
- cs4236_probe,
- cs4236_attach,
+static struct pnp_device cs423x = {
+ "cs423x/ymh0020",
+ cs423x_probe,
+ cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
-DATA_SET (pnpdevice_set, cs4236);
+DATA_SET (pnpdevice_set, cs423x);
static char *
-cs4236_probe(u_long csn, u_long vend_id)
+cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x3742630e)
+ u_long id = vend_id & 0xff00ffff;
+ if ( id == 0x3700630e )
s = "CS4237" ;
- else if (vend_id == 0x3642630e)
+ else if ( id == 0x3600630e )
s = "CS4236" ;
- else if (vend_id == 0x360b630e)
- s = "CS4236" ;
- else if (vend_id == 0x3242630e)
+ else if ( id == 0x3200630e)
s = "CS4232" ;
+ else if ( id == 0x2000a865)
+ s = "Yamaha SA2";
+ else if (vend_id == 0x8140d315)
+ s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
-cs4236_attach(u_long csn, u_long vend_id, char *name,
+cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) {
- printf("CS423x use sb-compatible codec\n");
- dev->id_iobase = d.port[2] ;
+ if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- tmp_d.alt_base = d.port[0] - 4;
+ if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
+ /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
+ dev->id_iobase = d.port[0] ;
+ tmp_d.alt_base = d.port[1] ;
+ d.irq[1] = 0 ; /* only needed for the VIVO */
+ } else {
+ dev->id_iobase = d.port[2] ;
+ tmp_d.alt_base = d.port[0] - 4;
+ }
d.drq[1] = 4 ; /* disable, it is not used ... */
- } else {
- /* mss-compatible codec */
- dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ } else { /* mss-compatible codec */
+ dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
- switch (vend_id) {
- case 0x3742630e: /* CS4237 */
- case 0x3642630e: /* CS4236 */
- case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
- tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
+ dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ tmp_d.alt_base = d.port[2];
+ switch (vend_id & 0xff00ffff) {
+
+ case 0x2000a865: /* yamaha SA-2 */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_YM0020 ;
+ break;
+
+ case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_VIVO ;
+ d.irq[1] = 0 ;
break;
- default:
+
+ case 0x3700630e: /* CS4237 */
+ tmp_d.bd_id = MD_CS4237 ;
+ break;
+
+ case 0x3600630e: /* CS4236 */
+ tmp_d.bd_id = MD_CS4236 ;
+ break;
+
+ default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
- tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- dev->id_alive = 1;
pcmattach(dev);
}
@@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
- opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
+ opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static void gus_mem_cfg(snddev_info *tmp);
+
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ u_char tmp;
+
read_pnp_parms ( &d , 0 ) ;
+
+ /* d.irq[1] = d.irq[0] ; */
+ printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
+ pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
+
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
- dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
+
+ tmp_d.io_base = d.port[2] - 4;
+ tmp_d.alt_base = d.port[0]; /* 0x220 */
+ tmp_d.conf_base = d.port[1]; /* gus control block... */
+ tmp_d.bd_id = MD_GUSPNP ;
+
+ /* reset */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
+ DELAY(1000 * 30);
+ /* release reset and enable DAC */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
+ printf("resetting the gus...\n");
+ DELAY(1000 * 30);
+ /* end of reset */
- tmp_d.alt_base = d.port[0];
+ outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
+
+ /*
+ * unmute left & right line. Need to go in mode3, unmute,
+ * and back to mode 2
+ */
+ tmp = ad_read(&tmp_d, 0x0c);
+ ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
+ ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
+ ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
+ ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
+
+ /* send codec interrupts on irq1 and only use that one */
+ gus_write(tmp_d.conf_base, 0x5a , 0x4f );
+
+ /* enable access to hidden regs */
+ tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
+ gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
+ printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+
+ strcpy(tmp_d.name, name);
pcmattach(dev);
}
+
+#if 0
+int
+gus_mem_write(snddev_info *d, int addr, u_char data)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ outb(d->conf_base + 7, data);
+}
+
+u_char
+gus_mem_read(snddev_info *d, int addr)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ return inb(d->conf_base + 7);
+}
+
+void
+gus_mem_cfg(snddev_info *d)
+{
+ int base;
+ u_char old;
+ u_char a, b;
+
+ printf("configuring gus memory...\n");
+ gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
+ old = gus_read(d->conf_base, 0x19);
+ gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
+ for (base = 0; base < 1024; base++) {
+ a=gus_mem_read(d, base*1024);
+ a = ~a ;
+ gus_mem_write(d, base*1024, a);
+ b=gus_mem_read(d, base*1024);
+ if ( b != a )
+ break ;
+ }
+ printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
+}
+#endif /* gus mem cfg... */
+
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */
diff --git a/sys/dev/pcm/isa/mss.h b/sys/dev/pcm/isa/mss.h
index 4b34e51..994f323 100644
--- a/sys/dev/pcm/isa/mss.h
+++ b/sys/dev/pcm/isa/mss.h
@@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
-#define GUSMAX_MIXER
-#ifdef GUSMAX_MIXER
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
-
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#else /* Generic mapping */
-
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#define MSS_REC_DEVICES \
+ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
-#endif
-
-#define OPTI931_MIXER_DEVICES \
- (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
- SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
- * Most of the mixer entries work in backwards. Setting the polarity field
- * makes them to work correctly.
+ * Table of mixer registers. There is a default table for the
+ * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * clones come out, there ought to be more tables.
+ *
+ * Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
- * The current version doesn't try to compensate this.
+ *
+ * Following there is a macro ...MIXER_DEVICES which is a bitmap
+ * of all non-zero fields in the table.
+ * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
+ * registers I0..I15)
*
*/
-mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
-MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
+mixer_ent mix_devices[32][2] = {
+MIX_NONE(SOUND_MIXER_VOLUME),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
+MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
-MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_NONE(SOUND_MIXER_LINE1),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define MODE2_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+#define MODE1_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+
+/*
+ * entries for the opti931...
+ */
+
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_IMIX),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define OPTI931_MIXER_DEVICES \
+ (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
+
+
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c
index e786f5f..d4825c1 100644
--- a/sys/dev/pcm/isa/sb.c
+++ b/sys/dev/pcm/isa/sb.c
@@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
- * Remebber, isa probe routines are supposed to return the
+ * Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
+ dev->id_alive = 16 ; /* number of io ports */
+ /* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
+ /* XXX previous location of ack... */
+ DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
+ if ( d->dbuf_out.dl && (reason & 1) )
+ dsp_wrintr(d);
+ if ( d->dbuf_in.dl && (reason & 2) )
+ dsp_rdintr(d);
+
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
-DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
- if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
- dsp_wrintr(d);
- if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
- dsp_rdintr(d);
-
/*
* the sb16 might have multiple sources etc.
*/
@@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
- int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+ int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
- break ;
+ break;
case SND_CB_START : /* called with int disabled */
- sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
- d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
+ * We use the following algorithm:
+ * 1. check which direction(s) are active;
+ * 2. check if we should swap dma channels
+ * 3. check if we can do the swap.
*/
- int b16 ;
- int swap = 0 ;
+ int swap = 1 ; /* default... */
- b16 = (rd) ? d->rec_fmt : d->play_fmt ;
- b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
- /*
- * check if I have to swap dma channels. Swap if
- * - !rd, dma1 <4, b16
- * - !rd, dma1 >=4, !b16
- * - rd, dma2 <4, b16
- * - rd, dma2 >=4, !b16
- */
- if (!rd) {
- if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ if (rd) {
+ if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
+ swap = 0;
+ if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
+ swap = 0;
+ if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
+ swap = 0;
} else {
- if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ if (d->flags & SND_F_READING || d->dbuf_in.dl)
+ swap = 0;
+ if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
+ swap = 0;
+ if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
+ swap = 0;
}
- /*
- * before swapping should make sure that there is no
- * pending DMA on the other channel...
- */
+
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ DEB(printf("START dma chan: play %d, rec %d\n",
+ d->dma1, d->dma2));
}
- DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
- d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
- /* fallthrough */
- case SND_CB_RESTART:
+ if (!rd)
+ sb_cmd(d->io_base, DSP_CMD_SPKON);
+
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
- /*
- * SB16 support still not completely working!!!
- *
- * in principle, on the SB16, I could support simultaneous
- * play & rec.
- * However, there is no way to ask explicitly for 8 or
- * 16 bit transfer. As a consequence, if we do 8-bit,
- * we need to use the 8-bit channel, and if we do 16-bit,
- * we need to use the other one. The only way I find to
- * do this is to swap d->dma1 and d->dma2 ...
- *
- */
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
- c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
- if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
- c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
+ /* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
- c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
+ case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
- /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
+ {
+ int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
+ if (d->bd_flags & BD_F_SB16) {
+ if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
+ cmd = DSP_CMD_DMAPAUSE_16 ;
+ }
+ if (d->bd_flags & BD_F_HISPEED) {
+ sb_reset_dsp(d->io_base);
+ d->flags |= SND_F_INIT ;
+ } else {
+ sb_cmd(d->io_base, cmd); /* pause dma. */
+ /*
+ * This seems to have the side effect of blocking the other
+ * side as well so I have to re-enable it :(
+ */
+ if ( (rd && d->dbuf_out.dl) ||
+ (!rd && d->dbuf_in.dl) )
+ sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
+ 0xd6 : 0xd4); /* continue other dma */
+ }
+ }
+ DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
- DEB(printf("sb_reset_dsp failed\n"));
+ DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
- * XXX check this code...
+ * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
- tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x01009305)
- s = "Avance Asound 100" ;
- if (vend_id == 0x2b008c0e)
- s = "SB16 Value PnP" ;
+
/*
- * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
- s = "SB AWE64 PnP";
+ s = "SB16 PnP";
+ else if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h
index 8548997..d7a24d5 100644
--- a/sys/dev/pcm/isa/sb.h
+++ b/sys/dev/pcm/isa/sb.h
@@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
+/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
+#define DSP_ADC8 0x20 /* direct ADC input */
+
+/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
+#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
+
+/* these should be used in the SB 2.0 and 2.01 */
+#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
+#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
+
+#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
+#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
+#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
+
+/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
+
+ /* prepare for dma input */
+#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
+
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
-#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
-#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
-
-#define DSP_ADC8 0x20 /* direct ADC input */
+#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
-#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
-#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
+/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
-#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
-#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
-#define DSP_CMD_DMAHALT 0xD0
+#define DSP_CMD_DMAPAUSE_8 0xD0
+#define DSP_CMD_DMAPAUSE_16 0xD5
+#define DSP_CMD_DMAEXIT_8 0xDA
+#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
-#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
-#define DSP_CMD_HSADC 0x99 /* high speed adc */
+#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
- /* prepare for dma input */
-#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
- /*
- * D9 and D5 are used on the sb16 on close... maybe a reset of
- * some subsystem ?
- */
-#define DSP_CMD_D9 0xD9
-#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c
index 1188d32..0cd2dfd 100644
--- a/sys/dev/sound/isa/mss.c
+++ b/sys/dev/sound/isa/mss.c
@@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
- * Modified by Luigi Rizzo (luigi@iet.unipi.it)
- *
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248
- *
- * CS4231, used in the GUS MAX and some other cards;
- * AD1845, CS4231A (CS4231-like)
- * CS4232 (CS4231+SB and MPU, PnP)
- * CS4236 (upgrade of the CS4232, has a better mixer)
- * OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- *
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
- *
- * The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
+ * but we do not use these modes.
*/
} ;
/*
- * this is the probe routine. Note, it is not necessary to
+ * mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
+ dev->id_alive = 8 ; /* number of io ports */
+ /* should be already set but just in case... */
+
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
+ dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
+ if (d->dma1 != d->dma2)
+ d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
- ask_init(d);
+ ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
+ * With the exception of INIT, all other callbacks are invoked
+ * with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
- u_long s;
u_char m;
int retry, wr, cnt;
@@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
- /* fallthrough */
- case SND_CB_RESTART :
- s = spltty();
- cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
+ cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
- DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
+ DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
- splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
- s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
-
- /* disable DMA by clearing count registers. */
+#if 1
+ /*
+ * try to disable DMA by clearing count registers. Not sure it
+ * is needed, and it might cause false interrupts when the
+ * DMA is re-enabled later.
+ */
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
- splx(s);
break;
+#endif
}
return 0 ;
}
@@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
+ * The correct way to operate for a device with multiple internal
+ * interrupt sources is to loop on the status register and ack
+ * interrupts until all interrupts are served and none are reported. At
+ * this point the IRQ line to the ISA IRQ controller should go low
+ * and be raised at the next interrupt.
+ *
+ * Since the ISA IRQ controller is sent EOI _before_ passing control
+ * to the isr, it might happen that we serve an interrupt early, in
+ * which case the status register at the next interrupt should just
+ * say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
- u_char i11, c;
- u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
+ u_char c, served = 0;
+ int i;
- i11 = ad_read(d, 11);
- reason = inb(io_Status(d));
+ DEB(printf("mss_intr\n"));
+ ad_read(d, 11); /* fake read of status bits */
- if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
- return;
- /* get exact reason */
- c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
- if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
- dsp_wrintr(d);
- c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
- if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
- dsp_rdintr(d);
- /* XXX check this on the 4236... */
- if (d->dma1 == d->dma2)
- outb(io_Status(d), 0); /* Clear interrupt status */
- else
- ad_write(d, 24, ~c); /* ack selectively */
+ /*
+ * loop until there are interrupts, but no more than 10 times.
+ */
+ for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
+ /* get exact reason for full-duplex boards */
+ c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
+ c &= ~served ;
+ if ( d->dbuf_out.dl && (c & 0x10) ) {
+ served |= 0x10 ;
+ dsp_wrintr(d);
+ }
+ if ( d->dbuf_in.dl && (c & 0x20) ) {
+ served |= 0x20 ;
+ dsp_rdintr(d);
+ }
+ /*
+ * now ack the interrupt
+ */
+ if (d->dma1 == d->dma2)
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ else
+ ad_write(d, 24, ~c); /* ack selectively */
+ }
}
/*
* the opti931 seems to miss interrupts when working in full
- * duplex. god know why...
+ * duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
- int w_miss=0, r_miss=0;
- static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning CD interrupt\n");
+ printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning MPU interrupt\n");
+ printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
- if (w_miss || r_miss ) {
- misses++;
- if ( (misses & 0xff) == 1 )
- printf("opti931: %d missed irq (now %s%s)\n",
- misses, w_miss?"play ":"",
- r_miss?"capture ":"");
- }
- if (loops==10)
+ if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- if (w_miss) mc11 |= 4 ;
- if (r_miss) mc11 |= 8 ;
- if (mc11==0)
- return;
+ }
+ return;
}
- if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
+ if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
- r_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
- r_miss = 1 ;
-
- if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
- if (isa_dmastatus1(d->dma1) != 0)
- printf("opti931_intr: wr dma %d\n",
- isa_dmastatus1(d->dma1));
+ if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
- w_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
- w_miss = 1 ;
-
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
+
+static void
+gus_write(int io_base, u_char reg, u_char value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 5, value);
+}
+
+static void
+gus_writew(int io_base, u_char reg, u_short value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 4, value);
+}
+
+static u_char
+gus_read(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inb(io_base+5);
+}
+
+static u_short
+gus_readw(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inw(io_base+4);
+}
+
+
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
- outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
-
- outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
- *
- * fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
- DDB(printf("nbits = 0 for dev %d\n", dev) );
+ DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
- DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
+ DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
- val = ad_read(d, regoffs);
+ old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
+ DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
+ dev, regoffs, old, val));
}
return 0; /* success */
}
@@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
- d->mix_rec_devs = MODE1_REC_DEVICES;
+ d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
+ /*
+ * some device-specific things, mostly mute the mic to
+ * the output mixer so as to avoid hisses. In many cases this
+ * is the default after reset, this code is here mostly as a
+ * reminder that this might be necessary on other boards.
+ */
+ switch(d->bd_id) {
+ case MD_OPTI931:
+ ad_write(d, 20, 0x88);
+ ad_write(d, 21, 0x88);
+ break;
+ case MD_GUSPNP:
+ /* this is only necessary in mode 3 ... */
+ ad_write(d, 22, 0x88);
+ ad_write(d, 23, 0x88);
+ }
}
/*
@@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
- *
- * fixed lr970724
*/
static int
@@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DDB(printf("mss_detect error, busy still set (0x%02x)\n",
+ DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
- return 0;
+ return 0;
}
ad_write(d, 0, 0x45);
@@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DDB(printf("mss_detect error - step H(%x)\n", tmp1));
+ DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DDB(printf("mss_detect() - step I\n"));
+ DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
- case 0x03: /* CS4236 on Intel PR440FX motherboard */
+ case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
- DDB(printf("mss_detect() - Detected %s\n", name));
+ DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
+#if 0
+ if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
+ printf("warning, cannot do ulaw rec + play on the GUS\n");
+ r = 0 ; /* move to U8 */
+ }
+#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
+ /*
+ * not sure if this is really needed...
+ */
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
-static char * cs4236_probe(u_long csn, u_long vend_id);
-static void cs4236_attach(u_long csn, u_long vend_id, char *name,
+static char * cs423x_probe(u_long csn, u_long vend_id);
+static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
-static struct pnp_device cs4236 = {
- "cs423x",
- cs4236_probe,
- cs4236_attach,
+static struct pnp_device cs423x = {
+ "cs423x/ymh0020",
+ cs423x_probe,
+ cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
-DATA_SET (pnpdevice_set, cs4236);
+DATA_SET (pnpdevice_set, cs423x);
static char *
-cs4236_probe(u_long csn, u_long vend_id)
+cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x3742630e)
+ u_long id = vend_id & 0xff00ffff;
+ if ( id == 0x3700630e )
s = "CS4237" ;
- else if (vend_id == 0x3642630e)
+ else if ( id == 0x3600630e )
s = "CS4236" ;
- else if (vend_id == 0x360b630e)
- s = "CS4236" ;
- else if (vend_id == 0x3242630e)
+ else if ( id == 0x3200630e)
s = "CS4232" ;
+ else if ( id == 0x2000a865)
+ s = "Yamaha SA2";
+ else if (vend_id == 0x8140d315)
+ s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
-cs4236_attach(u_long csn, u_long vend_id, char *name,
+cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) {
- printf("CS423x use sb-compatible codec\n");
- dev->id_iobase = d.port[2] ;
+ if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- tmp_d.alt_base = d.port[0] - 4;
+ if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
+ /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
+ dev->id_iobase = d.port[0] ;
+ tmp_d.alt_base = d.port[1] ;
+ d.irq[1] = 0 ; /* only needed for the VIVO */
+ } else {
+ dev->id_iobase = d.port[2] ;
+ tmp_d.alt_base = d.port[0] - 4;
+ }
d.drq[1] = 4 ; /* disable, it is not used ... */
- } else {
- /* mss-compatible codec */
- dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ } else { /* mss-compatible codec */
+ dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
- switch (vend_id) {
- case 0x3742630e: /* CS4237 */
- case 0x3642630e: /* CS4236 */
- case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
- tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
+ dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ tmp_d.alt_base = d.port[2];
+ switch (vend_id & 0xff00ffff) {
+
+ case 0x2000a865: /* yamaha SA-2 */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_YM0020 ;
+ break;
+
+ case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_VIVO ;
+ d.irq[1] = 0 ;
break;
- default:
+
+ case 0x3700630e: /* CS4237 */
+ tmp_d.bd_id = MD_CS4237 ;
+ break;
+
+ case 0x3600630e: /* CS4236 */
+ tmp_d.bd_id = MD_CS4236 ;
+ break;
+
+ default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
- tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- dev->id_alive = 1;
pcmattach(dev);
}
@@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
- opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
+ opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static void gus_mem_cfg(snddev_info *tmp);
+
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ u_char tmp;
+
read_pnp_parms ( &d , 0 ) ;
+
+ /* d.irq[1] = d.irq[0] ; */
+ printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
+ pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
+
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
- dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
+
+ tmp_d.io_base = d.port[2] - 4;
+ tmp_d.alt_base = d.port[0]; /* 0x220 */
+ tmp_d.conf_base = d.port[1]; /* gus control block... */
+ tmp_d.bd_id = MD_GUSPNP ;
+
+ /* reset */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
+ DELAY(1000 * 30);
+ /* release reset and enable DAC */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
+ printf("resetting the gus...\n");
+ DELAY(1000 * 30);
+ /* end of reset */
- tmp_d.alt_base = d.port[0];
+ outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
+
+ /*
+ * unmute left & right line. Need to go in mode3, unmute,
+ * and back to mode 2
+ */
+ tmp = ad_read(&tmp_d, 0x0c);
+ ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
+ ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
+ ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
+ ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
+
+ /* send codec interrupts on irq1 and only use that one */
+ gus_write(tmp_d.conf_base, 0x5a , 0x4f );
+
+ /* enable access to hidden regs */
+ tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
+ gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
+ printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+
+ strcpy(tmp_d.name, name);
pcmattach(dev);
}
+
+#if 0
+int
+gus_mem_write(snddev_info *d, int addr, u_char data)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ outb(d->conf_base + 7, data);
+}
+
+u_char
+gus_mem_read(snddev_info *d, int addr)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ return inb(d->conf_base + 7);
+}
+
+void
+gus_mem_cfg(snddev_info *d)
+{
+ int base;
+ u_char old;
+ u_char a, b;
+
+ printf("configuring gus memory...\n");
+ gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
+ old = gus_read(d->conf_base, 0x19);
+ gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
+ for (base = 0; base < 1024; base++) {
+ a=gus_mem_read(d, base*1024);
+ a = ~a ;
+ gus_mem_write(d, base*1024, a);
+ b=gus_mem_read(d, base*1024);
+ if ( b != a )
+ break ;
+ }
+ printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
+}
+#endif /* gus mem cfg... */
+
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */
diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h
index 4b34e51..994f323 100644
--- a/sys/dev/sound/isa/mss.h
+++ b/sys/dev/sound/isa/mss.h
@@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
-#define GUSMAX_MIXER
-#ifdef GUSMAX_MIXER
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
-
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#else /* Generic mapping */
-
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#define MSS_REC_DEVICES \
+ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
-#endif
-
-#define OPTI931_MIXER_DEVICES \
- (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
- SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
- * Most of the mixer entries work in backwards. Setting the polarity field
- * makes them to work correctly.
+ * Table of mixer registers. There is a default table for the
+ * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * clones come out, there ought to be more tables.
+ *
+ * Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
- * The current version doesn't try to compensate this.
+ *
+ * Following there is a macro ...MIXER_DEVICES which is a bitmap
+ * of all non-zero fields in the table.
+ * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
+ * registers I0..I15)
*
*/
-mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
-MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
+mixer_ent mix_devices[32][2] = {
+MIX_NONE(SOUND_MIXER_VOLUME),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
+MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
-MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_NONE(SOUND_MIXER_LINE1),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define MODE2_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+#define MODE1_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+
+/*
+ * entries for the opti931...
+ */
+
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_IMIX),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define OPTI931_MIXER_DEVICES \
+ (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
+
+
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c
index e786f5f..d4825c1 100644
--- a/sys/dev/sound/isa/sb.c
+++ b/sys/dev/sound/isa/sb.c
@@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
- * Remebber, isa probe routines are supposed to return the
+ * Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
+ dev->id_alive = 16 ; /* number of io ports */
+ /* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
+ /* XXX previous location of ack... */
+ DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
+ if ( d->dbuf_out.dl && (reason & 1) )
+ dsp_wrintr(d);
+ if ( d->dbuf_in.dl && (reason & 2) )
+ dsp_rdintr(d);
+
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
-DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
- if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
- dsp_wrintr(d);
- if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
- dsp_rdintr(d);
-
/*
* the sb16 might have multiple sources etc.
*/
@@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
- int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+ int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
- break ;
+ break;
case SND_CB_START : /* called with int disabled */
- sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
- d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
+ * We use the following algorithm:
+ * 1. check which direction(s) are active;
+ * 2. check if we should swap dma channels
+ * 3. check if we can do the swap.
*/
- int b16 ;
- int swap = 0 ;
+ int swap = 1 ; /* default... */
- b16 = (rd) ? d->rec_fmt : d->play_fmt ;
- b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
- /*
- * check if I have to swap dma channels. Swap if
- * - !rd, dma1 <4, b16
- * - !rd, dma1 >=4, !b16
- * - rd, dma2 <4, b16
- * - rd, dma2 >=4, !b16
- */
- if (!rd) {
- if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ if (rd) {
+ if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
+ swap = 0;
+ if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
+ swap = 0;
+ if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
+ swap = 0;
} else {
- if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ if (d->flags & SND_F_READING || d->dbuf_in.dl)
+ swap = 0;
+ if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
+ swap = 0;
+ if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
+ swap = 0;
}
- /*
- * before swapping should make sure that there is no
- * pending DMA on the other channel...
- */
+
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ DEB(printf("START dma chan: play %d, rec %d\n",
+ d->dma1, d->dma2));
}
- DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
- d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
- /* fallthrough */
- case SND_CB_RESTART:
+ if (!rd)
+ sb_cmd(d->io_base, DSP_CMD_SPKON);
+
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
- /*
- * SB16 support still not completely working!!!
- *
- * in principle, on the SB16, I could support simultaneous
- * play & rec.
- * However, there is no way to ask explicitly for 8 or
- * 16 bit transfer. As a consequence, if we do 8-bit,
- * we need to use the 8-bit channel, and if we do 16-bit,
- * we need to use the other one. The only way I find to
- * do this is to swap d->dma1 and d->dma2 ...
- *
- */
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
- c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
- if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
- c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
+ /* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
- c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
+ case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
- /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
+ {
+ int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
+ if (d->bd_flags & BD_F_SB16) {
+ if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
+ cmd = DSP_CMD_DMAPAUSE_16 ;
+ }
+ if (d->bd_flags & BD_F_HISPEED) {
+ sb_reset_dsp(d->io_base);
+ d->flags |= SND_F_INIT ;
+ } else {
+ sb_cmd(d->io_base, cmd); /* pause dma. */
+ /*
+ * This seems to have the side effect of blocking the other
+ * side as well so I have to re-enable it :(
+ */
+ if ( (rd && d->dbuf_out.dl) ||
+ (!rd && d->dbuf_in.dl) )
+ sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
+ 0xd6 : 0xd4); /* continue other dma */
+ }
+ }
+ DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
- DEB(printf("sb_reset_dsp failed\n"));
+ DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
- * XXX check this code...
+ * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
- tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x01009305)
- s = "Avance Asound 100" ;
- if (vend_id == 0x2b008c0e)
- s = "SB16 Value PnP" ;
+
/*
- * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
- s = "SB AWE64 PnP";
+ s = "SB16 PnP";
+ else if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h
index 8548997..d7a24d5 100644
--- a/sys/dev/sound/isa/sb.h
+++ b/sys/dev/sound/isa/sb.h
@@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
+/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
+#define DSP_ADC8 0x20 /* direct ADC input */
+
+/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
+#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
+
+/* these should be used in the SB 2.0 and 2.01 */
+#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
+#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
+
+#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
+#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
+#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
+
+/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
+
+ /* prepare for dma input */
+#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
+
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
-#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
-#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
-
-#define DSP_ADC8 0x20 /* direct ADC input */
+#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
-#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
-#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
+/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
-#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
-#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
-#define DSP_CMD_DMAHALT 0xD0
+#define DSP_CMD_DMAPAUSE_8 0xD0
+#define DSP_CMD_DMAPAUSE_16 0xD5
+#define DSP_CMD_DMAEXIT_8 0xDA
+#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
-#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
-#define DSP_CMD_HSADC 0x99 /* high speed adc */
+#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
- /* prepare for dma input */
-#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
- /*
- * D9 and D5 are used on the sb16 on close... maybe a reset of
- * some subsystem ?
- */
-#define DSP_CMD_D9 0xD9
-#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c
index e786f5f..d4825c1 100644
--- a/sys/dev/sound/isa/sb16.c
+++ b/sys/dev/sound/isa/sb16.c
@@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
- * Remebber, isa probe routines are supposed to return the
+ * Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
+ dev->id_alive = 16 ; /* number of io ports */
+ /* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
+ /* XXX previous location of ack... */
+ DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
+ if ( d->dbuf_out.dl && (reason & 1) )
+ dsp_wrintr(d);
+ if ( d->dbuf_in.dl && (reason & 2) )
+ dsp_rdintr(d);
+
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
-DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
- if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
- dsp_wrintr(d);
- if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
- dsp_rdintr(d);
-
/*
* the sb16 might have multiple sources etc.
*/
@@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
- int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+ int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
- break ;
+ break;
case SND_CB_START : /* called with int disabled */
- sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
- d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
+ * We use the following algorithm:
+ * 1. check which direction(s) are active;
+ * 2. check if we should swap dma channels
+ * 3. check if we can do the swap.
*/
- int b16 ;
- int swap = 0 ;
+ int swap = 1 ; /* default... */
- b16 = (rd) ? d->rec_fmt : d->play_fmt ;
- b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
- /*
- * check if I have to swap dma channels. Swap if
- * - !rd, dma1 <4, b16
- * - !rd, dma1 >=4, !b16
- * - rd, dma2 <4, b16
- * - rd, dma2 >=4, !b16
- */
- if (!rd) {
- if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ if (rd) {
+ if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
+ swap = 0;
+ if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
+ swap = 0;
+ if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
+ swap = 0;
} else {
- if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ if (d->flags & SND_F_READING || d->dbuf_in.dl)
+ swap = 0;
+ if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
+ swap = 0;
+ if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
+ swap = 0;
}
- /*
- * before swapping should make sure that there is no
- * pending DMA on the other channel...
- */
+
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ DEB(printf("START dma chan: play %d, rec %d\n",
+ d->dma1, d->dma2));
}
- DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
- d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
- /* fallthrough */
- case SND_CB_RESTART:
+ if (!rd)
+ sb_cmd(d->io_base, DSP_CMD_SPKON);
+
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
- /*
- * SB16 support still not completely working!!!
- *
- * in principle, on the SB16, I could support simultaneous
- * play & rec.
- * However, there is no way to ask explicitly for 8 or
- * 16 bit transfer. As a consequence, if we do 8-bit,
- * we need to use the 8-bit channel, and if we do 16-bit,
- * we need to use the other one. The only way I find to
- * do this is to swap d->dma1 and d->dma2 ...
- *
- */
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
- c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
- if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
- c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
+ /* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
- c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
+ case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
- /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
+ {
+ int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
+ if (d->bd_flags & BD_F_SB16) {
+ if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
+ cmd = DSP_CMD_DMAPAUSE_16 ;
+ }
+ if (d->bd_flags & BD_F_HISPEED) {
+ sb_reset_dsp(d->io_base);
+ d->flags |= SND_F_INIT ;
+ } else {
+ sb_cmd(d->io_base, cmd); /* pause dma. */
+ /*
+ * This seems to have the side effect of blocking the other
+ * side as well so I have to re-enable it :(
+ */
+ if ( (rd && d->dbuf_out.dl) ||
+ (!rd && d->dbuf_in.dl) )
+ sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
+ 0xd6 : 0xd4); /* continue other dma */
+ }
+ }
+ DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
- DEB(printf("sb_reset_dsp failed\n"));
+ DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
- * XXX check this code...
+ * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
- tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x01009305)
- s = "Avance Asound 100" ;
- if (vend_id == 0x2b008c0e)
- s = "SB16 Value PnP" ;
+
/*
- * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
- s = "SB AWE64 PnP";
+ s = "SB16 PnP";
+ else if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c
index e786f5f..d4825c1 100644
--- a/sys/dev/sound/isa/sb8.c
+++ b/sys/dev/sound/isa/sb8.c
@@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
- * Remebber, isa probe routines are supposed to return the
+ * Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
+ dev->id_alive = 16 ; /* number of io ports */
+ /* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
+ /* XXX previous location of ack... */
+ DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
+ if ( d->dbuf_out.dl && (reason & 1) )
+ dsp_wrintr(d);
+ if ( d->dbuf_in.dl && (reason & 2) )
+ dsp_rdintr(d);
+
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
-DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
- if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
- dsp_wrintr(d);
- if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
- dsp_rdintr(d);
-
/*
* the sb16 might have multiple sources etc.
*/
@@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
- int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+ int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
- break ;
+ break;
case SND_CB_START : /* called with int disabled */
- sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
- d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
+ * We use the following algorithm:
+ * 1. check which direction(s) are active;
+ * 2. check if we should swap dma channels
+ * 3. check if we can do the swap.
*/
- int b16 ;
- int swap = 0 ;
+ int swap = 1 ; /* default... */
- b16 = (rd) ? d->rec_fmt : d->play_fmt ;
- b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
- /*
- * check if I have to swap dma channels. Swap if
- * - !rd, dma1 <4, b16
- * - !rd, dma1 >=4, !b16
- * - rd, dma2 <4, b16
- * - rd, dma2 >=4, !b16
- */
- if (!rd) {
- if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ if (rd) {
+ if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
+ swap = 0;
+ if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
+ swap = 0;
+ if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
+ swap = 0;
} else {
- if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ if (d->flags & SND_F_READING || d->dbuf_in.dl)
+ swap = 0;
+ if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
+ swap = 0;
+ if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
+ swap = 0;
}
- /*
- * before swapping should make sure that there is no
- * pending DMA on the other channel...
- */
+
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ DEB(printf("START dma chan: play %d, rec %d\n",
+ d->dma1, d->dma2));
}
- DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
- d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
- /* fallthrough */
- case SND_CB_RESTART:
+ if (!rd)
+ sb_cmd(d->io_base, DSP_CMD_SPKON);
+
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
- /*
- * SB16 support still not completely working!!!
- *
- * in principle, on the SB16, I could support simultaneous
- * play & rec.
- * However, there is no way to ask explicitly for 8 or
- * 16 bit transfer. As a consequence, if we do 8-bit,
- * we need to use the 8-bit channel, and if we do 16-bit,
- * we need to use the other one. The only way I find to
- * do this is to swap d->dma1 and d->dma2 ...
- *
- */
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
- c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
- if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
- c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
+ /* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
- c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
+ case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
- /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
+ {
+ int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
+ if (d->bd_flags & BD_F_SB16) {
+ if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
+ cmd = DSP_CMD_DMAPAUSE_16 ;
+ }
+ if (d->bd_flags & BD_F_HISPEED) {
+ sb_reset_dsp(d->io_base);
+ d->flags |= SND_F_INIT ;
+ } else {
+ sb_cmd(d->io_base, cmd); /* pause dma. */
+ /*
+ * This seems to have the side effect of blocking the other
+ * side as well so I have to re-enable it :(
+ */
+ if ( (rd && d->dbuf_out.dl) ||
+ (!rd && d->dbuf_in.dl) )
+ sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
+ 0xd6 : 0xd4); /* continue other dma */
+ }
+ }
+ DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
- DEB(printf("sb_reset_dsp failed\n"));
+ DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
- * XXX check this code...
+ * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
- tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x01009305)
- s = "Avance Asound 100" ;
- if (vend_id == 0x2b008c0e)
- s = "SB16 Value PnP" ;
+
/*
- * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
- s = "SB AWE64 PnP";
+ s = "SB16 PnP";
+ else if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README
index 70e3c11..ac45b22 100644
--- a/sys/i386/isa/snd/README
+++ b/sys/i386/isa/snd/README
@@ -1,8 +1,8 @@
- --- A new FreeBSD sound driver ---
- by Luigi Rizzo (luigi@iet.unipi.it)
+ --- A new FreeBSD audio driver ---
+ by Luigi Rizzo (luigi@iet.unipi.it)
-This is an experimental version of the sound driver for FreeBSD.
+This is an experimental version of the audio driver for FreeBSD.
I have almost completely rewritten the main parts of the code,
starting from the Voxware 3.5-alpha release with patches from
Amancio Hasty. The only file which is largely similar to the original
@@ -20,15 +20,11 @@ For PnP cards, I also include the vendor_id and serial numbers of
cards I have encountered.
CS4236: PnP id 0x3642630e
+CS4237: PnP id 0x3742630e
works like a charm. All modes, including full duplex, supported in
MSS mode.
-CS4237: PnP id 0x3742630e
-
- I had early reports of success with this board, which is almost
- the same as the CS4236.
-
CS4232: PnP id 0x3242630e
this chip is reported as broken in the OSS documentation. As a
@@ -39,32 +35,66 @@ CS4232: PnP id 0x3242630e
OPTi931: PnP id 0x3109143e
- The data sheets of this chip are very cryptic. I have it working
- in full duplex in all modes _except_ capture of uLAW/ALAW data.
- I am strongly convinced of a bug in the chip. I have sent email
- to OPTI but got no reply so far. In SB emulation mode the
- driver does not work yet (maybe I do not initialize it the
- right way).
-
- Another bug seems to affect full duplex operation -- it appears
- that at times DMA transfer are requested but not counted by
- the device. In normal DMA mode this causes deadlocks. The only
- solution I have found is to fetch the count from the ISA DMA
- registers, but this does not seem to work very well either.
+ The data sheets of this chip are very cryptic and do not match
+ what the cards I have seem to do. I have it working
+ in WSS emulation, in full duplex and all modes.
+ In SB emulation mode the driver does not work yet (maybe I do
+ not initialize it the right way). It is not a major concern
+ anyways.
+ I am strongly convinced of a couple of bugs in the chip. I have
+ sent email to OPTI but got no reply so far. The bugs are:
+ - you cannot set both playback and capture format to use
+ a companded mode (ULAW, ALAW). If you do, the card will screw
+ up on the capture section.
+ The driver has a partial fix in software: when using ULAW, it
+ programs ULAW on the playback section, U8 on the capture, and
+ does a conversion in software (much like the SBPro). Of course
+ you lose 4-5 bits of dynamic range in the process.
+ - in full duplex (and single DMA mode), the card occasionally
+ misses interrupts, or generates spurious ones. Spurious ints are
+ not problematic since they can be ignored, but missed ones are
+ as you can imagine... This is fixed by auto-dma mode.
SB16 PnP: PnP id 0xXX008c0e
There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
- etc. all differing in the PnP id (and with different synthesis
- devices, which we do not support anyways).
+ etc.) all differing in the PnP id. They have different synthesis
+ devices, which we do not support, so we are not affected by these
+ differences. Don't worry if the driver identifies the card as a
+ different SB16 than the one you have.
+
+ Full duplex support of this card is tricky since one channel can
+ work in 16-bit and the other in 8-bit mode. You will need to use
+ the new set of ioctl to use separate data formats on the two
+ channels (the vat driver does this). Of course, quality in 8-bit
+ is much lower than in 16-bit.
+
+ Full duplex operation is unsupported by Creative. It seems to
+ work, although on my Vibra16 the command to stop DMA transfer
+ seems to erroneously affect both channels instead of the one
+ they are issued for. The driver has a workaround, but I cannot
+ guarantee that it works for everybody. I have had several
+ positive reports.
+
+Yamaha SA2
+
+ this card emulates a WSS or SB. Have reports that it works, although
+ it has mixer problems (maybe the driver has the wrong set of
+ parameters).
- Since 970903 the driver supports them all, both capture and
- playback, 8 and 16 bits.
+Ensoniq Soundscape VIVO
+
+ this card emulates a WSS or SB. Have reports that it works.
GusPnP: PnP id 0x0100561e
- I have code to recognize the board as MSS, but have not tested it
- since I don't own the board. Hopefully someone will test it soon.
+ I have code to recognize the board as MSS, but some initializations
+ of the IW chip is still missing, so the card is not supported yet.
+ I am working on this problem...
+
+OPTI924: PnP
+
+ I have this card but it is still unsupported.
OPTI925: PnP id 0x2509143e
@@ -76,3 +106,21 @@ OPTI930:
should work as an MSS clone, but support for it is not implemented
yet.
+ESS1868
+
+ this card is not supported yet. It might work in SB emulation but
+ am not sure.
+
+ESS688
+
+ this card is used on many notebook. I don't have docs on this card
+ so I cannot support it. Pointers to the data sheets are welcome.
+
+PCI cards:
+
+ some vendors have PCI cards. This code _cannot_ work on these
+ cards as it is now, since they cannot obviously use the ISA DMA
+ controller. As there are no data sheets available for these PCI
+ cards, none of them is supported at the moment, although support
+ should be easy as soon as I can put my hands on the data sheets
+ and cards.
diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c
index 1188d32..0cd2dfd 100644
--- a/sys/i386/isa/snd/ad1848.c
+++ b/sys/i386/isa/snd/ad1848.c
@@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
- * Modified by Luigi Rizzo (luigi@iet.unipi.it)
- *
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248
- *
- * CS4231, used in the GUS MAX and some other cards;
- * AD1845, CS4231A (CS4231-like)
- * CS4232 (CS4231+SB and MPU, PnP)
- * CS4236 (upgrade of the CS4232, has a better mixer)
- * OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- *
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
- *
- * The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
+ * but we do not use these modes.
*/
} ;
/*
- * this is the probe routine. Note, it is not necessary to
+ * mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
+ dev->id_alive = 8 ; /* number of io ports */
+ /* should be already set but just in case... */
+
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
+ dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
+ if (d->dma1 != d->dma2)
+ d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
- ask_init(d);
+ ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
+ * With the exception of INIT, all other callbacks are invoked
+ * with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
- u_long s;
u_char m;
int retry, wr, cnt;
@@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
- /* fallthrough */
- case SND_CB_RESTART :
- s = spltty();
- cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
+ cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
- DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
+ DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
- splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
- s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
-
- /* disable DMA by clearing count registers. */
+#if 1
+ /*
+ * try to disable DMA by clearing count registers. Not sure it
+ * is needed, and it might cause false interrupts when the
+ * DMA is re-enabled later.
+ */
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
- splx(s);
break;
+#endif
}
return 0 ;
}
@@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
+ * The correct way to operate for a device with multiple internal
+ * interrupt sources is to loop on the status register and ack
+ * interrupts until all interrupts are served and none are reported. At
+ * this point the IRQ line to the ISA IRQ controller should go low
+ * and be raised at the next interrupt.
+ *
+ * Since the ISA IRQ controller is sent EOI _before_ passing control
+ * to the isr, it might happen that we serve an interrupt early, in
+ * which case the status register at the next interrupt should just
+ * say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
- u_char i11, c;
- u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
+ u_char c, served = 0;
+ int i;
- i11 = ad_read(d, 11);
- reason = inb(io_Status(d));
+ DEB(printf("mss_intr\n"));
+ ad_read(d, 11); /* fake read of status bits */
- if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
- return;
- /* get exact reason */
- c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
- if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
- dsp_wrintr(d);
- c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
- if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
- dsp_rdintr(d);
- /* XXX check this on the 4236... */
- if (d->dma1 == d->dma2)
- outb(io_Status(d), 0); /* Clear interrupt status */
- else
- ad_write(d, 24, ~c); /* ack selectively */
+ /*
+ * loop until there are interrupts, but no more than 10 times.
+ */
+ for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
+ /* get exact reason for full-duplex boards */
+ c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
+ c &= ~served ;
+ if ( d->dbuf_out.dl && (c & 0x10) ) {
+ served |= 0x10 ;
+ dsp_wrintr(d);
+ }
+ if ( d->dbuf_in.dl && (c & 0x20) ) {
+ served |= 0x20 ;
+ dsp_rdintr(d);
+ }
+ /*
+ * now ack the interrupt
+ */
+ if (d->dma1 == d->dma2)
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ else
+ ad_write(d, 24, ~c); /* ack selectively */
+ }
}
/*
* the opti931 seems to miss interrupts when working in full
- * duplex. god know why...
+ * duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
- int w_miss=0, r_miss=0;
- static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning CD interrupt\n");
+ printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning MPU interrupt\n");
+ printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
- if (w_miss || r_miss ) {
- misses++;
- if ( (misses & 0xff) == 1 )
- printf("opti931: %d missed irq (now %s%s)\n",
- misses, w_miss?"play ":"",
- r_miss?"capture ":"");
- }
- if (loops==10)
+ if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- if (w_miss) mc11 |= 4 ;
- if (r_miss) mc11 |= 8 ;
- if (mc11==0)
- return;
+ }
+ return;
}
- if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
+ if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
- r_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
- r_miss = 1 ;
-
- if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
- if (isa_dmastatus1(d->dma1) != 0)
- printf("opti931_intr: wr dma %d\n",
- isa_dmastatus1(d->dma1));
+ if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
- w_miss = 0 ;
}
- else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
- w_miss = 1 ;
-
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
+
+static void
+gus_write(int io_base, u_char reg, u_char value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 5, value);
+}
+
+static void
+gus_writew(int io_base, u_char reg, u_short value)
+{
+ outb(io_base + 3, reg);
+ outb(io_base + 4, value);
+}
+
+static u_char
+gus_read(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inb(io_base+5);
+}
+
+static u_short
+gus_readw(int io_base, u_char reg)
+{
+ outb(io_base+3, reg);
+ return inw(io_base+4);
+}
+
+
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
- outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
-
- outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
+ prev &= ~IA_TRD ;
+ outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
- *
- * fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
- DDB(printf("nbits = 0 for dev %d\n", dev) );
+ DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
- DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
+ DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
- val = ad_read(d, regoffs);
+ old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
+ DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
+ dev, regoffs, old, val));
}
return 0; /* success */
}
@@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
- d->mix_rec_devs = MODE1_REC_DEVICES;
+ d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
+ /*
+ * some device-specific things, mostly mute the mic to
+ * the output mixer so as to avoid hisses. In many cases this
+ * is the default after reset, this code is here mostly as a
+ * reminder that this might be necessary on other boards.
+ */
+ switch(d->bd_id) {
+ case MD_OPTI931:
+ ad_write(d, 20, 0x88);
+ ad_write(d, 21, 0x88);
+ break;
+ case MD_GUSPNP:
+ /* this is only necessary in mode 3 ... */
+ ad_write(d, 22, 0x88);
+ ad_write(d, 23, 0x88);
+ }
}
/*
@@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
- *
- * fixed lr970724
*/
static int
@@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DDB(printf("mss_detect error, busy still set (0x%02x)\n",
+ DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
- return 0;
+ return 0;
}
ad_write(d, 0, 0x45);
@@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DDB(printf("mss_detect error - step H(%x)\n", tmp1));
+ DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DDB(printf("mss_detect() - step I\n"));
+ DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
- case 0x03: /* CS4236 on Intel PR440FX motherboard */
+ case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
- DDB(printf("mss_detect() - Detected %s\n", name));
+ DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
+#if 0
+ if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
+ printf("warning, cannot do ulaw rec + play on the GUS\n");
+ r = 0 ; /* move to U8 */
+ }
+#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
+ /*
+ * not sure if this is really needed...
+ */
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
-static char * cs4236_probe(u_long csn, u_long vend_id);
-static void cs4236_attach(u_long csn, u_long vend_id, char *name,
+static char * cs423x_probe(u_long csn, u_long vend_id);
+static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
-static struct pnp_device cs4236 = {
- "cs423x",
- cs4236_probe,
- cs4236_attach,
+static struct pnp_device cs423x = {
+ "cs423x/ymh0020",
+ cs423x_probe,
+ cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
-DATA_SET (pnpdevice_set, cs4236);
+DATA_SET (pnpdevice_set, cs423x);
static char *
-cs4236_probe(u_long csn, u_long vend_id)
+cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x3742630e)
+ u_long id = vend_id & 0xff00ffff;
+ if ( id == 0x3700630e )
s = "CS4237" ;
- else if (vend_id == 0x3642630e)
+ else if ( id == 0x3600630e )
s = "CS4236" ;
- else if (vend_id == 0x360b630e)
- s = "CS4236" ;
- else if (vend_id == 0x3242630e)
+ else if ( id == 0x3200630e)
s = "CS4232" ;
+ else if ( id == 0x2000a865)
+ s = "Yamaha SA2";
+ else if (vend_id == 0x8140d315)
+ s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
-cs4236_attach(u_long csn, u_long vend_id, char *name,
+cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) {
- printf("CS423x use sb-compatible codec\n");
- dev->id_iobase = d.port[2] ;
+ if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- tmp_d.alt_base = d.port[0] - 4;
+ if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
+ /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
+ dev->id_iobase = d.port[0] ;
+ tmp_d.alt_base = d.port[1] ;
+ d.irq[1] = 0 ; /* only needed for the VIVO */
+ } else {
+ dev->id_iobase = d.port[2] ;
+ tmp_d.alt_base = d.port[0] - 4;
+ }
d.drq[1] = 4 ; /* disable, it is not used ... */
- } else {
- /* mss-compatible codec */
- dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ } else { /* mss-compatible codec */
+ dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
- switch (vend_id) {
- case 0x3742630e: /* CS4237 */
- case 0x3642630e: /* CS4236 */
- case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
- tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
+ dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ tmp_d.alt_base = d.port[2];
+ switch (vend_id & 0xff00ffff) {
+
+ case 0x2000a865: /* yamaha SA-2 */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_YM0020 ;
+ break;
+
+ case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ tmp_d.bd_id = MD_VIVO ;
+ d.irq[1] = 0 ;
break;
- default:
+
+ case 0x3700630e: /* CS4237 */
+ tmp_d.bd_id = MD_CS4237 ;
+ break;
+
+ case 0x3600630e: /* CS4236 */
+ tmp_d.bd_id = MD_CS4236 ;
+ break;
+
+ default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
- tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- dev->id_alive = 1;
pcmattach(dev);
}
@@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
- opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
+ opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static void gus_mem_cfg(snddev_info *tmp);
+
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ u_char tmp;
+
read_pnp_parms ( &d , 0 ) ;
+
+ /* d.irq[1] = d.irq[0] ; */
+ printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
+ pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
+
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
- dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
+
+ tmp_d.io_base = d.port[2] - 4;
+ tmp_d.alt_base = d.port[0]; /* 0x220 */
+ tmp_d.conf_base = d.port[1]; /* gus control block... */
+ tmp_d.bd_id = MD_GUSPNP ;
+
+ /* reset */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
+ DELAY(1000 * 30);
+ /* release reset and enable DAC */
+ gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
+ printf("resetting the gus...\n");
+ DELAY(1000 * 30);
+ /* end of reset */
- tmp_d.alt_base = d.port[0];
+ outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
+
+ /*
+ * unmute left & right line. Need to go in mode3, unmute,
+ * and back to mode 2
+ */
+ tmp = ad_read(&tmp_d, 0x0c);
+ ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
+ ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
+ ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
+ ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
+
+ /* send codec interrupts on irq1 and only use that one */
+ gus_write(tmp_d.conf_base, 0x5a , 0x4f );
+
+ /* enable access to hidden regs */
+ tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
+ gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
+ printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+
+ strcpy(tmp_d.name, name);
pcmattach(dev);
}
+
+#if 0
+int
+gus_mem_write(snddev_info *d, int addr, u_char data)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ outb(d->conf_base + 7, data);
+}
+
+u_char
+gus_mem_read(snddev_info *d, int addr)
+{
+ gus_writew(d->conf_base, 0x43 , addr & 0xffff );
+ gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
+ return inb(d->conf_base + 7);
+}
+
+void
+gus_mem_cfg(snddev_info *d)
+{
+ int base;
+ u_char old;
+ u_char a, b;
+
+ printf("configuring gus memory...\n");
+ gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
+ old = gus_read(d->conf_base, 0x19);
+ gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
+ for (base = 0; base < 1024; base++) {
+ a=gus_mem_read(d, base*1024);
+ a = ~a ;
+ gus_mem_write(d, base*1024, a);
+ b=gus_mem_read(d, base*1024);
+ if ( b != a )
+ break ;
+ }
+ printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
+}
+#endif /* gus mem cfg... */
+
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */
diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c
index e01ddd6..d9a5f37 100644
--- a/sys/i386/isa/snd/dmabuf.c
+++ b/sys/i386/isa/snd/dmabuf.c
@@ -1,56 +1,58 @@
/*
* snd/dmabuf.c
*
- * New DMA routines -- Luigi Rizzo, 19 jul 97
* This file implements the new DMA routines for the sound driver.
+ * AUTO DMA MODE (ISA DMA SIDE).
*
* Copyright by Luigi Rizzo - 1997
*
* Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer. 2.
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
*/
#include <i386/isa/snd/sound.h>
#include <i386/isa/snd/ulaw.h>
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
-#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
-#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
+#define DMA_ALIGN_THRESHOLD 4
#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
static void dsp_wr_dmadone(snddev_info *d);
static void dsp_rd_dmadone(snddev_info *d);
/*
- *
-
-SOUND OUTPUT
+ * SOUND OUTPUT
We use a circular buffer to store samples directed to the DAC.
-The buffer is split into three variable-size regions, each identified
-by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl).
+The buffer is split into two variable-size regions, each identified
+by an offset in the buffer (rp,fp) and a lenght (rl,fl). dl contains
+the length of the current DMA transfer, dl=0 means that the dma is
+idle.
- 0 dp,dl rp,rl fp,fl bufsize
- |__________|_____>______|_____________|________|
- FREE DMA READY FREE
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE d READY w FREE
READY region: contains data written from the process and ready
to be sent to the DAC;
@@ -58,190 +60,149 @@ by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl).
FREE region: is the empty region of the buffer, where a process
can write new data.
- DMA region: contains data being sent to the DAC by the DMA engine.
- the actual boundary between the FREE and READY regions is in
- fact within the DMA region (indicated by the > symbol above),
- and advances as the DMA engine makes progress.
-
Both the "READY" and "FREE" regions can wrap around the end of the
-buffer. The "DMA" region can only wrap if AUTO DMA is used, otherwise
-it cannot cross the end of the buffer.
-
-Since dl can be updated on the fly, dl0 marks the value used when the
-operation was started. When using AUTO DMA, bufsize-(count in the ISA DMA
-register) directly reflects the position of dp.
-
-At initialization, DMA and READY are empty, and FREE takes all the
-available space:
+buffer. At initialization, READY is empty, FREE takes all the
+available space, and dma is idle.
- dp = rp = fp = 0 ; -- beginning of buffer
- dl0 = dl = 0 ; -- meaning no dma activity
- rl = 0 ; -- meaning no data ready
- fl = bufsize ;
+The two boundaries (rp,fp) in the buffers are advanced by DMA [d]
+and write() [w] operations. The first block of the READY region
+is used for DMA transfers. The transfer is started at rp and with
+chunks of length dl. During DMA operations, rp advances and rl,fl
+are updated by dsp_wr_dmaupdate(). When a new block is written,
+fp advances and rl,fl are updated accordingly.
-The DMA region is also empty whenever there is no DMA activity, for
-whatever reason (e.g. no ready data, or output is paused).
The code works as follows: the user write routine dsp_write_body()
fills up the READY region with new data (reclaiming space from the
-FREE region) and starts the write DMA engine if inactive (activity
-is indicated by d->flags & SND_F_WR_DMA ). The size of each
-DMA transfer is chosen according to a series of rules which will be
-discussed later. When a DMA transfer is complete, an interrupt causes
-dsp_wrintr() to be called which empties the DMA region, extends
-the FREE region and possibly starts the next transfer.
+FREE region) and starts the write DMA engine if inactive. When a
+DMA transfer is complete, an interrupt causes dsp_wrintr() to be
+called which extends the FREE region and possibly starts the next
+transfer.
In some cases, the code tries to track the current status of DMA
-operations by calling isa_dmastatus() and advancing the boundary
-between FREE and DMA regions accordingly.
+operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl.
-The size of a DMA transfer is selected according to the following
-rules:
+The sistem tries to make all DMA transfers use the same size,
+play_blocksize or rec_blocksize. The size is either selected
+by the user, or computed by the system to correspond to about .25s of
+audio. The blocksize must be within a range which is currently:
- 1. when not using AUTO DMA, do not wrap around the end of the
- buffer, and do not let fp move too close to the end of the
- buffer;
+ min(5ms, 40 bytes) ... 1/2 buffer size.
- 2. do not use more than half of the buffer size.
- This serves to allow room for a next write operation concurrent
- with the dma transfer, and to reduce the time which is necessary
- to wait before a pending dma will end (optionally, the max
- size could be further limited to a fixed amount of play time,
- depending on number of channels, sample size and sample speed);
+When there aren't enough data (write) or space (read), a transfer
+is started with a reduced size.
- 3. use the current blocksize (either specified by the user, or
- corresponding roughly to 0.25s of data);
+To reduce problems in case of overruns, the routine which fills up the
+buffer should initialize (e.g. by repeating the last value) a
+reasonably long area after the last block so that no noise is
+produced on overruns.
*
*/
/*
- * dsp_wr_dmadone moves the write DMA region into the FREE region.
- * It is assumed to be called at spltty() and with a write dma
- * previously started.
+ * dsp_wr_dmadone() updates pointers and wakes up any process sleeping
+ * or waiting on a select().
+ * Must be called at spltty().
*/
static void
dsp_wr_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
- isa_dmadone(B_WRITE, b->buf + b->dp, b->dl, d->dma1);
- b->fl += b->dl ; /* make dl bytes free */
+ dsp_wr_dmaupdate(b);
/*
* XXX here it would be more efficient to record if there
* actually is a sleeping process, but this should still work.
*/
wakeup(b); /* wakeup possible sleepers */
- if (d->wsel.si_pid &&
+ if (b->sel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
- selwakeup( & d->wsel );
- b->dp = b->rp ;
- b->dl0 = b->dl = 0 ;
+ selwakeup( & b->sel );
}
/*
- * The following function tracks the status of a (write) dma transfer,
- * and moves the boundary between the FREE and the DMA regions.
- * It works under the following assumptions:
- * - the DMA engine is running;
- * - the routine is called with interrupts blocked.
- * BEWARE: when using AUTO DMA, dl can go negative! We assume that it
- * does not wrap!
+ * dsp_wr_dmaupdate() tracks the status of a (write) dma transfer,
+ * updating pointers. It must be called at spltty() and the ISA DMA must
+ * have been started.
+ *
+ * NOTE: when we are using auto dma in the device, rl might become
+ * negative.
*/
void
-dsp_wr_dmaupdate(snddev_info *d)
+dsp_wr_dmaupdate(snd_dbuf *b)
{
- snd_dbuf *b = & (d->dbuf_out) ;
- int tmp;
+ int tmp, delta;
- tmp = b->dl - isa_dmastatus1(d->dma1) ;
+ tmp = b->bufsize - isa_dmastatus1(b->chan) ;
tmp &= DMA_ALIGN_MASK; /* align... */
- if (tmp > 0) { /* we have some new data */
- b->dp += tmp;
- if (b->dp >= b->bufsize)
- b->dp -= b->bufsize;
- b->dl -= tmp ;
- b->fl += tmp ;
- }
+ delta = tmp - b->rp;
+ if (delta < 0) /* wrapped */
+ delta += b->bufsize ;
+ b->rp = tmp;
+ b->rl -= delta ;
+ b->fl += delta ;
}
/*
- * Write interrupt routine. Can be called from other places, but
- * with interrupts disabled.
+ * Write interrupt routine. Can be called from other places (e.g.
+ * to start a paused transfer), but with interrupts disabled.
*/
void
dsp_wrintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
- int cb_reason = SND_CB_WR ;
- DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
- b->dl, b->rl, b->fl));
- if (d->flags & SND_F_WR_DMA) { /* dma was active */
+ if (b->dl) { /* dma was active */
b->int_count++;
- d->flags &= ~SND_F_WR_DMA;
- cb_reason = SND_CB_WR | SND_CB_RESTART ;
dsp_wr_dmadone(d);
- } else
- cb_reason = SND_CB_WR | SND_CB_START ;
+ }
+ DEB(if (b->rl < 0)
+ printf("dsp_wrintr: dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
+ b->dl, b->rp, b->rl, b->fp, b->fl));
/*
- * start another dma operation only if have ready data in the
- * buffer, there is no pending abort, have a full-duplex device
- * (dma1 != dma2) or have half duplex device and there is no
- * pending op on the other side.
+ * start another dma operation only if have ready data in the buffer,
+ * there is no pending abort, have a full-duplex device
+ * (dbuf_out.chan != dbuf_in.chan) or have half duplex device
+ * and there is no * pending op on the other side.
*
- * Force transfer to be aligned to a boundary of 4, which is
+ * Force transfers to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit. We could make this
* adaptive, but why bother for now...
*/
if ( b->rl >= DMA_ALIGN_THRESHOLD &&
! (d->flags & SND_F_ABORTING) &&
- ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
- b->dl = min(b->rl, b->bufsize - b->rp ) ; /* do not wrap */
- b->dl = min(b->dl, d->play_blocksize ); /* avoid too large transfer */
- b->dl &= DMA_ALIGN_MASK ; /* realign things */
- b->rl -= b->dl ;
- b->rp += b->dl ;
- if (b->rp == b->bufsize)
- b->rp = 0 ;
- /*
- * now try to avoid too small dma transfers in the near future.
- * This can happen if I let rp start too close to the end of
- * the buffer. If this happens, and have enough data, try to
- * split the available block in two approx. equal parts.
- * XXX this code can go when we use auto dma.
- */
- if (b->bufsize - b->rp < MIN_CHUNK_SIZE &&
- b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
- b->dl = (b->bufsize - b->dp) / 2;
- b->dl &= ~3 ; /* align to a boundary of 4 */
- b->rl += (b->rp - (b->dp + b->dl) ) ;
- b->rp = b->dp + b->dl ; /* no need to check for wraps */
- }
- /*
- * how important is the order of operations ?
- */
- if (b->dl == 0) {
- printf("ouch... want to start for 0 bytes!\n");
- goto ferma;
+ ( (d->dbuf_out.chan != d->dbuf_in.chan) ||
+ ! (d->flags & SND_F_READING) ) ) {
+ int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
+ l &= DMA_ALIGN_MASK ; /* realign things */
+
+ if (l != b->dl) {
+ /* for any reason, size has changed. Stop and restart */
+ DEB(printf("wrintr: bsz change from %d to %d, rp %d rl %d\n",
+ b->dl, l, b->rp, b->rl));
+ b->dl = l; /* record previous transfer size */
+ d->callback(d, SND_CB_WR | SND_CB_STOP );
+ d->callback(d, SND_CB_WR | SND_CB_START );
}
- b->dl0 = b->dl ; /* XXX */
- if (d->callback)
- d->callback(d, cb_reason ); /* start/restart dma */
- isa_dmastart( B_WRITE , b->buf + b->dp, b->dl, d->dma1);
- d->flags |= SND_F_WR_DMA;
} else {
-ferma:
- if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART )
+ /* cannot start a new dma transfer */
+ DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
+ d->flags, b->rp, b->rl));
+ if (b->dl > 0) { /* was active */
+ b->dl = 0;
d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
+ if (d->flags & SND_F_WRITING)
+ DEB(printf("Race! got wrint while reloading...\n"));
+ else
+ reset_dbuf(b, SND_CHAN_WR);
+ }
/*
* if switching to read, should start the read dma...
*/
- if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
+ if ( d->dbuf_out.chan == d->dbuf_in.chan && (d->flags & SND_F_READING) )
dsp_rdintr(d);
- DEB(printf("cannot start wr-dma flags 0x%08lx dma_dl %d rl %d\n",
- d->flags, isa_dmastatus1(d->dma1), b->rl));
}
}
@@ -259,65 +220,55 @@ ferma:
* data, we reduce the latency to something proportional to the length
* of the first piece, while keeping the overhead low and being able
* to feed the DMA with large blocks.
+ *
+ * assume d->flags |= SND_F_WRITING ; has been done before
*/
int
dsp_write_body(snddev_info *d, struct uio *buf)
{
- int timeout = 1, n, l, bsz, ret = 0 ;
+ int n, l, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_out) ;
- /* assume d->flags |= SND_F_WRITING ; has been done before */
/*
- * bsz is the max size for the next transfer. If the dma was
- * idle, we want it as large as possible. Otherwise, start with
+ * bsz is the max size for the next transfer. If the dma was idle
+ * (dl == 0), we want it as large as possible. Otherwise, start with
* a small block to avoid underruns if we are close to the end of
* the previous operation.
*/
- bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
+ bsz = b->dl ? MIN_CHUNK_SIZE : b->bufsize ;
while (( n = buf->uio_resid )) {
l = min (n, bsz); /* at most n bytes ... */
s = spltty(); /* no interrupts here ... */
- /*
- * if i) the dma engine is running, ii) we do not have enough space
- * in the FREE region, and iii) the current DMA transfer might let
- * us complete the _whole_ transfer without sleeping, or we are doing
- * non-blocking I/O, then try to extend the FREE region.
- * Otherwise do not bother, we will need to sleep anyways, and
- * make the timeout longer.
- */
-
- if ( d->flags & SND_F_WR_DMA && b->fl < l &&
- ( b->fl + b->dl >= n || d->flags & SND_F_NBIO ) )
- dsp_wr_dmaupdate(d); /* should really change timeout... */
- else
- timeout = hz;
+ dsp_wr_dmaupdate(b);
l = min( l, b->fl ); /* no more than avail. space */
- l = min( l, b->bufsize - b->fp ); /* do not wrap ... */
DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
/*
* at this point, we assume that if l==0 the dma engine
- * must (or will, in cause it is paused) be running.
+ * must be running.
*/
if (l == 0) { /* no space, must sleep */
+ int timeout;
if (d->flags & SND_F_NBIO) {
/* unless of course we are doing non-blocking i/o */
splx(s);
break;
}
DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
+ if ( b->fl < n )
+ timeout = hz;
+ else
+ timeout = 1 ;
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
- if (ret == ERESTART || ret == EINTR)
+ if (ret == EINTR)
break ;
- timeout = min(2*timeout, hz);
continue;
}
splx(s);
- timeout = 1 ; /* we got some data... */
/*
* copy data to the buffer, and possibly do format
@@ -325,13 +276,19 @@ dsp_write_body(snddev_info *d, struct uio *buf)
* NOTE: I can use fp here since it is not modified by the
* interrupt routines.
*/
- ret = uiomove(b->buf + b->fp, l, buf) ;
- if (ret !=0 ) { /* an error occurred ... */
- printf("uiomove error %d\n", ret);
- break ;
+ if (b->fp + l > b->bufsize) {
+ int l1 = b->bufsize - b->fp ;
+ uiomove(b->buf + b->fp, l1, buf) ;
+ uiomove(b->buf, l - l1, buf) ;
+ if (d->flags & SND_F_XLAT8) {
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
+ translate_bytes(ulaw_dsp, b->buf , l - l1);
+ }
+ } else {
+ uiomove(b->buf + b->fp, l, buf) ;
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l);
}
- if (d->flags & SND_F_XLAT8)
- translate_bytes(ulaw_dsp, b->buf + b->fp, l);
s = spltty(); /* no interrupts here ... */
b->rl += l ; /* this more ready bytes */
@@ -339,17 +296,51 @@ dsp_write_body(snddev_info *d, struct uio *buf)
b->fp += l ;
if (b->fp >= b->bufsize) /* handle wraps */
b->fp -= b->bufsize ;
- if ( !(d->flags & SND_F_WR_DMA) ) {/* dma was idle, restart it */
- if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
- SND_F_INIT) {
- /* want to init but no pending DMA activity */
- splx(s);
- d->callback(d, SND_CB_INIT); /* this is slow! */
- s = spltty();
- }
+ if ( b->dl == 0 ) /* dma was idle, restart it */
dsp_wrintr(d) ;
- }
splx(s) ;
+ if (buf->uio_resid == 0 && (b->fp & (b->sample_size - 1)) == 0) {
+ /*
+ * If data is correctly aligned, pad the region with
+ * replicas of the last sample. l0 goes from current to
+ * the buffer end, l1 is the portion which wraps around.
+ */
+ int l0, l1, i;
+
+ l1 = min(/* b->dl */ d->play_blocksize, b->fl);
+ l0 = min (l1, b->bufsize - b->fp);
+ l1 = l1 - l0 ;
+
+ i = b->fp - b->sample_size;
+ if (i < 0 ) i += b->bufsize ;
+ if (b->sample_size == 1) {
+ u_char *p= (u_char *)(b->buf + i), sample = *p;
+
+ for ( ; l0 ; l0--)
+ *p++ = sample ;
+ for (p= (u_char *)(b->buf) ; l1 ; l1--)
+ *p++ = sample ;
+ } else if (b->sample_size == 2) {
+ u_short *p= (u_short *)(b->buf + i), sample = *p;
+
+ l1 /= 2 ;
+ l0 /= 2 ;
+ for ( ; l0 ; l0--)
+ *p++ = sample ;
+ for (p= (u_short *)(b->buf) ; l1 ; l1--)
+ *p++ = sample ;
+ } else { /* must be 4 ... */
+ u_long *p= (u_long *)(b->buf + i), sample = *p;
+
+ l1 /= 4 ;
+ l0 /= 4 ;
+ for ( ; l0 ; l0--)
+ *p++ = sample ;
+ for (p= (u_long *)(b->buf) ; l1 ; l1--)
+ *p++ = sample ;
+ }
+
+ }
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
@@ -357,7 +348,8 @@ dsp_write_body(snddev_info *d, struct uio *buf)
if (d->flags & SND_F_ABORTING) {
d->flags &= ~SND_F_ABORTING;
splx(s);
- dsp_wrabort(d);
+ dsp_wrabort(d, 1 /* restart */);
+ /* XXX return EINTR ? */
}
splx(s) ;
return ret ;
@@ -367,45 +359,29 @@ dsp_write_body(snddev_info *d, struct uio *buf)
* SOUND INPUT
*
-The input part is similar to the output one. The only difference is in
-the ordering of regions, which is the following:
-
- 0 rp,rl dp,dl fp,fl bufsize
- |__________|____________|______>______|________|
- FREE READY DMA FREE
-
-and the fact that input data are in the READY region.
+The input part is similar to the output one, with a circular buffer
+split in two regions, and boundaries advancing because of read() calls
+[r] or dma operation [d]. At initialization, as for the write
+routine, READY is empty, and FREE takes all the space.
-At initialization, as for the write routine, DMA and READY are empty,
-and FREE takes all the space:
-
- dp = rp = fp = 0 ; -- beginning of buffer
- dl0 = dl = 0 ; -- meaning no dma activity
- rl = 0 ; -- meaning no data ready
- fl = bufsize ;
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE r READY d FREE
Operation is as follows: upon user read (dsp_read_body()) a DMA read
-is started if not already active (marked by d->flags & SND_F_RD_DMA),
+is started if not already active (marked by b->dl > 0),
then as soon as data are available in the READY region they are
transferred to the user buffer, thus advancing the boundary between FREE
and READY. Upon interrupts, caused by a completion of a DMA transfer,
the READY region is extended and possibly a new transfer is started.
-When necessary, isa_dmastatus() is called to advance the boundary
-between READY and DMA to the real position.
+When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
+rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
+updated accordingly.
The rules to choose the size of the new DMA area are similar to
-the other case, i.e:
-
- 1. if not using AUTO mode, do not wrap around the end of the
- buffer, and do not let fp move too close to the end of the
- buffer;
-
- 2. do not use more than half the buffer size; this serves to
- leave room for the next dma operation.
-
- 3. use the default blocksize, either user-specified, or
- corresponding to 0.25s of data;
+the other case, with a preferred constant transfer size equal to
+rec_blocksize, and fallback to smaller sizes if no space is available.
*
*/
@@ -419,14 +395,11 @@ dsp_rd_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
- isa_dmadone(B_READ, b->buf + b->dp, b->dl, d->dma2);
- b->rl += b->dl ; /* make dl bytes available */
+ dsp_rd_dmaupdate(b);
wakeup(b) ; /* wakeup possibly sleeping processes */
- if (d->rsel.si_pid &&
+ if (b->sel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
- selwakeup( & d->rsel );
- b->dp = b->fp ;
- b->dl0 = b->dl = 0 ;
+ selwakeup( & b->sel );
}
/*
@@ -437,20 +410,18 @@ dsp_rd_dmadone(snddev_info *d)
* - the function is called with interrupts blocked.
*/
void
-dsp_rd_dmaupdate(snddev_info *d)
+dsp_rd_dmaupdate(snd_dbuf *b)
{
- snd_dbuf *b = & (d->dbuf_in) ;
- int tmp ;
+ int delta, tmp ;
- tmp = b->dl - isa_dmastatus1(d->dma2) ;
+ tmp = b->bufsize - isa_dmastatus1(b->chan) ;
tmp &= DMA_ALIGN_MASK; /* align... */
- if (tmp > 0) { /* we have some data */
- b->dp += tmp;
- if (b->dp >= b->bufsize)
- b->dp -= b->bufsize;
- b->dl -= tmp ;
- b->rl += tmp ;
- }
+ delta = tmp - b->fp;
+ if (delta < 0) /* wrapped */
+ delta += b->bufsize ;
+ b->fp = tmp;
+ b->fl -= delta ;
+ b->rl += delta ;
}
/*
@@ -460,64 +431,41 @@ void
dsp_rdintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
- int cb_reason = SND_CB_RD ;
- DEB(printf("dsp_rdintr: start dl = %d fp %d blocksize %d\n",
- b->dl, b->fp, d->rec_blocksize));
- if (d->flags & SND_F_RD_DMA) { /* dma was active */
+ if (b->dl) { /* dma was active */
b->int_count++;
- d->flags &= ~SND_F_RD_DMA;
- cb_reason = SND_CB_RD | SND_CB_RESTART ;
dsp_rd_dmadone(d);
- } else
- cb_reason = SND_CB_RD | SND_CB_START ;
+ }
+
+ DEB(printf("dsp_rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
+ b->dl, b->rp, b->rl, b->fp, b->fl));
/*
- * Same checks as in the write case (mutatis mutandis) to decide
- * whether or not to restart a dma transfer.
+ * Restart if have enough free space to absorb overruns;
*/
- if ( b->fl >= DMA_ALIGN_THRESHOLD &&
- ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
- ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
- b->dl = min(b->fl, b->bufsize - b->fp ) ; /* do not wrap */
- b->dl = min(b->dl, d->rec_blocksize );
- b->dl &= DMA_ALIGN_MASK ; /* realign sizes */
- b->fl -= b->dl ;
- b->fp += b->dl ;
- if (b->fp == b->bufsize)
- b->fp = 0 ;
- /*
- * now try to avoid too small dma transfers in the near future.
- * This can happen if I let fp start too close to the end of
- * the buffer. If this happens, and have enough data, try to
- * split the available block in two approx. equal parts.
- */
- if (b->bufsize - b->fp < MIN_CHUNK_SIZE &&
- b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
- b->dl = (b->bufsize - b->dp) / 2;
- b->dl &= DMA_ALIGN_MASK ; /* align to multiples of 3 */
- b->fl += (b->fp - (b->dp + b->dl) ) ;
- b->fp = b->dp + b->dl ; /* no need to check that fp wraps */
- }
- if (b->dl == 0) {
- printf("ouch! want to read 0 bytes\n");
- goto ferma;
+ if ( b->fl > 0x200 &&
+ (d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0 &&
+ ( d->dbuf_out.chan != d->dbuf_in.chan ||
+ (d->flags & SND_F_WRITING) == 0 ) ) {
+ int l = min(b->fl - 0x100, d->rec_blocksize);
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+ if (l != b->dl) {
+ /* for any reason, size has changed. Stop and restart */
+ b->dl = l ;
+ d->callback(d, SND_CB_RD | SND_CB_STOP );
+ d->callback(d, SND_CB_RD | SND_CB_START );
}
- b->dl0 = b->dl ; /* XXX */
- if (d->callback)
- d->callback(d, cb_reason); /* restart_dma(); */
- isa_dmastart( B_READ , b->buf + b->dp, b->dl, d->dma2);
- d->flags |= SND_F_RD_DMA;
} else {
-ferma:
- if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART)
+ if (b->dl > 0) { /* was active */
+ b->dl = 0;
d->callback(d, SND_CB_RD | SND_CB_STOP);
+ }
/*
* if switching to write, start write dma engine
*/
- if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
+ if ( d->dbuf_out.chan == d->dbuf_in.chan && (d->flags & SND_F_WRITING) )
dsp_wrintr(d) ;
- DEB(printf("cannot start rd-dma flags 0x%08lx dma_dl %d fl %d\n",
- d->flags, isa_dmastatus1(d->dma2), b->fl));
+ DEB(printf("cannot start rd-dma rl %d fl %d\n",
+ b->rl, b->fl));
}
}
@@ -549,8 +497,6 @@ dsp_read_body(snddev_info *d, struct uio *buf)
long s;
snd_dbuf *b = & (d->dbuf_in) ;
- int timeout = 1 ; /* counter of how many ticks we sleep */
-
/*
* "limit" serves to return after at most one blocksize of data
* (unless more are already available). Otherwise, things like
@@ -569,116 +515,69 @@ dsp_read_body(snddev_info *d, struct uio *buf)
bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
while ( (n = buf->uio_resid) > limit ) {
DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
- /*
- * here compute how many bytes to transfer, enforcing various
- * limitations:
- */
- l = min (n, bsz); /* 1': at most bsz bytes ... */
+ l = min (n, bsz);
s = spltty(); /* no interrupts here ! */
- /*
- * if i) the dma engine is running, ii) we do not have enough
- * ready bytes, and iii) the current DMA transfer could give
- * us what we need, or we are doing non-blocking IO, then try
- * to extend the READY region.
- * Otherwise do not bother, we will need to sleep anyways,
- * and make the timeout longer.
- */
-
- if ( d->flags & SND_F_RD_DMA && b->rl < l &&
- ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
- dsp_rd_dmaupdate(d);
- else
- timeout = hz ;
- l = min( l, b->rl ); /* 2': no more than avail. data */
- l = min( l, b->bufsize - b->rp ); /* 3': do not wrap buffer. */
- /* the above limitation can be removed if we use auto DMA
- * on the ISA controller. But then we have to make a check
- * when doing the uiomove...
- */
- if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
- /*
- * There are two reasons the dma can be idle: either this
- * is the first read, or the buffer has become full. In
- * the latter case, the dma cannot be restarted until
- * we have removed some data, which will be true at the
- * second round.
- *
- * Call dsp_rdintr to start the dma. It would be nice to
- * have a "need" field in the snd_dbuf, so that we do
- * not start a long operation unnecessarily. However,
- * the restart code will ask for at most d->blocksize
- * bytes, and since we are sure we are the only reader,
- * and the routine is not interrupted, we patch and
- * restore d->blocksize around the call. A bit dirty,
- * but it works, and saves some overhead :)
- */
-
- int old_bs = d->rec_blocksize;
-
- if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
- SND_F_INIT) {
- /* want to init but no pending DMA activity */
- splx(s);
- d->callback(d, SND_CB_INIT); /* this is slow! */
- s = spltty();
- }
- if (l < MIN_CHUNK_SIZE)
- d->rec_blocksize = MIN_CHUNK_SIZE ;
- else if (l < d->rec_blocksize)
- d->rec_blocksize = l ;
- dsp_rdintr(d);
- d->rec_blocksize = old_bs ;
- }
-
+ dsp_rd_dmaupdate(b);
+ l = min( l, b->rl ); /* no more than avail. data */
if (l == 0) {
+ int timeout;
/*
- * If, after all these efforts, we still have no data ready,
- * then we must sleep (unless of course we have doing
- * non-blocking i/o. But use exponential delays, starting
- * at 1 tick and doubling each time.
+ * If there is no data ready, then we must sleep (unless
+ * of course we have doing non-blocking i/o). But also
+ * consider restarting the DMA engine.
*/
+ if ( b->dl == 0 ) { /* dma was idle, start it */
+ if ( d->flags & SND_F_INIT && d->dbuf_out.dl == 0 ) {
+ /* want to init and there is no pending DMA activity */
+ splx(s);
+ d->callback(d, SND_CB_INIT); /* this is slow! */
+ s = spltty();
+ }
+ dsp_rdintr(d);
+ }
if (d->flags & SND_F_NBIO) {
splx(s);
break;
}
- DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
- timeout, buf->uio_resid));
+ if (n-limit > b->dl)
+ timeout = hz; /* we need to wait for an int. */
+ else
+ timeout = 1; /* maybe data will be ready earlier */
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
- splx(s); /* necessary before the goto again... */
- if (ret == ERESTART || ret == EINTR)
+ splx(s);
+ if (ret == EINTR)
break ;
- DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
- timeout = min(timeout*2, hz);
continue;
}
splx(s);
- timeout = 1 ; /* we got some data, so reset exp. wait */
- /*
- * if we are using /dev/audio and the device does not
- * support it natively, we should do a format conversion.
- * (in this case from uLAW to natural format).
- * This can be messy in that it can require an intermediate
- * buffer, and also screw up the byte count.
- */
/*
+ * Do any necessary format conversion, and copy to user space.
* NOTE: I _can_ use rp here because it is not modified by the
* interrupt routines.
*/
- if (d->flags & SND_F_XLAT8)
- translate_bytes(dsp_ulaw, b->buf + b->rp, l);
- ret = uiomove(b->buf + b->rp, l, buf) ;
- if (ret !=0 ) /* an error occurred ... */
- break ;
+ if (b->rp + l > b->bufsize) { /* handle wraparounds */
+ int l1 = b->bufsize - b->rp ;
+ if (d->flags & SND_F_XLAT8) {
+ translate_bytes(dsp_ulaw, b->buf + b->rp, l1);
+ translate_bytes(dsp_ulaw, b->buf , l - l1);
+ }
+ uiomove(b->buf + b->rp, l1, buf) ;
+ uiomove(b->buf, l - l1, buf) ;
+ } else {
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(dsp_ulaw, b->buf + b->rp, l);
+ uiomove(b->buf + b->rp, l, buf) ;
+ }
s = spltty(); /* no interrupts here ... */
b->fl += l ; /* this more free bytes */
b->rl -= l ; /* this less ready bytes */
b->rp += l ; /* advance ready pointer */
- if (b->rp == b->bufsize) /* handle wraps */
- b->rp = 0 ;
+ if (b->rp >= b->bufsize) /* handle wraps */
+ b->rp -= b->bufsize ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
@@ -687,7 +586,8 @@ dsp_read_body(snddev_info *d, struct uio *buf)
if (d->flags & SND_F_ABORTING) {
d->flags |= ~SND_F_ABORTING;
splx(s);
- dsp_rdabort(d);
+ dsp_rdabort(d, 1 /* restart */);
+ /* XXX return EINTR ? */
}
splx(s) ;
return ret ;
@@ -697,30 +597,40 @@ dsp_read_body(snddev_info *d, struct uio *buf)
/*
* short routine to initialize a dma buffer descriptor (usually
* located in the XXX_desc structure). The first parameter is
- * the buffer size, the second one specifies the dma channel in use
- * At the moment we do not support more than 64K, since for some
- * cards I need to switch between dma1 and dma2. The channel is
- * unused.
+ * the buffer size, the second one specifies that a 16-bit dma channel
+ * is used (hence the buffer must be properly aligned).
*/
void
-alloc_dbuf(snd_dbuf *b, int size, int chan)
+alloc_dbuf(snd_dbuf *b, int size)
{
if (size > 0x10000)
panic("max supported size is 64k");
b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
0ul, 0xfffffful, 1ul, 0x10000ul);
- /* should check that it does not fail... */
- b->dp = b->rp = b->fp = 0 ;
- b->dl0 = b->dl = b->rl = 0 ;
+ /* should check that malloc does not fail... */
+ b->rp = b->fp = 0 ;
+ b->dl = b->rl = 0 ;
b->bufsize = b->fl = size ;
}
+/*
+ * this resets a buffer and starts the isa dma on that channel.
+ * Must be called when the dma on the card is disabled (e.g. after init).
+ */
void
-reset_dbuf(snd_dbuf *b)
+reset_dbuf(snd_dbuf *b, int chan)
{
- b->dp = b->rp = b->fp = 0 ;
- b->dl0 = b->dl = b->rl = 0 ;
+ DEB(printf("reset dbuf for chan %d\n", b->chan));
+ b->rp = b->fp = 0 ;
+ b->dl = b->rl = 0 ;
b->fl = b->bufsize ;
+ if (chan == SND_CHAN_NONE)
+ return ;
+ if (chan == SND_CHAN_WR)
+ chan = B_WRITE | B_RAW ;
+ else
+ chan = B_READ | B_RAW ;
+ isa_dmastart( chan , b->buf, b->bufsize, b->chan);
}
/*
@@ -741,9 +651,9 @@ snd_sync(snddev_info *d, int chan, int threshold)
for (;;) {
s=spltty();
if ( chan==1 )
- dsp_wr_dmaupdate(d);
+ dsp_wr_dmaupdate(b);
else
- dsp_rd_dmaupdate(d);
+ dsp_rd_dmaupdate(b);
if ( (chan == 1 && b->fl <= threshold) ||
(chan == 2 && b->rl <= threshold) ) {
ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
@@ -762,61 +672,53 @@ snd_sync(snddev_info *d, int chan, int threshold)
/*
* dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
* which abort a pending DMA transfer and flush the buffers.
+ * They return the number of bytes that has not been transferred.
+ * The second parameter is used to restart the engine if needed.
*/
int
-dsp_wrabort(snddev_info *d)
+dsp_wrabort(snddev_info *d, int restart)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_out) ;
s = spltty();
- if ( d->flags & SND_F_WR_DMA ) {
- d->flags &= ~(SND_F_WR_DMA | SND_F_WRITING);
+ if ( b->dl ) {
+ b->dl = 0 ;
+ d->flags &= ~ SND_F_WRITING ;
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_ABORT);
- missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
-
- b->rl += missing ; /* which are part of the ready area */
- b->rp -= missing ;
- if (b->rp < 0)
- b->rp += b->bufsize;
- DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
- b->dl - missing, b->dl));
- b->dl -= missing;
+ isa_dmastop(b->chan) ;
dsp_wr_dmadone(d);
- missing = b->rl;
+
+ DEB(printf("dsp_wrabort: stopped, %d bytes left\n", b->rl));
}
- reset_dbuf(b);
+ missing = b->rl;
+ isa_dmadone(B_WRITE, b->buf, b->bufsize, b->chan); /*free chan */
+ reset_dbuf(b, restart ? SND_CHAN_WR : SND_CHAN_NONE);
splx(s);
return missing;
}
int
-dsp_rdabort(snddev_info *d)
+dsp_rdabort(snddev_info *d, int restart)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_in) ;
s = spltty();
- if ( d->flags & SND_F_RD_DMA ) {
- d->flags &= ~(SND_F_RD_DMA | SND_F_READING);
+ if ( b->dl ) {
+ b->dl = 0 ;
+ d->flags &= ~ SND_F_READING ;
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_ABORT);
- missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
-
- b->fl += missing ; /* which are part of the free area */
- b->fp -= missing ;
- if (b->fp < 0)
- b->fp += b->bufsize;
- DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
- b->dl - missing, b->dl));
- b->dl -= missing;
+ isa_dmastop(b->chan) ;
dsp_rd_dmadone(d);
- missing = b->rl ;
}
- reset_dbuf(b);
+ missing = b->rl ;
+ isa_dmadone(B_READ, b->buf, b->bufsize, b->chan);
+ reset_dbuf(b, restart ? SND_CHAN_RD : SND_CHAN_NONE);
splx(s);
return missing;
}
@@ -831,31 +733,34 @@ dsp_rdabort(snddev_info *d)
int
snd_flush(snddev_info *d)
{
- int ret;
- int count=10;
+ int ret, count=10;
+ u_long s;
+ snd_dbuf *b = &(d->dbuf_out) ;
-DEB(printf("snd_flush d->flags 0x%08lx\n", d->flags));
- dsp_rdabort(d);
- if ( d->flags & SND_F_WR_DMA ) {
- /* close write */
- while ( d->flags & SND_F_WR_DMA ) {
- /*
- * still pending output data.
- */
- ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
- if (ret == ERESTART || ret == EINTR) {
- printf("tsleep returns %d\n", ret);
- return -1 ;
- }
- if ( ret && --count == 0) {
- printf("timeout flushing dma1, cnt 0x%x flags 0x%08lx\n",
- isa_dmastatus1(d->dma1), d->flags);
- return -1 ;
- }
+ DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
+ dsp_rdabort(d, 0 /* no restart */);
+ /* close write */
+ while ( b->dl ) {
+ /*
+ * still pending output data.
+ */
+ ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dmafl1", hz);
+ dsp_wr_dmaupdate(b);
+ DEB( printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl ) );
+ if (ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1 ;
+ }
+ if ( ret && --count == 0) {
+ printf("timeout flushing dbuf_out.chan, cnt 0x%x flags 0x%08lx\n",
+ b->rl, d->flags);
+ break;
}
- d->flags &= ~SND_F_CLOSING ;
}
- reset_dbuf(& (d->dbuf_out) );
+ s = spltty(); /* should not be necessary... */
+ d->flags &= ~SND_F_CLOSING ;
+ dsp_wrabort(d, 0 /* no restart */);
+ splx(s);
return 0 ;
}
diff --git a/sys/i386/isa/snd/dmabuf_auto.c b/sys/i386/isa/snd/dmabuf_auto.c
deleted file mode 100644
index 3972be8..0000000
--- a/sys/i386/isa/snd/dmabuf_auto.c
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * snd/dmabuf.c
- *
- * New DMA routines -- Luigi Rizzo, 05 sep 97
- * This file implements the new DMA routines for the sound driver.
- * AUTO DMA MODE (ISA DMA SIDE).
- *
- * Copyright by Luigi Rizzo - 1997
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <i386/isa/snd/sound.h>
-#include <i386/isa/snd/ulaw.h>
-
-#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
-#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
-#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
-#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
-
-static void dsp_wr_dmadone(snddev_info *d);
-static void dsp_rd_dmadone(snddev_info *d);
-
-/*
- *
-
-SOUND OUTPUT
-
-We use a circular buffer to store samples directed to the DAC.
-The buffer is split into two variable-size regions, each identified
-by an offset in the buffer (rp,fp) and a lenght (rl,fl).
-
- 0 rp,rl fp,fl bufsize
- |__________|____________|________|
- FREE d> READY w> FREE
-
- READY region: contains data written from the process and ready
- to be sent to the DAC;
-
- FREE region: is the empty region of the buffer, where a process
- can write new data.
-
-The first block of the READY region is used for DMA transfers. The
-transfer is started at rp and with chunks of length dl0 computed
-according to some rules. During DMA operations, rp advances (d>)
-and rl,fl are updated by dsp_wr_dmaupdate(). When a new block is
-written, fp advances (w>) and rl,fl are updated accordingly.
-
-Both the "READY" and "FREE" regions can wrap around the end of the
-buffer.
-
-At initialization, READY is empty, FREE takes all the
-available space, and dma is idle
-
-dl0 contains the new blocksize to be used in dma transfers (passed to
-the callback). dl contains the previous blocksize (needed when the
-codec uses AUTO DMA).
-
- rp = fp = 0 ; -- beginning of buffer
- dl0 = dl = 0 ;
- rl = 0 ; -- meaning no data ready
- fl = bufsize ;
-
-The code works as follows: the user write routine dsp_write_body()
-fills up the READY region with new data (reclaiming space from the
-FREE region) and starts the write DMA engine if inactive (activity
-is indicated by d->flags & SND_F_WR_DMA ). The size of each
-DMA transfer is chosen according to a series of rules which will be
-discussed later. When a DMA transfer is complete, an interrupt causes
-dsp_wrintr() to be called which empties the DMA region, extends
-the FREE region and possibly starts the next transfer.
-
-In some cases, the code tries to track the current status of DMA
-operations by calling isa_dmastatus() and advancing the boundary
-between FREE and DMA regions accordingly.
-
-The size of a DMA transfer is selected according to the following
-rules:
-
- 1. when not using AUTO DMA, do not wrap around the end of the
- buffer, and do not let fp move too close to the end of the
- buffer;
-
- 2. do not use more than half of the buffer size.
- This serves to allow room for a next write operation concurrent
- with the dma transfer, and to reduce the time which is necessary
- to wait before a pending dma will end (optionally, the max
- size could be further limited to a fixed amount of play time,
- depending on number of channels, sample size and sample speed);
-
- 3. use the current blocksize (either specified by the user, or
- corresponding roughly to 0.25s of data);
-
- *
- */
-
-
-/*
- * dsp_wr_dmadone moves the write DMA region into the FREE region.
- * It is assumed to be called at spltty() and with a write dma
- * previously started.
- */
-static void
-dsp_wr_dmadone(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_out) ;
-
- dsp_wr_dmaupdate(d);
- /*
- * XXX here it would be more efficient to record if there
- * actually is a sleeping process, but this should still work.
- */
- wakeup(b); /* wakeup possible sleepers */
- if (d->wsel.si_pid &&
- ( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
- selwakeup( & d->wsel );
-}
-
-/*
- * The following function tracks the status of a (write) dma transfer,
- * and moves the boundary between the FREE and the DMA regions.
- * It works under the following assumptions:
- * - the DMA engine is active;
- * - the routine is called with interrupts blocked.
- */
-void
-dsp_wr_dmaupdate(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_out) ;
- int tmp, delta;
-
- tmp = b->bufsize - isa_dmastatus1(d->dma1) ;
- tmp &= DMA_ALIGN_MASK; /* align... */
- delta = tmp - b->rp;
- if (delta < 0) /* wrapped */
- delta += b->bufsize ;
- b->rp = tmp;
- b->rl -= delta ;
- b->fl += delta ;
-}
-
-/*
- * Write interrupt routine. Can be called from other places, but
- * with interrupts disabled.
- */
-void
-dsp_wrintr(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_out) ;
-
- DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
- b->dl, b->rl, b->fl));
- if (d->flags & SND_F_WR_DMA) { /* dma was active */
- b->int_count++;
- d->flags &= ~SND_F_WR_DMA;
- dsp_wr_dmadone(d);
- } else
- b->dl = 0 ;
-
- /*
- * start another dma operation only if have ready data in the
- * buffer, there is no pending abort, have a full-duplex device
- * (dma1 != dma2) or have half duplex device and there is no
- * pending op on the other side.
- *
- * Force transfer to be aligned to a boundary of 4, which is
- * needed when doing stereo and 16-bit. We could make this
- * adaptive, but why bother for now...
- */
- if ( b->rl >= DMA_ALIGN_THRESHOLD &&
- ! (d->flags & SND_F_ABORTING) &&
- ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
- int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
- l &= DMA_ALIGN_MASK ; /* realign things */
-
- b->dl0 = l ;
- if (d->callback)
- d->callback(d, SND_CB_WR | SND_CB_START );
- d->flags |= SND_F_WR_DMA;
- b->dl = b->dl0;
- } else {
- if (b->dl > 0) { /* was active */
- b->dl0 = 0;
- d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
- }
- /*
- * if switching to read, should start the read dma...
- */
- if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
- dsp_rdintr(d);
- DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
- d->flags, isa_dmastatus1(d->dma1), b->rl));
- }
-}
-
-/*
- * user write routine
- *
- * advance the boundary between READY and FREE, fill the space with
- * uiomove(), and possibly start DMA. Do the above until the transfer
- * is complete.
- *
- * To minimize latency in case a pending DMA transfer is about to end,
- * we do the transfer in pieces of increasing sizes, extending the
- * READY area at every checkpoint. In the (necessary) assumption that
- * memory bandwidth is larger than the rate at which the dma consumes
- * data, we reduce the latency to something proportional to the length
- * of the first piece, while keeping the overhead low and being able
- * to feed the DMA with large blocks.
- */
-
-int
-dsp_write_body(snddev_info *d, struct uio *buf)
-{
- int timeout = 1, n, l, bsz, ret = 0 ;
- long s;
- snd_dbuf *b = & (d->dbuf_out) ;
-
- /* assume d->flags |= SND_F_WRITING ; has been done before */
- /*
- * bsz is the max size for the next transfer. If the dma was
- * idle, we want it as large as possible. Otherwise, start with
- * a small block to avoid underruns if we are close to the end of
- * the previous operation.
- */
- bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
- while ( n = buf->uio_resid ) {
- l = min (n, bsz); /* at most n bytes ... */
- s = spltty(); /* no interrupts here ... */
- /*
- * if i) the dma engine is running, ii) we do not have enough space
- * in the FREE region, and iii) the current DMA transfer might let
- * us complete the _whole_ transfer without sleeping, or we are doing
- * non-blocking I/O, then try to extend the FREE region.
- * Otherwise do not bother, we will need to sleep anyways, and
- * make the timeout longer.
- * Note that the check for iii) is only approximate since we
- * might have fewer than dl0 residual bytes.
- */
-
- if ( d->flags & SND_F_WR_DMA && b->fl < l &&
- ( b->fl + b->dl0 >= n || d->flags & SND_F_NBIO ) )
- dsp_wr_dmaupdate(d); /* should really change timeout... */
- else
- timeout = hz;
- l = min( l, b->fl ); /* no more than avail. space */
- DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
- /*
- * at this point, we assume that if l==0 the dma engine
- * must (or will, in cause it is paused) be running.
- */
- if (l == 0) { /* no space, must sleep */
- if (d->flags & SND_F_NBIO) {
- /* unless of course we are doing non-blocking i/o */
- splx(s);
- break;
- }
- DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
- ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
- if (ret == EINTR)
- d->flags |= SND_F_ABORTING ;
- splx(s);
- if (ret == ERESTART || ret == EINTR)
- break ;
- timeout = min(2*timeout, hz);
- continue;
- }
- splx(s);
- timeout = 1 ; /* we got some data... */
-
- /*
- * copy data to the buffer, and possibly do format
- * conversions (here, from ULAW to U8).
- * NOTE: I can use fp here since it is not modified by the
- * interrupt routines.
- */
- if (b->fp + l > b->bufsize) {
- int l1 = b->bufsize - b->fp ;
- uiomove(b->buf + b->fp, l1, buf) ;
- uiomove(b->buf, l - l1, buf) ;
- if (d->flags & SND_F_XLAT8) {
- translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
- translate_bytes(ulaw_dsp, b->buf , l - l1);
- }
- } else {
- uiomove(b->buf + b->fp, l, buf) ;
- if (d->flags & SND_F_XLAT8)
- translate_bytes(ulaw_dsp, b->buf + b->fp, l);
- }
-
- s = spltty(); /* no interrupts here ... */
- b->rl += l ; /* this more ready bytes */
- b->fl -= l ; /* this less free bytes */
- b->fp += l ;
- if (b->fp >= b->bufsize) /* handle wraps */
- b->fp -= b->bufsize ;
- if ( !(d->flags & SND_F_WR_DMA) ) /* dma was idle, restart it */
- dsp_wrintr(d) ;
- splx(s) ;
- bsz = min(b->bufsize, bsz*2);
- }
- s = spltty(); /* no interrupts here ... */
- d->flags &= ~SND_F_WRITING ;
- if (d->flags & SND_F_ABORTING) {
- d->flags &= ~SND_F_ABORTING;
- splx(s);
- dsp_wrabort(d);
- }
- splx(s) ;
- return ret ;
-}
-
-/*
- * SOUND INPUT
- *
-
-The input part is similar to the output one. The only difference is in
-the ordering of regions, which is the following:
-
- 0 rp,rl fp,fl bufsize
- |__________|____________|________|
- FREE r> READY d> FREE
-
-and the fact that input data are in the READY region.
-
-At initialization, as for the write routine, READY is empty,
-and FREE takes all the space:
-
- rp = fp = 0 ; -- beginning of buffer
- dl0 = dl = 0 ;
- rl = 0 ; -- meaning no data ready
- fl = bufsize ;
-
-Operation is as follows: upon user read (dsp_read_body()) a DMA read
-is started if not already active (marked by d->flags & SND_F_RD_DMA),
-then as soon as data are available in the READY region they are
-transferred to the user buffer, thus advancing the boundary between FREE
-and READY. Upon interrupts, caused by a completion of a DMA transfer,
-the READY region is extended and possibly a new transfer is started.
-
-When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
-rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
-updated accordingly.
-
-
-The rules to choose the size of the new DMA area are similar to
-the other case, i.e:
-
- 1. if not using AUTO mode, do not wrap around the end of the
- buffer, and do not let fp move too close to the end of the
- buffer;
-
- 2. do not use more than half the buffer size; this serves to
- leave room for the next dma operation.
-
- 3. use the default blocksize, either user-specified, or
- corresponding to 0.25s of data;
-
- *
- */
-
-/*
- * dsp_rd_dmadone moves bytes in the input buffer from DMA region to
- * READY region. We assume it is called at spltty() and with dl>0
- */
-static void
-dsp_rd_dmadone(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_in) ;
-
- dsp_rd_dmaupdate(d);
- b->rl += b->dl ; /* make dl bytes available */
- wakeup(b) ; /* wakeup possibly sleeping processes */
- if (d->rsel.si_pid &&
- ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
- selwakeup( & d->rsel );
- b->dp = b->fp ;
- b->dl0 = b->dl = 0 ;
-}
-
-/*
- * The following function tracks the status of a (read) dma transfer,
- * and moves the boundary between the READY and the DMA regions.
- * It works under the following assumptions:
- * - the DMA engine is running;
- * - the function is called with interrupts blocked.
- */
-void
-dsp_rd_dmaupdate(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_in) ;
- int tmp ;
-
- tmp = b->bufsize - isa_dmastatus1(d->dma2) ;
- tmp &= DMA_ALIGN_MASK; /* align... */
- delta = tmp - b->fp;
- if (delta < 0) /* wrapped */
- delta += b->bufsize ;
- b->fp = tmp;
- b->fl -= delta ;
- b->rl += delta ;
-}
-
-/*
- * read interrupt routine. Must be called with interrupts blocked.
- */
-void
-dsp_rdintr(snddev_info *d)
-{
- snd_dbuf *b = & (d->dbuf_in) ;
-
- if (d->flags & SND_F_RD_DMA) { /* dma was active */
- b->int_count++;
- d->flags &= ~SND_F_RD_DMA;
- dsp_rd_dmadone(d);
- } else
- b->dl = 0 ;
- /*
- * Same checks as in the write case (mutatis mutandis) to decide
- * whether or not to restart a dma transfer.
- */
- if ( b->fl >= DMA_ALIGN_THRESHOLD &&
- ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
- ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
- int l = min(b->fl, d->rec_blocksize);
- l &= DMA_ALIGN_MASK ; /* realign sizes */
- b->dl0 = l ;
- if (d->callback)
- d->callback(d, SND_CB_RD | SND_CB_START );
- d->flags |= SND_F_RD_DMA;
- b->dl = b->dl0;
- } else {
- if (b->dl > 0) {
- b->dl0 = 0 ; /* was active */
- d->callback(d, SND_CB_RD | SND_CB_STOP);
- }
- /*
- * if switching to write, start write dma engine
- */
- if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
- dsp_wrintr(d) ;
- DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
- d->flags, isa_dmastatus1(d->dma2), b->fl));
- }
-}
-
-/*
- * body of user-read routine
- *
- * Start DMA if not active; wait for READY not empty.
- * Transfer data from READY region using uiomove(), advance boundary
- * between FREE and READY. Repeat until transfer is complete.
- *
- * To avoid excessive latency in freeing up space for the DMA
- * engine, transfers are done in blocks of increasing size, so that
- * the latency is proportional to the size of the smallest block, but
- * we have a low overhead and are able to feed the dma engine with
- * large blocks.
- *
- * When we enter this routine, we assume that d->flags |= SND_F_READING
- * was done before.
- *
- * NOTE: in the current version, read will not return more than
- * blocksize bytes at once (unless more are already available), to
- * avoid that requests using very large buffers block for too long.
- */
-
-int
-dsp_read_body(snddev_info *d, struct uio *buf)
-{
- int limit, l, n, bsz, ret = 0 ;
- long s;
- snd_dbuf *b = & (d->dbuf_in) ;
-
- int timeout = 1 ; /* counter of how many ticks we sleep */
-
- /*
- * "limit" serves to return after at most one blocksize of data
- * (unless more are already available). Otherwise, things like
- * cat /dev/audio would use a 64K buffer and would start returning
- * data after a _very_ long time...
- * Note -- some applications depend on reads not returning short
- * blocks. But I believe these apps are broken, since interrupted
- * system calls might return short reads anyways, and the
- * application should better check that.
- */
-
- if (buf->uio_resid > d->rec_blocksize)
- limit = buf->uio_resid - d->rec_blocksize;
- else
- limit = 0;
- bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
- while ( (n = buf->uio_resid) > limit ) {
- DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
- /*
- * here compute how many bytes to transfer, enforcing various
- * limitations:
- */
- l = min (n, bsz); /* 1': at most bsz bytes ... */
- s = spltty(); /* no interrupts here ! */
- /*
- * if i) the dma engine is running, ii) we do not have enough
- * ready bytes, and iii) the current DMA transfer could give
- * us what we need, or we are doing non-blocking IO, then try
- * to extend the READY region.
- * Otherwise do not bother, we will need to sleep anyways,
- * and make the timeout longer.
- */
-
- if ( d->flags & SND_F_RD_DMA && b->rl < l &&
- ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
- dsp_rd_dmaupdate(d);
- else
- timeout = hz ;
- l = min( l, b->rl ); /* 2': no more than avail. data */
- if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
- /*
- * There are two reasons the dma can be idle: either this
- * is the first read, or the buffer has become full. In
- * the latter case, the dma cannot be restarted until
- * we have removed some data, which will be true at the
- * second round.
- *
- * Call dsp_rdintr to start the dma. It would be nice to
- * have a "need" field in the snd_dbuf, so that we do
- * not start a long operation unnecessarily. However,
- * the restart code will ask for at most d->blocksize
- * bytes, and since we are sure we are the only reader,
- * and the routine is not interrupted, we patch and
- * restore d->blocksize around the call. A bit dirty,
- * but it works, and saves some overhead :)
- */
-
- int old_bs = d->rec_blocksize;
- if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
- SND_F_INIT) {
- /* want to init but no pending DMA activity */
- splx(s);
- d->callback(d, SND_CB_INIT); /* this is slow! */
- s = spltty();
- }
- if (l < MIN_CHUNK_SIZE)
- d->rec_blocksize = MIN_CHUNK_SIZE ;
- else if (l < d->rec_blocksize)
- d->rec_blocksize = l ;
- d->dl0 = d->rec_blocksize ;
- dsp_rdintr(d);
- d->rec_blocksize = old_bs ;
- }
-
- if (l == 0) {
- /*
- * If, after all these efforts, we still have no data ready,
- * then we must sleep (unless of course we have doing
- * non-blocking i/o. But use exponential delays, starting
- * at 1 tick and doubling each time.
- */
- if (d->flags & SND_F_NBIO) {
- splx(s);
- break;
- }
- DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
- timeout, buf->uio_resid));
- ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
- if (ret == EINTR)
- d->flags |= SND_F_ABORTING ;
- splx(s); /* necessary before the goto again... */
- if (ret == ERESTART || ret == EINTR)
- break ;
- DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
- timeout = min(timeout*2, hz);
- continue;
- }
- splx(s);
-
- timeout = 1 ; /* we got some data, so reset exp. wait */
- /*
- * if we are using /dev/audio and the device does not
- * support it natively, we should do a format conversion.
- * (in this case from uLAW to natural format).
- * This can be messy in that it can require an intermediate
- * buffer, and also screw up the byte count.
- */
- /*
- * NOTE: I _can_ use rp here because it is not modified by the
- * interrupt routines.
- */
- if (d->flags & SND_F_XLAT8)
- translate_bytes(dsp_ulaw, b->buf + b->rp, l);
- ret = uiomove(b->buf + b->rp, l, buf) ;
- if (ret !=0 ) /* an error occurred ... */
- break ;
-
- s = spltty(); /* no interrupts here ... */
- b->fl += l ; /* this more free bytes */
- b->rl -= l ; /* this less ready bytes */
- b->rp += l ; /* advance ready pointer */
- if (b->rp == b->bufsize) /* handle wraps */
- b->rp = 0 ;
- splx(s) ;
- bsz = min(b->bufsize, bsz*2);
- }
- s = spltty(); /* no interrupts here ... */
- d->flags &= ~SND_F_READING ;
- if (d->flags & SND_F_ABORTING) {
- d->flags |= ~SND_F_ABORTING;
- splx(s);
- dsp_rdabort(d);
- }
- splx(s) ;
- return ret ;
-}
-
-
-/*
- * short routine to initialize a dma buffer descriptor (usually
- * located in the XXX_desc structure). The first parameter is
- * the buffer size, the second one specifies that a 16-bit dma channel
- * is used (hence the buffer must be properly aligned).
- */
-void
-alloc_dbuf(snd_dbuf *b, int size, int chan)
-{
- if (size > 0x10000)
- panic("max supported size is 64k");
- b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
- 0ul, 0xfffffful, 1ul, 0x10000ul);
- /* should check that it does not fail... */
- b->dp = b->rp = b->fp = 0 ;
- b->dl0 = b->dl = b->rl = 0 ;
- b->bufsize = b->fl = size ;
-}
-
-void
-reset_dbuf(snd_dbuf *b)
-{
- b->dp = b->rp = b->fp = 0 ;
- b->dl0 = b->dl = b->rl = 0 ;
- b->fl = b->bufsize ;
-}
-
-/*
- * snd_sync waits until the space in the given channel goes above
- * a threshold. chan = 1 : play, 2: capture. The threshold is
- * checked against fl or rl respectively.
- * Assume that the condition can become true, do not check here...
- */
-int
-snd_sync(snddev_info *d, int chan, int threshold)
-{
- u_long s;
- int ret;
- snd_dbuf *b;
-
- b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
-
- for (;;) {
- s=spltty();
- if ( chan==1 )
- dsp_wr_dmaupdate(d);
- else
- dsp_rd_dmaupdate(d);
- if ( (chan == 1 && b->fl <= threshold) ||
- (chan == 2 && b->rl <= threshold) ) {
- ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
- splx(s);
- if (ret == ERESTART || ret == EINTR) {
- printf("tsleep returns %d\n", ret);
- return -1 ;
- }
- } else
- break;
- }
- splx(s);
- return 0 ;
-}
-
-/*
- * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
- * which abort a pending DMA transfer and flush the buffers.
- */
-int
-dsp_wrabort(snddev_info *d)
-{
- long s;
- int missing = 0;
- snd_dbuf *b = & (d->dbuf_out) ;
-
- s = spltty();
- if ( d->flags & SND_F_WR_DMA ) {
- d->flags &= ~ ( SND_F_WR_DMA | SND_F_WRITING ) ;
- if (d->callback)
- d->callback(d, SND_CB_WR | SND_CB_ABORT);
- missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
-
- b->rl += missing ; /* which are part of the ready area */
- b->rp -= missing ;
- if (b->rp < 0)
- b->rp += b->bufsize;
- DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
- b->dl - missing, b->dl));
- b->dl -= missing;
- dsp_wr_dmadone(d);
- missing = b->rl;
- }
- reset_dbuf(b);
- splx(s);
- return missing;
-}
-
-int
-dsp_rdabort(snddev_info *d)
-{
- long s;
- int missing = 0;
- snd_dbuf *b = & (d->dbuf_in) ;
-
- s = spltty();
- if ( d->flags & SND_F_RD_DMA ) {
- d->flags &= ~ ( SND_F_RD_DMA | SND_F_READING ) ;
- if (d->callback)
- d->callback(d, SND_CB_RD | SND_CB_ABORT);
- missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
-
- b->fl += missing ; /* which are part of the free area */
- b->fp -= missing ;
- if (b->fp < 0)
- b->fp += b->bufsize;
- DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
- b->dl - missing, b->dl));
- b->dl -= missing;
- dsp_rd_dmadone(d);
- missing = b->rl ;
- }
- reset_dbuf(b);
- splx(s);
- return missing;
-}
-
-/*
- * this routine tries to flush the dma transfer. It is called
- * on a close. The caller must set SND_F_CLOSING, and insure that
- * interrupts are enabled. We immediately abort any read DMA
- * operation, and then wait for the play buffer to drain.
- */
-
-int
-snd_flush(snddev_info *d)
-{
- int ret, res, res1;
- int count=10;
-
- DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
- dsp_rdabort(d);
- if ( d->flags & SND_F_WR_DMA ) {
- /* close write */
- while ( d->flags & SND_F_WR_DMA ) {
- /*
- * still pending output data.
- */
- ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
- if (ret == ERESTART || ret == EINTR) {
- printf("tsleep returns %d\n", ret);
- return -1 ;
- }
- if ( ret && --count == 0) {
- printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
- isa_dmastatus1(d->dma1), d->flags);
- return -1 ;
- }
- }
- d->flags &= ~SND_F_CLOSING ;
- }
- reset_dbuf(& (d->dbuf_out) );
- return 0 ;
-}
-
-/*
- * end of new code for dma buffer handling
- */
diff --git a/sys/i386/isa/snd/mss.h b/sys/i386/isa/snd/mss.h
index 4b34e51..994f323 100644
--- a/sys/i386/isa/snd/mss.h
+++ b/sys/i386/isa/snd/mss.h
@@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
-#define GUSMAX_MIXER
-#ifdef GUSMAX_MIXER
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
-
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#else /* Generic mapping */
-
-#define MODE1_REC_DEVICES \
- (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
-#define MODE1_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#define MSS_REC_DEVICES \
+ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
-#define MODE2_MIXER_DEVICES \
- (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
- SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
-#endif
-
-#define OPTI931_MIXER_DEVICES \
- (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
- SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
- * Most of the mixer entries work in backwards. Setting the polarity field
- * makes them to work correctly.
+ * Table of mixer registers. There is a default table for the
+ * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * clones come out, there ought to be more tables.
+ *
+ * Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
- * The current version doesn't try to compensate this.
+ *
+ * Following there is a macro ...MIXER_DEVICES which is a bitmap
+ * of all non-zero fields in the table.
+ * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
+ * registers I0..I15)
*
*/
-mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
-MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
+mixer_ent mix_devices[32][2] = {
+MIX_NONE(SOUND_MIXER_VOLUME),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
+MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
-MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_NONE(SOUND_MIXER_LINE1),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define MODE2_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+#define MODE1_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
+
+
+/*
+ * entries for the opti931...
+ */
+
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
-MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_BASS),
+MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_NONE(SOUND_MIXER_IMIX),
+MIX_NONE(SOUND_MIXER_ALTPCM),
+MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+MIX_NONE(SOUND_MIXER_OGAIN),
+MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
+MIX_NONE(SOUND_MIXER_LINE2),
+MIX_NONE(SOUND_MIXER_LINE3),
};
+#define OPTI931_MIXER_DEVICES \
+ (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
+
+
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c
index e786f5f..d4825c1 100644
--- a/sys/i386/isa/snd/sb_dsp.c
+++ b/sys/i386/isa/snd/sb_dsp.c
@@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
- * are met: 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer. 2. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
- * Remebber, isa probe routines are supposed to return the
+ * Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
+ dev->id_alive = 16 ; /* number of io ports */
+ /* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
- d->esel.si_pid = 0;
- d->esel.si_flags = 0;
-
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
- reset_dbuf(& (d->dbuf_in) );
- reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
+ /* XXX previous location of ack... */
+ DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
+ if ( d->dbuf_out.dl && (reason & 1) )
+ dsp_wrintr(d);
+ if ( d->dbuf_in.dl && (reason & 2) )
+ dsp_rdintr(d);
+
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
-DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
- if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
- dsp_wrintr(d);
- if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
- dsp_rdintr(d);
-
/*
* the sb16 might have multiple sources etc.
*/
@@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
- int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+ int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
- break ;
+ break;
case SND_CB_START : /* called with int disabled */
- sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
- d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
+ * We use the following algorithm:
+ * 1. check which direction(s) are active;
+ * 2. check if we should swap dma channels
+ * 3. check if we can do the swap.
*/
- int b16 ;
- int swap = 0 ;
+ int swap = 1 ; /* default... */
- b16 = (rd) ? d->rec_fmt : d->play_fmt ;
- b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
- /*
- * check if I have to swap dma channels. Swap if
- * - !rd, dma1 <4, b16
- * - !rd, dma1 >=4, !b16
- * - rd, dma2 <4, b16
- * - rd, dma2 >=4, !b16
- */
- if (!rd) {
- if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ if (rd) {
+ if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
+ swap = 0;
+ if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
+ swap = 0;
+ if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
+ swap = 0;
} else {
- if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ if (d->flags & SND_F_READING || d->dbuf_in.dl)
+ swap = 0;
+ if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
+ swap = 0;
+ if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
+ swap = 0;
}
- /*
- * before swapping should make sure that there is no
- * pending DMA on the other channel...
- */
+
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ DEB(printf("START dma chan: play %d, rec %d\n",
+ d->dma1, d->dma2));
}
- DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
- d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
- /* fallthrough */
- case SND_CB_RESTART:
+ if (!rd)
+ sb_cmd(d->io_base, DSP_CMD_SPKON);
+
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
- /*
- * SB16 support still not completely working!!!
- *
- * in principle, on the SB16, I could support simultaneous
- * play & rec.
- * However, there is no way to ask explicitly for 8 or
- * 16 bit transfer. As a consequence, if we do 8-bit,
- * we need to use the 8-bit channel, and if we do 16-bit,
- * we need to use the other one. The only way I find to
- * do this is to swap d->dma1 and d->dma2 ...
- *
- */
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
- c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
- if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
- c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
+ /* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
- c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
+ case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
- /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
+ {
+ int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
+ if (d->bd_flags & BD_F_SB16) {
+ if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
+ cmd = DSP_CMD_DMAPAUSE_16 ;
+ }
+ if (d->bd_flags & BD_F_HISPEED) {
+ sb_reset_dsp(d->io_base);
+ d->flags |= SND_F_INIT ;
+ } else {
+ sb_cmd(d->io_base, cmd); /* pause dma. */
+ /*
+ * This seems to have the side effect of blocking the other
+ * side as well so I have to re-enable it :(
+ */
+ if ( (rd && d->dbuf_out.dl) ||
+ (!rd && d->dbuf_in.dl) )
+ sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
+ 0xd6 : 0xd4); /* continue other dma */
+ }
+ }
+ DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
- DEB(printf("sb_reset_dsp failed\n"));
+ DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
- * XXX check this code...
+ * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
- tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
- if (vend_id == 0x01009305)
- s = "Avance Asound 100" ;
- if (vend_id == 0x2b008c0e)
- s = "SB16 Value PnP" ;
+
/*
- * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
- s = "SB AWE64 PnP";
+ s = "SB16 PnP";
+ else if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h
index 8548997..d7a24d5 100644
--- a/sys/i386/isa/snd/sbcard.h
+++ b/sys/i386/isa/snd/sbcard.h
@@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
+/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
+#define DSP_ADC8 0x20 /* direct ADC input */
+
+/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
+#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
+
+/* these should be used in the SB 2.0 and 2.01 */
+#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
+#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
+
+#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
+#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
+#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
+
+/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
+
+ /* prepare for dma input */
+#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
+
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
-#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
-#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
-
-#define DSP_ADC8 0x20 /* direct ADC input */
+#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
-#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
-#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
+/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
-#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
-#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
-#define DSP_CMD_DMAHALT 0xD0
+#define DSP_CMD_DMAPAUSE_8 0xD0
+#define DSP_CMD_DMAPAUSE_16 0xD5
+#define DSP_CMD_DMAEXIT_8 0xDA
+#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
-#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
-#define DSP_CMD_HSADC 0x99 /* high speed adc */
+#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
- /* prepare for dma input */
-#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
- /*
- * D9 and D5 are used on the sb16 on close... maybe a reset of
- * some subsystem ?
- */
-#define DSP_CMD_D9 0xD9
-#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c
index 1ed61ef..920680f 100644
--- a/sys/i386/isa/snd/sound.c
+++ b/sys/i386/isa/snd/sound.c
@@ -1,5 +1,5 @@
/*
- * sound/sound.c
+ * snd/sound.c
*
* Main sound driver for FreeBSD. This file provides the main
* entry points for probe/attach and all i/o demultiplexing, including
@@ -31,15 +31,15 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
- * For each board type a template "snddev_info" structure contains
+ * For each card type a template "snddev_info" structure contains
* all the relevant parameters, both for configuration and runtime.
*
* In this file we build tables of pointers to the descriptors for
- * the various supported boards. The generic probe routine scans
+ * the various supported cards. The generic probe routine scans
* the table(s) looking for a matching entry, then invokes the
* board-specific probe routine. If successful, a pointer to the
* correct snddev_info is stored in snddev_last_probed, for subsequent
- * use in the attach routine. The generic attach routine copies
+ * use in the attach routine. The generic attach routine copies
* the template to a permanent descriptor (pcm_info[unit] and
* friends), initializes all generic parameters, and calls the
* board-specific attach routine.
@@ -51,15 +51,9 @@
*/
#include <i386/isa/snd/sound.h>
-#include <sys/poll.h>
#if NPCM > 0 /* from "snd.h" */
-#include "snd.h"
-#if NSND > 0
-#error Can't have both Luigi's (pcm0) and voxware (snd0) at the same time.
-#endif
-
#define SNDSTAT_BUF_SIZE 4000
static char status_buf[SNDSTAT_BUF_SIZE] ;
static int status_len = 0 ;
@@ -97,12 +91,11 @@ u_long nsnd = NPCM ; /* total number of sound devices */
*/
snddev_info *snddev_last_probed = NULL ;
-static void print_isadev_info(struct isa_device *d, char *s);
static snddev_info *
generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s);
/*
- * here are the lists of known devices. Similar devices (e.g. all
+ * here are the lists of known cards. Similar cards (e.g. all
* sb clones, all mss clones, ... are in the same array.
* All lists of devices of the same type (eg. all pcm, all midi...)
* are in the same array.
@@ -116,19 +109,17 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s);
extern snddev_info sb_op_desc;
extern snddev_info mss_op_desc;
-static snddev_info *sb_devs[] = {
+static snddev_info *sb_devs[] = { /* all SB clones */
&sb_op_desc,
NULL,
} ;
-static snddev_info *mss_devs[] = {
- /* MSS-like devices */
+static snddev_info *mss_devs[] = { /* all WSS clones */
&mss_op_desc,
NULL,
} ;
-static snddev_info **pcm_devslist[] = {
- /* all pcm devices */
+static snddev_info **pcm_devslist[] = { /* all pcm devices */
mss_devs,
sb_devs,
NULL
@@ -142,8 +133,7 @@ pcmprobe(struct isa_device * dev)
return generic_snd_probe(dev, pcm_devslist, "pcm") ? 1 : 0 ;
}
-static snddev_info **midi_devslist[] = {
- /* all midi devices */
+static snddev_info **midi_devslist[] = {/* all midi devices */
NULL
} ;
@@ -205,8 +195,8 @@ pcmattach(struct isa_device * dev)
* revisions, if we see that we have a single dma, we might decide
* to use a single buffer to save memory.
*/
- alloc_dbuf( &(d->dbuf_out), d->bufsize, d->dma1 );
- alloc_dbuf( &(d->dbuf_in), d->bufsize, d->dma2 );
+ alloc_dbuf( &(d->dbuf_out), d->bufsize );
+ alloc_dbuf( &(d->dbuf_in), d->bufsize );
isa_dma_acquire(d->dma1);
if (d->dma2 != d->dma1)
@@ -237,6 +227,7 @@ pcmattach(struct isa_device * dev)
dev->id_id = dvp->id_id;
}
+ d->magic = MAGIC(dev->id_unit); /* debugging... */
/*
* and finally, call the device attach routine
*/
@@ -267,20 +258,6 @@ pcmintr(int unit)
synth_info[unit].isr(unit);
}
-static void
-print_isadev_info(struct isa_device *d, char *s)
-{
- if (d == NULL )
- return ;
- printf("%s%d at 0x%x irq %d drq %d mem %p flags 0x%x en %d confl %d\n",
- d->id_driver ? d->id_driver->name : "NONAME",
- d->id_unit,
- (u_short)(d->id_iobase), ffs(d->id_irq) - 1 ,
- d->id_drq, d->id_maddr, d->id_flags,
- d->id_enabled, d->id_conflicts);
-}
-
-
static snddev_info *
generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s)
{
@@ -289,10 +266,11 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s)
snddev_last_probed = NULL ;
- print_isadev_info(dev, s);
-
saved_dev = *dev ; /* the probe routine might alter parameters */
+ /*
+ * XXX todo: should try to match flags with device type.
+ */
for ( ; p[0] != NULL ; p++ )
for ( q = *p ; q[0] ; q++ )
if (q[0]->probe && q[0]->probe(dev))
@@ -474,13 +452,13 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
DDB(printf("read denied, half duplex and a writer is in\n"));
return EBUSY ;
}
- while ( d->flags & SND_F_WR_DMA ) {
+ while ( d->dbuf_out.dl ) {
/*
* we have a pending dma operation, post a read request
* and wait for the write to complete.
*/
d->flags |= SND_F_READING ;
- DDB(printf("sndread: sleeping waiting for write to end\n"));
+ DEB(printf("sndread: sleeping waiting for write to end\n"));
ret = tsleep( (caddr_t)&(d->dbuf_out),
PRIBIO | PCATCH , "sndrdw", hz ) ;
if (ret == ERESTART || ret == EINTR) {
@@ -545,7 +523,7 @@ sndwrite(dev_t i_dev, struct uio * buf, int flag)
splx(s);
return EBUSY ;
}
- while ( d->flags & SND_F_RD_DMA ) {
+ while ( d->dbuf_in.dl ) {
/*
* we have a pending read dma. Post a write request
* and wait for the read to complete (in fact I could
@@ -636,8 +614,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
* we start with the new ioctl interface.
*/
case AIONWRITE : /* how many bytes can write ? */
- if (d->flags & SND_F_WR_DMA)
- dsp_wr_dmaupdate(d);
+ if (d->dbuf_out.dl)
+ dsp_wr_dmaupdate(&(d->dbuf_out));
*(int *)arg = d->dbuf_out.fl;
break;
@@ -647,9 +625,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */
d->flags &= ~SND_F_HAS_SIZE ;
} else {
- RANGE (p->play_size, 40, d->bufsize /4);
+ RANGE (p->play_size, 40, d->dbuf_out.bufsize /4);
d->play_blocksize = p->play_size & ~3 ;
- RANGE (p->rec_size, 40, d->bufsize /4);
+ RANGE (p->rec_size, 40, d->dbuf_in.bufsize /4);
d->rec_blocksize = p->rec_size & ~3 ;
d->flags |= SND_F_HAS_SIZE ;
}
@@ -668,9 +646,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
case AIOSFMT :
{
snd_chan_param *p = (snd_chan_param *)arg;
- /*
- * at the moment, only support same format on play & rec
- */
d->play_speed = p->play_rate;
d->rec_speed = p->play_rate; /* XXX */
if (p->play_format & SND_F_STEREO)
@@ -715,9 +690,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
case AIOSTOP:
if (*(int *)arg == AIOSYNC_PLAY) /* play */
- *(int *)arg = dsp_wrabort(d);
+ *(int *)arg = dsp_wrabort(d, 1 /* restart */);
else if (*(int *)arg == AIOSYNC_CAPTURE)
- *(int *)arg = dsp_rdabort(d);
+ *(int *)arg = dsp_rdabort(d, 1 /* restart */);
else {
splx(s);
printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg);
@@ -734,13 +709,13 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
* here follow the standard ioctls (filio.h etc.)
*/
case FIONREAD : /* get # bytes to read */
- if ( d->flags & SND_F_RD_DMA )
- dsp_rd_dmaupdate(d);
+ if ( d->dbuf_in.dl )
+ dsp_rd_dmaupdate(&(d->dbuf_in));
*(int *)arg = d->dbuf_in.rl;
break;
- case FIOASYNC: /* set/clear async i/o */
- /* do nothing, this is called from kern_descrip.c for fcntl() */
+ case FIOASYNC: /*set/clear async i/o */
+ printf("FIOASYNC\n");
break;
case SNDCTL_DSP_NONBLOCK :
@@ -764,7 +739,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
if (t <= 1) { /* means no blocks */
d->flags &= ~SND_F_HAS_SIZE ;
} else {
- RANGE (t, 40, d->bufsize /4);
+ RANGE (t, 40, d->dbuf_out.bufsize /4);
d->play_blocksize =
d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */
d->flags |= SND_F_HAS_SIZE ;
@@ -774,15 +749,15 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
ask_init(d);
break ;
case SNDCTL_DSP_RESET:
- DDB(printf("dsp reset\n"));
- dsp_wrabort(d);
- dsp_rdabort(d);
+ DEB(printf("dsp reset\n"));
+ dsp_wrabort(d, 1 /* restart */);
+ dsp_rdabort(d, 1 /* restart */);
break ;
case SNDCTL_DSP_SYNC:
- DDB(printf("dsp sync\n"));
+ DEB(printf("dsp sync\n"));
splx(s);
- snd_sync(d, 1, d->bufsize - 4); /* DMA does not start with <4 bytes */
+ snd_sync(d, 1, d->dbuf_out.bufsize - 4); /* DMA does not start with <4 bytes */
break ;
case SNDCTL_DSP_SPEED:
@@ -793,6 +768,10 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SNDCTL_DSP_STEREO:
+ if ( *(int *)arg == -1) {
+ *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ;
+ break;
+ }
if ( *(int *)arg == 0 )
d->flags &= ~SND_F_STEREO ; /* mono */
else if ( *(int *)arg == 1 )
@@ -807,7 +786,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SOUND_PCM_WRITE_CHANNELS:
- printf("dsp write channels %d\n", *(int *)arg);
if ( *(int *)arg == 1)
d->flags &= ~SND_F_STEREO ; /* mono */
else if ( *(int *)arg == 2)
@@ -875,8 +853,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
((audio_buf_info *)arg)->bytes = d->dbuf_in.fl ;
((audio_buf_info *)arg)->fragments = 1 ;
((audio_buf_info *)arg)->fragstotal =
- d->bufsize / d->play_blocksize ;
- ((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
+ d->dbuf_in.bufsize / d->rec_blocksize ;
+ ((audio_buf_info *)arg)->fragsize = d->rec_blocksize ;
break ;
case SNDCTL_DSP_GETOSPACE:
@@ -884,7 +862,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
((audio_buf_info *)arg)->bytes = d->dbuf_out.fl ;
((audio_buf_info *)arg)->fragments = 1 ;
((audio_buf_info *)arg)->fragstotal =
- d->bufsize / d->play_blocksize ;
+ d->dbuf_out.bufsize / d->play_blocksize ;
((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
break ;
@@ -916,6 +894,14 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
*(int *)arg = d->mix_recsrc ;
break;
+ case SOUND_MIXER_READ_CAPS :
+ /*
+ * XXX - This needs to be fixed when the sound driver actually
+ * supports multiple inputs.
+ */
+ *(int *)arg = SOUND_CAP_EXCL_INPUT ;
+ break;
+
default:
DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n",
unit, dev & 0xf, cmd));
@@ -926,72 +912,80 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
return ret ;
}
+/*
+ * function to poll what is currently available. Used to be select.
+ */
int
sndpoll(dev_t i_dev, int events, struct proc *p)
{
- int lim ;
- int revents = 0;
int dev, unit, c = 1 /* default: success */ ;
snddev_info *d ;
u_long flags;
dev = minor(i_dev);
d = get_snddev_info(dev, &unit);
- DEB(printf("sndpoll dev 0x%04x rw 0x%08x\n",i_dev, events));
+ DEB(printf("sndpoll dev 0x%04x events 0x%08x\n",i_dev, events));
if (d == NULL ) {
- printf("select: unit %d not configured\n", unit );
- return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM))
- | POLLHUP);
+ printf("poll: unit %d not configured\n", unit );
+ return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
}
if (d->poll == NULL)
- /* is this correct hear? */
- return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM))
- | POLLHUP);
+ return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
else if (d->poll != sndpoll )
return d->poll(i_dev, events, p);
-
- /* handle it here with the generic code */
-
- /*
- * if the user selected a block size, then we want to use the
- * device as a block device, and select will return ready when
- * we have a full block.
- * In all other cases, select will return when 1 byte is ready.
- */
- lim = 1;
- flags = spltty();
- /* XXX fix the test here for half duplex devices */
- if (events & (POLLOUT | POLLWRNORM)) {
- if ( d->flags & SND_F_HAS_SIZE )
- lim = d->play_blocksize ;
- if (d->flags & SND_F_WR_DMA)
- dsp_wr_dmaupdate(d);
- c = d->dbuf_out.fl ;
- if (c < lim) /* no space available */
- selrecord(p, & (d->wsel));
- else
- revents |= events & (POLLOUT | POLLWRNORM);
- }
-
- /* XXX fix the test here */
- if (events & (POLLIN | POLLRDNORM)) {
- if ( d->flags & SND_F_HAS_SIZE )
- lim = d->rec_blocksize ;
- if ( !(d->flags & SND_F_RD_DMA) ) /* dma idle, restart it */
- dsp_rdintr(d);
- else
- dsp_rd_dmaupdate(d);
- c = d->dbuf_in.rl ;
- if (c < lim) /* no data available */
- selrecord(p, & (d->rsel));
- else
- revents |= events & (POLLIN | POLLRDNORM);
- DEB(printf("sndpoll on read: %d >= %d flags 0x%08lx\n", c, lim,
- d->flags));
+ else {
+ /* handle it here with the generic code */
+
+ int lim ;
+ int revents = 0 ;
+
+ /*
+ * if the user selected a block size, then we want to use the
+ * device as a block device, and select will return ready when
+ * we have a full block.
+ * In all other cases, select will return when 1 byte is ready.
+ */
+ lim = 1;
+ if (events & (POLLOUT | POLLWRNORM) ) {
+ if ( d->flags & SND_F_HAS_SIZE )
+ lim = d->play_blocksize ;
+ /* XXX fix the test here for half duplex devices */
+ if (1 /* write is compatible with current mode */) {
+ flags = spltty();
+ if (d->dbuf_out.dl)
+ dsp_wr_dmaupdate(&(d->dbuf_out));
+ c = d->dbuf_out.fl ;
+ if (c < lim) /* no space available */
+ selrecord(p, & (d->wsel));
+ else
+ revents |= events & (POLLOUT | POLLWRNORM);
+ splx(flags);
+ }
+ }
+ if (events & (POLLIN | POLLRDNORM)) {
+ if ( d->flags & SND_F_HAS_SIZE )
+ lim = d->rec_blocksize ;
+ /* XXX fix the test here */
+ if (1 /* read is compatible with current mode */) {
+ flags = spltty();
+ if ( d->dbuf_in.dl == 0 ) /* dma idle, restart it */
+ dsp_rdintr(d);
+ else
+ dsp_rd_dmaupdate(&(d->dbuf_in));
+ c = d->dbuf_in.rl ;
+ if (c < lim) /* no data available */
+ selrecord(p, & (d->rsel));
+ else
+ revents |= events & (POLLIN | POLLRDNORM);
+ splx(flags);
+ }
+ DEB(printf("sndpoll on read: %d >= %d flags 0x%08x\n",
+ c, lim, d->flags));
+ return c < lim ? 0 : 1 ;
+ }
+ return revents;
}
- splx(flags);
-
- return revents;
+ return ENXIO ; /* notreached */
}
/*
@@ -1020,23 +1014,22 @@ sndmmap(dev_t dev, int offset, int nprot)
{
snddev_info *d = get_snddev_info(dev, NULL);
- DEB(printf("sndmmap d %p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
+ DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
d, dev, offset, nprot));
if (d == NULL || nprot & PROT_EXEC)
return -1 ; /* forbidden */
- if (offset > d->bufsize && (nprot & PROT_WRITE) )
+ if (offset >= d->dbuf_out.bufsize && (nprot & PROT_WRITE) )
return -1 ; /* can only write to the first block */
- if (offset < d->bufsize)
+ if (offset < d->dbuf_out.bufsize)
return i386_btop(vtophys(d->dbuf_out.buf + offset));
offset -= 1 << 24;
- if ( (offset >= 0) && (offset < d->bufsize))
+ if ( (offset >= 0) && (offset < d->dbuf_in.bufsize))
return i386_btop(vtophys(d->dbuf_in.buf + offset));
offset -= 1 << 24;
if ( (offset >= 0) && (offset < 0x2000)) {
- printf("name %s\n", d->name);
return i386_btop(vtophys( ((int)d & ~0xfff) + offset));
}
return -1 ;
@@ -1060,7 +1053,8 @@ ask_init(snddev_info *d)
if ( d->callback == NULL )
return 0 ;
s = spltty();
- if ( d->flags & SND_F_PENDING_IO ) {
+ if ( d->flags & SND_F_PENDING_IO ||
+ d->dbuf_out.dl || d->dbuf_in.dl ) {
/* cannot do it now, record the request and return */
d->flags |= SND_F_INIT ;
splx(s);
@@ -1093,7 +1087,7 @@ init_status(snddev_info *d)
if (status_len != 0) /* only do init once */
return ;
sprintf(status_buf,
- "FreeBSD Sound Driver " __DATE__ " " __TIME__ "\n"
+ "FreeBSD Audio Driver (971023) " __DATE__ " " __TIME__ "\n"
"Installed devices:\n");
for (i = 0; i < NPCM_MAX; i++) {
@@ -1165,22 +1159,28 @@ snd_conflict(int io_base)
void
snd_set_blocksize(snddev_info *d)
{
+ int tmp ;
/*
- * now set the blocksize so as to guarantee approx 1/4s
+ * compute the sample size, and possibly
+ * set the blocksize so as to guarantee approx 1/4s
* between callbacks.
*/
+ tmp = 1 ;
+ if (d->flags & SND_F_STEREO) tmp += tmp;
+ if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
+ d->dbuf_out.sample_size = tmp ;
+ tmp = tmp * d->play_speed;
if ( (d->flags & SND_F_HAS_SIZE) == 0) {
- /* make blocksize adaptive to 250ms or max 1/4 of the buf */
- int tmp ;
- tmp = d->play_speed;
- if (d->flags & SND_F_STEREO) tmp += tmp;
- if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3);
+ }
- tmp = d->rec_speed;
- if (d->flags & SND_F_STEREO) tmp += tmp;
- if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
+ tmp = 1 ;
+ if (d->flags & SND_F_STEREO) tmp += tmp;
+ if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
+ tmp = tmp * d->rec_speed;
+ d->dbuf_in.sample_size = tmp ;
+ if ( (d->flags & SND_F_HAS_SIZE) == 0) {
d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3);
}
diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h
index ed4e0b3..55ed654 100644
--- a/sys/i386/isa/snd/sound.h
+++ b/sys/i386/isa/snd/sound.h
@@ -28,7 +28,11 @@
*
*/
+#ifdef KERNEL
#include "pcm.h"
+#else
+#define NPCM 1
+#endif
#if NPCM > 0
/*
@@ -38,6 +42,7 @@
#ifndef _OS_H_
#define _OS_H_
+#ifdef KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
@@ -57,10 +62,20 @@
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/buf.h>
+#include <sys/poll.h>
#include <i386/isa/isa_device.h>
-
#include <machine/clock.h> /* for DELAY */
+
+#else
+struct isa_device { int dummy ; } ;
+#define d_open_t void
+#define d_close_t void
+#define d_read_t void
+#define d_write_t void
+#define d_ioctl_t void
+#define d_poll_t void
+#endif /* KERNEL */
typedef void (irq_proc_t) (int irq);
#endif /* _OS_H_ */
@@ -68,19 +83,20 @@ typedef void (irq_proc_t) (int irq);
/*
* descriptor of a dma buffer. See dmabuf.c for documentation.
* (rp,rl) and (fp,fl) identify the READY and FREE regions of the
- * buffer. (dp,dl) identify the region currently used by the DMA.
- * Only dmabuf.c should test the value of dl. The reason is that
- * in auto-dma mode looking at dl alone does not make much sense,
- * since the transfer potentially spans the entire buffer.
+ * buffer. dl contains the length used for dma transfer, dl>0 also
+ * means that the channel is busy and there is a DMA transfer in progress.
*/
typedef struct _snd_dbuf {
char *buf;
int bufsize ;
- volatile int dp, rp, fp;
- volatile int dl, rl, fl;
- volatile int dl0; /* value used last time in dl */
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* lenght of ready and free areas. */
int int_count;
+ int chan; /* dma channel */
+ int sample_size ; /* 1, 2, 4 */
+ struct selinfo sel;
} snd_dbuf ;
/*
@@ -125,7 +141,6 @@ struct _snddev_info {
#define SND_CB_WR 0x200 /* write callback */
#define SND_CB_REASON_MASK 0xff
#define SND_CB_START 0x01 /* start dma op */
-#define SND_CB_RESTART 0x02 /* restart dma op */
#define SND_CB_STOP 0x03 /* stop dma op */
#define SND_CB_ABORT 0x04 /* abort dma op */
#define SND_CB_INIT 0x05 /* init board parameters */
@@ -145,8 +160,8 @@ struct _snddev_info {
int synth_base ; /* base for the synth */
int irq ;
- int dma1, dma2 ; /* dma2=dma1 for half-duplex cards */
-
+#define dma1 dbuf_out.chan
+#define dma2 dbuf_in.chan
int bd_id ; /* used to hold board-id info, eg. sb version,
* mss codec type, etc. etc.
*/
@@ -198,12 +213,14 @@ struct _snddev_info {
* you might get interrupts, so some manipulations of the
* descriptors must be done with interrupts blocked.
*/
+#if 0
#define SND_F_RD_DMA 0x0010 /* read-dma active */
#define SND_F_WR_DMA 0x0020 /* write-dma active */
#define SND_F_PENDING_IN (SND_F_READING | SND_F_RD_DMA)
#define SND_F_PENDING_OUT (SND_F_WRITING | SND_F_WR_DMA)
-#define SND_F_PENDING_IO (SND_F_PENDING_IN | SND_F_PENDING_OUT)
+#endif
+#define SND_F_PENDING_IO (SND_F_READING | SND_F_WRITING)
/*
* flag used to mark a pending close.
@@ -242,7 +259,6 @@ struct _snddev_info {
* be done at the next convenient time.
*/
#define SND_F_INIT 0x4000 /* changed parameters. need init */
-#define SND_F_AUTO_DMA 0x8000 /* use auto-dma */
u_long bd_flags; /* board-specific flags */
int play_speed, rec_speed;
@@ -258,8 +274,11 @@ struct _snddev_info {
u_long mix_recsrc; /* current recording source(s) */
u_short mix_levels[32];
- struct selinfo wsel, rsel, esel ;
+#define wsel dbuf_out.sel
+#define rsel dbuf_in.sel
u_long interrupts; /* counter of interrupts */
+ u_long magic;
+#define MAGIC(unit) ( 0xa4d10de0 + unit )
void *device_data ; /* just in case it is needed...*/
} ;
@@ -310,13 +329,20 @@ struct _snddev_info {
#define MD_CS4232 0xA4
#define MD_CS4232A 0xA5
#define MD_CS4236 0xA6
+#define MD_CS4237 0xA7
#define MD_OPTI931 0xB1
+#define MD_GUSPNP 0xB8
+#define MD_YM0020 0xC1
+#define MD_VIVO 0xD1
/*
* TODO: add some card classes rather than specific types.
*/
+#ifdef KERNEL
#include <i386/isa/snd/soundcard.h>
-
+#else
+#include </sys/i386/isa/snd/soundcard.h>
+#endif
/*
* many variables should be reduced to a range. Here define a macro
*/
@@ -327,14 +353,17 @@ struct _snddev_info {
/*
* finally, all default parameters
*/
-#define DSP_BUFFSIZE 65536 /* XXX */
+#define DSP_BUFFSIZE (65536 - 256) /* XXX */
+/*
+ * the last 256 bytes are room for buggy soundcard to overflow.
+ */
-#if 1 /* prepare for pnp support! */
+#ifdef KERNEL
#include "pnp.h"
#if NPNP > 0
#include <i386/isa/pnp.h> /* XXX pnp support */
#endif
-#endif
+#endif /* KERNEL */
/*
* Minor numbers for the sound driver.
@@ -395,11 +424,15 @@ struct mixer_def {
typedef struct mixer_def mixer_ent;
typedef struct mixer_def mixer_tab[32][2];
+#ifdef KERNEL
+
#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
{{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
{{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
+#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)
+
#define DDB(x) x /* XXX */
#ifndef DEB
@@ -431,14 +464,23 @@ void dsp_wrintr(snddev_info *d);
void dsp_rdintr(snddev_info *d);
int dsp_write_body(snddev_info *d, struct uio *buf);
int dsp_read_body(snddev_info *d, struct uio *buf);
-void alloc_dbuf(snd_dbuf *d, int size, int b16);
-void reset_dbuf(snd_dbuf *b);
+void alloc_dbuf(snd_dbuf *d, int size);
+
int snd_flush(snddev_info *d);
+
+/* the following parameters are used in snd_sync and reset_dbuf
+ * to decide whether or not to restart a channel
+ */
+#define SND_CHAN_NONE 0x0
+#define SND_CHAN_WR 0x1
+#define SND_CHAN_RD 0x2
+
+void reset_dbuf(snd_dbuf *b, int chan);
int snd_sync(snddev_info *d, int chan, int threshold);
-int dsp_wrabort(snddev_info *d);
-int dsp_rdabort(snddev_info *d);
-void dsp_wr_dmaupdate(snddev_info *d);
-void dsp_rd_dmaupdate(snddev_info *d);
+int dsp_wrabort(snddev_info *d, int restart);
+int dsp_rdabort(snddev_info *d, int restart);
+void dsp_wr_dmaupdate(snd_dbuf *b);
+void dsp_rd_dmaupdate(snd_dbuf *b);
d_poll_t sndpoll;
@@ -463,6 +505,7 @@ int sb_reset_dsp (int io_base);
void sb_setmixer (int io_base, u_int port, u_int value);
int sb_getmixer (int io_base, u_int port);
+#endif /* KERNEL */
/*
* usage of flags in device config entry (config file)
OpenPOWER on IntegriCloud