freebsd-nq/sys/i386/isa/sound/gus_wave.c
Bruce Evans b01c51d18f Fixed staticization (the behaviour is undefined when something with
external linkage is redeclared as static).
1998-02-12 23:16:12 +00:00

4876 lines
122 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.
*
*/
#include <i386/isa/sound/sound_config.h>
#include <i386/isa/sound/ultrasound.h>
#include <i386/isa/sound/gus_hw.h>
#include <i386/isa/sound/iwdefs.h>
#include <machine/clock.h>
/* PnP stuff */
#define GUS_PNP_ID 0x100561e
#define MAX_CARDS 8
#define MAX_GUS_PNP 12
/* Static ports */
#define PADDRESS 0x279
#define PWRITE_DATA 0xa79
#define SET_CSN 0x06
#define PSTATUS 0x05
/* PnP Registers. Write to ADDRESS and then use WRITE/READ_DATA */
#define SET_RD_DATA 0x00
#define SERIAL_ISOLATION 0x01
#define WAKE 0x03
#if defined(CONFIG_GUS)
static IWAVE iw;
#define ENTER_CRITICAL
#define LEAVE_CRITICAL
#define MAX_SAMPLE 150
#define MAX_PATCH 256
static u_int gus_pnp_found[MAX_GUS_PNP] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct voice_info {
u_long orig_freq;
u_long current_freq;
u_long mode;
int bender;
int bender_range;
int panning;
int midi_volume;
u_int initial_volume;
u_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;
u_char env_rate[6];
u_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 int gus_dma2 = -1;
static int dual_dma_mode = 0;
static long gus_mem_size = 0;
static long free_mem_ptr = 0;
static int gus_no_dma = 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;
static int only_read_access = 0;
static int only_8_bits = 0;
int gus_wave_volume = 60;
static int gus_pcm_volume = 80;
int have_gus_max = 0;
static int gus_line_vol = 100, gus_mic_vol = 0;
static u_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 = 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;
static int *dram_sleeper = NULL;
static volatile struct snd_wait dram_sleep_flag =
{0};
/*
* 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 u_long pcm_current_buf;
static int pcm_current_count;
static int pcm_current_intrflag;
extern sound_os_info *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 struct patch_info *dbg_samples;
static int dbg_samplep;
static long sample_ptrs[MAX_SAMPLE + 1];
static int sample_map[32];
static int free_sample;
static int mixer_type = 0;
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_default_mixer_init(void);
static int guswave_start_note2(int dev, int voice, int note_num, int volume);
static void gus_poke(long addr, u_char data);
static void compute_and_set_volume(int voice, int volume, int ramp_time);
extern u_short gus_adagio_vol(int vel, int mainv, int xpn, int voicev);
extern u_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);
static void SEND(int d, int r);
static int get_serial(int rd_port, u_char *data);
static void send_Initiation_LFSR(void);
static int isolation_protocol(int rd_port);
#define INSTANT_RAMP -1 /* Instant change. No ramping */
#define FAST_RAMP 0 /* Fastest possible ramp */
/* Crystal Select */
#define CODEC_XTAL2 0x01 /* 16.9344 crystal */
#define CODEC_XTAL1 0x00 /* 24.576 crystal */
/************************************************************************/
/************************************************************************/
/* Definitions for CONFIG_1 register */
#define CODEC_CFIG1I_DEFAULT 0x03 | 0x8
#define CODEC_CAPTURE_PIO 0x80 /* Capture PIO enable */
#define CODEC_PLAYBACK_PIO 0x40 /* Playback PIO enable */
#define CODEC_AUTOCALIB 0x08 /* auto calibrate */
#define CODEC_SINGLE_DMA 0x04 /* Use single DMA channel */
#define CODEC_RE 0x02 /* Capture enable */
#define CODEC_PE 0x01 /* playback enable */
/************************************************************************/
/************************************************************************/
/* Definitions for CONFIG_2 register */
#define CODEC_CFIG2I_DEFAULT 0x81
#define CODEC_OFVS 0x80 /* Output Full Scale Voltage */
#define CODEC_TE 0x40 /* Timer Enable */
#define CODEC_RSCD 0x20 /* Recors Sample Counter Disable */
#define CODEC_PSCD 0x10 /* Playback Sample Counter Disable */
#define CODEC_DAOF 0x01 /* D/A Ouput Force Enable */
/************************************************************************/
/************************************************************************/
/* Definitions for CONFIG_3 register */
/* #define CODEC_CFIG3I_DEFAULT 0xe0 0x02 when synth DACs are working */
#define CODEC_CFIG3I_DEFAULT 0xc0 /* 0x02 when synth DACs are working */
#define CODEC_RPIE 0x80 /* Record FIFO IRQ Enable */
#define CODEC_PPIE 0x40 /* Playback FIFO IRQ Enable */
#define CODEC_FT_MASK 0x30 /* FIFO Threshold Select */
#define CODEC_PVFM 0x04 /* Playback Variable Frequency Mode */
#define CODEC_SYNA 0x02 /* AUX1/Synth Signal Select */
/************************************************************************/
/************************************************************************/
/* Definitions for EXTERNAL_CONTROL register */
#define CODEC_CEXTI_DEFAULT 0x00
#define CODEC_IRQ_ENABLE 0x02 /* interrupt enable */
#define CODEC_GPOUT1 0x80 /* external control #1 */
#define CODEC_GPOUT0 0x40 /* external control #0 */
/************************************************************************/
/************************************************************************/
/* Definitions for MODE_SELECT_ID register */
#define CODEC_MODE_DEFAULT 0x40
#define CODEC_MODE_MASK 0x60
#define CODEC_ID_BIT4 0x80
#define CODEC_ID_BIT3_0 0x0F
/************************************************************************/
#define CONFIG_1 0x09
#define EXTERNAL_CONTROL 0x0a/* Pin control */
#define STATUS_2 0x0b/* Test and initialization */
#define MODE_SELECT_ID 0x0c/* Miscellaneaous information */
#define LOOPBACK 0x0d/* Digital Mix */
#define UPPER_PLAY_COUNT 0x0e/* Playback Upper Base Count */
#define LOWER_PLAY_COUNT 0x0f/* Playback Lower Base Count */
#define CONFIG_2 0x10
#define CONFIG_3 0x11
#define IWL_CODEC_OUT(reg, val) \
{ outb(iwl_codec_base, reg); outb(iwl_codec_data, val); }
#define IWL_CODEC_IN(reg, val) \
{ outb(iwl_codec_base, reg); val = inb(iwl_codec_data); }
static u_char gus_look8(int reg);
static void gus_write16(int reg, u_int data);
static u_short gus_read16(int reg);
static void gus_write_addr(int reg, u_long address, int is16bit);
static void IwaveLineLevel(char level, char index);
static void IwaveInputSource(BYTE index, BYTE source);
static void IwaveDelay(WORD count);
static void IwaveStopDma(BYTE path);
static void IwavePnpGetCfg(void);
static void IwavePnpDevice(BYTE dev);
static void IwavePnpSetCfg(void);
static void IwavePnpKey(void);
static BYTE IwavePnpIsol(PORT * pnpread);
static void IwaveCfgIOSpace(void);
static void IwavePnpSerial(PORT pnprdp, BYTE csn,
BYTE * vendor, DWORD * serial);
static void IwavePnpPeek(PORT pnprdp, WORD bytes, BYTE * data);
static void IwavePnpEeprom(BYTE ctrl);
static void IwavePnpActivate(BYTE dev, BYTE bool);
static void IwavePnpPower(BYTE mode);
static void IwavePnpWake(BYTE csn);
static PORT IwavePnpIOcheck(PORT base, BYTE no_ports);
static BYTE IwavePnpGetCSN(DWORD VendorID, BYTE csn_max);
static BYTE IwavePnpPing(DWORD VendorID);
static WORD IwaveMemSize(void);
static BYTE IwaveMemPeek(ADDRESS addr);
static void IwaveMemPoke(ADDRESS addr, BYTE datum);
static void IwaveMemCfg(DWORD * lpbanks);
static void IwaveCodecIrq(BYTE mode);
static WORD IwaveRegPeek(DWORD reg_mnem);
static void IwaveRegPoke(DWORD reg_mnem, WORD datum);
static void IwaveCodecMode(char mode);
static void IwaveLineMute(BYTE mute, BYTE inx);
static void Iwaveinitcodec(void);
int IwaveOpen(char voices, char mode, struct address_info * hw);
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, u_char data)
{ /* Writes a byte to the DRAM */
u_long flags;
flags = splhigh();
outb(u_Command, 0x43);
outb(u_DataLo, addr & 0xff);
outb(u_DataHi, (addr >> 8) & 0xff);
outb(u_Command, 0x44);
outb(u_DataHi, (addr >> 16) & 0xff);
outb(u_DRAMIO, data);
splx(flags);
}
static u_char
gus_peek(long addr)
{ /* Reads a byte from the DRAM */
u_long flags;
u_char tmp;
flags = splhigh();
outb(u_Command, 0x43);
outb(u_DataLo, addr & 0xff);
outb(u_DataHi, (addr >> 8) & 0xff);
outb(u_Command, 0x44);
outb(u_DataHi, (addr >> 16) & 0xff);
tmp = inb(u_DRAMIO);
splx(flags);
return tmp;
}
void
gus_write8(int reg, u_int data)
{ /* Writes to an indirect register (8 bit) */
u_long flags;
flags = splhigh();
outb(u_Command, reg);
outb(u_DataHi, (u_char) (data & 0xff));
splx(flags);
}
u_char
gus_read8(int reg)
{ /* Reads from an indirect register (8 bit). Offset 0x80. */
u_long flags;
u_char val;
flags = splhigh();
outb(u_Command, reg | 0x80);
val = inb(u_DataHi);
splx(flags);
return val;
}
static u_char
gus_look8(int reg)
{ /* Reads from an indirect register (8 bit). No additional offset. */
u_long flags;
u_char val;
flags = splhigh();
outb(u_Command, reg);
val = inb(u_DataHi);
splx(flags);
return val;
}
static void
gus_write16(int reg, u_int data)
{ /* Writes to an indirect register (16 bit) */
u_long flags;
flags = splhigh();
outb(u_Command, reg);
outb(u_DataLo, (u_char) (data & 0xff));
outb(u_DataHi, (u_char) ((data >> 8) & 0xff));
splx(flags);
}
static u_short
gus_read16(int reg)
{ /* Reads from an indirect register (16 bit). Offset 0x80. */
u_long flags;
u_char hi, lo;
flags = splhigh();
outb(u_Command, reg | 0x80);
lo = inb(u_DataLo);
hi = inb(u_DataHi);
splx(flags);
return ((hi << 8) & 0xff00) | lo;
}
static void
gus_write_addr(int reg, u_long address, int is16bit)
{ /* Writes an 24 bit memory address */
u_long hold_address;
u_long flags;
flags = splhigh();
if (is16bit) {
/*
* Special processing required for 16 bit patches
*/
hold_address = address;
address = address >> 1;
address &= 0x0001ffffL;
address |= (hold_address & 0x000c0000L);
}
gus_write16(reg, (u_short) ((address >> 7) & 0xffff));
gus_write16(reg + 1, (u_short) ((address << 9) & 0xffff));
/*
* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try...
*/
gus_delay();
gus_write16(reg, (u_short) ((address >> 7) & 0xffff));
gus_write16(reg + 1, (u_short) ((address << 9) & 0xffff));
splx(flags);
}
static void
gus_select_voice(int voice)
{
if (voice < 0 || voice > 31)
return;
outb(u_Voice, 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(u_int mode)
{
gus_write8(0x00, (u_char) (mode & 0xfc));
gus_delay();
gus_write8(0x00, (u_char) (mode & 0xfc));
}
static void
gus_voice_off(void)
{
gus_write8(0x00, gus_read8(0x00) | 0x03);
}
static void
gus_voice_mode(u_int m)
{
u_char mode = (u_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(u_long freq)
{
u_long divisor = freq_div_table[nr_voices - 14];
u_short fc;
fc = (u_short) (((freq << 9) + (divisor >> 1)) / divisor);
fc = fc << 1;
gus_write16(0x01, fc);
}
static void
gus_voice_volume(u_int vol)
{
gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */
gus_write16(0x09, (u_short) (vol << 4));
}
static void
gus_voice_balance(u_int balance)
{
gus_write8(0x0c, (u_char) (balance & 0xff));
}
static void
gus_ramp_range(u_int low, u_int high)
{
gus_write8(0x07, (u_char) ((low >> 4) & 0xff));
gus_write8(0x08, (u_char) ((high >> 4) & 0xff));
}
static void
gus_ramp_rate(u_int scale, u_int rate)
{
gus_write8(0x06, (u_char) (((scale & 0x03) << 6) | (rate & 0x3f)));
}
static void
gus_rampon(u_int m)
{
u_char mode = (u_char) (m & 0xff);
gus_write8(0x0d, mode & 0xfc);
gus_delay();
gus_write8(0x0d, mode & 0xfc);
}
static void
gus_ramp_mode(u_int m)
{
u_char mode = (u_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)
{
u_long flags;
flags = splhigh();
gus_select_voice(voice);
gus_voice_volume(0);
gus_voice_off();
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;
voice_alloc->alloc_times[voice] = 0;
splx(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)
{
u_int vol, prev_vol, phase;
u_char rate;
long int flags;
if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) {
flags = splhigh();
gus_select_voice(voice);
gus_rampoff();
splx(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];
flags = splhigh();
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 */
splx(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;
splx(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();
splx(flags);
step_envelope(voice);
}
static void
gus_voice_fade(int voice)
{
int instr_no = sample_map[voice], is16bits;
long int flags;
flags = splhigh();
gus_select_voice(voice);
if (instr_no < 0 || instr_no > MAX_SAMPLE) {
gus_write8(0x00, 0x03); /* Hard stop */
voice_alloc->map[voice] = 0;
splx(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;
splx(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)
{
u_long flags;
u_char dma_image, irq_image, tmp;
static u_char gus_irq_map[16] =
{0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
static u_char gus_dma_map[8] =
{0, 1, 0, 2, 0, 3, 4, 5};
flags = splhigh();
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(gus_base + 0x0f, 0x05);
mix_image |= 0x02; /* Disable line out (for a moment) */
outb(u_Mixer, mix_image);
outb(u_IRQDMAControl, 0x00);
outb(gus_base + 0x0f, 0x00);
/*
* 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)
printf("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)
printf("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)
printf("Warning! GUS DMA not selected\n");
tmp = gus_dma_map[gus_dma2] << 3;
if (!tmp) {
printf("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(u_Mixer, mix_image); /* Select DMA control */
outb(u_IRQDMAControl, dma_image | 0x80); /* Set DMA address */
outb(u_Mixer, mix_image | 0x40); /* Select IRQ control */
outb(u_IRQDMAControl, irq_image); /* Set IRQ address */
/*
* Doing it second time
*/
outb(u_Mixer, mix_image); /* Select DMA control */
outb(u_IRQDMAControl, dma_image); /* Set DMA address */
outb(u_Mixer, mix_image | 0x40); /* Select IRQ control */
outb(u_IRQDMAControl, irq_image); /* 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(u_Mixer, mix_image); /* 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 */
splx(flags);
}
int
gus_wave_detect(int baseaddr)
{
u_long i;
u_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,
u_int cmd, ioctl_arg arg)
{
switch (cmd) {
case SNDCTL_SYNTH_INFO:
gus_info.nr_voices = nr_voices;
bcopy(&gus_info, &(((char *) arg)[0]), 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 -(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 -(EINVAL);
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 < 0) {
printf("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 */
printf("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)
{
u_long flags;
flags = splhigh();
/* voice_alloc->map[voice] = 0xffff; */
if (voices[voice].volume_irq_mode == VMODE_START_NOTE) {
voices[voice].kill_pending = 1;
splx(flags);
} else {
splx(flags);
gus_voice_fade(voice);
}
splx(flags);
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;
u_long flags;
compute_volume(voice, volume);
voices[voice].current_volume = voices[voice].initial_volume;
flags = splhigh();
/*
* 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);
splx(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);
splx(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 */
}
splx(flags);
}
static void
dynamic_volume_change(int voice)
{
u_char status;
u_long flags;
flags = splhigh();
gus_select_voice(voice);
status = gus_read8(0x00); /* Get voice status */
splx(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.
*/
flags = splhigh();
gus_select_voice(voice);
status = gus_read8(0x0d); /* Ramping status */
splx(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)
{
u_long flags;
u_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;
flags = splhigh();
gus_select_voice(voice);
gus_voice_freq(freq);
splx(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;
u_long note_freq, base_note, freq, flags;
u_char mode = 0;
if (voice < 0 || voice > 31) {
printf("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]) == -1)
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 && sample == -1) {
dbg_samples = samples;
dbg_samplep = samplep;
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) {
printf("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))
printf("GUS: Sample address error\n");
}
/*
* CAUTION! Interrupts disabled. Don't return before enabling
*/
flags = splhigh();
gus_select_voice(voice);
gus_voice_off();
gus_rampoff();
splx(flags);
if (voices[voice].mode & WAVE_ENVELOPES) {
compute_volume(voice, volume);
init_envelope(voice);
} else {
compute_and_set_volume(voice, volume, 0);
}
flags = splhigh();
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);
splx(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;
flags = splhigh();
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) {
splx(flags); /* Run temporarily with interrupts
* enabled */
guswave_set_instr(voices[voice].dev_pending, voice,
voices[voice].sample_pending);
voices[voice].sample_pending = -1;
flags = splhigh();
gus_select_voice(voice); /* Reselect the voice
* (just to be sure) */
}
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 */
}
}
splx(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;
int otherside = audio_devs[dev]->otherside;
if (otherside != -1) {
if (audio_devs[otherside]->busy)
return -(EBUSY);
}
if (audio_devs[dev]->busy)
return -(EBUSY);
gus_initialize();
voice_alloc->timestamp = 0;
if ((err = DMAbuf_open_dma(gus_devnum)) < 0) {
printf("GUS: Loading saples without DMA\n");
gus_no_dma = 1; /* Upload samples using PIO */
} else
gus_no_dma = 0;
dram_sleep_flag.aborting = 0;
dram_sleep_flag.mode = WK_NONE;
active_device = GUS_DEV_WAVE;
audio_devs[dev]->busy = 1;
gus_reset();
return 0;
}
static void
guswave_close(int dev)
{
int otherside = audio_devs[dev]->otherside;
if (otherside != -1) {
if (audio_devs[otherside]->busy)
return;
}
audio_devs[dev]->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, snd_rw_buf * addr,
int offs, int count, int pmgr_flag)
{
struct patch_info patch;
int instr;
long sizeof_patch;
u_long blk_size, blk_end, left, src_offs, target;
sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
if (format != GUS_PATCH) {
printf("GUS Error: Invalid patch format (key) 0x%x\n", format);
return -(EINVAL);
}
if (count < sizeof_patch) {
printf("GUS Error: Patch header too short\n");
return -(EINVAL);
}
count -= sizeof_patch;
if (free_sample >= MAX_SAMPLE) {
printf("GUS: Sample table full\n");
return -(ENOSPC);
}
/*
* Copy the header from user space but ignore the first bytes which
* have been transferred already.
*/
if (uiomove(&((char *) &patch)[offs], sizeof_patch - offs, addr)) {
printf("audio: Bad copyin()!\n");
};
instr = patch.instr_no;
if (instr < 0 || instr > MAX_PATCH) {
printf("GUS: Invalid patch number %d\n", instr);
return -(EINVAL);
}
if (count < patch.len) {
printf("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) {
printf("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) {
printf("GUS: Invalid loop start\n");
return -(EINVAL);
}
if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) {
printf("GUS: Invalid loop end\n");
return -(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) {
printf("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)) {
u_long tmp_mem = /* Aligning 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;
bcopy(&patch, (char *) &samples[free_sample], 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; */
blk_size = audio_devs[gus_devnum]->dmap_out->bytes_in_use;
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 (gus_no_dma) {
/*
* For some reason the DMA is not possible. We have
* to use PIO.
*/
long i;
u_char data;
for (i = 0; i < blk_size; i++) {
uiomove((char *) &(data), 1, addr);
if (patch.mode & WAVE_UNSIGNED)
if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
data ^= 0x80; /* Convert to signed */
gus_poke(target + i, data);
}
} else {
u_long address, hold_address;
u_char dma_command;
u_long flags;
/*
* OK, move now. First in and then out.
*/
if (uiomove(audio_devs[gus_devnum]->dmap_out->raw_buf, blk_size, addr)) {
printf("audio: Bad copyin()!\n");
};
flags = splhigh();
/******** INTERRUPTS DISABLED NOW ********/
gus_write8(0x41, 0); /* Disable GF1 DMA */
DMAbuf_start_dma(gus_devnum,
audio_devs[gus_devnum]->dmap_out->raw_buf_phys,
blk_size, 1);
/*
* Set the DRAM address for the wave data
*/
address = target;
if (audio_devs[gus_devnum]->dmachan1 > 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]->dmachan1 > 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;
{
int chn;
dram_sleep_flag.mode = WK_SLEEP;
dram_sleeper = &chn;
DO_SLEEP(chn, dram_sleep_flag, hz);
};
if ((dram_sleep_flag.mode & WK_TIMEOUT))
printf("GUS: DMA Transfer timed out\n");
splx(flags);
}
/*
* 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, u_char *event)
{
int voice, cmd;
u_short p1, p2;
u_long plong, flags;
cmd = event[2];
voice = event[3];
p1 = *(u_short *) &event[4];
p2 = *(u_short *) &event[6];
plong = *(u_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:
flags = splhigh();
gus_select_voice(voice);
gus_select_max_voices(p1);
splx(flags);
break;
case _GUS_VOICESAMPLE:
guswave_set_instr(dev, voice, p1);
break;
case _GUS_VOICEON:
flags = splhigh();
gus_select_voice(voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_voice_on(p1);
splx(flags);
break;
case _GUS_VOICEOFF:
flags = splhigh();
gus_select_voice(voice);
gus_voice_off();
splx(flags);
break;
case _GUS_VOICEFADE:
gus_voice_fade(voice);
break;
case _GUS_VOICEMODE:
flags = splhigh();
gus_select_voice(voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_voice_mode(p1);
splx(flags);
break;
case _GUS_VOICEBALA:
flags = splhigh();
gus_select_voice(voice);
gus_voice_balance(p1);
splx(flags);
break;
case _GUS_VOICEFREQ:
flags = splhigh();
gus_select_voice(voice);
gus_voice_freq(plong);
splx(flags);
break;
case _GUS_VOICEVOL:
flags = splhigh();
gus_select_voice(voice);
gus_voice_volume(p1);
splx(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 */
flags = splhigh();
gus_select_voice(voice);
gus_ramp_range(p1, p2);
splx(flags);
break;
case _GUS_RAMPRATE:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NJET-NJET */
flags = splhigh();
gus_select_voice(voice);
gus_ramp_rate(p1, p2);
splx(flags);
break;
case _GUS_RAMPMODE:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NO-NO */
flags = splhigh();
gus_select_voice(voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_ramp_mode(p1);
splx(flags);
break;
case _GUS_RAMPON:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* EI-EI */
flags = splhigh();
gus_select_voice(voice);
p1 &= ~0x20; /* Don't allow interrupts */
gus_rampon(p1);
splx(flags);
break;
case _GUS_RAMPOFF:
if (voices[voice].mode & WAVE_ENVELOPES)
break; /* NEJ-NEJ */
flags = splhigh();
gus_select_voice(voice);
gus_rampoff();
splx(flags);
break;
case _GUS_VOLUME_SCALE:
volume_base = p1;
volume_scale = p2;
break;
case _GUS_VOICE_POS:
flags = splhigh();
gus_select_voice(voice);
gus_set_voice_pos(voice, plong);
splx(flags);
break;
default:;
}
}
static int
gus_sampling_set_speed(int speed)
{
if (speed <= 0)
speed = gus_sampling_speed;
RANGE(speed, 4000, 44100);
gus_sampling_speed = speed;
if (only_read_access) {
/* Compute nearest valid recording speed and return it */
speed = (9878400 / (gus_sampling_speed + 2)) / 16;
speed = (9878400 / (speed * 16)) - 2;
}
return speed;
}
static int
gus_sampling_set_channels(int channels)
{
if (!channels)
return gus_sampling_channels;
RANGE(channels, 1, 2);
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;
if (only_8_bits)
bits = 8;
gus_sampling_bits = bits;
return bits;
}
static int
gus_sampling_ioctl(int dev, u_int cmd, ioctl_arg arg, int local)
{
switch (cmd) {
case SOUND_PCM_WRITE_RATE:
if (local)
return gus_sampling_set_speed((int) arg);
return *(int *) arg = gus_sampling_set_speed((*(int *) arg));
break;
case SOUND_PCM_READ_RATE:
if (local)
return gus_sampling_speed;
return *(int *) arg = gus_sampling_speed;
break;
case SNDCTL_DSP_STEREO:
if (local)
return gus_sampling_set_channels((int) arg + 1) - 1;
return *(int *) arg = gus_sampling_set_channels((*(int *) arg) + 1) - 1;
break;
case SOUND_PCM_WRITE_CHANNELS:
if (local)
return gus_sampling_set_channels((int) arg);
return *(int *) arg = gus_sampling_set_channels((*(int *) arg));
break;
case SOUND_PCM_READ_CHANNELS:
if (local)
return gus_sampling_channels;
return *(int *) arg = gus_sampling_channels;
break;
case SNDCTL_DSP_SETFMT:
if (local)
return gus_sampling_set_bits((int) arg);
return *(int *) arg = gus_sampling_set_bits((*(int *) arg));
break;
case SOUND_PCM_READ_BITS:
if (local)
return gus_sampling_bits;
return *(int *) arg = gus_sampling_bits;
case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
return *(int *) arg = -(EINVAL);
break;
case SOUND_PCM_READ_FILTER:
return *(int *) arg = -(EINVAL);
break;
}
return -(EINVAL);
}
static void
gus_sampling_reset(int dev)
{
if (recording_active) {
gus_write8(0x49, 0x00); /* Halt recording */
set_input_volumes();
}
}
static int
gus_sampling_open(int dev, int mode)
{
int otherside = audio_devs[dev]->otherside;
if (otherside != -1) {
if (audio_devs[otherside]->busy)
return -(EBUSY);
}
if (audio_devs[dev]->busy)
return -(EBUSY);
gus_initialize();
active_device = 0;
gus_reset();
reset_sample_memory();
gus_select_max_voices(14);
pcm_active = 0;
dma_active = 0;
pcm_opened = 1;
audio_devs[dev]->busy = 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_sampling_close(int dev)
{
int otherside = audio_devs[dev]->otherside;
audio_devs[dev]->busy = 0;
if (otherside != -1) {
if (audio_devs[otherside]->busy)
return;
}
gus_reset();
pcm_opened = 0;
active_device = 0;
if (recording_active) {
gus_write8(0x49, 0x00); /* Halt recording */
set_input_volumes();
}
recording_active = 0;
}
static void
gus_sampling_update_volume(void)
{
u_long flags;
int voice;
if (pcm_active && pcm_opened)
for (voice = 0; voice < gus_sampling_channels; voice++) {
flags = splhigh();
gus_select_voice(voice);
gus_rampoff();
gus_voice_volume(1530 + (25 * gus_pcm_volume));
gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
splx(flags);
}
}
static void
play_next_pcm_block(void)
{
u_long flags;
int speed = gus_sampling_speed;
int this_one, is16bits, chn;
u_long dram_loc;
u_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 */
}
flags = splhigh();
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) - 1,
is16bits); /* Loop end location */
}
if (chn == 0)
gus_write_addr(0x04, dram_loc + pcm_datasize[this_one] - 1,
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 */
}
}
splx(flags);
}
for (chn = 0; chn < gus_sampling_channels; chn++) {
flags = splhigh();
gus_select_voice(chn);
gus_write8(0x0d, ramp_mode[chn]);
gus_voice_on(mode[chn]);
splx(flags);
}
pcm_active = 1;
}
static void
gus_transfer_output_block(int dev, u_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;
u_long flags;
u_char dma_command;
u_long address, hold_address;
flags = splhigh();
count = total_count / gus_sampling_channels;
if (chn == 0) {
if (pcm_qlen >= pcm_nblk)
printf("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, 1);
address = this_one * pcm_bsize;
address += chn * pcm_banksize;
if (audio_devs[dev]->dmachan1 > 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]->dmachan1 > 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;
}
splx(flags);
}
static void
gus_sampling_output_block(int dev, u_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, u_long buf, int count,
int intrflag, int restart_dma)
{
u_long flags;
u_char mode;
flags = splhigh();
DMAbuf_start_dma(dev, buf, count, 0);
mode = 0xa0; /* DMA IRQ enabled, invert MSB */
if (audio_devs[dev]->dmachan2 > 3)
mode |= 0x04; /* 16 bit DMA channel */
if (gus_sampling_channels > 1)
mode |= 0x02; /* Stereo */
mode |= 0x01; /* DMA enable */
gus_write8(0x49, mode);
splx(flags);
}
static int
gus_sampling_prepare_for_input(int dev, int bsize, int bcount)
{
u_int rate;
rate = (9878400 / (gus_sampling_speed + 2)) / 16;
gus_write8(0x48, rate & 0xff); /* Set sampling rate */
if (gus_sampling_bits != 8) {
printf("GUS Error: 16 bit recording not supported\n");
return -(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) {
if (uiomove(&localbuf[localoffs], len, userbuf)) {
printf("audio: Bad copyin()!\n");
};
} 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++) {
uiomove((char *) &(*out_left++), 1, userbuf);
in_left += 2;
uiomove((char *) &(*out_right++), 1, userbuf);
in_right += 2;
}
} else {
int in_left = useroffs;
int in_right = useroffs + 2;
short *out_left, *out_right;
int i;
len /= 4;
localoffs /= 2;
out_left = (short *) &localbuf[localoffs];
out_right = out_left + (pcm_bsize / 2);
for (i = 0; i < len; i++) {
uiomove((char *) &(*out_left++), 2, userbuf);
in_left += 2;
uiomove((char *) &(*out_right++), 2, userbuf);
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_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) / 128;
voices[voice].panning =
(info->controllers[CTL_PAN] * 2) - 128;
voices[voice].bender = info->bender_value;
}
static void
guswave_bender(int dev, int voice, int value)
{
int freq;
u_long flags;
voices[voice].bender = value - 8192;
freq = compute_finetune(voices[voice].orig_freq, value - 8192,
voices[voice].bender_range);
voices[voice].current_freq = freq;
flags = splhigh();
gus_select_voice(voice);
gus_voice_freq(freq);
splx(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 -(EINVAL);
bcopy((char *) &samples[ptr], rec->data.data8, 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 -(EINVAL);
pat = (struct patch_info *) rec->data.data8;
if (pat->len > samples[ptr].len) /* Cannot expand sample */
return -(EINVAL);
pat->key = samples[ptr].key; /* Ensure the link is
* correct */
bcopy(rec->data.data8, (char *) &samples[ptr], 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 -(EINVAL);
if (offs < 0 || offs >= samples[sample].len)
return -(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 -(EINVAL); /* Was there a bug? */
offs += sample_ptrs[sample]; /* Begin offsess +
* offset to DRAM */
for (n = 0; n < l; n++)
rec->data.data8[n] = gus_peek(offs++);
rec->parm1 = n; /* Nr of bytes copied */
}
return 0;
break;
case PM_WRITE_PATCH: /* Writes a block of wave data to the DRAM */
{
int sample = rec->parm1;
int n;
long offs = rec->parm2;
int l = rec->parm3;
if (sample < 0 || sample >= free_sample)
return -(EINVAL);
if (offs < 0 || offs >= samples[sample].len)
return -(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 -(EINVAL); /* Was there a bug? */
offs += sample_ptrs[sample]; /* Begin offsess +
* offset to DRAM */
for (n = 0; n < l; n++)
gus_poke(offs++, rec->data.data8[n]);
rec->parm1 = n; /* Nr of bytes copied */
}
return 0;
break;
default:
return -(EINVAL);
}
}
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 =
{
&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,
guswave_setup_voice
};
static void
set_input_volumes(void)
{
u_long flags;
u_char mask = 0xff & ~0x06; /* Just line out enabled */
if (have_gus_max) /* Don't disturb GUS MAX */
return;
flags = splhigh();
/*
* 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(u_Mixer, mix_image);
splx(flags);
}
int
gus_default_mixer_ioctl(int dev, u_int cmd, ioctl_arg 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 = (*(int *) 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 *(int *) arg = gus_recmask;
break;
case SOUND_MIXER_MIC:
{
int vol = (*(int *) arg) & 0xff;
if (vol < 0)
vol = 0;
if (vol > 100)
vol = 100;
gus_mic_vol = vol;
set_input_volumes();
return *(int *) arg = vol | (vol << 8);
}
break;
case SOUND_MIXER_LINE:
{
int vol = (*(int *) arg) & 0xff;
if (vol < 0)
vol = 0;
if (vol > 100)
vol = 100;
gus_line_vol = vol;
set_input_volumes();
return *(int *) arg = vol | (vol << 8);
}
break;
case SOUND_MIXER_PCM:
gus_pcm_volume = (*(int *) arg) & 0xff;
RANGE (gus_pcm_volume, 0, 100);
gus_sampling_update_volume();
return *(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8);
break;
case SOUND_MIXER_SYNTH:
{
int voice;
gus_wave_volume = (*(int *) arg) & 0xff;
RANGE (gus_wave_volume , 0, 100);
if (active_device == GUS_DEV_WAVE)
for (voice = 0; voice < nr_voices; voice++)
dynamic_volume_change(voice); /* Apply the new vol */
return *(int *) arg = gus_wave_volume | (gus_wave_volume << 8);
}
break;
default:
return -(EINVAL);
}
else
switch (cmd & 0xff) { /* Return parameters */
case SOUND_MIXER_RECSRC:
return *(int *) arg = gus_recmask;
break;
case SOUND_MIXER_DEVMASK:
return *(int *) arg = MIX_DEVS;
break;
case SOUND_MIXER_STEREODEVS:
return *(int *) arg = 0;
break;
case SOUND_MIXER_RECMASK:
return *(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE;
break;
case SOUND_MIXER_CAPS:
return *(int *) arg = 0;
break;
case SOUND_MIXER_MIC:
return *(int *) arg = gus_mic_vol | (gus_mic_vol << 8);
break;
case SOUND_MIXER_LINE:
return *(int *) arg = gus_line_vol | (gus_line_vol << 8);
break;
case SOUND_MIXER_PCM:
return *(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8);
break;
case SOUND_MIXER_SYNTH:
return *(int *) arg = gus_wave_volume | (gus_wave_volume << 8);
break;
default:
return -(EINVAL);
}
} else
return -(EINVAL);
}
static struct mixer_operations gus_mixer_operations = {"Gravis Ultrasound", gus_default_mixer_ioctl};
static void
gus_default_mixer_init()
{
if (num_mixers < MAX_MIXER_DEV) /* Don't install if there is another
* mixer */
mixer_devs[num_mixers++] = &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(u_Mixer, mix_image);
}
}
/* start of pnp code */
static void
SEND(int d, int r)
{
outb(PADDRESS, d);
outb(PWRITE_DATA, r);
}
/*
* Get the device's serial number. Returns 1 if the serial is valid.
*/
static int
get_serial(int rd_port, u_char *data)
{
int i, bit, valid = 0, sum = 0x6a;
bzero(data, sizeof(char) * 9);
for (i = 0; i < 72; i++) {
bit = inb((rd_port << 2) | 0x3) == 0x55;
DELAY(250); /* Delay 250 usec */
/* Can't Short Circuit the next evaluation, so 'and' is last */
bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit;
DELAY(250); /* Delay 250 usec */
valid = valid || bit;
if (i < 64)
sum = (sum >> 1) |
(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
}
valid = valid && (data[8] == sum);
return valid;
}
static void
send_Initiation_LFSR()
{
int cur, i;
/* Reset the LSFR */
outb(PADDRESS, 0);
outb(PADDRESS, 0);
cur = 0x6a;
outb(PADDRESS, cur);
for (i = 1; i < 32; i++) {
cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
outb(PADDRESS, cur);
}
}
static int
isolation_protocol(int rd_port)
{
int csn;
u_char data[9];
send_Initiation_LFSR();
/* Reset CSN for All Cards */
SEND(0x02, 0x04);
for (csn = 1; (csn < MAX_CARDS); csn++) {
/* Wake up cards without a CSN */
SEND(WAKE, 0);
SEND(SET_RD_DATA, rd_port);
outb(PADDRESS, SERIAL_ISOLATION);
DELAY(1000); /* Delay 1 msec */
if (get_serial(rd_port, data)) {
printf("Board Vendor ID: %c%c%c%02x%02x",
((data[0] & 0x7c) >> 2) + 64,
(((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
(data[1] & 0x1f) + 64, data[2], data[3]);
printf(" Board Serial Number: %08x\n", *(int *) &(data[4]));
SEND(SET_CSN, csn); /* Move this out of this
* function XXX */
outb(PADDRESS, PSTATUS);
return rd_port;
} else
break;
}
return 0;
}
static void
IwaveDelay(WORD count)
{
WORD cur_cnt = 0, last_cnt;
BYTE reg, portb;
count = 1193 * count; /* convert number of ms to counter */
last_cnt = count;
portb = inb(0x61) & 0xFC;
outb(0x61, portb); /* disable counter */
outb(0x43, 0xB0); /* load LSB first then MSB */
outb(0x42, (BYTE) count);
outb(0x42, (BYTE) (count >> 8));
outb(0x61, (BYTE) (portb | 0x01)); /* enable counter */
while (cur_cnt <= count) {
outb(0x43, 0x80); /* latch counter */
reg = inb(0x42);/* read latched value */
cur_cnt = (((WORD) inb(0x42)) << 8) | reg;
if (cur_cnt > last_cnt)
break;
last_cnt = cur_cnt;
}
outb(0x61, portb); /* disable counter */
}
/*
* ########################################################################
*
* FUNCTION: IwaveStopDma
*
* PROFILE: This function stops an active DMA transfer to or from the record or
* playback FIFOs. Set the "path" variable to either PLAYBACK or RECORD.
* ########################################################################
*/
static void
IwaveStopDma(BYTE path)
{
BYTE reg;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | _CFIG1I); /* select CFIG1I */
outb(iw.cdatap, (BYTE) (inb(iw.cdatap) & ~path)); /* disable playback path */
LEAVE_CRITICAL;
}
/*
* ########################################################################
*
* FUNCTION : IwaveInputSource
*
* PROFILE: This function allows the calling program to select among any of
* several possible sources to the ADC's. The possible input sources and
* their corresponding symbolic constants are: - Line (LINE_IN) - Aux1
* (AUX1_IN) - Microphone (MIC_IN) - Mixer (MIX_IN)
*
* Set the first argument to either LEFT_SOURCE or RIGHT_SOURCE. Always use the
* symbolic contants for the arguments.
*
* ########################################################################
*/
static void
IwaveInputSource(BYTE index, BYTE source)
{
BYTE reg;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | index); /* select register CLICI or CRICI */
reg = inb(iw.cdatap) & ~MIX_IN;
source &= MIX_IN;
outb(iw.cdatap, (BYTE) (reg | source));
LEAVE_CRITICAL;
}
static void
IwavePnpGetCfg(void)
{
WORD val;
ENTER_CRITICAL;
IwavePnpDevice(AUDIO);
outb(_PIDXR, 0x60); /* select P2X0HI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get P2XR[9:8] */
outb(_PIDXR, 0x61); /* select P2XRLI */
iw.p2xr = val + (WORD) inb(iw.pnprdp); /* get P2XR[7:4] */
outb(_PIDXR, 0x62); /* select P3X0HI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get P3XR[9:8] */
outb(_PIDXR, 0x63); /* select P3X0LI */
iw.p3xr = val + (WORD) inb(iw.pnprdp); /* get P3XR[7:3] */
outb(_PIDXR, 0x64); /* select PHCAI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get PCODAR[9:8] */
outb(_PIDXR, 0x65); /* select PLCAI */
iw.pcodar = val + (WORD) inb(iw.pnprdp); /* get PCODAR[7:2] */
outb(_PIDXR, 0x70); /* select PUI1SI */
iw.synth_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* Synth IRQ number */
outb(_PIDXR, 0x72); /* select PUI2SI */
iw.midi_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* MIDI IRQ number */
outb(_PIDXR, 0x74); /* select PUD1SI */
iw.dma1_chan = inb(iw.pnprdp) & 0x07; /* DMA1 chan (LMC/Codec Rec) */
outb(_PIDXR, 0x75); /* select PUD2SI */
iw.dma2_chan = inb(iw.pnprdp) & 0x07; /* DMA2 chan (codec play) */
IwavePnpDevice(EXT); /* select external device */
outb(_PIDXR, 0x60); /* select PRAHI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get PCDRAR[9:8] */
outb(_PIDXR, 0x61); /* select PRALI */
iw.pcdrar = val + (WORD) inb(iw.pnprdp); /* get PCDRAR[7:4] */
outb(_PIDXR, 0x62); /* select PATAHI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get PATAAR[9:8] */
outb(_PIDXR, 0x63); /* select PATALI */
iw.pataar = val + (WORD) inb(iw.pnprdp); /* get PATAAR[7:1] */
outb(_PIDXR, 0x70); /* select PRISI */
iw.ext_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* Ext Dev IRQ number */
outb(_PIDXR, 0x74); /* select PRDSI */
iw.ext_chan = inb(iw.pnprdp) & 0x07; /* Ext Dev DMA channel */
IwavePnpDevice(MPU401); /* Select MPU401 Device */
outb(_PIDXR, 0x60); /* select P401HI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get P401AR[9:8] */
outb(_PIDXR, 0x61); /* select P401LI */
iw.p401ar = val + (WORD) inb(iw.pnprdp); /* get P401AR[7:1] */
outb(_PIDXR, 0x70); /* select PMISI */
iw.mpu_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* MPU401 Dev IRQ number */
IwavePnpDevice(GAME); /* Select GAME logical Device */
outb(_PIDXR, 0x60); /* select P201HI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get P201AR[9:8] */
outb(_PIDXR, 0x61); /* select P201LI */
iw.p201ar = val + (WORD) inb(iw.pnprdp); /* get P201AR[7:6] */
IwavePnpDevice(EMULATION); /* Select SB and ADLIB Device */
outb(_PIDXR, 0x60); /* select P388HI */
val = ((WORD) inb(iw.pnprdp)) << 8; /* get P388AR[9:8] */
outb(_PIDXR, 0x61); /* select P388LI */
iw.p388ar = val + inb(iw.pnprdp); /* get P388AR[7:6] */
outb(_PIDXR, 0x70); /* select PSBISI */
iw.emul_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* emulation Dev IRQ
* number */
LEAVE_CRITICAL;
}
static void
IwavePnpSetCfg(void)
{
ENTER_CRITICAL;
IwavePnpDevice(AUDIO); /* select audio device */
outb(_PIDXR, 0x60); /* select P2X0HI */
outb(_PNPWRP, (BYTE) (iw.p2xr >> 8)); /* set P2XR[9:8] */
outb(_PIDXR, 0x61); /* select P2X0LI */
outb(_PNPWRP, (BYTE) iw.p2xr); /* set P2XR[7:4] */
/* P2XR[3:0]=0 */
outb(_PIDXR, 0x62); /* select P3X0HI */
outb(_PNPWRP, (BYTE) (iw.p3xr >> 8)); /* set P3XR[9:8] */
outb(_PIDXR, 0x63); /* select P3X0LI */
outb(_PNPWRP, (BYTE) (iw.p3xr)); /* set P3XR[7:3] */
/* P3XR[2:0]=0 */
outb(_PIDXR, 0x64); /* select PHCAI */
outb(_PNPWRP, (BYTE) (iw.pcodar >> 8)); /* set PCODAR[9:8] */
outb(_PIDXR, 0x65); /* select PLCAI */
outb(_PNPWRP, (BYTE) iw.pcodar); /* set PCODAR[7:2] */
outb(_PIDXR, 0x70); /* select PUI1SI */
outb(_PNPWRP, (BYTE) (iw.synth_irq & 0x0F)); /* Synth IRQ number */
outb(_PIDXR, 0x72); /* select PUI2SI */
outb(_PNPWRP, (BYTE) (iw.midi_irq & 0x0F)); /* MIDI IRQ number */
outb(_PIDXR, 0x74); /* select PUD1SI */
outb(_PNPWRP, (BYTE) (iw.dma1_chan & 0x07)); /* DMA channel 1 */
outb(_PIDXR, 0x75); /* select PUD2SI */
outb(_PNPWRP, (BYTE) (iw.dma2_chan & 0x07)); /* DMA channel 2 */
IwavePnpDevice(EXT);
outb(_PIDXR, 0x60); /* select PRAHI */
outb(_PNPWRP, (BYTE) (iw.pcdrar >> 8)); /* set PCDRAR[9:8] */
outb(_PIDXR, 0x61); /* select PRALI */
outb(_PNPWRP, (BYTE) iw.pcdrar); /* set PCDRAR[7:3] */
/* PCDRAR[2:0]=0 */
outb(_PIDXR, 0x62); /* select PATAHI */
outb(_PNPWRP, (BYTE) (iw.pataar >> 8)); /* set PATAAR[9:8] */
outb(_PIDXR, 0x63); /* select PATALI */
outb(_PNPWRP, (BYTE) iw.pataar); /* set PATAAR[7:1] */
/* PATAAR[0]=0 */
outb(_PIDXR, 0x70); /* select PRISI */
outb(_PNPWRP, (BYTE) (iw.ext_irq & 0x0F)); /* Ext Dev IRQ number */
outb(_PIDXR, 0x74); /* select PRDSI */
outb(_PNPWRP, (BYTE) (iw.ext_chan & 0x07)); /* Ext Dev DMA channel */
IwavePnpDevice(GAME);
outb(_PIDXR, 0x60); /* select P201HI */
outb(_PNPWRP, (BYTE) (iw.p201ar >> 8)); /* set P201RAR[9:8] */
outb(_PIDXR, 0x61); /* select P201LI */
outb(_PNPWRP, (BYTE) iw.p201ar); /* set P201AR[7:6] */
IwavePnpDevice(EMULATION);
outb(_PIDXR, 0x60); /* select P388HI */
outb(_PNPWRP, (BYTE) (iw.p388ar >> 8)); /* set P388AR[9:8] */
outb(_PIDXR, 0x61); /* select P388LI */
outb(_PNPWRP, (BYTE) iw.p388ar); /* set P388AR[7:6] */
outb(_PIDXR, 0x70); /* select PSBISI */
outb(_PNPWRP, (BYTE) (iw.emul_irq & 0x0F)); /* emulation IRQ number */
IwavePnpDevice(MPU401);
outb(_PIDXR, 0x60); /* select P401HI */
outb(_PNPWRP, (BYTE) (iw.p401ar >> 8)); /* set P401AR[9:8] */
outb(_PIDXR, 0x61); /* select P401LI */
outb(_PNPWRP, (BYTE) iw.p401ar); /* set P401AR[7:1] */
outb(_PIDXR, 0x70); /* select PMISI */
outb(_PNPWRP, (BYTE) (iw.mpu_irq & 0x0F)); /* MPU emulation IRQ
* number */
LEAVE_CRITICAL;
}
static void
IwaveCfgIOSpace(void)
{
ENTER_CRITICAL;
IwavePnpDevice(AUDIO); /* select audio device */
outb(_PIDXR, 0x60); /* select P2X0HI */
outb(_PNPWRP, (BYTE) (iw.p2xr >> 8)); /* set P2XR[9:8] */
outb(_PIDXR, 0x61); /* select P2X0LI */
outb(_PNPWRP, (BYTE) iw.p2xr); /* set P2XR[7:4] */
/* P2XR[3:0]=0 */
outb(_PIDXR, 0x62); /* select P3X0HI */
outb(_PNPWRP, (BYTE) (iw.p3xr >> 8)); /* set P3XR[9:8] */
outb(_PIDXR, 0x63); /* select P3X0LI */
outb(_PNPWRP, (BYTE) (iw.p3xr)); /* set P3XR[7:3] */
/* P3XR[2:0]=0 */
outb(_PIDXR, 0x64); /* select PHCAI */
outb(_PNPWRP, (BYTE) (iw.pcodar >> 8)); /* set PCODAR[9:8] */
outb(_PIDXR, 0x65); /* select PLCAI */
outb(_PNPWRP, (BYTE) iw.pcodar); /* set PCODAR[7:2] */
IwavePnpDevice(EXT);
outb(_PIDXR, 0x60); /* select PRAHI */
outb(_PNPWRP, (BYTE) (iw.pcdrar >> 8)); /* set PCDRAR[9:8] */
outb(_PIDXR, 0x61); /* select PRALI */
outb(_PNPWRP, (BYTE) iw.pcdrar); /* set PCDRAR[7:3] */
/* PCDRAR[2:0]=0 */
outb(_PIDXR, 0x62); /* select PATAHI */
outb(_PNPWRP, (BYTE) (iw.pataar >> 8)); /* set PATAAR[9:8] */
outb(_PIDXR, 0x63); /* select PATALI */
outb(_PNPWRP, (BYTE) iw.pataar); /* set PATAAR[7:1] */
/* PATAAR[0]=0 */
IwavePnpDevice(GAME);
outb(_PIDXR, 0x60); /* select P201HI */
outb(_PNPWRP, (BYTE) (iw.p201ar >> 8)); /* set P201RAR[9:8] */
outb(_PIDXR, 0x61); /* select P201LI */
outb(_PNPWRP, (BYTE) iw.p201ar); /* set P201AR[7:6] */
IwavePnpDevice(EMULATION);
outb(_PIDXR, 0x60); /* select P388HI */
outb(_PNPWRP, (BYTE) (iw.p388ar >> 8)); /* set P388AR[9:8] */
outb(_PIDXR, 0x61); /* select P388LI */
outb(_PNPWRP, (BYTE) iw.p388ar); /* set P388AR[7:6] */
IwavePnpDevice(MPU401);
outb(_PIDXR, 0x60); /* select P401HI */
outb(_PNPWRP, (BYTE) (iw.p401ar >> 8)); /* set P401AR[9:8] */
outb(_PIDXR, 0x61); /* select P401LI */
outb(_PNPWRP, (BYTE) iw.p401ar); /* set P401AR[7:1] */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* FILE: iwpnp.c */
/* */
/* REMARKS: This file contains the definitions for the InterWave's DDK */
/* functions dedicated to the configuration of the InterWave */
/* PNP logic. */
/* */
/* UPDATE: 4/07/95 */
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpKey */
/* */
/* PROFILE: This function issues the initiation key that places the PNP */
/* logic into configuration mode. The PNP logic is quiescent at */
/* power up and must be enabled by software. This function will */
/* do 32 I/O writes to the PIDXR (0x0279). The function will */
/* first reset the LFSR to its initial value by a sequence of two */
/* write cycles of 0x00 to PIDXR before issuing the key. */
/* */
/* ######################################################################## */
static void
IwavePnpKey(void)
{
/* send_Initiation_LFSR(); */
BYTE code = 0x6A;
BYTE msb;
BYTE i;
/* ############################################### */
/* Reset Linear Feedback Shift Reg. */
/* ############################################### */
outb(0x279, 0x00);
outb(0x279, 0x00);
outb(0x279, code); /* Initial value */
for (i = 1; i < 32; i++) {
msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7;
code = (code >> 1) | msb;
outb(0x279, code);
}
}
static BYTE
IwavePnpIsol(PORT * pnpread)
{
int num_pnp_devs;
int rd_port = 0;
printf("Checking for GUS Plug-n-Play ...\n");
/* Try various READ_DATA ports from 0x203-0x3ff */
for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) {
if (0)
printf("Trying Read_Port at %x\n",
(rd_port << 2) | 0x3);
num_pnp_devs = isolation_protocol(rd_port);
if (num_pnp_devs) {
*pnpread = rd_port << 2 | 0x3;
break;
}
}
if (!num_pnp_devs) {
printf("No Plug-n-Play devices were found\n");
return 0;
}
return 1;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpSerial */
/* */
/* PROFILE: This function reads the first nine bytes of the data from */
/* the serial EEPROM and returns the Vendor ID and the serial */
/* number. First, it resets the EEPROM control logic by */
/* issuing a WAKE[CSN] command. The function will return an */
/* ASCII string for the vendor ID into the char array pointed */
/* to by "vendor" in the VVVNNNN format. The serial number */
/* is placed in the 32-bit variable pointed to by "serial". */
/* Note that the 9th byte is read but not used as it is invalid */
/* when the serial identifier is read via PRESDI. */
/* */
/* This function assumes that the PNP state machine is not in */
/* the "wait for key state". Otherwise, unpredictable results */
/* will be obtained. */
/* */
/* ######################################################################## */
static void
IwavePnpSerial(PORT pnprdp,
BYTE csn,
BYTE * vendor,
DWORD * serial)
{
BYTE presdi, digit, i;
*serial = 0L;
/* ####################################### */
/* Reset Serial EEPROM logic */
/* ####################################### */
IwavePnpWake(csn); /* Wake card up */
for (i = 1; i <= 4; i++) {
IwavePnpPeek(pnprdp, 1, &presdi);
switch (i) {
case 1:
*(vendor++) = ((presdi & 0x7C) >> 2) | 0x40; /* 1st char */
*vendor = (presdi & 0x03) << 3; /* isolate bits[4:3] of
* 2nd char */
break;
case 2:
*vendor = ((presdi & 0xE0) >> 5) | (*vendor);
*(vendor++) = (*vendor) | 0x40; /* 2nd char */
*vendor = (presdi & 0x1F) | 0x40; /* 3rd char */
break;
case 3:
case 4:
digit = (presdi & 0xF0) >> 4;
if (digit <= 0x09)
*(++vendor) = digit + 0x30; /* ASCII of digit */
else
*(++vendor) = (digit & 0x07) + 0x3F;
digit = presdi & 0x0F;
if (digit <= 0x09)
*(++vendor) = digit + 0x30;
else
*(++vendor) = (digit & 0x07) + 0x3F;
break;
}
}
*(++vendor) = '\0';
IwavePnpPeek(pnprdp, 4, (BYTE *) serial);
IwavePnpPeek(pnprdp, 1, NULL); /* discard checksum */
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpPeek */
/* */
/* PROFILE: This function will return the number of specified bytes of */
/* resource data from the serial EEPROM. The function will NOT */
/* reset the serial EEPROM logic to allow reading the entire */
/* EEPROM by issuing repeated calls. The caller must supply a */
/* pointer to where the data are to be stored. */
/* It is assumed that the InterWave is not in either "sleep" */
/* or "wait for key" states. Note that on the first call, if */
/* the caller means to read from the beggining of data the */
/* serial EEPROM logic must be reset. For this, the caller */
/* should issue a WAKE[CSN] command */
/* */
/* ######################################################################## */
static void
IwavePnpPeek(PORT pnprdp, WORD bytes, BYTE * data)
{
WORD i;
BYTE datum;
for (i = 1; i <= bytes; i++) {
outb(_PIDXR, 0x05); /* select PRESSI */
while (TRUE) { /* wait til new data byte is ready */
if (inb(pnprdp) & PNP_DATA_RDY)
break; /* new resource byte ready */
}
outb(_PIDXR, 0x04); /* select PRESDI */
datum = inb(pnprdp); /* read resource byte */
if (data != NULL)
*(data++) = datum; /* store it */
}
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpEeprom */
/* */
/* PROFILE: This function allows the caller to control the serial */
/* EEPROM directly while the audio device is inactive. To */
/* de-activate the audio device issue the call */
/* IwavePnpActivate(AUDIO,OFF). */
/* */
/* ######################################################################## */
static void
IwavePnpEeprom(BYTE ctrl)
{
ENTER_CRITICAL;
outb(_PIDXR, 0xF1); /* select PSECI */
outb(_PNPWRP, ctrl); /* write PSECI */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpActivate */
/* */
/* PROFILE: This function will activate or de-activate the audio device */
/* or the external device on the InterWave. Set the "dev" arg */
/* to AUDIO for the audio device or EXT for the external device. */
/* Set "bool" to ON or OFF to turn the device on or off the ISA */
/* bus. Notice that for a logical device to work, it must be */
/* activated. */
/* */
/* ######################################################################## */
static void
IwavePnpActivate(BYTE dev, BYTE bool)
{
IwavePnpDevice(dev); /* select audio device */
ENTER_CRITICAL;
outb(_PIDXR, ACTIVATE_DEV); /* select Activate Register */
outb(_PNPWRP, bool); /* write register */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpDevice */
/* */
/* PROFILE: This function allows the caller to select between five */
/* logical devices available on the InterWave.It is assumed */
/* that the PNP state machine is in configuration mode. */
/* */
/* ######################################################################## */
static void
IwavePnpDevice(BYTE dev)
{
ENTER_CRITICAL;
outb(_PIDXR, _PLDNI); /* select PLDNI */
outb(_PNPWRP, dev); /* write PLDNI */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpPower */
/* */
/* PROFILE: This function allows the caller to disable major sections of */
/* the InterWave to prevent them from consuming power and */
/* loading the ISA bus. */
/* */
/* It is assumed that the PNP state machine is in configuration */
/* mode. */
/* */
/* ######################################################################## */
static void
IwavePnpPower(BYTE mode)
{
ENTER_CRITICAL;
outb(_PIDXR, _PPWRI); /* select PPWRI */
outb(_PNPWRP, mode); /* write PPWRI */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpWake */
/* */
/* PROFILE: This function issues a WAKE[CSN] command to the InterWave. If */
/* the CSN matches the PNP state machine will enter the */
/* configuration state. Otherwise it will enter the sleep mode. */
/* */
/* It is assumed that the PNP state machine is not in the */
/* "wait for key" state. */
/* */
/* ######################################################################## */
static void
IwavePnpWake(BYTE csn)
{
ENTER_CRITICAL;
outb(_PIDXR, _PWAKEI); /* select PWAKEI */
outb(_PNPWRP, csn); /* write csn */
LEAVE_CRITICAL;
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpIOcheck */
/* */
/* PROFILE: This function allows the caller to perform a conflict check */
/* on an I/O port to be used by a logical device. The function */
/* receives the base address of the I/O range as well as the */
/* number of ports in the range and then performs the I/O check */
/* protocol. It returns the address of the port if a conflict */
/* is detected or IO_CHK if no conflict is detected. */
/* */
/* This function assumes that the logical device has been de- */
/* activated and that the PNP state machine is in config mode. */
/* */
/* ######################################################################## */
static PORT
IwavePnpIOcheck(PORT base, BYTE no_ports)
{
BYTE i;
PORT portid;
outb(_PIDXR, RANGE_IOCHK); /* select IO range check reg */
for (i = 0; i < no_ports; i++) {
portid = base + i; /* port to check */
outb(_PNPWRP, 0x02); /* must drive 0xAA onto bus */
if (inb(portid) != 0xAA)
return (portid); /* IO conflict detected */
outb(_PNPWRP, 0x03); /* must drive 0x55 onto bus */
if (inb(portid) != 0x55)
return (portid); /* IO conflict detected */
}
return (IO_OK);
}
/* ######################################################################## */
/* */
/* FUNCTION: IwavePnpGetCSN */
/* */
/* PROFILE: This function allows the caller to detect an InterWave based */
/* adapter board and will return its asigned CSN so that an */
/* an application can access its PnP interface and determine the */
/* borad's current configuration. In conducting its search for */
/* the InterWave IC, the function will use the first 32 bits of */
/* the Serial Identifier called the vendor ID in the PnP ISA */
/* spec. The last 4 bits in the Vendor ID represent a revision */
/* number for the particular product and this function gives the */
/* caller the option of taking this revision number into account */
/* or not in the search. If the function fails to find the */
/* InterWave IC it will return FALSE. */
/* */
/* ######################################################################## */
static BYTE
IwavePnpGetCSN(DWORD VendorID, BYTE csn_max)
{
BYTE csn;
DWORD vendor;
IwavePnpKey(); /* Key to access PnP Interface */
VendorID &= (0xFFFFFFF0); /* reset 4 least significant bits */
for (csn = 1; csn <= csn_max; csn++) {
IwavePnpWake(csn); /* Select card */
IwavePnpPeek(iw.pnprdp, 4, (BYTE *) & vendor); /* get vendor ID */
vendor &= (0xFFFFFFF0);
if (vendor == VendorID) { /* If IDs match, InterWave is
* found */
outb(_PIDXR, 0x02); /* Place all cards in
* wait-for-key state */
outb(0x0A79, 0x02);
return (csn);
}
}
outb(_PIDXR, 0x02); /* Place all cards in wait-for-key state */
outb(0x0A79, 0x02);
return (FALSE); /* InterWave IC not found */
}
/* ######################################################################## */
/* */
/*
* FUNCTION: IwavePnpPing
*/
/* */
/* PROFILE: This function allows the caller to detect an InterWave based */
/* adapter board and will return its asigned CSN so that an */
/* an application can access its PnP interface and determine the */
/* borad's current configuration. In conducting its search for */
/* the InterWave IC, the function will use the first 32 bits of */
/* the Serial Identifier called the vendor ID in the PnP ISA */
/* spec. The last 4 bits in the Vendor ID represent a revision */
/* number for the particular product and will not be included */
/* in the search. The function will return the Vendor ID and the */
/* calling application should check the revision bits to make */
/* sure they are compatible with the board. */
/* */
/* ######################################################################## */
static BYTE
IwavePnpPing(DWORD VendorID)
{
BYTE csn;
VendorID &= (0xFFFFFFF0); /* reset 4 least significant bits */
IwavePnpKey(); /* Key to access PnP Interface */
while (iw.pnprdp <= 0x23F) {
for (csn = 1; csn <= 10; csn++) {
IwavePnpWake(csn); /* Select card */
IwavePnpPeek(iw.pnprdp, 4, (BYTE *) & iw.vendor); /* get vendor ID */
if (((iw.vendor) & 0xFFFFFFF0) == VendorID) { /* If IDs match,
* InterWave is found */
outb(_PIDXR, 0x02); /* Place all cards in
* wait-for-key state */
outb(0x0A79, 0x02);
return (csn);
}
}
iw.pnprdp += 0x04;
}
outb(_PIDXR, 0x02); /* Place all cards in wait-for-key state */
outb(0x0A79, 0x02);
return (FALSE); /* InterWave IC not found */
}
/* end of pnp code */
static WORD
IwaveMemSize(void)
{
BYTE datum = 0x55;
ADDRESS local = 0L;
outb(iw.igidxr, _LMCI);
outb(iw.i8dp, inb(iw.i8dp) & 0xFD); /* DRAM I/O cycles selected */
while (TRUE) {
IwaveMemPoke(local, datum);
IwaveMemPoke(local + 1L, datum + 1);
if (IwaveMemPeek(local) != datum || IwaveMemPeek(local + 1L) != (datum + 1) || IwaveMemPeek(0L) != 0x55)
break;
local += RAM_STEP;
datum++;
}
return ((WORD) (local >> 10));
}
static BYTE
IwaveMemPeek(ADDRESS addr)
{
PORT p3xr;
p3xr = iw.p3xr;
outb(iw.igidxr, 0x43); /* Select LMALI */
outw(iw.i16dp, (WORD) addr); /* Lower 16 bits of LM */
outb(iw.igidxr, 0x44); /* Select LMAHI */
outb(iw.i8dp, (BYTE) (addr >> 16)); /* Upper 8 bits of LM */
return (inb(iw.lmbdr)); /* return byte from LMBDR */
}
static void
IwaveMemPoke(ADDRESS addr, BYTE datum)
{
PORT p3xr;
p3xr = iw.p3xr;
outb(iw.igidxr, 0x43); /* Select LMALI */
outw(iw.i16dp, (WORD) addr); /* Lower 16 bits of LM */
outb(iw.igidxr, 0x44); /* Select LMAHI */
outb(iw.i8dp, (BYTE) (addr >> 16)); /* Upper 8 bits of LM */
outb(iw.lmbdr, datum); /* Write byte to LMBDR */
}
/* ######################################################################## */
/* */
/* FUNCTION: IwaveMemCfg */
/* */
/* PROFILE : This function determines the amount of DRAM from its */
/* configuration accross all banks. It sets the configuration */
/* into register LMCFI and stores the total amount of DRAM */
/* into iw.size_mem (Kbytes). */
/* */
/* The function first places the IC in enhanced mode to allow */
/* full access to all DRAM locations. Then it selects full */
/* addressing span (LMCFI[3:0]=0x0C). Finally, it determines */
/* the amount of DRAM in each bank and from this the actual */
/* configuration. */
/* */
/* Note that if a configuration other than one indicated in */
/* the manual is implemented, this function will select */
/* full addressing span (LMCFI[3:0]=0xC). */
/* */
/* ######################################################################## */
static void
IwaveMemCfg(DWORD * lpbanks)
{
DWORD bank[4] = {0L, 0L, 0L, 0L};
DWORD addr = 0L, base = 0L, cnt = 0L;
BYTE i, reg, ram = FALSE;
WORD lmcfi;
/* */
ENTER_CRITICAL;
outb(iw.igidxr, 0x99);
reg = inb(iw.i8dp); /* image of sgmi */
outb(iw.igidxr, 0x19);
outb(iw.i8dp, (BYTE) (reg | 0x01)); /* enable enhaced mode */
outb(iw.igidxr, _LMCFI);/* select LM Conf Reg */
lmcfi = inw(iw.i16dp) & 0xFFF0;
outw(iw.i16dp, lmcfi | 0x000C); /* max addr span */
/* */
/* Clear every RAM_STEPth location */
/* */
while (addr < RAM_MAX) {
IwaveMemPoke(addr, 0x00);
addr += RAM_STEP;
}
/* */
/* Determine amount of RAM in each bank */
/* */
for (i = 0; i < 4; i++) {
IwaveMemPoke(base, 0xAA); /* mark start of bank */
IwaveMemPoke(base + 1L, 0x55);
if ((IwaveMemPeek(base) == 0xAA) && (IwaveMemPeek(base + 1L) == 0x55))
ram = TRUE;
if (ram) {
while (cnt < BANK_MAX) {
bank[i] += RAM_STEP;
cnt += RAM_STEP;
addr = base + cnt;
if (IwaveMemPeek(addr) == 0xAA)
break;
}
}
if (lpbanks != NULL) {
*lpbanks = bank[i];
lpbanks++;
}
bank[i] = bank[i] >> 10;
base += BANK_MAX;
cnt = 0L;
ram = FALSE;
}
/* */
iw.flags &= ~DRAM_HOLES;
outb(iw.igidxr, _LMCFI);
if (bank[0] == 256 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi);
else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x01);
else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 256 && bank[3] == 256)
outw(iw.i16dp, lmcfi | 0x02);
else if (bank[0] == 256 && bank[1] == 1024 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x03);
else if (bank[0] == 256 && bank[1] == 1024 && bank[2] == 1024 && bank[3] == 1024)
outw(iw.i16dp, lmcfi | 0x04);
else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 1024 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x05);
else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 1024 && bank[3] == 1024)
outw(iw.i16dp, lmcfi | 0x06);
else if (bank[0] == 1024 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x07);
else if (bank[0] == 1024 && bank[1] == 1024 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x08);
else if (bank[0] == 1024 && bank[1] == 1024 && bank[2] == 1024 && bank[3] == 1024)
outw(iw.i16dp, lmcfi | 0x09);
else if (bank[0] == 4096 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x0A);
else if (bank[0] == 4096 && bank[1] == 4096 && bank[2] == 0 && bank[3] == 0)
outw(iw.i16dp, lmcfi | 0x0B);
else /* Flag the non-contiguous config of memory */
iw.flags |= DRAM_HOLES;
/* */
outb(iw.igidxr, 0x19); /* restore sgmi */
outb(iw.i8dp, reg);
LEAVE_CRITICAL;
}
/* ######################################################################## */
/**/
/* FUNCTION: IwaveCodecIrq */
/**/
/* PROFILE: This function disables or enables the Codec Interrupts. To */
/* enable interrupts set CEXTI[2] high thus causing all interrupt */
/* sources (CSR3I[6:4]) to pass onto the IRQ pin. To disable */
/* interrupts set CEXTI[1]=0. To enable Code IRQs issue this call: */
/**/
/* IwaveCodecIrq(CODEC_IRQ_ENABLE). To disable IRQs issue the call */
/**/
/* IwaveCodeIrq(~CODEC_IRQ_ENABLE). */
/**/
/* ######################################################################## */
static void
IwaveCodecIrq(BYTE mode)
{
BYTE reg;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | _CSR3I); /* select CSR3I */
outb(iw.cdatap, 0x00); /* clear all interrupts */
outb(iw.pcodar + 0x02, 0x00); /* clear CSR1R */
outb(iw.pcodar, reg | _CEXTI); /* select CEXTI */
reg = inb(iw.cdatap);
if (mode == CODEC_IRQ_ENABLE) /* enable Codec Irqs */
outb(iw.cdatap, (BYTE) (reg | CODEC_IRQ_ENABLE));
else /* disable Codec Irqs */
outb(iw.cdatap, (BYTE) (reg & ~CODEC_IRQ_ENABLE));
LEAVE_CRITICAL;
}
/* ######################################################################### */
/**/
/* FUNCTION: IwaveRegPeek */
/**/
/* PROFILE : This function returns the value stored in any readable */
/* InterWave register. It takes as input a pointer to a */
/* structure containing the addresses of the relocatable I/O */
/* space as well as a register mnemonic. To correctly use this */
/* function, the programmer must use the mnemonics defined in */
/* "iwdefs.h". These mnemonics contain coded information used */
/* by the function to properly access the desired register. */
/**/
/* An attempt to read from a write-only register will return */
/* meaningless data. */
/**/
/* ######################################################################### */
static WORD
IwaveRegPeek(DWORD reg_mnem)
{
BYTE index, val;
WORD reg_id, offset;
offset = (WORD) ((BYTE) reg_mnem);
reg_id = (WORD) (reg_mnem >> 16);
index = (BYTE) (reg_mnem >> 8);
/* ################################################### */
/* Logic to read registers in P2XR block & GMCR */
/* ################################################### */
if (reg_id >= 0x0001 && reg_id <= 0x001A) { /* UMCR to GMCR */
if (reg_id <= 0x000E) /* UMCR to USRR */
return ((WORD) inb(iw.p2xr + offset));
if (reg_id == 0x0019)
return ((WORD) inb(iw.p201ar));
else { /* GUS Hidden registers or GMCR */
BYTE iveri;
outb(iw.igidxr, 0x5B); /* select IVERI */
iveri = inb(iw.i8dp); /* read IVERI */
outb(iw.i8dp, (BYTE) (iveri | 0x09)); /* set IVERI[3,0] */
if (reg_id == 0x001A) { /* GMCR */
val = inb(iw.p3xr);
outb(iw.i8dp, iveri); /* restore IVERI */
return ((WORD) val);
}
val = inb(iw.p2xr + 0x0F); /* read URCR */
val = (val & 0xF8) | index; /* value for URCR[2:0] */
outb(iw.p2xr + 0x0F, val); /* set URCR[2:0] */
if (reg_mnem == UDCI || reg_mnem == UICI) {
val = inb(iw.p2xr);
if (reg_mnem == UDCI)
outb(iw.p2xr, (BYTE) (val & 0xBF));
else
outb(iw.p2xr, (BYTE) (val | 0x40));
}
val = inb(iw.p2xr + 0x0B);
outb(iw.igidxr, 0x5B); /* select IVERI */
outb(iw.i8dp, iveri); /* restore IVERI */
return ((WORD) val); /* read register */
}
}
/* ################################################### */
/* Logic to read registers in P3XR block */
/* ################################################### */
if (reg_id >= 0x001B && reg_id <= 0x005C) { /* GMSR to LMBDR */
if (reg_id == 0x005C) /* LMBDR */
return ((WORD) inb(iw.lmbdr));
if (reg_id >= 0x001B && reg_id <= 0x0021) /* GMSR to I8DP */
if (offset == 0x04)
return (inw(iw.i16dp));
else
return ((WORD) inb(iw.p3xr + offset));
else { /* indexed registers */
if (reg_id <= 0x003F)
index |= 0x80; /* adjust for reading */
outb(iw.igidxr, index); /* select register */
if (offset == 0x04)
return (inw(iw.i16dp));
else
return ((WORD) inb(iw.i8dp));
}
}
/* #################################################### */
/* Logic to read registers in PCODAR block */
/* #################################################### */
if (reg_id >= 0x005D && reg_id <= 0x0081) { /* CIDXR to CLRCTI */
if (reg_id <= 0x0061)
return ((WORD) inb(iw.pcodar + offset)); /* CRDR */
else { /* indexed registers */
BYTE cidxr;
cidxr = inb(iw.pcodar);
cidxr = (cidxr & 0xE0) + index;
outb(iw.pcodar, cidxr); /* select register */
return ((WORD) inb(iw.cdatap));
}
}
/* ##################################################### */
/* Logic to read the PnP registers */
/* ##################################################### */
if (reg_id >= 0x0082 && reg_id <= 0x00B7) { /* PCSNBR to PMITI */
if (reg_id == 0x0085)
return ((WORD) inb(iw.pnprdp));
if (reg_id < 0x0085)
return ((WORD) inb((WORD) reg_mnem));
else { /* indexed registers */
if (reg_id >= 0x008E && reg_id <= 0x00B7) {
outb(0x0279, 0x07); /* select PLDNI */
outb(0xA79, (BYTE) offset); /* select logical dev */
}
outb(0x0279, index); /* select the register */
return ((WORD) inb(iw.pnprdp));
}
}
return 0;
}
/* ######################################################################### */
/**/
/* FUNCTION: IwaveRegPoke */
/**/
/* PROFILE : This function writes a value to any writable */
/* InterWave register. It takes as input a pointer to a */
/* structure containing the addresses of the relocatable I/O */
/* space as well as a register mnemonic. To correctly use this */
/* function, the programmer must use the mnemonics defined in */
/* "iwdefs.h". These mnemonics contain coded information used */
/* by the function to properly access the desired register. */
/**/
/* This function does not guard against writing to read-only */
/* registers. It is the programmer's responsibility to ensure */
/* that the writes are to valid registers. */
/**/
/* ######################################################################### */
static void
IwaveRegPoke(DWORD reg_mnem, WORD datum)
{
BYTE index;
BYTE val;
WORD reg_id;
WORD offset;
offset = (WORD) ((BYTE) reg_mnem);
reg_id = (WORD) (reg_mnem >> 16);
index = (BYTE) (reg_mnem >> 8);
/* ####################################################### */
/* Logic to write to registers in P2XR block */
/* ####################################################### */
if (reg_id >= 0x0001 && reg_id <= 0x0019) { /* UMCR to GGCR */
if (reg_id <= 0x000E) { /* UMCR to USRR */
outb(iw.p2xr + offset, (BYTE) datum);
return;
}
if (reg_id == 0x0019) {
outb(iw.p201ar, (BYTE) datum);
return;
} else { /* GUS Hidden registers */
BYTE iveri;
outb(iw.igidxr, 0x5B); /* select IVERI */
iveri = inb(iw.i8dp); /* read IVERI */
outb(iw.i8dp, (BYTE) (iveri | 0x09)); /* set IVERI[3,0] */
val = inb(iw.p2xr + 0x0F); /* read URCR */
val = (val & 0xF8) | index; /* value for URCR[2:0] */
outb(iw.p2xr + 0x0F, val); /* set URCR[2:0] */
if (reg_mnem == UDCI || reg_mnem == UICI) {
val = inb(iw.p2xr); /* read UMCR */
if (reg_mnem == UDCI)
outb(iw.p2xr, (BYTE) (val & 0xBF)); /* set UMCR[6]=0 */
else
outb(iw.p2xr, (BYTE) (val | 0x40)); /* set UMCR[6]=1 */
}
outb(iw.p2xr + 0x0B, (BYTE) datum); /* write register */
outb(iw.igidxr, 0x5B); /* select IVERI */
outb(iw.i8dp, iveri); /* restore IVERI */
return;
}
}
/* ############################################################# */
/* Logic to write to registers in P3XR block */
/* ############################################################# */
if (reg_id >= 0x001A && reg_id <= 0x005C) { /* GMCR to LMBDR */
if (reg_id == 0x005C) { /* LMBDR */
outb(iw.lmbdr, (BYTE) datum);
return;
}
if (reg_id == 0x001B) /* GMSR */
return;
if (reg_id >= 0x001A && reg_id <= 0x0021) /* GMCR to I8DP */
if (offset == 0x04)
outw(iw.i16dp, datum);
else
outb(iw.p3xr + offset, (BYTE) datum);
else { /* indexed registers */
outb(iw.igidxr, index); /* select register */
if (offset == 0x04)
outw(iw.i16dp, datum);
else
outb(iw.i8dp, (BYTE) datum);
}
}
/* /################################################### */
/* Logic to write to registers in PCODAR block */
/* ################################################### */
if (reg_id >= 0x005C && reg_id <= 0x0081) { /* CIDXR to CLRCTI */
if (reg_id <= 0x0061)
outb(iw.pcodar + offset, (BYTE) datum);
else { /* one of the indexed registers */
BYTE cidxr;
cidxr = inb(iw.pcodar);
cidxr = (cidxr & 0xE0) + index;
outb(iw.pcodar, cidxr); /* select register */
outb(iw.cdatap, (BYTE) datum);
}
}
/* ###################################################### */
/* Logic to write to the PnP registers */
/* ###################################################### */
if (reg_id >= 0x0082 && reg_id <= 0x00B7) {
if (reg_id == 0x0085) {
outb(iw.pnprdp, (BYTE) datum);
return;
}
if (reg_id < 0x0085)
outb((WORD) reg_mnem, (BYTE) datum);
else { /* one of the indexed registers */
if (reg_id >= 0x008E && reg_id <= 0x00B7) {
outb(0x0279, 0x07); /* select PLDNI */
outb(0xA79, (BYTE) offset); /* select logical dev */
}
outb(0x0279, index); /* select the register */
outb(0xA79, (BYTE) datum);
}
}
}
static void
IwaveLineLevel(char level, char index)
{
char reg;
level &= 0x1F;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | index); /* select register */
outb(iw.cdatap, (BYTE) ((inb(iw.cdatap) & 0x80) | level)); /* set level */
LEAVE_CRITICAL;
}
static void
IwaveCodecMode(char mode)
{
char reg;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | _CMODEI); /* select CMODEI */
outb(iw.cdatap, mode);
LEAVE_CRITICAL;
iw.cmode = mode;
}
static void
IwaveLineMute(BYTE mute, BYTE inx)
{
BYTE reg;
ENTER_CRITICAL;
reg = inb(iw.pcodar) & 0xE0;
outb(iw.pcodar, reg | inx); /* select register */
if (mute == ON)
outb(iw.cdatap, (BYTE) (inb(iw.cdatap) | 0x80)); /* mute */
else
outb(iw.cdatap, (BYTE) (inb(iw.cdatap) & 0x7F)); /* unmute */
LEAVE_CRITICAL;
}
static void
Iwaveinitcodec()
{
u_short iwl_codec_base = iw.pcodar;
u_short iwl_codec_data = iw.pcodar + 1;
u_short foo;
/*
* Set the CEXTI register foo = CODEC_CEXTI_DEFAULT;
* IWL_CODEC_OUT(EXTERNAL_CONTROL, foo);
*/
/*
* Disable Interrupts iwl_codec_disable_irqs();
*/
/* Set the CODEC to Operate in Mode 3 */
IWL_CODEC_OUT(MODE_SELECT_ID, 0x6C);
foo = inb(iwl_codec_data);
/* Set the configuration registers to their default values */
foo = CODEC_CFIG1I_DEFAULT;
IWL_CODEC_OUT(CONFIG_1 | CODEC_MCE, foo);
outb(iwl_codec_base, CONFIG_1);
foo = CODEC_CFIG2I_DEFAULT;
IWL_CODEC_OUT(CONFIG_2, foo);
foo = CODEC_CFIG3I_DEFAULT;
IWL_CODEC_OUT(CONFIG_3, foo);
}
int
IwaveOpen(char voices, char mode, struct address_info * hw)
{
u_long flags;
u_char tmp;
flags = splhigh();
iw.pnprdp = 0;
if (IwavePnpIsol(&iw.pnprdp)) {
iw.vendor = GUS_PNP_ID;
iw.csn = IwavePnpPing(iw.vendor);
IwavePnpKey();
IwavePnpWake(iw.csn);
IwavePnpGetCfg();
IwavePnpKey();
IwavePnpWake(iw.csn);
}
if (hw->irq > 0) {
/* I see the user wants to set the GUS PnP */
/* Okay lets do it */
iw.csn = 1;
iw.p2xr = hw->io_base;
iw.p3xr = hw->io_base + 0x100;
iw.pcodar = hw->io_base + 0x10c;
iw.synth_irq = hw->irq;
iw.midi_irq = hw->irq;
iw.dma1_chan = hw->dma;
if (hw->dma2 == -1) {
iw.dma2_chan = hw->dma;
} else {
iw.dma2_chan = hw->dma2;
}
} else {
/* tell the os what we are doing 8) */
hw->io_base = iw.p2xr;
hw->irq = iw.synth_irq;
/*
* iw.dma1_chan = 1; iw.dma2_chan = 3 ;
*/
hw->dma = iw.dma1_chan;
hw->dma2 = iw.dma2_chan;
}
if (iw.csn > 0 && iw.csn < MAX_GUS_PNP) {
gus_pnp_found[iw.csn] = hw->io_base;
}
iw.cdatap = iw.pcodar + 1;
iw.csr1r = iw.pcodar + 2;
iw.cxdr = iw.pcodar + 3;/* CPDR or CRDR */
iw.gmxr = iw.p3xr;
iw.gmxdr = iw.p3xr + 1; /* GMTDR or GMRDR */
iw.svsr = iw.p3xr + 2;
iw.igidxr = iw.p3xr + 3;
iw.i16dp = iw.p3xr + 4;
iw.i8dp = iw.p3xr + 5;
iw.lmbdr = iw.p3xr + 7;
iw.voices = voices;
if (iw.pnprdp > 0 && iw.csn > 0) {
IwavePnpSetCfg();
IwavePnpActivate(AUDIO, ON);
IwavePnpActivate(EXT, ON);
}
/* IwavePnpActivate(EMULATION,ON); */
/* reset */
outb(iw.igidxr, _URSTI);/* Pull reset */
outb(iw.i8dp, 0x00);
DELAY(1000 * 30);
outb(iw.i8dp, 0x01); /* Release reset */
DELAY(1000 * 30);
/* end of reset */
IwaveMemCfg(NULL);
tmp = IwaveRegPeek(IDECI);
IwaveRegPoke(IDECI, tmp | 0x18);
IwaveCodecMode(CODEC_MODE2); /* Default codec mode */
IwaveRegPoke(ICMPTI, 0);
outb(iw.igidxr, 0x99);
tmp = inb(iw.i8dp);
outb(iw.igidxr, 0x19);
outb(iw.i8dp, tmp);
IwaveCodecIrq(~CODEC_IRQ_ENABLE);
Iwaveinitcodec();
outb(iw.p2xr, 0x0c); /* Disable line in, mic and line out */
IwaveRegPoke(CLCI, 0x3f << 2);
IwaveLineLevel(0, _CLOAI);
IwaveLineLevel(0, _CROAI);
IwaveLineMute(OFF, _CLOAI);
IwaveLineMute(OFF, _CROAI);
IwaveLineLevel(0, _CLLICI);
IwaveLineLevel(0, _CRLICI);
IwaveLineMute(OFF, _CLLICI);
IwaveLineMute(OFF, _CRLICI);
IwaveLineLevel(0, _CLDACI);
IwaveLineLevel(0, _CRDACI);
IwaveLineMute(ON, _CLDACI);
IwaveLineMute(ON, _CRDACI);
IwaveLineLevel(0, _CLLICI);
IwaveLineLevel(0, _CRLICI);
IwaveLineMute(ON, _CLLICI);
IwaveLineMute(ON, _CRLICI);
IwaveInputSource(LEFT_SOURCE, MIC_IN);
IwaveInputSource(RIGHT_SOURCE, MIC_IN);
outb(iw.pcodar, 0x9 | 0x40);
outb(iw.cdatap, 0);
IwaveCodecIrq(CODEC_IRQ_ENABLE);
outb(iw.pcodar, _CFIG3I | 0x20);
outb(iw.cdatap, 0xC2); /* Enable Mode 3 IRQs & Synth */
outb(iw.igidxr, _URSTI);
outb(iw.i8dp, GF1_SET | GF1_OUT_ENABLE | GF1_IRQ_ENABLE);
DELAY(1000 * 30);
iw.size_mem = IwaveMemSize(); /* Bytes of RAM in this mode */
outb(iw.p2xr, 0xc); /* enable output */
IwaveRegPoke(CLCI, 0x3f << 2);
IwaveCodecIrq(CODEC_IRQ_ENABLE);
splx(flags);
DELAY(1000 * 100);
IwaveRegPoke(CPDFI, 0);
return (TRUE);
}
void
gus_wave_init(struct address_info * hw_config)
{
u_long flags;
u_char val, gus_pnp_seen = 0;
char *model_num = "2.4";
int gus_type = 0x24; /* 2.4 */
int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2;
int otherside = -1, i;
if (irq < 0 || irq > 15) {
printf("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
return;
}
if (dma < 0 || dma > 7) {
printf("ERROR! Invalid DMA#%d. GUS Disabled", dma);
return;
}
for (i = 0; i < MAX_GUS_PNP; i++) {
if (gus_pnp_found[i] != 0 && gus_pnp_found[i] == hw_config->io_base)
gus_pnp_seen = 1;
}
#ifdef NOGUSPNP
gus_pnp_seen = 0;
#endif
gus_irq = irq;
gus_dma = dma;
gus_dma2 = dma2;
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.
*/
flags = splhigh();
outb(gus_base + 0x0f, 0x20);
val = inb(gus_base + 0x0f);
splx(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.
*
*/
if (gus_pnp_seen)
val = 66;
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 {
if (gus_pnp_seen)
model_num = "PNP";
else
model_num = "MAX";
gus_type = 0x40;
mixer_type = CS4231;
#ifdef CONFIG_GUSMAX
{
u_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(gus_base + 0x106, max_config); /* UltraMax control */
}
if (ad1848_detect(gus_base + 0x10c, NULL, hw_config->osp)) {
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
gus_wave_volume = 90;
have_gus_max = 1;
if (gus_pnp_seen) {
ad1848_init("GUS PNP", gus_base + 0x10c,
-irq,
gus_dma2, /* Playback DMA */
gus_dma, /* Capture DMA */
1, /* Share DMA channels with GF1 */
hw_config->osp);
} else {
ad1848_init("GUS MAX", gus_base + 0x10c,
-irq,
gus_dma2, /* Playback DMA */
gus_dma, /* Capture DMA */
1, /* Share DMA channels with GF1 */
hw_config->osp);
}
otherside = num_audiodevs - 1;
} else
printf("[Where's the CS4231?]");
#else
printf("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\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 (gus_pnp_seen) {
sprintf(gus_info.name, "Gravis %s (%dk)", model_num, (int) gus_mem_size / 1024);
} else {
sprintf(gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
}
conf_printf(gus_info.name, hw_config);
if (num_synths >= MAX_SYNTH_DEV)
printf("GUS Error: Too many synthesizers\n");
else {
voice_alloc = &guswave_operations.alloc;
synth_devs[num_synths++] = &guswave_operations;
#ifdef CONFIG_SEQUENCER
gus_tmr_install(gus_base + 8);
#endif
}
samples = (struct patch_info *) malloc((MAX_SAMPLE + 1) * sizeof(*samples), M_DEVBUF, M_NOWAIT);
if (!samples)
panic("SOUND: Cannot allocate memory\n");
reset_sample_memory();
gus_initialize();
if (num_audiodevs < MAX_AUDIO_DEV) {
audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations;
audio_devs[gus_devnum]->otherside = otherside;
audio_devs[gus_devnum]->dmachan1 = dma;
audio_devs[gus_devnum]->dmachan2 = dma2;
audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
if (otherside != -1) {
/*
* glue logic to prevent people from opening the gus
* max via the gf1 and the cs4231 side . Only the gf1
* or the cs4231 are allowed to be open
*/
audio_devs[otherside]->otherside = gus_devnum;
}
if (dma2 != dma && dma2 != -1)
audio_devs[gus_devnum]->flags |= DMA_DUPLEX;
} else
printf("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;
ics2101_mixer_init();
return;
case CS4231:
/* Initialized elsewhere (ad1848.c) */
default:
gus_default_mixer_init();
return;
}
}
static void
do_loop_irq(int voice)
{
u_char tmp;
int mode, parm;
u_long flags;
flags = splhigh();
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:
{
int flag; /* 0 or 2 */
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 (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:;
}
splx(flags);
}
static void
do_volume_irq(int voice)
{
u_char tmp;
int mode, parm;
u_long flags;
flags = splhigh();
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 */
splx(flags);
gus_voice_init(voice);
break;
case VMODE_ENVELOPE:
gus_rampoff();
splx(flags);
step_envelope(voice);
break;
case VMODE_START_NOTE:
splx(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)
{
u_long wave_ignore = 0, volume_ignore = 0;
u_long voice_bit;
u_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)
{
u_char status;
status = gus_look8(0x41); /* Get DMA IRQ Status */
if (status & 0x40) /* DMA interrupt pending */
switch (active_device) {
case GUS_DEV_WAVE:
if ((dram_sleep_flag.mode & WK_SLEEP)) {
dram_sleep_flag.mode = WK_WAKEUP;
wakeup(dram_sleeper);
};
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);
}
}
#ifdef CONFIG_SEQUENCER
/*
* Timer stuff
*/
static volatile int select_addr, data_addr;
static volatile int curr_timer = 0;
void
gus_timer_command(u_int addr, u_int val)
{
int i;
outb(select_addr, (u_char) (addr & 0xff));
for (i = 0; i < 2; i++)
inb(select_addr);
outb(data_addr, (u_char) (val & 0xff));
for (i = 0; i < 2; i++)
inb(select_addr);
}
static void
arm_timer(int timer, u_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 = 0;
}
static u_int
gus_tmr_start(int dev, u_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 */
}
static struct sound_lowlev_timer gus_tmr =
{
0,
gus_tmr_start,
gus_tmr_disable,
gus_tmr_restart
};
static void
gus_tmr_install(int io_base)
{
select_addr = io_base;
data_addr = io_base + 1;
sound_timer_init(&gus_tmr, "GUS");
}
#endif
#endif