2005-04-17 02:20:36 +04:00
/*
2009-10-01 02:10:34 +04:00
* Low - level ALSA driver for the ENSONIQ SoundScape
2005-04-17 02:20:36 +04:00
* 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 <linux/init.h>
2005-11-17 19:13:12 +03:00
# include <linux/err.h>
2007-02-22 14:50:54 +03:00
# include <linux/isa.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
2009-10-01 02:10:34 +04:00
# include <linux/firmware.h>
2005-04-17 02:20:36 +04:00
# include <linux/pnp.h>
# include <linux/spinlock.h>
# include <linux/moduleparam.h>
# include <asm/dma.h>
# include <sound/core.h>
2008-07-31 23:02:42 +04:00
# include <sound/wss.h>
2005-04-17 02:20:36 +04:00
# include <sound/mpu401.h>
# include <sound/initval.h>
MODULE_AUTHOR ( " Chris Rankin " ) ;
2009-10-01 02:10:34 +04:00
MODULE_DESCRIPTION ( " ENSONIQ SoundScape driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2009-10-01 02:10:34 +04:00
MODULE_FIRMWARE ( " sndscape.co0 " ) ;
MODULE_FIRMWARE ( " sndscape.co1 " ) ;
MODULE_FIRMWARE ( " sndscape.co2 " ) ;
MODULE_FIRMWARE ( " sndscape.co3 " ) ;
MODULE_FIRMWARE ( " sndscape.co4 " ) ;
MODULE_FIRMWARE ( " scope.cod " ) ;
2005-04-17 02:20:36 +04:00
2009-10-05 20:27:28 +04:00
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ;
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ;
static long port [ SNDRV_CARDS ] = SNDRV_DEFAULT_PORT ;
static long wss_port [ SNDRV_CARDS ] = SNDRV_DEFAULT_PORT ;
static int irq [ SNDRV_CARDS ] = SNDRV_DEFAULT_IRQ ;
static int mpu_irq [ SNDRV_CARDS ] = SNDRV_DEFAULT_IRQ ;
static int dma [ SNDRV_CARDS ] = SNDRV_DEFAULT_DMA ;
static int dma2 [ SNDRV_CARDS ] = SNDRV_DEFAULT_DMA ;
static bool joystick [ SNDRV_CARDS ] ;
2005-04-17 02:20:36 +04:00
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. " ) ;
2007-09-17 18:23:13 +04:00
module_param_array ( wss_port , long , NULL , 0444 ) ;
MODULE_PARM_DESC ( wss_port , " WSS Port # for SoundScape driver. " ) ;
2005-04-17 02:20:36 +04:00
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. " ) ;
2005-12-07 11:13:42 +03:00
2007-09-17 18:23:13 +04:00
module_param_array ( dma2 , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( dma2 , " DMA2 # for SoundScape driver. " ) ;
2009-10-05 20:18:57 +04:00
module_param_array ( joystick , bool , NULL , 0444 ) ;
MODULE_PARM_DESC ( joystick , " Enable gameport. " ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PNP
2007-05-15 13:42:56 +04:00
static int isa_registered ;
2006-01-04 17:06:44 +03:00
static int pnp_registered ;
2007-05-15 13:42:56 +04:00
2005-04-17 02:20:36 +04:00
static struct pnp_card_device_id sscape_pnpids [ ] = {
2007-09-17 18:23:13 +04:00
{ . id = " ENS3081 " , . devs = { { " ENS0000 " } } } , /* Soundscape PnP */
{ . id = " ENS4081 " , . devs = { { " ENS1011 " } } } , /* VIVO90 */
2005-04-17 02:20:36 +04:00
{ . id = " " } /* end */
} ;
MODULE_DEVICE_TABLE ( pnp_card , sscape_pnpids ) ;
# endif
# 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
2009-10-10 12:27:58 +04:00
# 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
2005-04-17 02:20:36 +04:00
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
2007-09-17 18:23:13 +04:00
enum card_type {
2009-09-28 01:08:40 +04:00
MEDIA_FX , /* Sequoia S-1000 */
SSCAPE , /* Sequoia S-2000 */
2007-09-17 18:23:13 +04:00
SSCAPE_PNP ,
SSCAPE_VIVO ,
} ;
2005-04-17 02:20:36 +04:00
struct soundscape {
spinlock_t lock ;
unsigned io_base ;
int ic_type ;
2007-09-17 18:23:13 +04:00
enum card_type type ;
2005-04-17 02:20:36 +04:00
struct resource * io_res ;
2007-09-17 19:57:37 +04:00
struct resource * wss_res ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip ;
2005-04-17 02:20:36 +04:00
unsigned char midi_vol ;
} ;
# define INVALID_IRQ ((unsigned)-1)
2005-11-17 16:42:05 +03:00
static inline struct soundscape * get_card_soundscape ( struct snd_card * c )
2005-04-17 02:20:36 +04:00
{
return ( struct soundscape * ) ( c - > 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 .
*/
2009-10-10 12:27:58 +04:00
static struct snd_dma_buffer * get_dmabuf ( struct snd_dma_buffer * buf ,
unsigned long size )
2005-04-17 02:20:36 +04:00
{
if ( buf ) {
2009-10-10 12:27:58 +04:00
if ( snd_dma_alloc_pages_fallback ( SNDRV_DMA_TYPE_DEV ,
snd_dma_isa_data ( ) ,
2005-04-17 02:20:36 +04:00
size , buf ) < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Failed to allocate "
" %lu bytes for DMA \n " ,
size ) ;
2005-04-17 02:20:36 +04:00
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 " . . .
*/
2009-10-10 12:27:58 +04:00
static inline void sscape_write_unsafe ( unsigned io_base , enum GA_REG reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
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 . . .
*/
2009-10-10 12:27:58 +04:00
static void sscape_write ( struct soundscape * s , enum GA_REG reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
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 " . . .
*/
2009-10-10 12:27:58 +04:00
static inline unsigned char sscape_read_unsafe ( unsigned io_base ,
enum GA_REG reg )
2005-04-17 02:20:36 +04:00
{
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 ;
2009-10-10 12:27:58 +04:00
if ( ( inb ( HOST_CTRL_IO ( io_base ) ) & RX_READY ) ! = 0 )
2005-04-17 02:20:36 +04:00
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 ,
2009-10-10 12:27:58 +04:00
unsigned timeout )
2005-04-17 02:20:36 +04:00
{
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 .
*/
2009-10-10 12:27:58 +04:00
static inline int verify_mpu401 ( const struct snd_mpu401 * mpu )
2005-04-17 02:20:36 +04:00
{
2009-01-21 10:19:27 +03:00
return ( ( inb ( MPU401C ( mpu ) ) & 0xc0 ) = = 0x80 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* This is apparently the standard way to initailise an MPU - 401
*/
2009-10-10 12:27:58 +04:00
static inline void initialise_mpu401 ( const struct snd_mpu401 * mpu )
2005-04-17 02:20:36 +04:00
{
2009-01-21 10:19:27 +03:00
outb ( 0 , MPU401D ( mpu ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* 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 . . .
*/
2009-10-10 12:27:58 +04:00
static void activate_ad1845_unsafe ( unsigned io_base )
2005-04-17 02:20:36 +04:00
{
2009-10-10 12:27:58 +04:00
unsigned char val = sscape_read_unsafe ( io_base , GA_HMCTL_REG ) ;
sscape_write_unsafe ( io_base , GA_HMCTL_REG , ( val & 0xcf ) | 0x10 ) ;
2005-04-17 02:20:36 +04:00
sscape_write_unsafe ( io_base , GA_CDCFG_REG , 0x80 ) ;
}
/*
* Do the necessary ALSA - level cleanup to deallocate our driver . . .
*/
2005-11-17 16:42:05 +03:00
static void soundscape_free ( struct snd_card * c )
2005-04-17 02:20:36 +04:00
{
2007-09-17 19:57:37 +04:00
struct soundscape * sscape = get_card_soundscape ( c ) ;
2005-10-10 13:56:31 +04:00
release_and_free_resource ( sscape - > io_res ) ;
2007-09-17 19:57:37 +04:00
release_and_free_resource ( sscape - > wss_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 .
*/
2009-10-10 12:27:58 +04:00
static void sscape_start_dma_unsafe ( unsigned io_base , enum GA_REG reg )
2005-04-17 02:20:36 +04:00
{
2009-10-10 12:27:58 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Wait for a DMA transfer to complete . This is a " limited busy-wait " ,
* and all locking issues are left to the caller .
*/
2009-10-10 12:27:58 +04:00
static int sscape_wait_dma_unsafe ( unsigned io_base , enum GA_REG reg ,
unsigned timeout )
2005-04-17 02:20:36 +04:00
{
while ( ! ( sscape_read_unsafe ( io_base , reg ) & 0x01 ) & & ( timeout ! = 0 ) ) {
udelay ( 100 ) ;
- - timeout ;
} /* while */
2009-10-10 12:27:58 +04:00
return sscape_read_unsafe ( io_base , reg ) & 0x01 ;
2005-04-17 02:20:36 +04:00
}
/*
* 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 )
{
2009-01-12 23:25:04 +03:00
unsigned long end_time = jiffies + msecs_to_jiffies ( timeout ) ;
do {
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2009-10-01 02:10:34 +04:00
int x ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
2009-10-01 02:10:34 +04:00
x = host_read_unsafe ( s - > io_base ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
2009-10-01 02:10:34 +04:00
if ( x = = 0xfe | | x = = 0xff )
2005-04-17 02:20:36 +04:00
return 1 ;
2009-01-12 23:25:04 +03:00
msleep ( 10 ) ;
} while ( time_before ( jiffies , end_time ) ) ;
2005-04-17 02:20:36 +04:00
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 )
{
2009-01-12 23:25:04 +03:00
unsigned long end_time = jiffies + msecs_to_jiffies ( timeout ) ;
do {
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2009-10-01 02:10:34 +04:00
int x ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
2009-10-01 02:10:34 +04:00
x = host_read_unsafe ( s - > io_base ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
if ( x = = 0xfe )
return 1 ;
2009-01-12 23:25:04 +03:00
msleep ( 10 ) ;
} while ( time_before ( jiffies , end_time ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Upload a byte - stream into the SoundScape using DMA channel A .
*/
2009-10-10 12:27:58 +04:00
static int upload_dma_data ( struct soundscape * s , const unsigned char * data ,
size_t size )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
struct snd_dma_buffer dma ;
int ret ;
2009-10-10 12:27:58 +04:00
unsigned char val ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
if ( ! get_dmabuf ( & dma , PAGE_ALIGN ( 32 * 1024 ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
spin_lock_irqsave ( & s - > lock , flags ) ;
/*
* Reset the board . . .
*/
2009-10-10 12:27:58 +04:00
val = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) ;
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , val & 0x3f ) ;
2005-04-17 02:20:36 +04:00
/*
* Enable the DMA channels and configure them . . .
*/
2009-10-10 12:27:58 +04:00
val = ( s - > chip - > dma1 < < 4 ) | DMA_8BIT ;
sscape_write_unsafe ( s - > io_base , GA_DMAA_REG , val ) ;
2005-04-17 02:20:36 +04:00
sscape_write_unsafe ( s - > io_base , GA_DMAB_REG , 0x20 ) ;
/*
* Take the board out of reset . . .
*/
2009-10-10 12:27:58 +04:00
val = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) ;
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , val | 0x80 ) ;
2005-04-17 02:20:36 +04:00
/*
2009-10-01 02:10:34 +04:00
* Upload the firmware to the SoundScape
2005-04-17 02:20:36 +04:00
* board through the DMA channel . . .
*/
while ( size ! = 0 ) {
unsigned long len ;
len = min ( size , dma . bytes ) ;
2009-10-01 02:10:34 +04:00
memcpy ( dma . area , data , len ) ;
2005-04-17 02:20:36 +04:00
data + = len ;
size - = len ;
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 ) ) {
/*
2009-10-10 12:27:58 +04:00
* Don ' t forget to release this spinlock we ' re holding
2005-04-17 02:20:36 +04:00
*/
spin_unlock_irqrestore ( & s - > lock , flags ) ;
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR
" sscape: DMA upload has timed out \n " ) ;
2005-04-17 02:20:36 +04:00
ret = - EAGAIN ;
goto _release_dma ;
}
} /* while */
set_host_mode_unsafe ( s - > io_base ) ;
2009-10-01 02:10:34 +04:00
outb ( 0x0 , s - > io_base ) ;
2005-04-17 02:20:36 +04:00
/*
* Boot the board . . . ( I think )
*/
2009-10-10 12:27:58 +04:00
val = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) ;
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , val | 0x40 ) ;
2005-04-17 02:20:36 +04:00
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 ;
2009-01-12 23:25:04 +03:00
if ( ! obp_startup_ack ( s , 5000 ) ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: No response "
" from on-board processor after upload \n " ) ;
2005-04-17 02:20:36 +04:00
ret = - EAGAIN ;
2009-01-12 23:25:04 +03:00
} else if ( ! host_startup_ack ( s , 5000 ) ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR
" sscape: SoundScape failed to initialise \n " ) ;
2005-04-17 02:20:36 +04:00
ret = - EAGAIN ;
}
2007-09-17 18:23:13 +04:00
_release_dma :
2005-04-17 02:20:36 +04:00
/*
* NOTE ! ! ! We are NOT holding any spinlocks at this point ! ! !
*/
2009-10-01 02:10:34 +04:00
sscape_write ( s , GA_DMAA_REG , ( s - > ic_type = = IC_OPUS ? 0x40 : 0x70 ) ) ;
2005-04-17 02:20:36 +04:00
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 .
*/
2009-10-01 02:10:34 +04:00
static int sscape_upload_bootblock ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
2009-10-01 02:10:34 +04:00
struct soundscape * sscape = get_card_soundscape ( card ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2009-10-01 02:10:34 +04:00
const struct firmware * init_fw = NULL ;
2005-04-17 02:20:36 +04:00
int data = 0 ;
int ret ;
2009-10-01 02:10:34 +04:00
ret = request_firmware ( & init_fw , " scope.cod " , card - > dev ) ;
if ( ret < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Error loading scope.cod " ) ;
2009-10-01 02:10:34 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-10-01 02:10:34 +04:00
ret = upload_dma_data ( sscape , init_fw - > data , init_fw - > size ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
release_firmware ( init_fw ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
spin_lock_irqsave ( & sscape - > lock , flags ) ;
if ( ret = = 0 )
data = host_read_ctrl_unsafe ( sscape - > io_base , 100 ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
if ( data & 0x10 )
sscape_write_unsafe ( sscape - > io_base , GA_SMCFGA_REG , 0x2f ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
2009-10-01 02:10:34 +04:00
data & = 0xf ;
if ( ret = = 0 & & data > 7 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR
" sscape: timeout reading firmware version \n " ) ;
2009-10-01 02:10:34 +04:00
ret = - EAGAIN ;
}
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
return ( ret = = 0 ) ? data : ret ;
2005-04-17 02:20:36 +04:00
}
/*
2009-10-01 02:10:34 +04:00
* Upload the microcode into the SoundScape .
2005-04-17 02:20:36 +04:00
*/
2009-10-01 02:10:34 +04:00
static int sscape_upload_microcode ( struct snd_card * card , int version )
2005-04-17 02:20:36 +04:00
{
2009-10-01 02:10:34 +04:00
struct soundscape * sscape = get_card_soundscape ( card ) ;
const struct firmware * init_fw = NULL ;
char name [ 14 ] ;
2005-04-17 02:20:36 +04:00
int err ;
2009-10-01 02:10:34 +04:00
snprintf ( name , sizeof ( name ) , " sndscape.co%d " , version ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
err = request_firmware ( & init_fw , name , card - > dev ) ;
if ( err < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Error loading sndscape.co%d " ,
version ) ;
2009-10-01 02:10:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2009-10-01 02:10:34 +04:00
err = upload_dma_data ( sscape , init_fw - > data , init_fw - > size ) ;
if ( err = = 0 )
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_INFO " sscape: MIDI firmware loaded %d KBs \n " ,
2009-10-01 02:10:34 +04:00
init_fw - > size > > 10 ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
release_firmware ( init_fw ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
/*
* Mixer control for the SoundScape ' s MIDI device .
*/
2005-11-17 16:42:05 +03:00
static int sscape_midi_info ( struct snd_kcontrol * ctl ,
2009-10-10 12:27:58 +04:00
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 127 ;
return 0 ;
}
2005-11-17 16:42:05 +03:00
static int sscape_midi_get ( struct snd_kcontrol * kctl ,
2009-10-10 12:27:58 +04:00
struct snd_ctl_elem_value * uctl )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kctl ) ;
2005-11-17 16:42:05 +03:00
struct snd_card * card = chip - > card ;
2005-04-17 02:20:36 +04:00
register struct soundscape * s = get_card_soundscape ( card ) ;
unsigned long flags ;
spin_lock_irqsave ( & s - > lock , flags ) ;
2009-02-04 19:41:32 +03:00
uctl - > value . integer . value [ 0 ] = s - > midi_vol ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
return 0 ;
}
2005-11-17 16:42:05 +03:00
static int sscape_midi_put ( struct snd_kcontrol * kctl ,
2009-10-10 12:27:58 +04:00
struct snd_ctl_elem_value * uctl )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kctl ) ;
2005-11-17 16:42:05 +03:00
struct snd_card * card = chip - > card ;
2009-10-10 12:27:58 +04:00
struct soundscape * s = get_card_soundscape ( card ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int change ;
2009-10-10 12:27:58 +04:00
unsigned char new_val ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
2009-10-10 12:27:58 +04:00
new_val = uctl - > value . integer . value [ 0 ] & 127 ;
2005-04-17 02:20:36 +04:00
/*
* 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 " ?
*/
2009-10-10 12:27:58 +04:00
if ( s - > midi_vol = = new_val ) {
2005-04-17 02:20:36 +04:00
change = 0 ;
goto __skip_change ;
}
2009-10-10 12:27:58 +04:00
change = host_write_ctrl_unsafe ( s - > io_base , CMD_SET_MIDI_VOL , 100 )
& & host_write_ctrl_unsafe ( s - > io_base , new_val , 100 )
& & host_write_ctrl_unsafe ( s - > io_base , CMD_XXX_MIDI_VOL , 100 )
& & host_write_ctrl_unsafe ( s - > io_base , new_val , 100 ) ;
s - > midi_vol = new_val ;
__skip_change :
2005-04-17 02:20:36 +04:00
/*
* 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 ;
}
2005-11-17 16:42:05 +03:00
static struct snd_kcontrol_new midi_mixer_ctl = {
2005-04-17 02:20:36 +04:00
. 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 .
*/
2009-09-28 01:08:40 +04:00
static unsigned __devinit get_irq_config ( int sscape_type , int irq )
2005-04-17 02:20:36 +04:00
{
static const int valid_irq [ ] = { 9 , 5 , 7 , 10 } ;
2009-09-28 01:08:40 +04:00
static const int old_irq [ ] = { 9 , 7 , 5 , 15 } ;
2005-04-17 02:20:36 +04:00
unsigned cfg ;
2009-09-28 01:08:40 +04:00
if ( sscape_type = = MEDIA_FX ) {
for ( cfg = 0 ; cfg < ARRAY_SIZE ( old_irq ) ; + + cfg )
if ( irq = = old_irq [ cfg ] )
return cfg ;
} else {
for ( cfg = 0 ; cfg < ARRAY_SIZE ( valid_irq ) ; + + cfg )
if ( irq = = valid_irq [ cfg ] )
return cfg ;
}
2005-04-17 02:20:36 +04:00
return INVALID_IRQ ;
}
/*
* Perform certain arcane port - checks to see whether there
* is a SoundScape board lurking behind the given ports .
*/
2009-02-04 19:41:32 +03:00
static int __devinit detect_sscape ( struct soundscape * s , long wss_io )
2005-04-17 02:20:36 +04:00
{
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 ;
2009-02-04 19:41:32 +03:00
if ( d = = 0 )
2005-04-17 02:20:36 +04:00
s - > ic_type = IC_ODIE ;
2009-02-04 19:41:32 +03:00
else if ( ( d & 0x60 ) ! = 0 )
2005-04-17 02:20:36 +04:00
s - > ic_type = IC_OPUS ;
2009-02-04 19:41:32 +03:00
else
2005-04-17 02:20:36 +04:00
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 ;
2007-09-17 19:57:37 +04:00
outb ( 0xfe , ODIE_ADDR_IO ( s - > io_base ) ) ;
d = inb ( ODIE_DATA_IO ( s - > io_base ) ) ;
if ( s - > type ! = SSCAPE_VIVO & & ( d & 0x9f ) ! = 0x0e )
2005-04-17 02:20:36 +04:00
goto _done ;
2009-09-28 01:08:40 +04:00
if ( s - > ic_type = = IC_OPUS )
activate_ad1845_unsafe ( s - > io_base ) ;
2007-09-17 19:57:37 +04:00
if ( s - > type = = SSCAPE_VIVO )
2009-02-04 19:41:32 +03:00
wss_io + = 4 ;
2009-09-28 01:08:40 +04:00
2009-10-10 12:27:58 +04:00
d = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) ;
2009-09-28 01:08:40 +04:00
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , d | 0xc0 ) ;
/* wait for WSS codec */
for ( d = 0 ; d < 500 ; d + + ) {
if ( ( inb ( wss_io ) & 0x80 ) = = 0 )
break ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
msleep ( 1 ) ;
spin_lock_irqsave ( & s - > lock , flags ) ;
}
if ( ( inb ( wss_io ) & 0x80 ) ! = 0 )
goto _done ;
if ( inb ( wss_io + 2 ) = = 0xff )
goto _done ;
d = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) & 0x3f ;
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , d ) ;
if ( ( inb ( wss_io ) & 0x80 ) ! = 0 )
s - > type = MEDIA_FX ;
2009-10-10 12:27:58 +04:00
d = sscape_read_unsafe ( s - > io_base , GA_HMCTL_REG ) ;
2009-09-28 01:08:40 +04:00
sscape_write_unsafe ( s - > io_base , GA_HMCTL_REG , d | 0xc0 ) ;
2007-09-17 19:57:37 +04:00
/* wait for WSS codec */
for ( d = 0 ; d < 500 ; d + + ) {
2009-02-04 19:41:32 +03:00
if ( ( inb ( wss_io ) & 0x80 ) = = 0 )
2007-09-17 19:57:37 +04:00
break ;
spin_unlock_irqrestore ( & s - > lock , flags ) ;
msleep ( 1 ) ;
spin_lock_irqsave ( & s - > lock , flags ) ;
}
2005-04-17 02:20:36 +04:00
/*
* SoundScape successfully detected !
*/
retval = 1 ;
2009-10-10 12:27:58 +04:00
_done :
2005-04-17 02:20:36 +04:00
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 .
*/
2009-10-10 12:27:58 +04:00
static int mpu401_open ( struct snd_mpu401 * mpu )
2005-04-17 02:20:36 +04:00
{
if ( ! verify_mpu401 ( mpu ) ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: MIDI disabled, "
" please load firmware \n " ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2009-10-02 20:41:29 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Initialse an MPU - 401 subdevice for MIDI support on the SoundScape .
*/
2009-10-10 12:27:58 +04:00
static int __devinit create_mpu401 ( struct snd_card * card , int devnum ,
unsigned long port , int irq )
2005-04-17 02:20:36 +04:00
{
struct soundscape * sscape = get_card_soundscape ( card ) ;
2005-11-17 16:42:05 +03:00
struct snd_rawmidi * rawmidi ;
2005-04-17 02:20:36 +04:00
int err ;
2009-10-10 12:27:58 +04:00
err = snd_mpu401_uart_new ( card , devnum , MPU401_HW_MPU401 , port ,
MPU401_INFO_INTEGRATED , irq , IRQF_DISABLED ,
& rawmidi ) ;
if ( err = = 0 ) {
struct snd_mpu401 * mpu = rawmidi - > private_data ;
2005-04-17 02:20:36 +04:00
mpu - > open_input = mpu401_open ;
mpu - > open_output = mpu401_open ;
mpu - > private_data = sscape ;
initialise_mpu401 ( mpu ) ;
}
return err ;
}
/*
* 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 .
*/
2007-09-17 18:23:13 +04:00
static int __devinit create_ad1845 ( struct snd_card * card , unsigned port ,
int irq , int dma1 , int dma2 )
2005-04-17 02:20:36 +04:00
{
register struct soundscape * sscape = get_card_soundscape ( card ) ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip ;
2005-04-17 02:20:36 +04:00
int err ;
2009-10-05 20:18:57 +04:00
int codec_type = WSS_HW_DETECT ;
switch ( sscape - > type ) {
case MEDIA_FX :
case SSCAPE :
/*
* There are some freak examples of early Soundscape cards
* with CS4231 instead of AD1848 / CS4248 . Unfortunately , the
* CS4231 works only in CS4248 compatibility mode on
* these cards so force it .
*/
if ( sscape - > ic_type ! = IC_OPUS )
codec_type = WSS_HW_AD1848 ;
break ;
2005-04-17 02:20:36 +04:00
2009-10-05 20:18:57 +04:00
case SSCAPE_VIVO :
2007-09-17 19:57:37 +04:00
port + = 4 ;
2009-10-05 20:18:57 +04:00
break ;
default :
break ;
}
2007-09-17 19:57:37 +04:00
2008-07-31 23:03:41 +04:00
err = snd_wss_create ( card , port , - 1 , irq , dma1 , dma2 ,
2009-10-05 20:18:57 +04:00
codec_type , WSS_HWSHARE_DMA1 , & chip ) ;
2007-09-17 18:23:13 +04:00
if ( ! err ) {
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2005-11-17 16:42:05 +03:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
2007-09-17 19:57:37 +04:00
if ( sscape - > type ! = SSCAPE_VIVO ) {
/*
* 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 . . .
*/
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2007-09-17 19:57:37 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2009-01-10 01:10:52 +03:00
snd_wss_out ( chip , AD1845_CLOCK , 0x20 ) ;
2007-09-17 19:57:37 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
2007-09-17 19:57:37 +04:00
}
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
err = snd_wss_pcm ( chip , 0 , & pcm ) ;
2007-09-17 19:57:37 +04:00
if ( err < 0 ) {
snd_printk ( KERN_ERR " sscape: No PCM device "
" for AD1845 chip \n " ) ;
2005-04-17 02:20:36 +04:00
goto _error ;
}
2008-07-31 23:03:41 +04:00
err = snd_wss_mixer ( chip ) ;
2007-09-17 19:57:37 +04:00
if ( err < 0 ) {
snd_printk ( KERN_ERR " sscape: No mixer device "
" for AD1845 chip \n " ) ;
2005-04-17 02:20:36 +04:00
goto _error ;
}
2009-01-10 01:10:52 +03:00
if ( chip - > hardware ! = WSS_HW_AD1848 ) {
err = snd_wss_timer ( chip , 0 , NULL ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " sscape: No timer device "
" for AD1845 chip \n " ) ;
goto _error ;
}
2005-04-17 02:20:36 +04:00
}
2007-09-17 19:57:37 +04:00
if ( sscape - > type ! = SSCAPE_VIVO ) {
err = snd_ctl_add ( card ,
snd_ctl_new1 ( & midi_mixer_ctl , chip ) ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " sscape: Could not create "
" MIDI mixer control \n " ) ;
goto _error ;
}
}
2005-04-17 02:20:36 +04:00
sscape - > chip = chip ;
}
2009-10-10 12:27:58 +04:00
_error :
2005-04-17 02:20:36 +04:00
return err ;
}
/*
* Create an ALSA soundcard entry for the SoundScape , using
* the given list of port , IRQ and DMA resources .
*/
2007-09-17 18:23:13 +04:00
static int __devinit create_sscape ( int dev , struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
2007-09-17 19:57:37 +04:00
struct soundscape * sscape = get_card_soundscape ( card ) ;
unsigned dma_cfg ;
2005-04-17 02:20:36 +04:00
unsigned irq_cfg ;
unsigned mpu_irq_cfg ;
struct resource * io_res ;
2007-09-17 19:57:37 +04:00
struct resource * wss_res ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int err ;
2009-10-05 20:18:57 +04:00
int val ;
2009-09-28 01:08:40 +04:00
const char * name ;
2005-04-17 02:20:36 +04:00
/*
* Grab IO ports that we will need to probe so that we
* can detect and control this hardware . . .
*/
2009-02-04 19:41:32 +03:00
io_res = request_region ( port [ dev ] , 8 , " SoundScape " ) ;
2007-09-17 19:57:37 +04:00
if ( ! io_res ) {
2009-02-04 19:41:32 +03:00
snd_printk ( KERN_ERR
" sscape: can't grab port 0x%lx \n " , port [ dev ] ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2007-09-17 19:57:37 +04:00
wss_res = NULL ;
if ( sscape - > type = = SSCAPE_VIVO ) {
wss_res = request_region ( wss_port [ dev ] , 4 , " SoundScape " ) ;
if ( ! wss_res ) {
snd_printk ( KERN_ERR " sscape: can't grab port 0x%lx \n " ,
wss_port [ dev ] ) ;
err = - EBUSY ;
goto _release_region ;
}
}
2005-04-17 02:20:36 +04:00
/*
2007-09-17 19:57:37 +04:00
* Grab one DMA channel . . .
2005-04-17 02:20:36 +04:00
*/
2007-09-17 18:23:13 +04:00
err = request_dma ( dma [ dev ] , " SoundScape " ) ;
if ( err < 0 ) {
2005-11-17 19:13:12 +03:00
snd_printk ( KERN_ERR " sscape: can't grab DMA %d \n " , dma [ dev ] ) ;
2005-04-17 02:20:36 +04:00
goto _release_region ;
}
spin_lock_init ( & sscape - > lock ) ;
sscape - > io_res = io_res ;
2007-09-17 19:57:37 +04:00
sscape - > wss_res = wss_res ;
2009-02-04 19:41:32 +03:00
sscape - > io_base = port [ dev ] ;
2005-04-17 02:20:36 +04:00
2009-02-04 19:41:32 +03:00
if ( ! detect_sscape ( sscape , wss_port [ dev ] ) ) {
2009-10-02 20:41:29 +04:00
printk ( KERN_ERR " sscape: hardware not detected at 0x%x \n " ,
sscape - > io_base ) ;
2005-04-17 02:20:36 +04:00
err = - ENODEV ;
2007-09-17 18:23:13 +04:00
goto _release_dma ;
2005-04-17 02:20:36 +04:00
}
2009-09-28 01:08:40 +04:00
switch ( sscape - > type ) {
case MEDIA_FX :
name = " MediaFX/SoundFX " ;
break ;
case SSCAPE :
name = " Soundscape " ;
break ;
case SSCAPE_PNP :
name = " Soundscape PnP " ;
break ;
case SSCAPE_VIVO :
name = " Soundscape VIVO " ;
break ;
default :
name = " unknown Soundscape " ;
break ;
}
printk ( KERN_INFO " sscape: %s card detected at 0x%x, using IRQ %d, DMA %d \n " ,
name , sscape - > io_base , irq [ dev ] , dma [ dev ] ) ;
/*
* Check that the user didn ' t pass us garbage data . . .
*/
irq_cfg = get_irq_config ( sscape - > type , irq [ dev ] ) ;
if ( irq_cfg = = INVALID_IRQ ) {
snd_printk ( KERN_ERR " sscape: Invalid IRQ %d \n " , irq [ dev ] ) ;
return - ENXIO ;
}
mpu_irq_cfg = get_irq_config ( sscape - > type , mpu_irq [ dev ] ) ;
if ( mpu_irq_cfg = = INVALID_IRQ ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Invalid IRQ %d \n " , mpu_irq [ dev ] ) ;
2009-09-28 01:08:40 +04:00
return - ENXIO ;
}
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
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 ) ;
2009-09-28 01:08:40 +04:00
dma_cfg = ( sscape - > ic_type = = IC_OPUS ? 0x40 : 0x70 ) ;
2005-04-17 02:20:36 +04:00
sscape_write_unsafe ( sscape - > io_base , GA_DMAA_REG , dma_cfg ) ;
sscape_write_unsafe ( sscape - > io_base , GA_DMAB_REG , 0x20 ) ;
2009-09-28 01:08:40 +04:00
mpu_irq_cfg | = mpu_irq_cfg < < 2 ;
2009-10-05 20:18:57 +04:00
val = sscape_read_unsafe ( sscape - > io_base , GA_HMCTL_REG ) & 0xF7 ;
if ( joystick [ dev ] )
val | = 8 ;
sscape_write_unsafe ( sscape - > io_base , GA_HMCTL_REG , val | 0x10 ) ;
2009-09-28 01:08:40 +04:00
sscape_write_unsafe ( sscape - > io_base , GA_INTCFG_REG , 0xf0 | mpu_irq_cfg ) ;
2005-04-17 02:20:36 +04:00
sscape_write_unsafe ( sscape - > io_base ,
2007-09-17 18:23:13 +04:00
GA_CDCFG_REG , 0x09 | DMA_8BIT
| ( dma [ dev ] < < 4 ) | ( irq_cfg < < 1 ) ) ;
2009-10-10 12:27:58 +04:00
/*
* Enable the master IRQ . . .
*/
sscape_write_unsafe ( sscape - > io_base , GA_INTENA_REG , 0x80 ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
/*
* We have now enabled the codec chip , and so we should
* detect the AD1845 device . . .
*/
2007-09-17 18:23:13 +04:00
err = create_ad1845 ( card , wss_port [ dev ] , irq [ dev ] ,
dma [ dev ] , dma2 [ dev ] ) ;
if ( err < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR
" sscape: No AD1845 device at 0x%lx, IRQ %d \n " ,
wss_port [ dev ] , irq [ dev ] ) ;
2007-09-17 18:23:13 +04:00
goto _release_dma ;
2005-04-17 02:20:36 +04:00
}
2009-10-01 02:10:34 +04:00
strcpy ( card - > driver , " SoundScape " ) ;
strcpy ( card - > shortname , name ) ;
snprintf ( card - > longname , sizeof ( card - > longname ) ,
" %s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d \n " ,
name , sscape - > chip - > port , sscape - > chip - > irq ,
sscape - > chip - > dma1 , sscape - > chip - > dma2 ) ;
2005-04-17 02:20:36 +04:00
# define MIDI_DEVNUM 0
2007-09-17 19:57:37 +04:00
if ( sscape - > type ! = SSCAPE_VIVO ) {
2009-10-01 02:10:34 +04:00
err = sscape_upload_bootblock ( card ) ;
if ( err > = 0 )
err = sscape_upload_microcode ( card , err ) ;
2005-04-17 02:20:36 +04:00
2009-10-01 02:10:34 +04:00
if ( err = = 0 ) {
err = create_mpu401 ( card , MIDI_DEVNUM , port [ dev ] ,
mpu_irq [ dev ] ) ;
if ( err < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Failed to create "
2009-10-01 02:10:34 +04:00
" MPU-401 device at 0x%lx \n " ,
port [ dev ] ) ;
goto _release_dma ;
}
/*
* Initialize mixer
*/
spin_lock_irqsave ( & sscape - > lock , flags ) ;
sscape - > midi_vol = 0 ;
host_write_ctrl_unsafe ( sscape - > io_base ,
CMD_SET_MIDI_VOL , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base ,
sscape - > midi_vol , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base ,
CMD_XXX_MIDI_VOL , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base ,
sscape - > midi_vol , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base ,
CMD_SET_EXTMIDI , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base ,
0 , 100 ) ;
host_write_ctrl_unsafe ( sscape - > io_base , CMD_ACK , 100 ) ;
set_midi_mode_unsafe ( sscape - > io_base ) ;
spin_unlock_irqrestore ( & sscape - > lock , flags ) ;
}
2007-09-17 19:57:37 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* 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 ;
return 0 ;
2007-09-17 18:23:13 +04:00
_release_dma :
2005-11-17 19:13:12 +03:00
free_dma ( dma [ dev ] ) ;
2005-04-17 02:20:36 +04:00
2007-09-17 18:23:13 +04:00
_release_region :
2007-09-17 19:57:37 +04:00
release_and_free_resource ( wss_res ) ;
2005-10-10 13:56:31 +04:00
release_and_free_resource ( io_res ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2007-02-22 14:50:54 +03:00
static int __devinit snd_sscape_match ( struct device * pdev , unsigned int i )
{
/*
* Make sure we were given ALL of the other parameters .
*/
if ( port [ i ] = = SNDRV_AUTO_PORT )
return 0 ;
if ( irq [ i ] = = SNDRV_AUTO_IRQ | |
mpu_irq [ i ] = = SNDRV_AUTO_IRQ | |
dma [ i ] = = SNDRV_AUTO_DMA ) {
printk ( KERN_INFO
2009-10-10 12:27:58 +04:00
" sscape: insufficient parameters, "
" need IO, IRQ, MPU-IRQ and DMA \n " ) ;
2007-02-22 14:50:54 +03:00
return 0 ;
}
return 1 ;
}
static int __devinit snd_sscape_probe ( struct device * pdev , unsigned int dev )
2005-11-17 19:13:12 +03:00
{
struct snd_card * card ;
2007-09-17 18:23:13 +04:00
struct soundscape * sscape ;
2005-11-17 19:13:12 +03:00
int ret ;
2008-12-28 18:43:35 +03:00
ret = snd_card_create ( index [ dev ] , id [ dev ] , THIS_MODULE ,
sizeof ( struct soundscape ) , & card ) ;
if ( ret < 0 )
return ret ;
2007-09-17 18:23:13 +04:00
sscape = get_card_soundscape ( card ) ;
sscape - > type = SSCAPE ;
2005-11-17 19:13:12 +03:00
dma [ dev ] & = 0x03 ;
2009-10-01 02:10:34 +04:00
snd_card_set_dev ( card , pdev ) ;
2007-09-17 18:23:13 +04:00
ret = create_sscape ( dev , card ) ;
2005-11-17 19:13:12 +03:00
if ( ret < 0 )
2007-09-17 18:23:13 +04:00
goto _release_card ;
2009-10-10 12:27:58 +04:00
ret = snd_card_register ( card ) ;
if ( ret < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Failed to register sound card \n " ) ;
2007-09-17 18:23:13 +04:00
goto _release_card ;
2005-11-17 19:13:12 +03:00
}
2007-02-22 14:50:54 +03:00
dev_set_drvdata ( pdev , card ) ;
2005-11-17 19:13:12 +03:00
return 0 ;
2007-09-17 18:23:13 +04:00
_release_card :
snd_card_free ( card ) ;
return ret ;
2005-11-17 19:13:12 +03:00
}
2007-02-22 14:50:54 +03:00
static int __devexit snd_sscape_remove ( struct device * devptr , unsigned int dev )
2005-11-17 19:13:12 +03:00
{
2007-02-22 14:50:54 +03:00
snd_card_free ( dev_get_drvdata ( devptr ) ) ;
dev_set_drvdata ( devptr , NULL ) ;
2005-11-17 19:13:12 +03:00
return 0 ;
}
2007-03-20 13:33:46 +03:00
# define DEV_NAME "sscape"
2005-11-17 19:13:12 +03:00
2007-02-22 14:50:54 +03:00
static struct isa_driver snd_sscape_driver = {
. match = snd_sscape_match ,
2005-11-17 19:13:12 +03:00
. probe = snd_sscape_probe ,
. remove = __devexit_p ( snd_sscape_remove ) ,
/* FIXME: suspend/resume */
. driver = {
2007-03-20 13:33:46 +03:00
. name = DEV_NAME
2005-11-17 19:13:12 +03:00
} ,
} ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PNP
static inline int __devinit get_next_autoindex ( int i )
{
2005-11-17 19:13:12 +03:00
while ( i < SNDRV_CARDS & & port [ i ] ! = SNDRV_AUTO_PORT )
2005-04-17 02:20:36 +04:00
+ + i ;
return i ;
}
static int __devinit sscape_pnp_detect ( struct pnp_card_link * pcard ,
const struct pnp_card_device_id * pid )
{
static int idx = 0 ;
2005-11-17 19:13:12 +03:00
struct pnp_dev * dev ;
struct snd_card * card ;
2007-09-17 18:23:13 +04:00
struct soundscape * sscape ;
2005-04-17 02:20:36 +04:00
int ret ;
/*
* Allow this function to fail * quietly * if all the ISA PnP
* devices were configured using module parameters instead .
*/
2009-10-10 12:27:58 +04:00
idx = get_next_autoindex ( idx ) ;
if ( idx > = SNDRV_CARDS )
2005-04-17 02:20:36 +04:00
return - ENOSPC ;
/*
* Check that we still have room for another sound card . . .
*/
dev = pnp_request_card_device ( pcard , pid - > devs [ 0 ] . id , NULL ) ;
2009-10-10 12:27:58 +04:00
if ( ! dev )
2005-11-17 19:13:12 +03:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2005-11-17 19:13:12 +03:00
if ( ! pnp_is_active ( dev ) ) {
if ( pnp_activate_dev ( dev ) < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_INFO " sscape: device is inactive \n " ) ;
2005-11-17 19:13:12 +03:00
return - EBUSY ;
2005-04-17 02:20:36 +04:00
}
}
2007-09-17 18:23:13 +04:00
/*
* Create a new ALSA sound card entry , in anticipation
* of detecting our hardware . . .
*/
2008-12-28 18:43:35 +03:00
ret = snd_card_create ( index [ idx ] , id [ idx ] , THIS_MODULE ,
sizeof ( struct soundscape ) , & card ) ;
if ( ret < 0 )
return ret ;
2007-09-17 18:23:13 +04:00
sscape = get_card_soundscape ( card ) ;
/*
* Identify card model . . .
*/
if ( ! strncmp ( " ENS4081 " , pid - > id , 7 ) )
sscape - > type = SSCAPE_VIVO ;
else
sscape - > type = SSCAPE_PNP ;
2005-11-17 19:13:12 +03:00
/*
* Read the correct parameters off the ISA PnP bus . . .
*/
port [ idx ] = pnp_port_start ( dev , 0 ) ;
irq [ idx ] = pnp_irq ( dev , 0 ) ;
mpu_irq [ idx ] = pnp_irq ( dev , 1 ) ;
dma [ idx ] = pnp_dma ( dev , 0 ) & 0x03 ;
2007-09-17 19:57:37 +04:00
if ( sscape - > type = = SSCAPE_PNP ) {
dma2 [ idx ] = dma [ idx ] ;
wss_port [ idx ] = CODEC_IO ( port [ idx ] ) ;
} else {
wss_port [ idx ] = pnp_port_start ( dev , 1 ) ;
dma2 [ idx ] = pnp_dma ( dev , 1 ) ;
}
2009-10-01 02:10:34 +04:00
snd_card_set_dev ( card , & pcard - > card - > dev ) ;
2005-11-17 19:13:12 +03:00
2007-09-17 18:23:13 +04:00
ret = create_sscape ( idx , card ) ;
2005-11-17 19:13:12 +03:00
if ( ret < 0 )
2007-09-17 18:23:13 +04:00
goto _release_card ;
2009-10-10 12:27:58 +04:00
ret = snd_card_register ( card ) ;
if ( ret < 0 ) {
2009-10-02 20:41:29 +04:00
snd_printk ( KERN_ERR " sscape: Failed to register sound card \n " ) ;
2007-09-17 18:23:13 +04:00
goto _release_card ;
2005-11-17 19:13:12 +03:00
}
pnp_set_card_drvdata ( pcard , card ) ;
+ + idx ;
2007-09-17 18:23:13 +04:00
return 0 ;
2005-11-17 19:13:12 +03:00
2007-09-17 18:23:13 +04:00
_release_card :
snd_card_free ( card ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void __devexit sscape_pnp_remove ( struct pnp_card_link * pcard )
{
2005-11-17 19:13:12 +03:00
snd_card_free ( pnp_get_card_drvdata ( pcard ) ) ;
2005-04-17 02:20:36 +04:00
pnp_set_card_drvdata ( pcard , NULL ) ;
}
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_init ( void )
{
2007-05-15 13:42:56 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2007-05-15 13:42:56 +04:00
err = isa_register_driver ( & snd_sscape_driver , SNDRV_CARDS ) ;
2006-01-04 17:06:44 +03:00
# ifdef CONFIG_PNP
2007-05-15 13:42:56 +04:00
if ( ! err )
isa_registered = 1 ;
err = pnp_register_card_driver ( & sscape_pnpc_driver ) ;
if ( ! err )
2005-12-07 11:13:42 +03:00
pnp_registered = 1 ;
2007-05-15 13:42:56 +04:00
if ( isa_registered )
err = 0 ;
2006-01-04 17:06:44 +03:00
# endif
2007-05-15 13:42:56 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-02-22 14:50:54 +03:00
static void __exit sscape_exit ( void )
{
# ifdef CONFIG_PNP
if ( pnp_registered )
pnp_unregister_card_driver ( & sscape_pnpc_driver ) ;
2007-05-15 13:42:56 +04:00
if ( isa_registered )
2007-02-22 14:50:54 +03:00
# endif
2007-05-15 13:42:56 +04:00
isa_unregister_driver ( & snd_sscape_driver ) ;
2007-02-22 14:50:54 +03:00
}
2005-04-17 02:20:36 +04:00
module_init ( sscape_init ) ;
module_exit ( sscape_exit ) ;