2006-10-04 20:08:43 +04:00
/*
* ALSA Driver for the PT2258 volume controller .
*
* Copyright ( c ) 2006 Jochen Voss < voss @ seehuhn . de >
*
* 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 <sound/core.h>
# include <sound/control.h>
# include <sound/tlv.h>
# include <sound/i2c.h>
# include <sound/pt2258.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2006-10-04 20:08:43 +04:00
MODULE_AUTHOR ( " Jochen Voss <voss@seehuhn.de> " ) ;
MODULE_DESCRIPTION ( " PT2258 volume controller (Princeton Technology Corp.) " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define PT2258_CMD_RESET 0xc0
# define PT2258_CMD_UNMUTE 0xf8
# define PT2258_CMD_MUTE 0xf9
static const unsigned char pt2258_channel_code [ 12 ] = {
0x80 , 0x90 , /* channel 1: -10dB, -1dB */
0x40 , 0x50 , /* channel 2: -10dB, -1dB */
0x00 , 0x10 , /* channel 3: -10dB, -1dB */
0x20 , 0x30 , /* channel 4: -10dB, -1dB */
0x60 , 0x70 , /* channel 5: -10dB, -1dB */
0xa0 , 0xb0 /* channel 6: -10dB, -1dB */
} ;
int snd_pt2258_reset ( struct snd_pt2258 * pt )
{
unsigned char bytes [ 2 ] ;
int i ;
/* reset chip */
bytes [ 0 ] = PT2258_CMD_RESET ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 1 ) ! = 1 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
/* mute all channels */
pt - > mute = 1 ;
bytes [ 0 ] = PT2258_CMD_MUTE ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 1 ) ! = 1 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
/* set all channels to 0dB */
for ( i = 0 ; i < 6 ; + + i )
pt - > volume [ i ] = 0 ;
bytes [ 0 ] = 0xd0 ;
bytes [ 1 ] = 0xe0 ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 2 ) ! = 2 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
return 0 ;
__error :
snd_i2c_unlock ( pt - > i2c_bus ) ;
snd_printk ( KERN_ERR " PT2258 reset failed \n " ) ;
return - EIO ;
}
static int pt2258_stereo_volume_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 2 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 79 ;
return 0 ;
}
static int pt2258_stereo_volume_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_pt2258 * pt = kcontrol - > private_data ;
int base = kcontrol - > private_value ;
/* chip does not support register reads */
ucontrol - > value . integer . value [ 0 ] = 79 - pt - > volume [ base ] ;
ucontrol - > value . integer . value [ 1 ] = 79 - pt - > volume [ base + 1 ] ;
return 0 ;
}
static int pt2258_stereo_volume_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_pt2258 * pt = kcontrol - > private_data ;
int base = kcontrol - > private_value ;
unsigned char bytes [ 2 ] ;
int val0 , val1 ;
val0 = 79 - ucontrol - > value . integer . value [ 0 ] ;
val1 = 79 - ucontrol - > value . integer . value [ 1 ] ;
2007-11-15 18:17:24 +03:00
if ( val0 < 0 | | val0 > 79 | | val1 < 0 | | val1 > 79 )
return - EINVAL ;
2006-10-04 20:08:43 +04:00
if ( val0 = = pt - > volume [ base ] & & val1 = = pt - > volume [ base + 1 ] )
return 0 ;
pt - > volume [ base ] = val0 ;
bytes [ 0 ] = pt2258_channel_code [ 2 * base ] | ( val0 / 10 ) ;
bytes [ 1 ] = pt2258_channel_code [ 2 * base + 1 ] | ( val0 % 10 ) ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 2 ) ! = 2 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
pt - > volume [ base + 1 ] = val1 ;
bytes [ 0 ] = pt2258_channel_code [ 2 * base + 2 ] | ( val1 / 10 ) ;
bytes [ 1 ] = pt2258_channel_code [ 2 * base + 3 ] | ( val1 % 10 ) ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 2 ) ! = 2 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
return 1 ;
__error :
snd_i2c_unlock ( pt - > i2c_bus ) ;
snd_printk ( KERN_ERR " PT2258 access failed \n " ) ;
return - EIO ;
}
2007-07-23 17:42:26 +04:00
# define pt2258_switch_info snd_ctl_boolean_mono_info
2006-10-04 20:08:43 +04:00
static int pt2258_switch_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_pt2258 * pt = kcontrol - > private_data ;
ucontrol - > value . integer . value [ 0 ] = ! pt - > mute ;
return 0 ;
}
static int pt2258_switch_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_pt2258 * pt = kcontrol - > private_data ;
unsigned char bytes [ 2 ] ;
int val ;
val = ! ucontrol - > value . integer . value [ 0 ] ;
if ( pt - > mute = = val )
return 0 ;
pt - > mute = val ;
bytes [ 0 ] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE ;
snd_i2c_lock ( pt - > i2c_bus ) ;
if ( snd_i2c_sendbytes ( pt - > i2c_dev , bytes , 1 ) ! = 1 )
goto __error ;
snd_i2c_unlock ( pt - > i2c_bus ) ;
return 1 ;
__error :
snd_i2c_unlock ( pt - > i2c_bus ) ;
snd_printk ( KERN_ERR " PT2258 access failed 2 \n " ) ;
return - EIO ;
}
2007-01-29 17:33:49 +03:00
static const DECLARE_TLV_DB_SCALE ( pt2258_db_scale , - 7900 , 100 , 0 ) ;
2006-10-04 20:08:43 +04:00
int snd_pt2258_build_controls ( struct snd_pt2258 * pt )
{
struct snd_kcontrol_new knew ;
char * names [ 3 ] = {
" Mic Loopback Playback Volume " ,
" Line Loopback Playback Volume " ,
" CD Loopback Playback Volume "
} ;
int i , err ;
for ( i = 0 ; i < 3 ; + + i ) {
memset ( & knew , 0 , sizeof ( knew ) ) ;
knew . name = names [ i ] ;
knew . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
knew . count = 1 ;
knew . access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ;
knew . private_value = 2 * i ;
knew . info = pt2258_stereo_volume_info ;
knew . get = pt2258_stereo_volume_get ;
knew . put = pt2258_stereo_volume_put ;
knew . tlv . p = pt2258_db_scale ;
err = snd_ctl_add ( pt - > card , snd_ctl_new1 ( & knew , pt ) ) ;
if ( err < 0 )
return err ;
}
memset ( & knew , 0 , sizeof ( knew ) ) ;
knew . name = " Loopback Switch " ;
knew . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
knew . info = pt2258_switch_info ;
knew . get = pt2258_switch_get ;
knew . put = pt2258_switch_put ;
knew . access = 0 ;
err = snd_ctl_add ( pt - > card , snd_ctl_new1 ( & knew , pt ) ) ;
if ( err < 0 )
return err ;
return 0 ;
}
EXPORT_SYMBOL ( snd_pt2258_reset ) ;
EXPORT_SYMBOL ( snd_pt2258_build_controls ) ;