2005-04-17 02:20:36 +04:00
/*
* For the STS - Thompson TDA7432 audio processor chip
*
* Handles audio functions : volume , balance , tone , loudness
* This driver will not complain if used with any
* other i2c device with the same address .
*
* Muting and tone control by Jonathan Isom < jisom @ ematic . com >
*
* Copyright ( c ) 2000 Eric Sandeen < eric_sandeen @ bigfoot . com >
2006-08-25 23:53:09 +04:00
* Copyright ( c ) 2006 Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-04-17 02:20:36 +04: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
* set to 2 if you ' d like to be inundated with debug messages
*
* loudness - set between 0 and 15 for varying degrees of loudness effect
*
* maxvol - set maximium volume to + 20 db ( 1 ) , default is 0 db ( 0 )
*/
# include <linux/module.h>
# include <linux/init.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>
2008-07-25 12:32:50 +04:00
# include <linux/videodev2.h>
2005-04-17 02:20:36 +04:00
# include <linux/i2c.h>
2008-12-18 19:04:05 +03:00
# include <media/v4l2-device.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2013-02-05 19:17:38 +04:00
# include <media/v4l2-ctrls.h>
2006-03-24 03:45:24 +03:00
# include <media/i2c-addr.h>
2005-04-17 02:20:36 +04:00
# ifndef VIDEO_AUDIO_BALANCE
# define VIDEO_AUDIO_BALANCE 32
# endif
MODULE_AUTHOR ( " Eric Sandeen <eric_sandeen@bigfoot.com> " ) ;
MODULE_DESCRIPTION ( " bttv driver for the tda7432 audio processor chip " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int maxvol ;
static int loudness ; /* disable loudness by default */
static int debug ; /* insmod parameter */
module_param ( debug , int , S_IRUGO | S_IWUSR ) ;
2011-05-26 12:06:09 +04:00
MODULE_PARM_DESC ( debug , " Set debugging level from 0 to 3. Default is off(0). " ) ;
2005-04-17 02:20:36 +04:00
module_param ( loudness , int , S_IRUGO ) ;
2011-05-26 12:06:09 +04:00
MODULE_PARM_DESC ( loudness , " Turn loudness on(1) else off(0). Default is off(0). " ) ;
2005-04-17 02:20:36 +04:00
module_param ( maxvol , int , S_IRUGO | S_IWUSR ) ;
2011-05-26 12:06:09 +04:00
MODULE_PARM_DESC ( maxvol , " Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0). " ) ;
2005-04-17 02:20:36 +04:00
/* Structure of address and subaddresses for the tda7432 */
struct tda7432 {
2008-12-18 19:04:05 +03:00
struct v4l2_subdev sd ;
2013-02-05 19:17:38 +04:00
struct v4l2_ctrl_handler hdl ;
struct {
/* bass/treble cluster */
struct v4l2_ctrl * bass ;
struct v4l2_ctrl * treble ;
} ;
struct {
/* mute/balance cluster */
struct v4l2_ctrl * mute ;
struct v4l2_ctrl * balance ;
} ;
2005-04-17 02:20:36 +04:00
} ;
2008-12-18 19:04:05 +03:00
static inline struct tda7432 * to_state ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct tda7432 , sd ) ;
}
2005-04-17 02:20:36 +04:00
2013-02-05 19:17:38 +04:00
static inline struct v4l2_subdev * to_sd ( struct v4l2_ctrl * ctrl )
{
return & container_of ( ctrl - > handler , struct tda7432 , hdl ) - > sd ;
}
2005-04-17 02:20:36 +04:00
/* The TDA7432 is made by STS-Thompson
* http : //www.st.com
* http : //us.st.com/stonline/books/pdf/docs/4056.pdf
*
* TDA7432 : I2C - bus controlled basic audio processor
*
* The TDA7432 controls basic audio functions like volume , balance ,
* and tone control ( including loudness ) . It also has four channel
* output ( for front and rear ) . Since most vidcap cards probably
* don ' t have 4 channel output , this driver will set front & rear
* together ( no independent control ) .
*/
/* Subaddresses for TDA7432 */
# define TDA7432_IN 0x00 /* Input select */
# define TDA7432_VL 0x01 /* Volume */
# define TDA7432_TN 0x02 /* Bass, Treble (Tone) */
# define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */
# define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */
# define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */
# define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */
# define TDA7432_LD 0x07 /* Loudness */
/* Masks for bits in TDA7432 subaddresses */
/* Many of these not used - just for documentation */
/* Subaddress 0x00 - Input selection and bass control */
/* Bits 0,1,2 control input:
* 0x00 - Stereo input
* 0x02 - Mono input
* 0x03 - Mute ( Using Attenuators Plays better with modules )
* Mono probably isn ' t used - I ' m guessing only the stereo
* input is connected on most cards , so we ' ll set it to stereo .
*
* Bit 3 controls bass cut : 0 / 1 is non - symmetric / symmetric bass cut
* Bit 4 controls bass range : 0 / 1 is extended / standard bass range
*
* Highest 3 bits not used
*/
# define TDA7432_STEREO_IN 0
# define TDA7432_MONO_IN 2 /* Probably won't be used */
# define TDA7432_BASS_SYM 1 << 3
# define TDA7432_BASS_NORM 1 << 4
/* Subaddress 0x01 - Volume */
/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps
* Recommended maximum is + 20 dB
*
* + 32 dB : 0x00
* + 20 dB : 0x0c
* 0 dB : 0x20
* - 79 dB : 0x6f
*
* MSB ( bit 7 ) controls loudness : 1 / 0 is loudness on / off
*/
# define TDA7432_VOL_0DB 0x20
# define TDA7432_LD_ON 1 << 7
/* Subaddress 0x02 - Tone control */
/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB
* 0x0 is 14 dB , 0x7 is 0 dB
*
* Bit 3 controls treble attenuation / gain ( sign )
* 1 = gain ( + )
* 0 = attenuation ( - )
*
* Bits 4 , 5 , 6 control absolute bass gain from 0 dB to 14 dB
* ( This is only true for normal base range , set in 0x00 )
* 0x0 < < 4 is 14 dB , 0x7 is 0 dB
*
* Bit 7 controls bass attenuation / gain ( sign )
* 1 < < 7 = gain ( + )
* 0 < < 7 = attenuation ( - )
*
* Example :
* 1 1 0 1 0 1 0 1 is + 4 dB bass , - 4 dB treble
*/
# define TDA7432_TREBLE_0DB 0xf
# define TDA7432_TREBLE 7
# define TDA7432_TREBLE_GAIN 1 << 3
# define TDA7432_BASS_0DB 0xf
# define TDA7432_BASS 7 << 4
# define TDA7432_BASS_GAIN 1 << 7
/* Subaddress 0x03 - Left Front attenuation */
/* Subaddress 0x04 - Left Rear attenuation */
/* Subaddress 0x05 - Right Front attenuation */
/* Subaddress 0x06 - Right Rear attenuation */
/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB
* in 1.5 dB steps .
*
* 0x00 is 0 dB
* 0x1f is - 37.5 dB
*
* Bit 5 mutes that channel when set ( 1 = mute , 0 = unmute )
* We ' ll use the mute on the input , though ( above )
* Bits 6 , 7 unused
*/
# define TDA7432_ATTEN_0DB 0x00
# define TDA7432_MUTE 0x1 << 5
/* Subaddress 0x07 - Loudness Control */
/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps
* when bit 4 is NOT set
*
* 0x0 is 0 dB
* 0xf is - 15 dB
*
* If bit 4 is set , then there is a flat attenuation according to
* the lower 4 bits , as above .
*
* Bits 5 , 6 , 7 unused
*/
/* Begin code */
2008-12-18 19:04:05 +03:00
static int tda7432_write ( struct v4l2_subdev * sd , int subaddr , int val )
2005-04-17 02:20:36 +04:00
{
2008-12-18 19:04:05 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-04-17 02:20:36 +04:00
unsigned char buffer [ 2 ] ;
2008-12-18 19:04:05 +03:00
v4l2_dbg ( 2 , debug , sd , " In tda7432_write \n " ) ;
v4l2_dbg ( 1 , debug , sd , " Writing %d 0x%x \n " , subaddr , val ) ;
2005-04-17 02:20:36 +04:00
buffer [ 0 ] = subaddr ;
buffer [ 1 ] = val ;
2008-12-18 19:04:05 +03:00
if ( 2 ! = i2c_master_send ( client , buffer , 2 ) ) {
v4l2_err ( sd , " I/O error, trying (write %d 0x%x) \n " ,
2005-04-17 02:20:36 +04:00
subaddr , val ) ;
return - 1 ;
}
return 0 ;
}
2008-12-18 19:04:05 +03:00
static int tda7432_set ( struct v4l2_subdev * sd )
2005-04-17 02:20:36 +04:00
{
2008-12-18 19:04:05 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-04-17 02:20:36 +04:00
unsigned char buf [ 16 ] ;
buf [ 0 ] = TDA7432_IN ;
2013-02-05 19:17:38 +04:00
buf [ 1 ] = TDA7432_STEREO_IN | /* Main (stereo) input */
TDA7432_BASS_SYM | /* Symmetric bass cut */
TDA7432_BASS_NORM ; /* Normal bass range */
buf [ 2 ] = 0x3b ;
if ( loudness ) /* Turn loudness on? */
buf [ 2 ] | = TDA7432_LD_ON ;
buf [ 3 ] = TDA7432_TREBLE_0DB | ( TDA7432_BASS_0DB < < 4 ) ;
buf [ 4 ] = TDA7432_ATTEN_0DB ;
buf [ 5 ] = TDA7432_ATTEN_0DB ;
buf [ 6 ] = TDA7432_ATTEN_0DB ;
buf [ 7 ] = TDA7432_ATTEN_0DB ;
buf [ 8 ] = loudness ;
if ( 9 ! = i2c_master_send ( client , buf , 9 ) ) {
2008-12-18 19:04:05 +03:00
v4l2_err ( sd , " I/O error, trying tda7432_set \n " ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
return 0 ;
}
2013-02-05 19:17:38 +04:00
static int tda7432_log_status ( struct v4l2_subdev * sd )
2005-04-17 02:20:36 +04:00
{
2013-02-05 19:17:38 +04:00
struct tda7432 * state = to_state ( sd ) ;
2005-04-17 02:20:36 +04:00
2013-02-05 19:17:38 +04:00
v4l2_ctrl_handler_log_status ( & state - > hdl , sd - > name ) ;
return 0 ;
2006-08-25 23:53:09 +04:00
}
2005-04-17 02:20:36 +04:00
2013-02-05 19:17:38 +04:00
static int tda7432_s_ctrl ( struct v4l2_ctrl * ctrl )
2006-08-25 23:53:09 +04:00
{
2013-02-05 19:17:38 +04:00
struct v4l2_subdev * sd = to_sd ( ctrl ) ;
2008-12-18 19:04:05 +03:00
struct tda7432 * t = to_state ( sd ) ;
2013-02-05 19:17:38 +04:00
u8 bass , treble , volume ;
u8 lf , lr , rf , rr ;
2005-04-17 02:20:36 +04:00
2006-08-25 23:53:09 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2013-02-05 19:17:38 +04:00
if ( t - > balance - > val < 0 ) {
2005-04-17 02:20:36 +04:00
/* shifted to left, attenuate right */
2013-02-05 19:17:38 +04:00
rr = rf = - t - > balance - > val ;
lr = lf = TDA7432_ATTEN_0DB ;
} else if ( t - > balance - > val > 0 ) {
2005-04-17 02:20:36 +04:00
/* shifted to right, attenuate left */
2013-02-05 19:17:38 +04:00
rr = rf = TDA7432_ATTEN_0DB ;
lr = lf = t - > balance - > val ;
2006-08-25 23:53:09 +04:00
} else {
2005-04-17 02:20:36 +04:00
/* centered */
2013-02-05 19:17:38 +04:00
rr = rf = TDA7432_ATTEN_0DB ;
lr = lf = TDA7432_ATTEN_0DB ;
2005-04-17 02:20:36 +04:00
}
2013-02-05 19:17:38 +04:00
if ( t - > mute - > val ) {
lf | = TDA7432_MUTE ;
lr | = TDA7432_MUTE ;
lf | = TDA7432_MUTE ;
rr | = TDA7432_MUTE ;
}
/* Mute & update balance*/
tda7432_write ( sd , TDA7432_LF , lf ) ;
tda7432_write ( sd , TDA7432_LR , lr ) ;
tda7432_write ( sd , TDA7432_RF , rf ) ;
tda7432_write ( sd , TDA7432_RR , rr ) ;
2006-08-25 23:53:09 +04:00
return 0 ;
2013-02-05 19:17:38 +04:00
case V4L2_CID_AUDIO_VOLUME :
volume = 0x6f - ctrl - > val ;
if ( loudness ) /* Turn on the loudness bit */
volume | = TDA7432_LD_ON ;
2006-08-25 23:53:09 +04:00
2013-02-05 19:17:38 +04:00
tda7432_write ( sd , TDA7432_VL , volume ) ;
2006-08-25 23:53:09 +04:00
return 0 ;
2008-12-18 19:04:05 +03:00
case V4L2_CID_AUDIO_BASS :
2013-02-05 19:17:38 +04:00
bass = t - > bass - > val ;
treble = t - > treble - > val ;
if ( bass > = 0x8 )
bass = 14 - ( bass - 8 ) ;
if ( treble > = 0x8 )
treble = 14 - ( treble - 8 ) ;
tda7432_write ( sd , TDA7432_TN , 0x10 | ( bass < < 4 ) | treble ) ;
return 0 ;
2006-08-25 23:53:09 +04:00
}
2008-12-18 19:04:05 +03:00
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2008-12-18 19:04:05 +03:00
/* ----------------------------------------------------------------------- */
2013-02-05 19:17:38 +04:00
static const struct v4l2_ctrl_ops tda7432_ctrl_ops = {
2008-12-18 19:04:05 +03:00
. s_ctrl = tda7432_s_ctrl ,
2005-04-17 02:20:36 +04:00
} ;
2013-02-05 19:17:38 +04:00
static const struct v4l2_subdev_core_ops tda7432_core_ops = {
. log_status = tda7432_log_status ,
. g_ext_ctrls = v4l2_subdev_g_ext_ctrls ,
. try_ext_ctrls = v4l2_subdev_try_ext_ctrls ,
. s_ext_ctrls = v4l2_subdev_s_ext_ctrls ,
. g_ctrl = v4l2_subdev_g_ctrl ,
. s_ctrl = v4l2_subdev_s_ctrl ,
. queryctrl = v4l2_subdev_queryctrl ,
. querymenu = v4l2_subdev_querymenu ,
} ;
2008-12-18 19:04:05 +03:00
static const struct v4l2_subdev_ops tda7432_ops = {
. core = & tda7432_core_ops ,
2005-04-17 02:20:36 +04:00
} ;
2008-12-18 19:04:05 +03:00
/* ----------------------------------------------------------------------- */
/* *********************** *
* i2c interface functions *
* * * * * * * * * * * * * * * * * * * * * * * * */
static int tda7432_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-04-17 02:20:36 +04:00
{
2008-12-18 19:04:05 +03:00
struct tda7432 * t ;
struct v4l2_subdev * sd ;
v4l_info ( client , " chip found @ 0x%02x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
t = kzalloc ( sizeof ( * t ) , GFP_KERNEL ) ;
if ( ! t )
return - ENOMEM ;
sd = & t - > sd ;
v4l2_i2c_subdev_init ( sd , client , & tda7432_ops ) ;
2013-02-05 19:17:38 +04:00
v4l2_ctrl_handler_init ( & t - > hdl , 5 ) ;
v4l2_ctrl_new_std ( & t - > hdl , & tda7432_ctrl_ops ,
V4L2_CID_AUDIO_VOLUME , 0 , maxvol ? 0x68 : 0x4f , 1 , maxvol ? 0x5d : 0x47 ) ;
t - > mute = v4l2_ctrl_new_std ( & t - > hdl , & tda7432_ctrl_ops ,
V4L2_CID_AUDIO_MUTE , 0 , 1 , 1 , 0 ) ;
t - > balance = v4l2_ctrl_new_std ( & t - > hdl , & tda7432_ctrl_ops ,
V4L2_CID_AUDIO_BALANCE , - 31 , 31 , 1 , 0 ) ;
t - > bass = v4l2_ctrl_new_std ( & t - > hdl , & tda7432_ctrl_ops ,
V4L2_CID_AUDIO_BASS , 0 , 14 , 1 , 7 ) ;
t - > treble = v4l2_ctrl_new_std ( & t - > hdl , & tda7432_ctrl_ops ,
V4L2_CID_AUDIO_TREBLE , 0 , 14 , 1 , 7 ) ;
sd - > ctrl_handler = & t - > hdl ;
if ( t - > hdl . error ) {
int err = t - > hdl . error ;
v4l2_ctrl_handler_free ( & t - > hdl ) ;
kfree ( t ) ;
return err ;
}
v4l2_ctrl_cluster ( 2 , & t - > bass ) ;
v4l2_ctrl_cluster ( 2 , & t - > mute ) ;
v4l2_ctrl_handler_setup ( & t - > hdl ) ;
2008-12-18 19:04:05 +03:00
if ( loudness < 0 | | loudness > 15 ) {
v4l2_warn ( sd , " loudness parameter must be between 0 and 15 \n " ) ;
if ( loudness < 0 )
loudness = 0 ;
if ( loudness > 15 )
loudness = 15 ;
2005-04-17 02:20:36 +04:00
}
2013-02-05 19:17:38 +04:00
tda7432_set ( sd ) ;
2008-12-18 19:04:05 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-12-18 19:04:05 +03:00
static int tda7432_remove ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2008-12-18 19:04:05 +03:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
2013-02-05 19:17:38 +04:00
struct tda7432 * t = to_state ( sd ) ;
2005-04-17 02:20:36 +04:00
2013-02-05 19:17:38 +04:00
tda7432_set ( sd ) ;
2008-12-18 19:04:05 +03:00
v4l2_device_unregister_subdev ( sd ) ;
2013-02-05 19:17:38 +04:00
v4l2_ctrl_handler_free ( & t - > hdl ) ;
kfree ( t ) ;
2008-12-18 19:04:05 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-12-18 19:04:05 +03:00
static const struct i2c_device_id tda7432_id [ ] = {
{ " tda7432 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tda7432_id ) ;
2010-09-15 22:40:07 +04:00
static struct i2c_driver tda7432_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " tda7432 " ,
} ,
. probe = tda7432_probe ,
. remove = tda7432_remove ,
. id_table = tda7432_id ,
2008-12-18 19:04:05 +03:00
} ;
2010-09-15 22:40:07 +04:00
2012-02-12 13:56:32 +04:00
module_i2c_driver ( tda7432_driver ) ;