freebsd-skq/sys/i386/isa/sound/gus_wave.c
Steven Wallace 1e25d964d2 Reorganize how sound devices are configured. Use a snd controller
with individual devices for each type of sound card:
  opl, sb, sbxvi, sbmidi, pas, mpu, gus, gusxvi, gusmax, mss, uart

EXCLUDE_* options are no longer required to be included in the config file.
They are automatically determined by local.h depending on the devices
included.

Move #includes in local.h to os.h so files are included in the proper
order to avoid warnings.

soundcard.c now has additional code to reflect the device driver
routines needed.

Define new EXCLUDE_SB16MIDI for use in sb16_midi.c and dev_table.h.

#ifndef EXCLUDE_SEQUENCER or EXCLUDE_AUDIO have been added to
soundcard.c and sound_switch.c where appropriate.

Probe outputs changed to reflect new device names.

Readme.freebsd not needed.  Update sound.doc with new config instructions.

Reviewed by:	wollman
1995-03-12 23:34:12 +00:00

3260 lines
72 KiB
C

/*
* sound/gus_wave.c
*
* Driver for the Gravis UltraSound wave table synth.
*
* Copyright by Hannu Savolainen 1993, 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: gus_wave.c,v 1.11 1995/01/04 20:07:27 pst Exp $
*/
#include "sound_config.h"
#include <machine/ultrasound.h>
#include "gus_hw.h"
#undef OUTB
#define OUTB(val, port) outb(port, val)
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
#define MAX_SAMPLE 128
#define MAX_PATCH 256
struct voice_info
{
unsigned long orig_freq;
unsigned long current_freq;
unsigned long mode;
int bender;
int bender_range;
int panning;
int midi_volume;
unsigned int initial_volume;
unsigned int current_volume;
int loop_irq_mode, loop_irq_parm;
#define LMODE_FINISH 1
#define LMODE_PCM 2
#define LMODE_PCM_STOP 3
int volume_irq_mode, volume_irq_parm;
#define VMODE_HALT 1
#define VMODE_ENVELOPE 2
#define VMODE_START_NOTE 3
int env_phase;
unsigned char env_rate[6];
unsigned char env_offset[6];
/*
* Volume computation parameters for gus_adagio_vol()
*/
int main_vol, expression_vol, patch_vol;
/* Variables for "Ultraclick" removal */
int dev_pending, note_pending, volume_pending, sample_pending;
char kill_pending;
long offset_pending;
};
static struct voice_alloc_info *voice_alloc;
extern int gus_base;
extern int gus_irq, gus_dma;
static long gus_mem_size = 0;
static long free_mem_ptr = 0;
static int gus_busy = 0;
static int nr_voices = 0;
static int gus_devnum = 0;
static int volume_base, volume_scale, volume_method;
static int gus_recmask = SOUND_MASK_MIC;
static int recording_active = 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 = 0;
static unsigned char mix_image = 0x00;
/*
* Current version of this driver doesn't allow synth and PCM functions
* at the same time. The active_device specifies the active driver
*/
static int active_device = 0;
#define GUS_DEV_WAVE 1 /* Wave table synth */
#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */
#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */
static int gus_sampling_speed;
static int gus_sampling_channels;
static int gus_sampling_bits;
DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
/*
* Variables and buffers for PCM output
*/
#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */
static int pcm_bsize, 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 = 0;
static int pcm_current_dev;
static int pcm_current_block;
static unsigned long pcm_current_buf;
static int pcm_current_count;
static int pcm_current_intrflag;
struct voice_info voices[32];
static int freq_div_table[] =
{
44100, /* 14 */
41160, /* 15 */
38587, /* 16 */
36317, /* 17 */
34300, /* 18 */
32494, /* 19 */
30870, /* 20 */
29400, /* 21 */
28063, /* 22 */
26843, /* 23 */
25725, /* 24 */
24696, /* 25 */
23746, /* 26 */
22866, /* 27 */
22050, /* 28 */
21289, /* 29 */
20580, /* 30 */
19916, /* 31 */
19293 /* 32 */
};
static struct patch_info *samples;
static long sample_ptrs[MAX_SAMPLE + 1];
static int sample_map[32];
static int free_sample;
static int patch_table[MAX_PATCH];
static int patch_map[32];
static struct synth_info gus_info =
{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
static void gus_poke (long addr, unsigned char data);
static void compute_and_set_volume (int voice, int volume, int ramp_time);
extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
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);
#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] = -1;
}
void
gus_delay (void)
{
int i;
for (i = 0; i < 7; i++)
INB (u_DRAMIO);
}
static void
gus_poke (long addr, unsigned char data)
{ /* Writes a byte to the DRAM */
unsigned long flags;
DISABLE_INTR (flags);
OUTB (0x43, u_Command);
OUTB (addr & 0xff, u_DataLo);
OUTB ((addr >> 8) & 0xff, u_DataHi);
OUTB (0x44, u_Command);
OUTB ((addr >> 16) & 0xff, u_DataHi);
OUTB (data, u_DRAMIO);
RESTORE_INTR (flags);
}
static unsigned char
gus_peek (long addr)
{ /* Reads a byte from the DRAM */
unsigned long flags;
unsigned char tmp;
DISABLE_INTR (flags);
OUTB (0x43, u_Command);
OUTB (addr & 0xff, u_DataLo);
OUTB ((addr >> 8) & 0xff, u_DataHi);
OUTB (0x44, u_Command);
OUTB ((addr >> 16) & 0xff, u_DataHi);
tmp = INB (u_DRAMIO);
RESTORE_INTR (flags);
return tmp;
}
void
gus_write8 (int reg, unsigned int data)
{ /* Writes to an indirect register (8 bit) */
unsigned long flags;
DISABLE_INTR (flags);
OUTB (reg, u_Command);
OUTB ((unsigned char) (data & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
unsigned char
gus_read8 (int reg)
{ /* Reads from an indirect register (8 bit). Offset 0x80. */
unsigned long flags;
unsigned char val;
DISABLE_INTR (flags);
OUTB (reg | 0x80, u_Command);
val = INB (u_DataHi);
RESTORE_INTR (flags);
return val;
}
unsigned char
gus_look8 (int reg)
{ /* Reads from an indirect register (8 bit). No additional offset. */
unsigned long flags;
unsigned char val;
DISABLE_INTR (flags);
OUTB (reg, u_Command);
val = INB (u_DataHi);
RESTORE_INTR (flags);
return val;
}
void
gus_write16 (int reg, unsigned int data)
{ /* Writes to an indirect register (16 bit) */
unsigned long flags;
DISABLE_INTR (flags);
OUTB (reg, u_Command);
OUTB ((unsigned char) (data & 0xff), u_DataLo);
OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
unsigned short
gus_read16 (int reg)
{ /* Reads from an indirect register (16 bit). Offset 0x80. */
unsigned long flags;
unsigned char hi, lo;
DISABLE_INTR (flags);
OUTB (reg | 0x80, u_Command);
lo = INB (u_DataLo);
hi = INB (u_DataHi);
RESTORE_INTR (flags);
return ((hi << 8) & 0xff00) | lo;
}
void
gus_write_addr (int reg, unsigned long address, int is16bit)
{ /* Writes an 24 bit memory address */
unsigned long hold_address;
unsigned long flags;
DISABLE_INTR (flags);
if (is16bit)
{
/*
* Special processing required for 16 bit patches
*/
hold_address = address;
address = address >> 1;
address &= 0x0001ffffL;
address |= (hold_address & 0x000c0000L);
}
gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
/* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
gus_delay ();
gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
RESTORE_INTR (flags);
}
static void
gus_select_voice (int voice)
{
if (voice < 0 || voice > 31)
return;
OUTB (voice, u_Voice);
}
static void
gus_select_max_voices (int nvoices)
{
if (nvoices < 14)
nvoices = 14;
if (nvoices > 32)
nvoices = 32;
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;
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,
samples[sample_no].mode & WAVE_16_BITS);
}
static void
gus_voice_init (int voice)
{
unsigned long flags;
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_volume (0);
gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */
gus_write8 (0x00, 0x03); /* Voice off */
gus_write8 (0x0d, 0x03); /* Ramping off */
voice_alloc->map[voice] = 0;
RESTORE_INTR (flags);
}
static void
gus_voice_init2 (int voice)
{
voices[voice].panning = 0;
voices[voice].mode = 0;
voices[voice].orig_freq = 20000;
voices[voice].current_freq = 20000;
voices[voice].bender = 0;
voices[voice].bender_range = 200;
voices[voice].initial_volume = 0;
voices[voice].current_volume = 0;
voices[voice].loop_irq_mode = 0;
voices[voice].loop_irq_parm = 0;
voices[voice].volume_irq_mode = 0;
voices[voice].volume_irq_parm = 0;
voices[voice].env_phase = 0;
voices[voice].main_vol = 127;
voices[voice].patch_vol = 127;
voices[voice].expression_vol = 127;
voices[voice].sample_pending = -1;
}
static void
step_envelope (int voice)
{
unsigned vol, prev_vol, phase;
unsigned char rate;
long int flags;
if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
{
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_rampoff ();
RESTORE_INTR (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];
DISABLE_INTR (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 */
{
RESTORE_INTR (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;
RESTORE_INTR (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, long int flags)
{
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 ();
RESTORE_INTR (flags);
step_envelope (voice);
}
static void
gus_voice_fade (int voice)
{
int instr_no = sample_map[voice], is16bits;
long int flags;
DISABLE_INTR (flags);
gus_select_voice (voice);
if (instr_no < 0 || instr_no > MAX_SAMPLE)
{
gus_write8 (0x00, 0x03); /* Hard stop */
voice_alloc->map[voice] = 0;
RESTORE_INTR (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, 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);
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;
RESTORE_INTR (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);
}
INB (u_Status); /* Touch the status register */
gus_look8 (0x41); /* Clear any pending DMA IRQs */
gus_look8 (0x49); /* Clear any pending sample IRQs */
gus_read8 (0x0f); /* Clear pending IRQs */
}
static void
gus_initialize (void)
{
unsigned long flags;
unsigned char dma_image, irq_image, tmp;
static unsigned char gus_irq_map[16] =
{0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7};
static unsigned char gus_dma_map[8] =
{0, 1, 0, 2, 0, 3, 4, 5};
DISABLE_INTR (flags);
gus_write8 (0x4c, 0); /* Reset GF1 */
gus_delay ();
gus_delay ();
gus_write8 (0x4c, 1); /* Release Reset */
gus_delay ();
gus_delay ();
/*
* Clear all interrupts
*/
gus_write8 (0x41, 0); /* DMA control */
gus_write8 (0x45, 0); /* Timer control */
gus_write8 (0x49, 0); /* Sample control */
gus_select_max_voices (24);
INB (u_Status); /* Touch the status register */
gus_look8 (0x41); /* Clear any pending DMA IRQs */
gus_look8 (0x49); /* Clear any pending sample IRQs */
gus_read8 (0x0f); /* Clear pending IRQs */
gus_reset (); /* Resets all voices */
gus_look8 (0x41); /* Clear any pending DMA IRQs */
gus_look8 (0x49); /* Clear any pending sample IRQs */
gus_read8 (0x0f); /* Clear pending IRQs */
gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */
/*
* Set up for Digital ASIC
*/
OUTB (0x05, gus_base + 0x0f);
mix_image |= 0x02; /* Disable line out */
OUTB (mix_image, u_Mixer);
OUTB (0x00, u_IRQDMAControl);
OUTB (0x00, gus_base + 0x0f);
/*
* Now set up the DMA and IRQ interface
*
* The GUS supports two IRQs and two DMAs.
*
* Just one DMA channel is used. This prevents simultaneous ADC and DAC.
* Adding this support requires significant changes to the dmabuf.c, dsp.c
* and audio.c also.
*/
irq_image = 0;
tmp = gus_irq_map[gus_irq];
if (!tmp)
printk ("Warning! GUS IRQ not selected\n");
irq_image |= tmp;
irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
tmp = gus_dma_map[gus_dma];
if (!tmp)
printk ("Warning! GUS DMA not selected\n");
dma_image |= tmp;
/*
* For some reason the IRQ and DMA addresses must be written twice
*/
/*
* Doing it first time
*/
OUTB (mix_image, u_Mixer); /* Select DMA control */
OUTB (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */
OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
/*
* Doing it second time
*/
OUTB (mix_image, u_Mixer); /* Select DMA control */
OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */
OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
mix_image &= ~0x02; /* Enable line out */
mix_image |= 0x08; /* Enable IRQ */
OUTB (mix_image, u_Mixer); /*
* Turn mixer channels on
* Note! Mic in is left off.
*/
gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
gusintr (0); /* Serve pending interrupts */
RESTORE_INTR (flags);
}
int
gus_wave_detect (int baseaddr)
{
unsigned long i;
unsigned long loc;
gus_base = baseaddr;
gus_write8 (0x4c, 0); /* Reset GF1 */
gus_delay ();
gus_delay ();
gus_write8 (0x4c, 1); /* Release Reset */
gus_delay ();
gus_delay ();
/* See if there is first block there.... */
gus_poke (0L, 0xaa);
if (gus_peek (0L) != 0xaa)
return (0);
/* Now zero it out so that I can check for mirroring .. */
gus_poke (0L, 0x00);
for (i = 1L; i < 1024L; i++)
{
int n, failed;
/* check for mirroring ... */
if (gus_peek (0L) != 0)
break;
loc = i << 10;
for (n = loc - 1, failed = 0; n <= loc; n++)
{
gus_poke (loc, 0xaa);
if (gus_peek (loc) != 0xaa)
failed = 1;
gus_poke (loc, 0x55);
if (gus_peek (loc) != 0x55)
failed = 1;
}
if (failed)
break;
}
gus_mem_size = i << 10;
return 1;
}
static int
guswave_ioctl (int dev,
unsigned int cmd, unsigned int arg)
{
switch (cmd)
{
case SNDCTL_SYNTH_INFO:
gus_info.nr_voices = nr_voices;
IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info));
return 0;
break;
case SNDCTL_SEQ_RESETSAMPLES:
reset_sample_memory ();
return 0;
break;
case SNDCTL_SEQ_PERCMODE:
return 0;
break;
case SNDCTL_SYNTH_MEMAVL:
return gus_mem_size - free_mem_ptr - 32;
default:
return RET_ERROR (EINVAL);
}
}
static int
guswave_set_instr (int dev, int voice, int instr_no)
{
int sample_no;
if (instr_no < 0 || instr_no > MAX_PATCH)
return RET_ERROR (EINVAL);
if (voice < 0 || voice > 31)
return RET_ERROR (EINVAL);
if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
{
voices[voice].sample_pending = instr_no;
return 0;
}
sample_no = patch_table[instr_no];
patch_map[voice] = -1;
if (sample_no < 0)
{
printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
return RET_ERROR (EINVAL);/* Patch not defined */
}
if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
{
printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n",
sample_no, instr_no, voice);
return RET_ERROR (EINVAL);
}
sample_map[voice] = sample_no;
patch_map[voice] = instr_no;
return 0;
}
static int
guswave_kill_note (int dev, int voice, int note, int velocity)
{
unsigned long flags;
DISABLE_INTR (flags);
voice_alloc->map[voice] = 0xffff;
if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
{
voices[voice].kill_pending = 1;
RESTORE_INTR (flags);
}
else
{
RESTORE_INTR (flags);
gus_voice_fade (voice);
}
return 0;
}
static void
guswave_aftertouch (int dev, int voice, int pressure)
{
short lo_limit, hi_limit;
unsigned long flags;
return; /* Procedure currently disabled */
if (voice < 0 || voice > 31)
return;
if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
return; /* Don't mix with envelopes */
if (pressure < 32)
{
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_rampoff ();
compute_and_set_volume (voice, 255, 0); /* Back to original volume */
RESTORE_INTR (flags);
return;
}
hi_limit = voices[voice].current_volume;
lo_limit = hi_limit * 99 / 100;
if (lo_limit < 65)
lo_limit = 65;
DISABLE_INTR (flags);
gus_select_voice (voice);
if (hi_limit > (4095 - 65))
{
hi_limit = 4095 - 65;
gus_voice_volume (hi_limit);
}
gus_ramp_range (lo_limit, hi_limit);
gus_ramp_rate (3, 8);
gus_rampon (0x58); /* Bidirectional, dow, loop */
RESTORE_INTR (flags);
}
static void
guswave_panning (int dev, int voice, int value)
{
if (voice >= 0 || voice < 32)
voices[voice].panning = value;
}
static void
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 current, target, rate;
unsigned long flags;
compute_volume (voice, volume);
voices[voice].current_volume = voices[voice].initial_volume;
DISABLE_INTR (flags);
/*
* CAUTION! Interrupts disabled. Enable them before returning
*/
gus_select_voice (voice);
current = gus_read16 (0x09) >> 4;
target = voices[voice].initial_volume;
if (ramp_time == INSTANT_RAMP)
{
gus_rampoff ();
gus_voice_volume (target);
RESTORE_INTR (flags);
return;
}
if (ramp_time == FAST_RAMP)
rate = 63;
else
rate = 16;
gus_ramp_rate (0, rate);
if ((target - current) / 64 == 0) /* Close enough to target. */
{
gus_rampoff ();
gus_voice_volume (target);
RESTORE_INTR (flags);
return;
}
if (target > current)
{
if (target > (4095 - 65))
target = 4095 - 65;
gus_ramp_range (current, target);
gus_rampon (0x00); /* Ramp up, once, no IRQ */
}
else
{
if (target < 65)
target = 65;
gus_ramp_range (target, current);
gus_rampon (0x40); /* Ramp down, once, no irq */
}
RESTORE_INTR (flags);
}
static void
dynamic_volume_change (int voice)
{
unsigned char status;
unsigned long flags;
DISABLE_INTR (flags);
gus_select_voice (voice);
status = gus_read8 (0x00); /* Get voice status */
RESTORE_INTR (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.
*/
DISABLE_INTR (flags);
gus_select_voice (voice);
status = gus_read8 (0x0d); /* Ramping status */
RESTORE_INTR (flags);
if (status & 0x03) /* Sustain phase? */
{
compute_and_set_volume (voice, voices[voice].midi_volume, 1);
return;
}
if (voices[voice].env_phase < 0)
return;
compute_volume (voice, voices[voice].midi_volume);
}
static void
guswave_controller (int dev, int voice, int ctrl_num, int value)
{
unsigned long flags;
unsigned long freq;
if (voice < 0 || voice > 31)
return;
switch (ctrl_num)
{
case CTRL_PITCH_BENDER:
voices[voice].bender = value;
if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
{
freq = compute_finetune (voices[voice].orig_freq, value,
voices[voice].bender_range);
voices[voice].current_freq = freq;
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_freq (freq);
RESTORE_INTR (flags);
}
break;
case CTRL_PITCH_BENDER_RANGE:
voices[voice].bender_range = value;
break;
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 RET_ERROR (EINVAL);
}
if (note_num == 255)
{
if (voices[voice].mode & WAVE_ENVELOPES)
{
voices[voice].midi_volume = volume;
dynamic_volume_change (voice);
return 0;
}
compute_and_set_volume (voice, volume, 1);
return 0;
}
if ((patch = patch_map[voice]) == -1)
{
return RET_ERROR (EINVAL);
}
if ((samplep = patch_table[patch]) == -1)
{
return RET_ERROR (EINVAL);
}
note_freq = note_to_freq (note_num);
/*
* Find a sample within a patch so that the note_freq is between low_note
* and high_note.
*/
sample = -1;
best_sample = samplep;
best_delta = 1000000;
while (samplep >= 0 && sample == -1)
{
delta_freq = note_freq - samples[samplep].base_note;
if (delta_freq < 0)
delta_freq = -delta_freq;
if (delta_freq < best_delta)
{
best_sample = samplep;
best_delta = delta_freq;
}
if (samples[samplep].low_note <= note_freq &&
note_freq <= samples[samplep].high_note)
sample = samplep;
else
samplep = samples[samplep].key; /*
* Follow link
*/
}
if (sample == -1)
sample = best_sample;
if (sample == -1)
{
printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
return 0; /* Should play default patch ??? */
}
is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
voices[voice].mode = samples[sample].mode;
voices[voice].patch_vol = samples[sample].volume;
if (voices[voice].mode & WAVE_ENVELOPES)
{
int i;
for (i = 0; i < 6; i++)
{
voices[voice].env_rate[i] = samples[sample].env_rate[i];
voices[voice].env_offset[i] = samples[sample].env_offset[i];
}
}
sample_map[voice] = sample;
base_note = samples[sample].base_note / 100; /* Try to avoid overflows */
note_freq /= 100;
freq = samples[sample].base_freq * note_freq / base_note;
voices[voice].orig_freq = freq;
/*
* Since the pitch bender may have been set before playing the note, we
* have to calculate the bending now.
*/
freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
voices[voice].bender_range);
voices[voice].current_freq = freq;
pan = (samples[sample].panning + voices[voice].panning) / 32;
pan += 7;
if (pan < 0)
pan = 0;
if (pan > 15)
pan = 15;
if (samples[sample].mode & WAVE_16_BITS)
{
mode |= 0x04; /* 16 bits */
if ((sample_ptrs[sample] >> 18) !=
((sample_ptrs[sample] + samples[sample].len) >> 18))
printk ("GUS: Sample address error\n");
}
/*************************************************************************
* CAUTION! Interrupts disabled. Don't return before enabling
*************************************************************************/
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_off ();
gus_rampoff ();
RESTORE_INTR (flags);
if (voices[voice].mode & WAVE_ENVELOPES)
{
compute_volume (voice, volume);
init_envelope (voice);
}
else
compute_and_set_volume (voice, volume, 0);
DISABLE_INTR (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, is16bits); /* start=end */
else
gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
is16bits); /* Sample start=begin */
if (samples[sample].mode & WAVE_LOOPING)
{
mode |= 0x08;
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, is16bits);
mode |= 0x40;
}
gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start,
is16bits);/* Loop start location */
gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end,
is16bits);/* Loop end location */
}
else
{
mode |= 0x20; /* Loop IRQ at the end */
voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
voices[voice].loop_irq_parm = 1;
gus_write_addr (0x02, sample_ptrs[sample],
is16bits);/* Loop start location */
gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1,
is16bits);/* Loop end location */
}
gus_voice_freq (freq);
gus_voice_balance (pan);
gus_voice_on (mode);
RESTORE_INTR (flags);
return 0;
}
/*
* New guswave_start_note by Andrew J. Robinson attempts to minimize clicking
* when the note playing on the voice is changed. It uses volume
* ramping.
*/
static int
guswave_start_note (int dev, int voice, int note_num, int volume)
{
long int flags;
int mode;
int ret_val = 0;
DISABLE_INTR (flags);
if (note_num == 255)
{
if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
voices[voice].volume_pending = volume;
else
{
RESTORE_INTR (flags);
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)
{
RESTORE_INTR (flags);
guswave_set_instr (voices[voice].dev_pending, voice,
voices[voice].sample_pending);
voices[voice].sample_pending = -1;
DISABLE_INTR (flags);
}
if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < 2065))
{
ret_val = guswave_start_note2 (dev, voice, note_num, volume);
}
else
{
voices[voice].dev_pending = dev;
voices[voice].note_pending = note_num;
voices[voice].volume_pending = volume;
voices[voice].volume_irq_mode = VMODE_START_NOTE;
gus_rampoff ();
gus_ramp_range (2000, 4065);
gus_ramp_rate (0, 63);/* Fastest possible rate */
gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
RESTORE_INTR (flags);
}
}
return ret_val;
}
static void
guswave_reset (int dev)
{
int i;
for (i = 0; i < 32; i++)
{
gus_voice_init (i);
gus_voice_init2 (i);
}
}
static int
guswave_open (int dev, int mode)
{
int err;
if (gus_busy)
return RET_ERROR (EBUSY);
gus_initialize ();
if ((err = DMAbuf_open_dma (gus_devnum)) < 0)
return err;
RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
gus_busy = 1;
active_device = GUS_DEV_WAVE;
gus_reset ();
return 0;
}
static void
guswave_close (int dev)
{
gus_busy = 0;
active_device = 0;
gus_reset ();
DMAbuf_close_dma (gus_devnum);
}
static int
guswave_load_patch (int dev, int format, snd_rw_buf * addr,
int offs, int count, int pmgr_flag)
{
struct patch_info patch;
int instr;
long sizeof_patch;
unsigned long blk_size, blk_end, left, src_offs, target;
sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
if (format != GUS_PATCH)
{
printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
return RET_ERROR (EINVAL);
}
if (count < sizeof_patch)
{
printk ("GUS Error: Patch header too short\n");
return RET_ERROR (EINVAL);
}
count -= sizeof_patch;
if (free_sample >= MAX_SAMPLE)
{
printk ("GUS: Sample table full\n");
return RET_ERROR (ENOSPC);
}
/*
* Copy the header from user space but ignore the first bytes which have
* been transferred already.
*/
COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
instr = patch.instr_no;
if (instr < 0 || instr > MAX_PATCH)
{
printk ("GUS: Invalid patch number %d\n", instr);
return RET_ERROR (EINVAL);
}
if (count < patch.len)
{
printk ("GUS Warning: Patch record too short (%d<%d)\n",
count, (int) patch.len);
patch.len = count;
}
if (patch.len <= 0 || patch.len > gus_mem_size)
{
printk ("GUS: Invalid sample length %d\n", (int) patch.len);
return RET_ERROR (EINVAL);
}
if (patch.mode & WAVE_LOOPING)
{
if (patch.loop_start < 0 || patch.loop_start >= patch.len)
{
printk ("GUS: Invalid loop start\n");
return RET_ERROR (EINVAL);
}
if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
{
printk ("GUS: Invalid loop end\n");
return RET_ERROR (EINVAL);
}
}
free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
#define GUS_BANK_SIZE (256*1024)
if (patch.mode & WAVE_16_BITS)
{
/*
* 16 bit samples must fit one 256k bank.
*/
if (patch.len >= GUS_BANK_SIZE)
{
printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
return RET_ERROR (ENOSPC);
}
if ((free_mem_ptr / GUS_BANK_SIZE) !=
((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
{
unsigned long tmp_mem = /* Aling to 256K */
((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
if ((tmp_mem + patch.len) > gus_mem_size)
return RET_ERROR (ENOSPC);
free_mem_ptr = tmp_mem; /* This leaves unusable memory */
}
}
if ((free_mem_ptr + patch.len) > gus_mem_size)
return RET_ERROR (ENOSPC);
sample_ptrs[free_sample] = free_mem_ptr;
/*
* Tremolo is not possible with envelopes
*/
if (patch.mode & WAVE_ENVELOPES)
patch.mode &= ~WAVE_TREMOLO;
memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
/*
* Link this_one sample to the list of samples for patch 'instr'.
*/
samples[free_sample].key = patch_table[instr];
patch_table[instr] = free_sample;
/*
* Use DMA to transfer the wave data to the DRAM
*/
left = patch.len;
src_offs = 0;
target = free_mem_ptr;
while (left) /* Not completely transferred yet */
{
blk_size = audio_devs[gus_devnum]->buffsize;
if (blk_size > left)
blk_size = left;
/*
* DMA cannot cross 256k bank boundaries. Check for that.
*/
blk_end = target + blk_size;
if ((target >> 18) != (blk_end >> 18))
{ /* Split the block */
blk_end &= ~(256 * 1024 - 1);
blk_size = blk_end - target;
}
#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA)
/*
* For some reason the DMA is not possible. We have to use PIO.
*/
{
long i;
unsigned char data;
for (i = 0; i < blk_size; i++)
{
GET_BYTE_FROM_USER (data, addr, sizeof_patch + i);
if (patch.mode & WAVE_UNSIGNED)
if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
data ^= 0x80; /* Convert to signed */
gus_poke (target + i, data);
}
}
#else /* GUS_NO_DMA */
{
unsigned long address, hold_address;
unsigned char dma_command;
unsigned long flags;
/*
* OK, move now. First in and then out.
*/
COPY_FROM_USER (audio_devs[gus_devnum]->dmap->raw_buf[0],
addr, sizeof_patch + src_offs,
blk_size);
DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/
gus_write8 (0x41, 0); /* Disable GF1 DMA */
DMAbuf_start_dma (gus_devnum,
audio_devs[gus_devnum]->dmap->raw_buf_phys[0],
blk_size, DMA_MODE_WRITE);
/*
* Set the DRAM address for the wave data
*/
address = target;
if (audio_devs[gus_devnum]->dmachan > 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]->dmachan > 3)
dma_command |= 0x04; /* 16 bit DMA _channel_ */
gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */
/*
* Sleep here until the DRAM DMA done interrupt is served
*/
active_device = GUS_DEV_WAVE;
DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ);
if (TIMED_OUT (dram_sleeper, dram_sleep_flag))
printk ("GUS: DMA Transfer timed out\n");
RESTORE_INTR (flags);
}
#endif /* GUS_NO_DMA */
/*
* Now the next part
*/
left -= blk_size;
src_offs += blk_size;
target += blk_size;
gus_write8 (0x41, 0); /* Stop DMA */
}
free_mem_ptr += patch.len;
if (!pmgr_flag)
pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
free_sample++;
return 0;
}
static void
guswave_hw_control (int dev, unsigned char *event)
{
int voice, cmd;
unsigned short p1, p2;
unsigned long plong, flags;
cmd = event[2];
voice = event[3];
p1 = *(unsigned short *) &event[4];
p2 = *(unsigned short *) &event[6];
plong = *(unsigned long *) &event[4];
if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
(cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
do_volume_irq (voice);
switch (cmd)
{
case _GUS_NUMVOICES:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_select_max_voices (p1);
RESTORE_INTR (flags);
break;
case _GUS_VOICESAMPLE:
guswave_set_instr (dev, voice, p1);
break;
case _GUS_VOICEON:
DISABLE_INTR (flags);
gus_select_voice (voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_voice_on (p1);
RESTORE_INTR (flags);
break;
case _GUS_VOICEOFF:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_off ();
RESTORE_INTR (flags);
break;
case _GUS_VOICEFADE:
gus_voice_fade (voice);
break;
case _GUS_VOICEMODE:
DISABLE_INTR (flags);
gus_select_voice (voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_voice_mode (p1);
RESTORE_INTR (flags);
break;
case _GUS_VOICEBALA:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_balance (p1);
RESTORE_INTR (flags);
break;
case _GUS_VOICEFREQ:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_freq (plong);
RESTORE_INTR (flags);
break;
case _GUS_VOICEVOL:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_volume (p1);
RESTORE_INTR (flags);
break;
case _GUS_VOICEVOL2: /* Just update the 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 */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_ramp_range (p1, p2);
RESTORE_INTR (flags);
break;
case _GUS_RAMPRATE:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NJET-NJET */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_ramp_rate (p1, p2);
RESTORE_INTR (flags);
break;
case _GUS_RAMPMODE:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NO-NO */
DISABLE_INTR (flags);
gus_select_voice (voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_ramp_mode (p1);
RESTORE_INTR (flags);
break;
case _GUS_RAMPON:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* EI-EI */
DISABLE_INTR (flags);
gus_select_voice (voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_rampon (p1);
RESTORE_INTR (flags);
break;
case _GUS_RAMPOFF:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NEJ-NEJ */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_rampoff ();
RESTORE_INTR (flags);
break;
case _GUS_VOLUME_SCALE:
volume_base = p1;
volume_scale = p2;
break;
case _GUS_VOICE_POS:
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_set_voice_pos (voice, plong);
RESTORE_INTR (flags);
break;
default:;
}
}
static int
gus_sampling_set_speed (int speed)
{
if (speed <= 0)
return gus_sampling_speed;
if (speed > 44100)
speed = 44100;
gus_sampling_speed = speed;
return speed;
}
static int
gus_sampling_set_channels (int channels)
{
if (!channels)
return gus_sampling_channels;
if (channels > 2)
channels = 2;
if (channels < 1)
channels = 1;
gus_sampling_channels = channels;
return channels;
}
static int
gus_sampling_set_bits (int bits)
{
if (!bits)
return gus_sampling_bits;
if (bits != 8 && bits != 16)
bits = 8;
gus_sampling_bits = bits;
return bits;
}
static int
gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
{
switch (cmd)
{
case SOUND_PCM_WRITE_RATE:
if (local)
return gus_sampling_set_speed (arg);
return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg)));
break;
case SOUND_PCM_READ_RATE:
if (local)
return gus_sampling_speed;
return IOCTL_OUT (arg, gus_sampling_speed);
break;
case SNDCTL_DSP_STEREO:
if (local)
return gus_sampling_set_channels (arg + 1) - 1;
return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1);
break;
case SOUND_PCM_WRITE_CHANNELS:
if (local)
return gus_sampling_set_channels (arg);
return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg)));
break;
case SOUND_PCM_READ_CHANNELS:
if (local)
return gus_sampling_channels;
return IOCTL_OUT (arg, gus_sampling_channels);
break;
case SNDCTL_DSP_SETFMT:
if (local)
return gus_sampling_set_bits (arg);
return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg)));
break;
case SOUND_PCM_READ_BITS:
if (local)
return gus_sampling_bits;
return IOCTL_OUT (arg, gus_sampling_bits);
case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
return IOCTL_OUT (arg, RET_ERROR (EINVAL));
break;
case SOUND_PCM_READ_FILTER:
return IOCTL_OUT (arg, RET_ERROR (EINVAL));
break;
default:
return RET_ERROR (EINVAL);
}
return RET_ERROR (EINVAL);
}
static void
gus_sampling_reset (int dev)
{
}
static int
gus_sampling_open (int dev, int mode)
{
#ifdef GUS_NO_DMA
printk ("GUS: DMA mode not enabled. Device not supported\n");
return RET_ERROR (ENXIO);
#endif
if (gus_busy)
return RET_ERROR (EBUSY);
gus_initialize ();
gus_busy = 1;
active_device = 0;
gus_reset ();
reset_sample_memory ();
gus_select_max_voices (14);
pcm_active = 0;
dma_active = 0;
pcm_opened = 1;
if (mode & OPEN_READ)
{
recording_active = 1;
set_input_volumes ();
}
return 0;
}
static void
gus_sampling_close (int dev)
{
gus_reset ();
gus_busy = 0;
pcm_opened = 0;
active_device = 0;
if (recording_active)
set_input_volumes ();
recording_active = 0;
}
static void
gus_sampling_update_volume (void)
{
unsigned long flags;
int voice;
DISABLE_INTR (flags);
if (pcm_active && pcm_opened)
for (voice = 0; voice < gus_sampling_channels; voice++)
{
gus_select_voice (voice);
gus_rampoff ();
gus_voice_volume (1530 + (25 * gus_pcm_volume));
gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
}
RESTORE_INTR (flags);
}
static void
play_next_pcm_block (void)
{
unsigned long flags;
int speed = gus_sampling_speed;
int this_one, is16bits, chn;
unsigned long dram_loc;
unsigned char mode[2], ramp_mode[2];
if (!pcm_qlen)
return;
this_one = pcm_head;
for (chn = 0; chn < gus_sampling_channels; chn++)
{
mode[chn] = 0x00;
ramp_mode[chn] = 0x03; /* Ramping and rollover off */
if (chn == 0)
{
mode[chn] |= 0x20; /* Loop IRQ */
voices[chn].loop_irq_mode = LMODE_PCM;
}
if (gus_sampling_bits != 8)
{
is16bits = 1;
mode[chn] |= 0x04; /* 16 bit data */
}
else
is16bits = 0;
dram_loc = this_one * pcm_bsize;
dram_loc += chn * pcm_banksize;
if (this_one == (pcm_nblk - 1)) /* Last 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 */
}
DISABLE_INTR (flags);
gus_select_voice (chn);
gus_voice_freq (speed);
if (gus_sampling_channels == 1)
gus_voice_balance (7); /* mono */
else if (chn == 0)
gus_voice_balance (0); /* left */
else
gus_voice_balance (15); /* right */
if (!pcm_active) /* 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, dram_loc, is16bits); /* Starting position */
gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start */
if (chn != 0)
gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
is16bits); /* Loop end location */
}
if (chn == 0)
gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
is16bits); /* Loop end location */
else
mode[chn] |= 0x08; /* Enable looping */
if (pcm_datasize[this_one] != pcm_bsize)
{
/*
* Incompletely filled block. Possibly the last one.
*/
if (chn == 0)
{
mode[chn] &= ~0x08; /* Disable looping */
mode[chn] |= 0x20;/* Enable IRQ at the end */
voices[0].loop_irq_mode = LMODE_PCM_STOP;
ramp_mode[chn] = 0x03; /* No rollover bit */
}
else
{
gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
is16bits); /* Loop end location */
mode[chn] &= ~0x08; /* Disable looping */
}
}
RESTORE_INTR (flags);
}
for (chn = 0; chn < gus_sampling_channels; chn++)
{
DISABLE_INTR (flags);
gus_select_voice (chn);
gus_write8 (0x0d, ramp_mode[chn]);
gus_voice_on (mode[chn]);
RESTORE_INTR (flags);
}
pcm_active = 1;
}
static void
gus_transfer_output_block (int dev, unsigned long buf,
int total_count, int intrflag, int chn)
{
/*
* This routine transfers one block of audio data to the DRAM. In mono mode
* it's called just once. When in stereo mode, this_one routine is called
* once for both channels.
*
* The left/mono channel data is transferred to the beginning of dram and the
* right data to the area pointed by gus_page_size.
*/
int this_one, count;
unsigned long flags;
unsigned char dma_command;
unsigned long address, hold_address;
DISABLE_INTR (flags);
count = total_count / gus_sampling_channels;
if (chn == 0)
{
if (pcm_qlen >= pcm_nblk)
printk ("GUS Warning: PCM buffers out of sync\n");
this_one = pcm_current_block = pcm_tail;
pcm_qlen++;
pcm_tail = (pcm_tail + 1) % pcm_nblk;
pcm_datasize[this_one] = count;
}
else
this_one = pcm_current_block;
gus_write8 (0x41, 0); /* Disable GF1 DMA */
DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
address = this_one * pcm_bsize;
address += chn * pcm_banksize;
if (audio_devs[dev]->dmachan > 3)
{
hold_address = address;
address = address >> 1;
address &= 0x0001ffffL;
address |= (hold_address & 0x000c0000L);
}
gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
dma_command = 0x21; /* IRQ enable, DMA start */
if (gus_sampling_bits != 8)
dma_command |= 0x40; /* 16 bit _DATA_ */
else
dma_command |= 0x80; /* Invert MSB */
if (audio_devs[dev]->dmachan > 3)
dma_command |= 0x04; /* 16 bit DMA channel */
gus_write8 (0x41, dma_command); /* Kickstart */
if (chn == (gus_sampling_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 > 0 || count < pcm_bsize))
{
play_next_pcm_block ();
}
}
else
{
/*
* Left channel data. The right channel
* is transferred after DMA interrupt
*/
active_device = GUS_DEV_PCM_CONTINUE;
}
RESTORE_INTR (flags);
}
static void
gus_sampling_output_block (int dev, unsigned long buf, int total_count,
int intrflag, int restart_dma)
{
pcm_current_buf = buf;
pcm_current_count = total_count;
pcm_current_intrflag = intrflag;
pcm_current_dev = dev;
gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
}
static void
gus_sampling_start_input (int dev, unsigned long buf, int count,
int intrflag, int restart_dma)
{
unsigned long flags;
unsigned char mode;
DISABLE_INTR (flags);
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
mode = 0xa0; /* DMA IRQ enabled, invert MSB */
if (audio_devs[dev]->dmachan > 3)
mode |= 0x04; /* 16 bit DMA channel */
if (gus_sampling_channels > 1)
mode |= 0x02; /* Stereo */
mode |= 0x01; /* DMA enable */
gus_write8 (0x49, mode);
RESTORE_INTR (flags);
}
static int
gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
{
unsigned int rate;
rate = (9878400 / (gus_sampling_speed + 2)) / 16;
gus_write8 (0x48, rate & 0xff); /* Set sampling rate */
if (gus_sampling_bits != 8)
{
printk ("GUS Error: 16 bit recording not supported\n");
return RET_ERROR (EINVAL);
}
return 0;
}
static int
gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
{
int i;
long mem_ptr, mem_size;
mem_ptr = 0;
mem_size = gus_mem_size / gus_sampling_channels;
if (mem_size > (256 * 1024))
mem_size = 256 * 1024;
pcm_bsize = bsize / gus_sampling_channels;
pcm_head = pcm_tail = pcm_qlen = 0;
pcm_nblk = MAX_PCM_BUFFERS;
if ((pcm_bsize * pcm_nblk) > mem_size)
pcm_nblk = mem_size / pcm_bsize;
for (i = 0; i < pcm_nblk; i++)
pcm_datasize[i] = 0;
pcm_banksize = pcm_nblk * pcm_bsize;
if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
pcm_nblk--;
return 0;
}
static int
gus_local_qlen (int dev)
{
return pcm_qlen;
}
static void
gus_copy_from_user (int dev, char *localbuf, int localoffs,
snd_rw_buf * userbuf, int useroffs, int len)
{
if (gus_sampling_channels == 1)
{
COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
}
else if (gus_sampling_bits == 8)
{
int in_left = useroffs;
int in_right = useroffs + 1;
char *out_left, *out_right;
int i;
len /= 2;
localoffs /= 2;
out_left = &localbuf[localoffs];
out_right = out_left + pcm_bsize;
for (i = 0; i < len; i++)
{
GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
in_left += 2;
GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
in_right += 2;
}
}
else
{
int in_left = useroffs;
int in_right = useroffs + 1;
short *out_left, *out_right;
int i;
len /= 4;
localoffs /= 4;
out_left = (short *) &localbuf[localoffs];
out_right = out_left + (pcm_bsize / 2);
for (i = 0; i < len; i++)
{
GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
in_left += 2;
GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
in_right += 2;
}
}
}
static struct audio_operations gus_sampling_operations =
{
"Gravis UltraSound",
NEEDS_RESTART,
AFMT_U8 | AFMT_S16_LE,
NULL,
gus_sampling_open,
gus_sampling_close,
gus_sampling_output_block,
gus_sampling_start_input,
gus_sampling_ioctl,
gus_sampling_prepare_for_input,
gus_sampling_prepare_for_output,
gus_sampling_reset,
gus_sampling_reset,
gus_local_qlen,
gus_copy_from_user
};
static void
guswave_bender (int dev, int voice, int value)
{
int freq;
unsigned long flags;
voices[voice].bender = value - 8192;
freq = compute_finetune (voices[voice].orig_freq, value,
voices[voice].bender_range);
voices[voice].current_freq = freq;
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_freq (freq);
RESTORE_INTR (flags);
}
static int
guswave_patchmgr (int dev, struct patmgr_info *rec)
{
int i, n;
switch (rec->command)
{
case PM_GET_DEVTYPE:
rec->parm1 = PMTYPE_WAVE;
return 0;
break;
case PM_GET_NRPGM:
rec->parm1 = MAX_PATCH;
return 0;
break;
case PM_GET_PGMMAP:
rec->parm1 = MAX_PATCH;
for (i = 0; i < MAX_PATCH; i++)
{
int ptr = patch_table[i];
rec->data.data8[i] = 0;
while (ptr >= 0 && ptr < free_sample)
{
rec->data.data8[i]++;
ptr = samples[ptr].key; /* Follow link */
}
}
return 0;
break;
case PM_GET_PGM_PATCHES:
{
int ptr = patch_table[rec->parm1];
n = 0;
while (ptr >= 0 && ptr < free_sample)
{
rec->data.data32[n++] = ptr;
ptr = samples[ptr].key; /* Follow link */
}
}
rec->parm1 = n;
return 0;
break;
case PM_GET_PATCH:
{
int ptr = rec->parm1;
struct patch_info *pat;
if (ptr < 0 || ptr >= free_sample)
return RET_ERROR (EINVAL);
memcpy (rec->data.data8, (char *) &samples[ptr],
sizeof (struct patch_info));
pat = (struct patch_info *) rec->data.data8;
pat->key = GUS_PATCH; /* Restore patch type */
rec->parm1 = sample_ptrs[ptr]; /* DRAM location */
rec->parm2 = sizeof (struct patch_info);
}
return 0;
break;
case PM_SET_PATCH:
{
int ptr = rec->parm1;
struct patch_info *pat;
if (ptr < 0 || ptr >= free_sample)
return RET_ERROR (EINVAL);
pat = (struct patch_info *) rec->data.data8;
if (pat->len > samples[ptr].len) /* Cannot expand sample */
return RET_ERROR (EINVAL);
pat->key = samples[ptr].key; /* Ensure the link is correct */
memcpy ((char *) &samples[ptr], rec->data.data8,
sizeof (struct patch_info));
pat->key = GUS_PATCH;
}
return 0;
break;
case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */
{
int sample = rec->parm1;
int n;
long offs = rec->parm2;
int l = rec->parm3;
if (sample < 0 || sample >= free_sample)
return RET_ERROR (EINVAL);
if (offs < 0 || offs >= samples[sample].len)
return RET_ERROR (EINVAL); /* Invalid offset */
n = samples[sample].len - offs; /* Num of bytes left */
if (l > n)
l = n;
if (l > sizeof (rec->data.data8))
l = sizeof (rec->data.data8);
if (l <= 0)
return RET_ERROR (EINVAL); /*
* Was there a bug?
*/
offs += sample_ptrs[sample]; /*
* Begin offset + offset to DRAM
*/
for (n = 0; n < l; n++)
rec->data.data8[n] = gus_peek (offs++);
rec->parm1 = n; /*
* Nr of bytes copied
*/
}
return 0;
break;
case PM_WRITE_PATCH: /*
* Writes a block of wave data to the DRAM
*/
{
int sample = rec->parm1;
int n;
long offs = rec->parm2;
int l = rec->parm3;
if (sample < 0 || sample >= free_sample)
return RET_ERROR (EINVAL);
if (offs < 0 || offs >= samples[sample].len)
return RET_ERROR (EINVAL); /*
* Invalid offset
*/
n = samples[sample].len - offs; /*
* Nr of bytes left
*/
if (l > n)
l = n;
if (l > sizeof (rec->data.data8))
l = sizeof (rec->data.data8);
if (l <= 0)
return RET_ERROR (EINVAL); /*
* Was there a bug?
*/
offs += sample_ptrs[sample]; /*
* Begin offset + offset to DRAM
*/
for (n = 0; n < l; n++)
gus_poke (offs++, rec->data.data8[n]);
rec->parm1 = n; /*
* Nr of bytes copied
*/
}
return 0;
break;
default:
return RET_ERROR (EINVAL);
}
}
static int
guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
{
int i, p;
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;
}
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;
}
printk ("GUS: Out of free voices\n");
alloc->ptr = p;
return p;
}
static struct synth_operations guswave_operations =
{
&gus_info,
0,
SYNTH_TYPE_SAMPLE,
SAMPLE_TYPE_GUS,
guswave_open,
guswave_close,
guswave_ioctl,
guswave_kill_note,
guswave_start_note,
guswave_set_instr,
guswave_reset,
guswave_hw_control,
guswave_load_patch,
guswave_aftertouch,
guswave_controller,
guswave_panning,
guswave_volume_method,
guswave_patchmgr,
guswave_bender,
guswave_alloc
};
static void
set_input_volumes (void)
{
unsigned long flags;
unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
DISABLE_INTR (flags);
/*
* Enable channels having vol > 10%
* Note! bit 0x01 means line in DISABLED while 0x04 means
* mic in ENABLED.
*/
if (gus_line_vol > 10)
mask &= ~0x01;
if (gus_mic_vol > 10)
mask |= 0x04;
if (recording_active)
{
/*
* Disable channel, if not selected for recording
*/
if (!(gus_recmask & SOUND_MASK_LINE))
mask |= 0x01;
if (!(gus_recmask & SOUND_MASK_MIC))
mask &= ~0x04;
}
mix_image &= ~0x07;
mix_image |= mask & 0x07;
OUTB (mix_image, u_Mixer);
RESTORE_INTR (flags);
}
int
gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
{
#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
SOUND_MASK_SYNTH|SOUND_MASK_PCM)
if (((cmd >> 8) & 0xff) == 'M')
{
if (cmd & IOC_IN)
switch (cmd & 0xff)
{
case SOUND_MIXER_RECSRC:
gus_recmask = IOCTL_IN (arg) & MIX_DEVS;
if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
gus_recmask = SOUND_MASK_MIC;
/* Note! Input volumes are updated during next open for recording */
return IOCTL_OUT (arg, gus_recmask);
break;
case SOUND_MIXER_MIC:
{
int vol = IOCTL_IN (arg) & 0xff;
if (vol < 0)
vol = 0;
if (vol > 100)
vol = 100;
gus_mic_vol = vol;
set_input_volumes ();
return IOCTL_OUT (arg, vol | (vol << 8));
}
break;
case SOUND_MIXER_LINE:
{
int vol = IOCTL_IN (arg) & 0xff;
if (vol < 0)
vol = 0;
if (vol > 100)
vol = 100;
gus_line_vol = vol;
set_input_volumes ();
return IOCTL_OUT (arg, vol | (vol << 8));
}
break;
case SOUND_MIXER_PCM:
gus_pcm_volume = IOCTL_IN (arg) & 0xff;
if (gus_pcm_volume < 0)
gus_pcm_volume = 0;
if (gus_pcm_volume > 100)
gus_pcm_volume = 100;
gus_sampling_update_volume ();
return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
break;
case SOUND_MIXER_SYNTH:
{
int voice;
gus_wave_volume = IOCTL_IN (arg) & 0xff;
if (gus_wave_volume < 0)
gus_wave_volume = 0;
if (gus_wave_volume > 100)
gus_wave_volume = 100;
if (active_device == GUS_DEV_WAVE)
for (voice = 0; voice < nr_voices; voice++)
dynamic_volume_change (voice); /* Apply the new vol */
return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
}
break;
default:
return RET_ERROR (EINVAL);
}
else
switch (cmd & 0xff) /*
* Return parameters
*/
{
case SOUND_MIXER_RECSRC:
return IOCTL_OUT (arg, gus_recmask);
break;
case SOUND_MIXER_DEVMASK:
return IOCTL_OUT (arg, MIX_DEVS);
break;
case SOUND_MIXER_STEREODEVS:
return IOCTL_OUT (arg, 0);
break;
case SOUND_MIXER_RECMASK:
return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
break;
case SOUND_MIXER_CAPS:
return IOCTL_OUT (arg, 0);
break;
case SOUND_MIXER_MIC:
return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8));
break;
case SOUND_MIXER_LINE:
return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8));
break;
case SOUND_MIXER_PCM:
return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
break;
case SOUND_MIXER_SYNTH:
return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
break;
default:
return RET_ERROR (EINVAL);
}
}
else
return RET_ERROR (EINVAL);
}
static struct mixer_operations gus_mixer_operations =
{
gus_default_mixer_ioctl
};
static long
gus_default_mixer_init (long mem_start)
{
if (num_mixers < MAX_MIXER_DEV) /*
* Don't install if there is another
* mixer
*/
mixer_devs[num_mixers++] = &gus_mixer_operations;
return mem_start;
}
long
gus_wave_init (long mem_start, int irq, int dma)
{
unsigned long flags;
unsigned char val;
char *model_num = "2.4";
int gus_type = 0x24; /* 2.4 */
int mixer_type = 0;
/*
* Try to identify the GUS model.
*
* Versions < 3.6 don't have the digital ASIC. Try to probe it first.
*/
DISABLE_INTR (flags);
OUTB (0x20, gus_base + 0x0f);
val = INB (gus_base + 0x0f);
RESTORE_INTR (flags);
if (val != 0xff && (val & 0x06)) /* Should be 0x02?? */
{
/*
* It has the digital ASIC so the card is at least v3.4.
* Next try to detect the true model.
*/
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.
*
* Sorry. No GUS max support yet but it should be available
* soon after the SDK for GUS MAX is available.
*/
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;
}
else
{
model_num = "MAX";
gus_type = 0x40;
mixer_type = CS4231;
#ifndef EXCLUDE_GUSMAX
{
unsigned char max_config = 0x40; /* Codec enable */
if (dma > 3)
max_config |= 0x30; /* 16 bit playback and capture DMAs */
max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
OUTB (max_config, gus_base + 0x106); /* UltraMax control */
}
if (ad1848_detect (gus_base + 0x10c))
{
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
gus_wave_volume = 90;
have_gus_max = 1;
ad1848_init ("gusmax0: <GUS MAX>", gus_base + 0x10c,
-irq,
dma,
dma);
}
else
printk ("[Where's the CS4231?]");
#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.
* It has the same codec/mixer than MAX.
* At this time there is no support for it but it will appear soon.
*/
}
#ifdef __FreeBSD__
printk ("snd4: <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
#else /* __FreeBSD__ */
printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
#endif /* __FreeBSD__ */
#ifndef SCO
sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
#endif
if (irq < 0 || irq > 15)
{
printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
return mem_start;
}
if (dma < 0 || dma > 7)
{
printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
return mem_start;
}
gus_irq = irq;
gus_dma = dma;
if (num_synths >= MAX_SYNTH_DEV)
printk ("GUS Error: Too many synthesizers\n");
else
{
voice_alloc = &guswave_operations.alloc;
synth_devs[num_synths++] = &guswave_operations;
}
PERMANENT_MALLOC (struct patch_info *, samples,
(MAX_SAMPLE + 1) * sizeof (*samples), mem_start);
reset_sample_memory ();
gus_initialize ();
if (num_audiodevs < MAX_AUDIO_DEV)
{
audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations;
audio_devs[gus_devnum]->dmachan = dma;
#ifndef NO_AUTODMA
audio_devs[gus_devnum]->buffcount = 1;
#else
audio_devs[gus_devnum]->flags &= ~DMA_AUTOMODE;
audio_devs[gus_devnum]->buffcount = DSP_BUFFCOUNT;
#endif
audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
}
else
printk ("GUS: Too many PCM devices available\n");
/*
* Mixer dependent initialization.
*/
switch (mixer_type)
{
case ICS2101:
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
gus_wave_volume = 90;
return ics2101_mixer_init (mem_start);
case CS4231:
/* Initialized elsewhere (ad1848.c) */
default:
return gus_default_mixer_init (mem_start);
}
return mem_start;
}
static void
do_loop_irq (int voice)
{
unsigned char tmp;
int mode, parm;
unsigned long flags;
DISABLE_INTR (flags);
gus_select_voice (voice);
tmp = gus_read8 (0x00);
tmp &= ~0x20; /*
* Disable wave IRQ for this_one voice
*/
gus_write8 (0x00, tmp);
mode = voices[voice].loop_irq_mode;
voices[voice].loop_irq_mode = 0;
parm = voices[voice].loop_irq_parm;
switch (mode)
{
case LMODE_FINISH: /*
* Final loop finished, shoot volume down
*/
if ((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:
{
int orig_qlen = pcm_qlen;
int flag; /* 0 or 2 */
pcm_qlen--;
pcm_head = (pcm_head + 1) % pcm_nblk;
if (pcm_qlen)
{
play_next_pcm_block ();
}
else
{ /* Underrun. Just stop the voice */
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 (dma_active)
{
if (pcm_qlen == 0)
flag = 1; /* Underflow */
else
flag = 0;
dma_active = 0;
}
else
flag = 2; /* Just notify the dmabuf.c */
DMAbuf_outputintr (gus_devnum, flag);
}
break;
default:;
}
RESTORE_INTR (flags);
}
static void
do_volume_irq (int voice)
{
unsigned char tmp;
int mode, parm;
unsigned long flags;
DISABLE_INTR (flags);
gus_select_voice (voice);
tmp = gus_read8 (0x0d);
tmp &= ~0x20; /*
* Disable volume ramp IRQ
*/
gus_write8 (0x0d, tmp);
mode = voices[voice].volume_irq_mode;
voices[voice].volume_irq_mode = 0;
parm = voices[voice].volume_irq_parm;
switch (mode)
{
case VMODE_HALT: /*
* Decay phase finished
*/
RESTORE_INTR (flags);
gus_voice_init (voice);
break;
case VMODE_ENVELOPE:
gus_rampoff ();
RESTORE_INTR (flags);
step_envelope (voice);
break;
case VMODE_START_NOTE:
RESTORE_INTR (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:;
}
}
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:
if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
WAKE_UP (dram_sleeper, dram_sleep_flag);
break;
case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
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 */
if (pcm_qlen < pcm_nblk)
{
int flag = (1 - dma_active) * 2; /* 0 or 2 */
if (pcm_qlen == 0)
flag = 1; /* Underrun */
dma_active = 0;
DMAbuf_outputintr (gus_devnum, flag);
}
break;
default:;
}
status = gus_look8 (0x49); /*
* Get Sampling IRQ Status
*/
if (status & 0x40) /*
* Sampling Irq pending
*/
{
DMAbuf_inputintr (gus_devnum);
}
}
#endif