This updates Luigi's sound code to the basic code in snd971023...

changes:
    o rip the old select from his distribution to prevent extra pollution
    o the code now uses audio dma, helps reduce clicks
    o improved card support, should work in full duplex on sb16 cards
    o add better voxware ioctl support pointed out by Joao Carlos Mendes
       Luis <jonny@coppe.ufrj.br>
    o remove an unused file that I included for more complete history
    o and MANY other changes

I have personally tested this code with a CS4237 based card and an AWE32
(non-PnP).  Both cards worked fine in 8bit and 16bit mode.
This commit is contained in:
John-Mark Gurney 1997-10-31 12:24:28 +00:00
parent 95b6073cd5
commit faac9650a5
19 changed files with 2171 additions and 2416 deletions

View file

@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
* Modified by Luigi Rizzo (luigi@iet.unipi.it)
*
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248
*
* CS4231, used in the GUS MAX and some other cards;
* AD1845, CS4231A (CS4231-like)
* CS4232 (CS4231+SB and MPU, PnP)
* CS4236 (upgrade of the CS4232, has a better mixer)
* OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
*
* The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
* but we do not use these modes.
*/
} ;
/*
* this is the probe routine. Note, it is not necessary to
* mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
dev->id_alive = 8 ; /* number of io ports */
/* should be already set but just in case... */
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
if (d->dma1 != d->dma2)
d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
ask_init(d);
ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
* With the exception of INIT, all other callbacks are invoked
* with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
u_long s;
u_char m;
int retry, wr, cnt;
@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
/* fallthrough */
case SND_CB_RESTART :
s = spltty();
cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
/* disable DMA by clearing count registers. */
#if 1
/*
* try to disable DMA by clearing count registers. Not sure it
* is needed, and it might cause false interrupts when the
* DMA is re-enabled later.
*/
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
splx(s);
break;
#endif
}
return 0 ;
}
@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
* The correct way to operate for a device with multiple internal
* interrupt sources is to loop on the status register and ack
* interrupts until all interrupts are served and none are reported. At
* this point the IRQ line to the ISA IRQ controller should go low
* and be raised at the next interrupt.
*
* Since the ISA IRQ controller is sent EOI _before_ passing control
* to the isr, it might happen that we serve an interrupt early, in
* which case the status register at the next interrupt should just
* say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
u_char i11, c;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
u_char c, served = 0;
int i;
i11 = ad_read(d, 11);
reason = inb(io_Status(d));
DEB(printf("mss_intr\n"));
ad_read(d, 11); /* fake read of status bits */
if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
return;
/* get exact reason */
c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
dsp_wrintr(d);
c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
dsp_rdintr(d);
/* XXX check this on the 4236... */
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
/*
* loop until there are interrupts, but no more than 10 times.
*/
for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
/* get exact reason for full-duplex boards */
c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
c &= ~served ;
if ( d->dbuf_out.dl && (c & 0x10) ) {
served |= 0x10 ;
dsp_wrintr(d);
}
if ( d->dbuf_in.dl && (c & 0x20) ) {
served |= 0x20 ;
dsp_rdintr(d);
}
/*
* now ack the interrupt
*/
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
}
}
/*
* the opti931 seems to miss interrupts when working in full
* duplex. god know why...
* duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
int w_miss=0, r_miss=0;
static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
printf("Warning CD interrupt\n");
printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
printf("Warning MPU interrupt\n");
printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
if (w_miss || r_miss ) {
misses++;
if ( (misses & 0xff) == 1 )
printf("opti931: %d missed irq (now %s%s)\n",
misses, w_miss?"play ":"",
r_miss?"capture ":"");
}
if (loops==10)
if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
if (w_miss) mc11 |= 4 ;
if (r_miss) mc11 |= 8 ;
if (mc11==0)
return;
}
return;
}
if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
r_miss = 0 ;
}
else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
r_miss = 1 ;
if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
if (isa_dmastatus1(d->dma1) != 0)
printf("opti931_intr: wr dma %d\n",
isa_dmastatus1(d->dma1));
if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
w_miss = 0 ;
}
else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
w_miss = 1 ;
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
static void
gus_write(int io_base, u_char reg, u_char value)
{
outb(io_base + 3, reg);
outb(io_base + 5, value);
}
static void
gus_writew(int io_base, u_char reg, u_short value)
{
outb(io_base + 3, reg);
outb(io_base + 4, value);
}
static u_char
gus_read(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inb(io_base+5);
}
static u_short
gus_readw(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inw(io_base+4);
}
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
*
* fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
DDB(printf("nbits = 0 for dev %d\n", dev) );
DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
val = ad_read(d, regoffs);
old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
}
return 0; /* success */
}
@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
d->mix_rec_devs = MODE1_REC_DEVICES;
d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
/*
* some device-specific things, mostly mute the mic to
* the output mixer so as to avoid hisses. In many cases this
* is the default after reset, this code is here mostly as a
* reminder that this might be necessary on other boards.
*/
switch(d->bd_id) {
case MD_OPTI931:
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
ad_write(d, 23, 0x88);
}
}
/*
@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
*
* fixed lr970724
*/
static int
@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
DDB(printf("mss_detect error, busy still set (0x%02x)\n",
DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
return 0;
}
ad_write(d, 0, 0x45);
@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
DDB(printf("mss_detect error - step H(%x)\n", tmp1));
DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
DDB(printf("mss_detect() - step I\n"));
DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
case 0x03: /* CS4236 on Intel PR440FX motherboard */
case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
DDB(printf("mss_detect() - Detected %s\n", name));
DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
#if 0
if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
printf("warning, cannot do ulaw rec + play on the GUS\n");
r = 0 ; /* move to U8 */
}
#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
/*
* not sure if this is really needed...
*/
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
static char * cs4236_probe(u_long csn, u_long vend_id);
static void cs4236_attach(u_long csn, u_long vend_id, char *name,
static char * cs423x_probe(u_long csn, u_long vend_id);
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device cs4236 = {
"cs423x",
cs4236_probe,
cs4236_attach,
static struct pnp_device cs423x = {
"cs423x/ymh0020",
cs423x_probe,
cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, cs4236);
DATA_SET (pnpdevice_set, cs423x);
static char *
cs4236_probe(u_long csn, u_long vend_id)
cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x3742630e)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if (vend_id == 0x3642630e)
else if ( id == 0x3600630e )
s = "CS4236" ;
else if (vend_id == 0x360b630e)
s = "CS4236" ;
else if (vend_id == 0x3242630e)
else if ( id == 0x3200630e)
s = "CS4232" ;
else if ( id == 0x2000a865)
s = "Yamaha SA2";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
cs4236_attach(u_long csn, u_long vend_id, char *name,
cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
if (d.flags & DV_PNP_SBCODEC) {
printf("CS423x use sb-compatible codec\n");
dev->id_iobase = d.port[2] ;
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
tmp_d.alt_base = d.port[0] - 4;
if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
/* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
d.irq[1] = 0 ; /* only needed for the VIVO */
} else {
dev->id_iobase = d.port[2] ;
tmp_d.alt_base = d.port[0] - 4;
}
d.drq[1] = 4 ; /* disable, it is not used ... */
} else {
/* mss-compatible codec */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
} else { /* mss-compatible codec */
dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
switch (vend_id) {
case 0x3742630e: /* CS4237 */
case 0x3642630e: /* CS4236 */
case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
tmp_d.alt_base = d.port[2];
switch (vend_id & 0xff00ffff) {
case 0x2000a865: /* yamaha SA-2 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_YM0020 ;
break;
default:
case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_VIVO ;
d.irq[1] = 0 ;
break;
case 0x3700630e: /* CS4237 */
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3600630e: /* CS4236 */
tmp_d.bd_id = MD_CS4236 ;
break;
default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
dev->id_alive = 1;
pcmattach(dev);
}
@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
u_char tmp;
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
tmp_d.alt_base = d.port[0];
tmp_d.io_base = d.port[2] - 4;
tmp_d.alt_base = d.port[0]; /* 0x220 */
tmp_d.conf_base = d.port[1]; /* gus control block... */
tmp_d.bd_id = MD_GUSPNP ;
/* reset */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
/*
* unmute left & right line. Need to go in mode3, unmute,
* and back to mode 2
*/
tmp = ad_read(&tmp_d, 0x0c);
ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
/* send codec interrupts on irq1 and only use that one */
gus_write(tmp_d.conf_base, 0x5a , 0x4f );
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
strcpy(tmp_d.name, name);
pcmattach(dev);
}
#if 0
int
gus_mem_write(snddev_info *d, int addr, u_char data)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
outb(d->conf_base + 7, data);
}
u_char
gus_mem_read(snddev_info *d, int addr)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
return inb(d->conf_base + 7);
}
void
gus_mem_cfg(snddev_info *d)
{
int base;
u_char old;
u_char a, b;
printf("configuring gus memory...\n");
gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
old = gus_read(d->conf_base, 0x19);
gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
for (base = 0; base < 1024; base++) {
a=gus_mem_read(d, base*1024);
a = ~a ;
gus_mem_write(d, base*1024, a);
b=gus_mem_read(d, base*1024);
if ( b != a )
break ;
}
printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
}
#endif /* gus mem cfg... */
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */

View file

@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
#define MSS_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
* Following there is a macro ...MIXER_DEVICES which is a bitmap
* of all non-zero fields in the table.
* MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
* registers I0..I15)
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
mixer_ent mix_devices[32][2] = {
MIX_NONE(SOUND_MIXER_VOLUME),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_NONE(SOUND_MIXER_LINE1),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
/*
* entries for the opti931...
*/
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_IMIX),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */

View file

@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
* Remebber, isa probe routines are supposed to return the
* Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
dev->id_alive = 16 ; /* number of io ports */
/* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( d->dbuf_out.dl && (reason & 1) )
dsp_wrintr(d);
if ( d->dbuf_in.dl && (reason & 2) )
dsp_rdintr(d);
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
dsp_wrintr(d);
if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
dsp_rdintr(d);
/*
* the sb16 might have multiple sources etc.
*/
@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
break ;
break;
case SND_CB_START : /* called with int disabled */
sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
* We use the following algorithm:
* 1. check which direction(s) are active;
* 2. check if we should swap dma channels
* 3. check if we can do the swap.
*/
int b16 ;
int swap = 0 ;
int swap = 1 ; /* default... */
b16 = (rd) ? d->rec_fmt : d->play_fmt ;
b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
/*
* check if I have to swap dma channels. Swap if
* - !rd, dma1 <4, b16
* - !rd, dma1 >=4, !b16
* - rd, dma2 <4, b16
* - rd, dma2 >=4, !b16
*/
if (!rd) {
if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
if (rd) {
if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
swap = 0;
if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
swap = 0;
if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
swap = 0;
} else {
if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
if (d->flags & SND_F_READING || d->dbuf_in.dl)
swap = 0;
if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
swap = 0;
if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
swap = 0;
}
/*
* before swapping should make sure that there is no
* pending DMA on the other channel...
*/
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
DEB(printf("START dma chan: play %d, rec %d\n",
d->dma1, d->dma2));
}
DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
/* fallthrough */
case SND_CB_RESTART:
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
/*
* SB16 support still not completely working!!!
*
* in principle, on the SB16, I could support simultaneous
* play & rec.
* However, there is no way to ask explicitly for 8 or
* 16 bit transfer. As a consequence, if we do 8-bit,
* we need to use the 8-bit channel, and if we do 16-bit,
* we need to use the other one. The only way I find to
* do this is to swap d->dma1 and d->dma2 ...
*
*/
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
/* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
/* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if (d->bd_flags & BD_F_SB16) {
if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
cmd = DSP_CMD_DMAPAUSE_16 ;
}
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
} else {
sb_cmd(d->io_base, cmd); /* pause dma. */
/*
* This seems to have the side effect of blocking the other
* side as well so I have to re-enable it :(
*/
if ( (rd && d->dbuf_out.dl) ||
(!rd && d->dbuf_in.dl) )
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
DEB(printf("sb_reset_dsp failed\n"));
DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
* XXX check this code...
* XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (vend_id == 0x2b008c0e)
s = "SB16 Value PnP" ;
/*
* The SB16/AWE64 cards seem to differ in the fourth byte of
* The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB AWE64 PnP";
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);

View file

@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_ADC8 0x20 /* direct ADC input */
/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
/* these should be used in the SB 2.0 and 2.01 */
#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_DMAPAUSE_8 0xD0
#define DSP_CMD_DMAPAUSE_16 0xD5
#define DSP_CMD_DMAEXIT_8 0xDA
#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif

