2005-04-16 15:20:36 -07:00
/*
* For the TDA9875 chip
* ( The TDA9875 is used on the Diamond DTV2000 french version
* Other cards probably use these chips as well . )
* This driver will not complain if used with any
* other i2c device with the same address .
*
* Copyright ( c ) 2000 Guillaume Delvit based on Gerd Knorr source and
* Eric Sandeen
2006-08-25 16:53:10 -03:00
* Copyright ( c ) 2006 Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-04-16 15:20:36 -07:00
* This code is placed under the terms of the GNU General Public License
* Based on tda9855 . c by Steve VanDeBogart ( vandebo @ uclink . berkeley . edu )
* Which was based on tda8425 . c by Greg Alexander ( c ) 1998
*
* OPTIONS :
* debug - set to 1 if you ' d like to see debug messages
*
* Revision : 0.1 - original version
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/i2c.h>
2008-12-18 13:43:45 -03:00
# include <linux/videodev2.h>
# include <media/v4l2-device.h>
# include <media/v4l2-i2c-drv-legacy.h>
2006-03-23 21:45:24 -03:00
# include <media/i2c-addr.h>
2005-04-16 15:20:36 -07:00
static int debug ; /* insmod parameter */
module_param ( debug , int , S_IRUGO | S_IWUSR ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Addresses to scan */
static unsigned short normal_i2c [ ] = {
2006-03-18 08:31:34 -03:00
I2C_ADDR_TDA9875 > > 1 ,
2005-04-16 15:20:36 -07:00
I2C_CLIENT_END
} ;
2008-07-18 00:50:58 -03:00
2005-04-16 15:20:36 -07:00
I2C_CLIENT_INSMOD ;
/* This is a superset of the TDA9875 */
struct tda9875 {
2008-12-18 13:43:45 -03:00
struct v4l2_subdev sd ;
2005-04-16 15:20:36 -07:00
int rvol , lvol ;
int bass , treble ;
} ;
2008-12-18 13:43:45 -03:00
static inline struct tda9875 * to_state ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct tda9875 , sd ) ;
}
2005-04-16 15:20:36 -07:00
# define dprintk if (debug) printk
/* The TDA9875 is made by Philips Semiconductor
* http : //www.semiconductors.philips.com
* TDA9875 : I2C - bus controlled DSP audio processor , FM demodulator
*
*/
/* subaddresses for TDA9875 */
# define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/
# define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */
# define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/
# define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/
# define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/
# define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/
# define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/
# define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/
# define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/
# define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/
# define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/
# define TDA9875_MVL 0x1a /* Main volume gauche */
# define TDA9875_MVR 0x1b /* Main volume droite */
# define TDA9875_MBA 0x1d /* Main Basse */
# define TDA9875_MTR 0x1e /* Main treble */
# define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/
# define TDA9875_AVL 0x20 /* Auxilary volume gauche */
# define TDA9875_AVR 0x21 /* Auxilary volume droite */
# define TDA9875_ABA 0x22 /* Auxilary Basse */
# define TDA9875_ATR 0x23 /* Auxilary treble */
# define TDA9875_MSR 0x02 /* Monitor select register */
# define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */
# define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */
# define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */
# define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */
# define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */
# define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */
# define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/
# define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/
# define TDA9875_FMAT 0x0b /* FM Matrix regirter*/
/* values */
# define TDA9875_MUTE_ON 0xff /* general mute */
# define TDA9875_MUTE_OFF 0xcc /* general no mute */
/* Begin code */
2008-12-18 13:43:45 -03:00
static int tda9875_write ( struct v4l2_subdev * sd , int subaddr , unsigned char val )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-04-16 15:20:36 -07:00
unsigned char buffer [ 2 ] ;
2008-12-18 13:43:45 -03:00
v4l2_dbg ( 1 , debug , sd , " Writing %d 0x%x \n " , subaddr , val ) ;
2005-04-16 15:20:36 -07:00
buffer [ 0 ] = subaddr ;
buffer [ 1 ] = val ;
2008-12-18 13:43:45 -03:00
if ( 2 ! = i2c_master_send ( client , buffer , 2 ) ) {
v4l2_warn ( sd , " I/O error, trying (write %d 0x%x) \n " ,
2005-04-16 15:20:36 -07:00
subaddr , val ) ;
return - 1 ;
}
return 0 ;
}
2008-12-18 13:43:45 -03:00
static int i2c_read_register ( struct i2c_client * client , int addr , int reg )
2005-04-16 15:20:36 -07:00
{
2005-11-08 21:37:43 -08:00
unsigned char write [ 1 ] ;
unsigned char read [ 1 ] ;
struct i2c_msg msgs [ 2 ] = {
{ addr , 0 , 1 , write } ,
{ addr , I2C_M_RD , 1 , read }
} ;
2008-12-18 13:43:45 -03:00
2005-11-08 21:37:43 -08:00
write [ 0 ] = reg ;
2008-12-18 13:43:45 -03:00
if ( 2 ! = i2c_transfer ( client - > adapter , msgs , 2 ) ) {
v4l_warn ( client , " I/O error (read2) \n " ) ;
2005-11-08 21:37:43 -08:00
return - 1 ;
}
2008-12-18 13:43:45 -03:00
v4l_dbg ( 1 , debug , client , " chip_read2: reg%d=0x%x \n " , reg , read [ 0 ] ) ;
2005-11-08 21:37:43 -08:00
return read [ 0 ] ;
2005-04-16 15:20:36 -07:00
}
2008-12-18 13:43:45 -03:00
static void tda9875_set ( struct v4l2_subdev * sd )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
struct tda9875 * tda = to_state ( sd ) ;
2005-04-16 15:20:36 -07:00
unsigned char a ;
2008-12-18 13:43:45 -03:00
v4l2_dbg ( 1 , debug , sd , " tda9875_set(%04x,%04x,%04x,%04x) \n " ,
tda - > lvol , tda - > rvol , tda - > bass , tda - > treble ) ;
2005-04-16 15:20:36 -07:00
a = tda - > lvol & 0xff ;
2008-12-18 13:43:45 -03:00
tda9875_write ( sd , TDA9875_MVL , a ) ;
2005-04-16 15:20:36 -07:00
a = tda - > rvol & 0xff ;
2008-12-18 13:43:45 -03:00
tda9875_write ( sd , TDA9875_MVR , a ) ;
2005-04-16 15:20:36 -07:00
a = tda - > bass & 0xff ;
2008-12-18 13:43:45 -03:00
tda9875_write ( sd , TDA9875_MBA , a ) ;
2005-04-16 15:20:36 -07:00
a = tda - > treble & 0xff ;
2008-12-18 13:43:45 -03:00
tda9875_write ( sd , TDA9875_MTR , a ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-18 13:43:45 -03:00
static void do_tda9875_init ( struct v4l2_subdev * sd )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
struct tda9875 * t = to_state ( sd ) ;
v4l2_dbg ( 1 , debug , sd , " In tda9875_init \n " ) ;
tda9875_write ( sd , TDA9875_CFG , 0xd0 ) ; /*reg de config 0 (reset)*/
tda9875_write ( sd , TDA9875_MSR , 0x03 ) ; /* Monitor 0b00000XXX*/
tda9875_write ( sd , TDA9875_C1MSB , 0x00 ) ; /*Car1(FM) MSB XMHz*/
tda9875_write ( sd , TDA9875_C1MIB , 0x00 ) ; /*Car1(FM) MIB XMHz*/
tda9875_write ( sd , TDA9875_C1LSB , 0x00 ) ; /*Car1(FM) LSB XMHz*/
tda9875_write ( sd , TDA9875_C2MSB , 0x00 ) ; /*Car2(NICAM) MSB XMHz*/
tda9875_write ( sd , TDA9875_C2MIB , 0x00 ) ; /*Car2(NICAM) MIB XMHz*/
tda9875_write ( sd , TDA9875_C2LSB , 0x00 ) ; /*Car2(NICAM) LSB XMHz*/
tda9875_write ( sd , TDA9875_DCR , 0x00 ) ; /*Demod config 0x00*/
tda9875_write ( sd , TDA9875_DEEM , 0x44 ) ; /*DE-Emph 0b0100 0100*/
tda9875_write ( sd , TDA9875_FMAT , 0x00 ) ; /*FM Matrix reg 0x00*/
tda9875_write ( sd , TDA9875_SC1 , 0x00 ) ; /* SCART 1 (SC1)*/
tda9875_write ( sd , TDA9875_SC2 , 0x01 ) ; /* SCART 2 (sc2)*/
tda9875_write ( sd , TDA9875_CH1V , 0x10 ) ; /* Channel volume 1 mute*/
tda9875_write ( sd , TDA9875_CH2V , 0x10 ) ; /* Channel volume 2 mute */
tda9875_write ( sd , TDA9875_DACOS , 0x02 ) ; /* sig DAC i/o(in:nicam)*/
tda9875_write ( sd , TDA9875_ADCIS , 0x6f ) ; /* sig ADC input(in:mono)*/
tda9875_write ( sd , TDA9875_LOSR , 0x00 ) ; /* line out (in:mono)*/
tda9875_write ( sd , TDA9875_AER , 0x00 ) ; /*06 Effect (AVL+PSEUDO) */
tda9875_write ( sd , TDA9875_MCS , 0x44 ) ; /* Main ch select (DAC) */
tda9875_write ( sd , TDA9875_MVL , 0x03 ) ; /* Vol Main left 10dB */
tda9875_write ( sd , TDA9875_MVR , 0x03 ) ; /* Vol Main right 10dB*/
tda9875_write ( sd , TDA9875_MBA , 0x00 ) ; /* Main Bass Main 0dB*/
tda9875_write ( sd , TDA9875_MTR , 0x00 ) ; /* Main Treble Main 0dB*/
tda9875_write ( sd , TDA9875_ACS , 0x44 ) ; /* Aux chan select (dac)*/
tda9875_write ( sd , TDA9875_AVL , 0x00 ) ; /* Vol Aux left 0dB*/
tda9875_write ( sd , TDA9875_AVR , 0x00 ) ; /* Vol Aux right 0dB*/
tda9875_write ( sd , TDA9875_ABA , 0x00 ) ; /* Aux Bass Main 0dB*/
tda9875_write ( sd , TDA9875_ATR , 0x00 ) ; /* Aux Aigus Main 0dB*/
tda9875_write ( sd , TDA9875_MUT , 0xcc ) ; /* General mute */
t - > lvol = t - > rvol = 0 ; /* 0dB */
t - > bass = 0 ; /* 0dB */
t - > treble = 0 ; /* 0dB */
tda9875_set ( sd ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-18 13:43:45 -03:00
static int tda9875_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
struct tda9875 * t = to_state ( sd ) ;
2005-04-16 15:20:36 -07:00
2006-08-25 16:53:10 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
{
int left = ( t - > lvol + 84 ) * 606 ;
int right = ( t - > rvol + 84 ) * 606 ;
2005-04-16 15:20:36 -07:00
2006-08-25 16:53:10 -03:00
ctrl - > value = max ( left , right ) ;
return 0 ;
}
case V4L2_CID_AUDIO_BALANCE :
2005-04-16 15:20:36 -07:00
{
2006-08-25 16:53:10 -03:00
int left = ( t - > lvol + 84 ) * 606 ;
int right = ( t - > rvol + 84 ) * 606 ;
int volume = max ( left , right ) ;
int balance = ( 32768 * min ( left , right ) ) /
( volume ? volume : 1 ) ;
ctrl - > value = ( left < right ) ?
( 65535 - balance ) : balance ;
return 0 ;
}
case V4L2_CID_AUDIO_BASS :
ctrl - > value = ( t - > bass + 12 ) * 2427 ; /* min -12 max +15 */
return 0 ;
case V4L2_CID_AUDIO_TREBLE :
ctrl - > value = ( t - > treble + 12 ) * 2730 ; /* min -12 max +12 */
return 0 ;
}
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static int tda9875_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2006-08-25 16:53:10 -03:00
{
2008-12-18 13:43:45 -03:00
struct tda9875 * t = to_state ( sd ) ;
2009-01-17 13:09:11 -03:00
int chvol = 0 , volume = 0 , balance = 0 , left , right ;
2005-04-16 15:20:36 -07:00
2006-08-25 16:53:10 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
left = ( t - > lvol + 84 ) * 606 ;
right = ( t - > rvol + 84 ) * 606 ;
volume = max ( left , right ) ;
balance = ( 32768 * min ( left , right ) ) /
( volume ? volume : 1 ) ;
balance = ( left < right ) ?
( 65535 - balance ) : balance ;
volume = ctrl - > value ;
2005-04-16 15:20:36 -07:00
2006-08-25 16:53:10 -03:00
chvol = 1 ;
break ;
case V4L2_CID_AUDIO_BALANCE :
2005-04-16 15:20:36 -07:00
left = ( t - > lvol + 84 ) * 606 ;
right = ( t - > rvol + 84 ) * 606 ;
2006-08-25 16:53:10 -03:00
volume = max ( left , right ) ;
balance = ctrl - > value ;
chvol = 1 ;
break ;
case V4L2_CID_AUDIO_BASS :
t - > bass = ( ( ctrl - > value / 2400 ) - 12 ) & 0xff ;
if ( t - > bass > 15 )
t - > bass = 15 ;
if ( t - > bass < - 12 )
t - > bass = - 12 & 0xff ;
break ;
case V4L2_CID_AUDIO_TREBLE :
t - > treble = ( ( ctrl - > value / 2700 ) - 12 ) & 0xff ;
if ( t - > treble > 12 )
t - > treble = 12 ;
if ( t - > treble < - 12 )
t - > treble = - 12 & 0xff ;
break ;
default :
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2006-08-25 16:53:10 -03:00
if ( chvol ) {
left = ( min ( 65536 - balance , 32768 ) *
volume ) / 32768 ;
right = ( min ( balance , 32768 ) *
volume ) / 32768 ;
2005-04-16 15:20:36 -07:00
t - > lvol = ( ( left / 606 ) - 84 ) & 0xff ;
if ( t - > lvol > 24 )
2006-08-25 16:53:10 -03:00
t - > lvol = 24 ;
2005-04-16 15:20:36 -07:00
if ( t - > lvol < - 84 )
2006-08-25 16:53:10 -03:00
t - > lvol = - 84 & 0xff ;
2005-04-16 15:20:36 -07:00
t - > rvol = ( ( right / 606 ) - 84 ) & 0xff ;
if ( t - > rvol > 24 )
2006-08-25 16:53:10 -03:00
t - > rvol = 24 ;
2005-04-16 15:20:36 -07:00
if ( t - > rvol < - 84 )
2006-08-25 16:53:10 -03:00
t - > rvol = - 84 & 0xff ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
tda9875_set ( sd ) ;
2006-08-25 16:53:10 -03:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static int tda9875_queryctrl ( struct v4l2_subdev * sd , struct v4l2_queryctrl * qc )
2006-08-25 16:53:10 -03:00
{
2008-12-18 13:43:45 -03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_VOLUME :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 65535 , 65535 / 100 , 58880 ) ;
2008-12-18 13:43:45 -03:00
case V4L2_CID_AUDIO_BASS :
case V4L2_CID_AUDIO_TREBLE :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 65535 , 65535 / 100 , 32768 ) ;
2006-08-25 16:53:10 -03:00
}
2008-12-18 13:43:45 -03:00
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static int tda9875_command ( struct i2c_client * client , unsigned cmd , void * arg )
{
return v4l2_subdev_command ( i2c_get_clientdata ( client ) , cmd , arg ) ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
/* ----------------------------------------------------------------------- */
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static const struct v4l2_subdev_core_ops tda9875_core_ops = {
. queryctrl = tda9875_queryctrl ,
. g_ctrl = tda9875_g_ctrl ,
. s_ctrl = tda9875_s_ctrl ,
} ;
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static const struct v4l2_subdev_ops tda9875_ops = {
. core = & tda9875_core_ops ,
} ;
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
/* ----------------------------------------------------------------------- */
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
/* *********************** *
* i2c interface functions *
* * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static int tda9875_checkit ( struct i2c_client * client , int addr )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
int dic , rev ;
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
dic = i2c_read_register ( client , addr , 254 ) ;
rev = i2c_read_register ( client , addr , 255 ) ;
if ( dic = = 0 | | dic = = 2 ) { /* tda9875 and tda9875A */
v4l_info ( client , " tda9875%s rev. %d detected at 0x%02x \n " ,
dic = = 0 ? " " : " A " , rev , addr < < 1 ) ;
return 1 ;
}
v4l_info ( client , " no such chip at 0x%02x (dic=0x%x rev=0x%x) \n " ,
addr < < 1 , dic , rev ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-12-18 13:43:45 -03:00
static int tda9875_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-04-16 15:20:36 -07:00
{
2008-12-18 13:43:45 -03:00
struct tda9875 * t ;
struct v4l2_subdev * sd ;
v4l_info ( client , " chip found @ 0x%02x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
if ( ! tda9875_checkit ( client , client - > addr ) )
return - ENODEV ;
t = kzalloc ( sizeof ( * t ) , GFP_KERNEL ) ;
if ( ! t )
return - ENOMEM ;
sd = & t - > sd ;
v4l2_i2c_subdev_init ( sd , client , & tda9875_ops ) ;
do_tda9875_init ( sd ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-12-18 13:43:45 -03:00
static int tda9875_remove ( struct i2c_client * client )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
do_tda9875_init ( sd ) ;
v4l2_device_unregister_subdev ( sd ) ;
kfree ( to_state ( sd ) ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2008-12-18 13:43:45 -03:00
static const struct i2c_device_id tda9875_id [ ] = {
{ " tda9875 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tda9875_id ) ;
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " tda9875 " ,
. command = tda9875_command ,
. probe = tda9875_probe ,
. remove = tda9875_remove ,
. id_table = tda9875_id ,
} ;