a879550a11
possibly different size).
4878 lines
122 KiB
C
4878 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 <stddef.h>
|
|
|
|
#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 = offsetof(struct patch_info, data); /* 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
|