2005-04-17 02:20:36 +04:00
/*
* Low - level ALSA driver for the ENSONIQ SoundScape PnP
* Copyright ( c ) by Chris Rankin
*
* This driver was written in part using information obtained from
* the OSS / Free SoundScape driver , written by Hannu Savolainen .
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <sound/driver.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/pnp.h>
# include <linux/spinlock.h>
# include <linux/moduleparam.h>
# include <asm/dma.h>
# include <sound/core.h>
# include <sound/hwdep.h>
# include <sound/cs4231.h>
# include <sound/mpu401.h>
# include <sound/initval.h>
# include <sound/sscape_ioctl.h>
MODULE_AUTHOR ( " Chris Rankin " ) ;
MODULE_DESCRIPTION ( " ENSONIQ SoundScape PnP driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int index [ SNDRV_CARDS ] __devinitdata = SNDRV_DEFAULT_IDX ;
static char * id [ SNDRV_CARDS ] __devinitdata = SNDRV_DEFAULT_STR ;
static long port [ SNDRV_CARDS ] __devinitdata = { [ 0 . . . ( SNDRV_CARDS - 1 ) ] = SNDRV_AUTO_PORT } ;
static int irq [ SNDRV_CARDS ] __devinitdata = SNDRV_DEFAULT_IRQ ;
static int mpu_irq [ SNDRV_CARDS ] __devinitdata = SNDRV_DEFAULT_IRQ ;
static int dma [ SNDRV_CARDS ] __devinitdata = SNDRV_DEFAULT_DMA ;
module_param_array ( index , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( index , " Index number for SoundScape soundcard " ) ;
module_param_array ( id , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( id , " Description for SoundScape card " ) ;
module_param_array ( port , long , NULL , 0444 ) ;
MODULE_PARM_DESC ( port , " Port # for SoundScape driver. " ) ;
module_param_array ( irq , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( irq , " IRQ # for SoundScape driver. " ) ;
module_param_array ( mpu_irq , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( mpu_irq , " MPU401 IRQ # for SoundScape driver. " ) ;
module_param_array ( dma , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( dma , " DMA # for SoundScape driver. " ) ;
# ifdef CONFIG_PNP
static struct pnp_card_device_id sscape_pnpids [ ] = {
{ . id = " ENS3081 " , . devs = { { " ENS0000 " } } } ,
{ . id = " " } /* end */
} ;
MODULE_DEVICE_TABLE ( pnp_card , sscape_pnpids ) ;
# endif
static snd_card_t * sscape_card [ SNDRV_CARDS ] ;
# define MPU401_IO(i) ((i) + 0)
# define MIDI_DATA_IO(i) ((i) + 0)
# define MIDI_CTRL_IO(i) ((i) + 1)
# define HOST_CTRL_IO(i) ((i) + 2)
# define HOST_DATA_IO(i) ((i) + 3)
# define ODIE_ADDR_IO(i) ((i) + 4)
# define ODIE_DATA_IO(i) ((i) + 5)
# define CODEC_IO(i) ((i) + 8)
# define IC_ODIE 1
# define IC_OPUS 2
# define RX_READY 0x01
# define TX_READY 0x02
# define CMD_ACK 0x80
# define CMD_SET_MIDI_VOL 0x84
# define CMD_GET_MIDI_VOL 0x85
# define CMD_XXX_MIDI_VOL 0x86
# define CMD_SET_EXTMIDI 0x8a
# define CMD_GET_EXTMIDI 0x8b
# define CMD_SET_MT32 0x8c
# define CMD_GET_MT32 0x8d
enum GA_REG {
GA_INTSTAT_REG = 0 ,
GA_INTENA_REG ,
GA_DMAA_REG ,
GA_DMAB_REG ,
GA_INTCFG_REG ,
GA_DMACFG_REG ,
GA_CDCFG_REG ,
GA_SMCFGA_REG ,
GA_SMCFGB_REG ,
GA_HMCTL_REG
} ;
# define DMA_8BIT 0x80
# define AD1845_FREQ_SEL_MSB 0x16
# define AD1845_FREQ_SEL_LSB 0x17
struct soundscape {
spinlock_t lock ;
unsigned io_base ;
int codec_type ;
int ic_type ;
struct resource * io_res ;
cs4231_t * chip ;
mpu401_t * mpu ;
snd_hwdep_t * hw ;
/*
* The MIDI device won ' t work until we ' ve loaded
* its firmware via a hardware - dependent device IOCTL
*/
spinlock_t fwlock ;
int hw_in_use ;
unsigned long midi_usage ;
unsigned char midi_vol ;
} ;
# define INVALID_IRQ ((unsigned)-1)
static inline struct soundscape * get_card_soundscape ( snd_card_t * c )
{
return ( struct soundscape * ) ( c - > private_data ) ;
}
static inline struct soundscape * get_mpu401_soundscape ( mpu401_t * mpu )
{
return ( struct soundscape * ) ( mpu - > private_data ) ;
}
static inline struct soundscape * get_hwdep_soundscape ( snd_hwdep_t * hw )
{
return ( struct soundscape * ) ( hw - > private_data ) ;
}
/*
* Allocates some kernel memory that we can use for DMA .
* I think this means that the memory has to map to
* contiguous pages of physical memory .
*/
static struct snd_dma_buffer * get_dmabuf ( struct snd_dma_buffer * buf , unsigned long size )
{
if ( buf ) {
if ( snd_dma_alloc_pages_fallback ( SNDRV_DMA_TYPE_DEV , snd_dma_isa_data ( ) ,
size , buf ) < 0 ) {
snd_printk ( KERN_ERR " sscape: Failed to allocate %lu bytes for DMA \n " , size ) ;
return NULL ;
}
}
return buf ;
}
/*
* Release the DMA - able kernel memory . . .
*/
static void free_dmabuf ( struct snd_dma_buffer * buf )
{
if ( buf & & buf - > area )
snd_dma_free_pages ( buf ) ;
}
/*
* This function writes to the SoundScape ' s control registers ,
* but doesn ' t do any locking . It ' s up to the caller to do that .
* This is why this function is " unsafe " . . .
*/
static inline void sscape_write_unsafe ( unsigned io_base , enum GA_REG reg , unsigned char val )
{
outb ( reg , ODIE_ADDR_IO ( io_base ) ) ;
outb ( val , ODIE_DATA_IO ( io_base ) ) ;
}
/*
* Write to the SoundScape ' s control registers , and do the
* necessary locking . . .
*/
static void sscape_write ( struct soundscape * s , enum GA_REG reg , unsigned char val )
{
unsigned long flags ;
spin_lock_irqsave ( & s - > lock , flags ) ;
sscape_write_unsafe ( s - > io_base , reg , val ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
}
/*
* Read from the SoundScape ' s control registers , but leave any
* locking to the caller . This is why the function is " unsafe " . . .
*/
static inline unsigned char sscape_read_unsafe ( unsigned io_base , enum GA_REG reg )
{
outb ( reg , ODIE_ADDR_IO ( io_base ) ) ;
return inb ( ODIE_DATA_IO ( io_base ) ) ;
}
/*
* Puts the SoundScape into " host " mode , as compared to " MIDI " mode
*/
static inline void set_host_mode_unsafe ( unsigned io_base )
{
outb ( 0x0 , HOST_CTRL_IO ( io_base ) ) ;
}
/*
* Puts the SoundScape into " MIDI " mode , as compared to " host " mode
*/
static inline void set_midi_mode_unsafe ( unsigned io_base )
{
outb ( 0x3 , HOST_CTRL_IO ( io_base ) ) ;
}
/*
* Read the SoundScape ' s host - mode control register , but leave
* any locking issues to the caller . . .
*/
static inline int host_read_unsafe ( unsigned io_base )
{
int data = - 1 ;
if ( ( inb ( HOST_CTRL_IO ( io_base ) ) & RX_READY ) ! = 0 ) {
data = inb ( HOST_DATA_IO ( io_base ) ) ;
}
return data ;
}
/*
* Read the SoundScape ' s host - mode control register , performing
* a limited amount of busy - waiting if the register isn ' t ready .
* Also leaves all locking - issues to the caller . . .
*/
static int host_read_ctrl_unsafe ( unsigned io_base , unsigned timeout )
{
int data ;
while ( ( ( data = host_read_unsafe ( io_base ) ) < 0 ) & & ( timeout ! = 0 ) ) {
udelay ( 100 ) ;
- - timeout ;
} /* while */
return data ;
}
/*
* Write to the SoundScape ' s host - mode control registers , but
* leave any locking issues to the caller . . .
*/
static inline int host_write_unsafe ( unsigned io_base , unsigned char data )
{
if ( ( inb ( HOST_CTRL_IO ( io_base ) ) & TX_READY ) ! = 0 ) {
outb ( data , HOST_DATA_IO ( io_base ) ) ;
return 1 ;
}
return 0 ;
}
/*
* Write to the SoundScape ' s host - mode control registers , performing
* a limited amount of busy - waiting if the register isn ' t ready .
* Also leaves all locking - issues to the caller . . .
*/
static int host_write_ctrl_unsafe ( unsigned io_base , unsigned char data ,
unsigned timeout )
{
int err ;
while ( ! ( err = host_write_unsafe ( io_base , data ) ) & & ( timeout ! = 0 ) ) {
udelay ( 100 ) ;
- - timeout ;
} /* while */
return err ;
}
/*
* Check that the MIDI subsystem is operational . If it isn ' t ,
* then we will hang the computer if we try to use it . . .
*
* NOTE : This check is based upon observation , not documentation .
*/
static inline int verify_mpu401 ( const mpu401_t * mpu )
{
return ( ( inb ( MIDI_CTRL_IO ( mpu - > port ) ) & 0xc0 ) = = 0x80 ) ;
}
/*
* This is apparently the standard way to initailise an MPU - 401
*/
static inline void initialise_mpu401 ( const mpu401_t * mpu )
{
outb ( 0 , MIDI_DATA_IO ( mpu - > port ) ) ;
}
/*
* Tell the SoundScape to activate the AD1845 chip ( I think ) .
* The AD1845 detection fails if we * don ' t * do this , so I
* think that this is a good idea . . .
*/
static inline void activate_ad1845_unsafe ( unsigned io_base )
{
sscape_write_unsafe ( io_base , GA_HMCTL_REG , ( sscape_read_unsafe ( io_base , GA_HMCTL_REG ) & 0xcf ) | 0x10 ) ;
sscape_write_unsafe ( io_base , GA_CDCFG_REG , 0x80 ) ;
}
/*
* Do the necessary ALSA - level cleanup to deallocate our driver . . .
*/
static void soundscape_free ( snd_card_t * c )
{
register struct soundscape * sscape = get_card_soundscape ( c ) ;
2005-10-10 13:56:31 +04:00
release_and_free_resource ( sscape - > io_res ) ;
2005-04-17 02:20:36 +04:00
free_dma ( sscape - > chip - > dma1 ) ;
}
/*
* Tell the SoundScape to begin a DMA tranfer using the given channel .
* All locking issues are left to the caller .
*/
static inline void sscape_start_dma_unsafe ( unsigned io_base , enum GA_REG reg )
{
sscape_write_unsafe ( io_base , reg , sscape_read_unsafe ( io_base , reg ) | 0x01 ) ;
sscape_write_unsafe ( io_base , reg , sscape_read_unsafe ( io_base , reg ) & 0xfe ) ;
}
/*
* Wait for a DMA transfer to complete . This is a " limited busy-wait " ,
* and all locking issues are left to the caller .
*/
static int sscape_wait_dma_unsafe ( unsigned io_base , enum GA_REG reg , unsigned timeout )
{
while ( ! ( sscape_read_unsafe ( io_base , reg ) & 0x01 ) & & ( timeout ! = 0 ) ) {
udelay ( 100 ) ;
- - timeout ;
} /* while */
return ( sscape_read_unsafe ( io_base , reg ) & 0x01 ) ;
}
/*
* Wait for the On - Board Processor to return its start - up
* acknowledgement sequence . This wait is too long for
* us to perform " busy-waiting " , and so we must sleep .
* This in turn means that we must not be holding any
* spinlocks when we call this function .
*/
static int obp_startup_ack ( struct soundscape * s , unsigned timeout )
{
while ( timeout ! = 0 ) {
unsigned long flags ;
unsigned char x ;
2005-10-24 17:02:37 +04:00
schedule_timeout_interruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
x = inb ( HOST_DATA_IO ( s - > io_base ) ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
if ( ( x & 0xfe ) = = 0xfe )
return 1 ;
- - timeout ;
} /* while */
return 0 ;
}
/*
* Wait for the host to return its start - up acknowledgement
* sequence . This wait is too long for us to perform
* " busy-waiting " , and so we must sleep . This in turn means
* that we must not be holding any spinlocks when we call
* this function .
*/
static int host_startup_ack ( struct soundscape * s , unsigned timeout )
{
while ( timeout ! = 0 ) {
unsigned long flags ;
unsigned char x ;
2005-10-24 17:02:37 +04:00
schedule_timeout_interruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
x = inb ( HOST_DATA_IO ( s - > io_base ) ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
if ( x = = 0xfe )
return 1 ;
- - timeout ;
} /* while */
return 0 ;
}
/*
* Upload a byte - stream into the SoundScape using DMA channel A .
*/
static int upload_dma_data ( struct soundscape * s ,
const unsigned char __user * data ,
size_t size )
{
unsigned long flags ;
struct snd_dma_buffer dma ;
int ret ;
if ( ! get_dmabuf ( & dma , PAGE_ALIGN ( size ) ) )
return - ENOMEM ;
spin_lock_irqsave ( & s - > lock , flags ) ;
/*
* Reset the board . . .
*/
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) & 0x3f ) ;
/*
* Enable the DMA channels and configure them . . .
*/
sscape_write_unsafe ( s - > io_base , GA_DMACFG_REG , 0x50 ) ;
sscape_write_unsafe ( s - > io_base , GA_DMAA_REG , ( s - > chip - > dma1 < < 4 ) | DMA_8BIT ) ;
sscape_write_unsafe ( s - > io_base , GA_DMAB_REG , 0x20 ) ;
/*
* Take the board out of reset . . .
*/
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) | 0x80 ) ;
/*
* Upload the user ' s data ( firmware ? ) to the SoundScape
* board through the DMA channel . . .
*/
while ( size ! = 0 ) {
unsigned long len ;
/*
* Apparently , copying to / from userspace can sleep .
* We are therefore forbidden from holding any
* spinlocks while we copy . . .
*/
spin_unlock_irqrestore ( & s - > lock , flags ) ;
/*
* Remember that the data that we want to DMA
* comes from USERSPACE . We have already verified
* the userspace pointer . . .
*/
len = min ( size , dma . bytes ) ;
len - = __copy_from_user ( dma . area , data , len ) ;
data + = len ;
size - = len ;
/*
* Grab that spinlock again , now that we ' ve
* finished copying !
*/
spin_lock_irqsave ( & s - > lock , flags ) ;
snd_dma_program ( s - > chip - > dma1 , dma . addr , len , DMA_MODE_WRITE ) ;
sscape_start_dma_unsafe ( s - > io_base , GA_DMAA_REG ) ;
if ( ! sscape_wait_dma_unsafe ( s - > io_base , GA_DMAA_REG , 5000 ) ) {
/*
* Don ' t forget to release this spinlock we ' re holding . . .
*/
spin_unlock_irqrestore ( & s - > lock , flags ) ;
snd_printk ( KERN_ERR " sscape: DMA upload has timed out \n " ) ;
ret = - EAGAIN ;
goto _release_dma ;
}
} /* while */
set_host_mode_unsafe ( s - > io_base ) ;
/*
* Boot the board . . . ( I think )
*/
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) | 0x40 ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
/*
* If all has gone well , then the board should acknowledge
* the new upload and tell us that it has rebooted OK . We
* give it 5 seconds ( max ) . . .
*/
ret = 0 ;
if ( ! obp_startup_ack ( s , 5 ) ) {
snd_printk ( KERN_ERR " sscape: No response from on-board processor after upload \n " ) ;
ret = - EAGAIN ;
} else if ( ! host_startup_ack ( s , 5 ) ) {
snd_printk ( KERN_ERR " sscape: SoundScape failed to initialise \n " ) ;
ret = - EAGAIN ;
}
_release_dma :
/*
* NOTE ! ! ! We are NOT holding any spinlocks at this point ! ! !
*/
sscape_write ( s , GA_DMAA_REG , ( s - > ic_type = = IC_ODIE ? 0x70 : 0x40 ) ) ;
free_dmabuf ( & dma ) ;
return ret ;
}
/*
* Upload the bootblock ( ? ) into the SoundScape . The only
* purpose of this block of code seems to be to tell
* us which version of the microcode we should be using .
*
* NOTE : The boot - block data resides in USER - SPACE ! ! !
* However , we have already verified its memory
* addresses by the time we get here .
*/
static int sscape_upload_bootblock ( struct soundscape * sscape , struct sscape_bootblock __user * bb )
{
unsigned long flags ;
int data = 0 ;
int ret ;
ret = upload_dma_data ( sscape , bb - > code , sizeof ( bb - > code ) ) ;
spin_lock_irqsave ( & sscape - > lock , flags ) ;
if ( ret = = 0 ) {
data = host_read_ctrl_unsafe ( sscape - > io_base , 100 ) ;
}
set_midi_mode_unsafe ( sscape - > io_base ) ;
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
if ( ret = = 0 ) {
if ( data < 0 ) {
snd_printk ( KERN_ERR " sscape: timeout reading firmware version \n " ) ;
ret = - EAGAIN ;
}
else if ( __copy_to_user ( & bb - > version , & data , sizeof ( bb - > version ) ) ) {
ret = - EFAULT ;
}
}
return ret ;
}
/*
* Upload the microcode into the SoundScape . The
* microcode is 64 K of data , and if we try to copy
* it into a local variable then we will SMASH THE
* KERNEL ' S STACK ! We therefore leave it in USER
* SPACE , and save ourselves from copying it at all .
*/
static int sscape_upload_microcode ( struct soundscape * sscape ,
const struct sscape_microcode __user * mc )
{
unsigned long flags ;
char __user * code ;
int err ;
/*
* We are going to have to copy this data into a special
* DMA - able buffer before we can upload it . We shall therefore
* just check that the data pointer is valid for now .
*
* NOTE : This buffer is 64 K long ! That ' s WAY too big to
* copy into a stack - temporary anyway .
*/
if ( get_user ( code , & mc - > code ) | |
! access_ok ( VERIFY_READ , code , SSCAPE_MICROCODE_SIZE ) )
return - EFAULT ;
if ( ( err = upload_dma_data ( sscape , code , SSCAPE_MICROCODE_SIZE ) ) = = 0 ) {
snd_printk ( KERN_INFO " sscape: MIDI firmware loaded \n " ) ;
}
spin_lock_irqsave ( & sscape - > lock , flags ) ;
set_midi_mode_unsafe ( sscape - > io_base ) ;
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
initialise_mpu401 ( sscape - > mpu ) ;
return err ;
}
/*
* Hardware - specific device functions , to implement special
* IOCTLs for the SoundScape card . This is how we upload
* the microcode into the card , for example , and so we
* must ensure that no two processes can open this device
* simultaneously , and that we can ' t open it at all if
* someone is using the MIDI device .
*/
static int sscape_hw_open ( snd_hwdep_t * hw , struct file * file )
{
register struct soundscape * sscape = get_hwdep_soundscape ( hw ) ;
unsigned long flags ;
int err ;
spin_lock_irqsave ( & sscape - > fwlock , flags ) ;
if ( ( sscape - > midi_usage ! = 0 ) | | sscape - > hw_in_use ) {
err = - EBUSY ;
} else {
sscape - > hw_in_use = 1 ;
err = 0 ;
}
spin_unlock_irqrestore ( & sscape - > fwlock , flags ) ;
return err ;
}
static int sscape_hw_release ( snd_hwdep_t * hw , struct file * file )
{
register struct soundscape * sscape = get_hwdep_soundscape ( hw ) ;
unsigned long flags ;
spin_lock_irqsave ( & sscape - > fwlock , flags ) ;
sscape - > hw_in_use = 0 ;
spin_unlock_irqrestore ( & sscape - > fwlock , flags ) ;
return 0 ;
}
static int sscape_hw_ioctl ( snd_hwdep_t * hw , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct soundscape * sscape = get_hwdep_soundscape ( hw ) ;
int err = - EBUSY ;
switch ( cmd ) {
case SND_SSCAPE_LOAD_BOOTB :
{
register struct sscape_bootblock __user * bb = ( struct sscape_bootblock __user * ) arg ;
/*
* We are going to have to copy this data into a special
* DMA - able buffer before we can upload it . We shall therefore
* just check that the data pointer is valid for now . . .
*/
if ( ! access_ok ( VERIFY_READ , bb - > code , sizeof ( bb - > code ) ) )
return - EFAULT ;
/*
* Now check that we can write the firmware version number too . . .
*/
if ( ! access_ok ( VERIFY_WRITE , & bb - > version , sizeof ( bb - > version ) ) )
return - EFAULT ;
err = sscape_upload_bootblock ( sscape , bb ) ;
}
break ;
case SND_SSCAPE_LOAD_MCODE :
{
register const struct sscape_microcode __user * mc = ( const struct sscape_microcode __user * ) arg ;
err = sscape_upload_microcode ( sscape , mc ) ;
}
break ;
default :
err = - EINVAL ;
break ;
} /* switch */
return err ;
}
/*
* Mixer control for the SoundScape ' s MIDI device .
*/
static int sscape_midi_info ( snd_kcontrol_t * ctl ,
snd_ctl_elem_info_t * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 127 ;
return 0 ;
}
static int sscape_midi_get ( snd_kcontrol_t * kctl ,
snd_ctl_elem_value_t * uctl )
{
cs4231_t * chip = snd_kcontrol_chip ( kctl ) ;
snd_card_t * card = chip - > card ;
register struct soundscape * s = get_card_soundscape ( card ) ;
unsigned long flags ;
spin_lock_irqsave ( & s - > lock , flags ) ;
set_host_mode_unsafe ( s - > io_base ) ;
if ( host_write_ctrl_unsafe ( s - > io_base , CMD_GET_MIDI_VOL , 100 ) ) {
uctl - > value . integer . value [ 0 ] = host_read_ctrl_unsafe ( s - > io_base , 100 ) ;
}
set_midi_mode_unsafe ( s - > io_base ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
return 0 ;
}
static int sscape_midi_put ( snd_kcontrol_t * kctl ,
snd_ctl_elem_value_t * uctl )
{
cs4231_t * chip = snd_kcontrol_chip ( kctl ) ;
snd_card_t * card = chip - > card ;
register struct soundscape * s = get_card_soundscape ( card ) ;
unsigned long flags ;
int change ;
spin_lock_irqsave ( & s - > lock , flags ) ;
/*
* We need to put the board into HOST mode before we
* can send any volume - changing HOST commands . . .
*/
set_host_mode_unsafe ( s - > io_base ) ;
/*
* To successfully change the MIDI volume setting , you seem to
* have to write a volume command , write the new volume value ,
* and then perform another volume - related command . Perhaps the
* first command is an " open " and the second command is a " close " ?
*/
if ( s - > midi_vol = = ( ( unsigned char ) uctl - > value . integer . value [ 0 ] & 127 ) ) {
change = 0 ;
goto __skip_change ;
}
change = ( host_write_ctrl_unsafe ( s - > io_base , CMD_SET_MIDI_VOL , 100 )
& & host_write_ctrl_unsafe ( s - > io_base , ( ( unsigned char ) uctl - > value . integer . value [ 0 ] ) & 127 , 100 )
& & host_write_ctrl_unsafe ( s - > io_base , CMD_XXX_MIDI_VOL , 100 ) ) ;
__skip_change :
/*
* Take the board out of HOST mode and back into MIDI mode . . .
*/
set_midi_mode_unsafe ( s - > io_base ) ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
return change ;
}
static snd_kcontrol_new_t midi_mixer_ctl = {
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " MIDI " ,
. info = sscape_midi_info ,
. get = sscape_midi_get ,
. put = sscape_midi_put
} ;
/*
* The SoundScape can use two IRQs from a possible set of four .
* These IRQs are encoded as bit patterns so that they can be
* written to the control registers .
*/
static unsigned __devinit get_irq_config ( int irq )
{
static const int valid_irq [ ] = { 9 , 5 , 7 , 10 } ;
unsigned cfg ;
for ( cfg = 0 ; cfg < ARRAY_SIZE ( valid_irq ) ; + + cfg ) {
if ( irq = = valid_irq [ cfg ] )
return cfg ;
} /* for */
return INVALID_IRQ ;
}
/*
* Perform certain arcane port - checks to see whether there
* is a SoundScape board lurking behind the given ports .
*/
static int __devinit detect_sscape ( struct soundscape * s )
{
unsigned long flags ;
unsigned d ;
int retval = 0 ;
spin_lock_irqsave ( & s - > lock , flags ) ;
/*
* The following code is lifted from the original OSS driver ,
* and as I don ' t have a datasheet I cannot really comment
* on what it is doing . . .
*/
if ( ( inb ( HOST_CTRL_IO ( s - > io_base ) ) & 0x78 ) ! = 0 )
goto _done ;
d = inb ( ODIE_ADDR_IO ( s - > io_base ) ) & 0xf0 ;
if ( ( d & 0x80 ) ! = 0 )
goto _done ;
if ( d = = 0 ) {
s - > codec_type = 1 ;
s - > ic_type = IC_ODIE ;
} else if ( ( d & 0x60 ) ! = 0 ) {
s - > codec_type = 2 ;
s - > ic_type = IC_OPUS ;
} else
goto _done ;
outb ( 0xfa , ODIE_ADDR_IO ( s - > io_base ) ) ;
if ( ( inb ( ODIE_ADDR_IO ( s - > io_base ) ) & 0x9f ) ! = 0x0a )
goto _done ;
outb ( 0xfe , ODIE_ADDR_IO ( s - > io_base ) ) ;
if ( ( inb ( ODIE_ADDR_IO ( s - > io_base ) ) & 0x9f ) ! = 0x0e )
goto _done ;
if ( ( inb ( ODIE_DATA_IO ( s - > io_base ) ) & 0x9f ) ! = 0x0e )
goto _done ;
/*
* SoundScape successfully detected !
*/
retval = 1 ;
_done :
spin_unlock_irqrestore ( & s - > lock , flags ) ;
return retval ;
}
/*
* ALSA callback function , called when attempting to open the MIDI device .
* Check that the MIDI firmware has been loaded , because we don ' t want
* to crash the machine . Also check that someone isn ' t using the hardware
* IOCTL device .
*/
static int mpu401_open ( mpu401_t * mpu )
{
int err ;
if ( ! verify_mpu401 ( mpu ) ) {
snd_printk ( KERN_ERR " sscape: MIDI disabled, please load firmware \n " ) ;
err = - ENODEV ;
} else {
register struct soundscape * sscape = get_mpu401_soundscape ( mpu ) ;
unsigned long flags ;
spin_lock_irqsave ( & sscape - > fwlock , flags ) ;
if ( sscape - > hw_in_use | | ( sscape - > midi_usage = = ULONG_MAX ) ) {
err = - EBUSY ;
} else {
+ + ( sscape - > midi_usage ) ;
err = 0 ;
}
spin_unlock_irqrestore ( & sscape - > fwlock , flags ) ;
}
return err ;
}
static void mpu401_close ( mpu401_t * mpu )
{
register struct soundscape * sscape = get_mpu401_soundscape ( mpu ) ;
unsigned long flags ;
spin_lock_irqsave ( & sscape - > fwlock , flags ) ;
- - ( sscape - > midi_usage ) ;
spin_unlock_irqrestore ( & sscape - > fwlock , flags ) ;
}
/*
* Initialse an MPU - 401 subdevice for MIDI support on the SoundScape .
*/
static int __devinit create_mpu401 ( snd_card_t * card , int devnum , unsigned long port , int irq )
{
struct soundscape * sscape = get_card_soundscape ( card ) ;
snd_rawmidi_t * rawmidi ;
int err ;
# define MPU401_SHARE_HARDWARE 1
if ( ( err = snd_mpu401_uart_new ( card , devnum ,
MPU401_HW_MPU401 ,
port , MPU401_SHARE_HARDWARE ,
irq , SA_INTERRUPT ,
& rawmidi ) ) = = 0 ) {
mpu401_t * mpu = ( mpu401_t * ) rawmidi - > private_data ;
mpu - > open_input = mpu401_open ;
mpu - > open_output = mpu401_open ;
mpu - > close_input = mpu401_close ;
mpu - > close_output = mpu401_close ;
mpu - > private_data = sscape ;
sscape - > mpu = mpu ;
initialise_mpu401 ( mpu ) ;
}
return err ;
}
/*
* Override for the CS4231 playback format function .
* The AD1845 has much simpler format and rate selection .
*/
static void ad1845_playback_format ( cs4231_t * chip , snd_pcm_hw_params_t * params , unsigned char format )
{
unsigned long flags ;
unsigned rate = params_rate ( params ) ;
/*
* The AD1845 can ' t handle sample frequencies
* outside of 4 kHZ to 50 kHZ
*/
if ( rate > 50000 )
rate = 50000 ;
else if ( rate < 4000 )
rate = 4000 ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
/*
* Program the AD1845 correctly for the playback stream .
* Note that we do NOT need to toggle the MCE bit because
* the PLAYBACK_ENABLE bit of the Interface Configuration
* register is set .
*
* NOTE : We seem to need to write to the MSB before the LSB
* to get the correct sample frequency .
*/
snd_cs4231_out ( chip , CS4231_PLAYBK_FORMAT , ( format & 0xf0 ) ) ;
snd_cs4231_out ( chip , AD1845_FREQ_SEL_MSB , ( unsigned char ) ( rate > > 8 ) ) ;
snd_cs4231_out ( chip , AD1845_FREQ_SEL_LSB , ( unsigned char ) rate ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
}
/*
* Override for the CS4231 capture format function .
* The AD1845 has much simpler format and rate selection .
*/
static void ad1845_capture_format ( cs4231_t * chip , snd_pcm_hw_params_t * params , unsigned char format )
{
unsigned long flags ;
unsigned rate = params_rate ( params ) ;
/*
* The AD1845 can ' t handle sample frequencies
* outside of 4 kHZ to 50 kHZ
*/
if ( rate > 50000 )
rate = 50000 ;
else if ( rate < 4000 )
rate = 4000 ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
/*
* Program the AD1845 correctly for the playback stream .
* Note that we do NOT need to toggle the MCE bit because
* the CAPTURE_ENABLE bit of the Interface Configuration
* register is set .
*
* NOTE : We seem to need to write to the MSB before the LSB
* to get the correct sample frequency .
*/
snd_cs4231_out ( chip , CS4231_REC_FORMAT , ( format & 0xf0 ) ) ;
snd_cs4231_out ( chip , AD1845_FREQ_SEL_MSB , ( unsigned char ) ( rate > > 8 ) ) ;
snd_cs4231_out ( chip , AD1845_FREQ_SEL_LSB , ( unsigned char ) rate ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
}
/*
* Create an AD1845 PCM subdevice on the SoundScape . The AD1845
* is very much like a CS4231 , with a few extra bits . We will
* try to support at least some of the extra bits by overriding
* some of the CS4231 callback .
*/
static int __devinit create_ad1845 ( snd_card_t * card , unsigned port , int irq , int dma1 )
{
register struct soundscape * sscape = get_card_soundscape ( card ) ;
cs4231_t * chip ;
int err ;
# define CS4231_SHARE_HARDWARE (CS4231_HWSHARE_DMA1 | CS4231_HWSHARE_DMA2)
/*
* The AD1845 PCM device is only half - duplex , and so
* we only give it one DMA channel . . .
*/
if ( ( err = snd_cs4231_create ( card ,
port , - 1 , irq , dma1 , dma1 ,
CS4231_HW_DETECT ,
CS4231_HWSHARE_DMA1 , & chip ) ) = = 0 ) {
unsigned long flags ;
snd_pcm_t * pcm ;
# define AD1845_FREQ_SEL_ENABLE 0x08
# define AD1845_PWR_DOWN_CTRL 0x1b
# define AD1845_CRYS_CLOCK_SEL 0x1d
/*
* It turns out that the PLAYBACK_ENABLE bit is set
* by the lowlevel driver . . .
*
# define AD1845_IFACE_CONFIG \
( CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE )
snd_cs4231_mce_up ( chip ) ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_cs4231_out ( chip , CS4231_IFACE_CTRL , AD1845_IFACE_CONFIG ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
snd_cs4231_mce_down ( chip ) ;
*/
/*
* The input clock frequency on the SoundScape must
* be 14.31818 MHz , because we must set this register
* to get the playback to sound correct . . .
*/
snd_cs4231_mce_up ( chip ) ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_cs4231_out ( chip , AD1845_CRYS_CLOCK_SEL , 0x20 ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
snd_cs4231_mce_down ( chip ) ;
/*
* More custom configuration :
* a ) select " mode 2 " , and provide a current drive of 8 mA
* b ) enable frequency selection ( for capture / playback )
*/
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_cs4231_out ( chip , CS4231_MISC_INFO , ( CS4231_MODE2 | 0x10 ) ) ;
snd_cs4231_out ( chip , AD1845_PWR_DOWN_CTRL , snd_cs4231_in ( chip , AD1845_PWR_DOWN_CTRL ) | AD1845_FREQ_SEL_ENABLE ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( ( err = snd_cs4231_pcm ( chip , 0 , & pcm ) ) < 0 ) {
snd_printk ( KERN_ERR " sscape: No PCM device for AD1845 chip \n " ) ;
goto _error ;
}
if ( ( err = snd_cs4231_mixer ( chip ) ) < 0 ) {
snd_printk ( KERN_ERR " sscape: No mixer device for AD1845 chip \n " ) ;
goto _error ;
}
if ( ( err = snd_ctl_add ( card , snd_ctl_new1 ( & midi_mixer_ctl , chip ) ) ) < 0 ) {
snd_printk ( KERN_ERR " sscape: Could not create MIDI mixer control \n " ) ;
goto _error ;
}
strcpy ( card - > driver , " SoundScape " ) ;
strcpy ( card - > shortname , pcm - > name ) ;
snprintf ( card - > longname , sizeof ( card - > longname ) ,
" %s at 0x%lx, IRQ %d, DMA %d \n " ,
pcm - > name , chip - > port , chip - > irq , chip - > dma1 ) ;
chip - > set_playback_format = ad1845_playback_format ;
chip - > set_capture_format = ad1845_capture_format ;
sscape - > chip = chip ;
}
_error :
return err ;
}
struct params
{
int index ;
const char * id ;
unsigned port ;
int irq ;
int mpu_irq ;
int dma1 ;
} ;
static inline struct params *
init_params ( struct params * params ,
int index ,
const char * id ,
unsigned port ,
int irq ,
int mpu_irq ,
int dma1 )
{
params - > index = index ;
params - > id = id ;
params - > port = port ;
params - > irq = irq ;
params - > mpu_irq = mpu_irq ;
params - > dma1 = ( dma1 & 0x03 ) ;
return params ;
}
/*
* Create an ALSA soundcard entry for the SoundScape , using
* the given list of port , IRQ and DMA resources .
*/
static int __devinit create_sscape ( const struct params * params , snd_card_t * * rcardp )
{
snd_card_t * card ;
register struct soundscape * sscape ;
register unsigned dma_cfg ;
unsigned irq_cfg ;
unsigned mpu_irq_cfg ;
struct resource * io_res ;
unsigned long flags ;
int err ;
/*
* Check that the user didn ' t pass us garbage data . . .
*/
irq_cfg = get_irq_config ( params - > irq ) ;
if ( irq_cfg = = INVALID_IRQ ) {
snd_printk ( KERN_ERR " sscape: Invalid IRQ %d \n " , params - > irq ) ;
return - ENXIO ;
}
mpu_irq_cfg = get_irq_config ( params - > mpu_irq ) ;
if ( mpu_irq_cfg = = INVALID_IRQ ) {
printk ( KERN_ERR " sscape: Invalid IRQ %d \n " , params - > mpu_irq ) ;
return - ENXIO ;
}
/*
* Grab IO ports that we will need to probe so that we
* can detect and control this hardware . . .
*/
if ( ( io_res = request_region ( params - > port , 8 , " SoundScape " ) ) = = NULL ) {
snd_printk ( KERN_ERR " sscape: can't grab port 0x%x \n " , params - > port ) ;
return - EBUSY ;
}
/*
* Grab both DMA channels ( OK , only one for now ) . . .
*/
if ( ( err = request_dma ( params - > dma1 , " SoundScape " ) ) < 0 ) {
snd_printk ( KERN_ERR " sscape: can't grab DMA %d \n " , params - > dma1 ) ;
goto _release_region ;
}
/*
* Create a new ALSA sound card entry , in anticipation
* of detecting our hardware . . .
*/
if ( ( card = snd_card_new ( params - > index , params - > id , THIS_MODULE , sizeof ( struct soundscape ) ) ) = = NULL ) {
err = - ENOMEM ;
goto _release_dma ;
}
sscape = get_card_soundscape ( card ) ;
spin_lock_init ( & sscape - > lock ) ;
spin_lock_init ( & sscape - > fwlock ) ;
sscape - > io_res = io_res ;
sscape - > io_base = params - > port ;
if ( ! detect_sscape ( sscape ) ) {
printk ( KERN_ERR " sscape: hardware not detected at 0x%x \n " , sscape - > io_base ) ;
err = - ENODEV ;
goto _release_card ;
}
printk ( KERN_INFO " sscape: hardware detected at 0x%x, using IRQ %d, DMA %d \n " ,
sscape - > io_base , params - > irq , params - > dma1 ) ;
/*
* Now create the hardware - specific device so that we can
* load the microcode into the on - board processor .
* We cannot use the MPU - 401 MIDI system until this firmware
* has been loaded into the card .
*/
if ( ( err = snd_hwdep_new ( card , " MC68EC000 " , 0 , & ( sscape - > hw ) ) ) < 0 ) {
printk ( KERN_ERR " sscape: Failed to create firmware device \n " ) ;
goto _release_card ;
}
strlcpy ( sscape - > hw - > name , " SoundScape M68K " , sizeof ( sscape - > hw - > name ) ) ;
sscape - > hw - > name [ sizeof ( sscape - > hw - > name ) - 1 ] = ' \0 ' ;
sscape - > hw - > iface = SNDRV_HWDEP_IFACE_SSCAPE ;
sscape - > hw - > ops . open = sscape_hw_open ;
sscape - > hw - > ops . release = sscape_hw_release ;
sscape - > hw - > ops . ioctl = sscape_hw_ioctl ;
sscape - > hw - > private_data = sscape ;
/*
* Tell the on - board devices where their resources are ( I think -
* I can ' t be sure without a datasheet . . . So many magic values ! )
*/
spin_lock_irqsave ( & sscape - > lock , flags ) ;
activate_ad1845_unsafe ( sscape - > io_base ) ;
sscape_write_unsafe ( sscape - > io_base , GA_INTENA_REG , 0x00 ) ; /* disable */
sscape_write_unsafe ( sscape - > io_base , GA_SMCFGA_REG , 0x2e ) ;
sscape_write_unsafe ( sscape - > io_base , GA_SMCFGB_REG , 0x00 ) ;
/*
* Enable and configure the DMA channels . . .
*/
sscape_write_unsafe ( sscape - > io_base , GA_DMACFG_REG , 0x50 ) ;
dma_cfg = ( sscape - > ic_type = = IC_ODIE ? 0x70 : 0x40 ) ;
sscape_write_unsafe ( sscape - > io_base , GA_DMAA_REG , dma_cfg ) ;
sscape_write_unsafe ( sscape - > io_base , GA_DMAB_REG , 0x20 ) ;
sscape_write_unsafe ( sscape - > io_base ,
GA_INTCFG_REG , 0xf0 | ( mpu_irq_cfg < < 2 ) | mpu_irq_cfg ) ;
sscape_write_unsafe ( sscape - > io_base ,
GA_CDCFG_REG , 0x09 | DMA_8BIT | ( params - > dma1 < < 4 ) | ( irq_cfg < < 1 ) ) ;
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
/*
* We have now enabled the codec chip , and so we should
* detect the AD1845 device . . .
*/
if ( ( err = create_ad1845 ( card , CODEC_IO ( params - > port ) , params - > irq , params - > dma1 ) ) < 0 ) {
printk ( KERN_ERR " sscape: No AD1845 device at 0x%x, IRQ %d \n " ,
CODEC_IO ( params - > port ) , params - > irq ) ;
goto _release_card ;
}
# define MIDI_DEVNUM 0
if ( ( err = create_mpu401 ( card , MIDI_DEVNUM , MPU401_IO ( params - > port ) , params - > mpu_irq ) ) < 0 ) {
printk ( KERN_ERR " sscape: Failed to create MPU-401 device at 0x%x \n " ,
MPU401_IO ( params - > port ) ) ;
goto _release_card ;
}
/*
* Enable the master IRQ . . .
*/
sscape_write ( sscape , GA_INTENA_REG , 0x80 ) ;
/*
* Initialize mixer
*/
sscape - > midi_vol = 0 ;
host_write_ctrl_unsafe ( sscape - > io_base , CMD_SET_MIDI_VOL , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base , 0 , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base , CMD_XXX_MIDI_VOL , 100 ) ;
/*
* Now that we have successfully created this sound card ,
* it is safe to store the pointer .
* NOTE : we only register the sound card ' s " destructor "
* function now that our " constructor " has completed .
*/
card - > private_free = soundscape_free ;
* rcardp = card ;
return 0 ;
_release_card :
snd_card_free ( card ) ;
_release_dma :
free_dma ( params - > dma1 ) ;
_release_region :
2005-10-10 13:56:31 +04:00
release_and_free_resource ( io_res ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int sscape_cards __devinitdata ;
static struct params sscape_params [ SNDRV_CARDS ] __devinitdata ;
# ifdef CONFIG_PNP
static inline int __devinit get_next_autoindex ( int i )
{
while ( ( i < SNDRV_CARDS ) & & ( port [ i ] ! = SNDRV_AUTO_PORT ) ) {
+ + i ;
} /* while */
return i ;
}
static inline int __devinit is_port_known ( unsigned io , struct params * params , int cards )
{
while ( - - cards > = 0 ) {
if ( params [ cards ] . port = = io )
return 1 ;
} /* while */
return 0 ;
}
static int __devinit sscape_pnp_detect ( struct pnp_card_link * pcard ,
const struct pnp_card_device_id * pid )
{
struct pnp_dev * dev ;
static int idx = 0 ;
int ret ;
/*
* Allow this function to fail * quietly * if all the ISA PnP
* devices were configured using module parameters instead .
*/
if ( ( idx = get_next_autoindex ( idx ) ) > = SNDRV_CARDS ) {
return - ENOSPC ;
}
/*
* We have found a candidate ISA PnP card . Now we
* have to check that it has the devices that we
* expect it to have .
*
* We will NOT try and autoconfigure all of the resources
* needed and then activate the card as we are assuming that
* has already been done at boot - time using / proc / isapnp .
* We shall simply try to give each active card the resources
* that it wants . This is a sensible strategy for a modular
* system where unused modules are unloaded regularly .
*
* This strategy is utterly useless if we compile the driver
* into the kernel , of course .
*/
// printk(KERN_INFO "sscape: %s\n", card->name);
/*
* Check that we still have room for another sound card . . .
*/
if ( sscape_cards > = SNDRV_CARDS ) {
printk ( KERN_ERR " sscape: No room for another ALSA device \n " ) ;
return - ENOSPC ;
}
ret = - ENODEV ;
dev = pnp_request_card_device ( pcard , pid - > devs [ 0 ] . id , NULL ) ;
if ( dev ) {
struct params * this ;
if ( ! pnp_is_active ( dev ) ) {
if ( pnp_activate_dev ( dev ) < 0 ) {
printk ( KERN_INFO " sscape: device is inactive \n " ) ;
return - EBUSY ;
}
}
/*
* Read the correct parameters off the ISA PnP bus . . .
*/
this = init_params ( & sscape_params [ sscape_cards ] ,
index [ idx ] ,
id [ idx ] ,
pnp_port_start ( dev , 0 ) ,
pnp_irq ( dev , 0 ) ,
pnp_irq ( dev , 1 ) ,
pnp_dma ( dev , 0 ) ) ;
/*
* Do we know about this sound card already ?
*/
if ( ! is_port_known ( this - > port , sscape_params , sscape_cards ) ) {
snd_card_t * card ;
ret = create_sscape ( this , & card ) ;
if ( ret < 0 )
return ret ;
snd_card_set_dev ( card , & pcard - > card - > dev ) ;
[ALSA] Add snd_card_set_generic_dev() call to ISA drivers
ISA,CMI8330 driver,ES18xx driver,OPL3SA2 driver,Sound Galaxy driver
Sound Scape driver,AD1848 driver,CS4231 driver,CS4236+ driver
ES1688 driver,GUS Classic driver,GUS Extreme driver,GUS MAX driver
AMD InterWave driver,Opti9xx drivers,SB16/AWE driver,SB8 driver
Wavefront drivers
- Added snd_card_set_generic_dev() call.
- Added SND_GENERIC_DRIVER to Kconfig.
- Clean up the error path in probe if necessary.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-05 19:19:20 +04:00
if ( ( ret = snd_card_register ( card ) ) < 0 ) {
printk ( KERN_ERR " sscape: Failed to register sound card \n " ) ;
snd_card_free ( card ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
pnp_set_card_drvdata ( pcard , card ) ;
+ + sscape_cards ;
+ + idx ;
}
}
return ret ;
}
static void __devexit sscape_pnp_remove ( struct pnp_card_link * pcard )
{
snd_card_t * card = ( snd_card_t * ) pnp_get_card_drvdata ( pcard ) ;
pnp_set_card_drvdata ( pcard , NULL ) ;
snd_card_disconnect ( card ) ;
snd_card_free_in_thread ( card ) ;
}
static struct pnp_card_driver sscape_pnpc_driver = {
. flags = PNP_DRIVER_RES_DO_NOT_CHANGE ,
. name = " sscape " ,
. id_table = sscape_pnpids ,
. probe = sscape_pnp_detect ,
. remove = __devexit_p ( sscape_pnp_remove ) ,
} ;
# endif /* CONFIG_PNP */
static int __init sscape_manual_probe ( struct params * params )
{
int ret ;
unsigned i ;
snd_card_t * card ;
for ( i = 0 ; i < SNDRV_CARDS ; + + i ) {
/*
* We do NOT probe for ports .
* If we ' re not given a port number for this
* card then we completely ignore this line
* of parameters .
*/
if ( port [ i ] = = SNDRV_AUTO_PORT )
continue ;
/*
* Make sure we were given ALL of the other parameters .
*/
if ( ( irq [ i ] = = SNDRV_AUTO_IRQ ) | |
( mpu_irq [ i ] = = SNDRV_AUTO_IRQ ) | |
( dma [ i ] = = SNDRV_AUTO_DMA ) ) {
printk ( KERN_INFO
" sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA \n " ) ;
return - ENXIO ;
}
/*
* This cards looks OK . . .
*/
init_params ( params , index [ i ] , id [ i ] , port [ i ] , irq [ i ] , mpu_irq [ i ] , dma [ i ] ) ;
ret = create_sscape ( params , & card ) ;
if ( ret < 0 )
return ret ;
[ALSA] Add snd_card_set_generic_dev() call to ISA drivers
ISA,CMI8330 driver,ES18xx driver,OPL3SA2 driver,Sound Galaxy driver
Sound Scape driver,AD1848 driver,CS4231 driver,CS4236+ driver
ES1688 driver,GUS Classic driver,GUS Extreme driver,GUS MAX driver
AMD InterWave driver,Opti9xx drivers,SB16/AWE driver,SB8 driver
Wavefront drivers
- Added snd_card_set_generic_dev() call.
- Added SND_GENERIC_DRIVER to Kconfig.
- Clean up the error path in probe if necessary.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-05 19:19:20 +04:00
if ( ( ret = snd_card_set_generic_dev ( card ) ) < 0 ) {
snd_card_free ( card ) ;
return ret ;
}
if ( ( ret = snd_card_register ( card ) ) < 0 ) {
printk ( KERN_ERR " sscape: Failed to register sound card \n " ) ;
snd_card_free ( card ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
sscape_card [ sscape_cards ] = card ;
params + + ;
sscape_cards + + ;
} /* for */
return 0 ;
}
static void sscape_exit ( void )
{
unsigned i ;
# ifdef CONFIG_PNP
pnp_unregister_card_driver ( & sscape_pnpc_driver ) ;
# endif
for ( i = 0 ; i < ARRAY_SIZE ( sscape_card ) ; + + i ) {
snd_card_free ( sscape_card [ i ] ) ;
} /* for */
}
static int __init sscape_init ( void )
{
int ret ;
/*
* First check whether we were passed any parameters .
* These MUST take precedence over ANY automatic way
* of allocating cards , because the operator is
* S - P - E - L - L - I - N - G it out for us . . .
*/
ret = sscape_manual_probe ( sscape_params ) ;
if ( ret < 0 ) {
int i ;
for ( i = 0 ; i < sscape_cards ; + + i )
snd_card_free ( sscape_card [ i ] ) ;
return ret ;
}
# ifdef CONFIG_PNP
if ( sscape_cards < SNDRV_CARDS ) {
ret = pnp_register_card_driver ( & sscape_pnpc_driver ) ;
if ( ret < 0 ) {
sscape_exit ( ) ;
return ret ;
}
}
# endif
return 0 ;
}
module_init ( sscape_init ) ;
module_exit ( sscape_exit ) ;