?? sb_dsp.c
字號(hào):
/*
* sound/sb_dsp.c
*
* The low level driver for the SoundBlaster DSP chip.
*
* Copyright by Hannu Savolainen 1993
*
* 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 "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;
/*
* 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;
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;
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;
int sb_dsp_model = 1; /* 1=SB, 2=SB Pro */
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);
int sb_dsp_command (unsigned char val);
#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
/* Common code for the midi and pcm functions */
int
sb_dsp_command (unsigned char val)
{
int i, limit;
limit = GET_TIME () + 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 unit)
{
int status, data;
#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 (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:
printk ("+");
data = INB (DSP_READ);
printk ("%x", data);
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))<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;
if (speed < 4000)
speed = 4000;
if (speed > 44100)
speed = 44100; /* Invalid speed */
if (sb_dsp_model == 1 && speed > 22050)
speed = 22050;
/* SB Classic doesn't support higher speed */
if (dsp_stereo && speed > 22050)
speed = 22050;
/* Max. stereo speed is 22050 */
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 (sb_dsp_model == 1 || 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 (sound_dsp_dmachan[dev] > 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)
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -