2008-04-29 03:24:33 +04:00
/*
* cx18 I2C functions
*
* Derived from ivtv - i2c . c
*
* Copyright ( C ) 2007 Hans Verkuil < hverkuil @ xs4all . nl >
*
* 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 "cx18-driver.h"
2008-08-30 23:03:44 +04:00
# include "cx18-io.h"
2008-04-29 03:24:33 +04:00
# include "cx18-cards.h"
# include "cx18-gpio.h"
# include "cx18-av-core.h"
2008-05-06 01:25:22 +04:00
# include "cx18-i2c.h"
2008-04-29 03:24:33 +04:00
# define CX18_REG_I2C_1_WR 0xf15000
# define CX18_REG_I2C_1_RD 0xf15008
# define CX18_REG_I2C_2_WR 0xf25100
# define CX18_REG_I2C_2_RD 0xf25108
# define SETSCL_BIT 0x0001
# define SETSDL_BIT 0x0002
# define GETSCL_BIT 0x0004
# define GETSDL_BIT 0x0008
# define CX18_CS5345_I2C_ADDR 0x4c
/* This array should match the CX18_HW_ defines */
static const u8 hw_driverids [ ] = {
I2C_DRIVERID_TUNER ,
I2C_DRIVERID_TVEEPROM ,
I2C_DRIVERID_CS5345 ,
0 , /* CX18_HW_GPIO dummy driver ID */
0 /* CX18_HW_CX23418 dummy driver ID */
} ;
/* This array should match the CX18_HW_ defines */
static const u8 hw_addrs [ ] = {
0 ,
0 ,
CX18_CS5345_I2C_ADDR ,
0 , /* CX18_HW_GPIO dummy driver ID */
0 , /* CX18_HW_CX23418 dummy driver ID */
} ;
/* This array should match the CX18_HW_ defines */
/* This might well become a card-specific array */
static const u8 hw_bus [ ] = {
0 ,
0 ,
0 ,
0 , /* CX18_HW_GPIO dummy driver ID */
0 , /* CX18_HW_CX23418 dummy driver ID */
} ;
/* This array should match the CX18_HW_ defines */
2008-05-18 22:49:40 +04:00
static const char * const hw_devicenames [ ] = {
2008-04-29 03:24:33 +04:00
" tuner " ,
" tveeprom " ,
" cs5345 " ,
" gpio " ,
" cx23418 " ,
} ;
int cx18_i2c_register ( struct cx18 * cx , unsigned idx )
{
struct i2c_board_info info ;
struct i2c_client * c ;
u8 id , bus ;
int i ;
CX18_DEBUG_I2C ( " i2c client register \n " ) ;
if ( idx > = ARRAY_SIZE ( hw_driverids ) | | hw_driverids [ idx ] = = 0 )
return - 1 ;
id = hw_driverids [ idx ] ;
bus = hw_bus [ idx ] ;
memset ( & info , 0 , sizeof ( info ) ) ;
2008-05-18 22:49:40 +04:00
strlcpy ( info . type , hw_devicenames [ idx ] , sizeof ( info . type ) ) ;
2008-04-29 03:24:33 +04:00
info . addr = hw_addrs [ idx ] ;
for ( i = 0 ; i < I2C_CLIENTS_MAX ; i + + )
if ( cx - > i2c_clients [ i ] = = NULL )
break ;
if ( i = = I2C_CLIENTS_MAX ) {
CX18_ERR ( " insufficient room for new I2C client! \n " ) ;
return - ENOMEM ;
}
if ( id ! = I2C_DRIVERID_TUNER ) {
c = i2c_new_device ( & cx - > i2c_adap [ bus ] , & info ) ;
if ( c - > driver = = NULL )
i2c_unregister_device ( c ) ;
else
cx - > i2c_clients [ i ] = c ;
return cx - > i2c_clients [ i ] ? 0 : - ENODEV ;
}
/* special tuner handling */
c = i2c_new_probed_device ( & cx - > i2c_adap [ 1 ] , & info , cx - > card_i2c - > radio ) ;
if ( c & & c - > driver = = NULL )
i2c_unregister_device ( c ) ;
else if ( c )
cx - > i2c_clients [ i + + ] = c ;
c = i2c_new_probed_device ( & cx - > i2c_adap [ 1 ] , & info , cx - > card_i2c - > demod ) ;
if ( c & & c - > driver = = NULL )
i2c_unregister_device ( c ) ;
else if ( c )
cx - > i2c_clients [ i + + ] = c ;
c = i2c_new_probed_device ( & cx - > i2c_adap [ 1 ] , & info , cx - > card_i2c - > tv ) ;
if ( c & & c - > driver = = NULL )
i2c_unregister_device ( c ) ;
else if ( c )
cx - > i2c_clients [ i + + ] = c ;
return 0 ;
}
static int attach_inform ( struct i2c_client * client )
{
return 0 ;
}
static int detach_inform ( struct i2c_client * client )
{
int i ;
struct cx18 * cx = ( struct cx18 * ) i2c_get_adapdata ( client - > adapter ) ;
CX18_DEBUG_I2C ( " i2c client detach \n " ) ;
for ( i = 0 ; i < I2C_CLIENTS_MAX ; i + + ) {
if ( cx - > i2c_clients [ i ] = = client ) {
cx - > i2c_clients [ i ] = NULL ;
break ;
}
}
CX18_DEBUG_I2C ( " i2c detach [client=%s,%s] \n " ,
client - > name , ( i < I2C_CLIENTS_MAX ) ? " ok " : " failed " ) ;
return 0 ;
}
static void cx18_setscl ( void * data , int state )
{
struct cx18 * cx = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > cx ;
int bus_index = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > bus_index ;
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR ;
2008-08-30 23:03:44 +04:00
u32 r = cx18_read_reg ( cx , addr ) ;
2008-04-29 03:24:33 +04:00
if ( state )
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , r | SETSCL_BIT , addr ) ;
2008-04-29 03:24:33 +04:00
else
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , r & ~ SETSCL_BIT , addr ) ;
2008-04-29 03:24:33 +04:00
}
static void cx18_setsda ( void * data , int state )
{
struct cx18 * cx = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > cx ;
int bus_index = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > bus_index ;
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR ;
2008-08-30 23:03:44 +04:00
u32 r = cx18_read_reg ( cx , addr ) ;
2008-04-29 03:24:33 +04:00
if ( state )
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , r | SETSDL_BIT , addr ) ;
2008-04-29 03:24:33 +04:00
else
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , r & ~ SETSDL_BIT , addr ) ;
2008-04-29 03:24:33 +04:00
}
static int cx18_getscl ( void * data )
{
struct cx18 * cx = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > cx ;
int bus_index = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > bus_index ;
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD ;
2008-08-30 23:03:44 +04:00
return cx18_read_reg ( cx , addr ) & GETSCL_BIT ;
2008-04-29 03:24:33 +04:00
}
static int cx18_getsda ( void * data )
{
struct cx18 * cx = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > cx ;
int bus_index = ( ( struct cx18_i2c_algo_callback_data * ) data ) - > bus_index ;
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD ;
2008-08-30 23:03:44 +04:00
return cx18_read_reg ( cx , addr ) & GETSDL_BIT ;
2008-04-29 03:24:33 +04:00
}
/* template for i2c-bit-algo */
static struct i2c_adapter cx18_i2c_adap_template = {
. name = " cx18 i2c driver " ,
. id = I2C_HW_B_CX2341X ,
. algo = NULL , /* set by i2c-algo-bit */
. algo_data = NULL , /* filled from template */
. client_register = attach_inform ,
. client_unregister = detach_inform ,
. owner = THIS_MODULE ,
} ;
# define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
# define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
static struct i2c_algo_bit_data cx18_i2c_algo_template = {
. setsda = cx18_setsda ,
. setscl = cx18_setscl ,
. getsda = cx18_getsda ,
. getscl = cx18_getscl ,
. udelay = CX18_SCL_PERIOD / 2 , /* 1/2 clock period in usec*/
. timeout = CX18_ALGO_BIT_TIMEOUT * HZ /* jiffies */
} ;
static struct i2c_client cx18_i2c_client_template = {
. name = " cx18 internal " ,
} ;
int cx18_call_i2c_client ( struct cx18 * cx , int addr , unsigned cmd , void * arg )
{
struct i2c_client * client ;
int retval ;
int i ;
CX18_DEBUG_I2C ( " call_i2c_client addr=%02x \n " , addr ) ;
for ( i = 0 ; i < I2C_CLIENTS_MAX ; i + + ) {
client = cx - > i2c_clients [ i ] ;
if ( client = = NULL | | client - > driver = = NULL | |
client - > driver - > command = = NULL )
continue ;
if ( addr = = client - > addr ) {
retval = client - > driver - > command ( client , cmd , arg ) ;
return retval ;
}
}
if ( cmd ! = VIDIOC_G_CHIP_IDENT )
CX18_ERR ( " i2c addr 0x%02x not found for cmd 0x%x! \n " ,
addr , cmd ) ;
return - ENODEV ;
}
/* Find the i2c device based on the driver ID and return
its i2c address or - ENODEV if no matching device was found . */
static int cx18_i2c_id_addr ( struct cx18 * cx , u32 id )
{
struct i2c_client * client ;
int retval = - ENODEV ;
int i ;
for ( i = 0 ; i < I2C_CLIENTS_MAX ; i + + ) {
client = cx - > i2c_clients [ i ] ;
if ( client = = NULL | | client - > driver = = NULL )
continue ;
if ( id = = client - > driver - > id ) {
retval = client - > addr ;
break ;
}
}
return retval ;
}
/* Find the i2c device name matching the DRIVERID */
static const char * cx18_i2c_id_name ( u32 id )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hw_driverids ) ; i + + )
if ( hw_driverids [ i ] = = id )
2008-05-18 22:49:40 +04:00
return hw_devicenames [ i ] ;
2008-04-29 03:24:33 +04:00
return " unknown device " ;
}
/* Find the i2c device name matching the CX18_HW_ flag */
static const char * cx18_i2c_hw_name ( u32 hw )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hw_driverids ) ; i + + )
if ( 1 < < i = = hw )
2008-05-18 22:49:40 +04:00
return hw_devicenames [ i ] ;
2008-04-29 03:24:33 +04:00
return " unknown device " ;
}
/* Find the i2c device matching the CX18_HW_ flag and return
its i2c address or - ENODEV if no matching device was found . */
int cx18_i2c_hw_addr ( struct cx18 * cx , u32 hw )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hw_driverids ) ; i + + )
if ( 1 < < i = = hw )
return cx18_i2c_id_addr ( cx , hw_driverids [ i ] ) ;
return - ENODEV ;
}
/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
If hw = = CX18_HW_GPIO then call the gpio handler . */
int cx18_i2c_hw ( struct cx18 * cx , u32 hw , unsigned int cmd , void * arg )
{
int addr ;
2008-06-21 18:06:44 +04:00
if ( hw = = 0 )
2008-04-29 03:24:33 +04:00
return 0 ;
2008-06-21 18:06:44 +04:00
if ( hw = = CX18_HW_GPIO )
return cx18_gpio ( cx , cmd , arg ) ;
2008-04-29 03:24:33 +04:00
if ( hw = = CX18_HW_CX23418 )
return cx18_av_cmd ( cx , cmd , arg ) ;
addr = cx18_i2c_hw_addr ( cx , hw ) ;
if ( addr < 0 ) {
CX18_ERR ( " i2c hardware 0x%08x (%s) not found for cmd 0x%x! \n " ,
hw , cx18_i2c_hw_name ( hw ) , cmd ) ;
return addr ;
}
return cx18_call_i2c_client ( cx , addr , cmd , arg ) ;
}
/* Calls i2c device based on I2C driver ID. */
int cx18_i2c_id ( struct cx18 * cx , u32 id , unsigned int cmd , void * arg )
{
int addr ;
addr = cx18_i2c_id_addr ( cx , id ) ;
if ( addr < 0 ) {
if ( cmd ! = VIDIOC_G_CHIP_IDENT )
CX18_ERR ( " i2c ID 0x%08x (%s) not found for cmd 0x%x! \n " ,
id , cx18_i2c_id_name ( id ) , cmd ) ;
return addr ;
}
return cx18_call_i2c_client ( cx , addr , cmd , arg ) ;
}
/* broadcast cmd for all I2C clients and for the gpio subsystem */
void cx18_call_i2c_clients ( struct cx18 * cx , unsigned int cmd , void * arg )
{
if ( cx - > i2c_adap [ 0 ] . algo = = NULL | | cx - > i2c_adap [ 1 ] . algo = = NULL ) {
CX18_ERR ( " adapter is not set \n " ) ;
return ;
}
cx18_av_cmd ( cx , cmd , arg ) ;
i2c_clients_command ( & cx - > i2c_adap [ 0 ] , cmd , arg ) ;
i2c_clients_command ( & cx - > i2c_adap [ 1 ] , cmd , arg ) ;
2008-06-21 18:06:44 +04:00
if ( cx - > hw_flags & CX18_HW_GPIO )
cx18_gpio ( cx , cmd , arg ) ;
2008-04-29 03:24:33 +04:00
}
/* init + register i2c algo-bit adapter */
int init_cx18_i2c ( struct cx18 * cx )
{
int i ;
CX18_DEBUG_I2C ( " i2c init \n " ) ;
2008-06-21 18:06:44 +04:00
/* Sanity checks for the I2C hardware arrays. They must be the
* same size and GPIO / CX23418 must be the last entries .
*/
if ( ARRAY_SIZE ( hw_driverids ) ! = ARRAY_SIZE ( hw_addrs ) | |
ARRAY_SIZE ( hw_devicenames ) ! = ARRAY_SIZE ( hw_addrs ) | |
CX18_HW_GPIO ! = ( 1 < < ( ARRAY_SIZE ( hw_addrs ) - 2 ) ) | |
CX18_HW_CX23418 ! = ( 1 < < ( ARRAY_SIZE ( hw_addrs ) - 1 ) ) | |
hw_driverids [ ARRAY_SIZE ( hw_addrs ) - 1 ] ) {
CX18_ERR ( " Mismatched I2C hardware arrays \n " ) ;
return - ENODEV ;
}
2008-04-29 03:24:33 +04:00
for ( i = 0 ; i < 2 ; i + + ) {
memcpy ( & cx - > i2c_adap [ i ] , & cx18_i2c_adap_template ,
sizeof ( struct i2c_adapter ) ) ;
memcpy ( & cx - > i2c_algo [ i ] , & cx18_i2c_algo_template ,
sizeof ( struct i2c_algo_bit_data ) ) ;
cx - > i2c_algo_cb_data [ i ] . cx = cx ;
cx - > i2c_algo_cb_data [ i ] . bus_index = i ;
cx - > i2c_algo [ i ] . data = & cx - > i2c_algo_cb_data [ i ] ;
cx - > i2c_adap [ i ] . algo_data = & cx - > i2c_algo [ i ] ;
sprintf ( cx - > i2c_adap [ i ] . name + strlen ( cx - > i2c_adap [ i ] . name ) ,
" #%d-%d " , cx - > num , i ) ;
i2c_set_adapdata ( & cx - > i2c_adap [ i ] , cx ) ;
memcpy ( & cx - > i2c_client [ i ] , & cx18_i2c_client_template ,
sizeof ( struct i2c_client ) ) ;
sprintf ( cx - > i2c_client [ i ] . name +
strlen ( cx - > i2c_client [ i ] . name ) , " %d " , i ) ;
cx - > i2c_client [ i ] . adapter = & cx - > i2c_adap [ i ] ;
cx - > i2c_adap [ i ] . dev . parent = & cx - > dev - > dev ;
}
2008-08-30 23:03:44 +04:00
if ( cx18_read_reg ( cx , CX18_REG_I2C_2_WR ) ! = 0x0003c02f ) {
2008-04-29 03:24:33 +04:00
/* Reset/Unreset I2C hardware block */
2008-08-30 23:03:44 +04:00
/* Clock select 220MHz */
cx18_write_reg ( cx , 0x10000000 , 0xc71004 ) ;
/* Clock Enable */
cx18_write_reg_sync ( cx , 0x10001000 , 0xc71024 ) ;
2008-04-29 03:24:33 +04:00
}
/* courtesy of Steven Toth <stoth@hauppauge.com> */
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , 0x00c00000 , 0xc7001c ) ;
2008-04-29 03:24:33 +04:00
mdelay ( 10 ) ;
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , 0x00c000c0 , 0xc7001c ) ;
2008-04-29 03:24:33 +04:00
mdelay ( 10 ) ;
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , 0x00c00000 , 0xc7001c ) ;
2008-07-07 02:36:52 +04:00
mdelay ( 10 ) ;
2008-04-29 03:24:33 +04:00
2008-08-30 23:03:44 +04:00
/* Set to edge-triggered intrs. */
cx18_write_reg_sync ( cx , 0x00c00000 , 0xc730c8 ) ;
/* Clear any stale intrs */
cx18_write_reg_sync ( cx , 0x00c00000 , 0xc730c4 ) ;
2008-04-29 03:24:33 +04:00
/* Hw I2C1 Clock Freq ~100kHz */
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , 0x00021c0f & ~ 4 , CX18_REG_I2C_1_WR ) ;
2008-04-29 03:24:33 +04:00
cx18_setscl ( & cx - > i2c_algo_cb_data [ 0 ] , 1 ) ;
cx18_setsda ( & cx - > i2c_algo_cb_data [ 0 ] , 1 ) ;
/* Hw I2C2 Clock Freq ~100kHz */
2008-08-30 23:03:44 +04:00
cx18_write_reg_sync ( cx , 0x00021c0f & ~ 4 , CX18_REG_I2C_2_WR ) ;
2008-04-29 03:24:33 +04:00
cx18_setscl ( & cx - > i2c_algo_cb_data [ 1 ] , 1 ) ;
cx18_setsda ( & cx - > i2c_algo_cb_data [ 1 ] , 1 ) ;
2008-06-22 08:27:00 +04:00
cx18_reset_i2c_slaves_gpio ( cx ) ;
2008-04-29 03:24:33 +04:00
return i2c_bit_add_bus ( & cx - > i2c_adap [ 0 ] ) | |
i2c_bit_add_bus ( & cx - > i2c_adap [ 1 ] ) ;
}
void exit_cx18_i2c ( struct cx18 * cx )
{
int i ;
CX18_DEBUG_I2C ( " i2c exit \n " ) ;
2008-08-30 23:03:44 +04:00
cx18_write_reg ( cx , cx18_read_reg ( cx , CX18_REG_I2C_1_WR ) | 4 ,
CX18_REG_I2C_1_WR ) ;
cx18_write_reg ( cx , cx18_read_reg ( cx , CX18_REG_I2C_2_WR ) | 4 ,
CX18_REG_I2C_2_WR ) ;
2008-04-29 03:24:33 +04:00
for ( i = 0 ; i < 2 ; i + + ) {
i2c_del_adapter ( & cx - > i2c_adap [ i ] ) ;
}
}
/*
Hauppauge HVR1600 should have :
32 cx24227
98 unknown
a0 eeprom
c2 tuner
e ? zilog ir
*/