2005-04-16 15:20:36 -07:00
/*
2006-10-03 23:01:26 +02:00
* sound / oss / opl3sa2 . c
2005-04-16 15:20:36 -07:00
*
* A low level driver for Yamaha OPL3 - SA2 and SA3 cards .
* NOTE : All traces of the name OPL3 - SAx have now ( December 2000 ) been
* removed from the driver code , as an email exchange with Yamaha
* provided the information that the YMF - 719 is indeed just a
* re - badged 715.
*
* Copyright 1998 - 2001 Scott Murray < scott @ spiteful . org >
*
* Originally based on the CS4232 driver ( in cs4232 . c ) by Hannu Savolainen
* and others . Now incorporates code / ideas from pss . c , also by Hannu
* Savolainen . Both of those files are distributed with the following
* license :
*
* " Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS / Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE ( GPL )
* Version 2 ( June 1991 ) . See the " COPYING " file distributed with this software
* for more info . "
*
* As such , in accordance with the above license , this file , opl3sa2 . c , is
* distributed under the GNU GENERAL PUBLIC LICENSE ( GPL ) Version 2 ( June 1991 ) .
* See the " COPYING " file distributed with this software for more information .
*
* Change History
* - - - - - - - - - - - - - -
* Scott Murray Original driver ( Jun 14 , 1998 )
* Paul J . Y . Lahaie Changed probing / attach code order
* Scott Murray Added mixer support ( Dec 03 , 1998 )
* Scott Murray Changed detection code to be more forgiving ,
* added force option as last resort ,
* fixed ioctl return values . ( Dec 30 , 1998 )
* Scott Murray Simpler detection code should work all the time now
* ( with thanks to Ben Hutchings for the heuristic ) ,
* removed now unnecessary force option . ( Jan 5 , 1999 )
* Christoph Hellwig Adapted to module_init / module_exit ( Mar 4 , 2000 )
* Scott Murray Reworked SA2 versus SA3 mixer code , updated chipset
* version detection code ( again ! ) . ( Dec 5 , 2000 )
* Scott Murray Adjusted master volume mixer scaling . ( Dec 6 , 2000 )
* Scott Murray Based on a patch by Joel Yliluoma ( aka Bisqwit ) ,
* integrated wide mixer and adjusted mic , bass , treble
* scaling . ( Dec 6 , 2000 )
* Scott Murray Based on a patch by Peter Englmaier , integrated
* ymode and loopback options . ( Dec 6 , 2000 )
* Scott Murray Inspired by a patch by Peter Englmaier , and based on
* what ALSA does , added initialization code for the
* default DMA and IRQ settings . ( Dec 6 , 2000 )
* Scott Murray Added some more checks to the card detection code ,
* based on what ALSA does . ( Dec 12 , 2000 )
* Scott Murray Inspired by similar patches from John Fremlin ,
* Jim Radford , Mike Rolig , and Ingmar Steen , added 2.4
* ISA PnP API support , mainly based on bits from
* sb_card . c and awe_wave . c . ( Dec 12 , 2000 )
* Scott Murray Some small cleanups to the init code output .
* ( Jan 7 , 2001 )
* Zwane Mwaikambo Added PM support . ( Dec 4 2001 )
*
* Adam Belay Converted driver to new PnP Layer ( Oct 12 , 2002 )
* Zwane Mwaikambo Code , data structure cleanups . ( Feb 15 2002 )
* Zwane Mwaikambo Free resources during auxiliary device probe
* failures ( Apr 29 2002 )
*
*/
# include <linux/pnp.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/delay.h>
# include "sound_config.h"
# include "ad1848.h"
# include "mpu401.h"
# define OPL3SA2_MODULE_NAME "opl3sa2"
# define PFX OPL3SA2_MODULE_NAME ": "
/* Useful control port indexes: */
# define OPL3SA2_PM 0x01
# define OPL3SA2_SYS_CTRL 0x02
# define OPL3SA2_IRQ_CONFIG 0x03
# define OPL3SA2_DMA_CONFIG 0x06
# define OPL3SA2_MASTER_LEFT 0x07
# define OPL3SA2_MASTER_RIGHT 0x08
# define OPL3SA2_MIC 0x09
# define OPL3SA2_MISC 0x0A
# define OPL3SA3_WIDE 0x14
# define OPL3SA3_BASS 0x15
# define OPL3SA3_TREBLE 0x16
/* Useful constants: */
# define DEFAULT_VOLUME 50
# define DEFAULT_MIC 50
# define DEFAULT_TIMBRE 0
/* Power saving modes */
# define OPL3SA2_PM_MODE0 0x00
# define OPL3SA2_PM_MODE1 0x04 /* PSV */
# define OPL3SA2_PM_MODE2 0x05 /* PSV | PDX */
# define OPL3SA2_PM_MODE3 0x27 /* ADOWN | PSV | PDN | PDX */
/* For checking against what the card returns: */
# define VERSION_UNKNOWN 0
# define VERSION_YMF711 1
# define VERSION_YMF715 2
# define VERSION_YMF715B 3
# define VERSION_YMF715E 4
/* also assuming that anything > 4 but <= 7 is a 715E */
/* Chipset type constants for use below */
# define CHIPSET_UNKNOWN -1
# define CHIPSET_OPL3SA2 0
# define CHIPSET_OPL3SA3 1
static const char * CHIPSET_TABLE [ ] = { " OPL3-SA2 " , " OPL3-SA3 " } ;
# ifdef CONFIG_PNP
# define OPL3SA2_CARDS_MAX 4
# else
# define OPL3SA2_CARDS_MAX 1
# endif
/* This should be pretty obvious */
static int opl3sa2_cards_num ;
typedef struct {
/* device resources */
unsigned short cfg_port ;
struct address_info cfg ;
struct address_info cfg_mss ;
struct address_info cfg_mpu ;
# ifdef CONFIG_PNP
/* PnP Stuff */
struct pnp_dev * pdev ;
int activated ; /* Whether said devices have been activated */
# endif
unsigned int card ;
int chipset ; /* What's my version(s)? */
char * chipset_name ;
/* mixer data */
int mixer ;
unsigned int volume_l ;
unsigned int volume_r ;
unsigned int mic ;
unsigned int bass_l ;
unsigned int bass_r ;
unsigned int treble_l ;
unsigned int treble_r ;
unsigned int wide_l ;
unsigned int wide_r ;
} opl3sa2_state_t ;
static opl3sa2_state_t opl3sa2_state [ OPL3SA2_CARDS_MAX ] ;
/* Our parameters */
static int __initdata io = - 1 ;
static int __initdata mss_io = - 1 ;
static int __initdata mpu_io = - 1 ;
static int __initdata irq = - 1 ;
static int __initdata dma = - 1 ;
static int __initdata dma2 = - 1 ;
static int __initdata ymode = - 1 ;
static int __initdata loopback = - 1 ;
# ifdef CONFIG_PNP
/* PnP specific parameters */
static int __initdata isapnp = 1 ;
static int __initdata multiple = 1 ;
/* Whether said devices have been activated */
static int opl3sa2_activated [ OPL3SA2_CARDS_MAX ] ;
# else
static int __initdata isapnp ; /* = 0 */
static int __initdata multiple ; /* = 0 */
# endif
MODULE_DESCRIPTION ( " Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver). " ) ;
MODULE_AUTHOR ( " Scott Murray <scott@spiteful.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " Set I/O base of OPL3-SA2 or SA3 card (usually 0x370. Address must be even and must be from 0x100 to 0xFFE) " ) ;
module_param ( mss_io , int , 0 ) ;
MODULE_PARM_DESC ( mss_io , " Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48) " ) ;
module_param ( mpu_io , int , 0 ) ;
MODULE_PARM_DESC ( mpu_io , " Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334) " ) ;
module_param ( irq , int , 0 ) ;
2005-04-16 15:25:55 -07:00
MODULE_PARM_DESC ( irq , " Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12) " ) ;
2005-04-16 15:20:36 -07:00
module_param ( dma , int , 0 ) ;
MODULE_PARM_DESC ( dma , " Set MSS (audio) first DMA channel (0, 1, 3) " ) ;
module_param ( dma2 , int , 0 ) ;
MODULE_PARM_DESC ( dma2 , " Set MSS (audio) second DMA channel (0, 1, 3) " ) ;
module_param ( ymode , int , 0 ) ;
MODULE_PARM_DESC ( ymode , " Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi) " ) ;
module_param ( loopback , int , 0 ) ;
MODULE_PARM_DESC ( loopback , " Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback) " ) ;
# ifdef CONFIG_PNP
module_param ( isapnp , bool , 0 ) ;
MODULE_PARM_DESC ( isapnp , " When set to 0, ISA PnP support will be disabled " ) ;
module_param ( multiple , bool , 0 ) ;
MODULE_PARM_DESC ( multiple , " When set to 0, will not search for multiple cards " ) ;
# endif
/*
* Standard read and write functions
*/
static inline void opl3sa2_write ( unsigned short port ,
unsigned char index ,
unsigned char data )
{
outb_p ( index , port ) ;
outb ( data , port + 1 ) ;
}
static inline void opl3sa2_read ( unsigned short port ,
unsigned char index ,
unsigned char * data )
{
outb_p ( index , port ) ;
* data = inb ( port + 1 ) ;
}
/*
* All of the mixer functions . . .
*/
static void opl3sa2_set_volume ( opl3sa2_state_t * devc , int left , int right )
{
static unsigned char scale [ 101 ] = {
0x0f , 0x0f , 0x0f , 0x0f , 0x0f , 0x0f , 0x0f , 0x0e , 0x0e , 0x0e ,
0x0e , 0x0e , 0x0e , 0x0d , 0x0d , 0x0d , 0x0d , 0x0d , 0x0d , 0x0c ,
0x0c , 0x0c , 0x0c , 0x0c , 0x0c , 0x0c , 0x0b , 0x0b , 0x0b , 0x0b ,
0x0b , 0x0b , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x09 , 0x09 ,
0x09 , 0x09 , 0x09 , 0x09 , 0x09 , 0x08 , 0x08 , 0x08 , 0x08 , 0x08 ,
0x08 , 0x07 , 0x07 , 0x07 , 0x07 , 0x07 , 0x07 , 0x06 , 0x06 , 0x06 ,
0x06 , 0x06 , 0x06 , 0x06 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 ,
0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x03 , 0x03 , 0x03 , 0x03 ,
0x03 , 0x03 , 0x03 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00
} ;
unsigned char vol ;
vol = scale [ left ] ;
/* If level is zero, turn on mute */
if ( ! left )
vol | = 0x80 ;
opl3sa2_write ( devc - > cfg_port , OPL3SA2_MASTER_LEFT , vol ) ;
vol = scale [ right ] ;
/* If level is zero, turn on mute */
if ( ! right )
vol | = 0x80 ;
opl3sa2_write ( devc - > cfg_port , OPL3SA2_MASTER_RIGHT , vol ) ;
}
static void opl3sa2_set_mic ( opl3sa2_state_t * devc , int level )
{
unsigned char vol = 0x1F ;
if ( ( level > = 0 ) & & ( level < = 100 ) )
vol = 0x1F - ( unsigned char ) ( 32 * level / 101 ) ;
/* If level is zero, turn on mute */
if ( ! level )
vol | = 0x80 ;
opl3sa2_write ( devc - > cfg_port , OPL3SA2_MIC , vol ) ;
}
static void opl3sa3_set_bass ( opl3sa2_state_t * devc , int left , int right )
{
unsigned char bass ;
bass = left ? ( ( unsigned char ) ( 8 * left / 101 ) ) : 0 ;
bass | = ( right ? ( ( unsigned char ) ( 8 * right / 101 ) ) : 0 ) < < 4 ;
opl3sa2_write ( devc - > cfg_port , OPL3SA3_BASS , bass ) ;
}
static void opl3sa3_set_treble ( opl3sa2_state_t * devc , int left , int right )
{
unsigned char treble ;
treble = left ? ( ( unsigned char ) ( 8 * left / 101 ) ) : 0 ;
treble | = ( right ? ( ( unsigned char ) ( 8 * right / 101 ) ) : 0 ) < < 4 ;
opl3sa2_write ( devc - > cfg_port , OPL3SA3_TREBLE , treble ) ;
}
static void opl3sa2_mixer_reset ( opl3sa2_state_t * devc )
{
if ( devc ) {
opl3sa2_set_volume ( devc , DEFAULT_VOLUME , DEFAULT_VOLUME ) ;
devc - > volume_l = devc - > volume_r = DEFAULT_VOLUME ;
opl3sa2_set_mic ( devc , DEFAULT_MIC ) ;
devc - > mic = DEFAULT_MIC ;
if ( devc - > chipset = = CHIPSET_OPL3SA3 ) {
opl3sa3_set_bass ( devc , DEFAULT_TIMBRE , DEFAULT_TIMBRE ) ;
devc - > bass_l = devc - > bass_r = DEFAULT_TIMBRE ;
opl3sa3_set_treble ( devc , DEFAULT_TIMBRE , DEFAULT_TIMBRE ) ;
devc - > treble_l = devc - > treble_r = DEFAULT_TIMBRE ;
}
}
}
static inline void arg_to_vol_mono ( unsigned int vol , int * value )
{
int left ;
left = vol & 0x00ff ;
if ( left > 100 )
left = 100 ;
* value = left ;
}
static inline void arg_to_vol_stereo ( unsigned int vol , int * aleft , int * aright )
{
arg_to_vol_mono ( vol , aleft ) ;
arg_to_vol_mono ( vol > > 8 , aright ) ;
}
static inline int ret_vol_mono ( int vol )
{
return ( ( vol < < 8 ) | vol ) ;
}
static inline int ret_vol_stereo ( int left , int right )
{
return ( ( right < < 8 ) | left ) ;
}
static int opl3sa2_mixer_ioctl ( int dev , unsigned int cmd , void __user * arg )
{
int retval , value , cmdf = cmd & 0xff ;
int __user * p = ( int __user * ) arg ;
opl3sa2_state_t * devc = & opl3sa2_state [ dev ] ;
switch ( cmdf ) {
case SOUND_MIXER_VOLUME :
case SOUND_MIXER_MIC :
case SOUND_MIXER_DEVMASK :
case SOUND_MIXER_STEREODEVS :
case SOUND_MIXER_RECMASK :
case SOUND_MIXER_RECSRC :
case SOUND_MIXER_CAPS :
break ;
default :
return - EINVAL ;
}
if ( ( ( cmd > > 8 ) & 0xff ) ! = ' M ' )
return - EINVAL ;
retval = 0 ;
if ( _SIOC_DIR ( cmd ) & _SIOC_WRITE ) {
switch ( cmdf ) {
case SOUND_MIXER_VOLUME :
retval = get_user ( value , ( unsigned __user * ) arg ) ;
if ( retval )
break ;
arg_to_vol_stereo ( value , & devc - > volume_l , & devc - > volume_r ) ;
opl3sa2_set_volume ( devc , devc - > volume_l , devc - > volume_r ) ;
value = ret_vol_stereo ( devc - > volume_l , devc - > volume_r ) ;
retval = put_user ( value , p ) ;
break ;
case SOUND_MIXER_MIC :
retval = get_user ( value , ( unsigned __user * ) arg ) ;
if ( retval )
break ;
arg_to_vol_mono ( value , & devc - > mic ) ;
opl3sa2_set_mic ( devc , devc - > mic ) ;
value = ret_vol_mono ( devc - > mic ) ;
retval = put_user ( value , p ) ;
break ;
default :
retval = - EINVAL ;
}
}
else {
/*
* Return parameters
*/
switch ( cmdf ) {
case SOUND_MIXER_DEVMASK :
retval = put_user ( SOUND_MASK_VOLUME | SOUND_MASK_MIC , p ) ;
break ;
case SOUND_MIXER_STEREODEVS :
retval = put_user ( SOUND_MASK_VOLUME , p ) ;
break ;
case SOUND_MIXER_RECMASK :
/* No recording devices */
retval = put_user ( 0 , p ) ;
break ;
case SOUND_MIXER_CAPS :
retval = put_user ( SOUND_CAP_EXCL_INPUT , p ) ;
break ;
case SOUND_MIXER_RECSRC :
/* No recording source */
retval = put_user ( 0 , p ) ;
break ;
case SOUND_MIXER_VOLUME :
value = ret_vol_stereo ( devc - > volume_l , devc - > volume_r ) ;
retval = put_user ( value , p ) ;
break ;
case SOUND_MIXER_MIC :
value = ret_vol_mono ( devc - > mic ) ;
put_user ( value , p ) ;
break ;
default :
retval = - EINVAL ;
}
}
return retval ;
}
/* opl3sa2_mixer_ioctl end */
static int opl3sa3_mixer_ioctl ( int dev , unsigned int cmd , void __user * arg )
{
int value , retval , cmdf = cmd & 0xff ;
opl3sa2_state_t * devc = & opl3sa2_state [ dev ] ;
switch ( cmdf ) {
case SOUND_MIXER_BASS :
value = ret_vol_stereo ( devc - > bass_l , devc - > bass_r ) ;
retval = put_user ( value , ( int __user * ) arg ) ;
break ;
case SOUND_MIXER_TREBLE :
value = ret_vol_stereo ( devc - > treble_l , devc - > treble_r ) ;
retval = put_user ( value , ( int __user * ) arg ) ;
break ;
case SOUND_MIXER_DIGITAL1 :
value = ret_vol_stereo ( devc - > wide_l , devc - > wide_r ) ;
retval = put_user ( value , ( int __user * ) arg ) ;
break ;
default :
retval = - EINVAL ;
}
return retval ;
}
/* opl3sa3_mixer_ioctl end */
static struct mixer_operations opl3sa2_mixer_operations =
{
. owner = THIS_MODULE ,
. id = " OPL3-SA2 " ,
. name = " Yamaha OPL3-SA2 " ,
. ioctl = opl3sa2_mixer_ioctl
} ;
static struct mixer_operations opl3sa3_mixer_operations =
{
. owner = THIS_MODULE ,
. id = " OPL3-SA3 " ,
. name = " Yamaha OPL3-SA3 " ,
. ioctl = opl3sa3_mixer_ioctl
} ;
/* End of mixer-related stuff */
/*
* Component probe , attach , unload functions
*/
static inline void __exit unload_opl3sa2_mpu ( struct address_info * hw_config )
{
unload_mpu401 ( hw_config ) ;
}
static void __init attach_opl3sa2_mss ( struct address_info * hw_config , struct resource * ports )
{
int initial_mixers ;
initial_mixers = num_mixers ;
attach_ms_sound ( hw_config , ports , THIS_MODULE ) ; /* Slot 0 */
if ( hw_config - > slots [ 0 ] ! = - 1 ) {
/* Did the MSS driver install? */
if ( num_mixers = = ( initial_mixers + 1 ) ) {
2006-01-15 02:00:17 +01:00
/* The MSS mixer is installed, reroute mixers appropriately */
2005-04-16 15:20:36 -07:00
AD1848_REROUTE ( SOUND_MIXER_LINE1 , SOUND_MIXER_CD ) ;
AD1848_REROUTE ( SOUND_MIXER_LINE2 , SOUND_MIXER_SYNTH ) ;
AD1848_REROUTE ( SOUND_MIXER_LINE3 , SOUND_MIXER_LINE ) ;
}
else {
printk ( KERN_ERR PFX " MSS mixer not installed? \n " ) ;
}
}
}
static inline void __exit unload_opl3sa2_mss ( struct address_info * hw_config )
{
unload_ms_sound ( hw_config ) ;
}
static int __init probe_opl3sa2 ( struct address_info * hw_config , int card )
{
unsigned char misc ;
unsigned char tmp ;
unsigned char version ;
/*
* Try and allocate our I / O port range .
*/
if ( ! request_region ( hw_config - > io_base , 2 , OPL3SA2_MODULE_NAME ) ) {
printk ( KERN_ERR PFX " Control I/O port %#x not free \n " ,
hw_config - > io_base ) ;
goto out_nodev ;
}
/*
* Check if writing to the read - only version bits of the miscellaneous
* register succeeds or not ( it should not ) .
*/
opl3sa2_read ( hw_config - > io_base , OPL3SA2_MISC , & misc ) ;
opl3sa2_write ( hw_config - > io_base , OPL3SA2_MISC , misc ^ 0x07 ) ;
opl3sa2_read ( hw_config - > io_base , OPL3SA2_MISC , & tmp ) ;
if ( tmp ! = misc ) {
printk ( KERN_ERR PFX " Control I/O port %#x is not a YMF7xx chipset! \n " ,
hw_config - > io_base ) ;
goto out_region ;
}
/*
* Check if the MIC register is accessible .
*/
opl3sa2_read ( hw_config - > io_base , OPL3SA2_MIC , & tmp ) ;
opl3sa2_write ( hw_config - > io_base , OPL3SA2_MIC , 0x8a ) ;
opl3sa2_read ( hw_config - > io_base , OPL3SA2_MIC , & tmp ) ;
if ( ( tmp & 0x9f ) ! = 0x8a ) {
printk ( KERN_ERR
PFX " Control I/O port %#x is not a YMF7xx chipset! \n " ,
hw_config - > io_base ) ;
goto out_region ;
}
opl3sa2_write ( hw_config - > io_base , OPL3SA2_MIC , tmp ) ;
/*
* Determine chipset type ( SA2 or SA3 )
*
* This is done by looking at the chipset version in the lower 3 bits
* of the miscellaneous register .
*/
version = misc & 0x07 ;
printk ( KERN_DEBUG PFX " Chipset version = %#x \n " , version ) ;
switch ( version ) {
case 0 :
opl3sa2_state [ card ] . chipset = CHIPSET_UNKNOWN ;
printk ( KERN_ERR
PFX " Unknown Yamaha audio controller version \n " ) ;
break ;
case VERSION_YMF711 :
opl3sa2_state [ card ] . chipset = CHIPSET_OPL3SA2 ;
printk ( KERN_INFO PFX " Found OPL3-SA2 (YMF711) \n " ) ;
break ;
case VERSION_YMF715 :
opl3sa2_state [ card ] . chipset = CHIPSET_OPL3SA3 ;
printk ( KERN_INFO
PFX " Found OPL3-SA3 (YMF715 or YMF719) \n " ) ;
break ;
case VERSION_YMF715B :
opl3sa2_state [ card ] . chipset = CHIPSET_OPL3SA3 ;
printk ( KERN_INFO
PFX " Found OPL3-SA3 (YMF715B or YMF719B) \n " ) ;
break ;
case VERSION_YMF715E :
default :
opl3sa2_state [ card ] . chipset = CHIPSET_OPL3SA3 ;
printk ( KERN_INFO
PFX " Found OPL3-SA3 (YMF715E or YMF719E) \n " ) ;
break ;
}
if ( opl3sa2_state [ card ] . chipset ! = CHIPSET_UNKNOWN ) {
/* Generate a pretty name */
opl3sa2_state [ card ] . chipset_name = ( char * ) CHIPSET_TABLE [ opl3sa2_state [ card ] . chipset ] ;
return 0 ;
}
out_region :
release_region ( hw_config - > io_base , 2 ) ;
out_nodev :
return - ENODEV ;
}
static void __init attach_opl3sa2 ( struct address_info * hw_config , int card )
{
/* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */
opl3sa2_write ( hw_config - > io_base , OPL3SA2_IRQ_CONFIG , 0x0d ) ;
/* Initialize DMA configuration */
if ( hw_config - > dma2 = = hw_config - > dma ) {
/* Want DMA configuration DMA-B: -, DMA-A: WSS-P+WSS-R */
opl3sa2_write ( hw_config - > io_base , OPL3SA2_DMA_CONFIG , 0x03 ) ;
}
else {
/* Want DMA configuration DMA-B: WSS-R, DMA-A: WSS-P */
opl3sa2_write ( hw_config - > io_base , OPL3SA2_DMA_CONFIG , 0x21 ) ;
}
}
static void __init attach_opl3sa2_mixer ( struct address_info * hw_config , int card )
{
struct mixer_operations * mixer_operations ;
opl3sa2_state_t * devc = & opl3sa2_state [ card ] ;
/* Install master mixer */
if ( devc - > chipset = = CHIPSET_OPL3SA3 ) {
mixer_operations = & opl3sa3_mixer_operations ;
}
else {
mixer_operations = & opl3sa2_mixer_operations ;
}
devc - > cfg_port = hw_config - > io_base ;
devc - > mixer = sound_install_mixer ( MIXER_DRIVER_VERSION ,
mixer_operations - > name ,
mixer_operations ,
sizeof ( struct mixer_operations ) ,
devc ) ;
if ( devc - > mixer < 0 ) {
printk ( KERN_ERR PFX " Could not install %s master mixer \n " ,
mixer_operations - > name ) ;
}
else {
opl3sa2_mixer_reset ( devc ) ;
}
}
static void opl3sa2_clear_slots ( struct address_info * hw_config )
{
int i ;
for ( i = 0 ; i < 6 ; i + + ) {
hw_config - > slots [ i ] = - 1 ;
}
}
static void __init opl3sa2_set_ymode ( struct address_info * hw_config , int ymode )
{
/*
* Set the Yamaha 3 D enhancement mode ( aka Ymersion ) if asked to and
* it ' s supported .
*
* 0 : Desktop ( aka normal ) 5 - 12 cm speakers
* 1 : Notebook PC mode 1 3 cm speakers
* 2 : Notebook PC mode 2 1.5 cm speakers
* 3 : Hi - fi 16 - 38 cm speakers
*/
if ( ymode > = 0 & & ymode < = 3 ) {
unsigned char sys_ctrl ;
opl3sa2_read ( hw_config - > io_base , OPL3SA2_SYS_CTRL , & sys_ctrl ) ;
sys_ctrl = ( sys_ctrl & 0xcf ) | ( ( ymode & 3 ) < < 4 ) ;
opl3sa2_write ( hw_config - > io_base , OPL3SA2_SYS_CTRL , sys_ctrl ) ;
}
else {
printk ( KERN_ERR PFX " not setting ymode, it must be one of 0,1,2,3 \n " ) ;
}
}
static void __init opl3sa2_set_loopback ( struct address_info * hw_config , int loopback )
{
if ( loopback > = 0 & & loopback < = 1 ) {
unsigned char misc ;
opl3sa2_read ( hw_config - > io_base , OPL3SA2_MISC , & misc ) ;
misc = ( misc & 0xef ) | ( ( loopback & 1 ) < < 4 ) ;
opl3sa2_write ( hw_config - > io_base , OPL3SA2_MISC , misc ) ;
}
else {
printk ( KERN_ERR PFX " not setting loopback, it must be either 0 or 1 \n " ) ;
}
}
static void __exit unload_opl3sa2 ( struct address_info * hw_config , int card )
{
/* Release control ports */
release_region ( hw_config - > io_base , 2 ) ;
/* Unload mixer */
if ( opl3sa2_state [ card ] . mixer > = 0 )
sound_unload_mixerdev ( opl3sa2_state [ card ] . mixer ) ;
}
# ifdef CONFIG_PNP
static struct pnp_device_id pnp_opl3sa2_list [ ] = {
{ . id = " YMH0021 " , . driver_data = 0 } ,
{ . id = " " }
} ;
MODULE_DEVICE_TABLE ( pnp , pnp_opl3sa2_list ) ;
static int opl3sa2_pnp_probe ( struct pnp_dev * dev , const struct pnp_device_id * dev_id )
{
int card = opl3sa2_cards_num ;
/* we don't actually want to return an error as the user may have specified
* no multiple card search
*/
if ( opl3sa2_cards_num = = OPL3SA2_CARDS_MAX )
return 0 ;
opl3sa2_activated [ card ] = 1 ;
/* Our own config: */
opl3sa2_state [ card ] . cfg . io_base = pnp_port_start ( dev , 4 ) ;
opl3sa2_state [ card ] . cfg . irq = pnp_irq ( dev , 0 ) ;
opl3sa2_state [ card ] . cfg . dma = pnp_dma ( dev , 0 ) ;
opl3sa2_state [ card ] . cfg . dma2 = pnp_dma ( dev , 1 ) ;
/* The MSS config: */
opl3sa2_state [ card ] . cfg_mss . io_base = pnp_port_start ( dev , 1 ) ;
opl3sa2_state [ card ] . cfg_mss . irq = pnp_irq ( dev , 0 ) ;
opl3sa2_state [ card ] . cfg_mss . dma = pnp_dma ( dev , 0 ) ;
opl3sa2_state [ card ] . cfg_mss . dma2 = pnp_dma ( dev , 1 ) ;
opl3sa2_state [ card ] . cfg_mss . card_subtype = 1 ; /* No IRQ or DMA setup */
opl3sa2_state [ card ] . cfg_mpu . io_base = pnp_port_start ( dev , 3 ) ;
opl3sa2_state [ card ] . cfg_mpu . irq = pnp_irq ( dev , 0 ) ;
opl3sa2_state [ card ] . cfg_mpu . dma = - 1 ;
opl3sa2_state [ card ] . cfg_mpu . dma2 = - 1 ;
opl3sa2_state [ card ] . cfg_mpu . always_detect = 1 ; /* It's there, so use shared IRQs */
/* Call me paranoid: */
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg ) ;
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg_mss ) ;
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg_mpu ) ;
opl3sa2_state [ card ] . pdev = dev ;
opl3sa2_cards_num + + ;
return 0 ;
}
static struct pnp_driver opl3sa2_driver = {
. name = " opl3sa2 " ,
. id_table = pnp_opl3sa2_list ,
. probe = opl3sa2_pnp_probe ,
} ;
# endif /* CONFIG_PNP */
/* End of component functions */
/*
* Install OPL3 - SA2 based card ( s ) .
*
* Need to have ad1848 and mpu401 loaded ready .
*/
static int __init init_opl3sa2 ( void )
{
int card , max ;
/* Sanitize isapnp and multiple settings */
isapnp = isapnp ! = 0 ? 1 : 0 ;
multiple = multiple ! = 0 ? 1 : 0 ;
max = ( multiple & & isapnp ) ? OPL3SA2_CARDS_MAX : 1 ;
# ifdef CONFIG_PNP
if ( isapnp ) {
pnp_register_driver ( & opl3sa2_driver ) ;
if ( ! opl3sa2_cards_num ) {
printk ( KERN_INFO PFX " No PnP cards found \n " ) ;
isapnp = 0 ;
}
max = opl3sa2_cards_num ;
}
# endif
for ( card = 0 ; card < max ; card + + ) {
/* If a user wants an I/O then assume they meant it */
struct resource * ports ;
int base ;
if ( ! isapnp ) {
if ( io = = - 1 | | irq = = - 1 | | dma = = - 1 | |
dma2 = = - 1 | | mss_io = = - 1 ) {
printk ( KERN_ERR
PFX " io, mss_io, irq, dma, and dma2 must be set \n " ) ;
return - EINVAL ;
}
opl3sa2_cards_num + + ;
/*
* Our own config :
* ( NOTE : IRQ and DMA aren ' t used , so they ' re set to
* give pretty output from conf_printf . : )
*/
opl3sa2_state [ card ] . cfg . io_base = io ;
opl3sa2_state [ card ] . cfg . irq = irq ;
opl3sa2_state [ card ] . cfg . dma = dma ;
opl3sa2_state [ card ] . cfg . dma2 = dma2 ;
/* The MSS config: */
opl3sa2_state [ card ] . cfg_mss . io_base = mss_io ;
opl3sa2_state [ card ] . cfg_mss . irq = irq ;
opl3sa2_state [ card ] . cfg_mss . dma = dma ;
opl3sa2_state [ card ] . cfg_mss . dma2 = dma2 ;
opl3sa2_state [ card ] . cfg_mss . card_subtype = 1 ; /* No IRQ or DMA setup */
opl3sa2_state [ card ] . cfg_mpu . io_base = mpu_io ;
opl3sa2_state [ card ] . cfg_mpu . irq = irq ;
opl3sa2_state [ card ] . cfg_mpu . dma = - 1 ;
opl3sa2_state [ card ] . cfg_mpu . always_detect = 1 ; /* Use shared IRQs */
/* Call me paranoid: */
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg ) ;
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg_mss ) ;
opl3sa2_clear_slots ( & opl3sa2_state [ card ] . cfg_mpu ) ;
}
/* FIXME: leak */
if ( probe_opl3sa2 ( & opl3sa2_state [ card ] . cfg , card ) )
return - ENODEV ;
base = opl3sa2_state [ card ] . cfg_mss . io_base ;
if ( ! request_region ( base , 4 , " WSS config " ) )
goto failed ;
ports = request_region ( base + 4 , 4 , " ad1848 " ) ;
if ( ! ports )
goto failed2 ;
if ( ! probe_ms_sound ( & opl3sa2_state [ card ] . cfg_mss , ports ) ) {
/*
* If one or more cards are already registered , don ' t
* return an error but print a warning . Note , this
* should never really happen unless the hardware or
* ISA PnP screwed up .
*/
release_region ( base + 4 , 4 ) ;
failed2 :
release_region ( base , 4 ) ;
failed :
release_region ( opl3sa2_state [ card ] . cfg . io_base , 2 ) ;
if ( opl3sa2_cards_num ) {
printk ( KERN_WARNING
PFX " There was a problem probing one "
" of the ISA PNP cards, continuing \n " ) ;
opl3sa2_cards_num - - ;
continue ;
} else
return - ENODEV ;
}
attach_opl3sa2 ( & opl3sa2_state [ card ] . cfg , card ) ;
conf_printf ( opl3sa2_state [ card ] . chipset_name , & opl3sa2_state [ card ] . cfg ) ;
attach_opl3sa2_mixer ( & opl3sa2_state [ card ] . cfg , card ) ;
attach_opl3sa2_mss ( & opl3sa2_state [ card ] . cfg_mss , ports ) ;
/* ewww =) */
opl3sa2_state [ card ] . card = card ;
/*
* Set the Yamaha 3 D enhancement mode ( aka Ymersion ) if asked to and
* it ' s supported .
*/
if ( ymode ! = - 1 ) {
if ( opl3sa2_state [ card ] . chipset = = CHIPSET_OPL3SA2 ) {
printk ( KERN_ERR
PFX " ymode not supported on OPL3-SA2 \n " ) ;
}
else {
opl3sa2_set_ymode ( & opl3sa2_state [ card ] . cfg , ymode ) ;
}
}
/* Set A/D input to Mono loopback if asked to. */
if ( loopback ! = - 1 ) {
opl3sa2_set_loopback ( & opl3sa2_state [ card ] . cfg , loopback ) ;
}
/* Attach MPU if we've been asked to do so, failure isn't fatal */
if ( opl3sa2_state [ card ] . cfg_mpu . io_base ! = - 1 ) {
int base = opl3sa2_state [ card ] . cfg_mpu . io_base ;
struct resource * ports ;
ports = request_region ( base , 2 , " mpu401 " ) ;
if ( ! ports )
goto out ;
if ( ! probe_mpu401 ( & opl3sa2_state [ card ] . cfg_mpu , ports ) ) {
release_region ( base , 2 ) ;
goto out ;
}
if ( attach_mpu401 ( & opl3sa2_state [ card ] . cfg_mpu , THIS_MODULE ) ) {
printk ( KERN_ERR PFX " failed to attach MPU401 \n " ) ;
opl3sa2_state [ card ] . cfg_mpu . slots [ 1 ] = - 1 ;
}
}
}
out :
if ( isapnp ) {
printk ( KERN_NOTICE PFX " %d PnP card(s) found. \n " , opl3sa2_cards_num ) ;
}
return 0 ;
}
/*
* Uninstall OPL3 - SA2 based card ( s ) .
*/
static void __exit cleanup_opl3sa2 ( void )
{
int card ;
for ( card = 0 ; card < opl3sa2_cards_num ; card + + ) {
if ( opl3sa2_state [ card ] . cfg_mpu . slots [ 1 ] ! = - 1 ) {
unload_opl3sa2_mpu ( & opl3sa2_state [ card ] . cfg_mpu ) ;
}
unload_opl3sa2_mss ( & opl3sa2_state [ card ] . cfg_mss ) ;
unload_opl3sa2 ( & opl3sa2_state [ card ] . cfg , card ) ;
# ifdef CONFIG_PNP
pnp_unregister_driver ( & opl3sa2_driver ) ;
# endif
}
}
module_init ( init_opl3sa2 ) ;
module_exit ( cleanup_opl3sa2 ) ;
# ifndef MODULE
static int __init setup_opl3sa2 ( char * str )
{
/* io, irq, dma, dma2,... */
# ifdef CONFIG_PNP
int ints [ 11 ] ;
# else
int ints [ 9 ] ;
# endif
str = get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
io = ints [ 1 ] ;
irq = ints [ 2 ] ;
dma = ints [ 3 ] ;
dma2 = ints [ 4 ] ;
mss_io = ints [ 5 ] ;
mpu_io = ints [ 6 ] ;
ymode = ints [ 7 ] ;
loopback = ints [ 8 ] ;
# ifdef CONFIG_PNP
isapnp = ints [ 9 ] ;
multiple = ints [ 10 ] ;
# endif
return 1 ;
}
__setup ( " opl3sa2= " , setup_opl3sa2 ) ;
# endif