2005-04-17 02:20:36 +04:00
/*
* Initialisation code for Cyrix / NatSemi VSA1 softaudio
*
* ( C ) Copyright 2003 Red Hat Inc < alan @ redhat . com >
*
* XpressAudio ( tm ) is used on the Cyrix MediaGX ( now NatSemi Geode ) systems .
* The older version ( VSA1 ) provides fairly good soundblaster emulation
* although there are a couple of bugs : large DMA buffers break record ,
* and the MPU event handling seems suspect . VSA2 allows the native driver
* to control the AC97 audio engine directly and requires a different driver .
*
* Thanks to National Semiconductor for providing the needed information
* on the XpressAudio ( tm ) internals .
*
* 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 , 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 .
*
* TO DO :
* Investigate whether we can portably support Cognac ( 5520 ) in the
* same manner .
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/pci.h>
# include "sound_config.h"
# include "sb.h"
/*
* Read a soundblaster compatible mixer register .
* In this case we are actually reading an SMI trap
* not real hardware .
*/
static u8 __devinit mixer_read ( unsigned long io , u8 reg )
{
outb ( reg , io + 4 ) ;
udelay ( 20 ) ;
reg = inb ( io + 5 ) ;
udelay ( 20 ) ;
return reg ;
}
static int __devinit probe_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
struct address_info * hw_config ;
unsigned long base ;
void __iomem * mem ;
unsigned long io ;
u16 map ;
u8 irq , dma8 , dma16 ;
int oldquiet ;
extern int sb_be_quiet ;
base = pci_resource_start ( pdev , 0 ) ;
if ( base = = 0UL )
return 1 ;
mem = ioremap ( base , 128 ) ;
if ( mem = = 0UL )
return 1 ;
map = readw ( mem + 0x18 ) ; /* Read the SMI enables */
iounmap ( mem ) ;
/* Map bits
0 : 1 * 0x20 + 0x200 = sb base
2 sb enable
3 adlib enable
5 MPU enable 0x330
6 MPU enable 0x300
The other bits may be used internally so must be masked */
io = 0x220 + 0x20 * ( map & 3 ) ;
if ( map & ( 1 < < 2 ) )
printk ( KERN_INFO " kahlua: XpressAudio at 0x%lx \n " , io ) ;
else
return 1 ;
if ( map & ( 1 < < 5 ) )
printk ( KERN_INFO " kahlua: MPU at 0x300 \n " ) ;
else if ( map & ( 1 < < 6 ) )
printk ( KERN_INFO " kahlua: MPU at 0x330 \n " ) ;
irq = mixer_read ( io , 0x80 ) & 0x0F ;
dma8 = mixer_read ( io , 0x81 ) ;
// printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
if ( dma8 & 0x20 )
dma16 = 5 ;
else if ( dma8 & 0x40 )
dma16 = 6 ;
else if ( dma8 & 0x80 )
dma16 = 7 ;
else
{
printk ( KERN_ERR " kahlua: No 16bit DMA enabled. \n " ) ;
return 1 ;
}
if ( dma8 & 0x01 )
dma8 = 0 ;
else if ( dma8 & 0x02 )
dma8 = 1 ;
else if ( dma8 & 0x08 )
dma8 = 3 ;
else
{
printk ( KERN_ERR " kahlua: No 8bit DMA enabled. \n " ) ;
return 1 ;
}
if ( irq & 1 )
irq = 9 ;
else if ( irq & 2 )
irq = 5 ;
else if ( irq & 4 )
irq = 7 ;
else if ( irq & 8 )
irq = 10 ;
else
{
printk ( KERN_ERR " kahlua: SB IRQ not set. \n " ) ;
return 1 ;
}
printk ( KERN_INFO " kahlua: XpressAudio on IRQ %d, DMA %d, %d \n " ,
irq , dma8 , dma16 ) ;
2007-02-14 11:33:16 +03:00
hw_config = kzalloc ( sizeof ( struct address_info ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( hw_config = = NULL )
{
printk ( KERN_ERR " kahlua: out of memory. \n " ) ;
return 1 ;
}
pci_set_drvdata ( pdev , hw_config ) ;
hw_config - > io_base = io ;
hw_config - > irq = irq ;
hw_config - > dma = dma8 ;
hw_config - > dma2 = dma16 ;
hw_config - > name = " Cyrix XpressAudio " ;
hw_config - > driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ ;
if ( ! request_region ( io , 16 , " soundblaster " ) )
goto err_out_free ;
if ( sb_dsp_detect ( hw_config , 0 , 0 , NULL ) = = 0 )
{
printk ( KERN_ERR " kahlua: audio not responding. \n " ) ;
release_region ( io , 16 ) ;
goto err_out_free ;
}
oldquiet = sb_be_quiet ;
sb_be_quiet = 1 ;
if ( sb_dsp_init ( hw_config , THIS_MODULE ) )
{
sb_be_quiet = oldquiet ;
goto err_out_free ;
}
sb_be_quiet = oldquiet ;
return 0 ;
err_out_free :
pci_set_drvdata ( pdev , NULL ) ;
kfree ( hw_config ) ;
return 1 ;
}
static void __devexit remove_one ( struct pci_dev * pdev )
{
struct address_info * hw_config = pci_get_drvdata ( pdev ) ;
sb_dsp_unload ( hw_config , 0 ) ;
pci_set_drvdata ( pdev , NULL ) ;
kfree ( hw_config ) ;
}
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " Kahlua VSA1 PCI Audio " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* 5530 only . The 5510 / 5520 decode is different .
*/
static struct pci_device_id id_tbl [ ] = {
{ PCI_VENDOR_ID_CYRIX , PCI_DEVICE_ID_CYRIX_5530_AUDIO , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( pci , id_tbl ) ;
static struct pci_driver kahlua_driver = {
. name = " kahlua " ,
. id_table = id_tbl ,
. probe = probe_one ,
. remove = __devexit_p ( remove_one ) ,
} ;
static int __init kahlua_init_module ( void )
{
printk ( KERN_INFO " Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc \n " ) ;
2005-12-07 02:33:15 +03:00
return pci_register_driver ( & kahlua_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __devexit kahlua_cleanup_module ( void )
{
pci_unregister_driver ( & kahlua_driver ) ;
}
module_init ( kahlua_init_module ) ;
module_exit ( kahlua_cleanup_module ) ;