aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorLuigi Rizzo <luigi@FreeBSD.org>1998-10-02 17:26:37 +0000
committerLuigi Rizzo <luigi@FreeBSD.org>1998-10-02 17:26:37 +0000
commitfe854067c7036664227a88cea1e2436b67ac9723 (patch)
tree6c7faf07ef58002971c63796aceb924a59522877 /sys
parent2ebb50c0eb3af271ca6050459871ff072c3ca896 (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pcm/isa/mss.c162
-rw-r--r--sys/dev/pcm/isa/mss.h2
-rw-r--r--sys/dev/pcm/isa/sb.c274
-rw-r--r--sys/dev/pcm/isa/sb.h11
-rw-r--r--sys/dev/sound/isa/mss.c162
-rw-r--r--sys/dev/sound/isa/mss.h2
-rw-r--r--sys/dev/sound/isa/sb.c274
-rw-r--r--sys/dev/sound/isa/sb.h11
-rw-r--r--sys/dev/sound/isa/sb16.c274
-rw-r--r--sys/dev/sound/isa/sb8.c274
-rw-r--r--sys/i386/isa/snd/CARDS171
-rw-r--r--sys/i386/isa/snd/README33
-rw-r--r--sys/i386/isa/snd/ad1848.c162
-rw-r--r--sys/i386/isa/snd/dmabuf.c21
-rw-r--r--sys/i386/isa/snd/mss.h2
-rw-r--r--sys/i386/isa/snd/sb_dsp.c274
-rw-r--r--sys/i386/isa/snd/sbcard.h11
-rw-r--r--sys/i386/isa/snd/sound.c97
-rw-r--r--sys/i386/isa/snd/sound.h7
19 files changed, 1607 insertions, 617 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c
index 095d83f0bccf9..326c5863f01ad 100644
--- a/sys/dev/pcm/isa/mss.c
+++ b/sys/dev/pcm/isa/mss.c
@@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
+ if (1) { /* machine-specific code for the Toshiba Libretto */
+ u_char r6, r9;
+ outb( 0x370, 6 /* dma config */ );
+ outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
+ r6 = inb( 0x371 /* read */ );
+ outb( 0x370, 0xa /* version */ );
+ r9 = inb( 0x371 /* read */ );
+ DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
+ /*
+ * yamaha - set volume to max
+ */
+ outb( 0x370, 7 /* volume left */ );
+ outb( 0x371, 0 /* max level */ );
+ outb( 0x370, 8 /* volume right */ );
+ outb( 0x371, 0 /* max level */ );
+ }
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
+ if (d->bd_id == MD_YM0020) {
+ DDB(printf("setting up yamaha registers\n"));
+ outb(0x370, 6 /* dma config */ ) ;
+ if (FULL_DUPLEX(d))
+ outb(0x371, 0xa9 ); /* use both dma chans */
+ else
+ outb(0x371, 0x8b ); /* use low dma chan */
+ }
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
- d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
+ d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
+ /*
+ * the nice OPTi931 sets the IRQ line before setting the bits in
+ * mc11. So, on some occasions I have to retry (max 10 times).
+ */
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
- printf("one more try...\n");
- goto again;
+ DEB(printf("one more try...\n");)
+ if (--loops)
+ goto again;
+ else
+ DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
- if (loops==10) {
+ if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- }
return;
}
@@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
- if (--loops) goto again;
+ if (--loops)
+ goto again;
DEB(printf("xxx too many loops\n");)
}
@@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
+#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
-
+#endif
/*
* Set the left channel
*/
@@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
+
+ case MD_YM0020:
+ /* set master volume to max */
+ DDB(printf("set yamaha master volume to max"); )
+ outb(0x370, 7) ;
+ outb(0x371, 0) ;
+ outb(0x370, 8) ;
+ outb(0x371, 0) ;
+ break;
+
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
- if (d->bd_id == MD_OPTI931) {
+ if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
+ DDB(printf("... try to identify the yamaha\n") ;)
+ tmp = inb(0x370) ;
+ outb(0x370, 6 /* dma config */ ) ;
+ if (inb(0x370) != 6 ) /* not a yamaha... restore. */
+ outb(0x370, tmp ) ;
+ else
+ d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
-
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
- else if ( id == 0x3600630e )
+ else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
- else if ( id == 0x0000a865)
+ else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
+ if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
+ vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
- case 0x0000a865: /* Yamaha YMF719 */
+ case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
+ case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
- if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
+ if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
- * hardware controlled by both the WSS and the SB port...
+ * hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
- opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
+ opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
- opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
+ opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static char *opti925_probe(u_long csn, u_long vend_id);
+static void opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device opti925 = {
+ "opti925",
+ opti925_probe,
+ opti925_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, opti925);
+
+static char *
+opti925_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x2509143e) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an OPTi925, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "OPTi925" ;
+ }
+ return NULL ;
+}
+
+static void
+opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int the_irq = 0 ;
+
+ tmp_d = mss_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ the_irq = d.irq[0];
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 3 );
+
+ read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 2 );
+
+ read_pnp_parms ( &d , 1 ) ;
+ d.irq[0] = the_irq ;
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ tmp_d.conf_base = d.port[3];
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+}
+
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
diff --git a/sys/dev/pcm/isa/mss.h b/sys/dev/pcm/isa/mss.h
index 994f323c2bef5..54efc3cc21950 100644
--- a/sys/dev/pcm/isa/mss.h
+++ b/sys/dev/pcm/isa/mss.h
@@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
- * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c
index 575980cc8821b..349a7e2102138 100644
--- a/sys/dev/pcm/isa/sb.c
+++ b/sys/dev/pcm/isa/sb.c
@@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
- * Copyright 1997 Luigi Rizzo.
+ * Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d);
static void sb_mixer_reset(snddev_info *d);
u_int sb_get_byte(int io_base);
+int ess_write(int io_base, u_char reg, int val);
+int ess_read(int io_base, u_char reg);
/*
* Then put here the descriptors for the various boards supported
@@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* here are the main routines from the switches.
*/
+/*
+ * Unlike MSS, the sb only supports a single open (does not mean
+ * that only a single process is using it, since it can fork
+ * afterwards, or pass the descriptor to another process).
+ *
+ */
static int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
- if ( (flags & FREAD) == 0)
+ /*
+ * since the SB is not simmetric, I use the open mode to select
+ * which channel should be privileged, and disable I/O in the
+ * other direction.
+ * In case the board is opened RW, we don't have enough
+ * information on what to do. Temporarily, privilege the
+ * playback channel, which is used more often, and set the other
+ * one to U8.
+ */
+ if ( (flags & FREAD) == 0) /* opened write only */
d->rec_fmt = 0 ;
- if ( (flags & FWRITE) == 0)
+ else if ( (flags & FWRITE) == 0) /* opened read only */
d->play_fmt = 0 ;
+ else /* opened read/write */
+ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -275,7 +294,10 @@ sb_intr(int unit)
* SB < 4.0 is half duplex and has only 1 bit for int source,
* so we fake it. SB 4.x (SB16) has the int source in a separate
* register.
+ * The Vibra16X has separate flags for 8 and 16 bit transfers, but
+ * I have no idea how to tell capture from playback interrupts...
*/
+#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)
again:
if (d->bd_flags & BD_F_SB16) {
c = sb_getmixer(io_base, IRQ_STAT);
@@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
- if (d->dbuf_out.chan < 4)
+ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
- if (d->dbuf_in.chan < 4)
+ if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
- if (d->dbuf_out.chan >= 4)
+ if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
- if (d->dbuf_in.chan >= 4)
+ if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
- DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
+ DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n",
+ d->flags, reason, c));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
dsp_wrintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
+ /*
+ * set the speed
+ */
dsp_speed(d);
+ /*
+ * set the desired DMA blocksize (influences select behaviour)
+ */
snd_set_blocksize(d);
+ /*
+ * since native mulaw is not present, emulate it.
+ */
if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- if (d->bd_flags & BD_F_SB16) {
+ /*
+ * there are too many flavours of SB for my taste... here i try to do
+ * the proper initialization for each one.
+ */
+ if (PLAIN_SB16(d->bd_flags)) {
u_char c, c1 ;
- /* the SB16 can do full duplex using one 16-bit channel
+ /* the original SB16 (non-PnP, or PnP, or Vibra16C)
+ * 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:
+ * I DON'T do this for the Vibra16X because I have no idea
+ * of what needs to be done there...
+ *
+ * I 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.
@@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
- /* check for possible config errors. */
+ /* check for possible config errors.
+ * This cannot happen at open time since even in
+ * case of opening rw we privilege the play
+ * channel.
+ */
if (d->rec_fmt == d->play_fmt) {
DDB(printf("sorry, read DMA channel unavailable\n"));
}
@@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
+ } else if (d->bd_flags & BD_F_ESS) {
+ u_char c ;
+ if (d->play_fmt == 0) {
+ /* initialize for record */
+ static u_char cmd[] = {
+ 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
+ };
+ ess_write(d->io_base, 0xb8, 0x0e);
+ c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
+ if (d->flags & SND_F_STEREO)
+ c++ ;
+ ess_write(d->io_base, 0xa8, c);
+ ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
+ /*
+ * set format in b6, b7
+ */
+ } else {
+ /* initialize for play */
+ static u_char cmd[] = {
+ 0x80,0x51,0xd0,0x00,0x71,0xf4,
+ 0x80,0x51,0x98,0x00,0x71,0xbc
+ };
+ }
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
+
+ if (d->bd_flags & BD_F_SB16X) {
+ /* just a guess: on the Vibra16X, the first
+ * op started takes the first dma channel,
+ * the second one takes the next...
+ * The default is to be ready for play.
+ */
+ int swap = 0 ;
+ DEB(printf("start %s -- now dma %d:%d\n",
+ rd ? "rd" : "wr",
+ d->dbuf_out.chan, d->dbuf_in.chan););
+ /* swap only if both channels are idle
+ * play: dl=0, since there is no pause;
+ * rec: rl=0
+ */
+ if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
+ /* must swap channels, but also save dl */
+ int c = d->dbuf_in.chan ;
+ int dl = d->dbuf_in.dl ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ d->dbuf_in.dl = dl ;
+ printf("swapped -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
+
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
- if ( b->chan > 4 ) {
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
- if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ )
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
+ if (d->bd_flags & BD_F_SB16X) {
+ /* restore possible swapped channels.
+ * The default is to be ready for play.
+ * XXX right now, it kills all input on overflow
+ */
+ if ( rd && d->dbuf_out.dl == 0 ) {
+ /* must swap channels ? */
+ int c = d->dbuf_in.chan ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ printf("restored -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
+ /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
+ *
+ * sb_cmd write a single byte to the CMD port.
+ * sb_cmd2 write a CMD + 1 byte arg
+ * sb_cmd3 write a CMD + 2 byte arg
+ * sb_get_byte returns a single byte from the DSP data port
+ *
+ * ess_write is actually sb_cmd2
+ * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(io_base + 5);
+ val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
+int
+ess_write(int io_base, u_char reg, int val)
+{
+ return sb_cmd2(io_base, reg, val);
+}
+
+int
+ess_read(int io_base, u_char reg)
+{
+ if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
+ return 0xffff ;
+ return sb_get_byte(io_base);
+}
/*
@@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
- RANGE (speed, 4000, 48000);
+ RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
- t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
+ t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
- t = 128 - (397700 + speed / 2) / speed ;
+ t = 128 - t ;
}
- sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
+ ess_write(d->io_base, 0xa1, t); /* set time constant */
+ d->play_speed = d->rec_speed = speed ;
+ speed = (speed * 9 ) / 20 ;
+ t = 256-7160000/(speed*82);
+ ess_write(d->io_base,0xa2,t);
return speed ;
}
@@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
-static char *opti925_probe(u_long csn, u_long vend_id);
-static void opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev);
-
-static struct pnp_device opti925 = {
- "opti925",
- opti925_probe,
- opti925_attach,
- &nsnd, /* use this for all sound cards */
- &tty_imask /* imask */
-};
-DATA_SET (pnpdevice_set, opti925);
-
-static char *
-opti925_probe(u_long csn, u_long vend_id)
-{
- if (vend_id == 0x2509143e) {
- struct pnp_cinfo d ;
- read_pnp_parms ( &d , 1 ) ;
- if (d.enable == 0) {
- printf("This is an OPTi925, but LDN 1 is disabled\n");
- return NULL;
- }
- return "OPTi925" ;
- }
- return NULL ;
-}
-
-static void
-opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev)
-{
- struct pnp_cinfo d ;
- snddev_info tmp_d ; /* patched copy of the basic snddev_info */
- int the_irq = 0 ;
-
- tmp_d = sb_op_desc;
- snddev_last_probed = &tmp_d;
-
- read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
- the_irq = d.irq[0];
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 3 );
-
- read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 2 );
-
- read_pnp_parms ( &d , 1 ) ;
- d.irq[0] = the_irq ;
- dev->id_iobase = d.port[0];
- write_pnp_parms ( &d , 1 );
- enable_pnp_card();
-
- tmp_d.conf_base = d.port[3];
-
- dev->id_drq = d.drq[0] ; /* primary dma */
- dev->id_irq = (1 << d.irq[0] ) ;
- dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
-
- snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
-
- pcmattach(dev);
-
-}
-
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
+ * Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ * Vibra16X: 0xf0008c0e
*/
- if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ if (vend_id == 0xf0008c0e)
+ s = "Vibra16X" ;
+ else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
+ else if (vend_id == 0x10019305)
+ s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- pcm_info[dev->id_unit] = tmp_d;
+ pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
+ /*
+ * XXX please add here the vend_id for other vibra16X cards...
+ * And remember, must change tmp_d, not
+ */
+ tmp_d.bd_flags |= BD_F_SB16X ;
+ }
pcmattach(dev);
}
#endif /* NPNP */
diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h
index 0bd42102d715b..92ec966107911 100644
--- a/sys/dev/pcm/isa/sb.h
+++ b/sys/dev/pcm/isa/sb.h
@@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+#define SB_MIX_ADDR 0x4
+#define SB_MIX_DATA 0x5
#if 0
-#define MIXER_ADDR (io_base + 0x4)
-#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
-#define BD_F_NOREC 0x0200 /* recording not supported on this board */
+#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
+/*
+ * on some SB16 cards, at times I swap DMA channels. Remember this
+ * so that they can be restored later.
+ */
+#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*
diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c
index 095d83f0bccf9..326c5863f01ad 100644
--- a/sys/dev/sound/isa/mss.c
+++ b/sys/dev/sound/isa/mss.c
@@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
+ if (1) { /* machine-specific code for the Toshiba Libretto */
+ u_char r6, r9;
+ outb( 0x370, 6 /* dma config */ );
+ outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
+ r6 = inb( 0x371 /* read */ );
+ outb( 0x370, 0xa /* version */ );
+ r9 = inb( 0x371 /* read */ );
+ DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
+ /*
+ * yamaha - set volume to max
+ */
+ outb( 0x370, 7 /* volume left */ );
+ outb( 0x371, 0 /* max level */ );
+ outb( 0x370, 8 /* volume right */ );
+ outb( 0x371, 0 /* max level */ );
+ }
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
+ if (d->bd_id == MD_YM0020) {
+ DDB(printf("setting up yamaha registers\n"));
+ outb(0x370, 6 /* dma config */ ) ;
+ if (FULL_DUPLEX(d))
+ outb(0x371, 0xa9 ); /* use both dma chans */
+ else
+ outb(0x371, 0x8b ); /* use low dma chan */
+ }
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
- d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
+ d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
+ /*
+ * the nice OPTi931 sets the IRQ line before setting the bits in
+ * mc11. So, on some occasions I have to retry (max 10 times).
+ */
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
- printf("one more try...\n");
- goto again;
+ DEB(printf("one more try...\n");)
+ if (--loops)
+ goto again;
+ else
+ DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
- if (loops==10) {
+ if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- }
return;
}
@@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
- if (--loops) goto again;
+ if (--loops)
+ goto again;
DEB(printf("xxx too many loops\n");)
}
@@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
+#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
-
+#endif
/*
* Set the left channel
*/
@@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
+
+ case MD_YM0020:
+ /* set master volume to max */
+ DDB(printf("set yamaha master volume to max"); )
+ outb(0x370, 7) ;
+ outb(0x371, 0) ;
+ outb(0x370, 8) ;
+ outb(0x371, 0) ;
+ break;
+
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
- if (d->bd_id == MD_OPTI931) {
+ if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
+ DDB(printf("... try to identify the yamaha\n") ;)
+ tmp = inb(0x370) ;
+ outb(0x370, 6 /* dma config */ ) ;
+ if (inb(0x370) != 6 ) /* not a yamaha... restore. */
+ outb(0x370, tmp ) ;
+ else
+ d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
-
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
- else if ( id == 0x3600630e )
+ else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
- else if ( id == 0x0000a865)
+ else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
+ if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
+ vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
- case 0x0000a865: /* Yamaha YMF719 */
+ case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
+ case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
- if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
+ if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
- * hardware controlled by both the WSS and the SB port...
+ * hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
- opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
+ opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
- opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
+ opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static char *opti925_probe(u_long csn, u_long vend_id);
+static void opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device opti925 = {
+ "opti925",
+ opti925_probe,
+ opti925_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, opti925);
+
+static char *
+opti925_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x2509143e) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an OPTi925, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "OPTi925" ;
+ }
+ return NULL ;
+}
+
+static void
+opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int the_irq = 0 ;
+
+ tmp_d = mss_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ the_irq = d.irq[0];
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 3 );
+
+ read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 2 );
+
+ read_pnp_parms ( &d , 1 ) ;
+ d.irq[0] = the_irq ;
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ tmp_d.conf_base = d.port[3];
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+}
+
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h
index 994f323c2bef5..54efc3cc21950 100644
--- a/sys/dev/sound/isa/mss.h
+++ b/sys/dev/sound/isa/mss.h
@@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
- * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c
index 575980cc8821b..349a7e2102138 100644
--- a/sys/dev/sound/isa/sb.c
+++ b/sys/dev/sound/isa/sb.c
@@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
- * Copyright 1997 Luigi Rizzo.
+ * Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d);
static void sb_mixer_reset(snddev_info *d);
u_int sb_get_byte(int io_base);
+int ess_write(int io_base, u_char reg, int val);
+int ess_read(int io_base, u_char reg);
/*
* Then put here the descriptors for the various boards supported
@@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* here are the main routines from the switches.
*/
+/*
+ * Unlike MSS, the sb only supports a single open (does not mean
+ * that only a single process is using it, since it can fork
+ * afterwards, or pass the descriptor to another process).
+ *
+ */
static int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
- if ( (flags & FREAD) == 0)
+ /*
+ * since the SB is not simmetric, I use the open mode to select
+ * which channel should be privileged, and disable I/O in the
+ * other direction.
+ * In case the board is opened RW, we don't have enough
+ * information on what to do. Temporarily, privilege the
+ * playback channel, which is used more often, and set the other
+ * one to U8.
+ */
+ if ( (flags & FREAD) == 0) /* opened write only */
d->rec_fmt = 0 ;
- if ( (flags & FWRITE) == 0)
+ else if ( (flags & FWRITE) == 0) /* opened read only */
d->play_fmt = 0 ;
+ else /* opened read/write */
+ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -275,7 +294,10 @@ sb_intr(int unit)
* SB < 4.0 is half duplex and has only 1 bit for int source,
* so we fake it. SB 4.x (SB16) has the int source in a separate
* register.
+ * The Vibra16X has separate flags for 8 and 16 bit transfers, but
+ * I have no idea how to tell capture from playback interrupts...
*/
+#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)
again:
if (d->bd_flags & BD_F_SB16) {
c = sb_getmixer(io_base, IRQ_STAT);
@@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
- if (d->dbuf_out.chan < 4)
+ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
- if (d->dbuf_in.chan < 4)
+ if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
- if (d->dbuf_out.chan >= 4)
+ if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
- if (d->dbuf_in.chan >= 4)
+ if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
- DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
+ DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n",
+ d->flags, reason, c));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
dsp_wrintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
+ /*
+ * set the speed
+ */
dsp_speed(d);
+ /*
+ * set the desired DMA blocksize (influences select behaviour)
+ */
snd_set_blocksize(d);
+ /*
+ * since native mulaw is not present, emulate it.
+ */
if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- if (d->bd_flags & BD_F_SB16) {
+ /*
+ * there are too many flavours of SB for my taste... here i try to do
+ * the proper initialization for each one.
+ */
+ if (PLAIN_SB16(d->bd_flags)) {
u_char c, c1 ;
- /* the SB16 can do full duplex using one 16-bit channel
+ /* the original SB16 (non-PnP, or PnP, or Vibra16C)
+ * 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:
+ * I DON'T do this for the Vibra16X because I have no idea
+ * of what needs to be done there...
+ *
+ * I 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.
@@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
- /* check for possible config errors. */
+ /* check for possible config errors.
+ * This cannot happen at open time since even in
+ * case of opening rw we privilege the play
+ * channel.
+ */
if (d->rec_fmt == d->play_fmt) {
DDB(printf("sorry, read DMA channel unavailable\n"));
}
@@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
+ } else if (d->bd_flags & BD_F_ESS) {
+ u_char c ;
+ if (d->play_fmt == 0) {
+ /* initialize for record */
+ static u_char cmd[] = {
+ 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
+ };
+ ess_write(d->io_base, 0xb8, 0x0e);
+ c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
+ if (d->flags & SND_F_STEREO)
+ c++ ;
+ ess_write(d->io_base, 0xa8, c);
+ ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
+ /*
+ * set format in b6, b7
+ */
+ } else {
+ /* initialize for play */
+ static u_char cmd[] = {
+ 0x80,0x51,0xd0,0x00,0x71,0xf4,
+ 0x80,0x51,0x98,0x00,0x71,0xbc
+ };
+ }
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
+
+ if (d->bd_flags & BD_F_SB16X) {
+ /* just a guess: on the Vibra16X, the first
+ * op started takes the first dma channel,
+ * the second one takes the next...
+ * The default is to be ready for play.
+ */
+ int swap = 0 ;
+ DEB(printf("start %s -- now dma %d:%d\n",
+ rd ? "rd" : "wr",
+ d->dbuf_out.chan, d->dbuf_in.chan););
+ /* swap only if both channels are idle
+ * play: dl=0, since there is no pause;
+ * rec: rl=0
+ */
+ if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
+ /* must swap channels, but also save dl */
+ int c = d->dbuf_in.chan ;
+ int dl = d->dbuf_in.dl ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ d->dbuf_in.dl = dl ;
+ printf("swapped -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
+
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
- if ( b->chan > 4 ) {
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
- if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ )
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
+ if (d->bd_flags & BD_F_SB16X) {
+ /* restore possible swapped channels.
+ * The default is to be ready for play.
+ * XXX right now, it kills all input on overflow
+ */
+ if ( rd && d->dbuf_out.dl == 0 ) {
+ /* must swap channels ? */
+ int c = d->dbuf_in.chan ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ printf("restored -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
+ /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
+ *
+ * sb_cmd write a single byte to the CMD port.
+ * sb_cmd2 write a CMD + 1 byte arg
+ * sb_cmd3 write a CMD + 2 byte arg
+ * sb_get_byte returns a single byte from the DSP data port
+ *
+ * ess_write is actually sb_cmd2
+ * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(io_base + 5);
+ val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
+int
+ess_write(int io_base, u_char reg, int val)
+{
+ return sb_cmd2(io_base, reg, val);
+}
+
+int
+ess_read(int io_base, u_char reg)
+{
+ if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
+ return 0xffff ;
+ return sb_get_byte(io_base);
+}
/*
@@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
- RANGE (speed, 4000, 48000);
+ RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
- t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
+ t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
- t = 128 - (397700 + speed / 2) / speed ;
+ t = 128 - t ;
}
- sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
+ ess_write(d->io_base, 0xa1, t); /* set time constant */
+ d->play_speed = d->rec_speed = speed ;
+ speed = (speed * 9 ) / 20 ;
+ t = 256-7160000/(speed*82);
+ ess_write(d->io_base,0xa2,t);
return speed ;
}
@@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
-static char *opti925_probe(u_long csn, u_long vend_id);
-static void opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev);
-
-static struct pnp_device opti925 = {
- "opti925",
- opti925_probe,
- opti925_attach,
- &nsnd, /* use this for all sound cards */
- &tty_imask /* imask */
-};
-DATA_SET (pnpdevice_set, opti925);
-
-static char *
-opti925_probe(u_long csn, u_long vend_id)
-{
- if (vend_id == 0x2509143e) {
- struct pnp_cinfo d ;
- read_pnp_parms ( &d , 1 ) ;
- if (d.enable == 0) {
- printf("This is an OPTi925, but LDN 1 is disabled\n");
- return NULL;
- }
- return "OPTi925" ;
- }
- return NULL ;
-}
-
-static void
-opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev)
-{
- struct pnp_cinfo d ;
- snddev_info tmp_d ; /* patched copy of the basic snddev_info */
- int the_irq = 0 ;
-
- tmp_d = sb_op_desc;
- snddev_last_probed = &tmp_d;
-
- read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
- the_irq = d.irq[0];
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 3 );
-
- read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 2 );
-
- read_pnp_parms ( &d , 1 ) ;
- d.irq[0] = the_irq ;
- dev->id_iobase = d.port[0];
- write_pnp_parms ( &d , 1 );
- enable_pnp_card();
-
- tmp_d.conf_base = d.port[3];
-
- dev->id_drq = d.drq[0] ; /* primary dma */
- dev->id_irq = (1 << d.irq[0] ) ;
- dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
-
- snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
-
- pcmattach(dev);
-
-}
-
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
+ * Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ * Vibra16X: 0xf0008c0e
*/
- if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ if (vend_id == 0xf0008c0e)
+ s = "Vibra16X" ;
+ else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
+ else if (vend_id == 0x10019305)
+ s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- pcm_info[dev->id_unit] = tmp_d;
+ pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
+ /*
+ * XXX please add here the vend_id for other vibra16X cards...
+ * And remember, must change tmp_d, not
+ */
+ tmp_d.bd_flags |= BD_F_SB16X ;
+ }
pcmattach(dev);
}
#endif /* NPNP */
diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h
index 0bd42102d715b..92ec966107911 100644
--- a/sys/dev/sound/isa/sb.h
+++ b/sys/dev/sound/isa/sb.h
@@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+#define SB_MIX_ADDR 0x4
+#define SB_MIX_DATA 0x5
#if 0
-#define MIXER_ADDR (io_base + 0x4)
-#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
-#define BD_F_NOREC 0x0200 /* recording not supported on this board */
+#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
+/*
+ * on some SB16 cards, at times I swap DMA channels. Remember this
+ * so that they can be restored later.
+ */
+#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*
diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c
index 575980cc8821b..349a7e2102138 100644
--- a/sys/dev/sound/isa/sb16.c
+++ b/sys/dev/sound/isa/sb16.c
@@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
- * Copyright 1997 Luigi Rizzo.
+ * Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d);
static void sb_mixer_reset(snddev_info *d);
u_int sb_get_byte(int io_base);
+int ess_write(int io_base, u_char reg, int val);
+int ess_read(int io_base, u_char reg);
/*
* Then put here the descriptors for the various boards supported
@@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* here are the main routines from the switches.
*/
+/*
+ * Unlike MSS, the sb only supports a single open (does not mean
+ * that only a single process is using it, since it can fork
+ * afterwards, or pass the descriptor to another process).
+ *
+ */
static int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
- if ( (flags & FREAD) == 0)
+ /*
+ * since the SB is not simmetric, I use the open mode to select
+ * which channel should be privileged, and disable I/O in the
+ * other direction.
+ * In case the board is opened RW, we don't have enough
+ * information on what to do. Temporarily, privilege the
+ * playback channel, which is used more often, and set the other
+ * one to U8.
+ */
+ if ( (flags & FREAD) == 0) /* opened write only */
d->rec_fmt = 0 ;
- if ( (flags & FWRITE) == 0)
+ else if ( (flags & FWRITE) == 0) /* opened read only */
d->play_fmt = 0 ;
+ else /* opened read/write */
+ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -275,7 +294,10 @@ sb_intr(int unit)
* SB < 4.0 is half duplex and has only 1 bit for int source,
* so we fake it. SB 4.x (SB16) has the int source in a separate
* register.
+ * The Vibra16X has separate flags for 8 and 16 bit transfers, but
+ * I have no idea how to tell capture from playback interrupts...
*/
+#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)
again:
if (d->bd_flags & BD_F_SB16) {
c = sb_getmixer(io_base, IRQ_STAT);
@@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
- if (d->dbuf_out.chan < 4)
+ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
- if (d->dbuf_in.chan < 4)
+ if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
- if (d->dbuf_out.chan >= 4)
+ if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
- if (d->dbuf_in.chan >= 4)
+ if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
- DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
+ DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n",
+ d->flags, reason, c));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
dsp_wrintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
+ /*
+ * set the speed
+ */
dsp_speed(d);
+ /*
+ * set the desired DMA blocksize (influences select behaviour)
+ */
snd_set_blocksize(d);
+ /*
+ * since native mulaw is not present, emulate it.
+ */
if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- if (d->bd_flags & BD_F_SB16) {
+ /*
+ * there are too many flavours of SB for my taste... here i try to do
+ * the proper initialization for each one.
+ */
+ if (PLAIN_SB16(d->bd_flags)) {
u_char c, c1 ;
- /* the SB16 can do full duplex using one 16-bit channel
+ /* the original SB16 (non-PnP, or PnP, or Vibra16C)
+ * 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:
+ * I DON'T do this for the Vibra16X because I have no idea
+ * of what needs to be done there...
+ *
+ * I 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.
@@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
- /* check for possible config errors. */
+ /* check for possible config errors.
+ * This cannot happen at open time since even in
+ * case of opening rw we privilege the play
+ * channel.
+ */
if (d->rec_fmt == d->play_fmt) {
DDB(printf("sorry, read DMA channel unavailable\n"));
}
@@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
+ } else if (d->bd_flags & BD_F_ESS) {
+ u_char c ;
+ if (d->play_fmt == 0) {
+ /* initialize for record */
+ static u_char cmd[] = {
+ 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
+ };
+ ess_write(d->io_base, 0xb8, 0x0e);
+ c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
+ if (d->flags & SND_F_STEREO)
+ c++ ;
+ ess_write(d->io_base, 0xa8, c);
+ ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
+ /*
+ * set format in b6, b7
+ */
+ } else {
+ /* initialize for play */
+ static u_char cmd[] = {
+ 0x80,0x51,0xd0,0x00,0x71,0xf4,
+ 0x80,0x51,0x98,0x00,0x71,0xbc
+ };
+ }
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
+
+ if (d->bd_flags & BD_F_SB16X) {
+ /* just a guess: on the Vibra16X, the first
+ * op started takes the first dma channel,
+ * the second one takes the next...
+ * The default is to be ready for play.
+ */
+ int swap = 0 ;
+ DEB(printf("start %s -- now dma %d:%d\n",
+ rd ? "rd" : "wr",
+ d->dbuf_out.chan, d->dbuf_in.chan););
+ /* swap only if both channels are idle
+ * play: dl=0, since there is no pause;
+ * rec: rl=0
+ */
+ if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
+ /* must swap channels, but also save dl */
+ int c = d->dbuf_in.chan ;
+ int dl = d->dbuf_in.dl ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ d->dbuf_in.dl = dl ;
+ printf("swapped -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
+
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
- if ( b->chan > 4 ) {
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
- if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ )
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
+ if (d->bd_flags & BD_F_SB16X) {
+ /* restore possible swapped channels.
+ * The default is to be ready for play.
+ * XXX right now, it kills all input on overflow
+ */
+ if ( rd && d->dbuf_out.dl == 0 ) {
+ /* must swap channels ? */
+ int c = d->dbuf_in.chan ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ printf("restored -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
+ /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
+ *
+ * sb_cmd write a single byte to the CMD port.
+ * sb_cmd2 write a CMD + 1 byte arg
+ * sb_cmd3 write a CMD + 2 byte arg
+ * sb_get_byte returns a single byte from the DSP data port
+ *
+ * ess_write is actually sb_cmd2
+ * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(io_base + 5);
+ val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
+int
+ess_write(int io_base, u_char reg, int val)
+{
+ return sb_cmd2(io_base, reg, val);
+}
+
+int
+ess_read(int io_base, u_char reg)
+{
+ if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
+ return 0xffff ;
+ return sb_get_byte(io_base);
+}
/*
@@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
- RANGE (speed, 4000, 48000);
+ RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
- t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
+ t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
- t = 128 - (397700 + speed / 2) / speed ;
+ t = 128 - t ;
}
- sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
+ ess_write(d->io_base, 0xa1, t); /* set time constant */
+ d->play_speed = d->rec_speed = speed ;
+ speed = (speed * 9 ) / 20 ;
+ t = 256-7160000/(speed*82);
+ ess_write(d->io_base,0xa2,t);
return speed ;
}
@@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
-static char *opti925_probe(u_long csn, u_long vend_id);
-static void opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev);
-
-static struct pnp_device opti925 = {
- "opti925",
- opti925_probe,
- opti925_attach,
- &nsnd, /* use this for all sound cards */
- &tty_imask /* imask */
-};
-DATA_SET (pnpdevice_set, opti925);
-
-static char *
-opti925_probe(u_long csn, u_long vend_id)
-{
- if (vend_id == 0x2509143e) {
- struct pnp_cinfo d ;
- read_pnp_parms ( &d , 1 ) ;
- if (d.enable == 0) {
- printf("This is an OPTi925, but LDN 1 is disabled\n");
- return NULL;
- }
- return "OPTi925" ;
- }
- return NULL ;
-}
-
-static void
-opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev)
-{
- struct pnp_cinfo d ;
- snddev_info tmp_d ; /* patched copy of the basic snddev_info */
- int the_irq = 0 ;
-
- tmp_d = sb_op_desc;
- snddev_last_probed = &tmp_d;
-
- read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
- the_irq = d.irq[0];
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 3 );
-
- read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 2 );
-
- read_pnp_parms ( &d , 1 ) ;
- d.irq[0] = the_irq ;
- dev->id_iobase = d.port[0];
- write_pnp_parms ( &d , 1 );
- enable_pnp_card();
-
- tmp_d.conf_base = d.port[3];
-
- dev->id_drq = d.drq[0] ; /* primary dma */
- dev->id_irq = (1 << d.irq[0] ) ;
- dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
-
- snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
-
- pcmattach(dev);
-
-}
-
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
+ * Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ * Vibra16X: 0xf0008c0e
*/
- if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ if (vend_id == 0xf0008c0e)
+ s = "Vibra16X" ;
+ else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
+ else if (vend_id == 0x10019305)
+ s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- pcm_info[dev->id_unit] = tmp_d;
+ pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
+ /*
+ * XXX please add here the vend_id for other vibra16X cards...
+ * And remember, must change tmp_d, not
+ */
+ tmp_d.bd_flags |= BD_F_SB16X ;
+ }
pcmattach(dev);
}
#endif /* NPNP */
diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c
index 575980cc8821b..349a7e2102138 100644
--- a/sys/dev/sound/isa/sb8.c
+++ b/sys/dev/sound/isa/sb8.c
@@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
- * Copyright 1997 Luigi Rizzo.
+ * Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d);
static void sb_mixer_reset(snddev_info *d);
u_int sb_get_byte(int io_base);
+int ess_write(int io_base, u_char reg, int val);
+int ess_read(int io_base, u_char reg);
/*
* Then put here the descriptors for the various boards supported
@@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* here are the main routines from the switches.
*/
+/*
+ * Unlike MSS, the sb only supports a single open (does not mean
+ * that only a single process is using it, since it can fork
+ * afterwards, or pass the descriptor to another process).
+ *
+ */
static int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
- if ( (flags & FREAD) == 0)
+ /*
+ * since the SB is not simmetric, I use the open mode to select
+ * which channel should be privileged, and disable I/O in the
+ * other direction.
+ * In case the board is opened RW, we don't have enough
+ * information on what to do. Temporarily, privilege the
+ * playback channel, which is used more often, and set the other
+ * one to U8.
+ */
+ if ( (flags & FREAD) == 0) /* opened write only */
d->rec_fmt = 0 ;
- if ( (flags & FWRITE) == 0)
+ else if ( (flags & FWRITE) == 0) /* opened read only */
d->play_fmt = 0 ;
+ else /* opened read/write */
+ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -275,7 +294,10 @@ sb_intr(int unit)
* SB < 4.0 is half duplex and has only 1 bit for int source,
* so we fake it. SB 4.x (SB16) has the int source in a separate
* register.
+ * The Vibra16X has separate flags for 8 and 16 bit transfers, but
+ * I have no idea how to tell capture from playback interrupts...
*/
+#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)
again:
if (d->bd_flags & BD_F_SB16) {
c = sb_getmixer(io_base, IRQ_STAT);
@@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
- if (d->dbuf_out.chan < 4)
+ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
- if (d->dbuf_in.chan < 4)
+ if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
- if (d->dbuf_out.chan >= 4)
+ if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
- if (d->dbuf_in.chan >= 4)
+ if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
- DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
+ DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n",
+ d->flags, reason, c));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
dsp_wrintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
+ /*
+ * set the speed
+ */
dsp_speed(d);
+ /*
+ * set the desired DMA blocksize (influences select behaviour)
+ */
snd_set_blocksize(d);
+ /*
+ * since native mulaw is not present, emulate it.
+ */
if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- if (d->bd_flags & BD_F_SB16) {
+ /*
+ * there are too many flavours of SB for my taste... here i try to do
+ * the proper initialization for each one.
+ */
+ if (PLAIN_SB16(d->bd_flags)) {
u_char c, c1 ;
- /* the SB16 can do full duplex using one 16-bit channel
+ /* the original SB16 (non-PnP, or PnP, or Vibra16C)
+ * 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:
+ * I DON'T do this for the Vibra16X because I have no idea
+ * of what needs to be done there...
+ *
+ * I 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.
@@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
- /* check for possible config errors. */
+ /* check for possible config errors.
+ * This cannot happen at open time since even in
+ * case of opening rw we privilege the play
+ * channel.
+ */
if (d->rec_fmt == d->play_fmt) {
DDB(printf("sorry, read DMA channel unavailable\n"));
}
@@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
+ } else if (d->bd_flags & BD_F_ESS) {
+ u_char c ;
+ if (d->play_fmt == 0) {
+ /* initialize for record */
+ static u_char cmd[] = {
+ 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
+ };
+ ess_write(d->io_base, 0xb8, 0x0e);
+ c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
+ if (d->flags & SND_F_STEREO)
+ c++ ;
+ ess_write(d->io_base, 0xa8, c);
+ ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
+ /*
+ * set format in b6, b7
+ */
+ } else {
+ /* initialize for play */
+ static u_char cmd[] = {
+ 0x80,0x51,0xd0,0x00,0x71,0xf4,
+ 0x80,0x51,0x98,0x00,0x71,0xbc
+ };
+ }
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
+
+ if (d->bd_flags & BD_F_SB16X) {
+ /* just a guess: on the Vibra16X, the first
+ * op started takes the first dma channel,
+ * the second one takes the next...
+ * The default is to be ready for play.
+ */
+ int swap = 0 ;
+ DEB(printf("start %s -- now dma %d:%d\n",
+ rd ? "rd" : "wr",
+ d->dbuf_out.chan, d->dbuf_in.chan););
+ /* swap only if both channels are idle
+ * play: dl=0, since there is no pause;
+ * rec: rl=0
+ */
+ if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
+ /* must swap channels, but also save dl */
+ int c = d->dbuf_in.chan ;
+ int dl = d->dbuf_in.dl ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ d->dbuf_in.dl = dl ;
+ printf("swapped -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
+
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
- if ( b->chan > 4 ) {
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
- if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ )
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
+ if (d->bd_flags & BD_F_SB16X) {
+ /* restore possible swapped channels.
+ * The default is to be ready for play.
+ * XXX right now, it kills all input on overflow
+ */
+ if ( rd && d->dbuf_out.dl == 0 ) {
+ /* must swap channels ? */
+ int c = d->dbuf_in.chan ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ printf("restored -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
+ /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
+ *
+ * sb_cmd write a single byte to the CMD port.
+ * sb_cmd2 write a CMD + 1 byte arg
+ * sb_cmd3 write a CMD + 2 byte arg
+ * sb_get_byte returns a single byte from the DSP data port
+ *
+ * ess_write is actually sb_cmd2
+ * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(io_base + 5);
+ val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
+int
+ess_write(int io_base, u_char reg, int val)
+{
+ return sb_cmd2(io_base, reg, val);
+}
+
+int
+ess_read(int io_base, u_char reg)
+{
+ if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
+ return 0xffff ;
+ return sb_get_byte(io_base);
+}
/*
@@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
- RANGE (speed, 4000, 48000);
+ RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
- t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
+ t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
- t = 128 - (397700 + speed / 2) / speed ;
+ t = 128 - t ;
}
- sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
+ ess_write(d->io_base, 0xa1, t); /* set time constant */
+ d->play_speed = d->rec_speed = speed ;
+ speed = (speed * 9 ) / 20 ;
+ t = 256-7160000/(speed*82);
+ ess_write(d->io_base,0xa2,t);
return speed ;
}
@@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
-static char *opti925_probe(u_long csn, u_long vend_id);
-static void opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev);
-
-static struct pnp_device opti925 = {
- "opti925",
- opti925_probe,
- opti925_attach,
- &nsnd, /* use this for all sound cards */
- &tty_imask /* imask */
-};
-DATA_SET (pnpdevice_set, opti925);
-
-static char *
-opti925_probe(u_long csn, u_long vend_id)
-{
- if (vend_id == 0x2509143e) {
- struct pnp_cinfo d ;
- read_pnp_parms ( &d , 1 ) ;
- if (d.enable == 0) {
- printf("This is an OPTi925, but LDN 1 is disabled\n");
- return NULL;
- }
- return "OPTi925" ;
- }
- return NULL ;
-}
-
-static void
-opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev)
-{
- struct pnp_cinfo d ;
- snddev_info tmp_d ; /* patched copy of the basic snddev_info */
- int the_irq = 0 ;
-
- tmp_d = sb_op_desc;
- snddev_last_probed = &tmp_d;
-
- read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
- the_irq = d.irq[0];
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 3 );
-
- read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 2 );
-
- read_pnp_parms ( &d , 1 ) ;
- d.irq[0] = the_irq ;
- dev->id_iobase = d.port[0];
- write_pnp_parms ( &d , 1 );
- enable_pnp_card();
-
- tmp_d.conf_base = d.port[3];
-
- dev->id_drq = d.drq[0] ; /* primary dma */
- dev->id_irq = (1 << d.irq[0] ) ;
- dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
-
- snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
-
- pcmattach(dev);
-
-}
-
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
+ * Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ * Vibra16X: 0xf0008c0e
*/
- if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ if (vend_id == 0xf0008c0e)
+ s = "Vibra16X" ;
+ else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
+ else if (vend_id == 0x10019305)
+ s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- pcm_info[dev->id_unit] = tmp_d;
+ pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
+ /*
+ * XXX please add here the vend_id for other vibra16X cards...
+ * And remember, must change tmp_d, not
+ */
+ tmp_d.bd_flags |= BD_F_SB16X ;
+ }
pcmattach(dev);
}
#endif /* NPNP */
diff --git a/sys/i386/isa/snd/CARDS b/sys/i386/isa/snd/CARDS
index d0953578e8524..91ca79b682297 100644
--- a/sys/i386/isa/snd/CARDS
+++ b/sys/i386/isa/snd/CARDS
@@ -39,12 +39,20 @@ CS4232: PnP id 0x3242630e
and working in playback mode in MSS emulation.
Also have reports from some user that it works ok.
-OPTi931: PnP id 0x3109143e
+--------------------------
+CHIPSET:
+ OPTi931: PnP id 0x3109143e
- http://www.opti.com/ opti931_21.pdf
+MANUFACTURER:
+ ExpertColor MED931 (europe)
+DATA SHEETS:
+ http://www.opti.com/ opti931_21.pdf
+
+PNP CONFIG:
pnp 1 1 os enable port0 0x534 port2 0x220 port3 0xe0d irq0 10 drq0 1 drq1 6
+COMMENTS:
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.
@@ -61,21 +69,45 @@ OPTi931: PnP id 0x3109143e
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.
+ misses interrupts, or generates spurious ones, or generates
+ interrupts before setting the status bits in the registers.
+ Spurious ints are not problematic since they can be easily
+ ignored, but missing interrupts are a nightmare to handle...
+ The only way to have this card work semi-reliably is to use
+ auto-dma mode (which is the default mode of operation in the
+ driver).
+ In any case, these cards are very cheap and widely available and
+ are a reasonable solution if you cannot find some other decent WSS
+ device.
+
+-------------------------------
+CHIPSET:
+ SB16: PnP id 0x__008c0e
+ Vibra16X: PnP id 0xf0008c0e
-SB16 PnP: PnP id 0x__008c0e
+MANUFACTURER:
+ CreativeLabs
- http://www.creative.com sbhwpg.pdf or SBHWPG.EXE
+DATA SHEETS:
+ http://www.creative.com sbhwpg.pdf or SBHWPG.EXE
+PNP CONFIG:
pnp 1 0 os enable port0 0x220 irq0 5 drq0 1 drq1 5
- There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
- 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.
+COMMENTS:
+ SB16 really refers to a large number of different cards, starting
+ from the original, non-PnP SB16, to more modern cards (SB16PnP,
+ Vibra16C) and other including Wavetable Synthesis (AWE32, AWE64,
+ etc.). All these cards have different 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.
+
+ BEWARE -- all recent (1998 ?) SB16 use a new codec, Vibra16X,
+ which is sufficiently different from the old one to not work with
+ this driver in capture mode or in full duplex. Documentation is
+ not available from Creative. I suggest to stay away from these
+ cards (and from SB16 in general).
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
@@ -84,7 +116,7 @@ SB16 PnP: PnP id 0x__008c0e
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
+ work, although on my Vibra16C 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
@@ -93,67 +125,111 @@ SB16 PnP: PnP id 0x__008c0e
Some docs on how to use this card with the voxware driver
are at http://multiverse.com/~rhh/awedrv
- NOTE: 980122 I have had a couple of reports from people using
- the Vibra16X (and clones, e.g. based on the Avance Logic
- chipsets) which appears to use two 8-bit DMA channels. It
- might be possible that new SB16 are able to do true full duplex!
- The driver _does not_ support these boards although the fix
- should be relatively easy.
+--------------------------
+CHIPSET:
+ ALS100, ALS110, ...
+ Avance Asound 100: PnP id 0x01009305
-ASound:
- Avance Logic makes SB clones, and apparently documentation
- is available at
+MANUFACTURER:
+ Realtek (also Avance Asound and possibly other names)
+
- http://www.realtek.com.tw/cp/cp.html
+DOCUMENTATION:
+
+ http://www.realtek.com.tw/cp/cp.html
+ but not very useful or detailed.
- these card should be recognised as SB16 clones.
+COMMENTS:
+ These card should be recognised as SB16 clones. Some of them
+ emulate the Vibra16X, so the comments above apply.
-Yamaha SA2/SA3
- http://www.yamaha.com ? YM711.pdf
- (this is a huge file containing a few pages scanned and converted
- to pdf. Not very useful or detailed.)
+------------------------------
+CHIPSET:
+ Yamaha SA2/SA3 . Both PnP and non-PnP versions.
+ OPL3-SA2 Sound Chip: PnP id 0x2000a865
+ OPL/SA3 : PnP id 0x3000a865
- pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
+MANUFACTURER:
+ no-name cards, and also used in various laptops, e.g. Toshiba
+ Libretto and others. I
+
+DATA SHEETS:
+ http://www.yamaha.com ? YM711.pdf
+
+ This is a huge file containing a few pages scanned and converted
+ to pdf. Not very detailed. Luckily, this chipset appears to do a
+ good (i.e. bug-free) emulation of the WSS, so it is fully
+ supported by the driver.
+
+ pnp 1 0 os enable port0 0x220 port1 0x530 port2 0x388 port3 0x370 irq0 5 drq0 1 drq1 0
this card emulates a WSS or SB. Have reports that it works, although
it has incomplete mixer support (Yamaha defines an additional set
of registers to control some mixer functions, such as the master
- output volume). Currently we set the master volume to 100%.
- Driver reported to work also on Toshiba DynaBook Portege 300CT
- with OPL3SA-3(non-PNP)
+ output volume -- this is at 0x370 or 0x310). Currently we set
+ the master volume to 100% for the PnP card. Will put in code to do the
+ same for the non PnP card as soon as I find out how to tell
+ the Yamaha from other cards.
+ Driver reported to work also on
+ Toshiba DynaBook Portege 300CT with OPL3SA-3(non-PNP), and on the
+ Libretto50CT (has the non-PnP device).
-Ensoniq Soundscape VIVO
+------------------------
+CHIPSET:
+ ENSONIQ SoundscapeVIVO ENS4081: PnP id 0x8140d315
+MANUFACTURER:
+ Ensoniq Soundscape VIVO
+PNP CONFIG:
pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
+COMMENTS:
this card emulates a WSS or SB. Have reports that it works.
-GusPnP: PnP id 0x0100561e
+-------------------------
+CHIPSET:
+ AMD...
+
+MANUFACTURER:
+ GusPnP: PnP id 0x0100561e
+PNP CONFIG:
pnp 1 0 os enable port0 0x220 port1 0x320 port2 0x32c irq0 5 drq0 7 drq1 5
+COMMENTS:
It works in U8 and S16 mode, ulaw support still not working.
+ The Gus has been discontinued, so support for this card is only
+ there for historical reasons (the Gus used to be the only card
+ well supported in full duplex mode).
-NOT WORKING YET:
+---- THE FOLLOWING CARDS ARE NOT FULLY SUPPORTED: ----
-OPTI925: PnP id 0x2509143e
+CHIPSET:
+ OPTI925: PnP id 0x2509143e
+COMMENTS:
there is code to recognize it as a SB clone. I have reports that
it probes ok, but not sure if it works.
-OPTI924: PnP
+-------------------------
+CHIPSET:
+ OPTI924: PnP
+COMMENT:
I have this card but it is still unsupported.
-OPTI930:
+-------------------------
+CHIPSET:
+ OPTI930:
should work as an MSS clone, but support for it is not implemented
yet.
-ESS1868
-ESS688
+CHIPSET:
+ ESS1868
+ ESS688
http://www.esstech.com
@@ -168,12 +244,13 @@ ESS688
(the ESS688 is used on many notebooks. It is probably able to do 8
and 16-bit, half duplex).
-PCI cards:
+-------------------------
+CHIPSET:
+ various PCI cards from Ensoniq, OPTI, CreativeLabs.
- 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.
+ This code _cannot_ work on these cards as it is now, since I
+ don't think they can use the ISA DMA controller. As there are
+ no data sheets available for these PCI cards, none of them is
+ supported at the moment.
+--------------------------------------------------------------------
diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README
index 256ae249d846e..289f0442ad2ec 100644
--- a/sys/i386/isa/snd/README
+++ b/sys/i386/isa/snd/README
@@ -13,15 +13,18 @@ of the Voxware ioctl() audio calls, so that many applications --
even commercial ones -- will run unmodified with this driver. On
the other hand, at the moment this driver does not support /dev/midi
and /dev/synth, or some ioctl() used in xquake. Do not expect
-/dev/synth to be supported before summer'98.
+/dev/synth to be supported anytime soon.
I also have implemented a new software interface with an independent
set of ioctl(), to support some functions which were not easy to
express with the existing software interface (e.g. full duplex on
-the SB16). To make an effective use of the new functionalities you
-need to recompile applications by replacing the audio module(s).
+old SB16 cards). To make an effective use of the new functionalities
+you need to recompile applications by replacing the audio module(s).
+Such modified driver modules are present in the misc/ directory
+for several applications.
-Updated versions of this code will available at the following URL:
+This file gives quick information on how to install the driver.
+Updated versions of this code will be available at the following URL:
http://www.iet.unipi.it/~luigi/FreeBSD.html
@@ -49,10 +52,18 @@ APPLICATIONS:
- xanim
- various mpeg players (mpg123, amp, ...);
+ WITH SPECIAL DRIVER MODULE (supplied)
+ - speak_freely, full duplex (requires removing the definition of
+ HALF_DUPLEX in the Makefile);
+ - the realaudio player (3.0, dynamically linked);
+ - vat, full duplex (the version in ports is already modified);
+ - timidity, a software midi-to-pcm converter;
+
NOT WORKING
- xquake (we do not support mmapped buffers yet);
- ---INSTALLATION---
+
+ ---INSTALLATION---
* add the following lines to your kernel configuration file:
@@ -108,14 +119,14 @@ APPLICATIONS:
"flags 0x13" specifies that you have a dual dma board with
channel 3 as secondary DMA channel.
- * build the kernel using the following steps
+ * build the kernel using the standard steps
config MYKERNEL
cd /sys/compile/MYKERNEL
make depend
make
- * PnP support:
+ * note for PnP cards:
For PnP cards, only the line for "pcm0" is needed (the code
will allocate entries for more cards if found), but IT MUST
@@ -145,8 +156,8 @@ APPLICATIONS:
WHAT IF THIS DRIVER DOES NOT WORK:
-If the driver does not work with your hardware, I am willing to
-help but I need the following:
+If the driver does not work with your hardware, I might to help
+but I need the following information:
- relevant lines in your config file;
- dmesg output
@@ -200,8 +211,8 @@ this driver. In particular I would like to thank:
* Jim Lowe for his suggestion on the block-mode select;
* Allison Mankin and Brad Karp at ISI-East for supplying a GUS PnP
which allowed me to support this card;
- * Eric J. Schwertfeger for donating sn ES1868 card for writing the
- driver.
+ * Eric J. Schwertfeger for donating an ES1868 card for writing the
+ driver (which i haven't done yet...).
* and many people who had the patience to try the driver
on their cards and report success/fauilure and useful
information.
diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c
index 095d83f0bccf9..326c5863f01ad 100644
--- a/sys/i386/isa/snd/ad1848.c
+++ b/sys/i386/isa/snd/ad1848.c
@@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
- * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
+ * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
+ if (1) { /* machine-specific code for the Toshiba Libretto */
+ u_char r6, r9;
+ outb( 0x370, 6 /* dma config */ );
+ outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
+ r6 = inb( 0x371 /* read */ );
+ outb( 0x370, 0xa /* version */ );
+ r9 = inb( 0x371 /* read */ );
+ DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
+ /*
+ * yamaha - set volume to max
+ */
+ outb( 0x370, 7 /* volume left */ );
+ outb( 0x371, 0 /* max level */ );
+ outb( 0x370, 8 /* volume right */ );
+ outb( 0x371, 0 /* max level */ );
+ }
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
+ if (d->bd_id == MD_YM0020) {
+ DDB(printf("setting up yamaha registers\n"));
+ outb(0x370, 6 /* dma config */ ) ;
+ if (FULL_DUPLEX(d))
+ outb(0x371, 0xa9 ); /* use both dma chans */
+ else
+ outb(0x371, 0x8b ); /* use low dma chan */
+ }
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
- d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
+ d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
+ /*
+ * the nice OPTi931 sets the IRQ line before setting the bits in
+ * mc11. So, on some occasions I have to retry (max 10 times).
+ */
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
- printf("one more try...\n");
- goto again;
+ DEB(printf("one more try...\n");)
+ if (--loops)
+ goto again;
+ else
+ DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
- if (loops==10) {
+ if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
- }
return;
}
@@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
- if (--loops) goto again;
+ if (--loops)
+ goto again;
DEB(printf("xxx too many loops\n");)
}
@@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
- AD_WAIT_INIT(d, 100);
+ AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
+#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
-
+#endif
/*
* Set the left channel
*/
@@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
+
+ case MD_YM0020:
+ /* set master volume to max */
+ DDB(printf("set yamaha master volume to max"); )
+ outb(0x370, 7) ;
+ outb(0x371, 0) ;
+ outb(0x370, 8) ;
+ outb(0x371, 0) ;
+ break;
+
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
- if (d->bd_id == MD_OPTI931) {
+ if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
+ DDB(printf("... try to identify the yamaha\n") ;)
+ tmp = inb(0x370) ;
+ outb(0x370, 6 /* dma config */ ) ;
+ if (inb(0x370) != 6 ) /* not a yamaha... restore. */
+ outb(0x370, tmp ) ;
+ else
+ d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
-
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
- else if ( id == 0x3600630e )
+ else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
- else if ( id == 0x0000a865)
+ else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
- if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
+ if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
+ vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
- case 0x0000a865: /* Yamaha YMF719 */
+ case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
+ case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
- if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
+ if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
- * hardware controlled by both the WSS and the SB port...
+ * hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
- opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
+ opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
- opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
+ opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
+static char *opti925_probe(u_long csn, u_long vend_id);
+static void opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device opti925 = {
+ "opti925",
+ opti925_probe,
+ opti925_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, opti925);
+
+static char *
+opti925_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x2509143e) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an OPTi925, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "OPTi925" ;
+ }
+ return NULL ;
+}
+
+static void
+opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int the_irq = 0 ;
+
+ tmp_d = mss_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ the_irq = d.irq[0];
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 3 );
+
+ read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 2 );
+
+ read_pnp_parms ( &d , 1 ) ;
+ d.irq[0] = the_irq ;
+ dev->id_iobase = d.port[1];
+ tmp_d.alt_base = d.port[0];
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ tmp_d.conf_base = d.port[3];
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+}
+
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c
index a19ccc48500e5..c69435c2cd5d4 100644
--- a/sys/i386/isa/snd/dmabuf.c
+++ b/sys/i386/isa/snd/dmabuf.c
@@ -184,7 +184,9 @@ dsp_wrintr(snddev_info *d)
/* 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));
- d->callback(d, SND_CB_WR | SND_CB_STOP );
+ DEB(printf("wrintr: dl %d -> %d\n", b->dl, l);)
+ if (b->dl != 0)
+ d->callback(d, SND_CB_WR | SND_CB_STOP );
/*
* at high speed, it might well be that the count
* changes in the meantime. So we try to update b->rl
@@ -270,10 +272,10 @@ dsp_write_body(snddev_info *d, struct uio *buf)
else
timeout = 1 ;
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
- if (ret == EINTR || ret == ERESTART)
+ if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
- if (ret == EINTR)
+ if (ret == EINTR || ret == ERESTART)
break ;
continue;
}
@@ -457,10 +459,12 @@ dsp_rdintr(snddev_info *d)
( FULL_DUPLEX(d) || (d->flags & SND_F_WRITING) == 0 ) ) {
int l = min(b->fl - 0x100, d->rec_blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
+ DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
if (l != b->dl) {
/* for any reason, size has changed. Stop and restart */
+ if (b->dl > 0 )
+ d->callback(d, SND_CB_RD | SND_CB_STOP );
b->dl = l ;
- d->callback(d, SND_CB_RD | SND_CB_STOP );
d->callback(d, SND_CB_RD | SND_CB_START );
}
} else {
@@ -553,10 +557,10 @@ dsp_read_body(snddev_info *d, struct uio *buf)
else
timeout = 1; /* maybe data will be ready earlier */
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
- if (ret == EINTR || ret == ERESTART)
+ if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
- if (ret == EINTR)
+ if (ret == EINTR || ret == ERESTART)
break ;
continue;
}
@@ -593,7 +597,7 @@ dsp_read_body(snddev_info *d, struct uio *buf)
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_READING ;
if (d->flags & SND_F_ABORTING) {
- d->flags |= ~SND_F_ABORTING;
+ d->flags &= ~SND_F_ABORTING; /* XXX */
splx(s);
dsp_rdabort(d, 1 /* restart */);
/* XXX return EINTR ? */
@@ -761,7 +765,8 @@ snd_flush(snddev_info *d)
return -1 ;
}
if ( ret && --count == 0) {
- printf("timeout flushing dbuf_out.chan, cnt 0x%x flags 0x%08lx\n",
+ printf("timeout flushing dbuf_out, chan %d cnt 0x%x flags 0x%08lx\n",
+ b->chan,
b->rl, d->flags);
break;
}
diff --git a/sys/i386/isa/snd/mss.h b/sys/i386/isa/snd/mss.h
index 994f323c2bef5..54efc3cc21950 100644
--- a/sys/i386/isa/snd/mss.h
+++ b/sys/i386/isa/snd/mss.h
@@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
- * AD1848/CS423x clones, and one for the OPTI931. As more WSS
+ * AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c
index 575980cc8821b..349a7e2102138 100644
--- a/sys/i386/isa/snd/sb_dsp.c
+++ b/sys/i386/isa/snd/sb_dsp.c
@@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
- * Copyright 1997 Luigi Rizzo.
+ * Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d);
static void sb_mixer_reset(snddev_info *d);
u_int sb_get_byte(int io_base);
+int ess_write(int io_base, u_char reg, int val);
+int ess_read(int io_base, u_char reg);
/*
* Then put here the descriptors for the various boards supported
@@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* here are the main routines from the switches.
*/
+/*
+ * Unlike MSS, the sb only supports a single open (does not mean
+ * that only a single process is using it, since it can fork
+ * afterwards, or pass the descriptor to another process).
+ *
+ */
static int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
- if ( (flags & FREAD) == 0)
+ /*
+ * since the SB is not simmetric, I use the open mode to select
+ * which channel should be privileged, and disable I/O in the
+ * other direction.
+ * In case the board is opened RW, we don't have enough
+ * information on what to do. Temporarily, privilege the
+ * playback channel, which is used more often, and set the other
+ * one to U8.
+ */
+ if ( (flags & FREAD) == 0) /* opened write only */
d->rec_fmt = 0 ;
- if ( (flags & FWRITE) == 0)
+ else if ( (flags & FWRITE) == 0) /* opened read only */
d->play_fmt = 0 ;
+ else /* opened read/write */
+ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -275,7 +294,10 @@ sb_intr(int unit)
* SB < 4.0 is half duplex and has only 1 bit for int source,
* so we fake it. SB 4.x (SB16) has the int source in a separate
* register.
+ * The Vibra16X has separate flags for 8 and 16 bit transfers, but
+ * I have no idea how to tell capture from playback interrupts...
*/
+#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)
again:
if (d->bd_flags & BD_F_SB16) {
c = sb_getmixer(io_base, IRQ_STAT);
@@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
- if (d->dbuf_out.chan < 4)
+ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
- if (d->dbuf_in.chan < 4)
+ if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
- if (d->dbuf_out.chan >= 4)
+ if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
- if (d->dbuf_in.chan >= 4)
+ if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
- DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
+ DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n",
+ d->flags, reason, c));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
dsp_wrintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
- if (d->bd_flags & BD_F_SB16)
+ if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
+ /*
+ * set the speed
+ */
dsp_speed(d);
+ /*
+ * set the desired DMA blocksize (influences select behaviour)
+ */
snd_set_blocksize(d);
+ /*
+ * since native mulaw is not present, emulate it.
+ */
if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- if (d->bd_flags & BD_F_SB16) {
+ /*
+ * there are too many flavours of SB for my taste... here i try to do
+ * the proper initialization for each one.
+ */
+ if (PLAIN_SB16(d->bd_flags)) {
u_char c, c1 ;
- /* the SB16 can do full duplex using one 16-bit channel
+ /* the original SB16 (non-PnP, or PnP, or Vibra16C)
+ * 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:
+ * I DON'T do this for the Vibra16X because I have no idea
+ * of what needs to be done there...
+ *
+ * I 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.
@@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
- /* check for possible config errors. */
+ /* check for possible config errors.
+ * This cannot happen at open time since even in
+ * case of opening rw we privilege the play
+ * channel.
+ */
if (d->rec_fmt == d->play_fmt) {
DDB(printf("sorry, read DMA channel unavailable\n"));
}
@@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
+ } else if (d->bd_flags & BD_F_ESS) {
+ u_char c ;
+ if (d->play_fmt == 0) {
+ /* initialize for record */
+ static u_char cmd[] = {
+ 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
+ };
+ ess_write(d->io_base, 0xb8, 0x0e);
+ c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
+ if (d->flags & SND_F_STEREO)
+ c++ ;
+ ess_write(d->io_base, 0xa8, c);
+ ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
+ /*
+ * set format in b6, b7
+ */
+ } else {
+ /* initialize for play */
+ static u_char cmd[] = {
+ 0x80,0x51,0xd0,0x00,0x71,0xf4,
+ 0x80,0x51,0x98,0x00,0x71,0xbc
+ };
+ }
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
+
+ if (d->bd_flags & BD_F_SB16X) {
+ /* just a guess: on the Vibra16X, the first
+ * op started takes the first dma channel,
+ * the second one takes the next...
+ * The default is to be ready for play.
+ */
+ int swap = 0 ;
+ DEB(printf("start %s -- now dma %d:%d\n",
+ rd ? "rd" : "wr",
+ d->dbuf_out.chan, d->dbuf_in.chan););
+ /* swap only if both channels are idle
+ * play: dl=0, since there is no pause;
+ * rec: rl=0
+ */
+ if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
+ /* must swap channels, but also save dl */
+ int c = d->dbuf_in.chan ;
+ int dl = d->dbuf_in.dl ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ d->dbuf_in.dl = dl ;
+ printf("swapped -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
+
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
- if ( b->chan > 4 ) {
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
- if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
+ if ( b->chan > 4
+ || (rd && d->rec_fmt == AFMT_S16_LE)
+ || (!rd && d->play_fmt == AFMT_S16_LE)
+ )
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
+ if (d->bd_flags & BD_F_SB16X) {
+ /* restore possible swapped channels.
+ * The default is to be ready for play.
+ * XXX right now, it kills all input on overflow
+ */
+ if ( rd && d->dbuf_out.dl == 0 ) {
+ /* must swap channels ? */
+ int c = d->dbuf_in.chan ;
+ d->dbuf_in.chan = d->dbuf_out.chan;
+ d->dbuf_out.chan = c ;
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ printf("restored -- now dma %d:%d\n",
+ d->dbuf_out.chan, d->dbuf_in.chan);
+ }
+ }
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
+ /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
+ *
+ * sb_cmd write a single byte to the CMD port.
+ * sb_cmd2 write a CMD + 1 byte arg
+ * sb_cmd3 write a CMD + 2 byte arg
+ * sb_get_byte returns a single byte from the DSP data port
+ *
+ * ess_write is actually sb_cmd2
+ * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(io_base + 5);
+ val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
+int
+ess_write(int io_base, u_char reg, int val)
+{
+ return sb_cmd2(io_base, reg, val);
+}
+
+int
+ess_read(int io_base, u_char reg)
+{
+ if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
+ return 0xffff ;
+ return sb_get_byte(io_base);
+}
/*
@@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
- RANGE (speed, 4000, 48000);
+ RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
- t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
+ t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
- t = 128 - (397700 + speed / 2) / speed ;
+ t = 128 - t ;
}
- sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
+ ess_write(d->io_base, 0xa1, t); /* set time constant */
+ d->play_speed = d->rec_speed = speed ;
+ speed = (speed * 9 ) / 20 ;
+ t = 256-7160000/(speed*82);
+ ess_write(d->io_base,0xa2,t);
return speed ;
}
@@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
-static char *opti925_probe(u_long csn, u_long vend_id);
-static void opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev);
-
-static struct pnp_device opti925 = {
- "opti925",
- opti925_probe,
- opti925_attach,
- &nsnd, /* use this for all sound cards */
- &tty_imask /* imask */
-};
-DATA_SET (pnpdevice_set, opti925);
-
-static char *
-opti925_probe(u_long csn, u_long vend_id)
-{
- if (vend_id == 0x2509143e) {
- struct pnp_cinfo d ;
- read_pnp_parms ( &d , 1 ) ;
- if (d.enable == 0) {
- printf("This is an OPTi925, but LDN 1 is disabled\n");
- return NULL;
- }
- return "OPTi925" ;
- }
- return NULL ;
-}
-
-static void
-opti925_attach(u_long csn, u_long vend_id, char *name,
- struct isa_device *dev)
-{
- struct pnp_cinfo d ;
- snddev_info tmp_d ; /* patched copy of the basic snddev_info */
- int the_irq = 0 ;
-
- tmp_d = sb_op_desc;
- snddev_last_probed = &tmp_d;
-
- read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
- the_irq = d.irq[0];
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 3 );
-
- read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
- d.port[0] = 0 ;
- d.enable = 0 ;
- write_pnp_parms ( &d , 2 );
-
- read_pnp_parms ( &d , 1 ) ;
- d.irq[0] = the_irq ;
- dev->id_iobase = d.port[0];
- write_pnp_parms ( &d , 1 );
- enable_pnp_card();
-
- tmp_d.conf_base = d.port[3];
-
- dev->id_drq = d.drq[0] ; /* primary dma */
- dev->id_irq = (1 << d.irq[0] ) ;
- dev->id_intr = pcmintr ;
- dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
-
- snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
-
- pcmattach(dev);
-
-}
-
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
+ * Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ * Vibra16X: 0xf0008c0e
*/
- if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ if (vend_id == 0xf0008c0e)
+ s = "Vibra16X" ;
+ else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
+ else if (vend_id == 0x10019305)
+ s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
- pcm_info[dev->id_unit] = tmp_d;
+ pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
+ /*
+ * XXX please add here the vend_id for other vibra16X cards...
+ * And remember, must change tmp_d, not
+ */
+ tmp_d.bd_flags |= BD_F_SB16X ;
+ }
pcmattach(dev);
}
#endif /* NPNP */
diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h
index 0bd42102d715b..92ec966107911 100644
--- a/sys/i386/isa/snd/sbcard.h
+++ b/sys/i386/isa/snd/sbcard.h
@@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+#define SB_MIX_ADDR 0x4
+#define SB_MIX_DATA 0x5
#if 0
-#define MIXER_ADDR (io_base + 0x4)
-#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
-#define BD_F_NOREC 0x0200 /* recording not supported on this board */
+#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
+/*
+ * on some SB16 cards, at times I swap DMA channels. Remember this
+ * so that they can be restored later.
+ */
+#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*
diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c
index 51ea2f761b261..ef724cd5121b3 100644
--- a/sys/i386/isa/snd/sound.c
+++ b/sys/i386/isa/snd/sound.c
@@ -57,10 +57,10 @@
#include <sys/devfsext.h>
#endif /* DEVFS */
-#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
-extern struct isa_driver pcmdriver;
+#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
+extern struct isa_driver pcmdriver ;
#define SNDSTAT_BUF_SIZE 4000
static char status_buf[SNDSTAT_BUF_SIZE] ;
static int status_len = 0 ;
@@ -92,6 +92,75 @@ snddev_info synth_info[NPCM_MAX] ;
u_long nsnd = NPCM ; /* total number of sound devices */
/*
+ * Hooks for APM support, but code not operational yet.
+ */
+
+#include "apm.h"
+#include <i386/include/apm_bios.h>
+#if NAPM > 0
+
+static int
+sound_suspend(void *arg)
+{
+ /*
+ * I think i can safely do nothing here and
+ * reserve all the work for wakeup time
+ */
+ printf("Called APM sound suspend hook for unit %d\n", (int)arg);
+ return 0 ;
+}
+
+static int
+sound_resume(void *arg)
+{
+ snddev_info *d = NULL ;
+
+ d = &pcm_info[(int)arg] ;
+ /*
+ * reinitialize card registers.
+ * Flush buffers and reinitialize DMA channels.
+ * If a write was pending, pretend it is done
+ * (and issue any wakeup we need).
+ * If a read is pending, restart it.
+ */
+ if (d->bd_id == MD_YM0020) {
+ DDB(printf("setting up yamaha registers\n"));
+ outb(0x370, 6 /* dma config */ ) ;
+ if (FULL_DUPLEX(d))
+ outb(0x371, 0xa9 ); /* use both dma chans */
+ else
+ outb(0x371, 0x8b ); /* use low dma chan */
+ }
+ printf("Called APM sound resume hook for unit %d\n", (int)arg);
+ return 0 ;
+}
+
+static void
+init_sound_apm(int unit)
+{
+ struct apmhook *ap;
+
+ ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT);
+ bzero(ap, sizeof *ap);
+
+ ap->ah_fun = sound_resume;
+ ap->ah_arg = (void *)unit;
+ ap->ah_name = "pcm resume handler";
+ ap->ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME, ap);
+
+ ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT);
+ bzero(ap, sizeof *ap);
+
+ ap->ah_fun = sound_suspend;
+ ap->ah_arg = (void *)unit;
+ ap->ah_name = "pcm suspend handler";
+ ap->ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_SUSPEND, ap);
+}
+#endif /* NAPM */
+
+/*
* the probe routine can only return an int to the upper layer. Hence,
* it leaves the pointer to the last successfully
* probed device descriptor in snddev_last_probed
@@ -121,7 +190,7 @@ static snddev_info *sb_devs[] = { /* all SB clones */
NULL,
} ;
-static snddev_info *mss_devs[] = { /* all WSS clones */
+static snddev_info *mss_devs[] = { /* all MSS clones */
&mss_op_desc,
NULL,
} ;
@@ -194,7 +263,10 @@ pcmattach(struct isa_device * dev)
d->dbuf_in.chan = dev->id_flags & DV_F_DRQ_MASK ;
else
d->dbuf_in.chan = d->dbuf_out.chan ;
- /* XXX should also set bd_id from flags ? */
+#if 1 /* does this cause trouble with PnP cards ? */
+ if (d->bd_id == 0)
+ d->bd_id = (dev->id_flags & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT ;
+#endif
d->status_ptr = 0;
/*
@@ -224,7 +296,11 @@ pcmattach(struct isa_device * dev)
cdevsw_add(&isadev, &snd_cdevsw, NULL);
#ifdef DEVFS
-#define GID_SND GID_GAMES
+#ifndef GID_GAMES
+#define GID_SND UID_ROOT
+#else
+#define GID_SND GID_GAMES /* i am not really sure this is a good one. */
+#endif
#define UID_SND UID_ROOT
#define PERM_SND 0660
/*
@@ -294,6 +370,9 @@ pcmattach(struct isa_device * dev)
#endif
snddev_last_probed = NULL ;
+#if NAPM > 0
+ init_sound_apm(dev->id_unit);
+#endif
return stat ;
}
@@ -738,7 +817,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
snd_chan_param *p = (snd_chan_param *)arg;
d->play_speed = p->play_rate;
d->rec_speed = p->play_rate; /* XXX one speed allowed */
- if (p->play_format & SND_F_STEREO)
+ if (p->play_format & AFMT_STEREO)
d->flags |= SND_F_STEREO ;
else
d->flags &= ~SND_F_STEREO ;
@@ -805,7 +884,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
break;
case FIOASYNC: /*set/clear async i/o */
- printf("FIOASYNC\n");
+ DEB( printf("FIOASYNC\n") ; )
break;
case SNDCTL_DSP_NONBLOCK :
@@ -1055,7 +1134,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
* really sndpoll. Second arg for poll is not "rw" but "events"
*/
int
-sndselect(dev_t i_dev, int rw, struct proc *p)
+sndselect(dev_t i_dev, int rw, struct proc * p)
{
int dev, unit, c = 1 /* default: success */ ;
snddev_info *d ;
@@ -1220,7 +1299,7 @@ init_status(snddev_info *d)
if (status_len != 0) /* only do init once */
return ;
sprintf(status_buf,
- "FreeBSD Audio Driver (980215) " __DATE__ " " __TIME__ "\n"
+ "FreeBSD Audio Driver (981002) " __DATE__ " " __TIME__ "\n"
"Installed devices:\n");
for (i = 0; i < NPCM_MAX; i++) {
diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h
index fbcd3584c8ee9..eea64c639f4c2 100644
--- a/sys/i386/isa/snd/sound.h
+++ b/sys/i386/isa/snd/sound.h
@@ -65,7 +65,10 @@
#include <i386/isa/isa_device.h>
#include <machine/clock.h> /* for DELAY */
-/* To minimize changes with the code in 2.2.X */
+/*
+ * the following assumes that FreeBSD 3.X uses poll(2) instead of select(2).
+ * This change dates to late 1997.
+ */
#include <sys/poll.h>
#define d_select_t d_poll_t
@@ -158,7 +161,7 @@ struct _snddev_info {
*/
int io_base ; /* primary I/O address for the board */
- int alt_base ; /* some codecs are accessible as SB+WSS... */
+ int alt_base ; /* some codecs are accessible as SB+MSS... */
int conf_base ; /* and the opti931 also has a config space */
int mix_base ; /* base for the mixer... */
int midi_base ; /* base for the midi */