/* * linux/kernel/chr_drv/sound/sb_dsp.c * * The low level driver for the SoundBlaster DS chips. * * 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. * * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen * */ #include "sound_config.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) #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; 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 * 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 */ static int midi_disabled = 0; static int dsp_highspeed = 0, dsp_stereo = 0; static int dsp_current_speed = DSP_DEFAULT_SPEED; #ifndef EXCLUDE_SBPRO static int rec_devices = SOUND_MASK_MIC; static int hi_filter = 0, filter_in = 0, filter_out = 0; #endif static int midi_mode = NORMAL_MIDI; static int midi_busy = 0; /* 1 if the process has output to MIDI */ static int dsp_busy = 0; 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; /* 1=SB, 2=SB Pro */ static int duplex_midi = 0; static int my_dev = 0; static volatile int 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 #if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) /* Common code for the midi and pcm functions */ static int 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 * 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++) { if ((INB (DSP_STATUS) & 0x80) == 0) { OUTB (val, DSP_COMMAND); return 1; } } printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val); printk ("IRQ conflict???\n"); return 0; } void sbintr (int unused) { int status, data; status = INB (DSP_DATA_AVAIL);/* Clear interrupt */ if (intr_active) switch (irq_mode) { case IMODE_OUTPUT: intr_active = 0; DMAbuf_outputintr (my_dev); break; case IMODE_INPUT: 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; irq_ok = 1; break; case IMODE_MIDI: printk ("+"); data = INB (DSP_READ); printk ("%02x", data); break; default: printk ("SoundBlaster: Unexpected interrupt\n"); } } static int set_dsp_irq (int interrupt_level) { int retcode = EINVAL; #ifdef linux struct sigaction sa; sa.sa_handler = sbintr; #ifdef SND_SA_INTERRUPT sa.sa_flags = SA_INTERRUPT; #else sa.sa_flags = 0; #endif sa.sa_mask = 0; sa.sa_restorer = NULL; retcode = irqaction (interrupt_level, &sa); if (retcode < 0) { printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level); } #else /* # error Unimplemented for this OS */ #endif return retcode; } static int 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) dsp_command (DSP_CMD_SPKON); else 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 (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) && midi_busy) { printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); speed = 22050; } if (dsp_stereo) speed <<= 1; /* Now the speed should be valid */ if (speed > 22050) { /* High speed mode */ tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8); dsp_highspeed = 1; DISABLE_INTR (flags); if (dsp_command (0x40)) dsp_command (tconst); RESTORE_INTR (flags); speed = (256000000 / (65536 - (tconst << 8))); } else { dsp_highspeed = 0; tconst = (256 - (1000000 / speed)) & 0xff; DISABLE_INTR (flags); if (dsp_command (0x40)) /* Set time constant */ dsp_command (tconst); RESTORE_INTR (flags); speed = 1000000 / (256 - tconst); } if (dsp_stereo) speed >>= 1; dsp_current_speed = speed; return speed; } static int dsp_set_stereo (int mode) { dsp_stereo = 0; if (dsp_model == 1) return 0; /* Sorry no stereo */ if (mode && 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))); #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) { unsigned long flags; if (!irq_mode) dsp_speaker (ON); 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) { DISABLE_INTR (flags); if (dsp_command (0x48)) /* High speed size */ { dsp_command (count & 0xff); dsp_command ((count >> 8) & 0xff); 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 (dsp_command (0x14)) /* 8-bit DAC (DMA) */ { dsp_command (count & 0xff); dsp_command ((count >> 8) & 0xff); } else printk ("SB Error: Unable to start DAC\n"); RESTORE_INTR (flags); } intr_active = 1; } static void sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag) { /* Start a DMA input to the buffer pointed by dmaqtail */ unsigned long flags; if (!irq_mode) dsp_speaker (OFF); 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) { DISABLE_INTR (flags); if (dsp_command (0x48)) /* High speed size */ { dsp_command (count & 0xff); dsp_command ((count >> 8) & 0xff); 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 (dsp_command (0x24)) /* 8-bit ADC (DMA) */ { dsp_command (count & 0xff); dsp_command ((count >> 8) & 0xff); } else printk ("SB Error: Unable to start ADC\n"); RESTORE_INTR (flags); } intr_active = 1; } static void dsp_cleanup (void) { intr_active = 0; } static int sb_dsp_prepare_for_input (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (OFF); return 0; } static int sb_dsp_prepare_for_output (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (ON); return 0; } static void sb_dsp_halt_xfer (int dev) { } 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 (!irq_ok) { printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq); return RET_ERROR (ENXIO); } if (intr_active || (midi_busy && midi_mode == UART_MIDI)) { printk ("SB: PCM not possible during MIDI input\n"); return RET_ERROR (EBUSY); } if (mode != OPEN_READ && mode != OPEN_WRITE) { printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n"); return RET_ERROR (EINVAL); } retval = set_dsp_irq (sbc_irq); if (retval) return retval; if (!DMAbuf_open_dma (dev)) { RELEASE_IRQ (sbc_irq); printk ("SB: DMA Busy\n"); return RET_ERROR (EBUSY); } dsp_set_stereo (OFF); dsp_speed (DSP_DEFAULT_SPEED); irq_mode = IMODE_NONE; dsp_busy = 1; return 0; } static void sb_dsp_close (int dev) { DMAbuf_close_dma (dev); RELEASE_IRQ (sbc_irq); dsp_cleanup (); dsp_speed (DSP_DEFAULT_SPEED); dsp_set_stereo (OFF); dsp_speaker (OFF); dsp_busy = 0; } 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: 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; 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; 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); reset_dsp (); dsp_cleanup (); RESTORE_INTR (flags); } #endif 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 */ if (!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 *) ¶ms, 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 = { "SoundBlaster", 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, /* has_output_drained */ NULL /* copy_from_user */ }; #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; major = minor = 0; dsp_command (0xe1); /* Get version */ for (i = 1000; i; i--) { if (inb (DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ if (major == 0) major = inb (DSP_READ); else { minor = inb (DSP_READ); break; } } } #ifndef EXCLUDE_SBPRO if (detect_mixer ()) { sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor); init_mixer (); #if SBC_DMA < 4 /* This is a kludge for SB16 cards */ if (major == 3) dsp_model = 2; /* Do not enable if SB16 */ #endif mixer_devs[num_mixers++] = &sb_mixer_operations; if (major == 2 || major == 3) duplex_midi = 1; #ifndef EXCLUDE_YM8312 if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */ { enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); } #endif } else #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"); #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; #endif sb_dsp_ok = 1; return mem_start; } void sb_dsp_disable_midi (void) { midi_disabled = 1; } #endif