2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* sound / oss / sb_mixer . c
2005-04-17 02:20:36 +04:00
*
* The low level mixer driver for the Sound Blaster compatible cards .
*/
/*
* 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 .
*
*
* Thomas Sailer : ioctl code reworked ( vmalloc / vfree removed )
* Rolf Fokkens ( Dec 20 1998 ) : Moved ESS stuff into sb_ess . [ ch ]
* Stanislav Voronyi < stas @ esc . kharkov . com > : Support for AWE 3 DSE device ( Jun 7 1999 )
*/
# include "sound_config.h"
# define __SB_MIXER_C__
# include "sb.h"
# include "sb_mixer.h"
# include "sb_ess.h"
# define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
# define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
# define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME )
/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
* channel is the COVOX / DisneySoundSource emulation volume control
* on the mixer . It does NOT control speaker volume . Should have own
* mask eventually ?
*/
# define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER )
# define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD )
# define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD )
# define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
SOUND_MASK_IMIX )
/* These are the only devices that are working at the moment. Others could
* be added once they are identified and a method is found to control them .
*/
# define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | \
SOUND_MASK_VOLUME )
static mixer_tab sbpro_mix = {
MIX_ENT ( SOUND_MIXER_VOLUME , 0x22 , 7 , 4 , 0x22 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_BASS , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_TREBLE , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_SYNTH , 0x26 , 7 , 4 , 0x26 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_PCM , 0x04 , 7 , 4 , 0x04 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_SPEAKER , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_LINE , 0x2e , 7 , 4 , 0x2e , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_MIC , 0x0a , 2 , 3 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_CD , 0x28 , 7 , 4 , 0x28 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_IMIX , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_ALTPCM , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_RECLEV , 0x00 , 0 , 0 , 0x00 , 0 , 0 )
} ;
static mixer_tab sb16_mix = {
MIX_ENT ( SOUND_MIXER_VOLUME , 0x30 , 7 , 5 , 0x31 , 7 , 5 ) ,
MIX_ENT ( SOUND_MIXER_BASS , 0x46 , 7 , 4 , 0x47 , 7 , 4 ) ,
MIX_ENT ( SOUND_MIXER_TREBLE , 0x44 , 7 , 4 , 0x45 , 7 , 4 ) ,
MIX_ENT ( SOUND_MIXER_SYNTH , 0x34 , 7 , 5 , 0x35 , 7 , 5 ) ,
MIX_ENT ( SOUND_MIXER_PCM , 0x32 , 7 , 5 , 0x33 , 7 , 5 ) ,
MIX_ENT ( SOUND_MIXER_SPEAKER , 0x3b , 7 , 2 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_LINE , 0x38 , 7 , 5 , 0x39 , 7 , 5 ) ,
MIX_ENT ( SOUND_MIXER_MIC , 0x3a , 7 , 5 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_CD , 0x36 , 7 , 5 , 0x37 , 7 , 5 ) ,
MIX_ENT ( SOUND_MIXER_IMIX , 0x3c , 0 , 1 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_ALTPCM , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_RECLEV , 0x3f , 7 , 2 , 0x40 , 7 , 2 ) , /* Obsolete. Use IGAIN */
MIX_ENT ( SOUND_MIXER_IGAIN , 0x3f , 7 , 2 , 0x40 , 7 , 2 ) ,
MIX_ENT ( SOUND_MIXER_OGAIN , 0x41 , 7 , 2 , 0x42 , 7 , 2 )
} ;
static mixer_tab als007_mix =
{
MIX_ENT ( SOUND_MIXER_VOLUME , 0x62 , 7 , 4 , 0x62 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_BASS , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_TREBLE , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_SYNTH , 0x66 , 7 , 4 , 0x66 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_PCM , 0x64 , 7 , 4 , 0x64 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_SPEAKER , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_LINE , 0x6e , 7 , 4 , 0x6e , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_MIC , 0x6a , 2 , 3 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_CD , 0x68 , 7 , 4 , 0x68 , 3 , 4 ) ,
MIX_ENT ( SOUND_MIXER_IMIX , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_ALTPCM , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_RECLEV , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) , /* Obsolete. Use IGAIN */
MIX_ENT ( SOUND_MIXER_IGAIN , 0x00 , 0 , 0 , 0x00 , 0 , 0 ) ,
MIX_ENT ( SOUND_MIXER_OGAIN , 0x00 , 0 , 0 , 0x00 , 0 , 0 )
} ;
/* SM_GAMES Master volume is lower and PCM & FM volumes
higher than with SB Pro . This improves the
sound quality */
static int smg_default_levels [ 32 ] =
{
0x2020 , /* Master Volume */
0x4b4b , /* Bass */
0x4b4b , /* Treble */
0x6464 , /* FM */
0x6464 , /* PCM */
0x4b4b , /* PC Speaker */
0x4b4b , /* Ext Line */
0x0000 , /* Mic */
0x4b4b , /* CD */
0x4b4b , /* Recording monitor */
0x4b4b , /* SB PCM */
0x4b4b , /* Recording level */
0x4b4b , /* Input gain */
0x4b4b , /* Output gain */
0x4040 , /* Line1 */
0x4040 , /* Line2 */
0x1515 /* Line3 */
} ;
static int sb_default_levels [ 32 ] =
{
0x5a5a , /* Master Volume */
0x4b4b , /* Bass */
0x4b4b , /* Treble */
0x4b4b , /* FM */
0x4b4b , /* PCM */
0x4b4b , /* PC Speaker */
0x4b4b , /* Ext Line */
0x1010 , /* Mic */
0x4b4b , /* CD */
0x0000 , /* Recording monitor */
0x4b4b , /* SB PCM */
0x4b4b , /* Recording level */
0x4b4b , /* Input gain */
0x4b4b , /* Output gain */
0x4040 , /* Line1 */
0x4040 , /* Line2 */
0x1515 /* Line3 */
} ;
static unsigned char sb16_recmasks_L [ SOUND_MIXER_NRDEVICES ] =
{
0x00 , /* SOUND_MIXER_VOLUME */
0x00 , /* SOUND_MIXER_BASS */
0x00 , /* SOUND_MIXER_TREBLE */
0x40 , /* SOUND_MIXER_SYNTH */
0x00 , /* SOUND_MIXER_PCM */
0x00 , /* SOUND_MIXER_SPEAKER */
0x10 , /* SOUND_MIXER_LINE */
0x01 , /* SOUND_MIXER_MIC */
0x04 , /* SOUND_MIXER_CD */
0x00 , /* SOUND_MIXER_IMIX */
0x00 , /* SOUND_MIXER_ALTPCM */
0x00 , /* SOUND_MIXER_RECLEV */
0x00 , /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
} ;
static unsigned char sb16_recmasks_R [ SOUND_MIXER_NRDEVICES ] =
{
0x00 , /* SOUND_MIXER_VOLUME */
0x00 , /* SOUND_MIXER_BASS */
0x00 , /* SOUND_MIXER_TREBLE */
0x20 , /* SOUND_MIXER_SYNTH */
0x00 , /* SOUND_MIXER_PCM */
0x00 , /* SOUND_MIXER_SPEAKER */
0x08 , /* SOUND_MIXER_LINE */
0x01 , /* SOUND_MIXER_MIC */
0x02 , /* SOUND_MIXER_CD */
0x00 , /* SOUND_MIXER_IMIX */
0x00 , /* SOUND_MIXER_ALTPCM */
0x00 , /* SOUND_MIXER_RECLEV */
0x00 , /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
} ;
static char smw_mix_regs [ ] = /* Left mixer registers */
{
0x0b , /* SOUND_MIXER_VOLUME */
0x0d , /* SOUND_MIXER_BASS */
0x0d , /* SOUND_MIXER_TREBLE */
0x05 , /* SOUND_MIXER_SYNTH */
0x09 , /* SOUND_MIXER_PCM */
0x00 , /* SOUND_MIXER_SPEAKER */
0x03 , /* SOUND_MIXER_LINE */
0x01 , /* SOUND_MIXER_MIC */
0x07 , /* SOUND_MIXER_CD */
0x00 , /* SOUND_MIXER_IMIX */
0x00 , /* SOUND_MIXER_ALTPCM */
0x00 , /* SOUND_MIXER_RECLEV */
0x00 , /* SOUND_MIXER_IGAIN */
0x00 , /* SOUND_MIXER_OGAIN */
0x00 , /* SOUND_MIXER_LINE1 */
0x00 , /* SOUND_MIXER_LINE2 */
0x00 /* SOUND_MIXER_LINE3 */
} ;
static int sbmixnum = 1 ;
static void sb_mixer_reset ( sb_devc * devc ) ;
void sb_mixer_set_stereo ( sb_devc * devc , int mode )
{
sb_chgmixer ( devc , OUT_FILTER , STEREO_DAC , ( mode ? STEREO_DAC : MONO_DAC ) ) ;
}
static int detect_mixer ( sb_devc * devc )
{
/* Just trust the mixer is there */
return 1 ;
}
static void change_bits ( sb_devc * devc , unsigned char * regval , int dev , int chn , int newval )
{
unsigned char mask ;
int shift ;
mask = ( 1 < < ( * devc - > iomap ) [ dev ] [ chn ] . nbits ) - 1 ;
newval = ( int ) ( ( newval * mask ) + 50 ) / 100 ; /* Scale */
shift = ( * devc - > iomap ) [ dev ] [ chn ] . bitoffs - ( * devc - > iomap ) [ dev ] [ LEFT_CHN ] . nbits + 1 ;
* regval & = ~ ( mask < < shift ) ; /* Mask out previous value */
* regval | = ( newval & mask ) < < shift ; /* Set the new value */
}
static int sb_mixer_get ( sb_devc * devc , int dev )
{
if ( ! ( ( 1 < < dev ) & devc - > supported_devices ) )
return - EINVAL ;
return devc - > levels [ dev ] ;
}
void smw_mixer_init ( sb_devc * devc )
{
int i ;
sb_setmixer ( devc , 0x00 , 0x18 ) ; /* Mute unused (Telephone) line */
sb_setmixer ( devc , 0x10 , 0x38 ) ; /* Config register 2 */
devc - > supported_devices = 0 ;
for ( i = 0 ; i < sizeof ( smw_mix_regs ) ; i + + )
if ( smw_mix_regs [ i ] ! = 0 )
devc - > supported_devices | = ( 1 < < i ) ;
devc - > supported_rec_devices = devc - > supported_devices &
~ ( SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME ) ;
sb_mixer_reset ( devc ) ;
}
int sb_common_mixer_set ( sb_devc * devc , int dev , int left , int right )
{
int regoffs ;
unsigned char val ;
2006-03-25 14:08:07 +03:00
if ( ( dev < 0 ) | | ( dev > = devc - > iomap_sz ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
regoffs = ( * devc - > iomap ) [ dev ] [ LEFT_CHN ] . regno ;
if ( regoffs = = 0 )
return - EINVAL ;
val = sb_getmixer ( devc , regoffs ) ;
change_bits ( devc , & val , dev , LEFT_CHN , left ) ;
if ( ( * devc - > iomap ) [ dev ] [ RIGHT_CHN ] . regno ! = regoffs ) /*
* Change register
*/
{
sb_setmixer ( devc , regoffs , val ) ; /*
* Save the old one
*/
regoffs = ( * devc - > iomap ) [ dev ] [ RIGHT_CHN ] . regno ;
if ( regoffs = = 0 )
return left | ( left < < 8 ) ; /*
* Just left channel present
*/
val = sb_getmixer ( devc , regoffs ) ; /*
* Read the new one
*/
}
change_bits ( devc , & val , dev , RIGHT_CHN , right ) ;
sb_setmixer ( devc , regoffs , val ) ;
return left | ( right < < 8 ) ;
}
static int smw_mixer_set ( sb_devc * devc , int dev , int left , int right )
{
int reg , val ;
switch ( dev )
{
case SOUND_MIXER_VOLUME :
sb_setmixer ( devc , 0x0b , 96 - ( 96 * left / 100 ) ) ; /* 96=mute, 0=max */
sb_setmixer ( devc , 0x0c , 96 - ( 96 * right / 100 ) ) ;
break ;
case SOUND_MIXER_BASS :
case SOUND_MIXER_TREBLE :
devc - > levels [ dev ] = left | ( right < < 8 ) ;
/* Set left bass and treble values */
val = ( ( devc - > levels [ SOUND_MIXER_TREBLE ] & 0xff ) * 16 / ( unsigned ) 100 ) < < 4 ;
val | = ( ( devc - > levels [ SOUND_MIXER_BASS ] & 0xff ) * 16 / ( unsigned ) 100 ) & 0x0f ;
sb_setmixer ( devc , 0x0d , val ) ;
/* Set right bass and treble values */
val = ( ( ( devc - > levels [ SOUND_MIXER_TREBLE ] > > 8 ) & 0xff ) * 16 / ( unsigned ) 100 ) < < 4 ;
val | = ( ( ( devc - > levels [ SOUND_MIXER_BASS ] > > 8 ) & 0xff ) * 16 / ( unsigned ) 100 ) & 0x0f ;
sb_setmixer ( devc , 0x0e , val ) ;
break ;
default :
/* bounds check */
if ( dev < 0 | | dev > = ARRAY_SIZE ( smw_mix_regs ) )
return - EINVAL ;
reg = smw_mix_regs [ dev ] ;
if ( reg = = 0 )
return - EINVAL ;
sb_setmixer ( devc , reg , ( 24 - ( 24 * left / 100 ) ) | 0x20 ) ; /* 24=mute, 0=max */
sb_setmixer ( devc , reg + 1 , ( 24 - ( 24 * right / 100 ) ) | 0x40 ) ;
}
devc - > levels [ dev ] = left | ( right < < 8 ) ;
return left | ( right < < 8 ) ;
}
static int sb_mixer_set ( sb_devc * devc , int dev , int value )
{
int left = value & 0x000000ff ;
int right = ( value & 0x0000ff00 ) > > 8 ;
int retval ;
if ( left > 100 )
left = 100 ;
if ( right > 100 )
right = 100 ;
if ( ( dev < 0 ) | | ( dev > 31 ) )
return - EINVAL ;
if ( ! ( devc - > supported_devices & ( 1 < < dev ) ) ) /*
* Not supported
*/
return - EINVAL ;
/* Differentiate depending on the chipsets */
switch ( devc - > model ) {
case MDL_SMW :
retval = smw_mixer_set ( devc , dev , left , right ) ;
break ;
case MDL_ESS :
retval = ess_mixer_set ( devc , dev , left , right ) ;
break ;
default :
retval = sb_common_mixer_set ( devc , dev , left , right ) ;
}
if ( retval > = 0 ) devc - > levels [ dev ] = retval ;
return retval ;
}
/*
* set_recsrc doesn ' t apply to ES188x
*/
static void set_recsrc ( sb_devc * devc , int src )
{
sb_setmixer ( devc , RECORD_SRC , ( sb_getmixer ( devc , RECORD_SRC ) & ~ 7 ) | ( src & 0x7 ) ) ;
}
static int set_recmask ( sb_devc * devc , int mask )
{
int devmask , i ;
unsigned char regimageL , regimageR ;
devmask = mask & devc - > supported_rec_devices ;
switch ( devc - > model )
{
case MDL_SBPRO :
case MDL_ESS :
case MDL_JAZZ :
case MDL_SMW :
if ( devc - > model = = MDL_ESS & & ess_set_recmask ( devc , & devmask ) ) {
break ;
} ;
if ( devmask ! = SOUND_MASK_MIC & &
devmask ! = SOUND_MASK_LINE & &
devmask ! = SOUND_MASK_CD )
{
/*
* More than one device selected . Drop the
* previous selection
*/
devmask & = ~ devc - > recmask ;
}
if ( devmask ! = SOUND_MASK_MIC & &
devmask ! = SOUND_MASK_LINE & &
devmask ! = SOUND_MASK_CD )
{
/*
* More than one device selected . Default to
* mic
*/
devmask = SOUND_MASK_MIC ;
}
if ( devmask ^ devc - > recmask ) /*
* Input source changed
*/
{
switch ( devmask )
{
case SOUND_MASK_MIC :
set_recsrc ( devc , SRC__MIC ) ;
break ;
case SOUND_MASK_LINE :
set_recsrc ( devc , SRC__LINE ) ;
break ;
case SOUND_MASK_CD :
set_recsrc ( devc , SRC__CD ) ;
break ;
default :
set_recsrc ( devc , SRC__MIC ) ;
}
}
break ;
case MDL_SB16 :
if ( ! devmask )
devmask = SOUND_MASK_MIC ;
if ( devc - > submodel = = SUBMDL_ALS007 )
{
switch ( devmask )
{
case SOUND_MASK_LINE :
sb_setmixer ( devc , ALS007_RECORD_SRC , ALS007_LINE ) ;
break ;
case SOUND_MASK_CD :
sb_setmixer ( devc , ALS007_RECORD_SRC , ALS007_CD ) ;
break ;
case SOUND_MASK_SYNTH :
sb_setmixer ( devc , ALS007_RECORD_SRC , ALS007_SYNTH ) ;
break ;
default : /* Also takes care of SOUND_MASK_MIC case */
sb_setmixer ( devc , ALS007_RECORD_SRC , ALS007_MIC ) ;
break ;
}
}
else
{
regimageL = regimageR = 0 ;
for ( i = 0 ; i < SOUND_MIXER_NRDEVICES ; i + + )
{
if ( ( 1 < < i ) & devmask )
{
regimageL | = sb16_recmasks_L [ i ] ;
regimageR | = sb16_recmasks_R [ i ] ;
}
sb_setmixer ( devc , SB16_IMASK_L , regimageL ) ;
sb_setmixer ( devc , SB16_IMASK_R , regimageR ) ;
}
}
break ;
}
devc - > recmask = devmask ;
return devc - > recmask ;
}
static int set_outmask ( sb_devc * devc , int mask )
{
int devmask , i ;
unsigned char regimage ;
devmask = mask & devc - > supported_out_devices ;
switch ( devc - > model )
{
case MDL_SB16 :
if ( devc - > submodel = = SUBMDL_ALS007 )
break ;
else
{
regimage = 0 ;
for ( i = 0 ; i < SOUND_MIXER_NRDEVICES ; i + + )
{
if ( ( 1 < < i ) & devmask )
{
regimage | = ( sb16_recmasks_L [ i ] | sb16_recmasks_R [ i ] ) ;
}
sb_setmixer ( devc , SB16_OMASK , regimage ) ;
}
}
break ;
default :
break ;
}
devc - > outmask = devmask ;
return devc - > outmask ;
}
static int sb_mixer_ioctl ( int dev , unsigned int cmd , void __user * arg )
{
sb_devc * devc = mixer_devs [ dev ] - > devc ;
int val , ret ;
int __user * p = arg ;
/*
* Use ioctl ( fd , SOUND_MIXER_AGC , & mode ) to turn AGC off ( 0 ) or on ( 1 ) .
* Use ioctl ( fd , SOUND_MIXER_3DSE , & mode ) to turn 3 DSE off ( 0 ) or on ( 1 )
* or mode = = 2 put 3 DSE state to mode .
*/
if ( devc - > model = = MDL_SB16 ) {
if ( cmd = = SOUND_MIXER_AGC )
{
if ( get_user ( val , p ) )
return - EFAULT ;
sb_setmixer ( devc , 0x43 , ( ~ val ) & 0x01 ) ;
return 0 ;
}
if ( cmd = = SOUND_MIXER_3DSE )
{
/* I put here 15, but I don't know the exact version.
At least my 4.13 havn ' t 3 DSE , 4.16 has it . */
if ( devc - > minor < 15 )
return - EINVAL ;
if ( get_user ( val , p ) )
return - EFAULT ;
if ( val = = 0 | | val = = 1 )
sb_chgmixer ( devc , AWE_3DSE , 0x01 , val ) ;
else if ( val = = 2 )
{
ret = sb_getmixer ( devc , AWE_3DSE ) & 0x01 ;
return put_user ( ret , p ) ;
}
else
return - EINVAL ;
return 0 ;
}
}
if ( ( ( cmd > > 8 ) & 0xff ) = = ' M ' )
{
if ( _SIOC_DIR ( cmd ) & _SIOC_WRITE )
{
if ( get_user ( val , p ) )
return - EFAULT ;
switch ( cmd & 0xff )
{
case SOUND_MIXER_RECSRC :
ret = set_recmask ( devc , val ) ;
break ;
case SOUND_MIXER_OUTSRC :
ret = set_outmask ( devc , val ) ;
break ;
default :
ret = sb_mixer_set ( devc , cmd & 0xff , val ) ;
}
}
else switch ( cmd & 0xff )
{
case SOUND_MIXER_RECSRC :
ret = devc - > recmask ;
break ;
case SOUND_MIXER_OUTSRC :
ret = devc - > outmask ;
break ;
case SOUND_MIXER_DEVMASK :
ret = devc - > supported_devices ;
break ;
case SOUND_MIXER_STEREODEVS :
ret = devc - > supported_devices ;
/* The ESS seems to have stereo mic controls */
if ( devc - > model = = MDL_ESS )
ret & = ~ ( SOUND_MASK_SPEAKER | SOUND_MASK_IMIX ) ;
else if ( devc - > model ! = MDL_JAZZ & & devc - > model ! = MDL_SMW )
ret & = ~ ( SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX ) ;
break ;
case SOUND_MIXER_RECMASK :
ret = devc - > supported_rec_devices ;
break ;
case SOUND_MIXER_OUTMASK :
ret = devc - > supported_out_devices ;
break ;
case SOUND_MIXER_CAPS :
ret = devc - > mixer_caps ;
break ;
default :
ret = sb_mixer_get ( devc , cmd & 0xff ) ;
break ;
}
return put_user ( ret , p ) ;
} else
return - EINVAL ;
}
static struct mixer_operations sb_mixer_operations =
{
. owner = THIS_MODULE ,
. id = " SB " ,
. name = " Sound Blaster " ,
. ioctl = sb_mixer_ioctl
} ;
static struct mixer_operations als007_mixer_operations =
{
. owner = THIS_MODULE ,
. id = " ALS007 " ,
. name = " Avance ALS-007 " ,
. ioctl = sb_mixer_ioctl
} ;
static void sb_mixer_reset ( sb_devc * devc )
{
char name [ 32 ] ;
int i ;
sprintf ( name , " SB_%d " , devc - > sbmixnum ) ;
if ( devc - > sbmo . sm_games )
devc - > levels = load_mixer_volumes ( name , smg_default_levels , 1 ) ;
else
devc - > levels = load_mixer_volumes ( name , sb_default_levels , 1 ) ;
for ( i = 0 ; i < SOUND_MIXER_NRDEVICES ; i + + )
sb_mixer_set ( devc , i , devc - > levels [ i ] ) ;
if ( devc - > model ! = MDL_ESS | | ! ess_mixer_reset ( devc ) ) {
set_recmask ( devc , SOUND_MASK_MIC ) ;
} ;
}
int sb_mixer_init ( sb_devc * devc , struct module * owner )
{
int mixer_type = 0 ;
int m ;
devc - > sbmixnum = sbmixnum + + ;
devc - > levels = NULL ;
sb_setmixer ( devc , 0x00 , 0 ) ; /* Reset mixer */
if ( ! ( mixer_type = detect_mixer ( devc ) ) )
return 0 ; /* No mixer. Why? */
switch ( devc - > model )
{
case MDL_ESSPCI :
case MDL_YMPCI :
case MDL_SBPRO :
case MDL_AZTECH :
case MDL_JAZZ :
devc - > mixer_caps = SOUND_CAP_EXCL_INPUT ;
devc - > supported_devices = SBPRO_MIXER_DEVICES ;
devc - > supported_rec_devices = SBPRO_RECORDING_DEVICES ;
devc - > iomap = & sbpro_mix ;
devc - > iomap_sz = ARRAY_SIZE ( sbpro_mix ) ;
break ;
case MDL_ESS :
ess_mixer_init ( devc ) ;
break ;
case MDL_SMW :
devc - > mixer_caps = SOUND_CAP_EXCL_INPUT ;
devc - > supported_devices = 0 ;
devc - > supported_rec_devices = 0 ;
devc - > iomap = & sbpro_mix ;
devc - > iomap_sz = ARRAY_SIZE ( sbpro_mix ) ;
smw_mixer_init ( devc ) ;
break ;
case MDL_SB16 :
devc - > mixer_caps = 0 ;
devc - > supported_rec_devices = SB16_RECORDING_DEVICES ;
devc - > supported_out_devices = SB16_OUTFILTER_DEVICES ;
if ( devc - > submodel ! = SUBMDL_ALS007 )
{
devc - > supported_devices = SB16_MIXER_DEVICES ;
devc - > iomap = & sb16_mix ;
devc - > iomap_sz = ARRAY_SIZE ( sb16_mix ) ;
}
else
{
devc - > supported_devices = ALS007_MIXER_DEVICES ;
devc - > iomap = & als007_mix ;
devc - > iomap_sz = ARRAY_SIZE ( als007_mix ) ;
}
break ;
default :
printk ( KERN_WARNING " sb_mixer: Unsupported mixer type %d \n " , devc - > model ) ;
return 0 ;
}
m = sound_alloc_mixerdev ( ) ;
if ( m = = - 1 )
return 0 ;
2006-12-13 11:35:56 +03:00
mixer_devs [ m ] = kmalloc ( sizeof ( struct mixer_operations ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( mixer_devs [ m ] = = NULL )
{
printk ( KERN_ERR " sb_mixer: Can't allocate memory \n " ) ;
sound_unload_mixerdev ( m ) ;
return 0 ;
}
if ( devc - > submodel ! = SUBMDL_ALS007 )
memcpy ( ( char * ) mixer_devs [ m ] , ( char * ) & sb_mixer_operations , sizeof ( struct mixer_operations ) ) ;
else
memcpy ( ( char * ) mixer_devs [ m ] , ( char * ) & als007_mixer_operations , sizeof ( struct mixer_operations ) ) ;
mixer_devs [ m ] - > devc = devc ;
if ( owner )
mixer_devs [ m ] - > owner = owner ;
devc - > my_mixerdev = m ;
sb_mixer_reset ( devc ) ;
return 1 ;
}
void sb_mixer_unload ( sb_devc * devc )
{
if ( devc - > my_mixerdev = = - 1 )
return ;
kfree ( mixer_devs [ devc - > my_mixerdev ] ) ;
sound_unload_mixerdev ( devc - > my_mixerdev ) ;
sbmixnum - - ;
}