421 lines
9.8 KiB
C
421 lines
9.8 KiB
C
#define _PAS2_CARD_C_
|
|
/*
|
|
* sound/pas2_card.c
|
|
*
|
|
* Detection routine for the Pro Audio Spectrum cards.
|
|
*
|
|
* Copyright by Hannu Savolainen 1993
|
|
*
|
|
* 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 "sound_config.h"
|
|
|
|
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
|
|
|
|
#define DEFINE_TRANSLATIONS
|
|
#include "pas.h"
|
|
|
|
static int config_pas_hw __P((struct address_info *hw_config));
|
|
static int detect_pas_hw __P((struct address_info *hw_config));
|
|
static void pas2_msg __P((char *foo));
|
|
|
|
/*
|
|
* The Address Translation code is used to convert I/O register addresses to
|
|
* be relative to the given base -register
|
|
*/
|
|
|
|
int translat_code;
|
|
static int pas_intr_mask = 0;
|
|
static int pas_irq = 0;
|
|
|
|
char pas_model;
|
|
static char *pas_model_names[] =
|
|
{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
|
|
|
|
extern void mix_write (unsigned char data, int ioaddr);
|
|
/*
|
|
* pas_read() and pas_write() are equivalents of INB() and OUTB()
|
|
*/
|
|
/*
|
|
* These routines perform the I/O address translation required
|
|
*/
|
|
/*
|
|
* to support other than the default base address
|
|
*/
|
|
|
|
unsigned char
|
|
pas_read (int ioaddr)
|
|
{
|
|
return INB (ioaddr ^ translat_code);
|
|
}
|
|
|
|
void
|
|
pas_write (unsigned char data, int ioaddr)
|
|
{
|
|
OUTB (data, ioaddr ^ translat_code);
|
|
}
|
|
|
|
static void
|
|
pas2_msg (char *foo)
|
|
{
|
|
printk (" PAS2: %s.\n", foo);
|
|
}
|
|
|
|
/******************* Begin of the Interrupt Handler ********************/
|
|
|
|
void
|
|
pasintr (INT_HANDLER_PARMS (irq, dummy))
|
|
{
|
|
int status;
|
|
|
|
status = pas_read (INTERRUPT_STATUS);
|
|
pas_write (status, INTERRUPT_STATUS); /*
|
|
* Clear interrupt
|
|
*/
|
|
|
|
if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
|
|
{
|
|
#ifndef EXCLUDE_AUDIO
|
|
pas_pcm_interrupt (status, 1);
|
|
#endif
|
|
status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
|
|
}
|
|
if (status & I_S_MIDI_IRQ)
|
|
{
|
|
#ifndef EXCLUDE_MIDI
|
|
#ifdef EXCLUDE_PRO_MIDI
|
|
pas_midi_interrupt ();
|
|
#endif
|
|
#endif
|
|
status &= ~I_S_MIDI_IRQ;
|
|
}
|
|
|
|
}
|
|
|
|
int
|
|
pas_set_intr (int mask)
|
|
{
|
|
int err;
|
|
|
|
if (!mask)
|
|
return 0;
|
|
|
|
if (!pas_intr_mask)
|
|
{
|
|
if ((err = snd_set_irq_handler (pas_irq, pasintr, "PAS16")) < 0)
|
|
return err;
|
|
}
|
|
pas_intr_mask |= mask;
|
|
|
|
pas_write (pas_intr_mask, INTERRUPT_MASK);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pas_remove_intr (int mask)
|
|
{
|
|
if (!mask)
|
|
return 0;
|
|
|
|
pas_intr_mask &= ~mask;
|
|
pas_write (pas_intr_mask, INTERRUPT_MASK);
|
|
|
|
if (!pas_intr_mask)
|
|
{
|
|
snd_release_irq (pas_irq);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************* End of the Interrupt handler **********************/
|
|
|
|
/******************* Begin of the Initialization Code ******************/
|
|
|
|
static int
|
|
config_pas_hw (struct address_info *hw_config)
|
|
{
|
|
char ok = 1;
|
|
unsigned int_ptrs; /* scsi/sound interrupt pointers */
|
|
|
|
pas_irq = hw_config->irq;
|
|
|
|
pas_write (0x00, INTERRUPT_MASK);
|
|
|
|
pas_write (0x36, SAMPLE_COUNTER_CONTROL); /*
|
|
* Local timer control *
|
|
* register
|
|
*/
|
|
|
|
pas_write (0x36, SAMPLE_RATE_TIMER); /*
|
|
* Sample rate timer (16 bit)
|
|
*/
|
|
pas_write (0, SAMPLE_RATE_TIMER);
|
|
|
|
pas_write (0x74, SAMPLE_COUNTER_CONTROL); /*
|
|
* Local timer control *
|
|
* register
|
|
*/
|
|
|
|
pas_write (0x74, SAMPLE_BUFFER_COUNTER); /*
|
|
* Sample count register (16
|
|
* * bit)
|
|
*/
|
|
pas_write (0, SAMPLE_BUFFER_COUNTER);
|
|
|
|
pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
|
|
pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
|
|
pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /*
|
|
* |
|
|
* S_M_OPL3_DUAL_MONO
|
|
*/ , SERIAL_MIXER);
|
|
|
|
pas_write (I_C_1_BOOT_RESET_ENABLE
|
|
#ifdef PAS_JOYSTICK_ENABLE
|
|
| I_C_1_JOYSTICK_ENABLE
|
|
#endif
|
|
,IO_CONFIGURATION_1);
|
|
|
|
if (pas_irq < 0 || pas_irq > 15)
|
|
{
|
|
printk ("PAS2: Invalid IRQ %d", pas_irq);
|
|
ok = 0;
|
|
}
|
|
else
|
|
{
|
|
int_ptrs = pas_read (IO_CONFIGURATION_3);
|
|
int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf;
|
|
pas_write (int_ptrs, IO_CONFIGURATION_3);
|
|
if (!I_C_3_PCM_IRQ_translate[pas_irq])
|
|
{
|
|
printk ("PAS2: Invalid IRQ %d", pas_irq);
|
|
ok = 0;
|
|
}
|
|
}
|
|
|
|
if (hw_config->dma < 0 || hw_config->dma > 7)
|
|
{
|
|
printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
|
|
ok = 0;
|
|
}
|
|
else
|
|
{
|
|
pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
|
|
if (!I_C_2_PCM_DMA_translate[hw_config->dma])
|
|
{
|
|
printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
|
|
ok = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This fixes the timing problems of the PAS due to the Symphony chipset
|
|
* as per Media Vision. Only define this if your PAS doesn't work correctly.
|
|
*/
|
|
#ifdef SYMPHONY_PAS
|
|
OUTB (0x05, 0xa8);
|
|
OUTB (0x60, 0xa9);
|
|
#endif
|
|
|
|
#ifdef BROKEN_BUS_CLOCK
|
|
pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
|
|
#else
|
|
/*
|
|
* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1);
|
|
*/
|
|
pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
|
|
#endif
|
|
pas_write (0x18, SYSTEM_CONFIGURATION_3); /*
|
|
* ???
|
|
*/
|
|
|
|
pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /*
|
|
* Sets mute
|
|
* off and *
|
|
* selects
|
|
* filter
|
|
* rate * of
|
|
* 17.897 kHz
|
|
*/
|
|
|
|
if (pas_model == PAS_16 || pas_model == PAS_16D)
|
|
pas_write (8, PRESCALE_DIVIDER);
|
|
else
|
|
pas_write (0, PRESCALE_DIVIDER);
|
|
|
|
mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
|
|
mix_write (5, PARALLEL_MIXER);
|
|
|
|
#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
|
|
|
|
{
|
|
struct address_info *sb_config;
|
|
|
|
if ((sb_config = sound_getconf (SNDCARD_SB)))
|
|
{
|
|
unsigned char irq_dma;
|
|
|
|
/*
|
|
* Turn on Sound Blaster compatibility
|
|
*/
|
|
/*
|
|
* bit 1 = SB emulation
|
|
*/
|
|
/*
|
|
* bit 0 = MPU401 emulation (CDPC only :-( )
|
|
*/
|
|
pas_write (0x02, COMPATIBILITY_ENABLE);
|
|
|
|
/*
|
|
* "Emulation address"
|
|
*/
|
|
pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS);
|
|
|
|
if (!E_C_SB_DMA_translate[sb_config->dma])
|
|
printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
|
|
sb_config->dma);
|
|
|
|
if (!E_C_SB_IRQ_translate[sb_config->irq])
|
|
printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
|
|
sb_config->irq);
|
|
|
|
irq_dma = E_C_SB_DMA_translate[sb_config->dma] |
|
|
E_C_SB_IRQ_translate[sb_config->irq];
|
|
|
|
pas_write (irq_dma, EMULATION_CONFIGURATION);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!ok)
|
|
pas2_msg ("Driver not enabled");
|
|
|
|
return ok;
|
|
}
|
|
|
|
static int
|
|
detect_pas_hw (struct address_info *hw_config)
|
|
{
|
|
unsigned char board_id, foo;
|
|
|
|
/*
|
|
* WARNING: Setting an option like W:1 or so that disables warm boot reset
|
|
* of the card will screw up this detect code something fierce. Adding code
|
|
* to handle this means possibly interfering with other cards on the bus if
|
|
* you have something on base port 0x388. SO be forewarned.
|
|
*/
|
|
|
|
OUTB (0xBC, MASTER_DECODE); /*
|
|
* Talk to first board
|
|
*/
|
|
OUTB (hw_config->io_base >> 2, MASTER_DECODE); /*
|
|
* Set base address
|
|
*/
|
|
translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
|
|
pas_write (1, WAIT_STATE); /*
|
|
* One wait-state
|
|
*/
|
|
|
|
board_id = pas_read (INTERRUPT_MASK);
|
|
|
|
if (board_id == 0xff)
|
|
return 0;
|
|
|
|
/*
|
|
* We probably have a PAS-series board, now check for a PAS2-series board
|
|
* by trying to change the board revision bits. PAS2-series hardware won't
|
|
* let you do this - the bits are read-only.
|
|
*/
|
|
|
|
foo = board_id ^ 0xe0;
|
|
|
|
pas_write (foo, INTERRUPT_MASK);
|
|
foo = INB (INTERRUPT_MASK);
|
|
pas_write (board_id, INTERRUPT_MASK);
|
|
|
|
if (board_id != foo) /*
|
|
* Not a PAS2
|
|
*/
|
|
return 0;
|
|
|
|
pas_model = pas_read (CHIP_REV);
|
|
|
|
return pas_model;
|
|
}
|
|
|
|
long
|
|
attach_pas_card (long mem_start, struct address_info *hw_config)
|
|
{
|
|
pas_irq = hw_config->irq;
|
|
|
|
if (detect_pas_hw (hw_config))
|
|
{
|
|
|
|
if (pas_model = pas_read (CHIP_REV))
|
|
{
|
|
#ifdef __FreeBSD__
|
|
printk ("pas0: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
|
|
#else
|
|
printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
|
|
#endif
|
|
}
|
|
|
|
if (config_pas_hw (hw_config))
|
|
{
|
|
|
|
#ifndef EXCLUDE_AUDIO
|
|
mem_start = pas_pcm_init (mem_start, hw_config);
|
|
#endif
|
|
|
|
#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
|
|
|
|
sb_dsp_disable_midi (); /*
|
|
* The SB emulation don't support *
|
|
* midi
|
|
*/
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_YM3812
|
|
enable_opl3_mode (0x388, 0x38a, 0);
|
|
#endif
|
|
|
|
#ifndef EXCLUDE_MIDI
|
|
#ifdef EXCLUDE_PRO_MIDI
|
|
mem_start = pas_midi_init (mem_start);
|
|
#endif
|
|
#endif
|
|
|
|
pas_init_mixer ();
|
|
}
|
|
}
|
|
|
|
return mem_start;
|
|
}
|
|
|
|
int
|
|
probe_pas (struct address_info *hw_config)
|
|
{
|
|
return detect_pas_hw (hw_config);
|
|
}
|
|
|
|
#endif
|