2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* sound / oss / sscape . c
2005-04-17 02:20:36 +04:00
*
* Low level driver for Ensoniq SoundScape
*
*
* Copyright ( C ) by Hannu Savolainen 1993 - 1997
*
* OSS / Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE ( GPL )
* Version 2 ( June 1991 ) . See the " COPYING " file distributed with this software
* for more info .
*
*
* Thomas Sailer : ioctl code reworked ( vmalloc / vfree removed )
* Sergey Smitienko : ensoniq p ' n ' p support
* Christoph Hellwig : adapted to module_init / module_exit
* Bartlomiej Zolnierkiewicz : added __init to attach_sscape ( )
* Chris Rankin : Specify that this module owns the coprocessor
* Arnaldo C . de Melo : added missing restore_flags in sscape_pnp_upload_file
*/
# include <linux/init.h>
# include <linux/module.h>
# include "sound_config.h"
# include "sound_firmware.h"
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/fcntl.h>
# include <linux/ctype.h>
# include <linux/stddef.h>
# include <linux/kmod.h>
# include <asm/dma.h>
# include <asm/io.h>
# include <linux/wait.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/proc_fs.h>
2006-10-20 23:17:02 +04:00
# include <linux/mm.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
# include "coproc.h"
# include "ad1848.h"
# include "mpu401.h"
/*
* I / O ports
*/
# define MIDI_DATA 0
# define MIDI_CTRL 1
# define HOST_CTRL 2
# define TX_READY 0x02
# define RX_READY 0x01
# define HOST_DATA 3
# define ODIE_ADDR 4
# define ODIE_DATA 5
/*
* Indirect registers
*/
# define GA_INTSTAT_REG 0
# define GA_INTENA_REG 1
# define GA_DMAA_REG 2
# define GA_DMAB_REG 3
# define GA_INTCFG_REG 4
# define GA_DMACFG_REG 5
# define GA_CDCFG_REG 6
# define GA_SMCFGA_REG 7
# define GA_SMCFGB_REG 8
# define GA_HMCTL_REG 9
/*
* DMA channel identifiers ( A and B )
*/
# define SSCAPE_DMA_A 0
# define SSCAPE_DMA_B 1
# define PORT(name) (devc->base+name)
/*
* Host commands recognized by the OBP microcode
*/
# define CMD_GEN_HOST_ACK 0x80
# define CMD_GEN_MPU_ACK 0x81
# define CMD_GET_BOARD_TYPE 0x82
# define CMD_SET_CONTROL 0x88 /* Old firmware only */
# define CMD_GET_CONTROL 0x89 /* Old firmware only */
# define CTL_MASTER_VOL 0
# define CTL_MIC_MODE 2
# define CTL_SYNTH_VOL 4
# define CTL_WAVE_VOL 7
# define CMD_SET_EXTMIDI 0x8a
# define CMD_GET_EXTMIDI 0x8b
# define CMD_SET_MT32 0x8c
# define CMD_GET_MT32 0x8d
# define CMD_ACK 0x80
# define IC_ODIE 1
# define IC_OPUS 2
typedef struct sscape_info
{
int base , irq , dma ;
int codec , codec_irq ; /* required to setup pnp cards*/
int codec_type ;
int ic_type ;
char * raw_buf ;
unsigned long raw_buf_phys ;
int buffsize ; /* -------------------------- */
spinlock_t lock ;
int ok ; /* Properly detected */
int failed ;
int dma_allocated ;
int codec_audiodev ;
int opened ;
int * osp ;
int my_audiodev ;
} sscape_info ;
static struct sscape_info adev_info = {
0
} ;
static struct sscape_info * devc = & adev_info ;
static int sscape_mididev = - 1 ;
/* Some older cards have assigned interrupt bits differently than new ones */
static char valid_interrupts_old [ ] = {
9 , 7 , 5 , 15
} ;
static char valid_interrupts_new [ ] = {
9 , 5 , 7 , 10
} ;
static char * valid_interrupts = valid_interrupts_new ;
/*
* See the bottom of the driver . This can be set by spea = 0 / 1.
*/
# ifdef REVEAL_SPEA
static char old_hardware = 1 ;
# else
static char old_hardware ;
# endif
static void sleep ( unsigned howlong )
{
current - > state = TASK_INTERRUPTIBLE ;
schedule_timeout ( howlong ) ;
}
static unsigned char sscape_read ( struct sscape_info * devc , int reg )
{
unsigned long flags ;
unsigned char val ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
outb ( reg , PORT ( ODIE_ADDR ) ) ;
val = inb ( PORT ( ODIE_DATA ) ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return val ;
}
static void __sscape_write ( int reg , int data )
{
outb ( reg , PORT ( ODIE_ADDR ) ) ;
outb ( data , PORT ( ODIE_DATA ) ) ;
}
static void sscape_write ( struct sscape_info * devc , int reg , int data )
{
unsigned long flags ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
__sscape_write ( reg , data ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
}
static unsigned char sscape_pnp_read_codec ( sscape_info * devc , unsigned char reg )
{
unsigned char res ;
unsigned long flags ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
outb ( reg , devc - > codec ) ;
res = inb ( devc - > codec + 1 ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return res ;
}
static void sscape_pnp_write_codec ( sscape_info * devc , unsigned char reg , unsigned char data )
{
unsigned long flags ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
outb ( reg , devc - > codec ) ;
outb ( data , devc - > codec + 1 ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
}
static void host_open ( struct sscape_info * devc )
{
outb ( ( 0x00 ) , PORT ( HOST_CTRL ) ) ; /* Put the board to the host mode */
}
static void host_close ( struct sscape_info * devc )
{
outb ( ( 0x03 ) , PORT ( HOST_CTRL ) ) ; /* Put the board to the MIDI mode */
}
static int host_write ( struct sscape_info * devc , unsigned char * data , int count )
{
unsigned long flags ;
int i , timeout_val ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
/*
* Send the command and data bytes
*/
for ( i = 0 ; i < count ; i + + )
{
for ( timeout_val = 10000 ; timeout_val > 0 ; timeout_val - - )
if ( inb ( PORT ( HOST_CTRL ) ) & TX_READY )
break ;
if ( timeout_val < = 0 )
{
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return 0 ;
}
outb ( data [ i ] , PORT ( HOST_DATA ) ) ;
}
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return 1 ;
}
static int host_read ( struct sscape_info * devc )
{
unsigned long flags ;
int timeout_val ;
unsigned char data ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
/*
* Read a byte
*/
for ( timeout_val = 10000 ; timeout_val > 0 ; timeout_val - - )
if ( inb ( PORT ( HOST_CTRL ) ) & RX_READY )
break ;
if ( timeout_val < = 0 )
{
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return - 1 ;
}
data = inb ( PORT ( HOST_DATA ) ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return data ;
}
#if 0 /* unused */
static int host_command1 ( struct sscape_info * devc , int cmd )
{
unsigned char buf [ 10 ] ;
buf [ 0 ] = ( unsigned char ) ( cmd & 0xff ) ;
return host_write ( devc , buf , 1 ) ;
}
# endif /* unused */
static int host_command2 ( struct sscape_info * devc , int cmd , int parm1 )
{
unsigned char buf [ 10 ] ;
buf [ 0 ] = ( unsigned char ) ( cmd & 0xff ) ;
buf [ 1 ] = ( unsigned char ) ( parm1 & 0xff ) ;
return host_write ( devc , buf , 2 ) ;
}
static int host_command3 ( struct sscape_info * devc , int cmd , int parm1 , int parm2 )
{
unsigned char buf [ 10 ] ;
buf [ 0 ] = ( unsigned char ) ( cmd & 0xff ) ;
buf [ 1 ] = ( unsigned char ) ( parm1 & 0xff ) ;
buf [ 2 ] = ( unsigned char ) ( parm2 & 0xff ) ;
return host_write ( devc , buf , 3 ) ;
}
static void set_mt32 ( struct sscape_info * devc , int value )
{
host_open ( devc ) ;
host_command2 ( devc , CMD_SET_MT32 , value ? 1 : 0 ) ;
if ( host_read ( devc ) ! = CMD_ACK )
{
/* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
}
host_close ( devc ) ;
}
static void set_control ( struct sscape_info * devc , int ctrl , int value )
{
host_open ( devc ) ;
host_command3 ( devc , CMD_SET_CONTROL , ctrl , value ) ;
if ( host_read ( devc ) ! = CMD_ACK )
{
/* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */
}
host_close ( devc ) ;
}
static void do_dma ( struct sscape_info * devc , int dma_chan , unsigned long buf , int blk_size , int mode )
{
unsigned char temp ;
if ( dma_chan ! = SSCAPE_DMA_A )
{
printk ( KERN_WARNING " soundscape: Tried to use DMA channel != A. Why? \n " ) ;
return ;
}
audio_devs [ devc - > codec_audiodev ] - > flags & = ~ DMA_AUTOMODE ;
DMAbuf_start_dma ( devc - > codec_audiodev , buf , blk_size , mode ) ;
audio_devs [ devc - > codec_audiodev ] - > flags | = DMA_AUTOMODE ;
temp = devc - > dma < < 4 ; /* Setup DMA channel select bits */
if ( devc - > dma < = 3 )
temp | = 0x80 ; /* 8 bit DMA channel */
temp | = 1 ; /* Trigger DMA */
sscape_write ( devc , GA_DMAA_REG , temp ) ;
temp & = 0xfe ; /* Clear DMA trigger */
sscape_write ( devc , GA_DMAA_REG , temp ) ;
}
static int verify_mpu ( struct sscape_info * devc )
{
/*
* The SoundScape board could be in three modes ( MPU , 8250 and host ) .
* If the card is not in the MPU mode , enabling the MPU driver will
* cause infinite loop ( the driver believes that there is always some
* received data in the buffer .
*
* Detect this by looking if there are more than 10 received MIDI bytes
* ( 0x00 ) in the buffer .
*/
int i ;
for ( i = 0 ; i < 10 ; i + + )
{
if ( inb ( devc - > base + HOST_CTRL ) & 0x80 )
return 1 ;
if ( inb ( devc - > base ) ! = 0x00 )
return 1 ;
}
printk ( KERN_WARNING " SoundScape: The device is not in the MPU-401 mode \n " ) ;
return 0 ;
}
static int sscape_coproc_open ( void * dev_info , int sub_device )
{
if ( sub_device = = COPR_MIDI )
{
set_mt32 ( devc , 0 ) ;
if ( ! verify_mpu ( devc ) )
return - EIO ;
}
return 0 ;
}
static void sscape_coproc_close ( void * dev_info , int sub_device )
{
struct sscape_info * devc = dev_info ;
unsigned long flags ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
if ( devc - > dma_allocated )
{
__sscape_write ( GA_DMAA_REG , 0x20 ) ; /* DMA channel disabled */
devc - > dma_allocated = 0 ;
}
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return ;
}
static void sscape_coproc_reset ( void * dev_info )
{
}
static int sscape_download_boot ( struct sscape_info * devc , unsigned char * block , int size , int flag )
{
unsigned long flags ;
unsigned char temp ;
volatile int done , timeout_val ;
static unsigned char codec_dma_bits ;
if ( flag & CPF_FIRST )
{
/*
* First block . Have to allocate DMA and to reset the board
* before continuing .
*/
spin_lock_irqsave ( & devc - > lock , flags ) ;
codec_dma_bits = sscape_read ( devc , GA_CDCFG_REG ) ;
if ( devc - > dma_allocated = = 0 )
devc - > dma_allocated = 1 ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
sscape_write ( devc , GA_HMCTL_REG ,
( temp = sscape_read ( devc , GA_HMCTL_REG ) ) & 0x3f ) ; /*Reset */
for ( timeout_val = 10000 ; timeout_val > 0 ; timeout_val - - )
sscape_read ( devc , GA_HMCTL_REG ) ; /* Delay */
/* Take board out of reset */
sscape_write ( devc , GA_HMCTL_REG ,
( temp = sscape_read ( devc , GA_HMCTL_REG ) ) | 0x80 ) ;
}
/*
* Transfer one code block using DMA
*/
if ( audio_devs [ devc - > codec_audiodev ] - > dmap_out - > raw_buf = = NULL )
{
printk ( KERN_WARNING " soundscape: DMA buffer not available \n " ) ;
return 0 ;
}
memcpy ( audio_devs [ devc - > codec_audiodev ] - > dmap_out - > raw_buf , block , size ) ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
/******** INTERRUPTS DISABLED NOW ********/
do_dma ( devc , SSCAPE_DMA_A ,
audio_devs [ devc - > codec_audiodev ] - > dmap_out - > raw_buf_phys ,
size , DMA_MODE_WRITE ) ;
/*
* Wait until transfer completes .
*/
done = 0 ;
timeout_val = 30 ;
while ( ! done & & timeout_val - - > 0 )
{
int resid ;
if ( HZ / 50 )
sleep ( HZ / 50 ) ;
clear_dma_ff ( devc - > dma ) ;
if ( ( resid = get_dma_residue ( devc - > dma ) ) = = 0 )
done = 1 ;
}
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
if ( ! done )
return 0 ;
if ( flag & CPF_LAST )
{
/*
* Take the board out of reset
*/
outb ( ( 0x00 ) , PORT ( HOST_CTRL ) ) ;
outb ( ( 0x00 ) , PORT ( MIDI_CTRL ) ) ;
temp = sscape_read ( devc , GA_HMCTL_REG ) ;
temp | = 0x40 ;
sscape_write ( devc , GA_HMCTL_REG , temp ) ; /* Kickstart the board */
/*
* Wait until the ODB wakes up
*/
spin_lock_irqsave ( & devc - > lock , flags ) ;
done = 0 ;
timeout_val = 5 * HZ ;
while ( ! done & & timeout_val - - > 0 )
{
unsigned char x ;
sleep ( 1 ) ;
x = inb ( PORT ( HOST_DATA ) ) ;
if ( x = = 0xff | | x = = 0xfe ) /* OBP startup acknowledge */
{
DDB ( printk ( " Soundscape: Acknowledge = %x \n " , x ) ) ;
done = 1 ;
}
}
sscape_write ( devc , GA_CDCFG_REG , codec_dma_bits ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
if ( ! done )
{
printk ( KERN_ERR " soundscape: The OBP didn't respond after code download \n " ) ;
return 0 ;
}
spin_lock_irqsave ( & devc - > lock , flags ) ;
done = 0 ;
timeout_val = 5 * HZ ;
while ( ! done & & timeout_val - - > 0 )
{
sleep ( 1 ) ;
if ( inb ( PORT ( HOST_DATA ) ) = = 0xfe ) /* Host startup acknowledge */
done = 1 ;
}
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
if ( ! done )
{
printk ( KERN_ERR " soundscape: OBP Initialization failed. \n " ) ;
return 0 ;
}
printk ( KERN_INFO " SoundScape board initialized OK \n " ) ;
set_control ( devc , CTL_MASTER_VOL , 100 ) ;
set_control ( devc , CTL_SYNTH_VOL , 100 ) ;
# ifdef SSCAPE_DEBUG3
/*
* Temporary debugging aid . Print contents of the registers after
* downloading the code .
*/
{
int i ;
for ( i = 0 ; i < 13 ; i + + )
printk ( " I%d = %02x (new value) \n " , i , sscape_read ( devc , i ) ) ;
}
# endif
}
return 1 ;
}
static int download_boot_block ( void * dev_info , copr_buffer * buf )
{
if ( buf - > len < = 0 | | buf - > len > sizeof ( buf - > data ) )
return - EINVAL ;
if ( ! sscape_download_boot ( devc , buf - > data , buf - > len , buf - > flags ) )
{
printk ( KERN_ERR " soundscape: Unable to load microcode block to the OBP. \n " ) ;
return - EIO ;
}
return 0 ;
}
static int sscape_coproc_ioctl ( void * dev_info , unsigned int cmd , void __user * arg , int local )
{
copr_buffer * buf ;
int err ;
switch ( cmd )
{
case SNDCTL_COPR_RESET :
sscape_coproc_reset ( dev_info ) ;
return 0 ;
case SNDCTL_COPR_LOAD :
buf = ( copr_buffer * ) vmalloc ( sizeof ( copr_buffer ) ) ;
if ( buf = = NULL )
return - ENOSPC ;
if ( copy_from_user ( buf , arg , sizeof ( copr_buffer ) ) )
{
vfree ( buf ) ;
return - EFAULT ;
}
err = download_boot_block ( dev_info , buf ) ;
vfree ( buf ) ;
return err ;
default :
return - EINVAL ;
}
}
static coproc_operations sscape_coproc_operations =
{
" SoundScape M68K " ,
THIS_MODULE ,
sscape_coproc_open ,
sscape_coproc_close ,
sscape_coproc_ioctl ,
sscape_coproc_reset ,
& adev_info
} ;
static struct resource * sscape_ports ;
static int sscape_is_pnp ;
static void __init attach_sscape ( struct address_info * hw_config )
{
# ifndef SSCAPE_REGS
/*
* Config register values for Spea / V7 Media FX and Ensoniq S - 2000.
* These values are card
* dependent . If you have another SoundScape based card , you have to
* find the correct values . Do the following :
* - Compile this driver with SSCAPE_DEBUG1 defined .
* - Shut down and power off your machine .
* - Boot with DOS so that the SSINIT . EXE program is run .
* - Warm boot to { Linux | SYSV | BSD } and write down the lines displayed
* when detecting the SoundScape .
* - Modify the following list to use the values printed during boot .
* Undefine the SSCAPE_DEBUG1
*/
# define SSCAPE_REGS { \
/* I0 */ 0x00 , \
/* I1 */ 0xf0 , /* Note! Ignored. Set always to 0xf0 */ \
/* I2 */ 0x20 , /* Note! Ignored. Set always to 0x20 */ \
/* I3 */ 0x20 , /* Note! Ignored. Set always to 0x20 */ \
/* I4 */ 0xf5 , /* Ignored */ \
/* I5 */ 0x10 , \
/* I6 */ 0x00 , \
/* I7 */ 0x2e , /* I7 MEM config A. Likely to vary between models */ \
/* I8 */ 0x00 , /* I8 MEM config B. Likely to vary between models */ \
/* I9 */ 0x40 /* Ignored */ \
}
# endif
unsigned long flags ;
static unsigned char regs [ 10 ] = SSCAPE_REGS ;
int i , irq_bits = 0xff ;
if ( old_hardware )
{
valid_interrupts = valid_interrupts_old ;
conf_printf ( " Ensoniq SoundScape (old) " , hw_config ) ;
}
else
conf_printf ( " Ensoniq SoundScape " , hw_config ) ;
for ( i = 0 ; i < 4 ; i + + )
{
if ( hw_config - > irq = = valid_interrupts [ i ] )
{
irq_bits = i ;
break ;
}
}
if ( hw_config - > irq > 15 | | ( regs [ 4 ] = irq_bits = = 0xff ) )
{
printk ( KERN_ERR " Invalid IRQ%d \n " , hw_config - > irq ) ;
release_region ( devc - > base , 2 ) ;
release_region ( devc - > base + 2 , 6 ) ;
if ( sscape_is_pnp )
release_region ( devc - > codec , 2 ) ;
return ;
}
if ( ! sscape_is_pnp ) {
spin_lock_irqsave ( & devc - > lock , flags ) ;
/* Host interrupt enable */
sscape_write ( devc , 1 , 0xf0 ) ; /* All interrupts enabled */
/* DMA A status/trigger register */
sscape_write ( devc , 2 , 0x20 ) ; /* DMA channel disabled */
/* DMA B status/trigger register */
sscape_write ( devc , 3 , 0x20 ) ; /* DMA channel disabled */
/* Host interrupt config reg */
sscape_write ( devc , 4 , 0xf0 | ( irq_bits < < 2 ) | irq_bits ) ;
/* Don't destroy CD-ROM DMA config bits (0xc0) */
sscape_write ( devc , 5 , ( regs [ 5 ] & 0x3f ) | ( sscape_read ( devc , 5 ) & 0xc0 ) ) ;
/* CD-ROM config (WSS codec actually) */
sscape_write ( devc , 6 , regs [ 6 ] ) ;
sscape_write ( devc , 7 , regs [ 7 ] ) ;
sscape_write ( devc , 8 , regs [ 8 ] ) ;
/* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
sscape_write ( devc , 9 , ( sscape_read ( devc , 9 ) & 0xf0 ) | 0x08 ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
}
# ifdef SSCAPE_DEBUG2
/*
* Temporary debugging aid . Print contents of the registers after
* changing them .
*/
{
int i ;
for ( i = 0 ; i < 13 ; i + + )
printk ( " I%d = %02x (new value) \n " , i , sscape_read ( devc , i ) ) ;
}
# endif
if ( probe_mpu401 ( hw_config , sscape_ports ) )
hw_config - > always_detect = 1 ;
hw_config - > name = " SoundScape " ;
hw_config - > irq * = - 1 ; /* Negative value signals IRQ sharing */
attach_mpu401 ( hw_config , THIS_MODULE ) ;
hw_config - > irq * = - 1 ; /* Restore it */
if ( hw_config - > slots [ 1 ] ! = - 1 ) /* The MPU driver installed itself */
{
sscape_mididev = hw_config - > slots [ 1 ] ;
midi_devs [ hw_config - > slots [ 1 ] ] - > coproc = & sscape_coproc_operations ;
}
sscape_write ( devc , GA_INTENA_REG , 0x80 ) ; /* Master IRQ enable */
devc - > ok = 1 ;
devc - > failed = 0 ;
}
static int detect_ga ( sscape_info * devc )
{
unsigned char save ;
DDB ( printk ( " Entered Soundscape detect_ga(%x) \n " , devc - > base ) ) ;
/*
* First check that the address register of " ODIE " is
* there and that it has exactly 4 writable bits .
* First 4 bits
*/
if ( ( save = inb ( PORT ( ODIE_ADDR ) ) ) & 0xf0 )
{
DDB ( printk ( " soundscape: Detect error A \n " ) ) ;
return 0 ;
}
outb ( ( 0x00 ) , PORT ( ODIE_ADDR ) ) ;
if ( inb ( PORT ( ODIE_ADDR ) ) ! = 0x00 )
{
DDB ( printk ( " soundscape: Detect error B \n " ) ) ;
return 0 ;
}
outb ( ( 0xff ) , PORT ( ODIE_ADDR ) ) ;
if ( inb ( PORT ( ODIE_ADDR ) ) ! = 0x0f )
{
DDB ( printk ( " soundscape: Detect error C \n " ) ) ;
return 0 ;
}
outb ( ( save ) , PORT ( ODIE_ADDR ) ) ;
/*
* Now verify that some indirect registers return zero on some bits .
* This may break the driver with some future revisions of " ODIE " but . . .
*/
if ( sscape_read ( devc , 0 ) & 0x0c )
{
DDB ( printk ( " soundscape: Detect error D (%x) \n " , sscape_read ( devc , 0 ) ) ) ;
return 0 ;
}
if ( sscape_read ( devc , 1 ) & 0x0f )
{
DDB ( printk ( " soundscape: Detect error E \n " ) ) ;
return 0 ;
}
if ( sscape_read ( devc , 5 ) & 0x0f )
{
DDB ( printk ( " soundscape: Detect error F \n " ) ) ;
return 0 ;
}
return 1 ;
}
static int sscape_read_host_ctrl ( sscape_info * devc )
{
return host_read ( devc ) ;
}
static void sscape_write_host_ctrl2 ( sscape_info * devc , int a , int b )
{
host_command2 ( devc , a , b ) ;
}
static int sscape_alloc_dma ( sscape_info * devc )
{
char * start_addr , * end_addr ;
int dma_pagesize ;
int sz , size ;
struct page * page ;
if ( devc - > raw_buf ! = NULL ) return 0 ; /* Already done */
dma_pagesize = ( devc - > dma < 4 ) ? ( 64 * 1024 ) : ( 128 * 1024 ) ;
devc - > raw_buf = NULL ;
devc - > buffsize = 8192 * 4 ;
if ( devc - > buffsize > dma_pagesize ) devc - > buffsize = dma_pagesize ;
start_addr = NULL ;
/*
* Now loop until we get a free buffer . Try to get smaller buffer if
* it fails . Don ' t accept smaller than 8 k buffer for performance
* reasons .
*/
while ( start_addr = = NULL & & devc - > buffsize > PAGE_SIZE ) {
for ( sz = 0 , size = PAGE_SIZE ; size < devc - > buffsize ; sz + + , size < < = 1 ) ;
devc - > buffsize = PAGE_SIZE * ( 1 < < sz ) ;
start_addr = ( char * ) __get_free_pages ( GFP_ATOMIC | GFP_DMA , sz ) ;
if ( start_addr = = NULL ) devc - > buffsize / = 2 ;
}
if ( start_addr = = NULL ) {
printk ( KERN_ERR " sscape pnp init error: Couldn't allocate DMA buffer \n " ) ;
return 0 ;
} else {
/* make some checks */
end_addr = start_addr + devc - > buffsize - 1 ;
/* now check if it fits into the same dma-pagesize */
if ( ( ( long ) start_addr & ~ ( dma_pagesize - 1 ) ) ! = ( ( long ) end_addr & ~ ( dma_pagesize - 1 ) )
| | end_addr > = ( char * ) ( MAX_DMA_ADDRESS ) ) {
printk ( KERN_ERR " sscape pnp: Got invalid address 0x%lx for %db DMA-buffer \n " , ( long ) start_addr , devc - > buffsize ) ;
return 0 ;
}
}
devc - > raw_buf = start_addr ;
devc - > raw_buf_phys = virt_to_bus ( start_addr ) ;
for ( page = virt_to_page ( start_addr ) ; page < = virt_to_page ( end_addr ) ; page + + )
SetPageReserved ( page ) ;
return 1 ;
}
static void sscape_free_dma ( sscape_info * devc )
{
int sz , size ;
unsigned long start_addr , end_addr ;
struct page * page ;
if ( devc - > raw_buf = = NULL ) return ;
for ( sz = 0 , size = PAGE_SIZE ; size < devc - > buffsize ; sz + + , size < < = 1 ) ;
start_addr = ( unsigned long ) devc - > raw_buf ;
end_addr = start_addr + devc - > buffsize ;
for ( page = virt_to_page ( start_addr ) ; page < = virt_to_page ( end_addr ) ; page + + )
ClearPageReserved ( page ) ;
free_pages ( ( unsigned long ) devc - > raw_buf , sz ) ;
devc - > raw_buf = NULL ;
}
/* Intel version !!!!!!!!! */
static int sscape_start_dma ( int chan , unsigned long physaddr , int count , int dma_mode )
{
unsigned long flags ;
flags = claim_dma_lock ( ) ;
disable_dma ( chan ) ;
clear_dma_ff ( chan ) ;
set_dma_mode ( chan , dma_mode ) ;
set_dma_addr ( chan , physaddr ) ;
set_dma_count ( chan , count ) ;
enable_dma ( chan ) ;
release_dma_lock ( flags ) ;
return 0 ;
}
static void sscape_pnp_start_dma ( sscape_info * devc , int arg )
{
int reg ;
if ( arg = = 0 ) reg = 2 ;
else reg = 3 ;
sscape_write ( devc , reg , sscape_read ( devc , reg ) | 0x01 ) ;
sscape_write ( devc , reg , sscape_read ( devc , reg ) & 0xFE ) ;
}
static int sscape_pnp_wait_dma ( sscape_info * devc , int arg )
{
int reg ;
unsigned long i ;
unsigned char d ;
if ( arg = = 0 ) reg = 2 ;
else reg = 3 ;
sleep ( 1 ) ;
i = 0 ;
do {
d = sscape_read ( devc , reg ) & 1 ;
if ( d = = 1 ) break ;
i + + ;
} while ( i < 500000 ) ;
d = sscape_read ( devc , reg ) & 1 ;
return d ;
}
static int sscape_pnp_alloc_dma ( sscape_info * devc )
{
/* printk(KERN_INFO "sscape: requesting dma\n"); */
if ( request_dma ( devc - > dma , " sscape " ) ) return 0 ;
/* printk(KERN_INFO "sscape: dma channel allocated\n"); */
if ( ! sscape_alloc_dma ( devc ) ) {
free_dma ( devc - > dma ) ;
return 0 ;
} ;
return 1 ;
}
static void sscape_pnp_free_dma ( sscape_info * devc )
{
sscape_free_dma ( devc ) ;
free_dma ( devc - > dma ) ;
/* printk(KERN_INFO "sscape: dma released\n"); */
}
static int sscape_pnp_upload_file ( sscape_info * devc , char * fn )
{
int done = 0 ;
int timeout_val ;
char * data , * dt ;
int len , l ;
unsigned long flags ;
sscape_write ( devc , 9 , sscape_read ( devc , 9 ) & 0x3F ) ;
sscape_write ( devc , 2 , ( devc - > dma < < 4 ) | 0x80 ) ;
sscape_write ( devc , 3 , 0x20 ) ;
sscape_write ( devc , 9 , sscape_read ( devc , 9 ) | 0x80 ) ;
len = mod_firmware_load ( fn , & data ) ;
if ( len = = 0 ) {
printk ( KERN_ERR " sscape: file not found: %s \n " , fn ) ;
return 0 ;
}
dt = data ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
while ( len > 0 ) {
if ( len > devc - > buffsize ) l = devc - > buffsize ;
else l = len ;
len - = l ;
memcpy ( devc - > raw_buf , dt , l ) ; dt + = l ;
sscape_start_dma ( devc - > dma , devc - > raw_buf_phys , l , 0x48 ) ;
sscape_pnp_start_dma ( devc , 0 ) ;
if ( sscape_pnp_wait_dma ( devc , 0 ) = = 0 ) {
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
return 0 ;
}
}
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
vfree ( data ) ;
outb ( 0 , devc - > base + 2 ) ;
outb ( 0 , devc - > base ) ;
sscape_write ( devc , 9 , sscape_read ( devc , 9 ) | 0x40 ) ;
timeout_val = 5 * HZ ;
while ( ! done & & timeout_val - - > 0 )
{
unsigned char x ;
sleep ( 1 ) ;
x = inb ( devc - > base + 3 ) ;
if ( x = = 0xff | | x = = 0xfe ) /* OBP startup acknowledge */
{
//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
done = 1 ;
}
}
timeout_val = 5 * HZ ;
done = 0 ;
while ( ! done & & timeout_val - - > 0 )
{
unsigned char x ;
sleep ( 1 ) ;
x = inb ( devc - > base + 3 ) ;
if ( x = = 0xfe ) /* OBP startup acknowledge */
{
//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
done = 1 ;
}
}
if ( ! done ) printk ( KERN_ERR " soundscape: OBP Initialization failed. \n " ) ;
sscape_write ( devc , 2 , devc - > ic_type = = IC_ODIE ? 0x70 : 0x40 ) ;
sscape_write ( devc , 3 , ( devc - > dma < < 4 ) + 0x80 ) ;
return 1 ;
}
static void __init sscape_pnp_init_hw ( sscape_info * devc )
{
unsigned char midi_irq = 0 , sb_irq = 0 ;
unsigned i ;
static char code_file_name [ 23 ] = " /sndscape/sndscape.cox " ;
int sscape_joystic_enable = 0x7f ;
int sscape_mic_enable = 0 ;
int sscape_ext_midi = 0 ;
if ( ! sscape_pnp_alloc_dma ( devc ) ) {
printk ( KERN_ERR " sscape: faild to allocate dma \n " ) ;
return ;
}
for ( i = 0 ; i < 4 ; i + + ) {
if ( devc - > irq = = valid_interrupts [ i ] )
midi_irq = i ;
if ( devc - > codec_irq = = valid_interrupts [ i ] )
sb_irq = i ;
}
sscape_write ( devc , 5 , 0x50 ) ;
sscape_write ( devc , 7 , 0x2e ) ;
sscape_write ( devc , 8 , 0x00 ) ;
sscape_write ( devc , 2 , devc - > ic_type = = IC_ODIE ? 0x70 : 0x40 ) ;
sscape_write ( devc , 3 , ( devc - > dma < < 4 ) | 0x80 ) ;
2005-06-26 01:59:02 +04:00
sscape_write ( devc , 4 , 0xF0 | ( midi_irq < < 2 ) | midi_irq ) ;
2005-04-17 02:20:36 +04:00
i = 0x10 ; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
if ( sscape_joystic_enable ) i | = 8 ;
sscape_write ( devc , 9 , i ) ;
sscape_write ( devc , 6 , 0x80 ) ;
sscape_write ( devc , 1 , 0x80 ) ;
if ( devc - > codec_type = = 2 ) {
sscape_pnp_write_codec ( devc , 0x0C , 0x50 ) ;
sscape_pnp_write_codec ( devc , 0x10 , sscape_pnp_read_codec ( devc , 0x10 ) & 0x3F ) ;
sscape_pnp_write_codec ( devc , 0x11 , sscape_pnp_read_codec ( devc , 0x11 ) | 0xC0 ) ;
sscape_pnp_write_codec ( devc , 29 , 0x20 ) ;
}
if ( sscape_pnp_upload_file ( devc , " /sndscape/scope.cod " ) = = 0 ) {
printk ( KERN_ERR " sscape: faild to upload file /sndscape/scope.cod \n " ) ;
sscape_pnp_free_dma ( devc ) ;
return ;
}
i = sscape_read_host_ctrl ( devc ) ;
if ( ( i & 0x0F ) > 7 ) {
printk ( KERN_ERR " sscape: scope.cod faild \n " ) ;
sscape_pnp_free_dma ( devc ) ;
return ;
}
if ( i & 0x10 ) sscape_write ( devc , 7 , 0x2F ) ;
code_file_name [ 21 ] = ( char ) ( i & 0x0F ) + 0x30 ;
if ( sscape_pnp_upload_file ( devc , code_file_name ) = = 0 ) {
printk ( KERN_ERR " sscape: faild to upload file %s \n " , code_file_name ) ;
sscape_pnp_free_dma ( devc ) ;
return ;
}
if ( devc - > ic_type ! = IC_ODIE ) {
sscape_pnp_write_codec ( devc , 10 , ( sscape_pnp_read_codec ( devc , 10 ) & 0x7f ) |
( sscape_mic_enable = = 0 ? 0x00 : 0x80 ) ) ;
}
sscape_write_host_ctrl2 ( devc , 0x84 , 0x64 ) ; /* MIDI volume */
sscape_write_host_ctrl2 ( devc , 0x86 , 0x64 ) ; /* MIDI volume?? */
sscape_write_host_ctrl2 ( devc , 0x8A , sscape_ext_midi ) ;
sscape_pnp_write_codec ( devc , 6 , 0x3f ) ; //WAV_VOL
sscape_pnp_write_codec ( devc , 7 , 0x3f ) ; //WAV_VOL
sscape_pnp_write_codec ( devc , 2 , 0x1F ) ; //WD_CDXVOLL
sscape_pnp_write_codec ( devc , 3 , 0x1F ) ; //WD_CDXVOLR
if ( devc - > codec_type = = 1 ) {
sscape_pnp_write_codec ( devc , 4 , 0x1F ) ;
sscape_pnp_write_codec ( devc , 5 , 0x1F ) ;
sscape_write_host_ctrl2 ( devc , 0x88 , sscape_mic_enable ) ;
} else {
int t ;
sscape_pnp_write_codec ( devc , 0x10 , 0x1F < < 1 ) ;
sscape_pnp_write_codec ( devc , 0x11 , 0xC0 | ( 0x1F < < 1 ) ) ;
t = sscape_pnp_read_codec ( devc , 0x00 ) & 0xDF ;
if ( ( sscape_mic_enable = = 0 ) ) t | = 0 ;
else t | = 0x20 ;
sscape_pnp_write_codec ( devc , 0x00 , t ) ;
t = sscape_pnp_read_codec ( devc , 0x01 ) & 0xDF ;
if ( ( sscape_mic_enable = = 0 ) ) t | = 0 ;
else t | = 0x20 ;
sscape_pnp_write_codec ( devc , 0x01 , t ) ;
sscape_pnp_write_codec ( devc , 0x40 | 29 , 0x20 ) ;
outb ( 0 , devc - > codec ) ;
}
if ( devc - > ic_type = = IC_OPUS ) {
int i = sscape_read ( devc , 9 ) ;
sscape_write ( devc , 9 , i | 3 ) ;
sscape_write ( devc , 3 , 0x40 ) ;
if ( request_region ( 0x228 , 1 , " sscape setup junk " ) ) {
outb ( 0 , 0x228 ) ;
release_region ( 0x228 , 1 ) ;
}
sscape_write ( devc , 3 , ( devc - > dma < < 4 ) | 0x80 ) ;
sscape_write ( devc , 9 , i ) ;
}
host_close ( devc ) ;
sscape_pnp_free_dma ( devc ) ;
}
static int __init detect_sscape_pnp ( sscape_info * devc )
{
long i , irq_bits = 0xff ;
unsigned int d ;
DDB ( printk ( " Entered detect_sscape_pnp(%x) \n " , devc - > base ) ) ;
if ( ! request_region ( devc - > codec , 2 , " sscape codec " ) ) {
printk ( KERN_ERR " detect_sscape_pnp: port %x is not free \n " , devc - > codec ) ;
return 0 ;
}
if ( ( inb ( devc - > base + 2 ) & 0x78 ) ! = 0 )
goto fail ;
d = inb ( devc - > base + 4 ) & 0xF0 ;
if ( d & 0x80 )
goto fail ;
if ( d = = 0 ) {
devc - > codec_type = 1 ;
devc - > ic_type = IC_ODIE ;
} else if ( ( d & 0x60 ) ! = 0 ) {
devc - > codec_type = 2 ;
devc - > ic_type = IC_OPUS ;
} else if ( ( d & 0x40 ) ! = 0 ) { /* WTF? */
devc - > codec_type = 2 ;
devc - > ic_type = IC_ODIE ;
} else
goto fail ;
sscape_is_pnp = 1 ;
outb ( 0xFA , devc - > base + 4 ) ;
if ( ( inb ( devc - > base + 4 ) & 0x9F ) ! = 0x0A )
goto fail ;
outb ( 0xFE , devc - > base + 4 ) ;
if ( ( inb ( devc - > base + 4 ) & 0x9F ) ! = 0x0E )
goto fail ;
if ( ( inb ( devc - > base + 5 ) & 0x9F ) ! = 0x0E )
goto fail ;
if ( devc - > codec_type = = 2 ) {
if ( devc - > codec ! = devc - > base + 8 ) {
printk ( " soundscape warning: incorrect codec port specified \n " ) ;
goto fail ;
}
d = 0x10 | ( sscape_read ( devc , 9 ) & 0xCF ) ;
sscape_write ( devc , 9 , d ) ;
sscape_write ( devc , 6 , 0x80 ) ;
} else {
//todo: check codec is not base + 8
}
d = ( sscape_read ( devc , 9 ) & 0x3F ) | 0xC0 ;
sscape_write ( devc , 9 , d ) ;
for ( i = 0 ; i < 550000 ; i + + )
if ( ! ( inb ( devc - > codec ) & 0x80 ) ) break ;
d = inb ( devc - > codec ) ;
if ( d & 0x80 )
goto fail ;
if ( inb ( devc - > codec + 2 ) = = 0xFF )
goto fail ;
sscape_write ( devc , 9 , sscape_read ( devc , 9 ) & 0x3F ) ;
d = inb ( devc - > codec ) & 0x80 ;
if ( d = = 0 ) {
printk ( KERN_INFO " soundscape: hardware detected \n " ) ;
valid_interrupts = valid_interrupts_new ;
} else {
printk ( KERN_INFO " soundscape: board looks like media fx \n " ) ;
valid_interrupts = valid_interrupts_old ;
old_hardware = 1 ;
}
sscape_write ( devc , 9 , 0xC0 | ( sscape_read ( devc , 9 ) & 0x3F ) ) ;
for ( i = 0 ; i < 550000 ; i + + )
if ( ! ( inb ( devc - > codec ) & 0x80 ) )
break ;
sscape_pnp_init_hw ( devc ) ;
for ( i = 0 ; i < 4 ; i + + )
{
if ( devc - > codec_irq = = valid_interrupts [ i ] ) {
irq_bits = i ;
break ;
}
}
sscape_write ( devc , GA_INTENA_REG , 0x00 ) ;
sscape_write ( devc , GA_DMACFG_REG , 0x50 ) ;
sscape_write ( devc , GA_DMAA_REG , 0x70 ) ;
sscape_write ( devc , GA_DMAB_REG , 0x20 ) ;
sscape_write ( devc , GA_INTCFG_REG , 0xf0 ) ;
sscape_write ( devc , GA_CDCFG_REG , 0x89 | ( devc - > dma < < 4 ) | ( irq_bits < < 1 ) ) ;
sscape_pnp_write_codec ( devc , 0 , sscape_pnp_read_codec ( devc , 0 ) | 0x20 ) ;
sscape_pnp_write_codec ( devc , 0 , sscape_pnp_read_codec ( devc , 1 ) | 0x20 ) ;
return 1 ;
fail :
release_region ( devc - > codec , 2 ) ;
return 0 ;
}
static int __init probe_sscape ( struct address_info * hw_config )
{
devc - > base = hw_config - > io_base ;
devc - > irq = hw_config - > irq ;
devc - > dma = hw_config - > dma ;
devc - > osp = hw_config - > osp ;
# ifdef SSCAPE_DEBUG1
/*
* Temporary debugging aid . Print contents of the registers before
* changing them .
*/
{
int i ;
for ( i = 0 ; i < 13 ; i + + )
printk ( " I%d = %02x (old value) \n " , i , sscape_read ( devc , i ) ) ;
}
# endif
devc - > failed = 1 ;
sscape_ports = request_region ( devc - > base , 2 , " mpu401 " ) ;
if ( ! sscape_ports )
return 0 ;
if ( ! request_region ( devc - > base + 2 , 6 , " SoundScape " ) ) {
release_region ( devc - > base , 2 ) ;
return 0 ;
}
if ( ! detect_ga ( devc ) ) {
if ( detect_sscape_pnp ( devc ) )
return 1 ;
release_region ( devc - > base , 2 ) ;
release_region ( devc - > base + 2 , 6 ) ;
return 0 ;
}
if ( old_hardware ) /* Check that it's really an old Spea/Reveal card. */
{
unsigned char tmp ;
int cc ;
if ( ! ( ( tmp = sscape_read ( devc , GA_HMCTL_REG ) ) & 0xc0 ) )
{
sscape_write ( devc , GA_HMCTL_REG , tmp | 0x80 ) ;
for ( cc = 0 ; cc < 200000 ; + + cc )
inb ( devc - > base + ODIE_ADDR ) ;
}
}
return 1 ;
}
static int __init init_ss_ms_sound ( struct address_info * hw_config )
{
int i , irq_bits = 0xff ;
int ad_flags = 0 ;
struct resource * ports ;
if ( devc - > failed )
{
printk ( KERN_ERR " soundscape: Card not detected \n " ) ;
return 0 ;
}
if ( devc - > ok = = 0 )
{
printk ( KERN_ERR " soundscape: Invalid initialization order. \n " ) ;
return 0 ;
}
for ( i = 0 ; i < 4 ; i + + )
{
if ( hw_config - > irq = = valid_interrupts [ i ] )
{
irq_bits = i ;
break ;
}
}
if ( irq_bits = = 0xff ) {
printk ( KERN_ERR " soundscape: Invalid MSS IRQ%d \n " , hw_config - > irq ) ;
return 0 ;
}
if ( old_hardware )
ad_flags = 0x12345677 ; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
else if ( sscape_is_pnp )
ad_flags = 0x87654321 ; /* Tell that we have a soundscape pnp with 1845 chip */
ports = request_region ( hw_config - > io_base , 4 , " ad1848 " ) ;
if ( ! ports ) {
printk ( KERN_ERR " soundscape: ports busy \n " ) ;
return 0 ;
}
if ( ! ad1848_detect ( ports , & ad_flags , hw_config - > osp ) ) {
release_region ( hw_config - > io_base , 4 ) ;
return 0 ;
}
if ( ! sscape_is_pnp ) /*pnp is already setup*/
{
/*
* Setup the DMA polarity .
*/
sscape_write ( devc , GA_DMACFG_REG , 0x50 ) ;
/*
* Take the gate - array off of the DMA channel .
*/
sscape_write ( devc , GA_DMAB_REG , 0x20 ) ;
/*
* Init the AD1848 ( CD - ROM ) config reg .
*/
sscape_write ( devc , GA_CDCFG_REG , 0x89 | ( hw_config - > dma < < 4 ) | ( irq_bits < < 1 ) ) ;
}
if ( hw_config - > irq = = devc - > irq )
printk ( KERN_WARNING " soundscape: Warning! The WSS mode can't share IRQ with MIDI \n " ) ;
hw_config - > slots [ 0 ] = ad1848_init (
sscape_is_pnp ? " SoundScape " : " SoundScape PNP " ,
ports ,
hw_config - > irq ,
hw_config - > dma ,
hw_config - > dma ,
0 ,
devc - > osp ,
THIS_MODULE ) ;
if ( hw_config - > slots [ 0 ] ! = - 1 ) /* The AD1848 driver installed itself */
{
audio_devs [ hw_config - > slots [ 0 ] ] - > coproc = & sscape_coproc_operations ;
devc - > codec_audiodev = hw_config - > slots [ 0 ] ;
devc - > my_audiodev = hw_config - > slots [ 0 ] ;
/* Set proper routings here (what are they) */
AD1848_REROUTE ( SOUND_MIXER_LINE1 , SOUND_MIXER_LINE ) ;
}
# ifdef SSCAPE_DEBUG5
/*
* Temporary debugging aid . Print contents of the registers
* after the AD1848 device has been initialized .
*/
{
int i ;
for ( i = 0 ; i < 13 ; i + + )
printk ( " I%d = %02x \n " , i , sscape_read ( devc , i ) ) ;
}
# endif
return 1 ;
}
static void __exit unload_sscape ( struct address_info * hw_config )
{
release_region ( devc - > base + 2 , 6 ) ;
unload_mpu401 ( hw_config ) ;
if ( sscape_is_pnp )
release_region ( devc - > codec , 2 ) ;
}
static void __exit unload_ss_ms_sound ( struct address_info * hw_config )
{
ad1848_unload ( hw_config - > io_base ,
hw_config - > irq ,
devc - > dma ,
devc - > dma ,
0 ) ;
sound_unload_audiodev ( hw_config - > slots [ 0 ] ) ;
}
static struct address_info cfg ;
static struct address_info cfg_mpu ;
static int __initdata spea = - 1 ;
static int mss = 0 ;
static int __initdata dma = - 1 ;
static int __initdata irq = - 1 ;
static int __initdata io = - 1 ;
static int __initdata mpu_irq = - 1 ;
static int __initdata mpu_io = - 1 ;
module_param ( dma , int , 0 ) ;
module_param ( irq , int , 0 ) ;
module_param ( io , int , 0 ) ;
module_param ( spea , int , 0 ) ; /* spea=0/1 set the old_hardware */
module_param ( mpu_irq , int , 0 ) ;
module_param ( mpu_io , int , 0 ) ;
module_param ( mss , int , 0 ) ;
static int __init init_sscape ( void )
{
printk ( KERN_INFO " Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996 \n " ) ;
cfg . irq = irq ;
cfg . dma = dma ;
cfg . io_base = io ;
cfg_mpu . irq = mpu_irq ;
cfg_mpu . io_base = mpu_io ;
/* WEH - Try to get right dma channel */
cfg_mpu . dma = dma ;
devc - > codec = cfg . io_base ;
devc - > codec_irq = cfg . irq ;
devc - > codec_type = 0 ;
devc - > ic_type = 0 ;
devc - > raw_buf = NULL ;
spin_lock_init ( & devc - > lock ) ;
if ( cfg . dma = = - 1 | | cfg . irq = = - 1 | | cfg . io_base = = - 1 ) {
printk ( KERN_ERR " DMA, IRQ, and IO port must be specified. \n " ) ;
return - EINVAL ;
}
if ( cfg_mpu . irq = = - 1 & & cfg_mpu . io_base ! = - 1 ) {
printk ( KERN_ERR " MPU_IRQ must be specified if MPU_IO is set. \n " ) ;
return - EINVAL ;
}
if ( spea ! = - 1 ) {
old_hardware = spea ;
printk ( KERN_INFO " Forcing %s hardware support. \n " ,
spea ? " new " : " old " ) ;
}
if ( probe_sscape ( & cfg_mpu ) = = 0 )
return - ENODEV ;
attach_sscape ( & cfg_mpu ) ;
mss = init_ss_ms_sound ( & cfg ) ;
return 0 ;
}
static void __exit cleanup_sscape ( void )
{
if ( mss )
unload_ss_ms_sound ( & cfg ) ;
unload_sscape ( & cfg_mpu ) ;
}
module_init ( init_sscape ) ;
module_exit ( cleanup_sscape ) ;
# ifndef MODULE
static int __init setup_sscape ( char * str )
{
/* io, irq, dma, mpu_io, mpu_irq */
int ints [ 6 ] ;
str = get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
io = ints [ 1 ] ;
irq = ints [ 2 ] ;
dma = ints [ 3 ] ;
mpu_io = ints [ 4 ] ;
mpu_irq = ints [ 5 ] ;
return 1 ;
}
__setup ( " sscape= " , setup_sscape ) ;
# endif
MODULE_LICENSE ( " GPL " ) ;