2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) by Hannu Savolainen 1993 - 1997
*
* cs4232 . c
*
* The low level driver for Crystal CS4232 based cards . The CS4232 is
* a PnP compatible chip which contains a CS4231A codec , SB emulation ,
* a MPU401 compatible MIDI port , joystick and synthesizer and IDE CD - ROM
* interfaces . This is just a temporary driver until full PnP support
* gets implemented . Just the WSS codec , FM synth and the MIDI ports are
* supported . Other interfaces are left uninitialized .
*
* ifdef . . . WAVEFRONT . . .
*
* Support is provided for initializing the WaveFront synth
* interface as well , which is logical device # 4. Note that if
* you have a Tropez + card , you probably don ' t need to setup
* the CS4232 - supported MIDI interface , since it corresponds to
* the internal 26 - pin header that ' s hard to access . Using this
* requires an additional IRQ , a resource none too plentiful in
* this environment . Just don ' t set module parameters mpuio and
* mpuirq , and the MIDI port will be left uninitialized . You can
* still use the ICS2115 hosted MIDI interface which corresponds
* to the 9 - pin D connector on the back of the card .
*
* endif . . . WAVEFRONT . . .
*
* Supported chips are :
* CS4232
* CS4236
* CS4236B
*
* Note : You will need a PnP config setup to initialise some CS4232 boards
* anyway .
*
* Changes
* John Rood Added Bose Sound System Support .
* Toshio Spoor
* Alan Cox Modularisation , Basic cleanups .
* Paul Barton - Davis Separated MPU configuration , added
* Tropez + ( WaveFront ) support
* Christoph Hellwig Adapted to module_init / module_exit ,
* simple cleanups
* Arnaldo C . de Melo got rid of attach_uart401
* Bartlomiej Zolnierkiewicz
* Added some __init / __initdata / __exit
* Marcus Meissner Added ISA PnP support .
*/
# include <linux/config.h>
# include <linux/pnp.h>
# include <linux/module.h>
# include <linux/init.h>
# include "sound_config.h"
# include "ad1848.h"
# include "mpu401.h"
# define KEY_PORT 0x279 /* Same as LPT1 status port */
# define CSN_NUM 0x99 /* Just a random number */
# define INDEX_ADDRESS 0x00 /* (R0) Index Address Register */
# define INDEX_DATA 0x01 /* (R1) Indexed Data Register */
# define PIN_CONTROL 0x0a /* (I10) Pin Control */
# define ENABLE_PINS 0xc0 /* XCTRL0/XCTRL1 enable */
static void CS_OUT ( unsigned char a )
{
outb ( a , KEY_PORT ) ;
}
# define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);}
# define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);}
static int __initdata bss = 0 ;
static int mpu_base , mpu_irq ;
static int synth_base , synth_irq ;
static int mpu_detected ;
static int probe_cs4232_mpu ( struct address_info * hw_config )
{
/*
* Just write down the config values .
*/
mpu_base = hw_config - > io_base ;
mpu_irq = hw_config - > irq ;
return 1 ;
}
static unsigned char crystal_key [ ] = /* A 32 byte magic key sequence */
{
0x96 , 0x35 , 0x9a , 0xcd , 0xe6 , 0xf3 , 0x79 , 0xbc ,
0x5e , 0xaf , 0x57 , 0x2b , 0x15 , 0x8a , 0xc5 , 0xe2 ,
0xf1 , 0xf8 , 0x7c , 0x3e , 0x9f , 0x4f , 0x27 , 0x13 ,
0x09 , 0x84 , 0x42 , 0xa1 , 0xd0 , 0x68 , 0x34 , 0x1a
} ;
static void sleep ( unsigned howlong )
{
current - > state = TASK_INTERRUPTIBLE ;
schedule_timeout ( howlong ) ;
}
static void enable_xctrl ( int baseio )
{
unsigned char regd ;
/*
* Some IBM Aptiva ' s have the Bose Sound System . By default
* the Bose Amplifier is disabled . The amplifier will be
* activated , by setting the XCTRL0 and XCTRL1 bits .
* Volume of the monitor bose speakers / woofer , can then
* be set by changing the PCM volume .
*
*/
printk ( " cs4232: enabling Bose Sound System Amplifier. \n " ) ;
/* Switch to Pin Control Address */
regd = inb ( baseio + INDEX_ADDRESS ) & 0xe0 ;
outb ( ( ( unsigned char ) ( PIN_CONTROL | regd ) ) , baseio + INDEX_ADDRESS ) ;
/* Activate the XCTRL0 and XCTRL1 Pins */
regd = inb ( baseio + INDEX_DATA ) ;
outb ( ( ( unsigned char ) ( ENABLE_PINS | regd ) ) , baseio + INDEX_DATA ) ;
}
static int __init probe_cs4232 ( struct address_info * hw_config , int isapnp_configured )
{
int i , n ;
int base = hw_config - > io_base , irq = hw_config - > irq ;
int dma1 = hw_config - > dma , dma2 = hw_config - > dma2 ;
struct resource * ports ;
if ( base = = - 1 | | irq = = - 1 | | dma1 = = - 1 ) {
printk ( KERN_ERR " cs4232: dma, irq and io must be set. \n " ) ;
return 0 ;
}
/*
* Verify that the I / O port range is free .
*/
ports = request_region ( base , 4 , " ad1848 " ) ;
if ( ! ports ) {
printk ( KERN_ERR " cs4232.c: I/O port 0x%03x not free \n " , base ) ;
return 0 ;
}
if ( ad1848_detect ( ports , NULL , hw_config - > osp ) ) {
goto got_it ; /* The card is already active */
}
if ( isapnp_configured ) {
printk ( KERN_ERR " cs4232.c: ISA PnP configured, but not detected? \n " ) ;
goto fail ;
}
/*
* This version of the driver doesn ' t use the PnP method when configuring
* the card but a simplified method defined by Crystal . This means that
* just one CS4232 compatible device can exist on the system . Also this
* method conflicts with possible PnP support in the OS . For this reason
* driver is just a temporary kludge .
*
* Also the Cirrus / Crystal method doesn ' t always work . Try ISA PnP first ; )
*/
/*
* Repeat initialization few times since it doesn ' t always succeed in
* first time .
*/
for ( n = 0 ; n < 4 ; n + + )
{
/*
* Wake up the card by sending a 32 byte Crystal key to the key port .
*/
for ( i = 0 ; i < 32 ; i + + )
CS_OUT ( crystal_key [ i ] ) ;
sleep ( HZ / 10 ) ;
/*
* Now set the CSN ( Card Select Number ) .
*/
CS_OUT2 ( 0x06 , CSN_NUM ) ;
/*
* Then set some config bytes . First logical device 0
*/
CS_OUT2 ( 0x15 , 0x00 ) ; /* Select logical device 0 (WSS/SB/FM) */
CS_OUT3 ( 0x47 , ( base > > 8 ) & 0xff , base & 0xff ) ; /* WSS base */
2005-10-30 15:01:51 -08:00
if ( ! request_region ( 0x388 , 4 , " FM " ) ) /* Not free */
2005-04-16 15:20:36 -07:00
CS_OUT3 ( 0x48 , 0x00 , 0x00 ) /* FM base off */
2005-10-30 15:01:51 -08:00
else {
release_region ( 0x388 , 4 ) ;
2005-04-16 15:20:36 -07:00
CS_OUT3 ( 0x48 , 0x03 , 0x88 ) ; /* FM base 0x388 */
2005-10-30 15:01:51 -08:00
}
2005-04-16 15:20:36 -07:00
CS_OUT3 ( 0x42 , 0x00 , 0x00 ) ; /* SB base off */
CS_OUT2 ( 0x22 , irq ) ; /* SB+WSS IRQ */
CS_OUT2 ( 0x2a , dma1 ) ; /* SB+WSS DMA */
if ( dma2 ! = - 1 )
CS_OUT2 ( 0x25 , dma2 ) /* WSS DMA2 */
else
CS_OUT2 ( 0x25 , 4 ) ; /* No WSS DMA2 */
CS_OUT2 ( 0x33 , 0x01 ) ; /* Activate logical dev 0 */
sleep ( HZ / 10 ) ;
/*
* Initialize logical device 3 ( MPU )
*/
if ( mpu_base ! = 0 & & mpu_irq ! = 0 )
{
CS_OUT2 ( 0x15 , 0x03 ) ; /* Select logical device 3 (MPU) */
CS_OUT3 ( 0x47 , ( mpu_base > > 8 ) & 0xff , mpu_base & 0xff ) ; /* MPU base */
CS_OUT2 ( 0x22 , mpu_irq ) ; /* MPU IRQ */
CS_OUT2 ( 0x33 , 0x01 ) ; /* Activate logical dev 3 */
}
if ( synth_base ! = 0 )
{
CS_OUT2 ( 0x15 , 0x04 ) ; /* logical device 4 (WaveFront) */
CS_OUT3 ( 0x47 , ( synth_base > > 8 ) & 0xff ,
synth_base & 0xff ) ; /* base */
CS_OUT2 ( 0x22 , synth_irq ) ; /* IRQ */
CS_OUT2 ( 0x33 , 0x01 ) ; /* Activate logical dev 4 */
}
/*
* Finally activate the chip
*/
CS_OUT ( 0x79 ) ;
sleep ( HZ / 5 ) ;
/*
* Then try to detect the codec part of the chip
*/
if ( ad1848_detect ( ports , NULL , hw_config - > osp ) )
goto got_it ;
sleep ( HZ ) ;
}
fail :
release_region ( base , 4 ) ;
return 0 ;
got_it :
if ( dma2 = = - 1 )
dma2 = dma1 ;
hw_config - > slots [ 0 ] = ad1848_init ( " Crystal audio controller " , ports ,
irq ,
dma1 , /* Playback DMA */
dma2 , /* Capture DMA */
0 ,
hw_config - > osp ,
THIS_MODULE ) ;
if ( hw_config - > slots [ 0 ] ! = - 1 & &
audio_devs [ hw_config - > slots [ 0 ] ] - > mixer_dev ! = - 1 )
{
/* Assume the mixer map is as suggested in the CS4232 databook */
AD1848_REROUTE ( SOUND_MIXER_LINE1 , SOUND_MIXER_LINE ) ;
AD1848_REROUTE ( SOUND_MIXER_LINE2 , SOUND_MIXER_CD ) ;
AD1848_REROUTE ( SOUND_MIXER_LINE3 , SOUND_MIXER_SYNTH ) ; /* FM synth */
}
if ( mpu_base ! = 0 & & mpu_irq ! = 0 )
{
static struct address_info hw_config2 = {
0
} ; /* Ensure it's initialized */
hw_config2 . io_base = mpu_base ;
hw_config2 . irq = mpu_irq ;
hw_config2 . dma = - 1 ;
hw_config2 . dma2 = - 1 ;
hw_config2 . always_detect = 0 ;
hw_config2 . name = NULL ;
hw_config2 . driver_use_1 = 0 ;
hw_config2 . driver_use_2 = 0 ;
hw_config2 . card_subtype = 0 ;
if ( probe_uart401 ( & hw_config2 , THIS_MODULE ) )
{
mpu_detected = 1 ;
}
else
{
mpu_base = mpu_irq = 0 ;
}
hw_config - > slots [ 1 ] = hw_config2 . slots [ 1 ] ;
}
if ( bss )
enable_xctrl ( base ) ;
return 1 ;
}
static void __devexit unload_cs4232 ( struct address_info * hw_config )
{
int base = hw_config - > io_base , irq = hw_config - > irq ;
int dma1 = hw_config - > dma , dma2 = hw_config - > dma2 ;
if ( dma2 = = - 1 )
dma2 = dma1 ;
ad1848_unload ( base ,
irq ,
dma1 , /* Playback DMA */
dma2 , /* Capture DMA */
0 ) ;
sound_unload_audiodev ( hw_config - > slots [ 0 ] ) ;
if ( mpu_base ! = 0 & & mpu_irq ! = 0 & & mpu_detected )
{
static struct address_info hw_config2 =
{
0
} ; /* Ensure it's initialized */
hw_config2 . io_base = mpu_base ;
hw_config2 . irq = mpu_irq ;
hw_config2 . dma = - 1 ;
hw_config2 . dma2 = - 1 ;
hw_config2 . always_detect = 0 ;
hw_config2 . name = NULL ;
hw_config2 . driver_use_1 = 0 ;
hw_config2 . driver_use_2 = 0 ;
hw_config2 . card_subtype = 0 ;
hw_config2 . slots [ 1 ] = hw_config - > slots [ 1 ] ;
unload_uart401 ( & hw_config2 ) ;
}
}
static struct address_info cfg ;
static struct address_info cfg_mpu ;
static int __initdata io = - 1 ;
static int __initdata irq = - 1 ;
static int __initdata dma = - 1 ;
static int __initdata dma2 = - 1 ;
static int __initdata mpuio = - 1 ;
static int __initdata mpuirq = - 1 ;
static int __initdata synthio = - 1 ;
static int __initdata synthirq = - 1 ;
static int __initdata isapnp = 1 ;
MODULE_DESCRIPTION ( " CS4232 based soundcard driver " ) ;
MODULE_AUTHOR ( " Hannu Savolainen, Paul Barton-Davis " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " base I/O port for AD1848 " ) ;
module_param ( irq , int , 0 ) ;
MODULE_PARM_DESC ( irq , " IRQ for AD1848 chip " ) ;
module_param ( dma , int , 0 ) ;
MODULE_PARM_DESC ( dma , " 8 bit DMA for AD1848 chip " ) ;
module_param ( dma2 , int , 0 ) ;
MODULE_PARM_DESC ( dma2 , " 16 bit DMA for AD1848 chip " ) ;
module_param ( mpuio , int , 0 ) ;
MODULE_PARM_DESC ( mpuio , " MPU 401 base address " ) ;
module_param ( mpuirq , int , 0 ) ;
MODULE_PARM_DESC ( mpuirq , " MPU 401 IRQ " ) ;
module_param ( synthio , int , 0 ) ;
MODULE_PARM_DESC ( synthio , " Maui WaveTable base I/O port " ) ;
module_param ( synthirq , int , 0 ) ;
MODULE_PARM_DESC ( synthirq , " Maui WaveTable IRQ " ) ;
module_param ( isapnp , bool , 0 ) ;
MODULE_PARM_DESC ( isapnp , " Enable ISAPnP probing (default 1) " ) ;
module_param ( bss , bool , 0 ) ;
MODULE_PARM_DESC ( bss , " Enable Bose Sound System Support (default 0) " ) ;
/*
* Install a CS4232 based card . Need to have ad1848 and mpu401
* loaded ready .
*/
/* All cs4232 based cards have the main ad1848 card either as CSC0000 or
* CSC0100 . */
static const struct pnp_device_id cs4232_pnp_table [ ] = {
{ . id = " CSC0100 " , . driver_data = 0 } ,
{ . id = " CSC0000 " , . driver_data = 0 } ,
/* Guillemot Turtlebeach something appears to be cs4232 compatible
* ( untested ) */
{ . id = " GIM0100 " , . driver_data = 0 } ,
{ . id = " " }
} ;
MODULE_DEVICE_TABLE ( pnp , cs4232_pnp_table ) ;
static int cs4232_pnp_probe ( struct pnp_dev * dev , const struct pnp_device_id * dev_id )
{
struct address_info * isapnpcfg ;
isapnpcfg = ( struct address_info * ) kmalloc ( sizeof ( * isapnpcfg ) , GFP_KERNEL ) ;
if ( ! isapnpcfg )
return - ENOMEM ;
isapnpcfg - > irq = pnp_irq ( dev , 0 ) ;
isapnpcfg - > dma = pnp_dma ( dev , 0 ) ;
isapnpcfg - > dma2 = pnp_dma ( dev , 1 ) ;
isapnpcfg - > io_base = pnp_port_start ( dev , 0 ) ;
if ( probe_cs4232 ( isapnpcfg , TRUE ) = = 0 ) {
printk ( KERN_ERR " cs4232: ISA PnP card found, but not detected? \n " ) ;
kfree ( isapnpcfg ) ;
return - ENODEV ;
}
pnp_set_drvdata ( dev , isapnpcfg ) ;
return 0 ;
}
static void __devexit cs4232_pnp_remove ( struct pnp_dev * dev )
{
struct address_info * cfg = pnp_get_drvdata ( dev ) ;
if ( cfg ) {
unload_cs4232 ( cfg ) ;
kfree ( cfg ) ;
}
}
static struct pnp_driver cs4232_driver = {
. name = " cs4232 " ,
. id_table = cs4232_pnp_table ,
. probe = cs4232_pnp_probe ,
. remove = __devexit_p ( cs4232_pnp_remove ) ,
} ;
static int __init init_cs4232 ( void )
{
# ifdef CONFIG_SOUND_WAVEFRONT_MODULE
if ( synthio = = - 1 )
printk ( KERN_INFO " cs4232: set synthio and synthirq to use the wavefront facilities. \n " ) ;
else {
synth_base = synthio ;
synth_irq = synthirq ;
}
# else
if ( synthio ! = - 1 )
printk ( KERN_WARNING " cs4232: wavefront support not enabled in this driver. \n " ) ;
# endif
cfg . irq = - 1 ;
if ( isapnp & &
( pnp_register_driver ( & cs4232_driver ) > 0 )
)
return 0 ;
if ( io = = - 1 | | irq = = - 1 | | dma = = - 1 )
{
printk ( KERN_ERR " cs4232: Must set io, irq and dma. \n " ) ;
return - ENODEV ;
}
cfg . io_base = io ;
cfg . irq = irq ;
cfg . dma = dma ;
cfg . dma2 = dma2 ;
cfg_mpu . io_base = - 1 ;
cfg_mpu . irq = - 1 ;
if ( mpuio ! = - 1 & & mpuirq ! = - 1 ) {
cfg_mpu . io_base = mpuio ;
cfg_mpu . irq = mpuirq ;
probe_cs4232_mpu ( & cfg_mpu ) ; /* Bug always returns 0 not OK -- AC */
}
if ( probe_cs4232 ( & cfg , FALSE ) = = 0 )
return - ENODEV ;
return 0 ;
}
static void __exit cleanup_cs4232 ( void )
{
pnp_unregister_driver ( & cs4232_driver ) ;
if ( cfg . irq ! = - 1 )
unload_cs4232 ( & cfg ) ; /* Unloads global MPU as well, if needed */
}
module_init ( init_cs4232 ) ;
module_exit ( cleanup_cs4232 ) ;
# ifndef MODULE
static int __init setup_cs4232 ( char * str )
{
/* io, irq, dma, dma2 mpuio, mpuirq*/
int ints [ 7 ] ;
/* If we have isapnp cards, no need for options */
if ( pnp_register_driver ( & cs4232_driver ) > 0 )
return 1 ;
str = get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
io = ints [ 1 ] ;
irq = ints [ 2 ] ;
dma = ints [ 3 ] ;
dma2 = ints [ 4 ] ;
mpuio = ints [ 5 ] ;
mpuirq = ints [ 6 ] ;
return 1 ;
}
__setup ( " cs4232= " , setup_cs4232 ) ;
# endif