mirror of
https://github.com/opnsense/src.git
synced 2026-06-08 16:22:46 -04:00
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:
parent
95b6073cd5
commit
faac9650a5
19 changed files with 2171 additions and 2416 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue