2007-03-11 20:44:05 -03:00
/*
* Driver for the Conexant CX23885 PCIe bridge
*
2008-09-03 17:12:12 -03:00
* Copyright ( c ) 2006 Steven Toth < stoth @ linuxtv . org >
2007-03-11 20:44:05 -03:00
*
* 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/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <asm/io.h>
# include "cx23885.h"
# include <media/v4l2-common.h>
2008-01-12 11:36:36 -03:00
static unsigned int i2c_debug ;
2007-03-11 20:44:05 -03:00
module_param ( i2c_debug , int , 0644 ) ;
2007-03-20 23:00:18 -03:00
MODULE_PARM_DESC ( i2c_debug , " enable debug messages [i2c] " ) ;
2007-03-11 20:44:05 -03:00
2008-04-22 14:41:48 -03:00
static unsigned int i2c_scan ;
2007-03-11 20:44:05 -03:00
module_param ( i2c_scan , int , 0444 ) ;
2007-03-20 23:00:18 -03:00
MODULE_PARM_DESC ( i2c_scan , " scan i2c bus at insmod time " ) ;
2007-03-11 20:44:05 -03:00
2008-01-12 11:36:36 -03:00
# define dprintk(level, fmt, arg...)\
do { if ( i2c_debug > = level ) \
printk ( KERN_DEBUG " %s/0: " fmt , dev - > name , # # arg ) ; \
2007-12-15 03:31:09 -03:00
} while ( 0 )
2007-03-11 20:44:05 -03:00
# define I2C_WAIT_DELAY 32
# define I2C_WAIT_RETRY 64
# define I2C_EXTEND (1 << 3)
# define I2C_NOSTOP (1 << 4)
static inline int i2c_slave_did_ack ( struct i2c_adapter * i2c_adap )
{
struct cx23885_i2c * bus = i2c_adap - > algo_data ;
struct cx23885_dev * dev = bus - > dev ;
return cx_read ( bus - > reg_stat ) & 0x01 ;
}
static inline int i2c_is_busy ( struct i2c_adapter * i2c_adap )
{
struct cx23885_i2c * bus = i2c_adap - > algo_data ;
struct cx23885_dev * dev = bus - > dev ;
return cx_read ( bus - > reg_stat ) & 0x02 ? 1 : 0 ;
}
static int i2c_wait_done ( struct i2c_adapter * i2c_adap )
{
int count ;
for ( count = 0 ; count < I2C_WAIT_RETRY ; count + + ) {
if ( ! i2c_is_busy ( i2c_adap ) )
break ;
udelay ( I2C_WAIT_DELAY ) ;
}
if ( I2C_WAIT_RETRY = = count )
return 0 ;
return 1 ;
}
2007-03-20 23:00:18 -03:00
static int i2c_sendbytes ( struct i2c_adapter * i2c_adap ,
2007-12-15 03:31:09 -03:00
const struct i2c_msg * msg , int joined_rlen )
2007-03-11 20:44:05 -03:00
{
struct cx23885_i2c * bus = i2c_adap - > algo_data ;
struct cx23885_dev * dev = bus - > dev ;
u32 wdata , addr , ctrl ;
int retval , cnt ;
2007-12-15 03:31:09 -03:00
if ( joined_rlen )
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s(msg->wlen=%d, nextmsg->rlen=%d) \n " , __func__ ,
2007-12-15 03:31:09 -03:00
msg - > len , joined_rlen ) ;
else
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s(msg->len=%d) \n " , __func__ , msg - > len ) ;
2007-09-20 01:44:27 -03:00
2007-03-11 20:44:05 -03:00
/* Deal with i2c probe functions with zero payload */
if ( msg - > len = = 0 ) {
cx_write ( bus - > reg_addr , msg - > addr < < 25 ) ;
cx_write ( bus - > reg_ctrl , bus - > i2c_period | ( 1 < < 2 ) ) ;
if ( ! i2c_wait_done ( i2c_adap ) )
return - EIO ;
if ( ! i2c_slave_did_ack ( i2c_adap ) )
return - EIO ;
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s() returns 0 \n " , __func__ ) ;
2007-03-11 20:44:05 -03:00
return 0 ;
}
/* dev, reg + first byte */
addr = ( msg - > addr < < 25 ) | msg - > buf [ 0 ] ;
wdata = msg - > buf [ 0 ] ;
ctrl = bus - > i2c_period | ( 1 < < 12 ) | ( 1 < < 2 ) ;
if ( msg - > len > 1 )
ctrl | = I2C_NOSTOP | I2C_EXTEND ;
2007-12-15 03:31:09 -03:00
else if ( joined_rlen )
ctrl | = I2C_NOSTOP ;
2007-03-11 20:44:05 -03:00
cx_write ( bus - > reg_addr , addr ) ;
cx_write ( bus - > reg_wdata , wdata ) ;
cx_write ( bus - > reg_ctrl , ctrl ) ;
retval = i2c_wait_done ( i2c_adap ) ;
if ( retval < 0 )
goto err ;
if ( retval = = 0 )
goto eio ;
if ( i2c_debug ) {
printk ( " <W %02x %02x " , msg - > addr < < 1 , msg - > buf [ 0 ] ) ;
if ( ! ( ctrl & I2C_NOSTOP ) )
printk ( " > \n " ) ;
}
for ( cnt = 1 ; cnt < msg - > len ; cnt + + ) {
/* following bytes */
wdata = msg - > buf [ cnt ] ;
ctrl = bus - > i2c_period | ( 1 < < 12 ) | ( 1 < < 2 ) ;
2007-09-20 01:44:27 -03:00
if ( cnt < msg - > len - 1 )
2007-03-11 20:44:05 -03:00
ctrl | = I2C_NOSTOP | I2C_EXTEND ;
2007-12-15 03:31:09 -03:00
else if ( joined_rlen )
ctrl | = I2C_NOSTOP ;
2007-03-11 20:44:05 -03:00
cx_write ( bus - > reg_addr , addr ) ;
cx_write ( bus - > reg_wdata , wdata ) ;
cx_write ( bus - > reg_ctrl , ctrl ) ;
retval = i2c_wait_done ( i2c_adap ) ;
if ( retval < 0 )
goto err ;
if ( retval = = 0 )
goto eio ;
if ( i2c_debug ) {
printk ( " %02x " , msg - > buf [ cnt ] ) ;
if ( ! ( ctrl & I2C_NOSTOP ) )
printk ( " > \n " ) ;
}
}
return msg - > len ;
eio :
retval = - EIO ;
err :
2007-12-15 03:31:09 -03:00
if ( i2c_debug )
printk ( " ERR: %d \n " , retval ) ;
2007-03-11 20:44:05 -03:00
return retval ;
}
2007-03-20 23:00:18 -03:00
static int i2c_readbytes ( struct i2c_adapter * i2c_adap ,
2007-12-15 03:31:09 -03:00
const struct i2c_msg * msg , int joined )
2007-03-11 20:44:05 -03:00
{
struct cx23885_i2c * bus = i2c_adap - > algo_data ;
struct cx23885_dev * dev = bus - > dev ;
u32 ctrl , cnt ;
int retval ;
2007-12-15 03:31:09 -03:00
if ( i2c_debug & & ! joined )
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s(msg->len=%d) \n " , __func__ , msg - > len ) ;
2007-03-11 20:44:05 -03:00
/* Deal with i2c probe functions with zero payload */
if ( msg - > len = = 0 ) {
cx_write ( bus - > reg_addr , msg - > addr < < 25 ) ;
cx_write ( bus - > reg_ctrl , bus - > i2c_period | ( 1 < < 2 ) | 1 ) ;
if ( ! i2c_wait_done ( i2c_adap ) )
return - EIO ;
if ( ! i2c_slave_did_ack ( i2c_adap ) )
return - EIO ;
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s() returns 0 \n " , __func__ ) ;
2007-03-11 20:44:05 -03:00
return 0 ;
}
2007-12-15 03:31:09 -03:00
if ( i2c_debug ) {
if ( joined )
printk ( " R " ) ;
else
printk ( " <R %02x " , ( msg - > addr < < 1 ) + 1 ) ;
}
2007-09-20 01:44:27 -03:00
2007-03-11 20:44:05 -03:00
for ( cnt = 0 ; cnt < msg - > len ; cnt + + ) {
ctrl = bus - > i2c_period | ( 1 < < 12 ) | ( 1 < < 2 ) | 1 ;
2007-09-20 01:44:27 -03:00
if ( cnt < msg - > len - 1 )
2007-03-11 20:44:05 -03:00
ctrl | = I2C_NOSTOP | I2C_EXTEND ;
cx_write ( bus - > reg_addr , msg - > addr < < 25 ) ;
cx_write ( bus - > reg_ctrl , ctrl ) ;
retval = i2c_wait_done ( i2c_adap ) ;
if ( retval < 0 )
goto err ;
if ( retval = = 0 )
goto eio ;
msg - > buf [ cnt ] = cx_read ( bus - > reg_rdata ) & 0xff ;
if ( i2c_debug ) {
2007-09-20 01:44:27 -03:00
printk ( " %02x " , msg - > buf [ cnt ] ) ;
2007-03-11 20:44:05 -03:00
if ( ! ( ctrl & I2C_NOSTOP ) )
printk ( " > \n " ) ;
}
}
return msg - > len ;
eio :
retval = - EIO ;
err :
2007-12-15 03:31:09 -03:00
if ( i2c_debug )
printk ( " ERR: %d \n " , retval ) ;
2007-03-11 20:44:05 -03:00
return retval ;
}
2007-03-20 23:00:18 -03:00
static int i2c_xfer ( struct i2c_adapter * i2c_adap ,
struct i2c_msg * msgs , int num )
2007-03-11 20:44:05 -03:00
{
struct cx23885_i2c * bus = i2c_adap - > algo_data ;
struct cx23885_dev * dev = bus - > dev ;
int i , retval = 0 ;
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s(num = %d) \n " , __func__ , num ) ;
2007-03-11 20:44:05 -03:00
for ( i = 0 ; i < num ; i + + ) {
2007-03-20 23:00:18 -03:00
dprintk ( 1 , " %s(num = %d) addr = 0x%02x len = 0x%x \n " ,
2008-04-08 23:20:00 -03:00
__func__ , num , msgs [ i ] . addr , msgs [ i ] . len ) ;
2007-03-11 20:44:05 -03:00
if ( msgs [ i ] . flags & I2C_M_RD ) {
/* read */
2007-12-15 03:31:09 -03:00
retval = i2c_readbytes ( i2c_adap , & msgs [ i ] , 0 ) ;
} else if ( i + 1 < num & & ( msgs [ i + 1 ] . flags & I2C_M_RD ) & &
msgs [ i ] . addr = = msgs [ i + 1 ] . addr ) {
/* write then read from same address */
retval = i2c_sendbytes ( i2c_adap , & msgs [ i ] ,
msgs [ i + 1 ] . len ) ;
2007-03-11 20:44:05 -03:00
if ( retval < 0 )
goto err ;
2007-12-15 03:31:09 -03:00
i + + ;
retval = i2c_readbytes ( i2c_adap , & msgs [ i ] , 1 ) ;
2007-03-11 20:44:05 -03:00
} else {
/* write */
2007-12-15 03:31:09 -03:00
retval = i2c_sendbytes ( i2c_adap , & msgs [ i ] , 0 ) ;
2007-03-11 20:44:05 -03:00
}
2007-12-15 03:31:09 -03:00
if ( retval < 0 )
goto err ;
2007-03-11 20:44:05 -03:00
}
return num ;
err :
return retval ;
}
static int attach_inform ( struct i2c_client * client )
{
2008-01-10 03:40:49 -03:00
struct cx23885_i2c * bus = i2c_get_adapdata ( client - > adapter ) ;
struct cx23885_dev * dev = bus - > dev ;
struct tuner_setup tun_setup ;
2007-03-11 20:44:05 -03:00
dprintk ( 1 , " %s i2c attach [addr=0x%x,client=%s] \n " ,
client - > driver - > driver . name , client - > addr , client - > name ) ;
if ( ! client - > driver - > command )
return 0 ;
2008-01-10 03:40:49 -03:00
if ( dev - > tuner_type ! = UNSET ) {
dprintk ( 1 , " %s (tuner) i2c attach [addr=0x%x,client=%s] \n " ,
client - > driver - > driver . name , client - > addr ,
client - > name ) ;
if ( ( dev - > tuner_addr = = ADDR_UNSET ) | |
( dev - > tuner_addr = = client - > addr ) ) {
dprintk ( 1 , " %s (tuner || addr UNSET) \n " ,
client - > driver - > driver . name ) ;
dprintk ( 1 , " %s i2c attach [addr=0x%x,client=%s] \n " ,
client - > driver - > driver . name ,
client - > addr , client - > name ) ;
tun_setup . mode_mask = T_ANALOG_TV ;
tun_setup . type = dev - > tuner_type ;
tun_setup . addr = dev - > tuner_addr ;
client - > driver - > command ( client , TUNER_SET_TYPE_ADDR ,
& tun_setup ) ;
}
}
2007-03-11 20:44:05 -03:00
return 0 ;
}
static int detach_inform ( struct i2c_client * client )
{
struct cx23885_dev * dev = i2c_get_adapdata ( client - > adapter ) ;
dprintk ( 1 , " i2c detach [client=%s] \n " , client - > name ) ;
return 0 ;
}
2007-03-20 23:00:18 -03:00
void cx23885_call_i2c_clients ( struct cx23885_i2c * bus ,
unsigned int cmd , void * arg )
2007-03-11 20:44:05 -03:00
{
if ( bus - > i2c_rc ! = 0 )
return ;
2007-03-20 23:03:52 -03:00
i2c_clients_command ( & bus - > i2c_adap , cmd , arg ) ;
2007-03-11 20:44:05 -03:00
}
static u32 cx23885_functionality ( struct i2c_adapter * adap )
{
2007-03-19 18:03:03 -03:00
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C ;
2007-03-11 20:44:05 -03:00
}
static struct i2c_algorithm cx23885_i2c_algo_template = {
. master_xfer = i2c_xfer ,
. functionality = cx23885_functionality ,
} ;
/* ----------------------------------------------------------------------- */
static struct i2c_adapter cx23885_i2c_adap_template = {
. name = " cx23885 " ,
. owner = THIS_MODULE ,
. id = I2C_HW_B_CX23885 ,
. algo = & cx23885_i2c_algo_template ,
2008-01-10 03:40:49 -03:00
. class = I2C_CLASS_TV_ANALOG ,
2007-03-11 20:44:05 -03:00
. client_register = attach_inform ,
. client_unregister = detach_inform ,
} ;
static struct i2c_client cx23885_i2c_client_template = {
. name = " cx23885 internal " ,
} ;
static char * i2c_devs [ 128 ] = {
2008-04-19 01:14:19 -03:00
[ 0x10 > > 1 ] = " tda10048 " ,
2008-04-22 15:38:26 -03:00
[ 0x12 > > 1 ] = " dib7000pc " ,
2007-09-08 15:17:13 -03:00
[ 0x1c > > 1 ] = " lgdt3303 " ,
[ 0x86 > > 1 ] = " tda9887 " ,
2007-03-11 20:44:05 -03:00
[ 0x32 > > 1 ] = " cx24227 " ,
[ 0x88 > > 1 ] = " cx25837 " ,
[ 0x84 > > 1 ] = " tda8295 " ,
[ 0xa0 > > 1 ] = " eeprom " ,
2007-09-08 15:17:13 -03:00
[ 0xc0 > > 1 ] = " tuner/mt2131/tda8275 " ,
2008-04-22 15:38:26 -03:00
[ 0xc2 > > 1 ] = " tuner/mt2131/tda8275/xc5000/xc3028 " ,
[ 0xc8 > > 1 ] = " tuner/xc3028L " ,
2007-03-11 20:44:05 -03:00
} ;
static void do_i2c_scan ( char * name , struct i2c_client * c )
{
unsigned char buf ;
2007-03-20 23:00:18 -03:00
int i , rc ;
2007-03-11 20:44:05 -03:00
for ( i = 0 ; i < 128 ; i + + ) {
c - > addr = i ;
2007-03-20 23:00:18 -03:00
rc = i2c_master_recv ( c , & buf , 0 ) ;
2007-03-11 20:44:05 -03:00
if ( rc < 0 )
continue ;
printk ( " %s: i2c scan: found device @ 0x%x [%s] \n " ,
name , i < < 1 , i2c_devs [ i ] ? i2c_devs [ i ] : " ??? " ) ;
}
}
/* init + register i2c algo-bit adapter */
int cx23885_i2c_register ( struct cx23885_i2c * bus )
{
struct cx23885_dev * dev = bus - > dev ;
2008-04-08 23:20:00 -03:00
dprintk ( 1 , " %s(bus = %d) \n " , __func__ , bus - > nr ) ;
2007-03-11 20:44:05 -03:00
2007-03-20 23:00:18 -03:00
memcpy ( & bus - > i2c_adap , & cx23885_i2c_adap_template ,
sizeof ( bus - > i2c_adap ) ) ;
memcpy ( & bus - > i2c_algo , & cx23885_i2c_algo_template ,
sizeof ( bus - > i2c_algo ) ) ;
memcpy ( & bus - > i2c_client , & cx23885_i2c_client_template ,
sizeof ( bus - > i2c_client ) ) ;
2007-03-11 20:44:05 -03:00
bus - > i2c_adap . dev . parent = & dev - > pci - > dev ;
2007-03-20 23:00:18 -03:00
strlcpy ( bus - > i2c_adap . name , bus - > dev - > name ,
sizeof ( bus - > i2c_adap . name ) ) ;
2007-09-04 21:32:41 -03:00
2007-03-11 20:44:05 -03:00
bus - > i2c_algo . data = bus ;
bus - > i2c_adap . algo_data = bus ;
2008-01-10 03:40:49 -03:00
i2c_set_adapdata ( & bus - > i2c_adap , bus ) ;
2007-03-11 20:44:05 -03:00
i2c_add_adapter ( & bus - > i2c_adap ) ;
bus - > i2c_client . adapter = & bus - > i2c_adap ;
if ( 0 = = bus - > i2c_rc ) {
printk ( " %s: i2c bus %d registered \n " , dev - > name , bus - > nr ) ;
if ( i2c_scan )
do_i2c_scan ( dev - > name , & bus - > i2c_client ) ;
} else
printk ( " %s: i2c bus %d register FAILED \n " , dev - > name , bus - > nr ) ;
return bus - > i2c_rc ;
}
int cx23885_i2c_unregister ( struct cx23885_i2c * bus )
{
i2c_del_adapter ( & bus - > i2c_adap ) ;
return 0 ;
}
2008-01-13 23:44:47 -03:00
void cx23885_av_clk ( struct cx23885_dev * dev , int enable )
{
/* write 0 to bus 2 addr 0x144 via i2x_xfer() */
char buffer [ 3 ] ;
struct i2c_msg msg ;
dprintk ( 1 , " %s(enabled = %d) \n " , __func__ , enable ) ;
/* Register 0x144 */
buffer [ 0 ] = 0x01 ;
buffer [ 1 ] = 0x44 ;
if ( enable = = 1 )
buffer [ 2 ] = 0x05 ;
else
buffer [ 2 ] = 0x00 ;
msg . addr = 0x44 ;
msg . flags = I2C_M_TEN ;
msg . len = 3 ;
msg . buf = buffer ;
i2c_xfer ( & dev - > i2c_bus [ 2 ] . i2c_adap , & msg , 1 ) ;
}
2007-03-11 20:44:05 -03:00
/* ----------------------------------------------------------------------- */
/*
* Local variables :
* c - basic - offset : 8
* End :
*/