View file

@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
* Modified by Luigi Rizzo (luigi@iet.unipi.it)
*
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248
*
* CS4231, used in the GUS MAX and some other cards;
* AD1845, CS4231A (CS4231-like)
* CS4232 (CS4231+SB and MPU, PnP)
* CS4236 (upgrade of the CS4232, has a better mixer)
* OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
*
* The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
* but we do not use these modes.
*/
} ;
/*
* this is the probe routine. Note, it is not necessary to
* mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
dev->id_alive = 8 ; /* number of io ports */
/* should be already set but just in case... */
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
if (d->dma1 != d->dma2)
d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
ask_init(d);
ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
* With the exception of INIT, all other callbacks are invoked
* with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
u_long s;
u_char m;
int retry, wr, cnt;
@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
/* fallthrough */
case SND_CB_RESTART :
s = spltty();
cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
/* disable DMA by clearing count registers. */
#if 1
/*
* try to disable DMA by clearing count registers. Not sure it
* is needed, and it might cause false interrupts when the
* DMA is re-enabled later.
*/
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
splx(s);
break;
#endif
}
return 0 ;
}
@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
* The correct way to operate for a device with multiple internal
* interrupt sources is to loop on the status register and ack
* interrupts until all interrupts are served and none are reported. At
* this point the IRQ line to the ISA IRQ controller should go low
* and be raised at the next interrupt.
*
* Since the ISA IRQ controller is sent EOI _before_ passing control
* to the isr, it might happen that we serve an interrupt early, in
* which case the status register at the next interrupt should just
* say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
u_char i11, c;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
u_char c, served = 0;
int i;
i11 = ad_read(d, 11);
reason = inb(io_Status(d));
DEB(printf("mss_intr\n"));
ad_read(d, 11); /* fake read of status bits */
if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
return;
/* get exact reason */
c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
dsp_wrintr(d);
c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
dsp_rdintr(d);
/* XXX check this on the 4236... */
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
/*
* loop until there are interrupts, but no more than 10 times.
*/
for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
/* get exact reason for full-duplex boards */
c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
c &= ~served ;
if ( d->dbuf_out.dl && (c & 0x10) ) {
served |= 0x10 ;
dsp_wrintr(d);
}
if ( d->dbuf_in.dl && (c & 0x20) ) {
served |= 0x20 ;
dsp_rdintr(d);
}
/*
* now ack the interrupt
*/
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
}
}
/*
* the opti931 seems to miss interrupts when working in full
* duplex. god know why...
* duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
int w_miss=0, r_miss=0;
static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
printf("Warning CD interrupt\n");
printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
printf("Warning MPU interrupt\n");
printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
if (w_miss || r_miss ) {
misses++;
if ( (misses & 0xff) == 1 )
printf("opti931: %d missed irq (now %s%s)\n",
misses, w_miss?"play ":"",
r_miss?"capture ":"");
}
if (loops==10)
if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
if (w_miss) mc11 |= 4 ;
if (r_miss) mc11 |= 8 ;
if (mc11==0)
return;
}
return;
}
if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
r_miss = 0 ;
}
else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
r_miss = 1 ;
if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
if (isa_dmastatus1(d->dma1) != 0)
printf("opti931_intr: wr dma %d\n",
isa_dmastatus1(d->dma1));
if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
w_miss = 0 ;
}
else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
w_miss = 1 ;
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
static void
gus_write(int io_base, u_char reg, u_char value)
{
outb(io_base + 3, reg);
outb(io_base + 5, value);
}
static void
gus_writew(int io_base, u_char reg, u_short value)
{
outb(io_base + 3, reg);
outb(io_base + 4, value);
}
static u_char
gus_read(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inb(io_base+5);
}
static u_short
gus_readw(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inw(io_base+4);
}
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
*
* fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
DDB(printf("nbits = 0 for dev %d\n", dev) );
DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
val = ad_read(d, regoffs);
old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
}
return 0; /* success */
}
@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
d->mix_rec_devs = MODE1_REC_DEVICES;
d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
/*
* some device-specific things, mostly mute the mic to
* the output mixer so as to avoid hisses. In many cases this
* is the default after reset, this code is here mostly as a
* reminder that this might be necessary on other boards.
*/
switch(d->bd_id) {
case MD_OPTI931:
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
ad_write(d, 23, 0x88);
}
}
/*
@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
*
* fixed lr970724
*/
static int
@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
DDB(printf("mss_detect error, busy still set (0x%02x)\n",
DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
return 0;
}
ad_write(d, 0, 0x45);
@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
DDB(printf("mss_detect error - step H(%x)\n", tmp1));
DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
DDB(printf("mss_detect() - step I\n"));
DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
case 0x03: /* CS4236 on Intel PR440FX motherboard */
case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
DDB(printf("mss_detect() - Detected %s\n", name));
DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
#if 0
if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
printf("warning, cannot do ulaw rec + play on the GUS\n");
r = 0 ; /* move to U8 */
}
#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
/*
* not sure if this is really needed...
*/
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
static char * cs4236_probe(u_long csn, u_long vend_id);
static void cs4236_attach(u_long csn, u_long vend_id, char *name,
static char * cs423x_probe(u_long csn, u_long vend_id);
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device cs4236 = {
"cs423x",
cs4236_probe,
cs4236_attach,
static struct pnp_device cs423x = {
"cs423x/ymh0020",
cs423x_probe,
cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, cs4236);
DATA_SET (pnpdevice_set, cs423x);
static char *
cs4236_probe(u_long csn, u_long vend_id)
cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x3742630e)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if (vend_id == 0x3642630e)
else if ( id == 0x3600630e )
s = "CS4236" ;
else if (vend_id == 0x360b630e)
s = "CS4236" ;
else if (vend_id == 0x3242630e)
else if ( id == 0x3200630e)
s = "CS4232" ;
else if ( id == 0x2000a865)
s = "Yamaha SA2";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
cs4236_attach(u_long csn, u_long vend_id, char *name,
cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
if (d.flags & DV_PNP_SBCODEC) {
printf("CS423x use sb-compatible codec\n");
dev->id_iobase = d.port[2] ;
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
tmp_d.alt_base = d.port[0] - 4;
if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
/* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
d.irq[1] = 0 ; /* only needed for the VIVO */
} else {
dev->id_iobase = d.port[2] ;
tmp_d.alt_base = d.port[0] - 4;
}
d.drq[1] = 4 ; /* disable, it is not used ... */
} else {
/* mss-compatible codec */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
} else { /* mss-compatible codec */
dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
switch (vend_id) {
case 0x3742630e: /* CS4237 */
case 0x3642630e: /* CS4236 */
case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
tmp_d.alt_base = d.port[2];
switch (vend_id & 0xff00ffff) {
case 0x2000a865: /* yamaha SA-2 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_YM0020 ;
break;
default:
case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_VIVO ;
d.irq[1] = 0 ;
break;
case 0x3700630e: /* CS4237 */
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3600630e: /* CS4236 */
tmp_d.bd_id = MD_CS4236 ;
break;
default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
dev->id_alive = 1;
pcmattach(dev);
}
@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
u_char tmp;
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
tmp_d.alt_base = d.port[0];
tmp_d.io_base = d.port[2] - 4;
tmp_d.alt_base = d.port[0]; /* 0x220 */
tmp_d.conf_base = d.port[1]; /* gus control block... */
tmp_d.bd_id = MD_GUSPNP ;
/* reset */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
/*
* unmute left & right line. Need to go in mode3, unmute,
* and back to mode 2
*/
tmp = ad_read(&tmp_d, 0x0c);
ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
/* send codec interrupts on irq1 and only use that one */
gus_write(tmp_d.conf_base, 0x5a , 0x4f );
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
strcpy(tmp_d.name, name);
pcmattach(dev);
}
#if 0
int
gus_mem_write(snddev_info *d, int addr, u_char data)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
outb(d->conf_base + 7, data);
}
u_char
gus_mem_read(snddev_info *d, int addr)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
return inb(d->conf_base + 7);
}
void
gus_mem_cfg(snddev_info *d)
{
int base;
u_char old;
u_char a, b;
printf("configuring gus memory...\n");
gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
old = gus_read(d->conf_base, 0x19);
gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
for (base = 0; base < 1024; base++) {
a=gus_mem_read(d, base*1024);
a = ~a ;
gus_mem_write(d, base*1024, a);
b=gus_mem_read(d, base*1024);
if ( b != a )
break ;
}
printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
}
#endif /* gus mem cfg... */
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */

View file

@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
#define MSS_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
* Following there is a macro ...MIXER_DEVICES which is a bitmap
* of all non-zero fields in the table.
* MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
* registers I0..I15)
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
mixer_ent mix_devices[32][2] = {
MIX_NONE(SOUND_MIXER_VOLUME),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_NONE(SOUND_MIXER_LINE1),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
/*
* entries for the opti931...
*/
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_IMIX),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */

View file

@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
* Remebber, isa probe routines are supposed to return the
* Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
dev->id_alive = 16 ; /* number of io ports */
/* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( d->dbuf_out.dl && (reason & 1) )
dsp_wrintr(d);
if ( d->dbuf_in.dl && (reason & 2) )
dsp_rdintr(d);
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
dsp_wrintr(d);
if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
dsp_rdintr(d);
/*
* the sb16 might have multiple sources etc.
*/
@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
break ;
break;
case SND_CB_START : /* called with int disabled */
sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
* We use the following algorithm:
* 1. check which direction(s) are active;
* 2. check if we should swap dma channels
* 3. check if we can do the swap.
*/
int b16 ;
int swap = 0 ;
int swap = 1 ; /* default... */
b16 = (rd) ? d->rec_fmt : d->play_fmt ;
b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
/*
* check if I have to swap dma channels. Swap if
* - !rd, dma1 <4, b16
* - !rd, dma1 >=4, !b16
* - rd, dma2 <4, b16
* - rd, dma2 >=4, !b16
*/
if (!rd) {
if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
if (rd) {
if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
swap = 0;
if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
swap = 0;
if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
swap = 0;
} else {
if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
if (d->flags & SND_F_READING || d->dbuf_in.dl)
swap = 0;
if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
swap = 0;
if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
swap = 0;
}
/*
* before swapping should make sure that there is no
* pending DMA on the other channel...
*/
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
DEB(printf("START dma chan: play %d, rec %d\n",
d->dma1, d->dma2));
}
DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
/* fallthrough */
case SND_CB_RESTART:
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
/*
* SB16 support still not completely working!!!
*
* in principle, on the SB16, I could support simultaneous
* play & rec.
* However, there is no way to ask explicitly for 8 or
* 16 bit transfer. As a consequence, if we do 8-bit,
* we need to use the 8-bit channel, and if we do 16-bit,
* we need to use the other one. The only way I find to
* do this is to swap d->dma1 and d->dma2 ...
*
*/
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
/* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
/* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if (d->bd_flags & BD_F_SB16) {
if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
cmd = DSP_CMD_DMAPAUSE_16 ;
}
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
} else {
sb_cmd(d->io_base, cmd); /* pause dma. */
/*
* This seems to have the side effect of blocking the other
* side as well so I have to re-enable it :(
*/
if ( (rd && d->dbuf_out.dl) ||
(!rd && d->dbuf_in.dl) )
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
DEB(printf("sb_reset_dsp failed\n"));
DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
* XXX check this code...
* XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (vend_id == 0x2b008c0e)
s = "SB16 Value PnP" ;
/*
* The SB16/AWE64 cards seem to differ in the fourth byte of
* The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB AWE64 PnP";
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);

View file

@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_ADC8 0x20 /* direct ADC input */
/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
/* these should be used in the SB 2.0 and 2.01 */
#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_DMAPAUSE_8 0xD0
#define DSP_CMD_DMAPAUSE_16 0xD5
#define DSP_CMD_DMAEXIT_8 0xDA
#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif

View file

