2012-08-11 14:32:57 -03:00
/*
* STK1160 driver
*
* Copyright ( C ) 2012 Ezequiel Garcia
* < elezegarcia - - a . t - - gmail . com >
*
* Based on Easycap driver by R . M . Thomas
* Copyright ( C ) 2010 R . M . Thomas
* < rmthomas - - a . t - - sciolus . org >
*
* 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 .
*
*/
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/i2c.h>
# include "stk1160.h"
# include "stk1160-reg.h"
static unsigned int i2c_debug ;
module_param ( i2c_debug , int , 0644 ) ;
MODULE_PARM_DESC ( i2c_debug , " enable debug messages [i2c] " ) ;
# define dprintk_i2c(fmt, args...) \
do { \
if ( i2c_debug ) \
printk ( KERN_DEBUG fmt , # # args ) ; \
} while ( 0 )
static int stk1160_i2c_busy_wait ( struct stk1160 * dev , u8 wait_bit_mask )
{
unsigned long end ;
u8 flag ;
/* Wait until read/write finish bit is set */
end = jiffies + msecs_to_jiffies ( STK1160_I2C_TIMEOUT ) ;
while ( time_is_after_jiffies ( end ) ) {
stk1160_read_reg ( dev , STK1160_SICTL + 1 , & flag ) ;
/* read/write done? */
if ( flag & wait_bit_mask )
goto done ;
usleep_range ( 10 * USEC_PER_MSEC , 20 * USEC_PER_MSEC ) ;
}
return - ETIMEDOUT ;
done :
return 0 ;
}
static int stk1160_i2c_write_reg ( struct stk1160 * dev , u8 addr ,
u8 reg , u8 value )
{
int rc ;
/* Set serial device address */
rc = stk1160_write_reg ( dev , STK1160_SICTL_SDA , addr ) ;
if ( rc < 0 )
return rc ;
/* Set i2c device register sub-address */
rc = stk1160_write_reg ( dev , STK1160_SBUSW_WA , reg ) ;
if ( rc < 0 )
return rc ;
/* Set i2c device register value */
rc = stk1160_write_reg ( dev , STK1160_SBUSW_WD , value ) ;
if ( rc < 0 )
return rc ;
/* Start write now */
rc = stk1160_write_reg ( dev , STK1160_SICTL , 0x01 ) ;
if ( rc < 0 )
return rc ;
rc = stk1160_i2c_busy_wait ( dev , 0x04 ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
static int stk1160_i2c_read_reg ( struct stk1160 * dev , u8 addr ,
u8 reg , u8 * value )
{
int rc ;
/* Set serial device address */
rc = stk1160_write_reg ( dev , STK1160_SICTL_SDA , addr ) ;
if ( rc < 0 )
return rc ;
/* Set i2c device register sub-address */
rc = stk1160_write_reg ( dev , STK1160_SBUSR_RA , reg ) ;
if ( rc < 0 )
return rc ;
/* Start read now */
rc = stk1160_write_reg ( dev , STK1160_SICTL , 0x20 ) ;
if ( rc < 0 )
return rc ;
rc = stk1160_i2c_busy_wait ( dev , 0x01 ) ;
if ( rc < 0 )
return rc ;
2012-10-12 19:16:37 -03:00
rc = stk1160_read_reg ( dev , STK1160_SBUSR_RD , value ) ;
2012-08-11 14:32:57 -03:00
if ( rc < 0 )
return rc ;
return 0 ;
}
/*
* stk1160_i2c_check_for_device ( )
* check if there is a i2c_device at the supplied address
*/
static int stk1160_i2c_check_for_device ( struct stk1160 * dev ,
unsigned char addr )
{
int rc ;
/* Set serial device address */
rc = stk1160_write_reg ( dev , STK1160_SICTL_SDA , addr ) ;
if ( rc < 0 )
return rc ;
/* Set device sub-address, we'll chip version reg */
rc = stk1160_write_reg ( dev , STK1160_SBUSR_RA , 0x00 ) ;
if ( rc < 0 )
return rc ;
/* Start read now */
rc = stk1160_write_reg ( dev , STK1160_SICTL , 0x20 ) ;
if ( rc < 0 )
return rc ;
rc = stk1160_i2c_busy_wait ( dev , 0x01 ) ;
if ( rc < 0 )
return - ENODEV ;
return 0 ;
}
/*
* stk1160_i2c_xfer ( )
* the main i2c transfer function
*/
static int stk1160_i2c_xfer ( struct i2c_adapter * i2c_adap ,
struct i2c_msg msgs [ ] , int num )
{
struct stk1160 * dev = i2c_adap - > algo_data ;
int addr , rc , i ;
for ( i = 0 ; i < num ; i + + ) {
addr = msgs [ i ] . addr < < 1 ;
dprintk_i2c ( " %s: addr=%x " , __func__ , addr ) ;
if ( ! msgs [ i ] . len ) {
/* no len: check only for device presence */
rc = stk1160_i2c_check_for_device ( dev , addr ) ;
if ( rc < 0 ) {
dprintk_i2c ( " no device \n " ) ;
return rc ;
}
} else if ( msgs [ i ] . flags & I2C_M_RD ) {
/* read request without preceding register selection */
dprintk_i2c ( " subaddr not selected " ) ;
rc = - EOPNOTSUPP ;
goto err ;
} else if ( i + 1 < num & & msgs [ i ] . len < = 2 & &
( msgs [ i + 1 ] . flags & I2C_M_RD ) & &
msgs [ i ] . addr = = msgs [ i + 1 ] . addr ) {
if ( msgs [ i ] . len ! = 1 | | msgs [ i + 1 ] . len ! = 1 ) {
dprintk_i2c ( " len not supported " ) ;
rc = - EOPNOTSUPP ;
goto err ;
}
dprintk_i2c ( " subaddr=%x " , msgs [ i ] . buf [ 0 ] ) ;
rc = stk1160_i2c_read_reg ( dev , addr , msgs [ i ] . buf [ 0 ] ,
msgs [ i + 1 ] . buf ) ;
dprintk_i2c ( " read=%x " , * msgs [ i + 1 ] . buf ) ;
/* consumed two msgs, so we skip one of them */
i + + ;
} else {
if ( msgs [ i ] . len ! = 2 ) {
dprintk_i2c ( " len not supported " ) ;
rc = - EOPNOTSUPP ;
goto err ;
}
dprintk_i2c ( " subaddr=%x write=%x " ,
msgs [ i ] . buf [ 0 ] , msgs [ i ] . buf [ 1 ] ) ;
rc = stk1160_i2c_write_reg ( dev , addr , msgs [ i ] . buf [ 0 ] ,
msgs [ i ] . buf [ 1 ] ) ;
}
if ( rc < 0 )
goto err ;
dprintk_i2c ( " OK \n " ) ;
}
return num ;
err :
dprintk_i2c ( " ERROR: %d \n " , rc ) ;
return num ;
}
/*
* functionality ( ) , what da heck is this ?
*/
static u32 functionality ( struct i2c_adapter * adap )
{
return I2C_FUNC_SMBUS_EMUL ;
}
2016-08-29 10:12:01 -03:00
static const struct i2c_algorithm algo = {
2012-08-11 14:32:57 -03:00
. master_xfer = stk1160_i2c_xfer ,
. functionality = functionality ,
} ;
static struct i2c_adapter adap_template = {
. owner = THIS_MODULE ,
. name = " stk1160 " ,
. algo = & algo ,
} ;
static struct i2c_client client_template = {
. name = " stk1160 internal " ,
} ;
/*
* stk1160_i2c_register ( )
* register i2c bus
*/
int stk1160_i2c_register ( struct stk1160 * dev )
{
int rc ;
dev - > i2c_adap = adap_template ;
dev - > i2c_adap . dev . parent = dev - > dev ;
strcpy ( dev - > i2c_adap . name , " stk1160 " ) ;
dev - > i2c_adap . algo_data = dev ;
i2c_set_adapdata ( & dev - > i2c_adap , & dev - > v4l2_dev ) ;
rc = i2c_add_adapter ( & dev - > i2c_adap ) ;
if ( rc < 0 ) {
stk1160_err ( " cannot add i2c adapter (%d) \n " , rc ) ;
return rc ;
}
dev - > i2c_client = client_template ;
dev - > i2c_client . adapter = & dev - > i2c_adap ;
/* Set i2c clock divider device address */
stk1160_write_reg ( dev , STK1160_SICTL_CD , 0x0f ) ;
/* ??? */
stk1160_write_reg ( dev , STK1160_ASIC + 3 , 0x00 ) ;
return 0 ;
}
/*
* stk1160_i2c_unregister ( )
* unregister i2c_bus
*/
int stk1160_i2c_unregister ( struct stk1160 * dev )
{
i2c_del_adapter ( & dev - > i2c_adap ) ;
return 0 ;
}