mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-24 11:29:10 +00:00
d9983e87e7
Save 112K for SB, 64K for PAS and 64K for MSS. Since PAS use SB emulation, 176K normally saved for it. Few minor optimizations added like in Linux driver.
1224 lines
25 KiB
C
1224 lines
25 KiB
C
/*
|
|
* sound/sb_dsp.c
|
|
*
|
|
* The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
|
|
*
|
|
* Copyright by Hannu Savolainen 1994
|
|
*
|
|
* 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.
|
|
*
|
|
* Modified:
|
|
* Hunyue Yau Jan 6 1994
|
|
* Added code to support Sound Galaxy NX Pro
|
|
*
|
|
* JRA Gibson April 1995
|
|
* Code added for MV ProSonic/Jazz 16 in 16 bit mode
|
|
*/
|
|
|
|
#include "sound_config.h"
|
|
|
|
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
|
|
|
|
#include "sb.h"
|
|
#include "sb_mixer.h"
|
|
#undef SB_TEST_IRQ
|
|
|
|
int sbc_base = 0;
|
|
static int sbc_irq = 0;
|
|
static int open_mode = 0; /* Read, write or both */
|
|
int Jazz16_detected = 0;
|
|
|
|
/*
|
|
* The DSP channel can be used either for input or output. Variable
|
|
* '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.
|
|
*/
|
|
|
|
int sb_dsp_ok = 0; /*
|
|
|
|
|
|
* * * * Set to 1 after successful
|
|
* initialization * */
|
|
static int midi_disabled = 0;
|
|
int sb_dsp_highspeed = 0;
|
|
int sbc_major = 1, sbc_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;
|
|
|
|
int sb_midi_mode = NORMAL_MIDI;
|
|
int sb_midi_busy = 0; /*
|
|
|
|
|
|
* * * * 1 if the process has output
|
|
* to * * MIDI */
|
|
int sb_dsp_busy = 0;
|
|
|
|
volatile int sb_irq_mode = IMODE_NONE; /*
|
|
|
|
|
|
* * * * IMODE_INPUT, *
|
|
* IMODE_OUTPUT * * or *
|
|
* IMODE_NONE */
|
|
static volatile int irq_ok = 0;
|
|
|
|
#ifdef JAZZ16
|
|
/* 16 bit support
|
|
*/
|
|
|
|
static int dsp_16bit = 0;
|
|
static int dma8 = 1;
|
|
static int dma16 = 5;
|
|
|
|
static int dsp_set_bits (int arg);
|
|
static int initialize_ProSonic16 (void);
|
|
|
|
/* end of 16 bit support
|
|
*/
|
|
#endif
|
|
|
|
int sb_duplex_midi = 0;
|
|
static int my_dev = 0;
|
|
|
|
volatile int sb_intr_active = 0;
|
|
|
|
static int dsp_speed (int);
|
|
static int dsp_set_stereo (int mode);
|
|
|
|
#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
|
|
|
|
/*
|
|
* Common code for the midi and pcm functions
|
|
*/
|
|
|
|
int
|
|
sb_dsp_command (unsigned char val)
|
|
{
|
|
int i;
|
|
unsigned long limit;
|
|
|
|
limit = GET_TIME () + HZ / 10; /*
|
|
* The timeout is 0.1 secods
|
|
*/
|
|
|
|
/*
|
|
* 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 < 500000 && GET_TIME () < limit; i++)
|
|
{
|
|
if ((INB (DSP_STATUS) & 0x80) == 0)
|
|
{
|
|
OUTB (val, DSP_COMMAND);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
|
|
printk ("IRQ conflict???\n");
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sbintr (INT_HANDLER_PARMS (irq, dummy))
|
|
{
|
|
int status;
|
|
|
|
#ifndef EXCLUDE_SBPRO
|
|
if (sb16)
|
|
{
|
|
unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */
|
|
|
|
#ifndef EXCLUDE_SB16
|
|
if (src & 3)
|
|
sb16_dsp_interrupt (irq);
|
|
|
|
#ifndef EXCLUDE_MIDI
|
|
if (src & 4)
|
|
sb16midiintr (irq); /*
|
|
* SB MPU401 interrupt
|
|
*/
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if (!(src & 1))
|
|
return; /*
|
|
* Not a DSP interupt
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
status = INB (DSP_DATA_AVAIL); /*
|
|
* Clear interrupt
|
|
*/
|
|
|
|
if (sb_intr_active)
|
|
switch (sb_irq_mode)
|
|
{
|
|
case IMODE_OUTPUT:
|
|
sb_intr_active = 0;
|
|
DMAbuf_outputintr (my_dev, 1);
|
|
break;
|
|
|
|
case IMODE_INPUT:
|
|
sb_intr_active = 0;
|
|
DMAbuf_inputintr (my_dev);
|
|
/*
|
|
* A complete buffer has been input. Let's start new one
|
|
*/
|
|
break;
|
|
|
|
case IMODE_INIT:
|
|
sb_intr_active = 0;
|
|
irq_ok = 1;
|
|
break;
|
|
|
|
case IMODE_MIDI:
|
|
#ifndef EXCLUDE_MIDI
|
|
sb_midi_interrupt (irq);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
printk ("SoundBlaster: Unexpected interrupt\n");
|
|
}
|
|
}
|
|
|
|
static int sb_irq_usecount = 0;
|
|
|
|
int
|
|
sb_get_irq (void)
|
|
{
|
|
int ok;
|
|
|
|
if (!sb_irq_usecount)
|
|
if ((ok = snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster")) < 0)
|
|
return ok;
|
|
|
|
sb_irq_usecount++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sb_free_irq (void)
|
|
{
|
|
if (!sb_irq_usecount)
|
|
return;
|
|
|
|
sb_irq_usecount--;
|
|
|
|
if (!sb_irq_usecount)
|
|
snd_release_irq (sbc_irq);
|
|
}
|
|
|
|
int
|
|
sb_reset_dsp (void)
|
|
{
|
|
int loopc;
|
|
|
|
OUTB (1, DSP_RESET);
|
|
tenmicrosec ();
|
|
OUTB (0, DSP_RESET);
|
|
tenmicrosec ();
|
|
tenmicrosec ();
|
|
tenmicrosec ();
|
|
|
|
for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /*
|
|
* Wait
|
|
* for
|
|
* data
|
|
* *
|
|
* available
|
|
* status
|
|
*/
|
|
|
|
if (INB (DSP_READ) != 0xAA)
|
|
return 0; /*
|
|
* Sorry
|
|
*/
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_AUDIO
|
|
|
|
static void
|
|
dsp_speaker (char state)
|
|
{
|
|
if (state)
|
|
sb_dsp_command (DSP_CMD_SPKON);
|
|
else
|
|
sb_dsp_command (DSP_CMD_SPKOFF);
|
|
}
|
|
|
|
static int
|
|
dsp_speed (int speed)
|
|
{
|
|
unsigned char tconst;
|
|
unsigned long flags;
|
|
int max_speed = 44100;
|
|
|
|
if (speed < 4000)
|
|
speed = 4000;
|
|
|
|
/*
|
|
* Older SB models don't support higher speeds than 22050.
|
|
*/
|
|
|
|
if (sbc_major < 2 ||
|
|
(sbc_major == 2 && sbc_minor == 0))
|
|
max_speed = 22050;
|
|
|
|
/*
|
|
* SB models earlier than SB Pro have low limit for the input speed.
|
|
*/
|
|
if (open_mode != OPEN_WRITE) /* Recording is possible */
|
|
if (sbc_major < 3) /* Limited input speed with these cards */
|
|
if (sbc_major == 2 && sbc_minor > 0)
|
|
max_speed = 15000;
|
|
else
|
|
max_speed = 13000;
|
|
|
|
if (speed > max_speed)
|
|
speed = max_speed; /*
|
|
* Invalid speed
|
|
*/
|
|
|
|
/* Logitech SoundMan Games and Jazz16 cards can support 44.1kHz stereo */
|
|
#if !defined (SM_GAMES)
|
|
/*
|
|
* Max. stereo speed is 22050
|
|
*/
|
|
if (dsp_stereo && speed > 22050 && Jazz16_detected == 0)
|
|
speed = 22050;
|
|
#endif
|
|
|
|
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 *= 2;
|
|
|
|
/*
|
|
* Now the speed should be valid
|
|
*/
|
|
|
|
if (speed > 22050)
|
|
{ /*
|
|
* High speed mode
|
|
*/
|
|
int tmp;
|
|
|
|
tconst = (unsigned char) ((65536 -
|
|
((256000000 + speed / 2) / speed)) >> 8);
|
|
sb_dsp_highspeed = 1;
|
|
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x40))
|
|
sb_dsp_command (tconst);
|
|
RESTORE_INTR (flags);
|
|
|
|
tmp = 65536 - (tconst << 8);
|
|
speed = (256000000 + tmp / 2) / tmp;
|
|
}
|
|
else
|
|
{
|
|
int tmp;
|
|
|
|
sb_dsp_highspeed = 0;
|
|
tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
|
|
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x40)) /*
|
|
* Set time constant
|
|
*/
|
|
sb_dsp_command (tconst);
|
|
RESTORE_INTR (flags);
|
|
|
|
tmp = 256 - tconst;
|
|
speed = (1000000 + tmp / 2) / tmp;
|
|
}
|
|
|
|
if (dsp_stereo)
|
|
speed /= 2;
|
|
|
|
dsp_current_speed = speed;
|
|
return speed;
|
|
}
|
|
|
|
static int
|
|
dsp_set_stereo (int mode)
|
|
{
|
|
dsp_stereo = 0;
|
|
|
|
#ifdef EXCLUDE_SBPRO
|
|
return 0;
|
|
#else
|
|
if (sbc_major < 3 || sb16)
|
|
return 0; /*
|
|
* Sorry no stereo
|
|
*/
|
|
|
|
if (mode && sb_midi_busy)
|
|
{
|
|
printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
|
|
return 0;
|
|
}
|
|
|
|
dsp_stereo = !!mode;
|
|
return dsp_stereo;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sb_dsp_output_block (int dev, unsigned long buf, int count,
|
|
int intrflag, int restart_dma)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (!sb_irq_mode)
|
|
dsp_speaker (ON);
|
|
|
|
sb_irq_mode = IMODE_OUTPUT;
|
|
DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
|
|
|
|
if (audio_devs[dev]->dmachan > 3)
|
|
count >>= 1;
|
|
count--;
|
|
|
|
if (sb_dsp_highspeed)
|
|
{
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x48)) /*
|
|
* High speed size
|
|
*/
|
|
{
|
|
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");
|
|
RESTORE_INTR (flags);
|
|
}
|
|
else
|
|
{
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x14)) /*
|
|
* 8-bit DAC (DMA)
|
|
*/
|
|
{
|
|
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);
|
|
}
|
|
sb_intr_active = 1;
|
|
}
|
|
|
|
static void
|
|
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 (!sb_irq_mode)
|
|
dsp_speaker (OFF);
|
|
|
|
sb_irq_mode = IMODE_INPUT;
|
|
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
|
|
|
|
if (audio_devs[dev]->dmachan > 3)
|
|
count >>= 1;
|
|
count--;
|
|
|
|
if (sb_dsp_highspeed)
|
|
{
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x48)) /*
|
|
* High speed size
|
|
*/
|
|
{
|
|
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");
|
|
RESTORE_INTR (flags);
|
|
}
|
|
else
|
|
{
|
|
DISABLE_INTR (flags);
|
|
if (sb_dsp_command (0x24)) /*
|
|
* 8-bit ADC (DMA)
|
|
*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
sb_intr_active = 1;
|
|
}
|
|
|
|
static void
|
|
dsp_cleanup (void)
|
|
{
|
|
sb_intr_active = 0;
|
|
}
|
|
|
|
static int
|
|
sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
|
|
{
|
|
dsp_cleanup ();
|
|
dsp_speaker (OFF);
|
|
|
|
if (sbc_major == 3) /*
|
|
* SB Pro
|
|
*/
|
|
{
|
|
#ifdef JAZZ16
|
|
/* Select correct dma channel
|
|
* for 16/8 bit acccess
|
|
*/
|
|
audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
|
|
if (dsp_stereo)
|
|
sb_dsp_command (dsp_16bit ? 0xac : 0xa8);
|
|
else
|
|
sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0);
|
|
#else
|
|
/* 8 bit only cards use this
|
|
*/
|
|
if (dsp_stereo)
|
|
sb_dsp_command (0xa8);
|
|
else
|
|
sb_dsp_command (0xa0);
|
|
#endif
|
|
dsp_speed (dsp_current_speed); /*
|
|
* Speed must be recalculated if
|
|
* #channels * changes
|
|
*/
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
|
|
{
|
|
dsp_cleanup ();
|
|
dsp_speaker (ON);
|
|
|
|
#ifndef EXCLUDE_SBPRO
|
|
if (sbc_major == 3) /*
|
|
* SB Pro
|
|
*/
|
|
{
|
|
#ifdef JAZZ16
|
|
/* 16 bit specific instructions
|
|
*/
|
|
audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
|
|
if (Jazz16_detected != 2) /* SM Wave */
|
|
sb_mixer_set_stereo (dsp_stereo);
|
|
if (dsp_stereo)
|
|
sb_dsp_command (dsp_16bit ? 0xac : 0xa8);
|
|
else
|
|
sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0);
|
|
#else
|
|
sb_mixer_set_stereo (dsp_stereo);
|
|
#endif
|
|
dsp_speed (dsp_current_speed); /*
|
|
* Speed must be recalculated if
|
|
* #channels * changes
|
|
*/
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sb_dsp_halt_xfer (int dev)
|
|
{
|
|
}
|
|
|
|
static int
|
|
verify_irq (void)
|
|
{
|
|
#if 0
|
|
DEFINE_WAIT_QUEUE (testq, testf);
|
|
|
|
irq_ok = 0;
|
|
|
|
if (sb_get_irq () == -1)
|
|
{
|
|
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 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 (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 (!irq_verified)
|
|
{
|
|
verify_irq ();
|
|
irq_verified = 1;
|
|
}
|
|
else if (!irq_ok)
|
|
printk ("SB Warning: Incorrect IRQ setting %d\n",
|
|
sbc_irq);
|
|
|
|
retval = sb_get_irq ();
|
|
if (retval)
|
|
return retval;
|
|
|
|
/* Allocate 8 bit dma
|
|
*/
|
|
if (DMAbuf_open_dma (dev) < 0)
|
|
{
|
|
sb_free_irq ();
|
|
printk ("SB: DMA Busy\n");
|
|
return RET_ERROR (EBUSY);
|
|
}
|
|
#ifdef JAZZ16
|
|
/* Allocate 16 bit dma
|
|
*/
|
|
if (Jazz16_detected != 0)
|
|
if (dma16 != dma8)
|
|
{
|
|
if (ALLOC_DMA_CHN (dma16, "Jazz16 16 bit"))
|
|
{
|
|
sb_free_irq ();
|
|
RELEASE_DMA_CHN (dma8);
|
|
return RET_ERROR (EBUSY);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sb_irq_mode = IMODE_NONE;
|
|
|
|
sb_dsp_busy = 1;
|
|
open_mode = mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sb_dsp_close (int dev)
|
|
{
|
|
#ifdef JAZZ16
|
|
/* Release 16 bit dma channel
|
|
*/
|
|
if (Jazz16_detected)
|
|
RELEASE_DMA_CHN (dma16);
|
|
#endif
|
|
|
|
DMAbuf_close_dma (dev);
|
|
sb_free_irq ();
|
|
dsp_cleanup ();
|
|
dsp_speaker (OFF);
|
|
sb_dsp_busy = 0;
|
|
sb_dsp_highspeed = 0;
|
|
open_mode = 0;
|
|
}
|
|
|
|
#ifdef JAZZ16
|
|
/* Function dsp_set_bits() only required for 16 bit cards
|
|
*/
|
|
static int
|
|
dsp_set_bits (int arg)
|
|
{
|
|
if (arg)
|
|
if (Jazz16_detected == 0)
|
|
dsp_16bit = 0;
|
|
else
|
|
switch (arg)
|
|
{
|
|
case 8:
|
|
dsp_16bit = 0;
|
|
break;
|
|
case 16:
|
|
dsp_16bit = 1;
|
|
break;
|
|
default:
|
|
dsp_16bit = 0;
|
|
}
|
|
return dsp_16bit ? 16 : 8;
|
|
}
|
|
|
|
#endif /* ifdef JAZZ16 */
|
|
|
|
static int
|
|
sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case SOUND_PCM_WRITE_RATE:
|
|
if (local)
|
|
return dsp_speed (arg);
|
|
return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg)));
|
|
break;
|
|
|
|
case SOUND_PCM_READ_RATE:
|
|
if (local)
|
|
return dsp_current_speed;
|
|
return IOCTL_OUT (arg, dsp_current_speed);
|
|
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;
|
|
|
|
case SOUND_PCM_READ_CHANNELS:
|
|
if (local)
|
|
return dsp_stereo + 1;
|
|
return IOCTL_OUT (arg, dsp_stereo + 1);
|
|
break;
|
|
|
|
case SNDCTL_DSP_STEREO:
|
|
if (local)
|
|
return dsp_set_stereo (arg);
|
|
return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
|
|
break;
|
|
|
|
#ifdef JAZZ16
|
|
/* Word size specific cases here.
|
|
* SNDCTL_DSP_SETFMT=SOUND_PCM_WRITE_BITS
|
|
*/
|
|
case SNDCTL_DSP_SETFMT:
|
|
if (local)
|
|
return dsp_set_bits (arg);
|
|
return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
|
|
break;
|
|
|
|
case SOUND_PCM_READ_BITS:
|
|
if (local)
|
|
return dsp_16bit ? 16 : 8;
|
|
return IOCTL_OUT (arg, dsp_16bit ? 16 : 8);
|
|
break;
|
|
#else
|
|
case SOUND_PCM_WRITE_BITS:
|
|
case SOUND_PCM_READ_BITS:
|
|
if (local)
|
|
return 8;
|
|
return IOCTL_OUT (arg, 8); /*
|
|
* Only 8 bits/sample supported
|
|
*/
|
|
break;
|
|
#endif /* ifdef JAZZ16 */
|
|
|
|
case SOUND_PCM_WRITE_FILTER:
|
|
case SOUND_PCM_READ_FILTER:
|
|
return RET_ERROR (EINVAL);
|
|
break;
|
|
|
|
default:
|
|
return RET_ERROR (EINVAL);
|
|
}
|
|
|
|
return RET_ERROR (EINVAL);
|
|
}
|
|
|
|
static void
|
|
sb_dsp_reset (int dev)
|
|
{
|
|
unsigned long flags;
|
|
|
|
DISABLE_INTR (flags);
|
|
|
|
sb_reset_dsp ();
|
|
dsp_speed (dsp_current_speed);
|
|
dsp_cleanup ();
|
|
|
|
RESTORE_INTR (flags);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef JAZZ16
|
|
|
|
/*
|
|
* Initialization of a Media Vision ProSonic 16 Soundcard.
|
|
* The function initializes a ProSonic 16 like PROS.EXE does for DOS. It sets
|
|
* the base address, the DMA-channels, interrupts and enables the joystickport.
|
|
*
|
|
* Also used by Jazz 16 (same card, different name)
|
|
*
|
|
* written 1994 by Rainer Vranken
|
|
* E-Mail: rvranken@polaris.informatik.uni-essen.de
|
|
*/
|
|
|
|
|
|
#ifndef MPU_BASE /* take default values if not specified */
|
|
#define MPU_BASE 0x330
|
|
#endif
|
|
#ifndef MPU_IRQ
|
|
#define MPU_IRQ 9
|
|
#endif
|
|
|
|
unsigned int
|
|
get_sb_byte (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1000; i; i--)
|
|
if (INB (DSP_DATA_AVAIL) & 0x80)
|
|
{
|
|
return INB (DSP_READ);
|
|
}
|
|
|
|
return 0xffff;
|
|
}
|
|
|
|
#ifdef SM_WAVE
|
|
/*
|
|
* Logitech Soundman Wave detection and initialization by Hannu Savolainen.
|
|
*
|
|
* There is a microcontroller (8031) in the SM Wave card for MIDI emulation.
|
|
* it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific
|
|
* control register for MC reset, SCSI, OPL4 and DSP (future expansion)
|
|
* address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16
|
|
* based soundcard.
|
|
*/
|
|
|
|
static void
|
|
smw_putmem (int base, int addr, unsigned char val)
|
|
{
|
|
unsigned long flags;
|
|
|
|
DISABLE_INTR (flags);
|
|
|
|
OUTB (addr & 0xff, base + 1); /* Low address bits */
|
|
OUTB (addr >> 8, base + 2); /* High address bits */
|
|
OUTB (val, base); /* Data */
|
|
|
|
RESTORE_INTR (flags);
|
|
}
|
|
|
|
static unsigned char
|
|
smw_getmem (int base, int addr)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char val;
|
|
|
|
DISABLE_INTR (flags);
|
|
|
|
OUTB (addr & 0xff, base + 1); /* Low address bits */
|
|
OUTB (addr >> 8, base + 2); /* High address bits */
|
|
val = INB (base); /* Data */
|
|
|
|
RESTORE_INTR (flags);
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
initialize_smw (void)
|
|
{
|
|
#ifdef SMW_MIDI0001_INCLUDED
|
|
#include "smw-midi0001.h"
|
|
#else
|
|
unsigned char smw_ucode[1];
|
|
int smw_ucodeLen = 0;
|
|
|
|
#endif
|
|
|
|
int mp_base = MPU_BASE + 4; /* Microcontroller base */
|
|
int i;
|
|
unsigned char control;
|
|
|
|
/*
|
|
* Reset the microcontroller so that the RAM can be accessed
|
|
*/
|
|
|
|
control = INB (MPU_BASE + 7);
|
|
OUTB (control | 3, MPU_BASE + 7); /* Set last two bits to 1 (?) */
|
|
OUTB ((control & 0xfe) | 2, MPU_BASE + 7); /* xxxxxxx0 resets the mc */
|
|
|
|
for (i = 0; i < 300; i++) /* Wait at least 1ms */
|
|
tenmicrosec ();
|
|
|
|
OUTB (control & 0xfc, MPU_BASE + 7); /* xxxxxx00 enables RAM */
|
|
|
|
/*
|
|
* Detect microcontroller by probing the 8k RAM area
|
|
*/
|
|
smw_putmem (mp_base, 0, 0x00);
|
|
smw_putmem (mp_base, 1, 0xff);
|
|
tenmicrosec ();
|
|
|
|
if (smw_getmem (mp_base, 0) != 0x00 || smw_getmem (mp_base, 1) != 0xff)
|
|
{
|
|
printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
|
|
smw_getmem (mp_base, 0), smw_getmem (mp_base, 1));
|
|
return 0; /* No RAM */
|
|
}
|
|
|
|
/*
|
|
* There is RAM so assume it's really a SM Wave
|
|
*/
|
|
|
|
#ifdef SMW_MIDI0001_INCLUDED
|
|
if (smw_ucodeLen != 8192)
|
|
{
|
|
printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Download microcode
|
|
*/
|
|
|
|
for (i = 0; i < 8192; i++)
|
|
smw_putmem (mp_base, i, smw_ucode[i]);
|
|
|
|
/*
|
|
* Verify microcode
|
|
*/
|
|
|
|
for (i = 0; i < 8192; i++)
|
|
if (smw_getmem (mp_base, i) != smw_ucode[i])
|
|
{
|
|
printk ("SM Wave: Microcode verification failed\n");
|
|
return 0;
|
|
}
|
|
|
|
control = 0;
|
|
#ifdef SMW_SCSI_IRQ
|
|
/*
|
|
* Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
|
|
* is disabled by default.
|
|
*
|
|
* Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10.
|
|
*/
|
|
{
|
|
static unsigned char scsi_irq_bits[] =
|
|
{0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
|
|
|
|
control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SMW_OPL4_ENABLE
|
|
/*
|
|
* Make the OPL4 chip visible on the PC bus at 0x380.
|
|
*
|
|
* There is no need to enable this feature since VoxWare
|
|
* doesn't support OPL4 yet. Also there is no RAM in SM Wave so
|
|
* enabling OPL4 is pretty useless.
|
|
*/
|
|
control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
|
|
/* control |= 0x20; Uncomment this if you want to use IRQ7 */
|
|
#endif
|
|
|
|
OUTB (control | 0x03, MPU_BASE + 7); /* xxxxxx11 restarts */
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int
|
|
initialize_ProSonic16 (void)
|
|
{
|
|
int x;
|
|
static unsigned char int_translat[16] =
|
|
{0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}, dma_translat[8] =
|
|
{0, 1, 0, 2, 0, 3, 0, 4};
|
|
|
|
OUTB (0xAF, 0x201); /* ProSonic/Jazz16 wakeup */
|
|
for (x = 0; x < 1000; ++x) /* wait 10 milliseconds */
|
|
tenmicrosec ();
|
|
OUTB (0x50, 0x201);
|
|
OUTB ((sbc_base & 0x70) | ((MPU_BASE & 0x30) >> 4), 0x201);
|
|
|
|
if (sb_reset_dsp ())
|
|
{ /* OK. We have at least a SB */
|
|
|
|
/* Check the version number of ProSonic (I guess) */
|
|
|
|
if (!sb_dsp_command (0xFA))
|
|
return 1;
|
|
if (get_sb_byte () != 0x12)
|
|
return 1;
|
|
|
|
if (sb_dsp_command (0xFB) && /* set DMA-channels and Interrupts */
|
|
sb_dsp_command ((dma_translat[JAZZ_DMA16] << 4) | dma_translat[SBC_DMA]) &&
|
|
sb_dsp_command ((int_translat[MPU_IRQ] << 4) | int_translat[sbc_irq]))
|
|
{
|
|
Jazz16_detected = 1;
|
|
#ifdef SM_WAVE
|
|
if (initialize_smw ())
|
|
Jazz16_detected = 2;
|
|
#endif
|
|
sb_dsp_disable_midi ();
|
|
}
|
|
|
|
return 1; /* There was at least a SB */
|
|
}
|
|
return 0; /* No SB or ProSonic16 detected */
|
|
}
|
|
|
|
#endif /* ifdef JAZZ16 */
|
|
|
|
int
|
|
sb_dsp_detect (struct address_info *hw_config)
|
|
{
|
|
sbc_base = hw_config->io_base;
|
|
sbc_irq = hw_config->irq;
|
|
|
|
if (sb_dsp_ok)
|
|
return 0; /*
|
|
* Already initialized
|
|
*/
|
|
#ifdef JAZZ16
|
|
dma8 = hw_config->dma;
|
|
dma16 = JAZZ_DMA16;
|
|
|
|
if (!initialize_ProSonic16 ())
|
|
return 0;
|
|
#else
|
|
if (!sb_reset_dsp ())
|
|
return 0;
|
|
#endif
|
|
|
|
return 1; /*
|
|
* Detected
|
|
*/
|
|
}
|
|
|
|
#ifndef EXCLUDE_AUDIO
|
|
static struct audio_operations sb_dsp_operations =
|
|
{
|
|
"SoundBlaster",
|
|
NOTHING_SPECIAL,
|
|
AFMT_U8, /* Just 8 bits. Poor old SB */
|
|
NULL,
|
|
sb_dsp_open,
|
|
sb_dsp_close,
|
|
sb_dsp_output_block,
|
|
sb_dsp_start_input,
|
|
sb_dsp_ioctl,
|
|
sb_dsp_prepare_for_input,
|
|
sb_dsp_prepare_for_output,
|
|
sb_dsp_reset,
|
|
sb_dsp_halt_xfer,
|
|
NULL, /* local_qlen */
|
|
NULL /* copy_from_user */
|
|
};
|
|
|
|
#endif
|
|
|
|
long
|
|
sb_dsp_init (long mem_start, struct address_info *hw_config)
|
|
{
|
|
int i;
|
|
int mixer_type = 0;
|
|
|
|
sbc_major = sbc_minor = 0;
|
|
sb_dsp_command (0xe1); /*
|
|
* Get version
|
|
*/
|
|
|
|
for (i = 1000; i; i--)
|
|
{
|
|
if (INB (DSP_DATA_AVAIL) & 0x80)
|
|
{ /*
|
|
* wait for Data Ready
|
|
*/
|
|
if (sbc_major == 0)
|
|
sbc_major = INB (DSP_READ);
|
|
else
|
|
{
|
|
sbc_minor = INB (DSP_READ);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sbc_major == 2 || sbc_major == 3)
|
|
sb_duplex_midi = 1;
|
|
|
|
if (sbc_major == 4)
|
|
sb16 = 1;
|
|
|
|
#ifndef EXCLUDE_SBPRO
|
|
if (sbc_major >= 3)
|
|
mixer_type = sb_mixer_init (sbc_major);
|
|
#else
|
|
if (sbc_major >= 3)
|
|
printk ("\n\n\n\nNOTE! SB Pro support is required with your soundcard!\n\n\n");
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_YM3812
|
|
|
|
if (sbc_major > 3 ||
|
|
(sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */
|
|
enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_AUDIO
|
|
if (sbc_major >= 3)
|
|
{
|
|
if (Jazz16_detected)
|
|
{
|
|
if (Jazz16_detected == 2)
|
|
sprintf (sb_dsp_operations.name, "SoundMan Wave %d.%d", sbc_major, sbc_minor);
|
|
else
|
|
sprintf (sb_dsp_operations.name, "MV Jazz16 %d.%d", sbc_major, sbc_minor);
|
|
sb_dsp_operations.format_mask |= AFMT_S16_LE; /* Hurrah, 16 bits */
|
|
}
|
|
else
|
|
#ifdef __SGNXPRO__
|
|
if (mixer_type == 2)
|
|
{
|
|
sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
if (sbc_major == 4)
|
|
{
|
|
sprintf (sb_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
|
|
}
|
|
else
|
|
{
|
|
sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
|
|
}
|
|
|
|
#if defined(__FreeBSD__)
|
|
printk ("sb0: <%s>", sb_dsp_operations.name);
|
|
#else
|
|
printk (" <%s>", sb_dsp_operations.name);
|
|
#endif
|
|
|
|
#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
|
|
if (!sb16) /*
|
|
* There is a better driver for SB16
|
|
*/
|
|
#endif
|
|
if (num_audiodevs < MAX_AUDIO_DEV)
|
|
{
|
|
audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations;
|
|
audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT;
|
|
audio_devs[my_dev]->buffsize = (
|
|
(sbc_major > 2 || sbc_major == 2 && sbc_minor > 0) ?
|
|
16 : 8) * 1024;
|
|
audio_devs[my_dev]->dmachan = hw_config->dma;
|
|
}
|
|
else
|
|
printk ("SB: Too many DSP devices available\n");
|
|
#else
|
|
printk (" <SoundBlaster (configured without audio support)>");
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_MIDI
|
|
if (!midi_disabled && !sb16) /*
|
|
* Midi don't work in the SB emulation mode *
|
|
* of PAS, SB16 has better midi interface
|
|
*/
|
|
sb_midi_init (sbc_major);
|
|
#endif
|
|
|
|
sb_dsp_ok = 1;
|
|
return mem_start;
|
|
}
|
|
|
|
void
|
|
sb_dsp_disable_midi (void)
|
|
{
|
|
midi_disabled = 1;
|
|
}
|
|
|
|
#endif
|