@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
* Remebber, isa probe routines are supposed to return the
* Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
dev->id_alive = 16 ; /* number of io ports */
/* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( d->dbuf_out.dl && (reason & 1) )
dsp_wrintr(d);
if ( d->dbuf_in.dl && (reason & 2) )
dsp_rdintr(d);
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
dsp_wrintr(d);
if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
dsp_rdintr(d);
/*
* the sb16 might have multiple sources etc.
*/
@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
break ;
break;
case SND_CB_START : /* called with int disabled */
sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
* We use the following algorithm:
* 1. check which direction(s) are active;
* 2. check if we should swap dma channels
* 3. check if we can do the swap.
*/
int b16 ;
int swap = 0 ;
int swap = 1 ; /* default... */
b16 = (rd) ? d->rec_fmt : d->play_fmt ;
b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
/*
* check if I have to swap dma channels. Swap if
* - !rd, dma1 <4, b16
* - !rd, dma1 >=4, !b16
* - rd, dma2 <4, b16
* - rd, dma2 >=4, !b16
*/
if (!rd) {
if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
if (rd) {
if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
swap = 0;
if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
swap = 0;
if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
swap = 0;
} else {
if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
if (d->flags & SND_F_READING || d->dbuf_in.dl)
swap = 0;
if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
swap = 0;
if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
swap = 0;
}
/*
* before swapping should make sure that there is no
* pending DMA on the other channel...
*/
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
DEB(printf("START dma chan: play %d, rec %d\n",
d->dma1, d->dma2));
}
DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
/* fallthrough */
case SND_CB_RESTART:
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
/*
* SB16 support still not completely working!!!
*
* in principle, on the SB16, I could support simultaneous
* play & rec.
* However, there is no way to ask explicitly for 8 or
* 16 bit transfer. As a consequence, if we do 8-bit,
* we need to use the 8-bit channel, and if we do 16-bit,
* we need to use the other one. The only way I find to
* do this is to swap d->dma1 and d->dma2 ...
*
*/
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
/* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
/* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if (d->bd_flags & BD_F_SB16) {
if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
cmd = DSP_CMD_DMAPAUSE_16 ;
}
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
} else {
sb_cmd(d->io_base, cmd); /* pause dma. */
/*
* This seems to have the side effect of blocking the other
* side as well so I have to re-enable it :(
*/
if ( (rd && d->dbuf_out.dl) ||
(!rd && d->dbuf_in.dl) )
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
DEB(printf("sb_reset_dsp failed\n"));
DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
* XXX check this code...
* XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (vend_id == 0x2b008c0e)
s = "SB16 Value PnP" ;
/*
* The SB16/AWE64 cards seem to differ in the fourth byte of
* The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB AWE64 PnP";
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);

View file

@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
* Remebber, isa probe routines are supposed to return the
* Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
dev->id_alive = 16 ; /* number of io ports */
/* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( d->dbuf_out.dl && (reason & 1) )
dsp_wrintr(d);
if ( d->dbuf_in.dl && (reason & 2) )
dsp_rdintr(d);
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
dsp_wrintr(d);
if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
dsp_rdintr(d);
/*
* the sb16 might have multiple sources etc.
*/
@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
break ;
break;
case SND_CB_START : /* called with int disabled */
sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
* We use the following algorithm:
* 1. check which direction(s) are active;
* 2. check if we should swap dma channels
* 3. check if we can do the swap.
*/
int b16 ;
int swap = 0 ;
int swap = 1 ; /* default... */
b16 = (rd) ? d->rec_fmt : d->play_fmt ;
b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
/*
* check if I have to swap dma channels. Swap if
* - !rd, dma1 <4, b16
* - !rd, dma1 >=4, !b16
* - rd, dma2 <4, b16
* - rd, dma2 >=4, !b16
*/
if (!rd) {
if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
if (rd) {
if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
swap = 0;
if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
swap = 0;
if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
swap = 0;
} else {
if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
if (d->flags & SND_F_READING || d->dbuf_in.dl)
swap = 0;
if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
swap = 0;
if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
swap = 0;
}
/*
* before swapping should make sure that there is no
* pending DMA on the other channel...
*/
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
DEB(printf("START dma chan: play %d, rec %d\n",
d->dma1, d->dma2));
}
DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
/* fallthrough */
case SND_CB_RESTART:
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
/*
* SB16 support still not completely working!!!
*
* in principle, on the SB16, I could support simultaneous
* play & rec.
* However, there is no way to ask explicitly for 8 or
* 16 bit transfer. As a consequence, if we do 8-bit,
* we need to use the 8-bit channel, and if we do 16-bit,
* we need to use the other one. The only way I find to
* do this is to swap d->dma1 and d->dma2 ...
*
*/
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
/* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
/* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if (d->bd_flags & BD_F_SB16) {
if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
cmd = DSP_CMD_DMAPAUSE_16 ;
}
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
} else {
sb_cmd(d->io_base, cmd); /* pause dma. */
/*
* This seems to have the side effect of blocking the other
* side as well so I have to re-enable it :(
*/
if ( (rd && d->dbuf_out.dl) ||
(!rd && d->dbuf_in.dl) )
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
DEB(printf("sb_reset_dsp failed\n"));
DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
* XXX check this code...
* XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (vend_id == 0x2b008c0e)
s = "SB16 Value PnP" ;
/*
* The SB16/AWE64 cards seem to differ in the fourth byte of
* The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB AWE64 PnP";
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);

View file

@ -1,8 +1,8 @@
--- A new FreeBSD sound driver ---
by Luigi Rizzo (luigi@iet.unipi.it)
--- A new FreeBSD audio driver ---
by Luigi Rizzo (luigi@iet.unipi.it)
This is an experimental version of the sound driver for FreeBSD.
This is an experimental version of the audio driver for FreeBSD.
I have almost completely rewritten the main parts of the code,
starting from the Voxware 3.5-alpha release with patches from
Amancio Hasty. The only file which is largely similar to the original
@ -20,15 +20,11 @@ For PnP cards, I also include the vendor_id and serial numbers of
cards I have encountered.
CS4236: PnP id 0x3642630e
CS4237: PnP id 0x3742630e
works like a charm. All modes, including full duplex, supported in
MSS mode.
CS4237: PnP id 0x3742630e
I had early reports of success with this board, which is almost
the same as the CS4236.
CS4232: PnP id 0x3242630e
this chip is reported as broken in the OSS documentation. As a
@ -39,32 +35,66 @@ CS4232: PnP id 0x3242630e
OPTi931: PnP id 0x3109143e
The data sheets of this chip are very cryptic. I have it working
in full duplex in all modes _except_ capture of uLAW/ALAW data.
I am strongly convinced of a bug in the chip. I have sent email
to OPTI but got no reply so far. In SB emulation mode the
driver does not work yet (maybe I do not initialize it the
right way).
Another bug seems to affect full duplex operation -- it appears
that at times DMA transfer are requested but not counted by
the device. In normal DMA mode this causes deadlocks. The only
solution I have found is to fetch the count from the ISA DMA
registers, but this does not seem to work very well either.
The data sheets of this chip are very cryptic and do not match
what the cards I have seem to do. I have it working
in WSS emulation, in full duplex and all modes.
In SB emulation mode the driver does not work yet (maybe I do
not initialize it the right way). It is not a major concern
anyways.
I am strongly convinced of a couple of bugs in the chip. I have
sent email to OPTI but got no reply so far. The bugs are:
- you cannot set both playback and capture format to use
a companded mode (ULAW, ALAW). If you do, the card will screw
up on the capture section.
The driver has a partial fix in software: when using ULAW, it
programs ULAW on the playback section, U8 on the capture, and
does a conversion in software (much like the SBPro). Of course
you lose 4-5 bits of dynamic range in the process.
- in full duplex (and single DMA mode), the card occasionally
misses interrupts, or generates spurious ones. Spurious ints are
not problematic since they can be ignored, but missed ones are
as you can imagine... This is fixed by auto-dma mode.
SB16 PnP: PnP id 0xXX008c0e
There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
etc. all differing in the PnP id (and with different synthesis
devices, which we do not support anyways).
etc.) all differing in the PnP id. They have different synthesis
devices, which we do not support, so we are not affected by these
differences. Don't worry if the driver identifies the card as a
different SB16 than the one you have.
Since 970903 the driver supports them all, both capture and
playback, 8 and 16 bits.
Full duplex support of this card is tricky since one channel can
work in 16-bit and the other in 8-bit mode. You will need to use
the new set of ioctl to use separate data formats on the two
channels (the vat driver does this). Of course, quality in 8-bit
is much lower than in 16-bit.
Full duplex operation is unsupported by Creative. It seems to
work, although on my Vibra16 the command to stop DMA transfer
seems to erroneously affect both channels instead of the one
they are issued for. The driver has a workaround, but I cannot
guarantee that it works for everybody. I have had several
positive reports.
Yamaha SA2
this card emulates a WSS or SB. Have reports that it works, although
it has mixer problems (maybe the driver has the wrong set of
parameters).
Ensoniq Soundscape VIVO
this card emulates a WSS or SB. Have reports that it works.
GusPnP: PnP id 0x0100561e
I have code to recognize the board as MSS, but have not tested it
since I don't own the board. Hopefully someone will test it soon.
I have code to recognize the board as MSS, but some initializations
of the IW chip is still missing, so the card is not supported yet.
I am working on this problem...
OPTI924: PnP
I have this card but it is still unsupported.
OPTI925: PnP id 0x2509143e
@ -76,3 +106,21 @@ OPTI930:
should work as an MSS clone, but support for it is not implemented
yet.
ESS1868
this card is not supported yet. It might work in SB emulation but
am not sure.
ESS688
this card is used on many notebook. I don't have docs on this card
so I cannot support it. Pointers to the data sheets are welcome.
PCI cards:
some vendors have PCI cards. This code _cannot_ work on these
cards as it is now, since they cannot obviously use the ISA DMA
controller. As there are no data sheets available for these PCI
cards, none of them is supported at the moment, although support
should be easy as soon as I can put my hands on the data sheets
and cards.

View file

@ -1,30 +1,24 @@
/*
* sound/ad1848.c
*
* Modified by Luigi Rizzo (luigi@iet.unipi.it)
*
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248
*
* CS4231, used in the GUS MAX and some other cards;
* AD1845, CS4231A (CS4231-like)
* CS4232 (CS4231+SB and MPU, PnP)
* CS4236 (upgrade of the CS4232, has a better mixer)
* OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
*
* Copyright Luigi Rizzo, 1997
* Copyright by Hannu Savolainen 1994, 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@ -39,14 +33,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* Full data sheets in PDF format for the MSS-compatible chips
* are available at
*
* http://www.crystal.com/ for the CS42XX series, or
* http://www.opti.com/ for the OPTi931
*
* The OPTi931 appears to be quite buggy.
*/
#include <i386/isa/snd/sound.h>
@ -114,11 +105,12 @@ snddev_info mss_op_desc = {
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
/*
* the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
* but we do not use these modes.
*/
} ;
/*
* this is the probe routine. Note, it is not necessary to
* mss_probe() is the probe routine. Note, it is not necessary to
* go through this for PnP devices, since they are already
* indentified precisely using their PnP id.
*
@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev)
d->name, dev->id_unit,
d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
dev->id_alive = 8 ; /* number of io ports */
/* should be already set but just in case... */
if ( dev->id_flags & DV_F_TRUE_MSS ) {
/* has IRQ/DMA registers, set IRQ and DMA addr */
static char interrupt_bits[12] = {
@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev)
printf("invalid dual dma config %d:%d\n",
d->dma1, d->dma2);
dev->id_irq = 0 ;
dev->id_alive = 0 ; /* this makes attach fail. */
return 0 ;
}
outb(dev->id_iobase, bits );
}
}
if (d->dma1 != d->dma2)
d->audio_fmt |= AFMT_FULLDUPLEX ;
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
break;
}
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
ask_init(d);
ask_init(d); /* and reset buffers... */
}
splx(s);
return 0 ;
@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* the callback routine to handle all dma ops etc.
* With the exception of INIT, all other callbacks are invoked
* with interrupts disabled.
*/
static int
mss_callback(snddev_info *d, int reason)
{
u_long s;
u_char m;
int retry, wr, cnt;
@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason)
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1 ;
break ;
case SND_CB_START :
/* fallthrough */
case SND_CB_RESTART :
s = spltty();
cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ;
if (d->play_fmt == AFMT_S16_LE)
cnt /= 2;
if (d->flags & SND_F_STEREO)
@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason)
DEB(printf("-- (re)start cnt %d\n", cnt));
m = ad_read(d,9) ;
DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
/*
* on the OPTi931 the enable bit seems hard to set...
@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason)
else
ad_write_cnt(d, 30, cnt);
splx(s);
break ;
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
s = spltty();
m = ad_read(d,9) ;
m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
/*
@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason)
if (retry == 0)
printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
m, ad_read(d, 9) ) ;
/* disable DMA by clearing count registers. */
#if 1
/*
* try to disable DMA by clearing count registers. Not sure it
* is needed, and it might cause false interrupts when the
* DMA is re-enabled later.
*/
if (wr || (d->dma1 == d->dma2) )
ad_write_cnt(d, 14, 0);
else
ad_write_cnt(d, 30, 0);
splx(s);
break;
#endif
}
return 0 ;
}
@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason)
/*
* main irq handler for the CS423x. The OPTi931 code is
* a separate one.
* The correct way to operate for a device with multiple internal
* interrupt sources is to loop on the status register and ack
* interrupts until all interrupts are served and none are reported. At
* this point the IRQ line to the ISA IRQ controller should go low
* and be raised at the next interrupt.
*
* Since the ISA IRQ controller is sent EOI _before_ passing control
* to the isr, it might happen that we serve an interrupt early, in
* which case the status register at the next interrupt should just
* say that there are no more interrupts...
*/
static void
mss_intr(int unit)
{
snddev_info *d = &pcm_info[unit];
u_char i11, c;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
u_char c, served = 0;
int i;
i11 = ad_read(d, 11);
reason = inb(io_Status(d));
DEB(printf("mss_intr\n"));
ad_read(d, 11); /* fake read of status bits */
if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
return;
/* get exact reason */
c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
dsp_wrintr(d);
c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
dsp_rdintr(d);
/* XXX check this on the 4236... */
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
/*
* loop until there are interrupts, but no more than 10 times.
*/
for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) {
/* get exact reason for full-duplex boards */
c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24);
c &= ~served ;
if ( d->dbuf_out.dl && (c & 0x10) ) {
served |= 0x10 ;
dsp_wrintr(d);
}
if ( d->dbuf_in.dl && (c & 0x20) ) {
served |= 0x20 ;
dsp_rdintr(d);
}
/*
* now ack the interrupt
*/
if (d->dma1 == d->dma2)
outb(io_Status(d), 0); /* Clear interrupt status */
else
ad_write(d, 24, ~c); /* ack selectively */
}
}
/*
* the opti931 seems to miss interrupts when working in full
* duplex. god know why...
* duplex, so we try some heuristics to catch them.
*/
static void
opti931_intr(int unit)
@ -509,8 +523,6 @@ opti931_intr(int unit)
u_char masked=0, i11, mc11, c=0;
u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
int loops = 10;
int w_miss=0, r_miss=0;
static int misses=0; /* XXX kludge */
#if 0
reason = inb(io_Status(d));
@ -525,11 +537,11 @@ again:
c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
mc11 &= 0x0c ;
if (c & 0x10) {
printf("Warning CD interrupt\n");
printf("Warning: CD interrupt\n");
mc11 |= 0x10 ;
}
if (c & 0x20) {
printf("Warning MPU interrupt\n");
printf("Warning: MPU interrupt\n");
mc11 |= 0x20 ;
}
if (mc11 & masked)
@ -541,38 +553,18 @@ again:
printf("one more try...\n");
goto again;
}
if (w_miss || r_miss ) {
misses++;
if ( (misses & 0xff) == 1 )
printf("opti931: %d missed irq (now %s%s)\n",
misses, w_miss?"play ":"",
r_miss?"capture ":"");
}
if (loops==10)
if (loops==10) {
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
if (w_miss) mc11 |= 4 ;
if (r_miss) mc11 |= 8 ;
if (mc11==0)
return;
}
return;
}
if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
if ( d->dbuf_in.dl && (mc11 & 8) ) {
dsp_rdintr(d);
r_miss = 0 ;
}
else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
r_miss = 1 ;
if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
if (isa_dmastatus1(d->dma1) != 0)
printf("opti931_intr: wr dma %d\n",
isa_dmastatus1(d->dma1));
if ( d->dbuf_out.dl && (mc11 & 4) ) {
dsp_wrintr(d);
w_miss = 0 ;
}
else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
w_miss = 1 ;
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
printf("xxx too many loops\n");
@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg)
outb(io_base, reg);
return inb(io_base+1);
}
static void
gus_write(int io_base, u_char reg, u_char value)
{
outb(io_base + 3, reg);
outb(io_base + 5, value);
}
static void
gus_writew(int io_base, u_char reg, u_short value)
{
outb(io_base + 3, reg);
outb(io_base + 4, value);
}
static u_char
gus_read(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inb(io_base+5);
}
static u_short
gus_readw(int io_base, u_char reg)
{
outb(io_base+3, reg);
return inw(io_base+4);
}
/*
* AD_WAIT_INIT waits if we are initializing the board and
* we cannot modify its settings
@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d)
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
}
static void
@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d)
d->bd_flags &= ~BD_F_MCE_BIT;
prev = inb(io_Index_Addr(d));
outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */
wait_for_calibration(d);
splx(flags);
}
/*
* only one source can be set...
*
* fixed -- lr 970725
*/
static int
mss_set_recsrc(snddev_info *d, int mask)
@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
mix_d = &(opti931_devices);
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
DDB(printf("nbits = 0 for dev %d\n", dev) );
DEB(printf("nbits = 0 for dev %d\n", dev) );
return EINVAL;
}
@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, LEFT_CHN, left);
ad_write(d, regoffs, val);
DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value)
* Set the right channel
*/
regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
val = ad_read(d, regoffs);
old = val = ad_read(d, regoffs);
if (regoffs != 1)
val = old & 0x7f ; /* clear mute bit. */
change_bits(mix_d, &val, dev, RIGHT_CHN, right);
ad_write(d, regoffs, val);
DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n",
dev, regoffs, old, val));
}
return 0; /* success */
}
@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d)
else
d->mix_devs = MODE1_MIXER_DEVICES;
d->mix_rec_devs = MODE1_REC_DEVICES;
d->mix_rec_devs = MSS_REC_DEVICES;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (d->mix_devs & (1 << i))
mss_mixer_set(d, i, default_mixer_levels[i]);
mss_set_recsrc(d, SOUND_MASK_MIC);
/*
* some device-specific things, mostly mute the mic to
* the output mixer so as to avoid hisses. In many cases this
* is the default after reset, this code is here mostly as a
* reminder that this might be necessary on other boards.
*/
switch(d->bd_id) {
case MD_OPTI931:
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
ad_write(d, 23, 0x88);
}
}
/*
@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d)
* matching one. As a side effect, it returns the value to
* be written in the speed bits of the codec. It does _NOT_
* set the speed of the device (but it should!)
*
* fixed lr970724
*/
static int
@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
DDB(printf("mss_detect error, busy still set (0x%02x)\n",
DEB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
return 0;
}
ad_write(d, 0, 0x45);
@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
DDB(printf("mss_detect error - step H(%x)\n", tmp1));
DEB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
DDB(printf("mss_detect() - step I\n"));
DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev)
break;
case 0x83: /* CS4236 */
case 0x03: /* CS4236 on Intel PR440FX motherboard */
case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */
name = "CS4236";
d->bd_id = MD_CS4236;
break ;
@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev)
}
}
DDB(printf("mss_detect() - Detected %s\n", name));
DEB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d)
ad_write(d, 8, r) ;
if (d->dma1 != d->dma2) {
#if 0
if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) {
printf("warning, cannot do ulaw rec + play on the GUS\n");
r = 0 ; /* move to U8 */
}
#endif
ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
} else
ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
ad_leave_MCE(d);
/*
* not sure if this is really needed...
*/
ad_write_cnt(d, 14, 0 ); /* playback count */
if (d->dma1 != d->dma2)
ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d)
#if NPNP > 0
static char * cs4236_probe(u_long csn, u_long vend_id);
static void cs4236_attach(u_long csn, u_long vend_id, char *name,
static char * cs423x_probe(u_long csn, u_long vend_id);
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device cs4236 = {
"cs423x",
cs4236_probe,
cs4236_attach,
static struct pnp_device cs423x = {
"cs423x/ymh0020",
cs423x_probe,
cs423x_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, cs4236);
DATA_SET (pnpdevice_set, cs423x);
static char *
cs4236_probe(u_long csn, u_long vend_id)
cs423x_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x3742630e)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if (vend_id == 0x3642630e)
else if ( id == 0x3600630e )
s = "CS4236" ;
else if (vend_id == 0x360b630e)
s = "CS4236" ;
else if (vend_id == 0x3242630e)
else if ( id == 0x3200630e)
s = "CS4232" ;
else if ( id == 0x2000a865)
s = "Yamaha SA2";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id)
extern snddev_info sb_op_desc;
static void
cs4236_attach(u_long csn, u_long vend_id, char *name,
cs423x_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
if (d.flags & DV_PNP_SBCODEC) {
printf("CS423x use sb-compatible codec\n");
dev->id_iobase = d.port[2] ;
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
tmp_d.alt_base = d.port[0] - 4;
if (vend_id == 0x2000a865 || vend_id == 0x8140d315) {
/* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
d.irq[1] = 0 ; /* only needed for the VIVO */
} else {
dev->id_iobase = d.port[2] ;
tmp_d.alt_base = d.port[0] - 4;
}
d.drq[1] = 4 ; /* disable, it is not used ... */
} else {
/* mss-compatible codec */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
} else { /* mss-compatible codec */
dev->id_alive = 8 ; /* number of io ports ? */
tmp_d = mss_op_desc ;
switch (vend_id) {
case 0x3742630e: /* CS4237 */
case 0x3642630e: /* CS4236 */
case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */
tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */
dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
tmp_d.alt_base = d.port[2];
switch (vend_id & 0xff00ffff) {
case 0x2000a865: /* yamaha SA-2 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_YM0020 ;
break;
default:
case 0x8100d315: /* ENSONIQ SoundscapeVIVO */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.bd_id = MD_VIVO ;
d.irq[1] = 0 ;
break;
case 0x3700630e: /* CS4237 */
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3600630e: /* CS4236 */
tmp_d.bd_id = MD_CS4236 ;
break;
default:
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
break;
}
tmp_d.alt_base = d.port[2];
strcpy(tmp_d.name, name);
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
}
@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
dev->id_alive = 1;
pcmattach(dev);
}
@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);
static void guspnp_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
u_char tmp;
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
enable_pnp_card();
@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
snddev_last_probed = &tmp_d;
dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_drq = d.drq[1] ; /* XXX PLAY dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */
tmp_d.alt_base = d.port[0];
tmp_d.io_base = d.port[2] - 4;
tmp_d.alt_base = d.port[0]; /* 0x220 */
tmp_d.conf_base = d.port[1]; /* gus control block... */
tmp_d.bd_id = MD_GUSPNP ;
/* reset */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
outb( tmp_d.alt_base, 0xC ); /* enable int and dma */
/*
* unmute left & right line. Need to go in mode3, unmute,
* and back to mode 2
*/
tmp = ad_read(&tmp_d, 0x0c);
ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */
ad_write(&tmp_d, 0x19, 0 ); /* unmute left */
ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */
ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */
/* send codec interrupts on irq1 and only use that one */
gus_write(tmp_d.conf_base, 0x5a , 0x4f );
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
strcpy(tmp_d.name, name);
pcmattach(dev);
}
#if 0
int
gus_mem_write(snddev_info *d, int addr, u_char data)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
outb(d->conf_base + 7, data);
}
u_char
gus_mem_read(snddev_info *d, int addr)
{
gus_writew(d->conf_base, 0x43 , addr & 0xffff );
gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff );
return inb(d->conf_base + 7);
}
void
gus_mem_cfg(snddev_info *d)
{
int base;
u_char old;
u_char a, b;
printf("configuring gus memory...\n");
gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/);
old = gus_read(d->conf_base, 0x19);
gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */
for (base = 0; base < 1024; base++) {
a=gus_mem_read(d, base*1024);
a = ~a ;
gus_mem_write(d, base*1024, a);
b=gus_mem_read(d, base*1024);
if ( b != a )
break ;
}
printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b);
}
#endif /* gus mem cfg... */
#endif /* NPNP > 0 */
#endif /* NPCM > 0 */

