You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3464 lines
79 KiB
3464 lines
79 KiB
/*
|
|
* 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 */
|
|
}
|
|
else
|
|
{
|
|
if (target < 65)
|
|
target = 65;
|
|
|
|
gus_ramp_range(target, curr);
|
|
gus_rampon(0x40); /* Ramp down, once, no irq */
|
|
}
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
|
|
static void dynamic_volume_change(int voice)
|
|
{
|
|
unsigned char status;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
status = gus_read8(0x00); /* Get voice status */
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
|
|
if (status & 0x03)
|
|
return; /* Voice was not running */
|
|
|
|
if (!(voices[voice].mode & WAVE_ENVELOPES))
|
|
{
|
|
compute_and_set_volume(voice, voices[voice].midi_volume, 1);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Voice is running and has envelopes.
|
|
*/
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
status = gus_read8(0x0d); /* Ramping status */
|
|
spin_unlock_irqrestore(&gus_lock,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);
|
|
|
|
}
|
|
|
|
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, 0);
|
|
voices[voice].current_freq = freq;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_freq(freq);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
break;
|
|
|
|
case CTRL_PITCH_BENDER_RANGE:
|
|
voices[voice].bender_range = value;
|
|
break;
|
|
case CTL_EXPRESSION:
|
|
value /= 128;
|
|
case CTRL_EXPRESSION:
|
|
if (volume_method == VOL_METHOD_ADAGIO)
|
|
{
|
|
voices[voice].expression_vol = value;
|
|
if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
|
|
dynamic_volume_change(voice);
|
|
}
|
|
break;
|
|
|
|
case CTL_PAN:
|
|
voices[voice].panning = (value * 2) - 128;
|
|
break;
|
|
|
|
case CTL_MAIN_VOLUME:
|
|
value = (value * 100) / 16383;
|
|
|
|
case CTRL_MAIN_VOLUME:
|
|
voices[voice].main_vol = value;
|
|
if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
|
|
dynamic_volume_change(voice);
|
|
break;
|
|
|
|
default:
|
|
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 -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 -EINVAL;
|
|
if ((samplep = patch_table[patch]) == NOT_SAMPLE)
|
|
{
|
|
return -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 && samplep != NOT_SAMPLE && 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; /* Link to next sample */
|
|
}
|
|
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;
|
|
voices[voice].mode = samples[sample].mode;
|
|
voices[voice].patch_vol = samples[sample].volume;
|
|
|
|
if (iw_mode)
|
|
gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */
|
|
|
|
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;
|
|
|
|
if (voices[voice].fixed_pitch) /* Fixed pitch */
|
|
{
|
|
freq = samples[sample].base_freq;
|
|
}
|
|
else
|
|
{
|
|
base_note = samples[sample].base_note / 100;
|
|
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, 0);
|
|
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] / GUS_BANK_SIZE) !=
|
|
((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE))
|
|
printk(KERN_ERR "GUS: Sample address error\n");
|
|
}
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_off();
|
|
gus_rampoff();
|
|
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
{
|
|
compute_volume(voice, volume);
|
|
init_envelope(voice);
|
|
}
|
|
else
|
|
{
|
|
compute_and_set_volume(voice, volume, 0);
|
|
}
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
|
|
if (samples[sample].mode & WAVE_LOOP_BACK)
|
|
gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len -
|
|
voices[voice].offset_pending, 0, is16bits); /* start=end */
|
|
else
|
|
gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */
|
|
|
|
if (samples[sample].mode & WAVE_LOOPING)
|
|
{
|
|
mode |= 0x08;
|
|
|
|
if (samples[sample].mode & WAVE_BIDIR_LOOP)
|
|
mode |= 0x10;
|
|
|
|
if (samples[sample].mode & WAVE_LOOP_BACK)
|
|
{
|
|
gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end -
|
|
voices[voice].offset_pending,
|
|
(samples[sample].fractions >> 4) & 0x0f, is16bits);
|
|
mode |= 0x40;
|
|
}
|
|
gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start,
|
|
samples[sample].fractions & 0x0f, is16bits); /* Loop start location */
|
|
gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end,
|
|
(samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */
|
|
}
|
|
else
|
|
{
|
|
mode |= 0x20; /* Loop IRQ at the end */
|
|
voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
|
|
voices[voice].loop_irq_parm = 1;
|
|
gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */
|
|
gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1,
|
|
(samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */
|
|
}
|
|
gus_voice_freq(freq);
|
|
gus_voice_balance(pan);
|
|
gus_voice_on(mode);
|
|
spin_unlock_irqrestore(&gus_lock,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)
|
|
{
|
|
unsigned long flags;
|
|
int mode;
|
|
int ret_val = 0;
|
|
|
|
spin_lock_irqsave(&gus_lock,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)
|
|
{
|
|
spin_unlock_irqrestore(&gus_lock,flags); /* Run temporarily with interrupts enabled */
|
|
guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending);
|
|
voices[voice].sample_pending = -1;
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice); /* Reselect the voice (just to be sure) */
|
|
}
|
|
if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 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 */
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&gus_lock,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 -EBUSY;
|
|
|
|
voice_alloc->timestamp = 0;
|
|
|
|
if (gus_no_wave_dma) {
|
|
gus_no_dma = 1;
|
|
} else {
|
|
if ((err = DMAbuf_open_dma(gus_devnum)) < 0)
|
|
{
|
|
/* printk( "GUS: Loading samples without DMA\n"); */
|
|
gus_no_dma = 1; /* Upload samples using PIO */
|
|
}
|
|
else
|
|
gus_no_dma = 0;
|
|
}
|
|
|
|
init_waitqueue_head(&dram_sleeper);
|
|
gus_busy = 1;
|
|
active_device = GUS_DEV_WAVE;
|
|
|
|
gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */
|
|
gus_initialize();
|
|
gus_reset();
|
|
gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void guswave_close(int dev)
|
|
{
|
|
gus_busy = 0;
|
|
active_device = 0;
|
|
gus_reset();
|
|
|
|
if (!gus_no_dma)
|
|
DMAbuf_close_dma(gus_devnum);
|
|
}
|
|
|
|
static int guswave_load_patch(int dev, int format, const char __user *addr,
|
|
int offs, int count, int pmgr_flag)
|
|
{
|
|
struct patch_info patch;
|
|
int instr;
|
|
long sizeof_patch;
|
|
|
|
unsigned long blk_sz, blk_end, left, src_offs, target;
|
|
|
|
sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
|
|
|
|
if (format != GUS_PATCH)
|
|
{
|
|
/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/
|
|
return -EINVAL;
|
|
}
|
|
if (count < sizeof_patch)
|
|
{
|
|
/* printk("GUS Error: Patch header too short\n");*/
|
|
return -EINVAL;
|
|
}
|
|
count -= sizeof_patch;
|
|
|
|
if (free_sample >= MAX_SAMPLE)
|
|
{
|
|
/* printk("GUS: Sample table full\n");*/
|
|
return -ENOSPC;
|
|
}
|
|
/*
|
|
* Copy the header from user space but ignore the first bytes which have
|
|
* been transferred already.
|
|
*/
|
|
|
|
if (copy_from_user(&((char *) &patch)[offs], &(addr)[offs],
|
|
sizeof_patch - offs))
|
|
return -EFAULT;
|
|
|
|
if (patch.mode & WAVE_ROM)
|
|
return -EINVAL;
|
|
if (gus_mem_size == 0)
|
|
return -ENOSPC;
|
|
|
|
instr = patch.instr_no;
|
|
|
|
if (instr < 0 || instr > MAX_PATCH)
|
|
{
|
|
/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/
|
|
return -EINVAL;
|
|
}
|
|
if (count < patch.len)
|
|
{
|
|
/* printk(KERN_ERR "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(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/
|
|
return -EINVAL;
|
|
}
|
|
if (patch.mode & WAVE_LOOPING)
|
|
{
|
|
if (patch.loop_start < 0 || patch.loop_start >= patch.len)
|
|
{
|
|
/* printk(KERN_ERR "GUS: Invalid loop start\n");*/
|
|
return -EINVAL;
|
|
}
|
|
if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
|
|
{
|
|
/* printk(KERN_ERR "GUS: Invalid loop end\n");*/
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
|
|
|
|
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 -ENOSPC;
|
|
}
|
|
if ((free_mem_ptr / GUS_BANK_SIZE) !=
|
|
((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
|
|
{
|
|
unsigned long tmp_mem =
|
|
/* Align to 256K */
|
|
((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
|
|
|
|
if ((tmp_mem + patch.len) > gus_mem_size)
|
|
return -ENOSPC;
|
|
|
|
free_mem_ptr = tmp_mem; /* This leaves unusable memory */
|
|
}
|
|
}
|
|
if ((free_mem_ptr + patch.len) > gus_mem_size)
|
|
return -ENOSPC;
|
|
|
|
sample_ptrs[free_sample] = free_mem_ptr;
|
|
|
|
/*
|
|
* Tremolo is not possible with envelopes
|
|
*/
|
|
|
|
if (patch.mode & WAVE_ENVELOPES)
|
|
patch.mode &= ~WAVE_TREMOLO;
|
|
|
|
if (!(patch.mode & WAVE_FRACTIONS))
|
|
{
|
|
patch.fractions = 0;
|
|
}
|
|
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 completely transferred yet */
|
|
{
|
|
blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use;
|
|
if (blk_sz > left)
|
|
blk_sz = left;
|
|
|
|
/*
|
|
* DMA cannot cross bank (256k) boundaries. Check for that.
|
|
*/
|
|
|
|
blk_end = target + blk_sz;
|
|
|
|
if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE))
|
|
{
|
|
/* Split the block */
|
|
blk_end &= ~(GUS_BANK_SIZE - 1);
|
|
blk_sz = blk_end - target;
|
|
}
|
|
if (gus_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_sz; i++)
|
|
{
|
|
get_user(*(unsigned char *) &data, (unsigned char __user *) &((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
|
|
{
|
|
unsigned long address, hold_address;
|
|
unsigned char dma_command;
|
|
unsigned long flags;
|
|
|
|
if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL)
|
|
{
|
|
printk(KERN_ERR "GUS: DMA buffer == NULL\n");
|
|
return -ENOSPC;
|
|
}
|
|
/*
|
|
* OK, move now. First in and then out.
|
|
*/
|
|
|
|
if (copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf,
|
|
&(addr)[sizeof_patch + src_offs],
|
|
blk_sz))
|
|
return -EFAULT;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_write8(0x41, 0); /* Disable GF1 DMA */
|
|
DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys,
|
|
blk_sz, DMA_MODE_WRITE);
|
|
|
|
/*
|
|
* Set the DRAM address for the wave data
|
|
*/
|
|
|
|
if (iw_mode)
|
|
{
|
|
/* Different address translation in enhanced mode */
|
|
|
|
unsigned char hi;
|
|
|
|
if (gus_dma > 4)
|
|
address = target >> 1; /* Convert to 16 bit word address */
|
|
else
|
|
address = target;
|
|
|
|
hi = (unsigned char) ((address >> 16) & 0xf0);
|
|
hi += (unsigned char) (address & 0x0f);
|
|
|
|
gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */
|
|
gus_write8(0x50, hi);
|
|
}
|
|
else
|
|
{
|
|
address = target;
|
|
if (audio_devs[gus_devnum]->dmap_out->dma > 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 (audio_devs[gus_devnum]->dmap_out->dma > 3)
|
|
dma_command |= 0x04; /* 16 bit DMA _channel_ */
|
|
|
|
/*
|
|
* Sleep here until the DRAM DMA done interrupt is served
|
|
*/
|
|
active_device = GUS_DEV_WAVE;
|
|
gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */
|
|
|
|
spin_unlock_irqrestore(&gus_lock,flags); /* opens a race */
|
|
if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ))
|
|
printk("GUS: DMA Transfer timed out\n");
|
|
}
|
|
|
|
/*
|
|
* Now the next part
|
|
*/
|
|
|
|
left -= blk_sz;
|
|
src_offs += blk_sz;
|
|
target += blk_sz;
|
|
|
|
gus_write8(0x41, 0); /* Stop DMA */
|
|
}
|
|
|
|
free_mem_ptr += patch.len;
|
|
free_sample++;
|
|
return 0;
|
|
}
|
|
|
|
static void guswave_hw_control(int dev, unsigned char *event_rec)
|
|
{
|
|
int voice, cmd;
|
|
unsigned short p1, p2;
|
|
unsigned int plong;
|
|
unsigned long flags;
|
|
|
|
cmd = event_rec[2];
|
|
voice = event_rec[3];
|
|
p1 = *(unsigned short *) &event_rec[4];
|
|
p2 = *(unsigned short *) &event_rec[6];
|
|
plong = *(unsigned int *) &event_rec[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:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_select_max_voices(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICESAMPLE:
|
|
guswave_set_instr(dev, voice, p1);
|
|
break;
|
|
|
|
case _GUS_VOICEON:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
p1 &= ~0x20; /* Don't allow interrupts */
|
|
gus_voice_on(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEOFF:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_off();
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEFADE:
|
|
gus_voice_fade(voice);
|
|
break;
|
|
|
|
case _GUS_VOICEMODE:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
p1 &= ~0x20; /* Don't allow interrupts */
|
|
gus_voice_mode(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEBALA:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_balance(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEFREQ:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_freq(plong);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEVOL:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_volume(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOICEVOL2: /* Just update the software voice level */
|
|
voices[voice].initial_volume = voices[voice].current_volume = p1;
|
|
break;
|
|
|
|
case _GUS_RAMPRANGE:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* NO-NO */
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_ramp_range(p1, p2);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_RAMPRATE:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* NJET-NJET */
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_ramp_rate(p1, p2);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_RAMPMODE:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* NO-NO */
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
p1 &= ~0x20; /* Don't allow interrupts */
|
|
gus_ramp_mode(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_RAMPON:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* EI-EI */
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
p1 &= ~0x20; /* Don't allow interrupts */
|
|
gus_rampon(p1);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_RAMPOFF:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* NEJ-NEJ */
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_rampoff();
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
case _GUS_VOLUME_SCALE:
|
|
volume_base = p1;
|
|
volume_scale = p2;
|
|
break;
|
|
|
|
case _GUS_VOICE_POS:
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_set_voice_pos(voice, plong);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int gus_audio_set_speed(int speed)
|
|
{
|
|
if (speed <= 0)
|
|
speed = gus_audio_speed;
|
|
|
|
if (speed < 4000)
|
|
speed = 4000;
|
|
|
|
if (speed > 44100)
|
|
speed = 44100;
|
|
|
|
gus_audio_speed = speed;
|
|
|
|
if (only_read_access)
|
|
{
|
|
/* Compute nearest valid recording speed and return it */
|
|
|
|
/* speed = (9878400 / (gus_audio_speed + 2)) / 16; */
|
|
speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
|
|
speed = (9878400 / (speed * 16)) - 2;
|
|
}
|
|
return speed;
|
|
}
|
|
|
|
static int gus_audio_set_channels(int channels)
|
|
{
|
|
if (!channels)
|
|
return gus_audio_channels;
|
|
if (channels > 2)
|
|
channels = 2;
|
|
if (channels < 1)
|
|
channels = 1;
|
|
gus_audio_channels = channels;
|
|
return channels;
|
|
}
|
|
|
|
static int gus_audio_set_bits(int bits)
|
|
{
|
|
if (!bits)
|
|
return gus_audio_bits;
|
|
|
|
if (bits != 8 && bits != 16)
|
|
bits = 8;
|
|
|
|
if (only_8_bits)
|
|
bits = 8;
|
|
|
|
gus_audio_bits = bits;
|
|
return bits;
|
|
}
|
|
|
|
static int gus_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
|
|
{
|
|
int val;
|
|
|
|
switch (cmd)
|
|
{
|
|
case SOUND_PCM_WRITE_RATE:
|
|
if (get_user(val, (int __user*)arg))
|
|
return -EFAULT;
|
|
val = gus_audio_set_speed(val);
|
|
break;
|
|
|
|
case SOUND_PCM_READ_RATE:
|
|
val = gus_audio_speed;
|
|
break;
|
|
|
|
case SNDCTL_DSP_STEREO:
|
|
if (get_user(val, (int __user *)arg))
|
|
return -EFAULT;
|
|
val = gus_audio_set_channels(val + 1) - 1;
|
|
break;
|
|
|
|
case SOUND_PCM_WRITE_CHANNELS:
|
|
if (get_user(val, (int __user *)arg))
|
|
return -EFAULT;
|
|
val = gus_audio_set_channels(val);
|
|
break;
|
|
|
|
case SOUND_PCM_READ_CHANNELS:
|
|
val = gus_audio_channels;
|
|
break;
|
|
|
|
case SNDCTL_DSP_SETFMT:
|
|
if (get_user(val, (int __user *)arg))
|
|
return -EFAULT;
|
|
val = gus_audio_set_bits(val);
|
|
break;
|
|
|
|
case SOUND_PCM_READ_BITS:
|
|
val = gus_audio_bits;
|
|
break;
|
|
|
|
case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
|
|
case SOUND_PCM_READ_FILTER:
|
|
val = -EINVAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return put_user(val, (int __user *)arg);
|
|
}
|
|
|
|
static void gus_audio_reset(int dev)
|
|
{
|
|
if (recording_active)
|
|
{
|
|
gus_write8(0x49, 0x00); /* Halt recording */
|
|
set_input_volumes();
|
|
}
|
|
}
|
|
|
|
static int saved_iw_mode; /* A hack hack hack */
|
|
|
|
static int gus_audio_open(int dev, int mode)
|
|
{
|
|
if (gus_busy)
|
|
return -EBUSY;
|
|
|
|
if (gus_pnp_flag && mode & OPEN_READ)
|
|
{
|
|
/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/
|
|
return -EIO;
|
|
}
|
|
gus_initialize();
|
|
|
|
gus_busy = 1;
|
|
active_device = 0;
|
|
|
|
saved_iw_mode = iw_mode;
|
|
if (iw_mode)
|
|
{
|
|
/* There are some problems with audio in enhanced mode so disable it */
|
|
gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */
|
|
iw_mode = 0;
|
|
}
|
|
|
|
gus_reset();
|
|
reset_sample_memory();
|
|
gus_select_max_voices(14);
|
|
|
|
pcm_active = 0;
|
|
dma_active = 0;
|
|
pcm_opened = 1;
|
|
if (mode & OPEN_READ)
|
|
{
|
|
recording_active = 1;
|
|
set_input_volumes();
|
|
}
|
|
only_read_access = !(mode & OPEN_WRITE);
|
|
only_8_bits = mode & OPEN_READ;
|
|
if (only_8_bits)
|
|
audio_devs[dev]->format_mask = AFMT_U8;
|
|
else
|
|
audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gus_audio_close(int dev)
|
|
{
|
|
iw_mode = saved_iw_mode;
|
|
gus_reset();
|
|
gus_busy = 0;
|
|
pcm_opened = 0;
|
|
active_device = 0;
|
|
|
|
if (recording_active)
|
|
{
|
|
gus_write8(0x49, 0x00); /* Halt recording */
|
|
set_input_volumes();
|
|
}
|
|
recording_active = 0;
|
|
}
|
|
|
|
static void gus_audio_update_volume(void)
|
|
{
|
|
unsigned long flags;
|
|
int voice;
|
|
|
|
if (pcm_active && pcm_opened)
|
|
for (voice = 0; voice < gus_audio_channels; voice++)
|
|
{
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_rampoff();
|
|
gus_voice_volume(1530 + (25 * gus_pcm_volume));
|
|
gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
}
|
|
|
|
static void play_next_pcm_block(void)
|
|
{
|
|
unsigned long flags;
|
|
int speed = gus_audio_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_audio_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_audio_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 fragment of the DRAM buffer */
|
|
{
|
|
mode[chn] |= 0x08; /* Enable loop */
|
|
ramp_mode[chn] = 0x03; /* Disable rollover bit */
|
|
}
|
|
else
|
|
{
|
|
if (chn == 0)
|
|
ramp_mode[chn] = 0x04; /* Enable rollover bit */
|
|
}
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(chn);
|
|
gus_voice_freq(speed);
|
|
|
|
if (gus_audio_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) /* Playback not already active */
|
|
{
|
|
/*
|
|
* 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();
|
|
gus_rampoff();
|
|
gus_voice_volume(1530 + (25 * gus_pcm_volume));
|
|
gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
|
|
|
|
gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */
|
|
gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */
|
|
|
|
if (chn != 0)
|
|
gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1,
|
|
0, is16bits); /* Loop end location */
|
|
}
|
|
if (chn == 0)
|
|
gus_write_addr(0x04, dram_loc + pcm_bsize - 1,
|
|
0, is16bits); /* Loop end location */
|
|
else
|
|
mode[chn] |= 0x08; /* Enable looping */
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
for (chn = 0; chn < gus_audio_channels; chn++)
|
|
{
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(chn);
|
|
gus_write8(0x0d, ramp_mode[chn]);
|
|
if (iw_mode)
|
|
gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */
|
|
gus_voice_on(mode[chn]);
|
|
spin_unlock_irqrestore(&gus_lock,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;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
|
|
count = total_count / gus_audio_channels;
|
|
|
|
if (chn == 0)
|
|
{
|
|
if (pcm_qlen >= pcm_nblk)
|
|
printk(KERN_WARNING "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 (audio_devs[dev]->dmap_out->dma > 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_audio_bits != 8)
|
|
dma_command |= 0x40; /* 16 bit _DATA_ */
|
|
else
|
|
dma_command |= 0x80; /* Invert MSB */
|
|
|
|
if (audio_devs[dev]->dmap_out->dma > 3)
|
|
dma_command |= 0x04; /* 16 bit DMA channel */
|
|
|
|
gus_write8(0x41, dma_command); /* Kick start */
|
|
|
|
if (chn == (gus_audio_channels - 1)) /* Last channel */
|
|
{
|
|
/*
|
|
* Last (right or mono) channel data
|
|
*/
|
|
dma_active = 1; /* DMA started. There is a unacknowledged buffer */
|
|
active_device = GUS_DEV_PCM_DONE;
|
|
if (!pcm_active && (pcm_qlen > 1 || 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;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
|
|
static void gus_uninterleave8(char *buf, int l)
|
|
{
|
|
/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */
|
|
int i, p = 0, halfsize = l / 2;
|
|
char *buf2 = buf + halfsize, *src = bounce_buf;
|
|
|
|
memcpy(bounce_buf, buf, l);
|
|
|
|
for (i = 0; i < halfsize; i++)
|
|
{
|
|
buf[i] = src[p++]; /* Left channel */
|
|
buf2[i] = src[p++]; /* Right channel */
|
|
}
|
|
}
|
|
|
|
static void gus_uninterleave16(short *buf, int l)
|
|
{
|
|
/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */
|
|
int i, p = 0, halfsize = l / 2;
|
|
short *buf2 = buf + halfsize, *src = (short *) bounce_buf;
|
|
|
|
memcpy(bounce_buf, (char *) buf, l * 2);
|
|
|
|
for (i = 0; i < halfsize; i++)
|
|
{
|
|
buf[i] = src[p++]; /* Left channel */
|
|
buf2[i] = src[p++]; /* Right channel */
|
|
}
|
|
}
|
|
|
|
static void gus_audio_output_block(int dev, unsigned long buf, int total_count,
|
|
int intrflag)
|
|
{
|
|
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
|
|
|
|
dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT;
|
|
|
|
pcm_current_buf = buf;
|
|
pcm_current_count = total_count;
|
|
pcm_current_intrflag = intrflag;
|
|
pcm_current_dev = dev;
|
|
if (gus_audio_channels == 2)
|
|
{
|
|
char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys);
|
|
|
|
if (gus_audio_bits == 8)
|
|
gus_uninterleave8(b, total_count);
|
|
else
|
|
gus_uninterleave16((short *) b, total_count / 2);
|
|
}
|
|
gus_transfer_output_block(dev, buf, total_count, intrflag, 0);
|
|
}
|
|
|
|
static void gus_audio_start_input(int dev, unsigned long buf, int count,
|
|
int intrflag)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char mode;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
|
|
DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ);
|
|
mode = 0xa0; /* DMA IRQ enabled, invert MSB */
|
|
|
|
if (audio_devs[dev]->dmap_in->dma > 3)
|
|
mode |= 0x04; /* 16 bit DMA channel */
|
|
if (gus_audio_channels > 1)
|
|
mode |= 0x02; /* Stereo */
|
|
mode |= 0x01; /* DMA enable */
|
|
|
|
gus_write8(0x49, mode);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
|
|
static int gus_audio_prepare_for_input(int dev, int bsize, int bcount)
|
|
{
|
|
unsigned int rate;
|
|
|
|
gus_audio_bsize = bsize;
|
|
audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
|
|
rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
|
|
|
|
gus_write8(0x48, rate & 0xff); /* Set sampling rate */
|
|
|
|
if (gus_audio_bits != 8)
|
|
{
|
|
/* printk("GUS Error: 16 bit recording not supported\n");*/
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int gus_audio_prepare_for_output(int dev, int bsize, int bcount)
|
|
{
|
|
int i;
|
|
|
|
long mem_ptr, mem_size;
|
|
|
|
audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT;
|
|
mem_ptr = 0;
|
|
mem_size = gus_mem_size / gus_audio_channels;
|
|
|
|
if (mem_size > (256 * 1024))
|
|
mem_size = 256 * 1024;
|
|
|
|
pcm_bsize = bsize / gus_audio_channels;
|
|
pcm_head = pcm_tail = pcm_qlen = 0;
|
|
|
|
pcm_nblk = 2; /* 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_audio_bits != 8 && pcm_banksize == (256 * 1024))
|
|
pcm_nblk--;
|
|
gus_write8(0x41, 0); /* Disable GF1 DMA */
|
|
return 0;
|
|
}
|
|
|
|
static int gus_local_qlen(int dev)
|
|
{
|
|
return pcm_qlen;
|
|
}
|
|
|
|
|
|
static struct audio_driver gus_audio_driver =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.open = gus_audio_open,
|
|
.close = gus_audio_close,
|
|
.output_block = gus_audio_output_block,
|
|
.start_input = gus_audio_start_input,
|
|
.ioctl = gus_audio_ioctl,
|
|
.prepare_for_input = gus_audio_prepare_for_input,
|
|
.prepare_for_output = gus_audio_prepare_for_output,
|
|
.halt_io = gus_audio_reset,
|
|
.local_qlen = gus_local_qlen,
|
|
};
|
|
|
|
static void guswave_setup_voice(int dev, int voice, int chn)
|
|
{
|
|
struct channel_info *info = &synth_devs[dev]->chn_info[chn];
|
|
|
|
guswave_set_instr(dev, voice, info->pgm_num);
|
|
voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */
|
|
voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
|
|
voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
|
|
voices[voice].bender = 0;
|
|
voices[voice].bender_range = info->bender_range;
|
|
|
|
if (chn == 9)
|
|
voices[voice].fixed_pitch = 1;
|
|
}
|
|
|
|
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 - 8192, voices[voice].bender_range, 0);
|
|
voices[voice].current_freq = freq;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
gus_select_voice(voice);
|
|
gus_voice_freq(freq);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
|
|
static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
|
|
{
|
|
int i, p, best = -1, best_time = 0x7fffffff;
|
|
|
|
p = alloc->ptr;
|
|
/*
|
|
* First look for a completely stopped voice
|
|
*/
|
|
|
|
for (i = 0; i < alloc->max_voice; i++)
|
|
{
|
|
if (alloc->map[p] == 0)
|
|
{
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
if (alloc->alloc_times[p] < best_time)
|
|
{
|
|
best = p;
|
|
best_time = alloc->alloc_times[p];
|
|
}
|
|
p = (p + 1) % alloc->max_voice;
|
|
}
|
|
|
|
/*
|
|
* Then look for a releasing voice
|
|
*/
|
|
|
|
for (i = 0; i < alloc->max_voice; i++)
|
|
{
|
|
if (alloc->map[p] == 0xffff)
|
|
{
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
p = (p + 1) % alloc->max_voice;
|
|
}
|
|
if (best >= 0)
|
|
p = best;
|
|
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
|
|
static struct synth_operations guswave_operations =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.id = "GUS",
|
|
.info = &gus_info,
|
|
.midi_dev = 0,
|
|
.synth_type = SYNTH_TYPE_SAMPLE,
|
|
.synth_subtype = SAMPLE_TYPE_GUS,
|
|
.open = guswave_open,
|
|
.close = guswave_close,
|
|
.ioctl = guswave_ioctl,
|
|
.kill_note = guswave_kill_note,
|
|
.start_note = guswave_start_note,
|
|
.set_instr = guswave_set_instr,
|
|
.reset = guswave_reset,
|
|
.hw_control = guswave_hw_control,
|
|
.load_patch = guswave_load_patch,
|
|
.aftertouch = guswave_aftertouch,
|
|
.controller = guswave_controller,
|
|
.panning = guswave_panning,
|
|
.volume_method = guswave_volume_method,
|
|
.bender = guswave_bender,
|
|
.alloc_voice = guswave_alloc,
|
|
.setup_voice = guswave_setup_voice
|
|
};
|
|
|
|
static void set_input_volumes(void)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
|
|
|
|
if (have_gus_max) /* Don't disturb GUS MAX */
|
|
return;
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
|
|
/*
|
|
* Enable channels having vol > 10%
|
|
* Note! bit 0x01 means the line in DISABLED while 0x04 means
|
|
* the 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);
|
|
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
|
|
#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
|
|
SOUND_MASK_SYNTH|SOUND_MASK_PCM)
|
|
|
|
int gus_default_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
|
|
{
|
|
int vol, val;
|
|
|
|
if (((cmd >> 8) & 0xff) != 'M')
|
|
return -EINVAL;
|
|
|
|
if (!access_ok(VERIFY_WRITE, arg, sizeof(int)))
|
|
return -EFAULT;
|
|
|
|
if (_SIOC_DIR(cmd) & _SIOC_WRITE)
|
|
{
|
|
if (__get_user(val, (int __user *) arg))
|
|
return -EFAULT;
|
|
|
|
switch (cmd & 0xff)
|
|
{
|
|
case SOUND_MIXER_RECSRC:
|
|
gus_recmask = val & 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 */
|
|
val = gus_recmask;
|
|
break;
|
|
|
|
case SOUND_MIXER_MIC:
|
|
vol = val & 0xff;
|
|
if (vol < 0)
|
|
vol = 0;
|
|
if (vol > 100)
|
|
vol = 100;
|
|
gus_mic_vol = vol;
|
|
set_input_volumes();
|
|
val = vol | (vol << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_LINE:
|
|
vol = val & 0xff;
|
|
if (vol < 0)
|
|
vol = 0;
|
|
if (vol > 100)
|
|
vol = 100;
|
|
gus_line_vol = vol;
|
|
set_input_volumes();
|
|
val = vol | (vol << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_PCM:
|
|
gus_pcm_volume = val & 0xff;
|
|
if (gus_pcm_volume < 0)
|
|
gus_pcm_volume = 0;
|
|
if (gus_pcm_volume > 100)
|
|
gus_pcm_volume = 100;
|
|
gus_audio_update_volume();
|
|
val = gus_pcm_volume | (gus_pcm_volume << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_SYNTH:
|
|
gus_wave_volume = val & 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)
|
|
{
|
|
int voice;
|
|
for (voice = 0; voice < nr_voices; voice++)
|
|
dynamic_volume_change(voice); /* Apply the new vol */
|
|
}
|
|
val = gus_wave_volume | (gus_wave_volume << 8);
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (cmd & 0xff)
|
|
{
|
|
/*
|
|
* Return parameters
|
|
*/
|
|
case SOUND_MIXER_RECSRC:
|
|
val = gus_recmask;
|
|
break;
|
|
|
|
case SOUND_MIXER_DEVMASK:
|
|
val = MIX_DEVS;
|
|
break;
|
|
|
|
case SOUND_MIXER_STEREODEVS:
|
|
val = 0;
|
|
break;
|
|
|
|
case SOUND_MIXER_RECMASK:
|
|
val = SOUND_MASK_MIC | SOUND_MASK_LINE;
|
|
break;
|
|
|
|
case SOUND_MIXER_CAPS:
|
|
val = 0;
|
|
break;
|
|
|
|
case SOUND_MIXER_MIC:
|
|
val = gus_mic_vol | (gus_mic_vol << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_LINE:
|
|
val = gus_line_vol | (gus_line_vol << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_PCM:
|
|
val = gus_pcm_volume | (gus_pcm_volume << 8);
|
|
break;
|
|
|
|
case SOUND_MIXER_SYNTH:
|
|
val = gus_wave_volume | (gus_wave_volume << 8);
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
return __put_user(val, (int __user *)arg);
|
|
}
|
|
|
|
static struct mixer_operations gus_mixer_operations =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.id = "GUS",
|
|
.name = "Gravis Ultrasound",
|
|
.ioctl = gus_default_mixer_ioctl
|
|
};
|
|
|
|
static int __init gus_default_mixer_init(void)
|
|
{
|
|
int n;
|
|
|
|
if ((n = sound_alloc_mixerdev()) != -1)
|
|
{
|
|
/*
|
|
* Don't install if there is another
|
|
* mixer
|
|
*/
|
|
mixer_devs[n] = &gus_mixer_operations;
|
|
}
|
|
if (have_gus_max)
|
|
{
|
|
/*
|
|
* Enable all mixer channels on the GF1 side. Otherwise recording will
|
|
* not be possible using GUS MAX.
|
|
*/
|
|
mix_image &= ~0x07;
|
|
mix_image |= 0x04; /* All channels enabled */
|
|
outb((mix_image), u_Mixer);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void __init gus_wave_init(struct address_info *hw_config)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char val;
|
|
char *model_num = "2.4";
|
|
char tmp[64];
|
|
int gus_type = 0x24; /* 2.4 */
|
|
|
|
int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2;
|
|
int sdev;
|
|
|
|
hw_config->slots[0] = -1; /* No wave */
|
|
hw_config->slots[1] = -1; /* No ad1848 */
|
|
hw_config->slots[4] = -1; /* No audio */
|
|
hw_config->slots[5] = -1; /* No mixer */
|
|
|
|
if (!gus_pnp_flag)
|
|
{
|
|
if (irq < 0 || irq > 15)
|
|
{
|
|
printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (dma < 0 || dma > 7 || dma == 4)
|
|
{
|
|
printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma);
|
|
return;
|
|
}
|
|
gus_irq = irq;
|
|
gus_dma = dma;
|
|
gus_dma2 = dma2;
|
|
gus_hw_config = hw_config;
|
|
|
|
if (gus_dma2 == -1)
|
|
gus_dma2 = dma;
|
|
|
|
/*
|
|
* Try to identify the GUS model.
|
|
*
|
|
* Versions < 3.6 don't have the digital ASIC. Try to probe it first.
|
|
*/
|
|
|
|
spin_lock_irqsave(&gus_lock,flags);
|
|
outb((0x20), gus_base + 0x0f);
|
|
val = inb(gus_base + 0x0f);
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
|
|
if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */
|
|
{
|
|
int ad_flags = 0;
|
|
|
|
if (gus_pnp_flag)
|
|
ad_flags = 0x12345678; /* Interwave "magic" */
|
|
/*
|
|
* It has the digital ASIC so the card is at least v3.4.
|
|
* Next try to detect the true model.
|
|
*/
|
|
|
|
if (gus_pnp_flag) /* Hack hack hack */
|
|
val = 10;
|
|
else
|
|
val = inb(u_MixSelect);
|
|
|
|
/*
|
|
* Value 255 means pre-3.7 which don't have mixer.
|
|
* Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
|
|
* 10 and above is GUS MAX which has the CS4231 codec/mixer.
|
|
*
|
|
*/
|
|
|
|
if (val == 255 || val < 5)
|
|
{
|
|
model_num = "3.4";
|
|
gus_type = 0x34;
|
|
}
|
|
else if (val < 10)
|
|
{
|
|
model_num = "3.7";
|
|
gus_type = 0x37;
|
|
mixer_type = ICS2101;
|
|
request_region(u_MixSelect, 1, "GUS mixer");
|
|
}
|
|
else
|
|
{
|
|
struct resource *ports;
|
|
ports = request_region(gus_base + 0x10c, 4, "ad1848");
|
|
model_num = "MAX";
|
|
gus_type = 0x40;
|
|
mixer_type = CS4231;
|
|
#ifdef CONFIG_SOUND_GUSMAX
|
|
{
|
|
unsigned char max_config = 0x40; /* Codec enable */
|
|
|
|
if (gus_dma2 == -1)
|
|
gus_dma2 = gus_dma;
|
|
|
|
if (gus_dma > 3)
|
|
max_config |= 0x10; /* 16 bit capture DMA */
|
|
|
|
if (gus_dma2 > 3)
|
|
max_config |= 0x20; /* 16 bit playback DMA */
|
|
|
|
max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
|
|
|
|
outb((max_config), gus_base + 0x106); /* UltraMax control */
|
|
}
|
|
|
|
if (!ports)
|
|
goto no_cs4231;
|
|
|
|
if (ad1848_detect(ports, &ad_flags, hw_config->osp))
|
|
{
|
|
char *name = "GUS MAX";
|
|
int old_num_mixers = num_mixers;
|
|
|
|
if (gus_pnp_flag)
|
|
name = "GUS PnP";
|
|
|
|
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
|
|
gus_wave_volume = 90;
|
|
have_gus_max = 1;
|
|
if (hw_config->name)
|
|
name = hw_config->name;
|
|
|
|
hw_config->slots[1] = ad1848_init(name, ports,
|
|
-irq, gus_dma2, /* Playback DMA */
|
|
gus_dma, /* Capture DMA */
|
|
1, /* Share DMA channels with GF1 */
|
|
hw_config->osp,
|
|
THIS_MODULE);
|
|
|
|
if (num_mixers > old_num_mixers)
|
|
{
|
|
/* GUS has it's own mixer map */
|
|
AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH);
|
|
AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
|
|
AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
|
|
}
|
|
}
|
|
else {
|
|
release_region(gus_base + 0x10c, 4);
|
|
no_cs4231:
|
|
printk(KERN_WARNING "GUS: No CS4231 ??");
|
|
}
|
|
#else
|
|
printk(KERN_ERR "GUS MAX found, but not compiled in\n");
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* ASIC not detected so the card must be 2.2 or 2.4.
|
|
* There could still be the 16-bit/mixer daughter card.
|
|
*/
|
|
}
|
|
|
|
if (hw_config->name)
|
|
snprintf(tmp, sizeof(tmp), "%s (%dk)", hw_config->name,
|
|
(int) gus_mem_size / 1024);
|
|
else if (gus_pnp_flag)
|
|
snprintf(tmp, sizeof(tmp), "Gravis UltraSound PnP (%dk)",
|
|
(int) gus_mem_size / 1024);
|
|
else
|
|
snprintf(tmp, sizeof(tmp), "Gravis UltraSound %s (%dk)", model_num,
|
|
(int) gus_mem_size / 1024);
|
|
|
|
|
|
samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples));
|
|
if (samples == NULL)
|
|
{
|
|
printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n");
|
|
return;
|
|
}
|
|
conf_printf(tmp, hw_config);
|
|
strlcpy(gus_info.name, tmp, sizeof(gus_info.name));
|
|
|
|
if ((sdev = sound_alloc_synthdev()) == -1)
|
|
printk(KERN_WARNING "gus_init: Too many synthesizers\n");
|
|
else
|
|
{
|
|
voice_alloc = &guswave_operations.alloc;
|
|
if (iw_mode)
|
|
guswave_operations.id = "IWAVE";
|
|
hw_config->slots[0] = sdev;
|
|
synth_devs[sdev] = &guswave_operations;
|
|
sequencer_init();
|
|
gus_tmr_install(gus_base + 8);
|
|
}
|
|
|
|
reset_sample_memory();
|
|
|
|
gus_initialize();
|
|
|
|
if ((gus_mem_size > 0) && !gus_no_wave_dma)
|
|
{
|
|
hw_config->slots[4] = -1;
|
|
if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
|
|
"Ultrasound",
|
|
&gus_audio_driver,
|
|
sizeof(struct audio_driver),
|
|
NEEDS_RESTART |
|
|
((!iw_mode && dma2 != dma && dma2 != -1) ?
|
|
DMA_DUPLEX : 0),
|
|
AFMT_U8 | AFMT_S16_LE,
|
|
NULL, dma, dma2)) < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hw_config->slots[4] = gus_devnum;
|
|
audio_devs[gus_devnum]->min_fragment = 9; /* 512k */
|
|
audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */
|
|
audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */
|
|
audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
|
|
}
|
|
|
|
/*
|
|
* Mixer dependent initialization.
|
|
*/
|
|
|
|
switch (mixer_type)
|
|
{
|
|
case ICS2101:
|
|
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
|
|
gus_wave_volume = 90;
|
|
request_region(u_MixSelect, 1, "GUS mixer");
|
|
hw_config->slots[5] = ics2101_mixer_init();
|
|
audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */
|
|
return;
|
|
|
|
case CS4231:
|
|
/* Initialized elsewhere (ad1848.c) */
|
|
default:
|
|
hw_config->slots[5] = gus_default_mixer_init();
|
|
audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */
|
|
return;
|
|
}
|
|
}
|
|
|
|
void __exit gus_wave_unload(struct address_info *hw_config)
|
|
{
|
|
#ifdef CONFIG_SOUND_GUSMAX
|
|
if (have_gus_max)
|
|
{
|
|
ad1848_unload(gus_base + 0x10c,
|
|
-gus_irq,
|
|
gus_dma2, /* Playback DMA */
|
|
gus_dma, /* Capture DMA */
|
|
1); /* Share DMA channels with GF1 */
|
|
}
|
|
#endif
|
|
|
|
if (mixer_type == ICS2101)
|
|
{
|
|
release_region(u_MixSelect, 1);
|
|
}
|
|
if (hw_config->slots[0] != -1)
|
|
sound_unload_synthdev(hw_config->slots[0]);
|
|
if (hw_config->slots[1] != -1)
|
|
sound_unload_audiodev(hw_config->slots[1]);
|
|
if (hw_config->slots[2] != -1)
|
|
sound_unload_mididev(hw_config->slots[2]);
|
|
if (hw_config->slots[4] != -1)
|
|
sound_unload_audiodev(hw_config->slots[4]);
|
|
if (hw_config->slots[5] != -1)
|
|
sound_unload_mixerdev(hw_config->slots[5]);
|
|
|
|
vfree(samples);
|
|
samples=NULL;
|
|
}
|
|
/* called in interrupt context */
|
|
static void do_loop_irq(int voice)
|
|
{
|
|
unsigned char tmp;
|
|
int mode, parm;
|
|
|
|
spin_lock(&gus_lock);
|
|
gus_select_voice(voice);
|
|
|
|
tmp = gus_read8(0x00);
|
|
tmp &= ~0x20; /*
|
|
* Disable wave IRQ for this_one voice
|
|
*/
|
|
gus_write8(0x00, tmp);
|
|
|
|
if (tmp & 0x03) /* Voice stopped */
|
|
voice_alloc->map[voice] = 0;
|
|
|
|
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 ((int) (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; /* Signal to the play_next_pcm_block routine */
|
|
case LMODE_PCM:
|
|
{
|
|
pcm_qlen--;
|
|
pcm_head = (pcm_head + 1) % pcm_nblk;
|
|
if (pcm_qlen && pcm_active)
|
|
{
|
|
play_next_pcm_block();
|
|
}
|
|
else
|
|
{
|
|
/* Underrun. Just stop the voice */
|
|
gus_select_voice(0); /* Left channel */
|
|
gus_voice_off();
|
|
gus_rampoff();
|
|
gus_select_voice(1); /* Right channel */
|
|
gus_voice_off();
|
|
gus_rampoff();
|
|
pcm_active = 0;
|
|
}
|
|
|
|
/*
|
|
* If the queue was full before this interrupt, the DMA transfer was
|
|
* suspended. Let it continue now.
|
|
*/
|
|
|
|
if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
|
|
DMAbuf_outputintr(gus_devnum, 0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
spin_unlock(&gus_lock);
|
|
}
|
|
|
|
static void do_volume_irq(int voice)
|
|
{
|
|
unsigned char tmp;
|
|
int mode, parm;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gus_lock,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 */
|
|
if (iw_mode)
|
|
gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
gus_voice_init(voice);
|
|
break;
|
|
|
|
case VMODE_ENVELOPE:
|
|
gus_rampoff();
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
step_envelope(voice);
|
|
break;
|
|
|
|
case VMODE_START_NOTE:
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
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,
|
|
voices[voice].note_pending, 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:
|
|
spin_unlock_irqrestore(&gus_lock,flags);
|
|
}
|
|
}
|
|
/* called in irq context */
|
|
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) && (int) 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) && (int) 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 interrupt pending */
|
|
switch (active_device)
|
|
{
|
|
case GUS_DEV_WAVE:
|
|
wake_up(&dram_sleeper);
|
|
break;
|
|
|
|
case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
|
|
gus_write8(0x41, 0); /* Disable GF1 DMA */
|
|
gus_transfer_output_block(pcm_current_dev, pcm_current_buf,
|
|
pcm_current_count,
|
|
pcm_current_intrflag, 1);
|
|
break;
|
|
|
|
case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
|
|
gus_write8(0x41, 0); /* Disable GF1 DMA */
|
|
if (pcm_qlen < pcm_nblk)
|
|
{
|
|
dma_active = 0;
|
|
if (gus_busy)
|
|
{
|
|
if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
|
|
DMAbuf_outputintr(gus_devnum, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
status = gus_look8(0x49); /*
|
|
* Get Sampling IRQ Status
|
|
*/
|
|
if (status & 0x40) /*
|
|
* Sampling Irq pending
|
|
*/
|
|
{
|
|
DMAbuf_inputintr(gus_devnum);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Timer stuff
|
|
*/
|
|
|
|
static volatile int select_addr, data_addr;
|
|
static volatile int curr_timer;
|
|
|
|
void gus_timer_command(unsigned int addr, unsigned int val)
|
|
{
|
|
int i;
|
|
|
|
outb(((unsigned char) (addr & 0xff)), select_addr);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
inb(select_addr);
|
|
|
|
outb(((unsigned char) (val & 0xff)), data_addr);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
inb(select_addr);
|
|
}
|
|
|
|
static void arm_timer(int timer, unsigned int interval)
|
|
{
|
|
curr_timer = timer;
|
|
|
|
if (timer == 1)
|
|
{
|
|
gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */
|
|
gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */
|
|
gus_timer_command(0x04, 0x01); /* Start timer 1 */
|
|
}
|
|
else
|
|
{
|
|
gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */
|
|
gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */
|
|
gus_timer_command(0x04, 0x02); /* Start timer 2 */
|
|
}
|
|
|
|
gus_timer_enabled = 1;
|
|
}
|
|
|
|
static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick)
|
|
{
|
|
int timer_no, resolution;
|
|
int divisor;
|
|
|
|
if (usecs_per_tick > (256 * 80))
|
|
{
|
|
timer_no = 2;
|
|
resolution = 320; /* usec */
|
|
}
|
|
else
|
|
{
|
|
timer_no = 1;
|
|
resolution = 80; /* usec */
|
|
}
|
|
divisor = (usecs_per_tick + (resolution / 2)) / resolution;
|
|
arm_timer(timer_no, divisor);
|
|
|
|
return divisor * resolution;
|
|
}
|
|
|
|
static void gus_tmr_disable(int dev)
|
|
{
|
|
gus_write8(0x45, 0); /* Disable both timers */
|
|
gus_timer_enabled = 0;
|
|
}
|
|
|
|
static void gus_tmr_restart(int dev)
|
|
{
|
|
if (curr_timer == 1)
|
|
gus_write8(0x45, 0x04); /* Start timer 1 again */
|
|
else
|
|
gus_write8(0x45, 0x08); /* Start timer 2 again */
|
|
gus_timer_enabled = 1;
|
|
}
|
|
|
|
static struct sound_lowlev_timer gus_tmr =
|
|
{
|
|
0,
|
|
1,
|
|
gus_tmr_start,
|
|
gus_tmr_disable,
|
|
gus_tmr_restart
|
|
};
|
|
|
|
static void gus_tmr_install(int io_base)
|
|
{
|
|
struct sound_lowlev_timer *tmr;
|
|
|
|
select_addr = io_base;
|
|
data_addr = io_base + 1;
|
|
|
|
tmr = &gus_tmr;
|
|
|
|
#ifdef THIS_GETS_FIXED
|
|
sound_timer_init(&gus_tmr, "GUS");
|
|
#endif
|
|
}
|
|
|