2005-11-08 21:37:55 -08:00
/*
* cs53l32a ( Adaptec AVC - 2010 and AVC - 2410 ) i2c ivtv driver .
* Copyright ( C ) 2005 Martin Vaughan
*
* Audio source switching for Adaptec AVC - 2410 added by Trev Jackson
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/ioctl.h>
# include <asm/uaccess.h>
# include <linux/i2c.h>
2005-11-08 21:38:49 -08:00
# include <linux/i2c-id.h>
2008-07-25 05:32:50 -03:00
# include <linux/videodev2.h>
2008-11-29 12:45:59 -03:00
# include <media/v4l2-device.h>
2007-04-27 12:31:26 -03:00
# include <media/v4l2-chip-ident.h>
2009-03-28 09:32:42 -03:00
# include <media/v4l2-i2c-drv.h>
2005-11-08 21:37:55 -08:00
MODULE_DESCRIPTION ( " i2c device driver for cs53l32a Audio ADC " ) ;
MODULE_AUTHOR ( " Martin Vaughan " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-11-01 07:54:57 -03:00
static int debug ;
2005-11-08 21:37:55 -08:00
module_param ( debug , bool , 0644 ) ;
2008-07-31 00:07:23 -07:00
MODULE_PARM_DESC ( debug , " Debugging messages, 0=Off (default), 1=On " ) ;
2005-11-08 21:37:55 -08:00
/* ----------------------------------------------------------------------- */
2008-11-29 12:45:59 -03:00
static int cs53l32a_write ( struct v4l2_subdev * sd , u8 reg , u8 value )
2005-11-08 21:37:55 -08:00
{
2008-11-29 12:45:59 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-08 21:37:55 -08:00
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
2008-11-29 12:45:59 -03:00
static int cs53l32a_read ( struct v4l2_subdev * sd , u8 reg )
2005-11-08 21:37:55 -08:00
{
2008-11-29 12:45:59 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-08 21:37:55 -08:00
return i2c_smbus_read_byte_data ( client , reg ) ;
}
2009-04-02 11:26:22 -03:00
static int cs53l32a_s_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2005-11-08 21:37:55 -08:00
{
2008-11-29 12:45:59 -03:00
/* There are 2 physical inputs, but the second input can be
placed in two modes , the first mode bypasses the PGA ( gain ) ,
the second goes through the PGA . Hence there are three
possible inputs to choose from . */
2009-04-02 11:26:22 -03:00
if ( input > 2 ) {
v4l2_err ( sd , " Invalid input %d. \n " , input ) ;
2005-11-08 21:37:55 -08:00
return - EINVAL ;
}
2009-04-02 11:26:22 -03:00
cs53l32a_write ( sd , 0x01 , 0x01 + ( input < < 4 ) ) ;
2008-11-29 12:45:59 -03:00
return 0 ;
}
static int cs53l32a_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
if ( ctrl - > id = = V4L2_CID_AUDIO_MUTE ) {
ctrl - > value = ( cs53l32a_read ( sd , 0x03 ) & 0xc0 ) ! = 0 ;
return 0 ;
}
if ( ctrl - > id ! = V4L2_CID_AUDIO_VOLUME )
return - EINVAL ;
ctrl - > value = ( s8 ) cs53l32a_read ( sd , 0x04 ) ;
return 0 ;
}
static int cs53l32a_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
if ( ctrl - > id = = V4L2_CID_AUDIO_MUTE ) {
cs53l32a_write ( sd , 0x03 , ctrl - > value ? 0xf0 : 0x30 ) ;
return 0 ;
}
if ( ctrl - > id ! = V4L2_CID_AUDIO_VOLUME )
return - EINVAL ;
if ( ctrl - > value > 12 | | ctrl - > value < - 96 )
return - EINVAL ;
cs53l32a_write ( sd , 0x04 , ( u8 ) ctrl - > value ) ;
cs53l32a_write ( sd , 0x05 , ( u8 ) ctrl - > value ) ;
2005-11-08 21:37:55 -08:00
return 0 ;
}
2008-12-30 07:14:19 -03:00
static int cs53l32a_g_chip_ident ( struct v4l2_subdev * sd , struct v4l2_dbg_chip_ident * chip )
2008-11-29 12:45:59 -03:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
return v4l2_chip_ident_i2c_client ( client ,
chip , V4L2_IDENT_CS53l32A , 0 ) ;
}
static int cs53l32a_log_status ( struct v4l2_subdev * sd )
{
u8 v = cs53l32a_read ( sd , 0x01 ) ;
u8 m = cs53l32a_read ( sd , 0x03 ) ;
s8 vol = cs53l32a_read ( sd , 0x04 ) ;
v4l2_info ( sd , " Input: %d%s \n " , ( v > > 4 ) & 3 ,
( m & 0xC0 ) ? " (muted) " : " " ) ;
v4l2_info ( sd , " Volume: %d dB \n " , vol ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
. log_status = cs53l32a_log_status ,
. g_chip_ident = cs53l32a_g_chip_ident ,
. g_ctrl = cs53l32a_g_ctrl ,
. s_ctrl = cs53l32a_s_ctrl ,
} ;
static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
. s_routing = cs53l32a_s_routing ,
} ;
static const struct v4l2_subdev_ops cs53l32a_ops = {
. core = & cs53l32a_core_ops ,
. audio = & cs53l32a_audio_ops ,
} ;
2005-11-08 21:37:55 -08:00
/* ----------------------------------------------------------------------- */
/* i2c implementation */
/*
* Generic i2c probe
* concerning the addresses : i2c wants 7 bit ( without the r / w bit ) , so ' > > 1 '
*/
2008-04-29 23:11:39 +02:00
static int cs53l32a_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-11-08 21:37:55 -08:00
{
2008-11-29 12:45:59 -03:00
struct v4l2_subdev * sd ;
2005-11-08 21:37:55 -08:00
int i ;
/* Check if the adapter supports the needed features */
2007-09-13 11:30:38 -03:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
2007-09-16 10:47:15 -03:00
return - EIO ;
2005-11-08 21:37:55 -08:00
2008-05-18 20:49:40 +02:00
if ( ! id )
strlcpy ( client - > name , " cs53l32a " , sizeof ( client - > name ) ) ;
2005-11-08 21:37:55 -08:00
2007-11-01 07:54:57 -03:00
v4l_info ( client , " chip found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
2005-11-08 21:37:55 -08:00
2008-11-29 12:45:59 -03:00
sd = kmalloc ( sizeof ( struct v4l2_subdev ) , GFP_KERNEL ) ;
if ( sd = = NULL )
return - ENOMEM ;
v4l2_i2c_subdev_init ( sd , client , & cs53l32a_ops ) ;
2005-11-08 21:37:55 -08:00
for ( i = 1 ; i < = 7 ; i + + ) {
2008-11-29 12:45:59 -03:00
u8 v = cs53l32a_read ( sd , i ) ;
2005-11-08 21:37:55 -08:00
2008-11-29 12:45:59 -03:00
v4l2_dbg ( 1 , debug , sd , " Read Reg %d %02x \n " , i , v ) ;
2005-11-08 21:37:55 -08:00
}
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
2008-11-29 12:45:59 -03:00
cs53l32a_write ( sd , 0x01 , ( u8 ) 0x21 ) ;
cs53l32a_write ( sd , 0x02 , ( u8 ) 0x29 ) ;
cs53l32a_write ( sd , 0x03 , ( u8 ) 0x30 ) ;
cs53l32a_write ( sd , 0x04 , ( u8 ) 0x00 ) ;
cs53l32a_write ( sd , 0x05 , ( u8 ) 0x00 ) ;
cs53l32a_write ( sd , 0x06 , ( u8 ) 0x00 ) ;
cs53l32a_write ( sd , 0x07 , ( u8 ) 0x00 ) ;
2005-11-08 21:37:55 -08:00
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
for ( i = 1 ; i < = 7 ; i + + ) {
2008-11-29 12:45:59 -03:00
u8 v = cs53l32a_read ( sd , i ) ;
2005-11-08 21:37:55 -08:00
2008-11-29 12:45:59 -03:00
v4l2_dbg ( 1 , debug , sd , " Read Reg %d %02x \n " , i , v ) ;
2005-11-08 21:37:55 -08:00
}
return 0 ;
}
2008-11-29 12:45:59 -03:00
static int cs53l32a_remove ( struct i2c_client * client )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
v4l2_device_unregister_subdev ( sd ) ;
kfree ( sd ) ;
return 0 ;
}
2008-05-18 20:49:40 +02:00
static const struct i2c_device_id cs53l32a_id [ ] = {
{ " cs53l32a " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , cs53l32a_id ) ;
2007-09-13 11:30:38 -03:00
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " cs53l32a " ,
2008-11-29 12:45:59 -03:00
. remove = cs53l32a_remove ,
2007-09-13 11:30:38 -03:00
. probe = cs53l32a_probe ,
2008-05-18 20:49:40 +02:00
. id_table = cs53l32a_id ,
2005-11-08 21:37:55 -08:00
} ;