aboutsummaryrefslogtreecommitdiff
path: root/sound/oss/gus_wave.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/oss/gus_wave.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/oss/gus_wave.c')
-rw-r--r--sound/oss/gus_wave.c3464
1 files changed, 3464 insertions, 0 deletions
diff --git a/sound/oss/gus_wave.c b/sound/oss/gus_wave.c
new file mode 100644
index 00000000000..942d5186580
--- /dev/null
+++ b/sound/oss/gus_wave.c
@@ -0,0 +1,3464 @@
+/*
+ * sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
+ * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious
+ * usage of CS4231A codec, GUS wave and MIDI for GUS MAX.
+ * Bartlomiej Zolnierkiewicz : added some __init/__exit
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/spinlock.h>
+
+#define GUSPNP_AUTODETECT
+
+#include "sound_config.h"
+#include <linux/ultrasound.h>
+
+#include "gus.h"
+#include "gus_hw.h"
+
+#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
+
+#define MAX_SAMPLE 150
+#define MAX_PATCH 256
+
+#define NOT_SAMPLE 0xffff
+
+struct voice_info
+{
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ unsigned long mode;
+ int fixed_pitch;
+ 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;
+
+};
+
+static struct voice_alloc_info *voice_alloc;
+static struct address_info *gus_hw_config;
+extern int gus_base;
+extern int gus_irq, gus_dma;
+extern int gus_pnp_flag;
+extern int gus_no_wave_dma;
+static int gus_dma2 = -1;
+static int dual_dma_mode;
+static long gus_mem_size;
+static long free_mem_ptr;
+static int gus_busy;
+static int gus_no_dma;
+static int nr_voices;
+static int gus_devnum;
+static int volume_base, volume_scale, volume_method;
+static int gus_recmask = SOUND_MASK_MIC;
+static int recording_active;
+static int only_read_access;
+static int only_8_bits;
+
+static int iw_mode = 0;
+int gus_wave_volume = 60;
+int gus_pcm_volume = 80;
+int have_gus_max = 0;
+static int gus_line_vol = 100, gus_mic_vol;
+static unsigned char mix_image = 0x00;
+
+int gus_timer_enabled = 0;
+
+/*
+ * 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;
+
+#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 done ch. 1/2 */
+
+static int gus_audio_speed;
+static int gus_audio_channels;
+static int gus_audio_bits;
+static int gus_audio_bsize;
+static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */
+
+static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper);
+
+/*
+ * Variables and buffers for PCM output
+ */
+
+#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */
+
+static int pcm_bsize, pcm_nblk, pcm_banksize;
+static int pcm_datasize[MAX_PCM_BUFFERS];
+static volatile int pcm_head, pcm_tail, pcm_qlen;
+static volatile int pcm_active;
+static volatile int dma_active;
+static int pcm_opened;
+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;
+DEFINE_SPINLOCK(gus_lock);
+
+extern int *gus_osp;
+
+static 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 mixer_type;
+
+
+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);
+extern unsigned short gus_linear_vol(int vol, int mainvol);
+static void compute_volume(int voice, int volume);
+static void do_volume_irq(int voice);
+static void set_input_volumes(void);
+static void gus_tmr_install(int io_base);
+
+#define INSTANT_RAMP -1 /* Instant change. No 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 a silent sample to the beginning */
+ gus_poke(1, 0);
+ free_mem_ptr = 2;
+
+ free_sample = 0;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ patch_table[i] = NOT_SAMPLE;
+}
+
+void gus_delay(void)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ inb(u_DRAMIO);
+}
+
+static void gus_poke(long addr, unsigned char data)
+{ /* Writes a byte to the DRAM */
+ 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);
+}
+
+static unsigned char gus_peek(long addr)
+{ /* Reads a byte from the DRAM */
+ unsigned char tmp;
+
+ 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);
+
+ return tmp;
+}
+
+void gus_write8(int reg, unsigned int data)
+{ /* Writes to an indirect register (8 bit) */
+ outb((reg), u_Command);
+ outb(((unsigned char) (data & 0xff)), u_DataHi);
+}
+
+static unsigned char gus_read8(int reg)
+{
+ /* Reads from an indirect register (8 bit). Offset 0x80. */
+ unsigned char val;
+
+ outb((reg | 0x80), u_Command);
+ val = inb(u_DataHi);
+
+ return val;
+}
+
+static unsigned char gus_look8(int reg)
+{
+ /* Reads from an indirect register (8 bit). No additional offset. */
+ unsigned char val;
+
+ outb((reg), u_Command);
+ val = inb(u_DataHi);
+
+ return val;
+}
+
+static void gus_write16(int reg, unsigned int data)
+{
+ /* Writes to an indirect register (16 bit) */
+ outb((reg), u_Command);
+
+ outb(((unsigned char) (data & 0xff)), u_DataLo);
+ outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi);
+}
+
+static unsigned short gus_read16(int reg)
+{
+ /* Reads from an indirect register (16 bit). Offset 0x80. */
+ unsigned char hi, lo;
+
+ outb((reg | 0x80), u_Command);
+
+ lo = inb(u_DataLo);
+ hi = inb(u_DataHi);
+
+ return ((hi << 8) & 0xff00) | lo;
+}
+
+static unsigned short gus_look16(int reg)
+{
+ /* Reads from an indirect register (16 bit). No additional offset. */
+ unsigned char hi, lo;
+
+ outb((reg), u_Command);
+
+ lo = inb(u_DataLo);
+ hi = inb(u_DataHi);
+
+ return ((hi << 8) & 0xff00) | lo;
+}
+
+static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit)
+{
+ /* Writes an 24 bit memory address */
+ unsigned long hold_address;
+
+ if (is16bit)
+ {
+ if (iw_mode)
+ {
+ /* Interwave spesific address translations */
+ address >>= 1;
+ }
+ else
+ {
+ /*
+ * 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)
+ + (frac << 5));
+ /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */
+ gus_delay();
+ gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff)
+ + (frac << 5));
+}
+
+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 (iw_mode)
+ nvoices = 32;
+ if (nvoices < 14)
+ nvoices = 14;
+ if (nvoices > 32)
+ nvoices = 32;
+
+ voice_alloc->max_voice = 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 touch last two bits */
+ 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;
+
+ /* Interwave plays at 44100 Hz with any number of voices */
+ if (iw_mode)
+ fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100);
+ else
+ 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)); /* Leave the last 2 bits alone */
+ 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, 0,
+ samples[sample_no].mode & WAVE_16_BITS);
+ }
+ }
+}
+
+static void gus_voice_init(int voice)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus_lock,flags);
+ gus_select_voice(voice);
+ gus_voice_volume(0);
+ gus_voice_off();
+ gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */
+ gus_write8(0x00, 0x03); /* Voice off */
+ gus_write8(0x0d, 0x03); /* Ramping off */
+ voice_alloc->map[voice] = 0;
+ voice_alloc->alloc_times[voice] = 0;
+ spin_unlock_irqrestore(&gus_lock,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;
+ voices[voice].fixed_pitch = 0;
+}
+
+static void step_envelope(int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+ unsigned long flags;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ spin_lock_irqsave(&gus_lock,flags);
+ gus_select_voice(voice);
+ gus_rampoff();
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ /*
+ * Sustain phase begins. Continue envelope after receiving note off.
+ */
+ }
+ if (voices[voice].env_phase >= 5)
+ {
+ /* Envelope finished. Shoot the voice down */
+ gus_voice_init(voice);
+ return;
+ }
+ prev_vol = voices[voice].current_volume;
+ 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];
+
+ spin_lock_irqsave(&gus_lock,flags);
+ gus_select_voice(voice);
+
+ gus_voice_volume(prev_vol);
+
+
+ gus_write8(0x06, rate); /* Ramping rate */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
+ {
+ spin_unlock_irqrestore(&gus_lock,flags);
+ step_envelope(voice); /* Continue the envelope on the next step */
+ return;
+ }
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range(0, vol);
+ gus_rampon(0x20); /* Increasing volume, with IRQ */
+ }
+ else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range(vol, 4030);
+ gus_rampon(0x60); /* Decreasing volume, with IRQ */
+ }
+ voices[voice].current_volume = vol;
+ spin_unlock_irqrestore(&gus_lock,flags);
+}
+
+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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus_lock,flags);
+ gus_select_voice(voice);
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8(0x00, 0x03); /* Hard stop */
+ voice_alloc->map[voice] = 0;
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ }
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release(voice);
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ }
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off();
+ gus_rampoff();
+ gus_voice_init(voice);
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ }
+ gus_ramp_range(65, 4030);
+ gus_ramp_rate(2, 4);
+ gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ spin_unlock_irqrestore(&gus_lock,flags);
+}
+
+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);
+ }
+}
+
+static void gus_initialize(void)
+{
+ unsigned long flags;
+ unsigned char dma_image, irq_image, tmp;
+
+ static unsigned char gus_irq_map[16] = {
+ 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7
+ };
+
+ static unsigned char gus_dma_map[8] = {
+ 0, 1, 0, 2, 0, 3, 4, 5
+ };
+
+ spin_lock_irqsave(&gus_lock,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 (for a moment) */
+ 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 (!gus_pnp_flag && !tmp)
+ printk(KERN_WARNING "Warning! GUS IRQ not selected\n");
+ irq_image |= tmp;
+ irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+
+ dual_dma_mode = 1;
+ if (gus_dma2 == gus_dma || gus_dma2 == -1)
+ {
+ dual_dma_mode = 0;
+ dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+
+ tmp = gus_dma_map[gus_dma];
+ if (!tmp)
+ printk(KERN_WARNING "Warning! GUS DMA not selected\n");
+
+ dma_image |= tmp;
+ }
+ else
+ {
+ /* Setup dual DMA channel mode for GUS MAX */
+
+ dma_image = gus_dma_map[gus_dma];
+ if (!dma_image)
+ printk(KERN_WARNING "Warning! GUS DMA not selected\n");
+
+ tmp = gus_dma_map[gus_dma2] << 3;
+ if (!tmp)
+ {
+ printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n");
+ tmp = 0x40; /* Combine DMA channels */
+ dual_dma_mode = 0;
+ }
+ 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(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */
+
+ 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 */
+
+ if (iw_mode)
+ gus_write8(0x19, gus_read8(0x19) | 0x01);
+ spin_unlock_irqrestore(&gus_lock,flags);
+}
+
+
+static void __init pnp_mem_init(void)
+{
+#include "iwmem.h"
+#define CHUNK_SIZE (256*1024)
+#define BANK_SIZE (4*1024*1024)
+#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE)
+
+ int bank, chunk, addr, total = 0;
+ int bank_sizes[4];
+ int i, j, bits = -1, testbits = -1, nbanks = 0;
+
+ /*
+ * This routine determines what kind of RAM is installed in each of the four
+ * SIMM banks and configures the DRAM address decode logic accordingly.
+ */
+
+ /*
+ * Place the chip into enhanced mode
+ */
+ gus_write8(0x19, gus_read8(0x19) | 0x01);
+ gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */
+
+ /*
+ * Set memory configuration to 4 DRAM banks of 4M in each (16M total).
+ */
+
+ gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c);
+
+ /*
+ * Perform the DRAM size detection for each bank individually.
+ */
+ for (bank = 0; bank < 4; bank++)
+ {
+ int size = 0;
+
+ addr = bank * BANK_SIZE;
+
+ /* Clean check points of each chunk */
+ for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+ {
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+ }
+
+ /* Write a value to each chunk point and verify the result */
+ for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+ {
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA);
+
+ if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 &&
+ gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA)
+ {
+ /* OK. There is RAM. Now check for possible shadows */
+ int ok = 1, chunk2;
+
+ for (chunk2 = 0; ok && chunk2 < chunk; chunk2++)
+ if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) ||
+ gus_peek(addr + chunk2 * CHUNK_SIZE + 1L))
+ ok = 0; /* Addressing wraps */
+
+ if (ok)
+ size = (chunk + 1) * CHUNK_SIZE;
+ }
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+ }
+ bank_sizes[bank] = size;
+ if (size)
+ nbanks = bank + 1;
+ DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024));
+ }
+
+ if (nbanks == 0) /* No RAM - Give up */
+ {
+ printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n");
+ printk(KERN_ERR "Sound: Unable to work with this card.\n");
+ gus_write8(0x19, gus_read8(0x19) & ~0x01);
+ gus_mem_size = 0;
+ return;
+ }
+
+ /*
+ * Now we know how much DRAM there is in each bank. The next step is
+ * to find a DRAM size encoding (0 to 12) which is best for the combination
+ * we have.
+ *
+ * First try if any of the possible alternatives matches exactly the amount
+ * of memory we have.
+ */
+
+ for (i = 0; bits == -1 && i < 13; i++)
+ {
+ bits = i;
+
+ for (j = 0; bits != -1 && j < 4; j++)
+ if (mem_decode[i][j] != bank_sizes[j])
+ bits = -1; /* No hit */
+ }
+
+ /*
+ * If necessary, try to find a combination where other than the last
+ * bank matches our configuration and the last bank is left oversized.
+ * In this way we don't leave holes in the middle of memory.
+ */
+
+ if (bits == -1) /* No luck yet */
+ {
+ for (i = 0; bits == -1 && i < 13; i++)
+ {
+ bits = i;
+
+ for (j = 0; bits != -1 && j < nbanks - 1; j++)
+ if (mem_decode[i][j] != bank_sizes[j])
+ bits = -1; /* No hit */
+ if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1])
+ bits = -1; /* The last bank is too small */
+ }
+ }
+ /*
+ * The last resort is to search for a combination where the banks are
+ * smaller than the actual SIMMs. This leaves some memory in the banks
+ * unused but doesn't leave holes in the DRAM address space.
+ */
+ if (bits == -1) /* No luck yet */
+ {
+ for (i = 0; i < 13; i++)
+ {
+ testbits = i;
+ for (j = 0; testbits != -1 && j < nbanks - 1; j++)
+ if (mem_decode[i][j] > bank_sizes[j]) {
+ testbits = -1;
+ }
+ if(testbits > bits) bits = testbits;
+ }
+ if (bits != -1)
+ {
+ printk(KERN_INFO "Interwave: Can't use all installed RAM.\n");
+ printk(KERN_INFO "Interwave: Try reordering SIMMS.\n");
+ }
+ printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n");
+ printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n");
+ bits = 0;
+ }
+ DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits));
+
+ for (bank = 0; bank < 4; bank++)
+ {
+ DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024));
+
+ if (bank_sizes[bank] > mem_decode[bits][bank])
+ total += mem_decode[bits][bank];
+ else
+ total += bank_sizes[bank];
+ }
+
+ DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024));
+
+ /*
+ * Set the memory addressing mode.
+ */
+ gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits);
+
+/* Leave the chip into enhanced mode. Disable LFO */
+ gus_mem_size = total;
+ iw_mode = 1;
+ gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02);
+}
+
+int __init gus_wave_detect(int baseaddr)
+{
+ unsigned long i, max_mem = 1024L;
+ unsigned long loc;
+ unsigned char val;
+
+ if (!request_region(baseaddr, 16, "GUS"))
+ return 0;
+ if (!request_region(baseaddr + 0x100, 12, "GUS")) { /* 0x10c-> is MAX */
+ release_region(baseaddr, 16);
+ return 0;
+ }
+
+ gus_base = baseaddr;
+
+ gus_write8(0x4c, 0); /* Reset GF1 */
+ gus_delay();
+ gus_delay();
+
+ gus_write8(0x4c, 1); /* Release Reset */
+ gus_delay();
+ gus_delay();
+
+#ifdef GUSPNP_AUTODETECT
+ val = gus_look8(0x5b); /* Version number register */
+ gus_write8(0x5b, ~val); /* Invert all bits */
+
+ if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */
+ {
+ if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */
+ {
+ DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4));
+ gus_pnp_flag = 1;
+ }
+ else
+ {
+ DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b)));
+ gus_pnp_flag = 0;
+ }
+ }
+ gus_write8(0x5b, val); /* Restore all bits */
+#endif
+
+ if (gus_pnp_flag)
+ pnp_mem_init();
+ if (iw_mode)
+ return 1;
+
+ /* See if there is first block there.... */
+ gus_poke(0L, 0xaa);
+ if (gus_peek(0L) != 0xaa) {
+ release_region(baseaddr + 0x100, 12);
+ release_region(baseaddr, 16);
+ return 0;
+ }
+
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke(0L, 0x00);
+ for (i = 1L; i < max_mem; 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, void __user *arg)
+{
+
+ switch (cmd)
+ {
+ case SNDCTL_SYNTH_INFO:
+ gus_info.nr_voices = nr_voices;
+ if (copy_to_user(arg, &gus_info, sizeof(gus_info)))
+ return -EFAULT;
+ return 0;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ reset_sample_memory();
+ return 0;
+
+ case SNDCTL_SEQ_PERCMODE:
+ return 0;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int guswave_set_instr(int dev, int voice, int instr_no)
+{
+ int sample_no;
+
+ if (instr_no < 0 || instr_no > MAX_PATCH)
+ instr_no = 0; /* Default to acoustic piano */
+
+ if (voice < 0 || voice > 31)
+ return -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 == NOT_SAMPLE)
+ {
+/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/
+ return -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 -EINVAL;
+ }
+ sample_map[voice] = sample_no;
+ patch_map[voice] = instr_no;
+ return 0;
+}
+
+static int guswave_kill_note(int dev, int voice, int note, int velocity)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus_lock,flags);
+ /* voice_alloc->map[voice] = 0xffff; */
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].kill_pending = 1;
+ spin_unlock_irqrestore(&gus_lock,flags);
+ }
+ else
+ {
+ spin_unlock_irqrestore(&gus_lock,flags);
+ gus_voice_fade(voice);
+ }
+
+ return 0;
+}
+
+static void guswave_aftertouch(int dev, int voice, int pressure)
+{
+}
+
+static void guswave_panning(int dev, int voice, int value)
+{
+ if (voice >= 0 || voice < 32)
+ voices[voice].panning = value;
+}
+
+static void guswave_volume_method(int dev, int mode)
+{
+ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+ volume_method = mode;
+}
+
+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;
+
+ case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
+ voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_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 curr, target, rate;
+ unsigned long flags;
+
+ compute_volume(voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ spin_lock_irqsave(&gus_lock,flags);
+ /*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice(voice);
+
+ curr = gus_read16(0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff();
+ gus_voice_volume(target);
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ }
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate(0, rate);
+
+ if ((target - curr) / 64 == 0) /* Close enough to target. */
+ {
+ gus_rampoff();
+ gus_voice_volume(target);
+ spin_unlock_irqrestore(&gus_lock,flags);
+ return;
+ }
+ if (target > curr)
+ {
+ if (target > (4095 - 65))
+ target = 4095 - 65;
+ gus_ramp_range(curr, target);
+ gus_rampon(0x00); /* Ramp up, once, no IRQ */</