summaryrefslogtreecommitdiffstats
path: root/sys/dev/pcm
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 /sys/dev/pcm
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.
Diffstat (limited to 'sys/dev/pcm')
-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
4 files changed, 463 insertions, 286 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
OpenPOWER on IntegriCloud