| |
| /* |
| * sound/gus_wave.c |
| * |
| * Driver for the Gravis UltraSound wave table synth. |
| * |
| * 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" |
| #include <linux/ultrasound.h> |
| #include "gus_hw.h" |
| |
| #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) |
| |
| #define MAX_SAMPLE 128 |
| #define MAX_PATCH 256 |
| |
| struct voice_info |
| { |
| unsigned long orig_freq; |
| unsigned long current_freq; |
| unsigned long mode; |
| int bender; |
| int bender_range; |
| int panning; |
| int midi_volume; |
| unsigned int initial_volume; |
| unsigned int current_volume; |
| int loop_irq_mode, loop_irq_parm; |
| #define LMODE_FINISH 1 |
| #define LMODE_PCM 2 |
| #define LMODE_PCM_STOP 3 |
| int volume_irq_mode, volume_irq_parm; |
| #define VMODE_HALT 1 |
| #define VMODE_ENVELOPE 2 |
| #define VMODE_START_NOTE 3 |
| |
| int env_phase; |
| unsigned char env_rate[6]; |
| unsigned char env_offset[6]; |
| |
| /* |
| * Volume computation parameters for gus_adagio_vol() |
| */ |
| int main_vol, expression_vol, patch_vol; |
| |
| /* Variables for "Ultraclick" removal */ |
| int dev_pending, note_pending, volume_pending, sample_pending; |
| char kill_pending; |
| long offset_pending; |
| |
| }; |
| |
| extern int gus_base; |
| extern int gus_irq, gus_dma; |
| extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; |
| extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; |
| extern int snd_raw_count[MAX_DSP_DEV]; |
| static long gus_mem_size = 0; |
| static long free_mem_ptr = 0; |
| static int gus_busy = 0; |
| static int nr_voices = 0; |
| static int gus_devnum = 0; |
| static int volume_base, volume_scale, volume_method; |
| static int gus_line_vol = 100, gus_mic_vol = 0; |
| static int gus_recmask = SOUND_MASK_MIC; |
| static int recording_active = 0; |
| |
| #define VOL_METHOD_ADAGIO 1 |
| int gus_wave_volume = 60; |
| int gus_pcm_volume = 80; |
| static unsigned char mix_image = 0x00; |
| |
| /* |
| * Current version of this driver doesn't allow synth and PCM functions |
| * at the same time. The active_device specifies the active driver |
| */ |
| static int active_device = 0; |
| |
| #define GUS_DEV_WAVE 1 /* |
| * * * Wave table synth */ |
| #define GUS_DEV_PCM_DONE 2 /* |
| * * * PCM device, transfer done */ |
| #define GUS_DEV_PCM_CONTINUE 3 /* |
| * * * PCM device, transfer the |
| * second * * * chn */ |
| |
| static int gus_sampling_speed; |
| static int gus_sampling_channels; |
| static int gus_sampling_bits; |
| |
| DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); |
| |
| /* |
| * Variables and buffers for PCM output |
| */ |
| #define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* |
| * * * Don't |
| * * * change |
| * |
| */ |
| |
| static int pcm_bsize, /* |
| * Current blocksize |
| */ |
| pcm_nblk, /* |
| * Current # of blocks |
| */ |
| pcm_banksize; /* |
| |
| * |
| * * * * # bytes allocated for channels */ |
| static int pcm_datasize[MAX_PCM_BUFFERS]; /* |
| |
| * |
| * * * * Actual # of bytes |
| * in blk * */ |
| static volatile int pcm_head, pcm_tail, pcm_qlen; /* |
| |
| * |
| * * * * DRAM queue |
| * */ |
| static volatile int pcm_active; |
| static int pcm_opened = 0; |
| static int pcm_current_dev; |
| static int pcm_current_block; |
| static unsigned long pcm_current_buf; |
| static int pcm_current_count; |
| static int pcm_current_intrflag; |
| |
| struct voice_info voices[32]; |
| |
| static int freq_div_table[] = |
| { |
| 44100, /* |
| * 14 |
| */ |
| 41160, /* |
| * 15 |
| */ |
| 38587, /* |
| * 16 |
| */ |
| 36317, /* |
| * 17 |
| */ |
| 34300, /* |
| * 18 |
| */ |
| 32494, /* |
| * 19 |
| */ |
| 30870, /* |
| * 20 |
| */ |
| 29400, /* |
| * 21 |
| */ |
| 28063, /* |
| * 22 |
| */ |
| 26843, /* |
| * 23 |
| */ |
| 25725, /* |
| * 24 |
| */ |
| 24696, /* |
| * 25 |
| */ |
| 23746, /* |
| * 26 |
| */ |
| 22866, /* |
| * 27 |
| */ |
| 22050, /* |
| * 28 |
| */ |
| 21289, /* |
| * 29 |
| */ |
| 20580, /* |
| * 30 |
| */ |
| 19916, /* |
| * 31 |
| */ |
| 19293 /* |
| * 32 |
| */ |
| }; |
| |
| static struct patch_info *samples; |
| static long sample_ptrs[MAX_SAMPLE + 1]; |
| static int sample_map[32]; |
| static int free_sample; |
| |
| |
| static int patch_table[MAX_PATCH]; |
| static int patch_map[32]; |
| |
| static struct synth_info gus_info = |
| {"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; |
| |
| static void gus_poke (long addr, unsigned char data); |
| static void compute_and_set_volume (int voice, int volume, int ramp_time); |
| extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); |
| static void compute_volume (int voice, int volume); |
| static void do_volume_irq (int voice); |
| static void set_input_volumes(void); |
| |
| #define INSTANT_RAMP -1 /* |
| * * * Dont use ramping */ |
| #define FAST_RAMP 0 /* |
| * * * Fastest possible ramp */ |
| |
| static void |
| reset_sample_memory (void) |
| { |
| int i; |
| |
| for (i = 0; i <= MAX_SAMPLE; i++) |
| sample_ptrs[i] = -1; |
| for (i = 0; i < 32; i++) |
| sample_map[i] = -1; |
| for (i = 0; i < 32; i++) |
| patch_map[i] = -1; |
| |
| gus_poke (0, 0); /* |
| * Put silence here |
| */ |
| gus_poke (1, 0); |
| |
| free_mem_ptr = 2; |
| free_sample = 0; |
| |
| for (i = 0; i < MAX_PATCH; i++) |
| patch_table[i] = -1; |
| } |
| |
| void |
| gus_delay (void) |
| { |
| int i; |
| |
| for (i = 0; i < 7; i++) |
| INB (u_DRAMIO); |
| } |
| |
| static void |
| gus_poke (long addr, unsigned char data) |
| { |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| OUTB (0x43, u_Command); |
| OUTB (addr & 0xff, u_DataLo); |
| OUTB ((addr >> 8) & 0xff, u_DataHi); |
| |
| OUTB (0x44, u_Command); |
| OUTB ((addr >> 16) & 0xff, u_DataHi); |
| OUTB (data, u_DRAMIO); |
| RESTORE_INTR (flags); |
| } |
| |
| static unsigned char |
| gus_peek (long addr) |
| { |
| unsigned long flags; |
| unsigned char tmp; |
| |
| DISABLE_INTR (flags); |
| OUTB (0x43, u_Command); |
| OUTB (addr & 0xff, u_DataLo); |
| OUTB ((addr >> 8) & 0xff, u_DataHi); |
| |
| OUTB (0x44, u_Command); |
| OUTB ((addr >> 16) & 0xff, u_DataHi); |
| tmp = INB (u_DRAMIO); |
| RESTORE_INTR (flags); |
| |
| return tmp; |
| } |
| |
| void |
| gus_write8 (int reg, unsigned int data) |
| { |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| |
| OUTB (reg, u_Command); |
| OUTB ((unsigned char) (data & 0xff), u_DataHi); |
| |
| RESTORE_INTR (flags); |
| } |
| |
| unsigned char |
| gus_read8 (int reg) |
| { |
| unsigned long flags; |
| unsigned char val; |
| |
| DISABLE_INTR (flags); |
| OUTB (reg | 0x80, u_Command); |
| val = INB (u_DataHi); |
| RESTORE_INTR (flags); |
| |
| return val; |
| } |
| |
| unsigned char |
| gus_look8 (int reg) |
| { |
| unsigned long flags; |
| unsigned char val; |
| |
| DISABLE_INTR (flags); |
| OUTB (reg, u_Command); |
| val = INB (u_DataHi); |
| RESTORE_INTR (flags); |
| |
| return val; |
| } |
| |
| void |
| gus_write16 (int reg, unsigned int data) |
| { |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| |
| OUTB (reg, u_Command); |
| |
| OUTB ((unsigned char) (data & 0xff), u_DataLo); |
| OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi); |
| |
| RESTORE_INTR (flags); |
| } |
| |
| unsigned short |
| gus_read16 (int reg) |
| { |
| unsigned long flags; |
| unsigned char hi, lo; |
| |
| DISABLE_INTR (flags); |
| |
| OUTB (reg | 0x80, u_Command); |
| |
| lo = INB (u_DataLo); |
| hi = INB (u_DataHi); |
| |
| RESTORE_INTR (flags); |
| |
| return ((hi << 8) & 0xff00) | lo; |
| } |
| |
| void |
| gus_write_addr (int reg, unsigned long address, int is16bit) |
| { |
| unsigned long hold_address; |
| |
| if (is16bit) |
| { |
| /* |
| * Special processing required for 16 bit patches |
| */ |
| |
| hold_address = address; |
| address = address >> 1; |
| address &= 0x0001ffffL; |
| address |= (hold_address & 0x000c0000L); |
| } |
| |
| gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); |
| gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); |
| } |
| |
| static void |
| gus_select_voice (int voice) |
| { |
| if (voice < 0 || voice > 31) |
| return; |
| |
| OUTB (voice, u_Voice); |
| } |
| |
| static void |
| gus_select_max_voices (int nvoices) |
| { |
| if (nvoices < 14) |
| nvoices = 14; |
| if (nvoices > 32) |
| nvoices = 32; |
| |
| nr_voices = nvoices; |
| |
| gus_write8 (0x0e, (nvoices - 1) | 0xc0); |
| } |
| |
| static void |
| gus_voice_on (unsigned int mode) |
| { |
| gus_write8 (0x00, (unsigned char) (mode & 0xfc)); |
| gus_delay (); |
| gus_write8 (0x00, (unsigned char) (mode & 0xfc)); |
| } |
| |
| static void |
| gus_voice_off (void) |
| { |
| gus_write8 (0x00, gus_read8 (0x00) | 0x03); |
| } |
| |
| static void |
| gus_voice_mode (unsigned int m) |
| { |
| unsigned char mode = (unsigned char) (m & 0xff); |
| |
| gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* |
| * Don't |
| * start |
| * or |
| * stop |
| * * |
| * voice |
| */ |
| gus_delay (); |
| gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); |
| } |
| |
| static void |
| gus_voice_freq (unsigned long freq) |
| { |
| unsigned long divisor = freq_div_table[nr_voices - 14]; |
| unsigned short fc; |
| |
| fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); |
| fc = fc << 1; |
| |
| gus_write16 (0x01, fc); |
| } |
| |
| static void |
| gus_voice_volume (unsigned int vol) |
| { |
| gus_write8 (0x0d, 0x03); /* |
| * Stop ramp before setting volume |
| */ |
| gus_write16 (0x09, (unsigned short) (vol << 4)); |
| } |
| |
| static void |
| gus_voice_balance (unsigned int balance) |
| { |
| gus_write8 (0x0c, (unsigned char) (balance & 0xff)); |
| } |
| |
| static void |
| gus_ramp_range (unsigned int low, unsigned int high) |
| { |
| gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); |
| gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); |
| } |
| |
| static void |
| gus_ramp_rate (unsigned int scale, unsigned int rate) |
| { |
| gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); |
| } |
| |
| static void |
| gus_rampon (unsigned int m) |
| { |
| unsigned char mode = (unsigned char) (m & 0xff); |
| |
| gus_write8 (0x0d, mode & 0xfc); |
| gus_delay (); |
| gus_write8 (0x0d, mode & 0xfc); |
| } |
| |
| static void |
| gus_ramp_mode (unsigned int m) |
| { |
| unsigned char mode = (unsigned char) (m & 0xff); |
| |
| gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* |
| * Don't |
| * start |
| * or |
| * stop |
| * * |
| * ramping |
| */ |
| gus_delay (); |
| gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); |
| } |
| |
| static void |
| gus_rampoff (void) |
| { |
| gus_write8 (0x0d, 0x03); |
| } |
| |
| static void |
| gus_set_voice_pos (int voice, long position) |
| { |
| int sample_no; |
| |
| if ((sample_no = sample_map[voice]) != -1) |
| if (position < samples[sample_no].len) |
| if (voices[voice].volume_irq_mode == VMODE_START_NOTE) |
| voices[voice].offset_pending = position; |
| else |
| gus_write_addr (0x0a, sample_ptrs[sample_no] + position, |
| samples[sample_no].mode & WAVE_16_BITS); |
| } |
| |
| static void |
| gus_voice_init (int voice) |
| { |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_volume (0); |
| gus_write_addr (0x0a, 0, 0); /* |
| * Set current position to 0 |
| */ |
| gus_write8 (0x00, 0x03); /* |
| * Voice off |
| */ |
| gus_write8 (0x0d, 0x03); /* |
| * Ramping off |
| */ |
| RESTORE_INTR (flags); |
| |
| } |
| |
| static void |
| gus_voice_init2 (int voice) |
| { |
| voices[voice].panning = 0; |
| voices[voice].mode = 0; |
| voices[voice].orig_freq = 20000; |
| voices[voice].current_freq = 20000; |
| voices[voice].bender = 0; |
| voices[voice].bender_range = 200; |
| voices[voice].initial_volume = 0; |
| voices[voice].current_volume = 0; |
| voices[voice].loop_irq_mode = 0; |
| voices[voice].loop_irq_parm = 0; |
| voices[voice].volume_irq_mode = 0; |
| voices[voice].volume_irq_parm = 0; |
| voices[voice].env_phase = 0; |
| voices[voice].main_vol = 127; |
| voices[voice].patch_vol = 127; |
| voices[voice].expression_vol = 127; |
| voices[voice].sample_pending = -1; |
| } |
| |
| static void |
| step_envelope (int voice) |
| { |
| unsigned vol, prev_vol, phase; |
| unsigned char rate; |
| |
| if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) |
| { |
| gus_rampoff (); |
| return; /* |
| * Sustain |
| */ |
| } |
| |
| if (voices[voice].env_phase >= 5) |
| { |
| /* |
| * Shoot the voice off |
| */ |
| |
| gus_voice_init (voice); |
| return; |
| } |
| |
| prev_vol = voices[voice].current_volume; |
| gus_voice_volume (prev_vol); |
| phase = ++voices[voice].env_phase; |
| |
| compute_volume (voice, voices[voice].midi_volume); |
| |
| vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; |
| rate = voices[voice].env_rate[phase]; |
| gus_write8 (0x06, rate); /* |
| * Ramping rate |
| */ |
| |
| voices[voice].volume_irq_mode = VMODE_ENVELOPE; |
| |
| if (((vol - prev_vol) / 64) == 0) /* |
| * No significant volume change |
| */ |
| { |
| step_envelope (voice); /* |
| * Continue with the next phase |
| */ |
| return; |
| } |
| |
| if (vol > prev_vol) |
| { |
| if (vol >= (4096 - 64)) |
| vol = 4096 - 65; |
| gus_ramp_range (0, vol); |
| gus_rampon (0x20); /* |
| * Increasing, irq |
| */ |
| } |
| else |
| { |
| if (vol <= 64) |
| vol = 65; |
| gus_ramp_range (vol, 4030); |
| gus_rampon (0x60); /* |
| * Decreasing, irq |
| */ |
| } |
| voices[voice].current_volume = vol; |
| } |
| |
| static void |
| init_envelope (int voice) |
| { |
| voices[voice].env_phase = -1; |
| voices[voice].current_volume = 64; |
| |
| step_envelope (voice); |
| } |
| |
| static void |
| start_release (int voice) |
| { |
| if (gus_read8 (0x00) & 0x03) |
| return; /* |
| * Voice already stopped |
| */ |
| |
| voices[voice].env_phase = 2; /* |
| * Will be incremented by step_envelope |
| */ |
| |
| voices[voice].current_volume = |
| voices[voice].initial_volume = |
| gus_read16 (0x09) >> 4; /* |
| * Get current volume |
| */ |
| |
| voices[voice].mode &= ~WAVE_SUSTAIN_ON; |
| gus_rampoff (); |
| step_envelope (voice); |
| } |
| |
| static void |
| gus_voice_fade (int voice) |
| { |
| int instr_no = sample_map[voice], is16bits; |
| |
| if (instr_no < 0 || instr_no > MAX_SAMPLE) |
| { |
| gus_write8 (0x00, 0x03); /* |
| * Hard stop |
| */ |
| return; |
| } |
| |
| is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* |
| * 8 or 16 |
| * bit |
| * samples |
| */ |
| |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| { |
| start_release (voice); |
| return; |
| } |
| |
| /* |
| * Ramp the volume down but not too quickly. |
| */ |
| if ((gus_read16 (0x09) >> 4) < 100) /* |
| * Get current volume |
| */ |
| { |
| gus_voice_off (); |
| gus_rampoff (); |
| gus_voice_init (voice); |
| return; |
| } |
| |
| gus_ramp_range (65, 4030); |
| gus_ramp_rate (2, 4); |
| gus_rampon (0x40 | 0x20); /* |
| * Down, once, irq |
| */ |
| voices[voice].volume_irq_mode = VMODE_HALT; |
| } |
| |
| static void |
| gus_reset (void) |
| { |
| int i; |
| |
| gus_select_max_voices (24); |
| volume_base = 3071; |
| volume_scale = 4; |
| volume_method = VOL_METHOD_ADAGIO; |
| |
| for (i = 0; i < 32; i++) |
| { |
| gus_voice_init (i); /* |
| * Turn voice off |
| */ |
| gus_voice_init2 (i); |
| } |
| |
| INB (u_Status); /* |
| * Touch the status register |
| */ |
| |
| gus_look8 (0x41); /* |
| * Clear any pending DMA IRQs |
| */ |
| gus_look8 (0x49); /* |
| * Clear any pending sample IRQs |
| */ |
| gus_read8 (0x0f); /* |
| * Clear pending IRQs |
| */ |
| |
| } |
| |
| static void |
| gus_initialize (void) |
| { |
| unsigned long flags; |
| unsigned char dma_image, irq_image, tmp; |
| |
| static unsigned char gus_irq_map[16] = |
| {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7}; |
| |
| static unsigned char gus_dma_map[8] = |
| {0, 1, 0, 2, 0, 3, 4, 5}; |
| |
| DISABLE_INTR (flags); |
| |
| gus_write8 (0x4c, 0); /* |
| * Reset GF1 |
| */ |
| gus_delay (); |
| gus_delay (); |
| |
| gus_write8 (0x4c, 1); /* |
| * Release Reset |
| */ |
| gus_delay (); |
| gus_delay (); |
| |
| /* |
| * Clear all interrupts |
| */ |
| |
| gus_write8 (0x41, 0); /* |
| * DMA control |
| */ |
| gus_write8 (0x45, 0); /* |
| * Timer control |
| */ |
| gus_write8 (0x49, 0); /* |
| * Sample control |
| */ |
| |
| gus_select_max_voices (24); |
| |
| INB (u_Status); /* |
| * Touch the status register |
| */ |
| |
| gus_look8 (0x41); /* |
| * Clear any pending DMA IRQs |
| */ |
| gus_look8 (0x49); /* |
| * Clear any pending sample IRQs |
| */ |
| gus_read8 (0x0f); /* |
| * Clear pending IRQs |
| */ |
| |
| gus_reset (); /* |
| * Resets all voices |
| */ |
| |
| gus_look8 (0x41); /* |
| * Clear any pending DMA IRQs |
| */ |
| gus_look8 (0x49); /* |
| * Clear any pending sample IRQs |
| */ |
| gus_read8 (0x0f); /* |
| * Clear pending IRQs |
| */ |
| |
| gus_write8 (0x4c, 7); /* |
| * Master reset | DAC enable | IRQ enable |
| */ |
| |
| /* |
| * Set up for Digital ASIC |
| */ |
| |
| OUTB (0x05, gus_base + 0x0f); |
| |
| mix_image |= 0x02; /* |
| * Disable line out |
| */ |
| OUTB (mix_image, u_Mixer); |
| |
| OUTB (0x00, u_IRQDMAControl); |
| |
| OUTB (0x00, gus_base + 0x0f); |
| |
| /* |
| * Now set up the DMA and IRQ interface |
| * |
| * The GUS supports two IRQs and two DMAs. |
| * |
| * Just one DMA channel is used. This prevents simultaneous ADC and DAC. |
| * Adding this support requires significant changes to the dmabuf.c, dsp.c |
| * and audio.c also. |
| */ |
| |
| irq_image = 0; |
| tmp = gus_irq_map[gus_irq]; |
| if (!tmp) |
| printk ("Warning! GUS IRQ not selected\n"); |
| irq_image |= tmp; |
| irq_image |= 0x40; /* |
| * Combine IRQ1 (GF1) and IRQ2 (Midi) |
| */ |
| |
| dma_image = 0x40; /* |
| * Combine DMA1 (DRAM) and IRQ2 (ADC) |
| */ |
| tmp = gus_dma_map[gus_dma]; |
| if (!tmp) |
| printk ("Warning! GUS DMA not selected\n"); |
| dma_image |= tmp; |
| |
| /* |
| * For some reason the IRQ and DMA addresses must be written twice |
| */ |
| |
| /* |
| * Doing it first time |
| */ |
| |
| OUTB (mix_image, u_Mixer); /* |
| * Select DMA control |
| */ |
| OUTB (dma_image | 0x80, u_IRQDMAControl); /* |
| * Set DMA address |
| */ |
| |
| OUTB (mix_image | 0x40, u_Mixer); /* |
| * Select IRQ control |
| */ |
| OUTB (irq_image, u_IRQDMAControl); /* |
| * Set IRQ address |
| */ |
| |
| /* |
| * Doing it second time |
| */ |
| |
| OUTB (mix_image, u_Mixer); /* |
| * Select DMA control |
| */ |
| OUTB (dma_image, u_IRQDMAControl); /* |
| * Set DMA address |
| */ |
| |
| OUTB (mix_image | 0x40, u_Mixer); /* |
| * Select IRQ control |
| */ |
| OUTB (irq_image, u_IRQDMAControl); /* |
| * Set IRQ address |
| */ |
| |
| gus_select_voice (0); /* |
| * This disables writes to IRQ/DMA reg |
| */ |
| |
| mix_image &= ~0x02; /* |
| * Enable line out |
| */ |
| mix_image |= 0x08; /* |
| * Enable IRQ |
| */ |
| OUTB (mix_image, u_Mixer); /* |
| * Turn mixer channels on |
| * Note! Mic in is left off. |
| */ |
| |
| gus_select_voice (0); /* |
| * This disables writes to IRQ/DMA reg |
| */ |
| |
| gusintr (0); /* |
| * Serve pending interrupts |
| */ |
| RESTORE_INTR (flags); |
| } |
| |
| int |
| gus_wave_detect (int baseaddr) |
| { |
| unsigned long i; |
| unsigned long loc; |
| |
| gus_base = baseaddr; |
| |
| gus_write8 (0x4c, 0); /* Reset GF1 */ |
| gus_delay (); |
| gus_delay (); |
| |
| gus_write8 (0x4c, 1); /* Release Reset */ |
| gus_delay (); |
| gus_delay (); |
| |
| /* See if there is first block there.... */ |
| gus_poke (0L, 0xaa); |
| if (gus_peek (0L) != 0xaa) |
| return (0); |
| |
| /* Now zero it out so that I can check for mirroring .. */ |
| gus_poke (0L, 0x00); |
| for (i = 1L; i < 1024L; i++) |
| { |
| int n, failed; |
| |
| /* check for mirroring ... */ |
| if (gus_peek (0L) != 0) |
| break; |
| loc = i << 10; |
| |
| for (n = loc - 1, failed = 0; n <= loc; n++) |
| { |
| gus_poke (loc, 0xaa); |
| if (gus_peek (loc) != 0xaa) |
| failed = 1; |
| |
| gus_poke (loc, 0x55); |
| if (gus_peek (loc) != 0x55) |
| failed = 1; |
| } |
| |
| if (failed) |
| break; |
| } |
| gus_mem_size = i << 10; |
| return 1; |
| } |
| |
| static int |
| guswave_ioctl (int dev, |
| unsigned int cmd, unsigned int arg) |
| { |
| |
| switch (cmd) |
| { |
| case SNDCTL_SYNTH_INFO: |
| gus_info.nr_voices = nr_voices; |
| IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info)); |
| return 0; |
| break; |
| |
| case SNDCTL_SEQ_RESETSAMPLES: |
| reset_sample_memory (); |
| return 0; |
| break; |
| |
| case SNDCTL_SEQ_PERCMODE: |
| return 0; |
| break; |
| |
| case SNDCTL_SYNTH_MEMAVL: |
| return gus_mem_size - free_mem_ptr - 32; |
| |
| default: |
| return RET_ERROR (EINVAL); |
| } |
| } |
| |
| static int |
| guswave_set_instr (int dev, int voice, int instr_no) |
| { |
| int sample_no; |
| |
| if (instr_no < 0 || instr_no > MAX_PATCH) |
| return RET_ERROR (EINVAL); |
| |
| if (voice < 0 || voice > 31) |
| return RET_ERROR (EINVAL); |
| |
| if (voices[voice].volume_irq_mode == VMODE_START_NOTE) |
| { |
| voices[voice].sample_pending = instr_no; |
| return 0; |
| } |
| |
| sample_no = patch_table[instr_no]; |
| patch_map[voice] = -1; |
| |
| if (sample_no < 0) |
| { |
| printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); |
| return RET_ERROR (EINVAL); /* |
| * Patch not defined |
| */ |
| } |
| |
| if (sample_ptrs[sample_no] == -1) /* |
| * Sample not loaded |
| */ |
| { |
| printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); |
| return RET_ERROR (EINVAL); |
| } |
| |
| sample_map[voice] = sample_no; |
| patch_map[voice] = instr_no; |
| return 0; |
| } |
| |
| static int |
| #ifdef FUTURE_VERSION |
| guswave_kill_note (int dev, int voice, int note, int velocity) |
| #else |
| guswave_kill_note (int dev, int voice, int velocity) |
| #endif |
| { |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| if (voices[voice].volume_irq_mode == VMODE_START_NOTE) |
| voices[voice].kill_pending = 1; |
| else |
| { |
| gus_select_voice (voice); |
| gus_voice_fade (voice); |
| } |
| RESTORE_INTR (flags); |
| |
| return 0; |
| } |
| |
| static void |
| guswave_aftertouch (int dev, int voice, int pressure) |
| { |
| short lo_limit, hi_limit; |
| unsigned long flags; |
| |
| return; /* |
| * Currently disabled |
| */ |
| |
| if (voice < 0 || voice > 31) |
| return; |
| |
| if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) |
| return; /* |
| * Don't mix with envelopes |
| */ |
| |
| if (pressure < 32) |
| { |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_rampoff (); |
| compute_and_set_volume (voice, 255, 0); /* |
| * Back to original volume |
| */ |
| RESTORE_INTR (flags); |
| return; |
| } |
| |
| hi_limit = voices[voice].current_volume; |
| lo_limit = hi_limit * 99 / 100; |
| if (lo_limit < 65) |
| lo_limit = 65; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| if (hi_limit > (4095 - 65)) |
| { |
| hi_limit = 4095 - 65; |
| gus_voice_volume (hi_limit); |
| } |
| gus_ramp_range (lo_limit, hi_limit); |
| gus_ramp_rate (3, 8); |
| gus_rampon (0x58); /* |
| * Bidirectional, Down, Loop |
| */ |
| RESTORE_INTR (flags); |
| } |
| |
| static void |
| guswave_panning (int dev, int voice, int value) |
| { |
| if (voice >= 0 || voice < 32) |
| voices[voice].panning = value; |
| } |
| |
| static void |
| compute_volume (int voice, int volume) |
| { |
| if (volume < 128) |
| voices[voice].midi_volume = volume; |
| |
| switch (volume_method) |
| { |
| case VOL_METHOD_ADAGIO: |
| voices[voice].initial_volume = |
| gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, |
| voices[voice].expression_vol, |
| voices[voice].patch_vol); |
| break; |
| |
| default: |
| voices[voice].initial_volume = volume_base + |
| (voices[voice].midi_volume * volume_scale); |
| } |
| |
| if (voices[voice].initial_volume > 4030) |
| voices[voice].initial_volume = 4030; |
| } |
| |
| static void |
| compute_and_set_volume (int voice, int volume, int ramp_time) |
| { |
| int current, target, rate; |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| /* |
| * CAUTION! Interrupts disabled. Enable them before returning |
| */ |
| |
| gus_select_voice (voice); |
| |
| compute_volume (voice, volume); |
| voices[voice].current_volume = voices[voice].initial_volume; |
| |
| current = gus_read16 (0x09) >> 4; |
| target = voices[voice].initial_volume; |
| |
| if (ramp_time == INSTANT_RAMP) |
| { |
| gus_rampoff (); |
| gus_voice_volume (target); |
| RESTORE_INTR (flags); |
| return; |
| } |
| |
| if (ramp_time == FAST_RAMP) |
| rate = 63; |
| else |
| rate = 16; |
| gus_ramp_rate (0, rate); |
| |
| if ((target - current) / 64 == 0) /* |
| * Too close |
| */ |
| { |
| gus_rampoff (); |
| gus_voice_volume (target); |
| RESTORE_INTR (flags); |
| return; |
| } |
| |
| if (target > current) |
| { |
| if (target > (4095 - 65)) |
| target = 4095 - 65; |
| gus_ramp_range (current, target); |
| gus_rampon (0x00); /* |
| * Ramp up, once, no irq |
| */ |
| } |
| else |
| { |
| if (target < 65) |
| target = 65; |
| |
| gus_ramp_range (target, current); |
| gus_rampon (0x40); /* |
| * Ramp down, once, no irq |
| */ |
| } |
| RESTORE_INTR (flags); |
| } |
| |
| static void |
| dynamic_volume_change (int voice) |
| { |
| unsigned char status; |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| status = gus_read8 (0x00); /* |
| * Voice status |
| */ |
| RESTORE_INTR (flags); |
| |
| if (status & 0x03) |
| return; /* |
| * Voice not started |
| */ |
| |
| if (!(voices[voice].mode & WAVE_ENVELOPES)) |
| { |
| compute_and_set_volume (voice, voices[voice].midi_volume, 1); |
| return; |
| } |
| |
| /* |
| * Voice is running and has envelopes. |
| */ |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| status = gus_read8 (0x0d); /* |
| * Ramping status |
| */ |
| RESTORE_INTR (flags); |
| |
| if (status & 0x03) /* |
| * Sustain phase? |
| */ |
| { |
| compute_and_set_volume (voice, voices[voice].midi_volume, 1); |
| return; |
| } |
| |
| if (voices[voice].env_phase < 0) |
| return; |
| |
| compute_volume (voice, voices[voice].midi_volume); |
| |
| #if 0 /* |
| * * * Is this really required */ |
| voices[voice].current_volume = |
| gus_read16 (0x09) >> 4; /* |
| * Get current volume |
| */ |
| |
| voices[voice].env_phase--; |
| step_envelope (voice); |
| #endif |
| } |
| |
| static void |
| guswave_controller (int dev, int voice, int ctrl_num, int value) |
| { |
| unsigned long flags; |
| unsigned long freq; |
| |
| if (voice < 0 || voice > 31) |
| return; |
| |
| switch (ctrl_num) |
| { |
| case CTRL_PITCH_BENDER: |
| voices[voice].bender = value; |
| |
| if (voices[voice].volume_irq_mode != VMODE_START_NOTE) |
| { |
| freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); |
| voices[voice].current_freq = freq; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_freq (freq); |
| RESTORE_INTR (flags); |
| } |
| break; |
| |
| case CTRL_PITCH_BENDER_RANGE: |
| voices[voice].bender_range = value; |
| break; |
| #ifdef FUTURE_VERSION |
| case CTL_EXPRESSION: |
| value /= 128; |
| #endif |
| case CTRL_EXPRESSION: |
| volume_method = VOL_METHOD_ADAGIO; |
| voices[voice].expression_vol = value; |
| if (voices[voice].volume_irq_mode != VMODE_START_NOTE) |
| dynamic_volume_change (voice); |
| break; |
| |
| #ifdef FUTURE_VERSION |
| case CTL_PAN: |
| voices[voice].panning = (value * 2) - 128; |
| break; |
| |
| case CTL_MAIN_VOLUME: |
| value = (value * 100) / 16383; |
| #endif |
| |
| case CTRL_MAIN_VOLUME: |
| volume_method = VOL_METHOD_ADAGIO; |
| voices[voice].main_vol = value; |
| if (voices[voice].volume_irq_mode != VMODE_START_NOTE) |
| dynamic_volume_change (voice); |
| break; |
| |
| default: /* |
| * Ignore |
| */ |
| break; |
| } |
| } |
| |
| static int |
| guswave_start_note2 (int dev, int voice, int note_num, int volume) |
| { |
| int sample, best_sample, best_delta, delta_freq; |
| int is16bits, samplep, patch, pan; |
| unsigned long note_freq, base_note, freq, flags; |
| unsigned char mode = 0; |
| |
| if (voice < 0 || voice > 31) |
| { |
| printk ("GUS: Invalid voice\n"); |
| return RET_ERROR (EINVAL); |
| } |
| |
| if (note_num == 255) |
| { |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| { |
| voices[voice].midi_volume = volume; |
| dynamic_volume_change (voice); |
| return 0; |
| } |
| |
| compute_and_set_volume (voice, volume, 1); |
| return 0; |
| } |
| |
| if ((patch = patch_map[voice]) == -1) |
| { |
| return RET_ERROR (EINVAL); |
| } |
| |
| if ((samplep = patch_table[patch]) == -1) |
| { |
| return RET_ERROR (EINVAL); |
| } |
| |
| note_freq = note_to_freq (note_num); |
| |
| /* |
| * Find a sample within a patch so that the note_freq is between low_note |
| * and high_note. |
| */ |
| sample = -1; |
| |
| best_sample = samplep; |
| best_delta = 1000000; |
| while (samplep >= 0 && sample == -1) |
| { |
| delta_freq = note_freq - samples[samplep].base_note; |
| if (delta_freq < 0) |
| delta_freq = -delta_freq; |
| if (delta_freq < best_delta) |
| { |
| best_sample = samplep; |
| best_delta = delta_freq; |
| } |
| if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) |
| sample = samplep; |
| else |
| samplep = samples[samplep].key; /* |
| * Follow link |
| */ |
| } |
| if (sample == -1) |
| sample = best_sample; |
| |
| if (sample == -1) |
| { |
| printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); |
| return 0; /* |
| * Should play default patch ??? |
| */ |
| } |
| |
| is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* |
| * 8 or 16 |
| * bit |
| * samples |
| */ |
| voices[voice].mode = samples[sample].mode; |
| voices[voice].patch_vol = samples[sample].volume; |
| |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| { |
| int i; |
| |
| for (i = 0; i < 6; i++) |
| { |
| voices[voice].env_rate[i] = samples[sample].env_rate[i]; |
| voices[voice].env_offset[i] = samples[sample].env_offset[i]; |
| } |
| } |
| |
| sample_map[voice] = sample; |
| |
| base_note = samples[sample].base_note / 100; /* |
| * To avoid overflows |
| */ |
| note_freq /= 100; |
| |
| freq = samples[sample].base_freq * note_freq / base_note; |
| |
| voices[voice].orig_freq = freq; |
| |
| /* |
| * Since the pitch bender may have been set before playing the note, we |
| * have to calculate the bending now. |
| */ |
| |
| freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); |
| voices[voice].current_freq = freq; |
| |
| pan = (samples[sample].panning + voices[voice].panning) / 32; |
| pan += 7; |
| if (pan < 0) |
| pan = 0; |
| if (pan > 15) |
| pan = 15; |
| |
| if (samples[sample].mode & WAVE_16_BITS) |
| { |
| mode |= 0x04; /* |
| * 16 bits |
| */ |
| if ((sample_ptrs[sample] >> 18) != |
| ((sample_ptrs[sample] + samples[sample].len) >> 18)) |
| printk ("GUS: Sample address error\n"); |
| } |
| |
| /************************************************************************* |
| * CAUTION! Interrupts disabled. Don't return before enabling |
| *************************************************************************/ |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_off (); /* |
| * It may still be running |
| */ |
| gus_rampoff (); |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| { |
| compute_volume (voice, volume); |
| init_envelope (voice); |
| } |
| else |
| compute_and_set_volume (voice, volume, 0); |
| |
| if (samples[sample].mode & WAVE_LOOP_BACK) |
| gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - |
| voices[voice].offset_pending, is16bits); /* Sample |
| * start=end */ |
| else |
| gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, |
| is16bits); /* Sample start=begin */ |
| |
| if (samples[sample].mode & WAVE_LOOPING) |
| { |
| mode |= 0x08; /* |
| * Looping on |
| */ |
| |
| if (samples[sample].mode & WAVE_BIDIR_LOOP) |
| mode |= 0x10; /* |
| * Bidirectional looping on |
| */ |
| |
| if (samples[sample].mode & WAVE_LOOP_BACK) |
| { |
| gus_write_addr (0x0a, |
| sample_ptrs[sample] + samples[sample].loop_end - |
| voices[voice].offset_pending, is16bits); |
| mode |= 0x40; |
| } |
| |
| gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* |
| * Loop |
| * start |
| * location |
| */ |
| gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* |
| * Loop |
| * end |
| * location |
| */ |
| } |
| else |
| { |
| mode |= 0x20; /* |
| * Loop irq at the end |
| */ |
| voices[voice].loop_irq_mode = LMODE_FINISH; /* |
| * Ramp it down at |
| * the * end |
| */ |
| voices[voice].loop_irq_parm = 1; |
| gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* |
| * Loop start |
| * location |
| */ |
| gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len-1, is16bits); /* |
| * Loop |
| * end |
| * location |
| */ |
| } |
| gus_voice_freq (freq); |
| gus_voice_balance (pan); |
| gus_voice_on (mode); |
| RESTORE_INTR (flags); |
| |
| return 0; |
| } |
| |
| /* |
| * * New guswave_start_note by Andrew J. Robinson attempts to minimize |
| * clicking * when the note playing on the voice is changed. It uses volume |
| * ramping. */ |
| |
| static int |
| guswave_start_note (int dev, int voice, int note_num, int volume) |
| { |
| long int flags; |
| int mode; |
| int ret_val = 0; |
| |
| DISABLE_INTR (flags); |
| if (note_num == 255) |
| { |
| if (voices[voice].volume_irq_mode == VMODE_START_NOTE) |
| voices[voice].volume_pending = volume; |
| else |
| ret_val = guswave_start_note2 (dev, voice, note_num, volume); |
| } |
| else |
| { |
| gus_select_voice (voice); |
| mode = gus_read8 (0x00); |
| if (mode & 0x20) |
| gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ |
| |
| voices[voice].offset_pending = 0; |
| voices[voice].kill_pending = 0; |
| voices[voice].volume_irq_mode = 0; |
| voices[voice].loop_irq_mode = 0; |
| |
| if (voices[voice].sample_pending >= 0) |
| { |
| guswave_set_instr (voices[voice].dev_pending, voice, |
| voices[voice].sample_pending); |
| voices[voice].sample_pending = -1; |
| } |
| |
| if ((mode & 0x01) || ((gus_read16 (0x09) >> 4) < 2065)) |
| { |
| ret_val = guswave_start_note2 (dev, voice, note_num, volume); |
| } |
| else |
| { |
| voices[voice].dev_pending = dev; |
| voices[voice].note_pending = note_num; |
| voices[voice].volume_pending = volume; |
| voices[voice].volume_irq_mode = VMODE_START_NOTE; |
| |
| gus_rampoff (); |
| gus_ramp_range (2000, 4065); |
| gus_ramp_rate (0, 63); /* Fastest possible rate */ |
| gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ |
| } |
| } |
| RESTORE_INTR (flags); |
| return ret_val; |
| } |
| |
| static void |
| guswave_reset (int dev) |
| { |
| int i; |
| |
| for (i = 0; i < 32; i++) |
| { |
| gus_voice_init (i); |
| gus_voice_init2 (i); |
| } |
| } |
| |
| static int |
| guswave_open (int dev, int mode) |
| { |
| int err; |
| |
| if (gus_busy) |
| return RET_ERROR (EBUSY); |
| |
| gus_initialize (); |
| |
| if ((err = DMAbuf_open_dma (gus_devnum))) |
| return err; |
| |
| RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); |
| gus_busy = 1; |
| active_device = GUS_DEV_WAVE; |
| |
| gus_reset (); |
| |
| return 0; |
| } |
| |
| static void |
| guswave_close (int dev) |
| { |
| gus_busy = 0; |
| active_device = 0; |
| gus_reset (); |
| |
| DMAbuf_close_dma (gus_devnum); |
| } |
| |
| static int |
| guswave_load_patch (int dev, int format, snd_rw_buf * addr, |
| int offs, int count, int pmgr_flag) |
| { |
| struct patch_info patch; |
| int instr; |
| long sizeof_patch; |
| |
| unsigned long blk_size, blk_end, left, src_offs, target; |
| |
| sizeof_patch = (long) &patch.data[0] - (long) &patch; /* |
| * Size of |
| * the header |
| * * info |
| */ |
| |
| if (format != GUS_PATCH) |
| { |
| printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); |
| return RET_ERROR (EINVAL); |
| } |
| |
| if (count < sizeof_patch) |
| { |
| printk ("GUS Error: Patch header too short\n"); |
| return RET_ERROR (EINVAL); |
| } |
| |
| count -= sizeof_patch; |
| |
| if (free_sample >= MAX_SAMPLE) |
| { |
| printk ("GUS: Sample table full\n"); |
| return RET_ERROR (ENOSPC); |
| } |
| |
| /* |
| * Copy the header from user space but ignore the first bytes which have |
| * been transferred already. |
| */ |
| |
| COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs); |
| |
| instr = patch.instr_no; |
| |
| if (instr < 0 || instr > MAX_PATCH) |
| { |
| printk ("GUS: Invalid patch number %d\n", instr); |
| return RET_ERROR (EINVAL); |
| } |
| |
| if (count < patch.len) |
| { |
| printk ("GUS Warning: Patch record too short (%d<%d)\n", |
| count, (int) patch.len); |
| patch.len = count; |
| } |
| |
| if (patch.len <= 0 || patch.len > gus_mem_size) |
| { |
| printk ("GUS: Invalid sample length %d\n", (int) patch.len); |
| return RET_ERROR (EINVAL); |
| } |
| |
| if (patch.mode & WAVE_LOOPING) |
| { |
| if (patch.loop_start < 0 || patch.loop_start >= patch.len) |
| { |
| printk ("GUS: Invalid loop start\n"); |
| return RET_ERROR (EINVAL); |
| } |
| |
| if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) |
| { |
| printk ("GUS: Invalid loop end\n"); |
| return RET_ERROR (EINVAL); |
| } |
| } |
| |
| free_mem_ptr = (free_mem_ptr + 31) & ~31; /* |
| * Alignment 32 bytes |
| */ |
| |
| #define GUS_BANK_SIZE (256*1024) |
| |
| if (patch.mode & WAVE_16_BITS) |
| { |
| /* |
| * 16 bit samples must fit one 256k bank. |
| */ |
| if (patch.len >= GUS_BANK_SIZE) |
| { |
| printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); |
| return RET_ERROR (ENOSPC); |
| } |
| |
| if ((free_mem_ptr / GUS_BANK_SIZE) != |
| ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) |
| { |
| unsigned long tmp_mem = /* |
| * Align to 256K*N |
| */ |
| ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; |
| |
| if ((tmp_mem + patch.len) > gus_mem_size) |
| return RET_ERROR (ENOSPC); |
| |
| free_mem_ptr = tmp_mem; /* |
| * This leaves unusable memory |
| */ |
| } |
| } |
| |
| if ((free_mem_ptr + patch.len) > gus_mem_size) |
| return RET_ERROR (ENOSPC); |
| |
| sample_ptrs[free_sample] = free_mem_ptr; |
| |
| /* |
| * Tremolo is not possible with envelopes |
| */ |
| |
| if (patch.mode & WAVE_ENVELOPES) |
| patch.mode &= ~WAVE_TREMOLO; |
| |
| memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); |
| |
| /* |
| * Link this_one sample to the list of samples for patch 'instr'. |
| */ |
| |
| samples[free_sample].key = patch_table[instr]; |
| patch_table[instr] = free_sample; |
| |
| /* |
| * Use DMA to transfer the wave data to the DRAM |
| */ |
| |
| left = patch.len; |
| src_offs = 0; |
| target = free_mem_ptr; |
| |
| while (left) /* |
| * Not all moved |
| */ |
| { |
| blk_size = sound_buffsizes[gus_devnum]; |
| if (blk_size > left) |
| blk_size = left; |
| |
| /* |
| * DMA cannot cross 256k bank boundaries. Check for that. |
| */ |
| blk_end = target + blk_size; |
| |
| if ((target >> 18) != (blk_end >> 18)) |
| { /* |
| * Have to split the block |
| */ |
| |
| blk_end &= ~(256 * 1024 - 1); |
| blk_size = blk_end - target; |
| } |
| |
| #if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA) |
| /* |
| * For some reason the DMA is not possible. We have to use PIO. |
| */ |
| { |
| long i; |
| unsigned char data; |
| |
| for (i = 0; i < blk_size; i++) |
| { |
| GET_BYTE_FROM_USER (data, addr, sizeof_patch + i); |
| if (patch.mode & WAVE_UNSIGNED) |
| |
| if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) |
| data ^= 0x80; /* |
| * Convert to signed |
| */ |
| gus_poke (target + i, data); |
| } |
| } |
| #else /* |
| * * * GUS_NO_DMA */ |
| { |
| unsigned long address, hold_address; |
| unsigned char dma_command; |
| unsigned long flags; |
| |
| /* |
| * OK, move now. First in and then out. |
| */ |
| |
| COPY_FROM_USER (snd_raw_buf[gus_devnum][0], |
| addr, sizeof_patch + src_offs, |
| blk_size); |
| |
| DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ |
| gus_write8 (0x41, 0); /* |
| * Disable GF1 DMA |
| */ |
| DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0], |
| blk_size, DMA_MODE_WRITE); |
| |
| /* |
| * Set the DRAM address for the wave data |
| */ |
| |
| address = target; |
| |
| if (sound_dsp_dmachan[gus_devnum] > 3) |
| { |
| hold_address = address; |
| address = address >> 1; |
| address &= 0x0001ffffL; |
| address |= (hold_address & 0x000c0000L); |
| } |
| |
| gus_write16 (0x42, (address >> 4) & 0xffff); /* |
| * DRAM DMA address |
| */ |
| |
| /* |
| * Start the DMA transfer |
| */ |
| |
| dma_command = 0x21; /* |
| * IRQ enable, DMA start |
| */ |
| if (patch.mode & WAVE_UNSIGNED) |
| dma_command |= 0x80; /* |
| * Invert MSB |
| */ |
| if (patch.mode & WAVE_16_BITS) |
| dma_command |= 0x40; /* |
| * 16 bit _DATA_ |
| */ |
| if (sound_dsp_dmachan[gus_devnum] > 3) |
| dma_command |= 0x04; /* |
| * 16 bit DMA channel |
| */ |
| |
| gus_write8 (0x41, dma_command); /* |
| * Let's go luteet (=bugs) |
| */ |
| |
| /* |
| * Sleep here until the DRAM DMA done interrupt is served |
| */ |
| active_device = GUS_DEV_WAVE; |
| |
| DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ); |
| if (TIMED_OUT (dram_sleeper, dram_sleep_flag)) |
| printk ("GUS: DMA Transfer timed out\n"); |
| RESTORE_INTR (flags); |
| } |
| #endif /* |
| * * * GUS_NO_DMA */ |
| |
| /* |
| * Now the next part |
| */ |
| |
| left -= blk_size; |
| src_offs += blk_size; |
| target += blk_size; |
| |
| gus_write8 (0x41, 0); /* |
| * Stop DMA |
| */ |
| } |
| |
| free_mem_ptr += patch.len; |
| |
| if (!pmgr_flag) |
| pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); |
| free_sample++; |
| return 0; |
| } |
| |
| static void |
| guswave_hw_control (int dev, unsigned char *event) |
| { |
| int voice, cmd; |
| unsigned short p1, p2; |
| unsigned long plong, flags; |
| |
| cmd = event[2]; |
| voice = event[3]; |
| p1 = *(unsigned short *) &event[4]; |
| p2 = *(unsigned short *) &event[6]; |
| plong = *(unsigned long *) &event[4]; |
| |
| if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && |
| (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) |
| do_volume_irq (voice); |
| |
| switch (cmd) |
| { |
| |
| case _GUS_NUMVOICES: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_select_max_voices (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICESAMPLE: |
| guswave_set_instr (dev, voice, p1); |
| break; |
| |
| case _GUS_VOICEON: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| p1 &= ~0x20; /* |
| * Disable intr |
| */ |
| gus_voice_on (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEOFF: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_off (); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEFADE: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_fade (voice); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEMODE: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| p1 &= ~0x20; /* |
| * Disable intr |
| */ |
| gus_voice_mode (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEBALA: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_balance (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEFREQ: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_freq (plong); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEVOL: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_volume (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOICEVOL2: /* |
| * Just update the voice value |
| */ |
| voices[voice].initial_volume = |
| voices[voice].current_volume = p1; |
| break; |
| |
| case _GUS_RAMPRANGE: |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| break; /* |
| * NO-NO |
| */ |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_ramp_range (p1, p2); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_RAMPRATE: |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| break; /* |
| * NO-NO |
| */ |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_ramp_rate (p1, p2); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_RAMPMODE: |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| break; /* |
| * NO-NO |
| */ |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| p1 &= ~0x20; /* |
| * Disable intr |
| */ |
| gus_ramp_mode (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_RAMPON: |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| break; /* |
| * NO-NO |
| */ |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| p1 &= ~0x20; /* |
| * Disable intr |
| */ |
| gus_rampon (p1); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_RAMPOFF: |
| if (voices[voice].mode & WAVE_ENVELOPES) |
| break; /* |
| * NO-NO |
| */ |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_rampoff (); |
| RESTORE_INTR (flags); |
| break; |
| |
| case _GUS_VOLUME_SCALE: |
| volume_base = p1; |
| volume_scale = p2; |
| break; |
| |
| case _GUS_VOICE_POS: |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_set_voice_pos (voice, plong); |
| RESTORE_INTR (flags); |
| break; |
| |
| default:; |
| } |
| } |
| |
| static int |
| gus_sampling_set_speed (int speed) |
| { |
| if (speed <= 0) |
| return gus_sampling_speed; |
| |
| if (speed > 44100) |
| speed = 44100; |
| |
| gus_sampling_speed = speed; |
| return speed; |
| } |
| |
| static int |
| gus_sampling_set_channels (int channels) |
| { |
| if (!channels) |
| return gus_sampling_channels; |
| if (channels > 2) |
| channels = 2; |
| if (channels < 1) |
| channels = 1; |
| gus_sampling_channels = channels; |
| return channels; |
| } |
| |
| static int |
| gus_sampling_set_bits (int bits) |
| { |
| if (!bits) |
| return gus_sampling_bits; |
| |
| if (bits != 8 && bits != 16) |
| bits = 8; |
| |
| gus_sampling_bits = bits; |
| return bits; |
| } |
| |
| static int |
| gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) |
| { |
| switch (cmd) |
| { |
| case SOUND_PCM_WRITE_RATE: |
| if (local) |
| return gus_sampling_set_speed (arg); |
| return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg))); |
| break; |
| |
| case SOUND_PCM_READ_RATE: |
| if (local) |
| return gus_sampling_speed; |
| return IOCTL_OUT (arg, gus_sampling_speed); |
| break; |
| |
| case SNDCTL_DSP_STEREO: |
| if (local) |
| return gus_sampling_set_channels (arg + 1) - 1; |
| return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1); |
| break; |
| |
| case SOUND_PCM_WRITE_CHANNELS: |
| if (local) |
| return gus_sampling_set_channels (arg); |
| return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); |
| break; |
| |
| case SOUND_PCM_READ_CHANNELS: |
| if (local) |
| return gus_sampling_channels; |
| return IOCTL_OUT (arg, gus_sampling_channels); |
| break; |
| |
| case SNDCTL_DSP_SAMPLESIZE: |
| if (local) |
| return gus_sampling_set_bits (arg); |
| return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg))); |
| break; |
| |
| case SOUND_PCM_READ_BITS: |
| if (local) |
| return gus_sampling_bits; |
| return IOCTL_OUT (arg, gus_sampling_bits); |
| |
| case SOUND_PCM_WRITE_FILTER: /* |
| * NOT YET IMPLEMENTED |
| */ |
| return IOCTL_OUT (arg, RET_ERROR (EINVAL)); |
| break; |
| |
| case SOUND_PCM_READ_FILTER: |
| return IOCTL_OUT (arg, RET_ERROR (EINVAL)); |
| break; |
| |
| default: |
| return RET_ERROR (EINVAL); |
| } |
| return RET_ERROR (EINVAL); |
| } |
| |
| static void |
| gus_sampling_reset (int dev) |
| { |
| } |
| |
| static int |
| gus_sampling_open (int dev, int mode) |
| { |
| #ifdef GUS_NO_DMA |
| printk ("GUS: DMA mode not enabled. Device not supported\n"); |
| return RET_ERROR (ENXIO); |
| #endif |
| |
| if (gus_busy) |
| return RET_ERROR (EBUSY); |
| |
| gus_initialize (); |
| |
| gus_busy = 1; |
| active_device = 0; |
| |
| gus_reset (); |
| reset_sample_memory (); |
| gus_select_max_voices (14); |
| |
| pcm_active = 0; |
| pcm_opened = 1; |
| if (mode & OPEN_READ) |
| { |
| recording_active = 1; |
| set_input_volumes(); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| gus_sampling_close (int dev) |
| { |
| gus_reset (); |
| gus_busy = 0; |
| pcm_opened = 0; |
| active_device = 0; |
| |
| if (recording_active) |
| set_input_volumes(); |
| |
| recording_active = 0; |
| } |
| |
| static void |
| gus_sampling_update_volume (void) |
| { |
| unsigned long flags; |
| int voice; |
| |
| DISABLE_INTR (flags); |
| if (pcm_active && pcm_opened) |
| for (voice = 0; voice < gus_sampling_channels; voice++) |
| { |
| gus_select_voice (voice); |
| gus_rampoff (); |
| gus_voice_volume (1530 + (25 * gus_pcm_volume)); |
| gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); |
| } |
| RESTORE_INTR (flags); |
| } |
| |
| static void |
| play_next_pcm_block (void) |
| { |
| unsigned long flags; |
| int speed = gus_sampling_speed; |
| int this_one, is16bits, chn; |
| unsigned long dram_loc; |
| unsigned char mode[2], ramp_mode[2]; |
| |
| if (!pcm_qlen) |
| return; |
| |
| this_one = pcm_head; |
| |
| for (chn = 0; chn < gus_sampling_channels; chn++) |
| { |
| mode[chn] = 0x00; |
| ramp_mode[chn] = 0x03; /* |
| * Ramping and rollover off |
| */ |
| |
| if (chn == 0) |
| { |
| mode[chn] |= 0x20; /* |
| * Loop irq |
| */ |
| voices[chn].loop_irq_mode = LMODE_PCM; |
| } |
| |
| if (gus_sampling_bits != 8) |
| { |
| is16bits = 1; |
| mode[chn] |= 0x04; /* |
| * 16 bit data |
| */ |
| } |
| else |
| is16bits = 0; |
| |
| dram_loc = this_one * pcm_bsize; |
| dram_loc += chn * pcm_banksize; |
| |
| if (this_one == (pcm_nblk - 1)) /* |
| * Last of the DRAM buffers |
| */ |
| { |
| mode[chn] |= 0x08; /* |
| * Enable loop |
| */ |
| ramp_mode[chn] = 0x03; /* |
| * Disable rollover |
| */ |
| } |
| else |
| { |
| if (chn == 0) |
| ramp_mode[chn] = 0x04; /* |
| * Enable rollover bit |
| */ |
| } |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (chn); |
| gus_voice_freq (speed); |
| |
| if (gus_sampling_channels == 1) |
| gus_voice_balance (7); /* |
| * mono |
| */ |
| else if (chn == 0) |
| gus_voice_balance (0); /* |
| * left |
| */ |
| else |
| gus_voice_balance (15); /* |
| * right |
| */ |
| |
| if (!pcm_active) /* |
| * Voice not started yet |
| */ |
| { |
| /* |
| * The playback was not started yet (or there has been a pause). |
| * Start the voice (again) and ask for a rollover irq at the end of |
| * this_one block. If this_one one is last of the buffers, use just |
| * the normal loop with irq. |
| */ |
| |
| gus_voice_off (); /* |
| * It could already be running |
| */ |
| gus_rampoff (); |
| gus_voice_volume (1530 + (25 * gus_pcm_volume)); |
| gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); |
| |
| gus_write_addr (0x0a, dram_loc, is16bits); /* |
| * Starting position |
| */ |
| gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* |
| * Loop start |
| * location |
| */ |
| |
| if (chn != 0) |
| gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk), |
| is16bits); /* |
| * Loop end location |
| */ |
| } |
| |
| if (chn == 0) |
| gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* |
| * Loop |
| * end |
| * location |
| */ |
| else |
| mode[chn] |= 0x08; /* |
| * Enable loop |
| */ |
| |
| if (pcm_datasize[this_one] != pcm_bsize) |
| { |
| /* |
| * Incomplete block. Possibly the last one. |
| */ |
| if (chn == 0) |
| { |
| mode[chn] &= ~0x08; /* |
| * Disable loop |
| */ |
| mode[chn] |= 0x20; /* |
| * Enable loop IRQ |
| */ |
| voices[0].loop_irq_mode = LMODE_PCM_STOP; |
| ramp_mode[chn] = 0x03; /* |
| * No rollover bit |
| */ |
| } |
| else |
| { |
| gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* |
| * Loop |
| * end |
| * location |
| */ |
| mode[chn] &= ~0x08; /* |
| * Disable loop |
| */ |
| } |
| } |
| |
| RESTORE_INTR (flags); |
| } |
| |
| for (chn = 0; chn < gus_sampling_channels; chn++) |
| { |
| DISABLE_INTR (flags); |
| gus_select_voice (chn); |
| gus_write8 (0x0d, ramp_mode[chn]); |
| gus_voice_on (mode[chn]); |
| RESTORE_INTR (flags); |
| } |
| |
| pcm_active = 1; |
| } |
| |
| static void |
| gus_transfer_output_block (int dev, unsigned long buf, |
| int total_count, int intrflag, int chn) |
| { |
| /* |
| * This routine transfers one block of audio data to the DRAM. In mono mode |
| * it's called just once. When in stereo mode, this_one routine is called |
| * once for both channels. |
| * |
| * The left/mono channel data is transferred to the beginning of dram and the |
| * right data to the area pointed by gus_page_size. |
| */ |
| |
| int this_one, count; |
| unsigned long flags; |
| unsigned char dma_command; |
| unsigned long address, hold_address; |
| |
| DISABLE_INTR (flags); |
| |
| count = total_count / gus_sampling_channels; |
| |
| if (chn == 0) |
| { |
| if (pcm_qlen >= pcm_nblk) |
| printk ("GUS Warning: PCM buffers out of sync\n"); |
| |
| this_one = pcm_current_block = pcm_tail; |
| pcm_qlen++; |
| pcm_tail = (pcm_tail + 1) % pcm_nblk; |
| pcm_datasize[this_one] = count; |
| } |
| else |
| this_one = pcm_current_block; |
| |
| gus_write8 (0x41, 0); /* |
| * Disable GF1 DMA |
| */ |
| DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); |
| |
| address = this_one * pcm_bsize; |
| address += chn * pcm_banksize; |
| |
| if (sound_dsp_dmachan[dev] > 3) |
| { |
| hold_address = address; |
| address = address >> 1; |
| address &= 0x0001ffffL; |
| address |= (hold_address & 0x000c0000L); |
| } |
| |
| gus_write16 (0x42, (address >> 4) & 0xffff); /* |
| * DRAM DMA address |
| */ |
| |
| dma_command = 0x21; /* |
| * IRQ enable, DMA start |
| */ |
| |
| if (gus_sampling_bits != 8) |
| dma_command |= 0x40; /* |
| * 16 bit _DATA_ |
| */ |
| else |
| dma_command |= 0x80; /* |
| * Invert MSB |
| */ |
| |
| if (sound_dsp_dmachan[dev] > 3) |
| dma_command |= 0x04; /* |
| * 16 bit DMA channel |
| */ |
| |
| gus_write8 (0x41, dma_command); /* |
| * Kick on |
| */ |
| |
| if (chn == (gus_sampling_channels - 1)) /* |
| * Last channel |
| */ |
| { |
| /* |
| * Last (right or mono) channel data |
| */ |
| active_device = GUS_DEV_PCM_DONE; |
| if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize)) |
| { |
| play_next_pcm_block (); |
| } |
| } |
| else /* |
| * * * Left channel data. The right channel |
| * is * * * transferred after DMA interrupt */ |
| active_device = GUS_DEV_PCM_CONTINUE; |
| |
| RESTORE_INTR (flags); |
| } |
| |
| static void |
| gus_sampling_output_block (int dev, unsigned long buf, int total_count, |
| int intrflag, int restart_dma) |
| { |
| pcm_current_buf = buf; |
| pcm_current_count = total_count; |
| pcm_current_intrflag = intrflag; |
| pcm_current_dev = dev; |
| gus_transfer_output_block (dev, buf, total_count, intrflag, 0); |
| } |
| |
| static void |
| gus_sampling_start_input (int dev, unsigned long buf, int count, |
| int intrflag, int restart_dma) |
| { |
| unsigned long flags; |
| unsigned char mode; |
| |
| DISABLE_INTR (flags); |
| |
| DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); |
| |
| mode = 0xa0; /* |
| * DMA IRQ enable, invert MSB |
| */ |
| |
| if (sound_dsp_dmachan[dev] > 3) |
| mode |= 0x04; /* |
| * 16 bit DMA channel |
| */ |
| if (gus_sampling_channels > 1) |
| mode |= 0x02; /* |
| * Stereo |
| */ |
| mode |= 0x01; /* |
| * DMA enable |
| */ |
| |
| gus_write8 (0x49, mode); |
| |
| RESTORE_INTR (flags); |
| } |
| |
| static int |
| gus_sampling_prepare_for_input (int dev, int bsize, int bcount) |
| { |
| unsigned int rate; |
| |
| rate = (9878400 / (gus_sampling_speed + 2)) / 16; |
| |
| gus_write8 (0x48, rate & 0xff); /* |
| * Set sampling frequency |
| */ |
| |
| if (gus_sampling_bits != 8) |
| { |
| printk ("GUS Error: 16 bit recording not supported\n"); |
| return RET_ERROR (EINVAL); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| gus_sampling_prepare_for_output (int dev, int bsize, int bcount) |
| { |
| int i; |
| |
| long mem_ptr, mem_size; |
| |
| mem_ptr = 0; |
| mem_size = gus_mem_size / gus_sampling_channels; |
| |
| if (mem_size > (256 * 1024)) |
| mem_size = 256 * 1024; |
| |
| pcm_bsize = bsize / gus_sampling_channels; |
| pcm_head = pcm_tail = pcm_qlen = 0; |
| |
| pcm_nblk = MAX_PCM_BUFFERS; |
| if ((pcm_bsize * pcm_nblk) > mem_size) |
| pcm_nblk = mem_size / pcm_bsize; |
| |
| for (i = 0; i < pcm_nblk; i++) |
| pcm_datasize[i] = 0; |
| |
| pcm_banksize = pcm_nblk * pcm_bsize; |
| |
| if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) |
| pcm_nblk--; |
| |
| return 0; |
| } |
| |
| static int |
| gus_has_output_drained (int dev) |
| { |
| return !pcm_qlen; |
| } |
| |
| static void |
| gus_copy_from_user (int dev, char *localbuf, int localoffs, |
| snd_rw_buf * userbuf, int useroffs, int len) |
| { |
| if (gus_sampling_channels == 1) |
| { |
| COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); |
| } |
| else if (gus_sampling_bits == 8) |
| { |
| int in_left = useroffs; |
| int in_right = useroffs + 1; |
| char *out_left, *out_right; |
| int i; |
| |
| len /= 2; |
| localoffs /= 2; |
| out_left = &localbuf[localoffs]; |
| out_right = out_left + pcm_bsize; |
| |
| for (i = 0; i < len; i++) |
| { |
| GET_BYTE_FROM_USER (*out_left++, userbuf, in_left); |
| in_left += 2; |
| GET_BYTE_FROM_USER (*out_right++, userbuf, in_right); |
| in_right += 2; |
| } |
| } |
| else |
| { |
| int in_left = useroffs; |
| int in_right = useroffs + 1; |
| short *out_left, *out_right; |
| int i; |
| |
| len /= 4; |
| localoffs /= 4; |
| |
| out_left = (short *) &localbuf[localoffs]; |
| out_right = out_left + (pcm_bsize / 2); |
| |
| for (i = 0; i < len; i++) |
| { |
| GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left); |
| in_left += 2; |
| GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right); |
| in_right += 2; |
| } |
| } |
| } |
| |
| static struct audio_operations gus_sampling_operations = |
| { |
| "Gravis UltraSound", |
| gus_sampling_open, |
| gus_sampling_close, |
| gus_sampling_output_block, |
| gus_sampling_start_input, |
| gus_sampling_ioctl, |
| gus_sampling_prepare_for_input, |
| gus_sampling_prepare_for_output, |
| gus_sampling_reset, |
| gus_sampling_reset, |
| gus_has_output_drained, |
| gus_copy_from_user |
| }; |
| |
| #ifdef FUTURE_VERSION |
| static void |
| guswave_bender (int dev, int voice, int value) |
| { |
| int freq; |
| unsigned long flags; |
| |
| voices[voice].bender = value - 8192; |
| freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); |
| voices[voice].current_freq = freq; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| gus_voice_freq (freq); |
| RESTORE_INTR (flags); |
| } |
| #endif |
| |
| static int |
| guswave_patchmgr (int dev, struct patmgr_info *rec) |
| { |
| int i, n; |
| |
| switch (rec->command) |
| { |
| case PM_GET_DEVTYPE: |
| rec->parm1 = PMTYPE_WAVE; |
| return 0; |
| break; |
| |
| case PM_GET_NRPGM: |
| rec->parm1 = MAX_PATCH; |
| return 0; |
| break; |
| |
| case PM_GET_PGMMAP: |
| rec->parm1 = MAX_PATCH; |
| |
| for (i = 0; i < MAX_PATCH; i++) |
| { |
| int ptr = patch_table[i]; |
| |
| rec->data.data8[i] = 0; |
| |
| while (ptr >= 0 && ptr < free_sample) |
| { |
| rec->data.data8[i]++; |
| ptr = samples[ptr].key; /* |
| * Follow link |
| */ |
| } |
| } |
| return 0; |
| break; |
| |
| case PM_GET_PGM_PATCHES: |
| { |
| int ptr = patch_table[rec->parm1]; |
| |
| n = 0; |
| |
| while (ptr >= 0 && ptr < free_sample) |
| { |
| rec->data.data32[n++] = ptr; |
| ptr = samples[ptr].key; /* |
| * Follow link |
| */ |
| } |
| } |
| rec->parm1 = n; |
| return 0; |
| break; |
| |
| case PM_GET_PATCH: |
| { |
| int ptr = rec->parm1; |
| struct patch_info *pat; |
| |
| if (ptr < 0 || ptr >= free_sample) |
| return RET_ERROR (EINVAL); |
| |
| memcpy (rec->data.data8, (char *) &samples[ptr], |
| sizeof (struct patch_info)); |
| |
| pat = (struct patch_info *) rec->data.data8; |
| |
| pat->key = GUS_PATCH; /* |
| * Restore patch type |
| */ |
| rec->parm1 = sample_ptrs[ptr]; /* |
| * DRAM address |
| */ |
| rec->parm2 = sizeof (struct patch_info); |
| } |
| return 0; |
| break; |
| |
| case PM_SET_PATCH: |
| { |
| int ptr = rec->parm1; |
| struct patch_info *pat; |
| |
| if (ptr < 0 || ptr >= free_sample) |
| return RET_ERROR (EINVAL); |
| |
| pat = (struct patch_info *) rec->data.data8; |
| |
| if (pat->len > samples[ptr].len) /* |
| * Cannot expand sample |
| */ |
| return RET_ERROR (EINVAL); |
| |
| pat->key = samples[ptr].key; /* |
| * Ensure the link is correct |
| */ |
| |
| memcpy ((char *) &samples[ptr], rec->data.data8, |
| sizeof (struct patch_info)); |
| |
| pat->key = GUS_PATCH; |
| } |
| return 0; |
| break; |
| |
| case PM_READ_PATCH: /* |
| * Returns a block of wave data from the DRAM |
| */ |
| { |
| int sample = rec->parm1; |
| int n; |
| long offs = rec->parm2; |
| int l = rec->parm3; |
| |
| if (sample < 0 || sample >= free_sample) |
| return RET_ERROR (EINVAL); |
| |
| if (offs < 0 || offs >= samples[sample].len) |
| return RET_ERROR (EINVAL); /* |
| * Invalid offset |
| */ |
| |
| n = samples[sample].len - offs; /* |
| * Nr of bytes left |
| */ |
| |
| if (l > n) |
| l = n; |
| |
| if (l > sizeof (rec->data.data8)) |
| l = sizeof (rec->data.data8); |
| |
| if (l <= 0) |
| return RET_ERROR (EINVAL); /* |
| * Was there a bug? |
| */ |
| |
| offs += sample_ptrs[sample]; /* |
| * Begin offsess + offset to DRAM |
| */ |
| |
| for (n = 0; n < l; n++) |
| rec->data.data8[n] = gus_peek (offs++); |
| rec->parm1 = n; /* |
| * Nr of bytes copied |
| */ |
| } |
| return 0; |
| break; |
| |
| case PM_WRITE_PATCH: /* |
| * Writes a block of wave data to the DRAM |
| */ |
| { |
| int sample = rec->parm1; |
| int n; |
| long offs = rec->parm2; |
| int l = rec->parm3; |
| |
| if (sample < 0 || sample >= free_sample) |
| return RET_ERROR (EINVAL); |
| |
| if (offs < 0 || offs >= samples[sample].len) |
| return RET_ERROR (EINVAL); /* |
| * Invalid offset |
| */ |
| |
| n = samples[sample].len - offs; /* |
| * Nr of bytes left |
| */ |
| |
| if (l > n) |
| l = n; |
| |
| if (l > sizeof (rec->data.data8)) |
| l = sizeof (rec->data.data8); |
| |
| if (l <= 0) |
| return RET_ERROR (EINVAL); /* |
| * Was there a bug? |
| */ |
| |
| offs += sample_ptrs[sample]; /* |
| * Begin offsess + offset to DRAM |
| */ |
| |
| for (n = 0; n < l; n++) |
| gus_poke (offs++, rec->data.data8[n]); |
| rec->parm1 = n; /* |
| * Nr of bytes copied |
| */ |
| } |
| return 0; |
| break; |
| |
| default: |
| return RET_ERROR (EINVAL); |
| } |
| } |
| |
| static struct synth_operations guswave_operations = |
| { |
| &gus_info, |
| #ifdef FUTURE_VERSION |
| 0, |
| #endif |
| SYNTH_TYPE_SAMPLE, |
| SAMPLE_TYPE_GUS, |
| guswave_open, |
| guswave_close, |
| guswave_ioctl, |
| guswave_kill_note, |
| guswave_start_note, |
| guswave_set_instr, |
| guswave_reset, |
| guswave_hw_control, |
| guswave_load_patch, |
| guswave_aftertouch, |
| guswave_controller, |
| guswave_panning, |
| guswave_patchmgr, |
| #ifdef FUTURE_VERSION |
| guswave_bender |
| #endif |
| }; |
| |
| static void |
| set_input_volumes(void) |
| { |
| unsigned long flags; |
| unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ |
| |
| DISABLE_INTR(flags); |
| |
| /* |
| * Enable channels having vol > 10% |
| * Note! bit 0x01 means line in DISABLED while 0x04 means |
| * mic in ENABLED. |
| */ |
| if (gus_line_vol > 10) mask &= ~0x01; |
| if (gus_mic_vol > 10) mask |= 0x04; |
| |
| if (recording_active) |
| { |
| /* |
| * Disable channel, if not selected for recording |
| */ |
| if (!(gus_recmask & SOUND_MASK_LINE)) mask |= 0x01; |
| if (!(gus_recmask & SOUND_MASK_MIC)) mask &= ~0x04; |
| } |
| |
| mix_image &= ~0x07; |
| mix_image |= mask & 0x07; |
| OUTB (mix_image, u_Mixer); |
| |
| RESTORE_INTR(flags); |
| } |
| |
| static int |
| gus_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) |
| { |
| #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ |
| SOUND_MASK_SYNTH|SOUND_MASK_PCM) |
| if (((cmd >> 8) & 0xff) == 'M') |
| { |
| if (cmd & IOC_IN) |
| switch (cmd & 0xff) |
| { |
| case SOUND_MIXER_RECSRC: |
| gus_recmask = IOCTL_IN(arg) & MIX_DEVS; |
| if (!(gus_recmask & (SOUND_MASK_MIC|SOUND_MASK_LINE))) |
| gus_recmask = SOUND_MASK_MIC; |
| /* Note! Input volumes are updated during next open for recording */ |
| return IOCTL_OUT (arg, gus_recmask); |
| break; |
| |
| case SOUND_MIXER_MIC: |
| { |
| int vol = IOCTL_IN (arg) & 0xff; |
| if (vol < 0) vol = 0; |
| if (vol > 100) vol = 100; |
| gus_mic_vol = vol; |
| set_input_volumes(); |
| return IOCTL_OUT (arg, vol | (vol << 8)); |
| } |
| break; |
| |
| case SOUND_MIXER_LINE: |
| { |
| int vol = IOCTL_IN (arg) & 0xff; |
| if (vol < 0) vol = 0; |
| if (vol > 100) vol = 100; |
| gus_line_vol = vol; |
| set_input_volumes(); |
| return IOCTL_OUT (arg, vol | (vol << 8)); |
| } |
| break; |
| |
| case SOUND_MIXER_PCM: |
| gus_pcm_volume = IOCTL_IN (arg) & 0xff; |
| if (gus_pcm_volume < 0) |
| gus_pcm_volume = 0; |
| if (gus_pcm_volume > 100) |
| gus_pcm_volume = 100; |
| gus_sampling_update_volume (); |
| return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); |
| break; |
| |
| case SOUND_MIXER_SYNTH: |
| { |
| int voice; |
| |
| gus_wave_volume = IOCTL_IN (arg) & 0xff; |
| |
| if (gus_wave_volume < 0) |
| gus_wave_volume = 0; |
| if (gus_wave_volume > 100) |
| gus_wave_volume = 100; |
| |
| if (active_device == GUS_DEV_WAVE) |
| for (voice = 0; voice < nr_voices; voice++) |
| dynamic_volume_change (voice); /* |
| * Apply the new |
| * volume |
| */ |
| |
| return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); |
| } |
| break; |
| |
| default: |
| return RET_ERROR (EINVAL); |
| } |
| else |
| switch (cmd & 0xff) /* |
| * Return parameters |
| */ |
| { |
| |
| case SOUND_MIXER_RECSRC: |
| return IOCTL_OUT (arg, gus_recmask); |
| break; |
| |
| case SOUND_MIXER_DEVMASK: |
| return IOCTL_OUT (arg, MIX_DEVS); |
| break; |
| |
| case SOUND_MIXER_STEREODEVS: |
| return IOCTL_OUT (arg, 0); |
| break; |
| |
| case SOUND_MIXER_RECMASK: |
| return IOCTL_OUT (arg, SOUND_MASK_MIC|SOUND_MASK_LINE); |
| break; |
| |
| case SOUND_MIXER_CAPS: |
| return IOCTL_OUT (arg, 0); |
| break; |
| |
| case SOUND_MIXER_MIC: |
| return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8)); |
| break; |
| |
| case SOUND_MIXER_LINE: |
| return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8)); |
| break; |
| |
| case SOUND_MIXER_PCM: |
| return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); |
| break; |
| |
| case SOUND_MIXER_SYNTH: |
| return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); |
| break; |
| |
| default: |
| return RET_ERROR (EINVAL); |
| } |
| } |
| else |
| return RET_ERROR (EINVAL); |
| } |
| |
| static struct mixer_operations gus_mixer_operations = |
| { |
| gus_mixer_ioctl |
| }; |
| |
| long |
| gus_wave_init (long mem_start, int irq, int dma) |
| { |
| printk (" <Gravis UltraSound %dk>", (int) gus_mem_size / 1024); |
| |
| if (irq < 0 || irq > 15) |
| { |
| printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); |
| return mem_start; |
| } |
| |
| if (dma < 0 || dma > 7) |
| { |
| printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); |
| return mem_start; |
| } |
| |
| gus_irq = irq; |
| gus_dma = dma; |
| |
| if (num_synths >= MAX_SYNTH_DEV) |
| printk ("GUS Error: Too many synthesizers\n"); |
| else |
| synth_devs[num_synths++] = &guswave_operations; |
| |
| PERMANENT_MALLOC (struct patch_info *, samples, |
| (MAX_SAMPLE + 1) * sizeof (*samples), mem_start); |
| |
| reset_sample_memory (); |
| |
| gus_initialize (); |
| |
| if (num_dspdevs < MAX_DSP_DEV) |
| { |
| dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations; |
| sound_dsp_dmachan[gus_devnum] = dma; |
| sound_buffcounts[gus_devnum] = 1; |
| sound_buffsizes[gus_devnum] = DSP_BUFFSIZE; |
| sound_dma_automode[gus_devnum] = 0; |
| } |
| else |
| printk ("GUS: Too many PCM devices available\n"); |
| |
| if (num_mixers < MAX_MIXER_DEV) /* |
| * Don't install if there is another |
| * mixer |
| */ |
| mixer_devs[num_mixers++] = &gus_mixer_operations; |
| |
| return mem_start; |
| } |
| |
| static void |
| do_loop_irq (int voice) |
| { |
| unsigned char tmp; |
| int mode, parm; |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| gus_select_voice (voice); |
| |
| tmp = gus_read8 (0x00); |
| tmp &= ~0x20; /* |
| * Disable wave IRQ for this_one voice |
| */ |
| gus_write8 (0x00, tmp); |
| |
| mode = voices[voice].loop_irq_mode; |
| voices[voice].loop_irq_mode = 0; |
| parm = voices[voice].loop_irq_parm; |
| |
| switch (mode) |
| { |
| |
| case LMODE_FINISH: /* |
| * Final loop finished, shoot volume down |
| */ |
| |
| if ((gus_read16 (0x09) >> 4) < 100) /* |
| * Get current volume |
| */ |
| { |
| gus_voice_off (); |
| gus_rampoff (); |
| gus_voice_init (voice); |
| break; |
| } |
| gus_ramp_range (65, 4065); |
| gus_ramp_rate (0, 63); /* |
| * Fastest possible rate |
| */ |
| gus_rampon (0x20 | 0x40); /* |
| * Ramp down, once, irq |
| */ |
| voices[voice].volume_irq_mode = VMODE_HALT; |
| break; |
| |
| case LMODE_PCM_STOP: |
| pcm_active = 0; /* |
| * Requires extensive processing |
| */ |
| case LMODE_PCM: |
| { |
| int orig_qlen = pcm_qlen; |
| |
| pcm_qlen--; |
| pcm_head = (pcm_head + 1) % pcm_nblk; |
| if (pcm_qlen) |
| { |
| play_next_pcm_block (); |
| } |
| else |
| { /* |
| * Out of data. Just stop the voice |
| */ |
| gus_voice_off (); |
| gus_rampoff (); |
| pcm_active = 0; |
| } |
| |
| if (orig_qlen == pcm_nblk) |
| { |
| DMAbuf_outputintr (gus_devnum, 0); |
| } |
| } |
| break; |
| |
| default:; |
| } |
| RESTORE_INTR (flags); |
| } |
| |
| static void |
| do_volume_irq (int voice) |
| { |
| unsigned char tmp; |
| int mode, parm; |
| unsigned long flags; |
| |
| DISABLE_INTR (flags); |
| |
| gus_select_voice (voice); |
| |
| tmp = gus_read8 (0x0d); |
| tmp &= ~0x20; /* |
| * Disable volume ramp IRQ |
| */ |
| gus_write8 (0x0d, tmp); |
| |
| mode = voices[voice].volume_irq_mode; |
| voices[voice].volume_irq_mode = 0; |
| parm = voices[voice].volume_irq_parm; |
| |
| switch (mode) |
| { |
| case VMODE_HALT: /* |
| * Decay phase finished |
| */ |
| gus_voice_init (voice); |
| break; |
| |
| case VMODE_ENVELOPE: |
| gus_rampoff (); |
| step_envelope (voice); |
| break; |
| |
| case VMODE_START_NOTE: |
| guswave_start_note2 (voices[voice].dev_pending, voice, |
| voices[voice].note_pending, voices[voice].volume_pending); |
| if (voices[voice].kill_pending) |
| guswave_kill_note (voices[voice].dev_pending, voice, 0); |
| if (voices[voice].sample_pending >= 0) |
| { |
| guswave_set_instr (voices[voice].dev_pending, voice, |
| voices[voice].sample_pending); |
| voices[voice].sample_pending = -1; |
| } |
| break; |
| |
| default:; |
| } |
| |
| RESTORE_INTR (flags); |
| } |
| |
| void |
| gus_voice_irq (void) |
| { |
| unsigned long wave_ignore = 0, volume_ignore = 0; |
| unsigned long voice_bit; |
| |
| unsigned char src, voice; |
| |
| while (1) |
| { |
| src = gus_read8 (0x0f); /* |
| * Get source info |
| */ |
| voice = src & 0x1f; |
| src &= 0xc0; |
| |
| if (src == (0x80 | 0x40)) |
| return; /* |
| * No interrupt |
| */ |
| |
| voice_bit = 1 << voice; |
| |
| if (!(src & 0x80)) /* |
| * Wave IRQ pending |
| */ |
| if (!(wave_ignore & voice_bit) && voice < nr_voices) /* |
| * Not done |
| * yet |
| */ |
| { |
| wave_ignore |= voice_bit; |
| do_loop_irq (voice); |
| } |
| |
| if (!(src & 0x40)) /* |
| * Volume IRQ pending |
| */ |
| if (!(volume_ignore & voice_bit) && voice < nr_voices) /* |
| * Not done |
| * yet |
| */ |
| { |
| volume_ignore |= voice_bit; |
| do_volume_irq (voice); |
| } |
| } |
| } |
| |
| void |
| guswave_dma_irq (void) |
| { |
| unsigned char status; |
| |
| status = gus_look8 (0x41); /* |
| * Get DMA IRQ Status |
| */ |
| if (status & 0x40) /* |
| * DMA Irq pending |
| */ |
| switch (active_device) |
| { |
| case GUS_DEV_WAVE: |
| if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag)) |
| WAKE_UP (dram_sleeper, dram_sleep_flag); |
| break; |
| |
| case GUS_DEV_PCM_CONTINUE: |
| gus_transfer_output_block (pcm_current_dev, pcm_current_buf, |
| pcm_current_count, |
| pcm_current_intrflag, 1); |
| break; |
| |
| case GUS_DEV_PCM_DONE: |
| if (pcm_qlen < pcm_nblk) |
| { |
| DMAbuf_outputintr (gus_devnum, pcm_qlen == 0); |
| } |
| break; |
| |
| default:; |
| } |
| |
| status = gus_look8 (0x49); /* |
| * Get Sampling IRQ Status |
| */ |
| if (status & 0x40) /* |
| * Sampling Irq pending |
| */ |
| { |
| DMAbuf_inputintr (gus_devnum); |
| } |
| |
| } |
| |
| #endif |