summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/sound/sb_dsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/isa/sound/sb_dsp.c')
-rw-r--r--sys/i386/isa/sound/sb_dsp.c1083
1 files changed, 252 insertions, 831 deletions
diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c
index 4c27364..2ed058f 100644
--- a/sys/i386/isa/sound/sb_dsp.c
+++ b/sys/i386/isa/sound/sb_dsp.c
@@ -1,7 +1,7 @@
/*
- * linux/kernel/chr_drv/sound/sb_dsp.c
+ * sound/sb_dsp.c
*
- * The low level driver for the SoundBlaster DS chips.
+ * The low level driver for the SoundBlaster DSP chip.
*
* Copyright by Hannu Savolainen 1993
*
@@ -25,152 +25,75 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen
- * <shaehnic@ucsd.edu>
*/
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+#include "sb.h"
+#include "sb_mixer.h"
#undef SB_TEST_IRQ
-#define DSP_RESET (sbc_base + 0x6)
-#define DSP_READ (sbc_base + 0xA)
-#define DSP_WRITE (sbc_base + 0xC)
-#define DSP_COMMAND (sbc_base + 0xC)
-#define DSP_STATUS (sbc_base + 0xC)
-#define DSP_DATA_AVAIL (sbc_base + 0xE)
-#define MIXER_ADDR (sbc_base + 0x4)
-#define MIXER_DATA (sbc_base + 0x5)
-#define OPL3_LEFT (sbc_base + 0x0)
-#define OPL3_RIGHT (sbc_base + 0x2)
-#define OPL3_BOTH (sbc_base + 0x8)
-
-static int sbc_base = 0;
+int sbc_base = 0;
static int sbc_irq = 0;
-#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_VOLUME)
-
-/*
- * Mixer registers
- *
- * NOTE! RECORD_SRC == IN_FILTER
- */
-
-#define VOC_VOL 0x04
-#define MIC_VOL 0x0A
-#define MIC_MIX 0x0A
-#define RECORD_SRC 0x0C
-#define IN_FILTER 0x0C
-#define OUT_FILTER 0x0E
-#define MASTER_VOL 0x22
-#define FM_VOL 0x26
-#define CD_VOL 0x28
-#define LINE_VOL 0x2E
-
-#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
-#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
-#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
-#define FILT_OFF (1 << 5)
-
-/* Convenient byte masks */
-#define B1(x) ((x) & 0x01)
-#define B2(x) ((x) & 0x03)
-#define B3(x) ((x) & 0x07)
-#define B4(x) ((x) & 0x0f)
-#define B5(x) ((x) & 0x1f)
-#define B6(x) ((x) & 0x3f)
-#define B7(x) ((x) & 0x7f)
-#define B8(x) ((x) & 0xff)
-#define F(x) (!!(x)) /* 0 or 1 only */
-
-#define MONO_DAC 0x00
-#define STEREO_DAC 0x02
-
-/* DSP Commands */
-
-#define DSP_CMD_SPKON 0xD1
-#define DSP_CMD_SPKOFF 0xD3
-
/*
* The DSP channel can be used either for input or output. Variable
- * 'irq_mode' will be set when the program calls read or write first time
+ * 'sb_irq_mode' will be set when the program calls read or write first time
* after open. Current version doesn't support mode changes without closing
* and reopening the device. Support for this feature may be implemented in a
* future version of this driver.
*/
-#define IMODE_NONE 0
-#define IMODE_OUTPUT 1
-#define IMODE_INPUT 2
-#define IMODE_INIT 3
-#define IMODE_MIDI 4
-
-#define NORMAL_MIDI 0
-#define UART_MIDI 1
-
-static int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
+int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
static int midi_disabled = 0;
-static int dsp_highspeed = 0, dsp_stereo = 0;
+int sb_dsp_highspeed = 0;
+static int major = 1, minor = 0; /* DSP version */
+static int dsp_stereo = 0;
static int dsp_current_speed = DSP_DEFAULT_SPEED;
+static int sb16 = 0;
+static int irq_verified = 0;
-#ifndef EXCLUDE_SBPRO
-static int rec_devices = SOUND_MASK_MIC;
-static int hi_filter = 0, filter_in = 0, filter_out = 0;
-
-#endif
+int sb_midi_mode = NORMAL_MIDI;
+int sb_midi_busy = 0; /* 1 if the process has output to MIDI */
+int sb_dsp_busy = 0;
-static int midi_mode = NORMAL_MIDI;
-static int midi_busy = 0; /* 1 if the process has output to MIDI */
-static int dsp_busy = 0;
+volatile int sb_irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
-static volatile int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
* or IMODE_NONE */
static volatile int irq_ok = 0;
-static int dsp_model = 1; /* DSP version */
-static int dsp_mono = 1; /* 1 SB, 0 SB Pro */
-static int duplex_midi = 0;
+int sb_dsp_model = 1; /* 1=SB, 2=SB Pro */
+int sb_duplex_midi = 0;
static int my_dev = 0;
-static volatile int intr_active = 0;
+volatile int sb_intr_active = 0;
static int dsp_speed (int);
static int dsp_set_stereo (int mode);
-static int dsp_command (unsigned char val);
-
-#ifndef EXCLUDE_SBPRO
-static void setmixer (unsigned char port, unsigned char value);
-static int getmixer (unsigned char port);
-static void init_mixer (void);
-static int detect_mixer (void);
-
-#endif
+int sb_dsp_command (unsigned char val);
#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
/* Common code for the midi and pcm functions */
-static int
-dsp_command (unsigned char val)
+int
+sb_dsp_command (unsigned char val)
{
int i, limit;
limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
/*
- * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+ * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
* called while interrupts are disabled. This means that the timer is
* disabled also. However the timeout situation is a abnormal condition.
* Normally the DSP should be ready to accept commands after just couple of
* loops.
*/
- for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
+ for (i = 0; i < 500000 && GET_TIME () < limit; i++)
{
if ((INB (DSP_STATUS) & 0x80) == 0)
{
@@ -179,41 +102,62 @@ dsp_command (unsigned char val)
}
}
- printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val);
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
printk ("IRQ conflict???\n");
return 0;
}
void
-sbintr (int unused)
+sbintr (int unit)
{
int status, data;
- status = INB (DSP_DATA_AVAIL);/* Clear interrupt */
+#ifndef EXCLUDE_SBPRO
+ if (sb16)
+ {
+ unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */
+
+#ifndef EXCLUDE_SB16
+ if (src & 3)
+ sb16_dsp_interrupt (unit);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4)
+ sb16midiintr (unit); /* MPU401 interrupt */
+#endif
+
+#endif
+
+ if (!(src & 1))
+ return; /* Not a DSP interupt */
+ }
+#endif
+
+ status = INB (DSP_DATA_AVAIL); /* Clear interrupt */
- if (intr_active)
- switch (irq_mode)
+ if (sb_intr_active)
+ switch (sb_irq_mode)
{
case IMODE_OUTPUT:
- intr_active = 0;
- DMAbuf_outputintr (my_dev);
+ sb_intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
break;
case IMODE_INPUT:
- intr_active = 0;
+ sb_intr_active = 0;
DMAbuf_inputintr (my_dev);
/* A complete buffer has been input. Let's start new one */
break;
case IMODE_INIT:
- intr_active = 0;
+ sb_intr_active = 0;
irq_ok = 1;
break;
case IMODE_MIDI:
printk ("+");
data = INB (DSP_READ);
- printk ("%02x", data);
+ printk ("%x", data);
break;
@@ -222,40 +166,36 @@ sbintr (int unused)
}
}
-static int
-set_dsp_irq (int interrupt_level)
-{
- int retcode = EINVAL;
+static int sb_irq_usecount = 0;
-#ifdef linux
- struct sigaction sa;
+int
+sb_get_irq (void)
+{
+ int ok;
- sa.sa_handler = sbintr;
+ if (!sb_irq_usecount)
+ if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0)
+ return ok;
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
+ sb_irq_usecount++;
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
+ return 0;
+}
- retcode = irqaction (interrupt_level, &sa);
+void
+sb_free_irq (void)
+{
+ if (!sb_irq_usecount)
+ return;
- if (retcode < 0)
- {
- printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level);
- }
+ sb_irq_usecount--;
-#else
- /* # error Unimplemented for this OS */
-#endif
- return retcode;
+ if (!sb_irq_usecount)
+ snd_release_irq (sbc_irq);
}
-static int
-reset_dsp (void)
+int
+sb_reset_dsp (void)
{
int loopc;
@@ -283,9 +223,9 @@ static void
dsp_speaker (char state)
{
if (state)
- dsp_command (DSP_CMD_SPKON);
+ sb_dsp_command (DSP_CMD_SPKON);
else
- dsp_command (DSP_CMD_SPKOFF);
+ sb_dsp_command (DSP_CMD_SPKOFF);
}
static int
@@ -294,14 +234,13 @@ dsp_speed (int speed)
unsigned char tconst;
unsigned long flags;
-
if (speed < 4000)
speed = 4000;
if (speed > 44100)
speed = 44100; /* Invalid speed */
- if (dsp_model == 1 && speed > 22050)
+ if (sb_dsp_model == 1 && speed > 22050)
speed = 22050;
/* SB Classic doesn't support higher speed */
@@ -310,44 +249,51 @@ dsp_speed (int speed)
speed = 22050;
/* Max. stereo speed is 22050 */
- if ((speed > 22050) && midi_busy)
+ if ((speed > 22050) && sb_midi_busy)
{
printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
speed = 22050;
}
if (dsp_stereo)
- speed <<= 1;
+ speed *= 2;
/* Now the speed should be valid */
if (speed > 22050)
{ /* High speed mode */
- tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8);
- dsp_highspeed = 1;
+ int tmp;
+
+ tconst = (unsigned char) ((65536 -
+ ((256000000 + speed / 2) / speed)) >> 8);
+ sb_dsp_highspeed = 1;
DISABLE_INTR (flags);
- if (dsp_command (0x40))
- dsp_command (tconst);
+ if (sb_dsp_command (0x40))
+ sb_dsp_command (tconst);
RESTORE_INTR (flags);
- speed = (256000000 / (65536 - (tconst << 8)));
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
}
else
{
- dsp_highspeed = 0;
- tconst = (256 - (1000000 / speed)) & 0xff;
+ int tmp;
+
+ sb_dsp_highspeed = 0;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
DISABLE_INTR (flags);
- if (dsp_command (0x40)) /* Set time constant */
- dsp_command (tconst);
+ if (sb_dsp_command (0x40)) /* Set time constant */
+ sb_dsp_command (tconst);
RESTORE_INTR (flags);
- speed = 1000000 / (256 - tconst);
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
}
if (dsp_stereo)
- speed >>= 1;
+ speed /= 2;
dsp_current_speed = speed;
return speed;
@@ -358,49 +304,47 @@ dsp_set_stereo (int mode)
{
dsp_stereo = 0;
- if (dsp_mono == 1)
+#ifdef EXCLUDE_SBPRO
+ return 0;
+#else
+ if (sb_dsp_model == 1 || sb16)
return 0; /* Sorry no stereo */
- if (mode && midi_busy)
+ if (mode && sb_midi_busy)
{
printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
return 0;
}
dsp_stereo = !!mode;
-
-#ifndef EXCLUDE_SBPRO
- setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC)
- | (mode ? STEREO_DAC : MONO_DAC)));
+ return dsp_stereo;
#endif
- dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels
- * changes */
- return mode;
}
static void
-sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
+sb_dsp_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
{
unsigned long flags;
- if (!irq_mode)
+ if (!sb_irq_mode)
dsp_speaker (ON);
- irq_mode = IMODE_OUTPUT;
+ sb_irq_mode = IMODE_OUTPUT;
DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
count--;
- if (dsp_highspeed)
+ if (sb_dsp_highspeed)
{
DISABLE_INTR (flags);
- if (dsp_command (0x48)) /* High speed size */
+ if (sb_dsp_command (0x48)) /* High speed size */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
- dsp_command (0x91); /* High speed 8 bit DAC */
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x91); /* High speed 8 bit DAC */
}
else
printk ("SB Error: Unable to start (high speed) DAC\n");
@@ -409,43 +353,44 @@ sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
else
{
DISABLE_INTR (flags);
- if (dsp_command (0x14)) /* 8-bit DAC (DMA) */
+ if (sb_dsp_command (0x14)) /* 8-bit DAC (DMA) */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
}
else
printk ("SB Error: Unable to start DAC\n");
RESTORE_INTR (flags);
}
- intr_active = 1;
+ sb_intr_active = 1;
}
static void
-sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
+ int restart_dma)
{
/* Start a DMA input to the buffer pointed by dmaqtail */
unsigned long flags;
- if (!irq_mode)
+ if (!sb_irq_mode)
dsp_speaker (OFF);
- irq_mode = IMODE_INPUT;
+ sb_irq_mode = IMODE_INPUT;
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
count--;
- if (dsp_highspeed)
+ if (sb_dsp_highspeed)
{
DISABLE_INTR (flags);
- if (dsp_command (0x48)) /* High speed size */
+ if (sb_dsp_command (0x48)) /* High speed size */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
- dsp_command (0x99); /* High speed 8 bit ADC */
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x99); /* High speed 8 bit ADC */
}
else
printk ("SB Error: Unable to start (high speed) ADC\n");
@@ -454,23 +399,23 @@ sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
else
{
DISABLE_INTR (flags);
- if (dsp_command (0x24)) /* 8-bit ADC (DMA) */
+ if (sb_dsp_command (0x24)) /* 8-bit ADC (DMA) */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
}
else
printk ("SB Error: Unable to start ADC\n");
RESTORE_INTR (flags);
}
- intr_active = 1;
+ sb_intr_active = 1;
}
static void
dsp_cleanup (void)
{
- intr_active = 0;
+ sb_intr_active = 0;
}
static int
@@ -478,6 +423,17 @@ sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
{
dsp_cleanup ();
dsp_speaker (OFF);
+
+ if (major == 3) /* SB Pro */
+ {
+ if (dsp_stereo)
+ sb_dsp_command (0xa8);
+ else
+ sb_dsp_command (0xa0);
+
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
return 0;
}
@@ -486,6 +442,15 @@ sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
{
dsp_cleanup ();
dsp_speaker (ON);
+
+#ifndef EXCLUDE_SBPRO
+ if (major == 3) /* SB Pro */
+ {
+ sb_mixer_set_stereo (dsp_stereo);
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
+#endif
return 0;
}
@@ -495,50 +460,79 @@ sb_dsp_halt_xfer (int dev)
}
static int
-sb_dsp_open (int dev, int mode)
+verify_irq (void)
{
- int retval;
+#if 0
+ DEFINE_WAIT_QUEUE (testq, testf);
- if (!sb_dsp_ok)
+ irq_ok = 0;
+
+ if (sb_get_irq () == -1)
{
- printk ("SB Error: SoundBlaster board not installed\n");
- return RET_ERROR (ENXIO);
+ printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+ return 0;
}
+
+ sb_irq_mode = IMODE_INIT;
+
+ sb_dsp_command (0xf2); /* This should cause immediate interrupt */
+
+ DO_SLEEP (testq, testf, HZ / 5);
+
+ sb_free_irq ();
+
if (!irq_ok)
{
- printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq);
+ printk ("SB Warning: IRQ%d test not passed!", sbc_irq);
+ irq_ok = 1;
+ }
+#else
+ irq_ok = 1;
+#endif
+ return irq_ok;
+}
+
+static int
+sb_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: SoundBlaster board not installed\n");
return RET_ERROR (ENXIO);
}
- if (intr_active || (midi_busy && midi_mode == UART_MIDI))
+ if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI))
{
printk ("SB: PCM not possible during MIDI input\n");
return RET_ERROR (EBUSY);
}
- if (mode != OPEN_READ && mode != OPEN_WRITE)
+ if (!irq_verified)
{
- printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n");
- return RET_ERROR (EINVAL);
+ verify_irq ();
+ irq_verified = 1;
}
+ else if (!irq_ok)
+ printk ("SB Warning: Incorrect IRQ setting %d\n",
+ sbc_irq);
- retval = set_dsp_irq (sbc_irq);
+ retval = sb_get_irq ();
if (retval)
return retval;
if (!DMAbuf_open_dma (dev))
{
- RELEASE_IRQ (sbc_irq);
+ sb_free_irq ();
printk ("SB: DMA Busy\n");
return RET_ERROR (EBUSY);
}
- dsp_set_stereo (OFF);
- dsp_speed (DSP_DEFAULT_SPEED);
- irq_mode = IMODE_NONE;
+ sb_irq_mode = IMODE_NONE;
- dsp_busy = 1;
+ sb_dsp_busy = 1;
return 0;
}
@@ -547,12 +541,11 @@ static void
sb_dsp_close (int dev)
{
DMAbuf_close_dma (dev);
- RELEASE_IRQ (sbc_irq);
+ sb_free_irq ();
dsp_cleanup ();
- dsp_speed (DSP_DEFAULT_SPEED);
- dsp_set_stereo (OFF);
dsp_speaker (OFF);
- dsp_busy = 0;
+ sb_dsp_busy = 0;
+ sb_dsp_highspeed = 0;
}
static int
@@ -573,6 +566,8 @@ sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
break;
case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
break;
@@ -592,7 +587,7 @@ sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
case SOUND_PCM_READ_BITS:
if (local)
return 8;
- return IOCTL_OUT (arg, 8);/* Only 8 bits/sample supported */
+ return IOCTL_OUT (arg, 8); /* Only 8 bits/sample supported */
break;
case SOUND_PCM_WRITE_FILTER:
@@ -614,7 +609,7 @@ sb_dsp_reset (int dev)
DISABLE_INTR (flags);
- reset_dsp ();
+ sb_reset_dsp ();
dsp_cleanup ();
RESTORE_INTR (flags);
@@ -631,538 +626,12 @@ sb_dsp_detect (struct address_info *hw_config)
if (sb_dsp_ok)
return 0; /* Already initialized */
- if (!reset_dsp ())
+ if (!sb_reset_dsp ())
return 0;
return 1; /* Detected */
}
-#ifndef EXCLUDE_SBPRO
-
-static void
-setmixer (unsigned char port, unsigned char value)
-{
- OUTB (port, MIXER_ADDR); /* Select register */
- tenmicrosec ();
- OUTB (value, MIXER_DATA);
- tenmicrosec ();
-}
-
-static int
-getmixer (unsigned char port)
-{
- int val;
-
- OUTB (port, MIXER_ADDR); /* Select register */
- tenmicrosec ();
- val = INB (MIXER_DATA);
- tenmicrosec ();
-
- return val;
-}
-
-static int
-detect_mixer (void)
-{
- /*
- * Detect the mixer by changing parameters of two volume channels. If the
- * values read back match with the values written, the mixer is there (is
- * it?)
- */
- setmixer (FM_VOL, 0xff);
- setmixer (VOC_VOL, 0x33);
-
- if (getmixer (FM_VOL) != 0xff)
- return 0; /* No match */
- if (getmixer (VOC_VOL) != 0x33)
- return 0;
-
- return 1;
-}
-
-static void
-init_mixer (void)
-{
- setmixer (MASTER_VOL, 0xbb);
- setmixer (VOC_VOL, 0x99);
- setmixer (LINE_VOL, 0xbb);
- setmixer (FM_VOL, 0x99);
- setmixer (CD_VOL, 0x11);
- setmixer (MIC_MIX, 0x11);
- setmixer (RECORD_SRC, 0x31);
- setmixer (OUT_FILTER, 0x31);
-}
-
-static void
-set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output)
-{
- setmixer (RECORD_SRC, (record_source
- | (hifreq_filter ? FREQ_HI : FREQ_LOW)
- | (filter_input ? FILT_ON : FILT_OFF)));
-
- setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC)
- | (filter_output ? FILT_ON : FILT_OFF)));
-
- hi_filter = hifreq_filter;
- filter_in = filter_input;
- filter_out = filter_output;
-}
-
-static int
-mixer_output (int right_vol, int left_vol, int div, int device)
-{
- int left = ((left_vol * div) + 50) / 100;
- int right = ((right_vol * div) + 50) / 100;
-
- setmixer (device, ((left & 0xf) << 4) | (right & 0xf));
-
- return (left_vol | (right_vol << 8));
-}
-
-static int
-sbp_mixer_set (int whichDev, unsigned int level)
-{
- int left, right, devmask;
-
- left = level & 0x7f;
- right = (level & 0x7f00) >> 8;
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
- return mixer_output (right, left, 15, MASTER_VOL);
- break;
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
- return mixer_output (right, left, 15, FM_VOL);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
- return mixer_output (right, left, 15, VOC_VOL);
- break;
- case SOUND_MIXER_LINE: /* External line (0-15) */
- return mixer_output (right, left, 15, LINE_VOL);
- break;
- case SOUND_MIXER_CD: /* CD (0-15) */
- return mixer_output (right, left, 15, CD_VOL);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-7) */
- return mixer_output (right, left, 7, MIC_VOL);
- break;
-
- case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
-
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- { /* More than one devices selected. Drop the
- * previous selection */
- devmask &= ~rec_devices;
- }
-
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- { /* More than one devices selected. Default to
- * mic */
- devmask = SOUND_MASK_MIC;
- }
-
- if (devmask ^ rec_devices)/* Input source changed */
- {
- switch (devmask)
- {
-
- case SOUND_MASK_MIC:
- set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
- break;
-
- case SOUND_MASK_LINE:
- set_filter (SRC_LINE, hi_filter, filter_in, filter_out);
- break;
-
- case SOUND_MASK_CD:
- set_filter (SRC_CD, hi_filter, filter_in, filter_out);
- break;
-
- default:
- set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
- }
- }
-
- rec_devices = devmask;
-
- return rec_devices;
- break;
-
- default:
- return RET_ERROR (EINVAL);
- }
-
-}
-
-static int
-mixer_input (int div, int device)
-{
- int level, left, right, half;
-
- level = getmixer (device);
- half = div / 2;
-
- left = ((((level & 0xf0) >> 4) * 100) + half) / div;
- right = (((level & 0x0f) * 100) + half) / div;
-
- return (right << 8) | left;
-}
-
-static int
-sbp_mixer_get (int whichDev)
-{
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
- return mixer_input (15, MASTER_VOL);
- break;
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
- return mixer_input (15, FM_VOL);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
- return mixer_input (15, VOC_VOL);
- break;
- case SOUND_MIXER_LINE: /* External line (0-15) */
- return mixer_input (15, LINE_VOL);
- break;
- case SOUND_MIXER_CD: /* CD (0-15) */
- return mixer_input (15, CD_VOL);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-7) */
- return mixer_input (7, MIC_VOL);
- break;
-
- default:
- return RET_ERROR (EINVAL);
- }
-
-}
-
-/*
- * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See
- * sbinfo.doc for details on granularity and such. Basically, the mixer
- * forces the lowest bit high, effectively reducing the possible settings by
- * one half. Yes, that's right, volume levels have 8 settings, and
- * microphone has four. Sucks.
- */
-static int
-mixer_set_levels (struct sb_mixer_levels *user_l)
-{
- struct sb_mixer_levels l;
-
- IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l));
-
- if (l.master.l & ~0xF || l.master.r & ~0xF
- || l.line.l & ~0xF || l.line.r & ~0xF
- || l.voc.l & ~0xF || l.voc.r & ~0xF
- || l.fm.l & ~0xF || l.fm.r & ~0xF
- || l.cd.l & ~0xF || l.cd.r & ~0xF
- || l.mic & ~0x7)
- return (RET_ERROR (EINVAL));
-
- setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r);
- setmixer (LINE_VOL, (l.line.l << 4) | l.line.r);
- setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r);
- setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r);
- setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r);
- setmixer (MIC_VOL, l.mic);
- return (0);
-}
-
-/*
- * This sets aspects of the Mixer that are not volume levels. (Recording
- * source, filter level, I/O filtering, and stereo.)
- */
-
-static int
-mixer_set_params (struct sb_mixer_params *user_p)
-{
- struct sb_mixer_params p;
-
- IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
-
- if (p.record_source != SRC_MIC
- && p.record_source != SRC_CD
- && p.record_source != SRC_LINE)
- return (EINVAL);
-
- /*
- * I'm not sure if this is The Right Thing. Should stereo be entirely
- * under control of DSP? I like being able to toggle it while a sound is
- * playing, so I do this... because I can.
- */
-
- dsp_stereo = !!p.dsp_stereo;
-
- set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output);
-
- switch (p.record_source)
- {
-
- case SRC_MIC:
- rec_devices = SOUND_MASK_MIC;
- break;
-
- case SRC_LINE:
- rec_devices = SOUND_MASK_LINE;
- break;
-
- case SRC_CD:
- rec_devices = SOUND_MASK_CD;
- }
-
- return (0);
-}
-
-/* Read the current mixer level settings into the user's struct. */
-static int
-mixer_get_levels (struct sb_mixer_levels *user_l)
-{
- S_BYTE val;
- struct sb_mixer_levels l;
-
- val = getmixer (MASTER_VOL); /* Master */
- l.master.l = B4 (val >> 4);
- l.master.r = B4 (val);
-
- val = getmixer (LINE_VOL); /* FM */
- l.line.l = B4 (val >> 4);
- l.line.r = B4 (val);
-
- val = getmixer (VOC_VOL); /* DAC */
- l.voc.l = B4 (val >> 4);
- l.voc.r = B4 (val);
-
- val = getmixer (FM_VOL); /* FM */
- l.fm.l = B4 (val >> 4);
- l.fm.r = B4 (val);
-
- val = getmixer (CD_VOL); /* CD */
- l.cd.l = B4 (val >> 4);
- l.cd.r = B4 (val);
-
- val = getmixer (MIC_VOL); /* Microphone */
- l.mic = B3 (val);
-
- IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
-
- return (0);
-}
-
-/* Read the current mixer parameters into the user's struct. */
-static int
-mixer_get_params (struct sb_mixer_params *user_params)
-{
- S_BYTE val;
- struct sb_mixer_params params;
-
- val = getmixer (RECORD_SRC);
- params.record_source = val & 0x07;
- params.hifreq_filter = !!(val & FREQ_HI);
- params.filter_input = (val & FILT_OFF) ? OFF : ON;
- params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON;
- params.dsp_stereo = dsp_stereo;
-
- IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
- return (0);
-}
-
-static int
-sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
-{
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (cmd & IOC_IN)
- return IOCTL_OUT (arg, sbp_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
- else
- { /* Read parameters */
-
- switch (cmd & 0xff)
- {
-
- case SOUND_MIXER_RECSRC:
- return IOCTL_OUT (arg, rec_devices);
- break;
-
- case SOUND_MIXER_DEVMASK:
- return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC);
- break;
-
- case SOUND_MIXER_RECMASK:
- return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES);
- break;
-
- case SOUND_MIXER_CAPS:
- return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT);
- break;
-
- default:
- return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff));
- }
- }
- }
- else
- {
- switch (cmd)
- {
- case MIXER_IOCTL_SET_LEVELS:
- return (mixer_set_levels ((struct sb_mixer_levels *) arg));
- case MIXER_IOCTL_SET_PARAMS:
- return (mixer_set_params ((struct sb_mixer_params *) arg));
- case MIXER_IOCTL_READ_LEVELS:
- return (mixer_get_levels ((struct sb_mixer_levels *) arg));
- case MIXER_IOCTL_READ_PARAMS:
- return (mixer_get_params ((struct sb_mixer_params *) arg));
- case MIXER_IOCTL_RESET:
- init_mixer ();
- return (0);
- default:
- return RET_ERROR (EINVAL);
- }
- }
-}
-
-/* End of mixer code */
-#endif
-
-#ifndef EXCLUDE_MIDI
-
-/* Midi code */
-
-static int
-sb_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- int ret;
-
- if (!sb_dsp_ok)
- {
- printk ("SB Error: MIDI hardware not installed\n");
- return RET_ERROR (ENXIO);
- }
-
- if (mode != OPEN_WRITE && !duplex_midi)
- {
- if (num_midis == 1)
- printk ("SoundBlaster: Midi input not currently supported\n");
- return RET_ERROR (EPERM);
- }
-
- midi_mode = NORMAL_MIDI;
- if (mode != OPEN_WRITE)
- {
- if (dsp_busy || intr_active)
- return RET_ERROR (EBUSY);
- midi_mode = UART_MIDI;
- }
-
- if (dsp_highspeed || dsp_stereo)
- {
- printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
- return RET_ERROR (EBUSY);
- }
-
- if (midi_mode == UART_MIDI)
- {
- irq_mode = IMODE_MIDI;
-
- reset_dsp ();
- dsp_speaker (OFF);
-
- if (!dsp_command (0x35))
- return RET_ERROR (EIO); /* Enter the UART mode */
- intr_active = 1;
-
- if ((ret = set_dsp_irq (sbc_irq)) < 0)
- {
- reset_dsp ();
- return 0; /* IRQ not free */
- }
- }
-
- midi_busy = 1;
-
- return 0;
-}
-
-static void
-sb_midi_close (int dev)
-{
- if (midi_mode == UART_MIDI)
- {
- reset_dsp (); /* The only way to kill the UART mode */
- RELEASE_IRQ (sbc_irq);
- }
- intr_active = 0;
- midi_busy = 0;
-}
-
-static int
-sb_midi_out (int dev, unsigned char midi_byte)
-{
- unsigned long flags;
-
- midi_busy = 1; /* Kill all notes after close */
-
- if (midi_mode == NORMAL_MIDI)
- {
- DISABLE_INTR (flags);
- if (dsp_command (0x38))
- dsp_command (midi_byte);
- else
- printk ("SB Error: Unable to send a MIDI byte\n");
- RESTORE_INTR (flags);
- }
- else
- dsp_command (midi_byte); /* UART write */
-
- return 1;
-}
-
-static int
-sb_midi_start_read (int dev)
-{
- if (midi_mode != UART_MIDI)
- {
- printk ("SoundBlaster: MIDI input not implemented.\n");
- return RET_ERROR (EPERM);
- }
- return 0;
-}
-
-static int
-sb_midi_end_read (int dev)
-{
- if (midi_mode == UART_MIDI)
- {
- reset_dsp ();
- intr_active = 0;
- }
- return 0;
-}
-
-static int
-sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
-{
- return RET_ERROR (EPERM);
-}
-
-/* End of midi code */
-#endif
-
#ifndef EXCLUDE_AUDIO
static struct audio_operations sb_dsp_operations =
{
@@ -1182,132 +651,84 @@ static struct audio_operations sb_dsp_operations =
#endif
-#ifndef EXCLUDE_SBPRO
-static struct mixer_operations sb_mixer_operations =
-{
- sb_mixer_ioctl
-};
-
-#endif
-
-#ifndef EXCLUDE_MIDI
-static struct midi_operations sb_midi_operations =
-{
- {"SoundBlaster", 0},
- sb_midi_open,
- sb_midi_close,
- sb_midi_ioctl,
- sb_midi_out,
- sb_midi_start_read,
- sb_midi_end_read,
- NULL, /* Kick */
- NULL, /* command */
- NULL /* buffer_status */
-};
-
-#endif
-
-static int
-verify_irq (void)
-{
-#if 0
- unsigned long loop;
-
- irq_ok = 0;
-
- if (set_dsp_irq (sbc_irq) == -1)
- {
- printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
- return 0;
- }
-
-
- irq_mode = IMODE_INIT;
-
- dsp_command (0xf2); /* This should cause immediate interrupt */
-
- for (loop = 100000; loop > 0 && !irq_ok; loop--);
-
- RELEASE_IRQ (sbc_irq);
-
- if (!irq_ok)
- {
- printk ("SB Warning: IRQ test not passed!");
- irq_ok = 1;
- }
-#else
- irq_ok = 1;
-#endif
- return irq_ok;
-}
-
long
sb_dsp_init (long mem_start, struct address_info *hw_config)
{
- int i, major, minor;
+ int i;
major = minor = 0;
- dsp_command (0xe1); /* Get version */
+ sb_dsp_command (0xe1); /* Get version */
for (i = 1000; i; i--)
{
- if (inb (DSP_DATA_AVAIL) & 0x80)
+ if (INB (DSP_DATA_AVAIL) & 0x80)
{ /* wait for Data Ready */
if (major == 0)
- major = inb (DSP_READ);
+ major = INB (DSP_READ);
else
{
- minor = inb (DSP_READ);
+ minor = INB (DSP_READ);
break;
}
}
}
- dsp_model = major;
+
+ if (major == 2 || major == 3)
+ sb_duplex_midi = 1;
+
+ if (major == 4)
+ sb16 = 1;
+
+ if (major >= 3)
+ sb_dsp_model = 2;
#ifndef EXCLUDE_SBPRO
- if (detect_mixer ())
- {
- dsp_mono = 0;
- sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor);
- init_mixer ();
- mixer_devs[num_mixers++] = &sb_mixer_operations;
+ if (major >= 3)
+ sb_mixer_init (major);
+#endif
- if (major >= 2)
- duplex_midi = 1;
+#ifndef EXCLUDE_YM3812
+ if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */
+ {
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+ }
+#endif
-#ifndef EXCLUDE_YM8312
- if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */
- {
- enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
- }
+ if (major >= 3)
+ {
+#ifndef SCO
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor);
#endif
}
else
+ {
+#ifndef SCO
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor);
#endif
- sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor);
+ }
printk ("snd2: <%s>", sb_dsp_operations.name);
- if (!verify_irq ())
- return mem_start;
-
#ifndef EXCLUDE_AUDIO
- if (num_dspdevs < MAX_DSP_DEV)
- {
- dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
- sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
- sound_buffsizes[my_dev] = DSP_BUFFSIZE;
- sound_dsp_dmachan[my_dev] = hw_config->dma;
- sound_dma_automode[my_dev] = 0;
- }
- else
- printk ("SB: Too many DSP devices available\n");
+#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /* There is a better driver for SB16 */
+#endif
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+ sound_dma_automode[my_dev] = 0;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
#endif
#ifndef EXCLUDE_MIDI
- if (!midi_disabled) /* Midi don't work in the SB emulation mode
- * of PAS */
- midi_devs[num_midis++] = &sb_midi_operations;
+ if (!midi_disabled && !sb16) /* Midi don't work in the SB emulation mode
+ * of PAS, SB16 has better midi interface */
+ sb_midi_init (major);
#endif
sb_dsp_ok = 1;
OpenPOWER on IntegriCloud