File diff suppressed because it is too large Load diff

View file

@ -1,802 +0,0 @@
/*
* snd/dmabuf.c
*
* New DMA routines -- Luigi Rizzo, 05 sep 97
* This file implements the new DMA routines for the sound driver.
* AUTO DMA MODE (ISA DMA SIDE).
*
* Copyright by Luigi Rizzo - 1997
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <i386/isa/snd/sound.h>
#include <i386/isa/snd/ulaw.h>
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
static void dsp_wr_dmadone(snddev_info *d);
static void dsp_rd_dmadone(snddev_info *d);
/*
*
SOUND OUTPUT
We use a circular buffer to store samples directed to the DAC.
The buffer is split into two variable-size regions, each identified
by an offset in the buffer (rp,fp) and a lenght (rl,fl).
0 rp,rl fp,fl bufsize
|__________|____________|________|
FREE d> READY w> FREE
READY region: contains data written from the process and ready
to be sent to the DAC;
FREE region: is the empty region of the buffer, where a process
can write new data.
The first block of the READY region is used for DMA transfers. The
transfer is started at rp and with chunks of length dl0 computed
according to some rules. During DMA operations, rp advances (d>)
and rl,fl are updated by dsp_wr_dmaupdate(). When a new block is
written, fp advances (w>) and rl,fl are updated accordingly.
Both the "READY" and "FREE" regions can wrap around the end of the
buffer.
At initialization, READY is empty, FREE takes all the
available space, and dma is idle
dl0 contains the new blocksize to be used in dma transfers (passed to
the callback). dl contains the previous blocksize (needed when the
codec uses AUTO DMA).
rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ;
rl = 0 ; -- meaning no data ready
fl = bufsize ;
The code works as follows: the user write routine dsp_write_body()
fills up the READY region with new data (reclaiming space from the
FREE region) and starts the write DMA engine if inactive (activity
is indicated by d->flags & SND_F_WR_DMA ). The size of each
DMA transfer is chosen according to a series of rules which will be
discussed later. When a DMA transfer is complete, an interrupt causes
dsp_wrintr() to be called which empties the DMA region, extends
the FREE region and possibly starts the next transfer.
In some cases, the code tries to track the current status of DMA
operations by calling isa_dmastatus() and advancing the boundary
between FREE and DMA regions accordingly.
The size of a DMA transfer is selected according to the following
rules:
1. when not using AUTO DMA, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half of the buffer size.
This serves to allow room for a next write operation concurrent
with the dma transfer, and to reduce the time which is necessary
to wait before a pending dma will end (optionally, the max
size could be further limited to a fixed amount of play time,
depending on number of channels, sample size and sample speed);
3. use the current blocksize (either specified by the user, or
corresponding roughly to 0.25s of data);
*
*/
/*
* dsp_wr_dmadone moves the write DMA region into the FREE region.
* It is assumed to be called at spltty() and with a write dma
* previously started.
*/
static void
dsp_wr_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
dsp_wr_dmaupdate(d);
/*
* XXX here it would be more efficient to record if there
* actually is a sleeping process, but this should still work.
*/
wakeup(b); /* wakeup possible sleepers */
if (d->wsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
selwakeup( & d->wsel );
}
/*
* The following function tracks the status of a (write) dma transfer,
* and moves the boundary between the FREE and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is active;
* - the routine is called with interrupts blocked.
*/
void
dsp_wr_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
int tmp, delta;
tmp = b->bufsize - isa_dmastatus1(d->dma1) ;
tmp &= DMA_ALIGN_MASK; /* align... */
delta = tmp - b->rp;
if (delta < 0) /* wrapped */
delta += b->bufsize ;
b->rp = tmp;
b->rl -= delta ;
b->fl += delta ;
}
/*
* Write interrupt routine. Can be called from other places, but
* with interrupts disabled.
*/
void
dsp_wrintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
b->dl, b->rl, b->fl));
if (d->flags & SND_F_WR_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_WR_DMA;
dsp_wr_dmadone(d);
} else
b->dl = 0 ;
/*
* start another dma operation only if have ready data in the
* buffer, there is no pending abort, have a full-duplex device
* (dma1 != dma2) or have half duplex device and there is no
* pending op on the other side.
*
* Force transfer to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit. We could make this
* adaptive, but why bother for now...
*/
if ( b->rl >= DMA_ALIGN_THRESHOLD &&
! (d->flags & SND_F_ABORTING) &&
( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
l &= DMA_ALIGN_MASK ; /* realign things */
b->dl0 = l ;
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_START );
d->flags |= SND_F_WR_DMA;
b->dl = b->dl0;
} else {
if (b->dl > 0) { /* was active */
b->dl0 = 0;
d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
}
/*
* if switching to read, should start the read dma...
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
dsp_rdintr(d);
DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
d->flags, isa_dmastatus1(d->dma1), b->rl));
}
}
/*
* user write routine
*
* advance the boundary between READY and FREE, fill the space with
* uiomove(), and possibly start DMA. Do the above until the transfer
* is complete.
*
* To minimize latency in case a pending DMA transfer is about to end,
* we do the transfer in pieces of increasing sizes, extending the
* READY area at every checkpoint. In the (necessary) assumption that
* memory bandwidth is larger than the rate at which the dma consumes
* data, we reduce the latency to something proportional to the length
* of the first piece, while keeping the overhead low and being able
* to feed the DMA with large blocks.
*/
int
dsp_write_body(snddev_info *d, struct uio *buf)
{
int timeout = 1, n, l, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_out) ;
/* assume d->flags |= SND_F_WRITING ; has been done before */
/*
* bsz is the max size for the next transfer. If the dma was
* idle, we want it as large as possible. Otherwise, start with
* a small block to avoid underruns if we are close to the end of
* the previous operation.
*/
bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
while ( n = buf->uio_resid ) {
l = min (n, bsz); /* at most n bytes ... */
s = spltty(); /* no interrupts here ... */
/*
* if i) the dma engine is running, ii) we do not have enough space
* in the FREE region, and iii) the current DMA transfer might let
* us complete the _whole_ transfer without sleeping, or we are doing
* non-blocking I/O, then try to extend the FREE region.
* Otherwise do not bother, we will need to sleep anyways, and
* make the timeout longer.
* Note that the check for iii) is only approximate since we
* might have fewer than dl0 residual bytes.
*/
if ( d->flags & SND_F_WR_DMA && b->fl < l &&
( b->fl + b->dl0 >= n || d->flags & SND_F_NBIO ) )
dsp_wr_dmaupdate(d); /* should really change timeout... */
else
timeout = hz;
l = min( l, b->fl ); /* no more than avail. space */
DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
/*
* at this point, we assume that if l==0 the dma engine
* must (or will, in cause it is paused) be running.
*/
if (l == 0) { /* no space, must sleep */
if (d->flags & SND_F_NBIO) {
/* unless of course we are doing non-blocking i/o */
splx(s);
break;
}
DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
if (ret == ERESTART || ret == EINTR)
break ;
timeout = min(2*timeout, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data... */
/*
* copy data to the buffer, and possibly do format
* conversions (here, from ULAW to U8).
* NOTE: I can use fp here since it is not modified by the
* interrupt routines.
*/
if (b->fp + l > b->bufsize) {
int l1 = b->bufsize - b->fp ;
uiomove(b->buf + b->fp, l1, buf) ;
uiomove(b->buf, l - l1, buf) ;
if (d->flags & SND_F_XLAT8) {
translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
translate_bytes(ulaw_dsp, b->buf , l - l1);
}
} else {
uiomove(b->buf + b->fp, l, buf) ;
if (d->flags & SND_F_XLAT8)
translate_bytes(ulaw_dsp, b->buf + b->fp, l);
}
s = spltty(); /* no interrupts here ... */
b->rl += l ; /* this more ready bytes */
b->fl -= l ; /* this less free bytes */
b->fp += l ;
if (b->fp >= b->bufsize) /* handle wraps */
b->fp -= b->bufsize ;
if ( !(d->flags & SND_F_WR_DMA) ) /* dma was idle, restart it */
dsp_wrintr(d) ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_WRITING ;
if (d->flags & SND_F_ABORTING) {
d->flags &= ~SND_F_ABORTING;
splx(s);
dsp_wrabort(d);
}
splx(s) ;
return ret ;
}
/*
* SOUND INPUT
*
The input part is similar to the output one. The only difference is in
the ordering of regions, which is the following:
0 rp,rl fp,fl bufsize
|__________|____________|________|
FREE r> READY d> FREE
and the fact that input data are in the READY region.
At initialization, as for the write routine, READY is empty,
and FREE takes all the space:
rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ;
rl = 0 ; -- meaning no data ready
fl = bufsize ;
Operation is as follows: upon user read (dsp_read_body()) a DMA read
is started if not already active (marked by d->flags & SND_F_RD_DMA),
then as soon as data are available in the READY region they are
transferred to the user buffer, thus advancing the boundary between FREE
and READY. Upon interrupts, caused by a completion of a DMA transfer,
the READY region is extended and possibly a new transfer is started.
When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
updated accordingly.
The rules to choose the size of the new DMA area are similar to
the other case, i.e:
1. if not using AUTO mode, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half the buffer size; this serves to
leave room for the next dma operation.
3. use the default blocksize, either user-specified, or
corresponding to 0.25s of data;
*
*/
/*
* dsp_rd_dmadone moves bytes in the input buffer from DMA region to
* READY region. We assume it is called at spltty() and with dl>0
*/
static void
dsp_rd_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
dsp_rd_dmaupdate(d);
b->rl += b->dl ; /* make dl bytes available */
wakeup(b) ; /* wakeup possibly sleeping processes */
if (d->rsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
selwakeup( & d->rsel );
b->dp = b->fp ;
b->dl0 = b->dl = 0 ;
}
/*
* The following function tracks the status of a (read) dma transfer,
* and moves the boundary between the READY and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is running;
* - the function is called with interrupts blocked.
*/
void
dsp_rd_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
int tmp ;
tmp = b->bufsize - isa_dmastatus1(d->dma2) ;
tmp &= DMA_ALIGN_MASK; /* align... */
delta = tmp - b->fp;
if (delta < 0) /* wrapped */
delta += b->bufsize ;
b->fp = tmp;
b->fl -= delta ;
b->rl += delta ;
}
/*
* read interrupt routine. Must be called with interrupts blocked.
*/
void
dsp_rdintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
if (d->flags & SND_F_RD_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_RD_DMA;
dsp_rd_dmadone(d);
} else
b->dl = 0 ;
/*
* Same checks as in the write case (mutatis mutandis) to decide
* whether or not to restart a dma transfer.
*/
if ( b->fl >= DMA_ALIGN_THRESHOLD &&
((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
int l = min(b->fl, d->rec_blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
b->dl0 = l ;
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_START );
d->flags |= SND_F_RD_DMA;
b->dl = b->dl0;
} else {
if (b->dl > 0) {
b->dl0 = 0 ; /* was active */
d->callback(d, SND_CB_RD | SND_CB_STOP);
}
/*
* if switching to write, start write dma engine
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
dsp_wrintr(d) ;
DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
d->flags, isa_dmastatus1(d->dma2), b->fl));
}
}
/*
* body of user-read routine
*
* Start DMA if not active; wait for READY not empty.
* Transfer data from READY region using uiomove(), advance boundary
* between FREE and READY. Repeat until transfer is complete.
*
* To avoid excessive latency in freeing up space for the DMA
* engine, transfers are done in blocks of increasing size, so that
* the latency is proportional to the size of the smallest block, but
* we have a low overhead and are able to feed the dma engine with
* large blocks.
*
* When we enter this routine, we assume that d->flags |= SND_F_READING
* was done before.
*
* NOTE: in the current version, read will not return more than
* blocksize bytes at once (unless more are already available), to
* avoid that requests using very large buffers block for too long.
*/
int
dsp_read_body(snddev_info *d, struct uio *buf)
{
int limit, l, n, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_in) ;
int timeout = 1 ; /* counter of how many ticks we sleep */
/*
* "limit" serves to return after at most one blocksize of data
* (unless more are already available). Otherwise, things like
* cat /dev/audio would use a 64K buffer and would start returning
* data after a _very_ long time...
* Note -- some applications depend on reads not returning short
* blocks. But I believe these apps are broken, since interrupted
* system calls might return short reads anyways, and the
* application should better check that.
*/
if (buf->uio_resid > d->rec_blocksize)
limit = buf->uio_resid - d->rec_blocksize;
else
limit = 0;
bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
while ( (n = buf->uio_resid) > limit ) {
DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
/*
* here compute how many bytes to transfer, enforcing various
* limitations:
*/
l = min (n, bsz); /* 1': at most bsz bytes ... */
s = spltty(); /* no interrupts here ! */
/*
* if i) the dma engine is running, ii) we do not have enough
* ready bytes, and iii) the current DMA transfer could give
* us what we need, or we are doing non-blocking IO, then try
* to extend the READY region.
* Otherwise do not bother, we will need to sleep anyways,
* and make the timeout longer.
*/
if ( d->flags & SND_F_RD_DMA && b->rl < l &&
( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
dsp_rd_dmaupdate(d);
else
timeout = hz ;
l = min( l, b->rl ); /* 2': no more than avail. data */
if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
/*
* There are two reasons the dma can be idle: either this
* is the first read, or the buffer has become full. In
* the latter case, the dma cannot be restarted until
* we have removed some data, which will be true at the
* second round.
*
* Call dsp_rdintr to start the dma. It would be nice to
* have a "need" field in the snd_dbuf, so that we do
* not start a long operation unnecessarily. However,
* the restart code will ask for at most d->blocksize
* bytes, and since we are sure we are the only reader,
* and the routine is not interrupted, we patch and
* restore d->blocksize around the call. A bit dirty,
* but it works, and saves some overhead :)
*/
int old_bs = d->rec_blocksize;
if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
SND_F_INIT) {
/* want to init but no pending DMA activity */
splx(s);
d->callback(d, SND_CB_INIT); /* this is slow! */
s = spltty();
}
if (l < MIN_CHUNK_SIZE)
d->rec_blocksize = MIN_CHUNK_SIZE ;
else if (l < d->rec_blocksize)
d->rec_blocksize = l ;
d->dl0 = d->rec_blocksize ;
dsp_rdintr(d);
d->rec_blocksize = old_bs ;
}
if (l == 0) {
/*
* If, after all these efforts, we still have no data ready,
* then we must sleep (unless of course we have doing
* non-blocking i/o. But use exponential delays, starting
* at 1 tick and doubling each time.
*/
if (d->flags & SND_F_NBIO) {
splx(s);
break;
}
DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
timeout, buf->uio_resid));
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s); /* necessary before the goto again... */
if (ret == ERESTART || ret == EINTR)
break ;
DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
timeout = min(timeout*2, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data, so reset exp. wait */
/*
* if we are using /dev/audio and the device does not
* support it natively, we should do a format conversion.
* (in this case from uLAW to natural format).
* This can be messy in that it can require an intermediate
* buffer, and also screw up the byte count.
*/
/*
* NOTE: I _can_ use rp here because it is not modified by the
* interrupt routines.
*/
if (d->flags & SND_F_XLAT8)
translate_bytes(dsp_ulaw, b->buf + b->rp, l);
ret = uiomove(b->buf + b->rp, l, buf) ;
if (ret !=0 ) /* an error occurred ... */
break ;
s = spltty(); /* no interrupts here ... */
b->fl += l ; /* this more free bytes */
b->rl -= l ; /* this less ready bytes */
b->rp += l ; /* advance ready pointer */
if (b->rp == b->bufsize) /* handle wraps */
b->rp = 0 ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_READING ;
if (d->flags & SND_F_ABORTING) {
d->flags |= ~SND_F_ABORTING;
splx(s);
dsp_rdabort(d);
}
splx(s) ;
return ret ;
}
/*
* short routine to initialize a dma buffer descriptor (usually
* located in the XXX_desc structure). The first parameter is
* the buffer size, the second one specifies that a 16-bit dma channel
* is used (hence the buffer must be properly aligned).
*/
void
alloc_dbuf(snd_dbuf *b, int size, int chan)
{
if (size > 0x10000)
panic("max supported size is 64k");
b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
0ul, 0xfffffful, 1ul, 0x10000ul);
/* should check that it does not fail... */
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->bufsize = b->fl = size ;
}
void
reset_dbuf(snd_dbuf *b)
{
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->fl = b->bufsize ;
}
/*
* snd_sync waits until the space in the given channel goes above
* a threshold. chan = 1 : play, 2: capture. The threshold is
* checked against fl or rl respectively.
* Assume that the condition can become true, do not check here...
*/
int
snd_sync(snddev_info *d, int chan, int threshold)
{
u_long s;
int ret;
snd_dbuf *b;
b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
for (;;) {
s=spltty();
if ( chan==1 )
dsp_wr_dmaupdate(d);
else
dsp_rd_dmaupdate(d);
if ( (chan == 1 && b->fl <= threshold) ||
(chan == 2 && b->rl <= threshold) ) {
ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
splx(s);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
} else
break;
}
splx(s);
return 0 ;
}
/*
* dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
* which abort a pending DMA transfer and flush the buffers.
*/
int
dsp_wrabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_out) ;
s = spltty();
if ( d->flags & SND_F_WR_DMA ) {
d->flags &= ~ ( SND_F_WR_DMA | SND_F_WRITING ) ;
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_ABORT);
missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
b->rl += missing ; /* which are part of the ready area */
b->rp -= missing ;
if (b->rp < 0)
b->rp += b->bufsize;
DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_wr_dmadone(d);
missing = b->rl;
}
reset_dbuf(b);
splx(s);
return missing;
}
int
dsp_rdabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_in) ;
s = spltty();
if ( d->flags & SND_F_RD_DMA ) {
d->flags &= ~ ( SND_F_RD_DMA | SND_F_READING ) ;
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_ABORT);
missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
b->fl += missing ; /* which are part of the free area */
b->fp -= missing ;
if (b->fp < 0)
b->fp += b->bufsize;
DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_rd_dmadone(d);
missing = b->rl ;
}
reset_dbuf(b);
splx(s);
return missing;
}
/*
* this routine tries to flush the dma transfer. It is called
* on a close. The caller must set SND_F_CLOSING, and insure that
* interrupts are enabled. We immediately abort any read DMA
* operation, and then wait for the play buffer to drain.
*/
int
snd_flush(snddev_info *d)
{
int ret, res, res1;
int count=10;
DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
dsp_rdabort(d);
if ( d->flags & SND_F_WR_DMA ) {
/* close write */
while ( d->flags & SND_F_WR_DMA ) {
/*
* still pending output data.
*/
ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
if ( ret && --count == 0) {
printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
isa_dmastatus1(d->dma1), d->flags);
return -1 ;
}
}
d->flags &= ~SND_F_CLOSING ;
}
reset_dbuf(& (d->dbuf_out) );
return 0 ;
}
/*
* end of new code for dma buffer handling
*/

View file

@ -143,90 +143,89 @@ ahead.
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
#define MSS_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
* Following there is a macro ...MIXER_DEVICES which is a bitmap
* of all non-zero fields in the table.
* MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
* registers I0..I15)
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
mixer_ent mix_devices[32][2] = {
MIX_NONE(SOUND_MIXER_VOLUME),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_NONE(SOUND_MIXER_LINE1),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
/*
* entries for the opti931...
*/
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_IMIX),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */

View file

@ -11,13 +11,14 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@ -112,7 +113,7 @@ snddev_info sb_op_desc = {
* resetting the dsp and testing if it is there.
* Version detection etc. will be done at attach time.
*
* Remebber, isa probe routines are supposed to return the
* Remember, ISA probe routines are supposed to return the
* size of io space used.
*/
@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev)
{
snddev_info *d = &pcm_info[dev->id_unit] ;
dev->id_alive = 16 ; /* number of io ports */
/* should be already set but just in case... */
sb_dsp_init(d, dev);
return 0 ;
}
@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->rsel.si_pid = 0;
d->rsel.si_flags = 0;
d->esel.si_pid = 0;
d->esel.si_flags = 0;
d->flags = 0 ;
d->bd_flags &= ~BD_F_HISPEED ;
@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
if (flags & O_NONBLOCK)
d->flags |= SND_F_NBIO ;
reset_dbuf(& (d->dbuf_in) );
reset_dbuf(& (d->dbuf_out) );
sb_reset_dsp(d->io_base);
ask_init(d);
@ -287,17 +285,18 @@ again:
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( d->dbuf_out.dl && (reason & 1) )
dsp_wrintr(d);
if ( d->dbuf_in.dl && (reason & 2) )
dsp_rdintr(d);
if ( c & 2 )
inb(DSP_DATA_AVL16); /* 16-bit int ack */
if (c & 1)
inb(DSP_DATA_AVAIL); /* 8-bit int ack */
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
dsp_wrintr(d);
if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
dsp_rdintr(d);
/*
* the sb16 might have multiple sources etc.
*/
@ -320,7 +319,7 @@ static int
sb_callback(snddev_info *d, int reason)
{
int rd = reason & SND_CB_RD ;
int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ;
switch (reason & SND_CB_REASON_MASK) {
case SND_CB_INIT : /* called with int enabled and no pending io */
@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason)
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
return 1;
break ;
break;
case SND_CB_START : /* called with int disabled */
sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
d->flags &= ~SND_F_INIT ;
if (d->bd_flags & BD_F_SB16) {
/* the SB16 can do full duplex using one 16-bit channel
* and one 8-bit channel. It needs to be programmed to
* use split format though.
* We use the following algorithm:
* 1. check which direction(s) are active;
* 2. check if we should swap dma channels
* 3. check if we can do the swap.
*/
int b16 ;
int swap = 0 ;
int swap = 1 ; /* default... */
b16 = (rd) ? d->rec_fmt : d->play_fmt ;
b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
/*
* check if I have to swap dma channels. Swap if
* - !rd, dma1 <4, b16
* - !rd, dma1 >=4, !b16
* - rd, dma2 <4, b16
* - rd, dma2 >=4, !b16
*/
if (!rd) {
if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
if (rd) {
if (d->flags & SND_F_WRITING || d->dbuf_out.dl)
swap = 0;
if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4)
swap = 0;
if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4)
swap = 0;
} else {
if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
if (d->flags & SND_F_READING || d->dbuf_in.dl)
swap = 0;
if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4)
swap = 0;
if (d->play_fmt != AFMT_S16_LE && d->dma1 <4)
swap = 0;
}
/*
* before swapping should make sure that there is no
* pending DMA on the other channel...
*/
if (swap) {
int c = d->dma2 ;
d->dma2 = d->dma1;
d->dma1 = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
DEB(printf("START dma chan: play %d, rec %d\n",
d->dma1, d->dma2));
}
DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n",
d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
}
/* fallthrough */
case SND_CB_RESTART:
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
/*
* SB16 support still not completely working!!!
*
* in principle, on the SB16, I could support simultaneous
* play & rec.
* However, there is no way to ask explicitly for 8 or
* 16 bit transfer. As a consequence, if we do 8-bit,
* we need to use the 8-bit channel, and if we do 16-bit,
* we need to use the other one. The only way I find to
* do this is to swap d->dma1 and d->dma2 ...
*
*/
if (rd) {
c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_ADC ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->rec_fmt == AFMT_S16_LE)
l /= 2 ;
} else {
c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
DSP_F16_AUTO |
DSP_F16_FIFO_ON | DSP_F16_DAC ;
c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
if (d->play_fmt == AFMT_S16_LE)
l /= 2 ;
@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, c );
sb_cmd3(d->io_base, c1 , l - 1) ;
} else {
/* code for the SB2 and SB3 */
u_char c ;
if (d->bd_flags & BD_F_HISPEED)
c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
else
c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
sb_cmd3(d->io_base, c , l - 1) ;
}
break;
case SND_CB_ABORT : /* XXX */
case SND_CB_STOP :
/* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if (d->bd_flags & BD_F_SB16) {
if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) )
cmd = DSP_CMD_DMAPAUSE_16 ;
}
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
} else {
sb_cmd(d->io_base, cmd); /* pause dma. */
/*
* This seems to have the side effect of blocking the other
* side as well so I have to re-enable it :(
*/
if ( (rd && d->dbuf_out.dl) ||
(!rd && d->dbuf_in.dl) )
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
}
@ -443,7 +459,7 @@ sb_reset_dsp(int io_base)
DELAY(30);
if (inb(DSP_READ) != 0xAA) {
DEB(printf("sb_reset_dsp failed\n"));
DEB(printf("sb_reset_dsp 0x%x failed\n", io_base));
return 0; /* Sorry */
}
return 1;
@ -753,13 +769,13 @@ dsp_speed(snddev_info *d)
* Now the speed should be valid. Compute the value to be
* programmed into the board.
*
* XXX check this code...
* XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
int tmp;
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ;
d->bd_flags |= BD_F_HISPEED ;
flags = spltty();
@ -1022,19 +1038,18 @@ static char *
sb16pnp_probe(u_long csn, u_long vend_id)
{
char *s = NULL ;
if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (vend_id == 0x2b008c0e)
s = "SB16 Value PnP" ;
/*
* The SB16/AWE64 cards seem to differ in the fourth byte of
* The SB16/AWExx cards seem to differ in the fourth byte of
* the vendor id, so I have just masked it for the time being...
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB AWE64 PnP";
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);

View file

@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ;
* DSP Commands. There are many, and in many cases they are used explicitly
*/
/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_ADC8 0x20 /* direct ADC input */
/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
/* these should be used in the SB 2.0 and 2.01 */
#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
/* SB16 commands */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ;
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_DMAPAUSE_8 0xD0
#define DSP_CMD_DMAPAUSE_16 0xD5
#define DSP_CMD_DMAEXIT_8 0xDA
#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif

View file

@ -1,5 +1,5 @@
/*
* sound/sound.c
* snd/sound.c
*
* Main sound driver for FreeBSD. This file provides the main
* entry points for probe/attach and all i/o demultiplexing, including
@ -31,15 +31,15 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* For each board type a template "snddev_info" structure contains
* For each card type a template "snddev_info" structure contains
* all the relevant parameters, both for configuration and runtime.
*
* In this file we build tables of pointers to the descriptors for
* the various supported boards. The generic probe routine scans
* the various supported cards. The generic probe routine scans
* the table(s) looking for a matching entry, then invokes the
* board-specific probe routine. If successful, a pointer to the
* correct snddev_info is stored in snddev_last_probed, for subsequent
* use in the attach routine. The generic attach routine copies
* use in the attach routine. The generic attach routine copies
* the template to a permanent descriptor (pcm_info[unit] and
* friends), initializes all generic parameters, and calls the
* board-specific attach routine.
@ -51,15 +51,9 @@
*/
#include <i386/isa/snd/sound.h>
#include <sys/poll.h>
#if NPCM > 0 /* from "snd.h" */
#include "snd.h"
#if NSND > 0
#error Can't have both Luigi's (pcm0) and voxware (snd0) at the same time.
#endif
#define SNDSTAT_BUF_SIZE 4000
static char status_buf[SNDSTAT_BUF_SIZE] ;
static int status_len = 0 ;
@ -97,12 +91,11 @@ u_long nsnd = NPCM ; /* total number of sound devices */
*/
snddev_info *snddev_last_probed = NULL ;
static void print_isadev_info(struct isa_device *d, char *s);
static snddev_info *
generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s);
/*
* here are the lists of known devices. Similar devices (e.g. all
* here are the lists of known cards. Similar cards (e.g. all
* sb clones, all mss clones, ... are in the same array.
* All lists of devices of the same type (eg. all pcm, all midi...)
* are in the same array.
@ -116,19 +109,17 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s);
extern snddev_info sb_op_desc;
extern snddev_info mss_op_desc;
static snddev_info *sb_devs[] = {
static snddev_info *sb_devs[] = { /* all SB clones */
&sb_op_desc,
NULL,
} ;
static snddev_info *mss_devs[] = {
/* MSS-like devices */
static snddev_info *mss_devs[] = { /* all WSS clones */
&mss_op_desc,
NULL,
} ;
static snddev_info **pcm_devslist[] = {
/* all pcm devices */
static snddev_info **pcm_devslist[] = { /* all pcm devices */
mss_devs,
sb_devs,
NULL
@ -142,8 +133,7 @@ pcmprobe(struct isa_device * dev)
return generic_snd_probe(dev, pcm_devslist, "pcm") ? 1 : 0 ;
}
static snddev_info **midi_devslist[] = {
/* all midi devices */
static snddev_info **midi_devslist[] = {/* all midi devices */
NULL
} ;
@ -205,8 +195,8 @@ pcmattach(struct isa_device * dev)
* revisions, if we see that we have a single dma, we might decide
* to use a single buffer to save memory.
*/
alloc_dbuf( &(d->dbuf_out), d->bufsize, d->dma1 );
alloc_dbuf( &(d->dbuf_in), d->bufsize, d->dma2 );
alloc_dbuf( &(d->dbuf_out), d->bufsize );
alloc_dbuf( &(d->dbuf_in), d->bufsize );
isa_dma_acquire(d->dma1);
if (d->dma2 != d->dma1)
@ -237,6 +227,7 @@ pcmattach(struct isa_device * dev)
dev->id_id = dvp->id_id;
}
d->magic = MAGIC(dev->id_unit); /* debugging... */
/*
* and finally, call the device attach routine
*/
@ -267,20 +258,6 @@ pcmintr(int unit)
synth_info[unit].isr(unit);
}
static void
print_isadev_info(struct isa_device *d, char *s)
{
if (d == NULL )
return ;
printf("%s%d at 0x%x irq %d drq %d mem %p flags 0x%x en %d confl %d\n",
d->id_driver ? d->id_driver->name : "NONAME",
d->id_unit,
(u_short)(d->id_iobase), ffs(d->id_irq) - 1 ,
d->id_drq, d->id_maddr, d->id_flags,
d->id_enabled, d->id_conflicts);
}
static snddev_info *
generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s)
{
@ -289,10 +266,11 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s)
snddev_last_probed = NULL ;
print_isadev_info(dev, s);
saved_dev = *dev ; /* the probe routine might alter parameters */
/*
* XXX todo: should try to match flags with device type.
*/
for ( ; p[0] != NULL ; p++ )
for ( q = *p ; q[0] ; q++ )
if (q[0]->probe && q[0]->probe(dev))
@ -474,13 +452,13 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
DDB(printf("read denied, half duplex and a writer is in\n"));
return EBUSY ;
}
while ( d->flags & SND_F_WR_DMA ) {
while ( d->dbuf_out.dl ) {
/*
* we have a pending dma operation, post a read request
* and wait for the write to complete.
*/
d->flags |= SND_F_READING ;
DDB(printf("sndread: sleeping waiting for write to end\n"));
DEB(printf("sndread: sleeping waiting for write to end\n"));
ret = tsleep( (caddr_t)&(d->dbuf_out),
PRIBIO | PCATCH , "sndrdw", hz ) ;
if (ret == ERESTART || ret == EINTR) {
@ -545,7 +523,7 @@ sndwrite(dev_t i_dev, struct uio * buf, int flag)
splx(s);
return EBUSY ;
}
while ( d->flags & SND_F_RD_DMA ) {
while ( d->dbuf_in.dl ) {
/*
* we have a pending read dma. Post a write request
* and wait for the read to complete (in fact I could
@ -636,8 +614,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
* we start with the new ioctl interface.
*/
case AIONWRITE : /* how many bytes can write ? */
if (d->flags & SND_F_WR_DMA)
dsp_wr_dmaupdate(d);
if (d->dbuf_out.dl)
dsp_wr_dmaupdate(&(d->dbuf_out));
*(int *)arg = d->dbuf_out.fl;
break;
@ -647,9 +625,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */
d->flags &= ~SND_F_HAS_SIZE ;
} else {
RANGE (p->play_size, 40, d->bufsize /4);
RANGE (p->play_size, 40, d->dbuf_out.bufsize /4);
d->play_blocksize = p->play_size & ~3 ;
RANGE (p->rec_size, 40, d->bufsize /4);
RANGE (p->rec_size, 40, d->dbuf_in.bufsize /4);
d->rec_blocksize = p->rec_size & ~3 ;
d->flags |= SND_F_HAS_SIZE ;
}
@ -668,9 +646,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
case AIOSFMT :
{
snd_chan_param *p = (snd_chan_param *)arg;
/*
* at the moment, only support same format on play & rec
*/
d->play_speed = p->play_rate;
d->rec_speed = p->play_rate; /* XXX */
if (p->play_format & SND_F_STEREO)
@ -715,9 +690,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
case AIOSTOP:
if (*(int *)arg == AIOSYNC_PLAY) /* play */
*(int *)arg = dsp_wrabort(d);
*(int *)arg = dsp_wrabort(d, 1 /* restart */);
else if (*(int *)arg == AIOSYNC_CAPTURE)
*(int *)arg = dsp_rdabort(d);
*(int *)arg = dsp_rdabort(d, 1 /* restart */);
else {
splx(s);
printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg);
@ -734,13 +709,13 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
* here follow the standard ioctls (filio.h etc.)
*/
case FIONREAD : /* get # bytes to read */
if ( d->flags & SND_F_RD_DMA )
dsp_rd_dmaupdate(d);
if ( d->dbuf_in.dl )
dsp_rd_dmaupdate(&(d->dbuf_in));
*(int *)arg = d->dbuf_in.rl;
break;
case FIOASYNC: /* set/clear async i/o */
/* do nothing, this is called from kern_descrip.c for fcntl() */
case FIOASYNC: /*set/clear async i/o */
printf("FIOASYNC\n");
break;
case SNDCTL_DSP_NONBLOCK :
@ -764,7 +739,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
if (t <= 1) { /* means no blocks */
d->flags &= ~SND_F_HAS_SIZE ;
} else {
RANGE (t, 40, d->bufsize /4);
RANGE (t, 40, d->dbuf_out.bufsize /4);
d->play_blocksize =
d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */
d->flags |= SND_F_HAS_SIZE ;
@ -774,15 +749,15 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
ask_init(d);
break ;
case SNDCTL_DSP_RESET:
DDB(printf("dsp reset\n"));
dsp_wrabort(d);
dsp_rdabort(d);
DEB(printf("dsp reset\n"));
dsp_wrabort(d, 1 /* restart */);
dsp_rdabort(d, 1 /* restart */);
break ;
case SNDCTL_DSP_SYNC:
DDB(printf("dsp sync\n"));
DEB(printf("dsp sync\n"));
splx(s);
snd_sync(d, 1, d->bufsize - 4); /* DMA does not start with <4 bytes */
snd_sync(d, 1, d->dbuf_out.bufsize - 4); /* DMA does not start with <4 bytes */
break ;
case SNDCTL_DSP_SPEED:
@ -793,6 +768,10 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SNDCTL_DSP_STEREO:
if ( *(int *)arg == -1) {
*(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ;
break;
}
if ( *(int *)arg == 0 )
d->flags &= ~SND_F_STEREO ; /* mono */
else if ( *(int *)arg == 1 )
@ -807,7 +786,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SOUND_PCM_WRITE_CHANNELS:
printf("dsp write channels %d\n", *(int *)arg);
if ( *(int *)arg == 1)
d->flags &= ~SND_F_STEREO ; /* mono */
else if ( *(int *)arg == 2)
@ -875,8 +853,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
((audio_buf_info *)arg)->bytes = d->dbuf_in.fl ;
((audio_buf_info *)arg)->fragments = 1 ;
((audio_buf_info *)arg)->fragstotal =
d->bufsize / d->play_blocksize ;
((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
d->dbuf_in.bufsize / d->rec_blocksize ;
((audio_buf_info *)arg)->fragsize = d->rec_blocksize ;
break ;
case SNDCTL_DSP_GETOSPACE:
@ -884,7 +862,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
((audio_buf_info *)arg)->bytes = d->dbuf_out.fl ;
((audio_buf_info *)arg)->fragments = 1 ;
((audio_buf_info *)arg)->fragstotal =
d->bufsize / d->play_blocksize ;
d->dbuf_out.bufsize / d->play_blocksize ;
((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
break ;
@ -916,6 +894,14 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
*(int *)arg = d->mix_recsrc ;
break;
case SOUND_MIXER_READ_CAPS :
/*
* XXX - This needs to be fixed when the sound driver actually
* supports multiple inputs.
*/
*(int *)arg = SOUND_CAP_EXCL_INPUT ;
break;
default:
DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n",
unit, dev & 0xf, cmd));
@ -926,72 +912,80 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
return ret ;
}
/*
* function to poll what is currently available. Used to be select.
*/
int
sndpoll(dev_t i_dev, int events, struct proc *p)
{
int lim ;
int revents = 0;
int dev, unit, c = 1 /* default: success */ ;
snddev_info *d ;
u_long flags;
dev = minor(i_dev);
d = get_snddev_info(dev, &unit);
DEB(printf("sndpoll dev 0x%04x rw 0x%08x\n",i_dev, events));
DEB(printf("sndpoll dev 0x%04x events 0x%08x\n",i_dev, events));
if (d == NULL ) {
printf("select: unit %d not configured\n", unit );
return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM))
| POLLHUP);
printf("poll: unit %d not configured\n", unit );
return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
}
if (d->poll == NULL)
/* is this correct hear? */
return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM))
| POLLHUP);
return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
else if (d->poll != sndpoll )
return d->poll(i_dev, events, p);
else {
/* handle it here with the generic code */
/* handle it here with the generic code */
int lim ;
int revents = 0 ;
/*
* if the user selected a block size, then we want to use the
* device as a block device, and select will return ready when
* we have a full block.
* In all other cases, select will return when 1 byte is ready.
*/
lim = 1;
flags = spltty();
/* XXX fix the test here for half duplex devices */
if (events & (POLLOUT | POLLWRNORM)) {
if ( d->flags & SND_F_HAS_SIZE )
lim = d->play_blocksize ;
if (d->flags & SND_F_WR_DMA)
dsp_wr_dmaupdate(d);
c = d->dbuf_out.fl ;
if (c < lim) /* no space available */
selrecord(p, & (d->wsel));
else
revents |= events & (POLLOUT | POLLWRNORM);
/*
* if the user selected a block size, then we want to use the
* device as a block device, and select will return ready when
* we have a full block.
* In all other cases, select will return when 1 byte is ready.
*/
lim = 1;
if (events & (POLLOUT | POLLWRNORM) ) {
if ( d->flags & SND_F_HAS_SIZE )
lim = d->play_blocksize ;
/* XXX fix the test here for half duplex devices */
if (1 /* write is compatible with current mode */) {
flags = spltty();
if (d->dbuf_out.dl)
dsp_wr_dmaupdate(&(d->dbuf_out));
c = d->dbuf_out.fl ;
if (c < lim) /* no space available */
selrecord(p, & (d->wsel));
else
revents |= events & (POLLOUT | POLLWRNORM);
splx(flags);
}
}
if (events & (POLLIN | POLLRDNORM)) {
if ( d->flags & SND_F_HAS_SIZE )
lim = d->rec_blocksize ;
/* XXX fix the test here */
if (1 /* read is compatible with current mode */) {
flags = spltty();
if ( d->dbuf_in.dl == 0 ) /* dma idle, restart it */
dsp_rdintr(d);
else
dsp_rd_dmaupdate(&(d->dbuf_in));
c = d->dbuf_in.rl ;
if (c < lim) /* no data available */
selrecord(p, & (d->rsel));
else
revents |= events & (POLLIN | POLLRDNORM);
splx(flags);
}
DEB(printf("sndpoll on read: %d >= %d flags 0x%08x\n",
c, lim, d->flags));
return c < lim ? 0 : 1 ;
}
return revents;
}
/* XXX fix the test here */
if (events & (POLLIN | POLLRDNORM)) {
if ( d->flags & SND_F_HAS_SIZE )
lim = d->rec_blocksize ;
if ( !(d->flags & SND_F_RD_DMA) ) /* dma idle, restart it */
dsp_rdintr(d);
else
dsp_rd_dmaupdate(d);
c = d->dbuf_in.rl ;
if (c < lim) /* no data available */
selrecord(p, & (d->rsel));
else
revents |= events & (POLLIN | POLLRDNORM);
DEB(printf("sndpoll on read: %d >= %d flags 0x%08lx\n", c, lim,
d->flags));
}
splx(flags);
return revents;
return ENXIO ; /* notreached */
}
/*
@ -1020,23 +1014,22 @@ sndmmap(dev_t dev, int offset, int nprot)
{
snddev_info *d = get_snddev_info(dev, NULL);
DEB(printf("sndmmap d %p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
d, dev, offset, nprot));
if (d == NULL || nprot & PROT_EXEC)
return -1 ; /* forbidden */
if (offset > d->bufsize && (nprot & PROT_WRITE) )
if (offset >= d->dbuf_out.bufsize && (nprot & PROT_WRITE) )
return -1 ; /* can only write to the first block */
if (offset < d->bufsize)
if (offset < d->dbuf_out.bufsize)
return i386_btop(vtophys(d->dbuf_out.buf + offset));
offset -= 1 << 24;
if ( (offset >= 0) && (offset < d->bufsize))
if ( (offset >= 0) && (offset < d->dbuf_in.bufsize))
return i386_btop(vtophys(d->dbuf_in.buf + offset));
offset -= 1 << 24;
if ( (offset >= 0) && (offset < 0x2000)) {
printf("name %s\n", d->name);
return i386_btop(vtophys( ((int)d & ~0xfff) + offset));
}
return -1 ;
@ -1060,7 +1053,8 @@ ask_init(snddev_info *d)
if ( d->callback == NULL )
return 0 ;
s = spltty();
if ( d->flags & SND_F_PENDING_IO ) {
if ( d->flags & SND_F_PENDING_IO ||
d->dbuf_out.dl || d->dbuf_in.dl ) {
/* cannot do it now, record the request and return */
d->flags |= SND_F_INIT ;
splx(s);
@ -1093,7 +1087,7 @@ init_status(snddev_info *d)
if (status_len != 0) /* only do init once */
return ;
sprintf(status_buf,
"FreeBSD Sound Driver " __DATE__ " " __TIME__ "\n"
"FreeBSD Audio Driver (971023) " __DATE__ " " __TIME__ "\n"
"Installed devices:\n");
for (i = 0; i < NPCM_MAX; i++) {
@ -1165,22 +1159,28 @@ snd_conflict(int io_base)
void
snd_set_blocksize(snddev_info *d)
{
int tmp ;
/*
* now set the blocksize so as to guarantee approx 1/4s
* compute the sample size, and possibly
* set the blocksize so as to guarantee approx 1/4s
* between callbacks.
*/
tmp = 1 ;
if (d->flags & SND_F_STEREO) tmp += tmp;
if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
d->dbuf_out.sample_size = tmp ;
tmp = tmp * d->play_speed;
if ( (d->flags & SND_F_HAS_SIZE) == 0) {
/* make blocksize adaptive to 250ms or max 1/4 of the buf */
int tmp ;
tmp = d->play_speed;
if (d->flags & SND_F_STEREO) tmp += tmp;
if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3);
}
tmp = d->rec_speed;
if (d->flags & SND_F_STEREO) tmp += tmp;
if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
tmp = 1 ;
if (d->flags & SND_F_STEREO) tmp += tmp;
if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
tmp = tmp * d->rec_speed;
d->dbuf_in.sample_size = tmp ;
if ( (d->flags & SND_F_HAS_SIZE) == 0) {
d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3);
}

View file

@ -28,7 +28,11 @@
*
*/
#ifdef KERNEL
#include "pcm.h"
#else
#define NPCM 1
#endif
#if NPCM > 0
/*
@ -38,6 +42,7 @@
#ifndef _OS_H_
#define _OS_H_
#ifdef KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
@ -57,10 +62,20 @@
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/poll.h>
#include <i386/isa/isa_device.h>
#include <machine/clock.h> /* for DELAY */
#else
struct isa_device { int dummy ; } ;
#define d_open_t void
#define d_close_t void
#define d_read_t void
#define d_write_t void
#define d_ioctl_t void
#define d_poll_t void
#endif /* KERNEL */
typedef void (irq_proc_t) (int irq);
#endif /* _OS_H_ */
@ -68,19 +83,20 @@ typedef void (irq_proc_t) (int irq);
/*
* descriptor of a dma buffer. See dmabuf.c for documentation.
* (rp,rl) and (fp,fl) identify the READY and FREE regions of the
* buffer. (dp,dl) identify the region currently used by the DMA.
* Only dmabuf.c should test the value of dl. The reason is that
* in auto-dma mode looking at dl alone does not make much sense,
* since the transfer potentially spans the entire buffer.
* buffer. dl contains the length used for dma transfer, dl>0 also
* means that the channel is busy and there is a DMA transfer in progress.
*/
typedef struct _snd_dbuf {
char *buf;
int bufsize ;
volatile int dp, rp, fp;
volatile int dl, rl, fl;
volatile int dl0; /* value used last time in dl */
volatile int rp, fp; /* pointers to the ready and free area */
volatile int dl; /* transfer size */
volatile int rl, fl; /* lenght of ready and free areas. */
int int_count;
int chan; /* dma channel */
int sample_size ; /* 1, 2, 4 */
struct selinfo sel;
} snd_dbuf ;
/*
@ -125,7 +141,6 @@ struct _snddev_info {
#define SND_CB_WR 0x200 /* write callback */
#define SND_CB_REASON_MASK 0xff
#define SND_CB_START 0x01 /* start dma op */
#define SND_CB_RESTART 0x02 /* restart dma op */
#define SND_CB_STOP 0x03 /* stop dma op */
#define SND_CB_ABORT 0x04 /* abort dma op */
#define SND_CB_INIT 0x05 /* init board parameters */
@ -145,8 +160,8 @@ struct _snddev_info {
int synth_base ; /* base for the synth */
int irq ;
int dma1, dma2 ; /* dma2=dma1 for half-duplex cards */
#define dma1 dbuf_out.chan
#define dma2 dbuf_in.chan
int bd_id ; /* used to hold board-id info, eg. sb version,
* mss codec type, etc. etc.
*/
@ -198,12 +213,14 @@ struct _snddev_info {
* you might get interrupts, so some manipulations of the
* descriptors must be done with interrupts blocked.
*/
#if 0
#define SND_F_RD_DMA 0x0010 /* read-dma active */
#define SND_F_WR_DMA 0x0020 /* write-dma active */
#define SND_F_PENDING_IN (SND_F_READING | SND_F_RD_DMA)
#define SND_F_PENDING_OUT (SND_F_WRITING | SND_F_WR_DMA)
#define SND_F_PENDING_IO (SND_F_PENDING_IN | SND_F_PENDING_OUT)
#endif
#define SND_F_PENDING_IO (SND_F_READING | SND_F_WRITING)
/*
* flag used to mark a pending close.
@ -242,7 +259,6 @@ struct _snddev_info {
* be done at the next convenient time.
*/
#define SND_F_INIT 0x4000 /* changed parameters. need init */
#define SND_F_AUTO_DMA 0x8000 /* use auto-dma */
u_long bd_flags; /* board-specific flags */
int play_speed, rec_speed;
@ -258,8 +274,11 @@ struct _snddev_info {
u_long mix_recsrc; /* current recording source(s) */
u_short mix_levels[32];
struct selinfo wsel, rsel, esel ;
#define wsel dbuf_out.sel
#define rsel dbuf_in.sel
u_long interrupts; /* counter of interrupts */
u_long magic;
#define MAGIC(unit) ( 0xa4d10de0 + unit )
void *device_data ; /* just in case it is needed...*/
} ;
@ -310,13 +329,20 @@ struct _snddev_info {
#define MD_CS4232 0xA4
#define MD_CS4232A 0xA5
#define MD_CS4236 0xA6
#define MD_CS4237 0xA7
#define MD_OPTI931 0xB1
#define MD_GUSPNP 0xB8
#define MD_YM0020 0xC1
#define MD_VIVO 0xD1
/*
* TODO: add some card classes rather than specific types.
*/
#ifdef KERNEL
#include <i386/isa/snd/soundcard.h>
#else
#include </sys/i386/isa/snd/soundcard.h>
#endif
/*
* many variables should be reduced to a range. Here define a macro
*/
@ -327,14 +353,17 @@ struct _snddev_info {
/*
* finally, all default parameters
*/
#define DSP_BUFFSIZE 65536 /* XXX */
#define DSP_BUFFSIZE (65536 - 256) /* XXX */
/*
* the last 256 bytes are room for buggy soundcard to overflow.
*/
#if 1 /* prepare for pnp support! */
#ifdef KERNEL
#include "pnp.h"
#if NPNP > 0
#include <i386/isa/pnp.h> /* XXX pnp support */
#endif
#endif
#endif /* KERNEL */
/*
* Minor numbers for the sound driver.
@ -395,11 +424,15 @@ struct mixer_def {
typedef struct mixer_def mixer_ent;
typedef struct mixer_def mixer_tab[32][2];
#ifdef KERNEL
#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
{{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
{{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)
#define DDB(x) x /* XXX */
#ifndef DEB
@ -431,14 +464,23 @@ void dsp_wrintr(snddev_info *d);
void dsp_rdintr(snddev_info *d);
int dsp_write_body(snddev_info *d, struct uio *buf);
int dsp_read_body(snddev_info *d, struct uio *buf);
void alloc_dbuf(snd_dbuf *d, int size, int b16);
void reset_dbuf(snd_dbuf *b);
void alloc_dbuf(snd_dbuf *d, int size);
int snd_flush(snddev_info *d);
/* the following parameters are used in snd_sync and reset_dbuf
* to decide whether or not to restart a channel
*/
#define SND_CHAN_NONE 0x0
#define SND_CHAN_WR 0x1
#define SND_CHAN_RD 0x2
void reset_dbuf(snd_dbuf *b, int chan);
int snd_sync(snddev_info *d, int chan, int threshold);
int dsp_wrabort(snddev_info *d);
int dsp_rdabort(snddev_info *d);
void dsp_wr_dmaupdate(snddev_info *d);
void dsp_rd_dmaupdate(snddev_info *d);
int dsp_wrabort(snddev_info *d, int restart);
int dsp_rdabort(snddev_info *d, int restart);
void dsp_wr_dmaupdate(snd_dbuf *b);
void dsp_rd_dmaupdate(snd_dbuf *b);
d_poll_t sndpoll;
@ -463,6 +505,7 @@ int sb_reset_dsp (int io_base);
void sb_setmixer (int io_base, u_int port, u_int value);
int sb_getmixer (int io_base, u_int port);
#endif /* KERNEL */
/*
* usage of flags in device config entry (config file)