2012-09-09 05:07:24 +04:00
/*
* FCI FC2580 silicon tuner driver
*
* Copyright ( C ) 2012 Antti Palosaari < crope @ iki . fi >
*
* 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 . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include "fc2580_priv.h"
2013-11-02 13:07:42 +04:00
/* Max transfer size done by I2C transfer functions */
# define MAX_XFER_SIZE 64
2012-09-09 05:07:24 +04:00
/*
* TODO :
* I2C write and read works only for one single register . Multiple registers
* could not be accessed using normal register address auto - increment .
* There could be ( very likely ) register to change that behavior . . . .
*
* Due to that limitation functions :
* fc2580_wr_regs ( )
* fc2580_rd_regs ( )
* could not be used for accessing more than one register at once .
*
* TODO :
* Currently it blind writes bunch of static registers from the
* fc2580_freq_regs_lut [ ] when fc2580_set_params ( ) is called . Add some
* logic to reduce unneeded register writes .
*/
/* write multiple registers */
static int fc2580_wr_regs ( struct fc2580_priv * priv , u8 reg , u8 * val , int len )
{
int ret ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ;
2012-09-09 05:07:24 +04:00
struct i2c_msg msg [ 1 ] = {
{
. addr = priv - > cfg - > i2c_addr ,
. flags = 0 ,
2013-11-02 13:07:42 +04:00
. len = 1 + len ,
2012-09-09 05:07:24 +04:00
. buf = buf ,
}
} ;
2013-11-02 13:07:42 +04:00
if ( 1 + len > sizeof ( buf ) ) {
dev_warn ( & priv - > i2c - > dev ,
" %s: i2c wr reg=%04x: len=%d is too big! \n " ,
KBUILD_MODNAME , reg , len ) ;
return - EINVAL ;
}
2012-09-09 05:07:24 +04:00
buf [ 0 ] = reg ;
memcpy ( & buf [ 1 ] , val , len ) ;
ret = i2c_transfer ( priv - > i2c , msg , 1 ) ;
if ( ret = = 1 ) {
ret = 0 ;
} else {
dev_warn ( & priv - > i2c - > dev , " %s: i2c wr failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
ret = - EREMOTEIO ;
}
return ret ;
}
/* read multiple registers */
static int fc2580_rd_regs ( struct fc2580_priv * priv , u8 reg , u8 * val , int len )
{
int ret ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ;
2012-09-09 05:07:24 +04:00
struct i2c_msg msg [ 2 ] = {
{
. addr = priv - > cfg - > i2c_addr ,
. flags = 0 ,
. len = 1 ,
. buf = & reg ,
} , {
. addr = priv - > cfg - > i2c_addr ,
. flags = I2C_M_RD ,
2013-11-02 13:07:42 +04:00
. len = len ,
2012-09-09 05:07:24 +04:00
. buf = buf ,
}
} ;
2013-11-02 13:07:42 +04:00
if ( len > sizeof ( buf ) ) {
dev_warn ( & priv - > i2c - > dev ,
" %s: i2c rd reg=%04x: len=%d is too big! \n " ,
KBUILD_MODNAME , reg , len ) ;
return - EINVAL ;
}
2012-09-09 05:07:24 +04:00
ret = i2c_transfer ( priv - > i2c , msg , 2 ) ;
if ( ret = = 2 ) {
memcpy ( val , buf , len ) ;
ret = 0 ;
} else {
dev_warn ( & priv - > i2c - > dev , " %s: i2c rd failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
ret = - EREMOTEIO ;
}
return ret ;
}
/* write single register */
static int fc2580_wr_reg ( struct fc2580_priv * priv , u8 reg , u8 val )
{
return fc2580_wr_regs ( priv , reg , & val , 1 ) ;
}
/* read single register */
static int fc2580_rd_reg ( struct fc2580_priv * priv , u8 reg , u8 * val )
{
return fc2580_rd_regs ( priv , reg , val , 1 ) ;
}
2012-11-06 23:48:41 +04:00
/* write single register conditionally only when value differs from 0xff
* XXX : This is special routine meant only for writing fc2580_freq_regs_lut [ ]
* values . Do not use for the other purposes . */
static int fc2580_wr_reg_ff ( struct fc2580_priv * priv , u8 reg , u8 val )
{
if ( val = = 0xff )
return 0 ;
else
return fc2580_wr_regs ( priv , reg , & val , 1 ) ;
}
2012-09-09 05:07:24 +04:00
static int fc2580_set_params ( struct dvb_frontend * fe )
{
struct fc2580_priv * priv = fe - > tuner_priv ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2012-09-24 15:51:01 +04:00
int ret = 0 , i ;
2012-09-09 05:07:24 +04:00
unsigned int r_val , n_val , k_val , k_val_reg , f_ref ;
u8 tmp_val , r18_val ;
u64 f_vco ;
/*
* Fractional - N synthesizer / PLL .
* Most likely all those PLL calculations are not correct . I am not
* sure , but it looks like it is divider based Fractional - N synthesizer .
* There is divider for reference clock too ?
* Anyhow , synthesizer calculation results seems to be quite correct .
*/
dev_dbg ( & priv - > i2c - > dev , " %s: delivery_system=%d frequency=%d " \
" bandwidth_hz=%d \n " , __func__ ,
c - > delivery_system , c - > frequency , c - > bandwidth_hz ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
/* PLL */
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_pll_lut ) ; i + + ) {
if ( c - > frequency < = fc2580_pll_lut [ i ] . freq )
break ;
}
if ( i = = ARRAY_SIZE ( fc2580_pll_lut ) )
goto err ;
f_vco = c - > frequency ;
f_vco * = fc2580_pll_lut [ i ] . div ;
2012-09-24 14:37:16 +04:00
if ( f_vco > = 2600000000UL )
2012-09-09 05:07:24 +04:00
tmp_val = 0x0e | fc2580_pll_lut [ i ] . band ;
else
tmp_val = 0x06 | fc2580_pll_lut [ i ] . band ;
ret = fc2580_wr_reg ( priv , 0x02 , tmp_val ) ;
if ( ret < 0 )
goto err ;
if ( f_vco > = 2UL * 76 * priv - > cfg - > clock ) {
r_val = 1 ;
r18_val = 0x00 ;
} else if ( f_vco > = 1UL * 76 * priv - > cfg - > clock ) {
r_val = 2 ;
r18_val = 0x10 ;
} else {
r_val = 4 ;
r18_val = 0x20 ;
}
f_ref = 2UL * priv - > cfg - > clock / r_val ;
2012-09-24 14:37:18 +04:00
n_val = div_u64_rem ( f_vco , f_ref , & k_val ) ;
2014-04-11 04:18:16 +04:00
k_val_reg = div_u64 ( 1ULL * k_val * ( 1 < < 20 ) , f_ref ) ;
2012-09-09 05:07:24 +04:00
ret = fc2580_wr_reg ( priv , 0x18 , r18_val | ( ( k_val_reg > > 16 ) & 0xff ) ) ;
if ( ret < 0 )
goto err ;
ret = fc2580_wr_reg ( priv , 0x1a , ( k_val_reg > > 8 ) & 0xff ) ;
if ( ret < 0 )
goto err ;
ret = fc2580_wr_reg ( priv , 0x1b , ( k_val_reg > > 0 ) & 0xff ) ;
if ( ret < 0 )
goto err ;
ret = fc2580_wr_reg ( priv , 0x1c , n_val ) ;
if ( ret < 0 )
goto err ;
if ( priv - > cfg - > clock > = 28000000 ) {
ret = fc2580_wr_reg ( priv , 0x4b , 0x22 ) ;
if ( ret < 0 )
goto err ;
}
if ( fc2580_pll_lut [ i ] . band = = 0x00 ) {
if ( c - > frequency < = 794000000 )
tmp_val = 0x9f ;
else
tmp_val = 0x8f ;
ret = fc2580_wr_reg ( priv , 0x2d , tmp_val ) ;
if ( ret < 0 )
goto err ;
}
/* registers */
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_freq_regs_lut ) ; i + + ) {
if ( c - > frequency < = fc2580_freq_regs_lut [ i ] . freq )
break ;
}
if ( i = = ARRAY_SIZE ( fc2580_freq_regs_lut ) )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x25 , fc2580_freq_regs_lut [ i ] . r25_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x27 , fc2580_freq_regs_lut [ i ] . r27_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x28 , fc2580_freq_regs_lut [ i ] . r28_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x29 , fc2580_freq_regs_lut [ i ] . r29_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x2b , fc2580_freq_regs_lut [ i ] . r2b_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x2c , fc2580_freq_regs_lut [ i ] . r2c_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x2d , fc2580_freq_regs_lut [ i ] . r2d_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x30 , fc2580_freq_regs_lut [ i ] . r30_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x44 , fc2580_freq_regs_lut [ i ] . r44_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x50 , fc2580_freq_regs_lut [ i ] . r50_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x53 , fc2580_freq_regs_lut [ i ] . r53_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x5f , fc2580_freq_regs_lut [ i ] . r5f_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x61 , fc2580_freq_regs_lut [ i ] . r61_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x62 , fc2580_freq_regs_lut [ i ] . r62_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x63 , fc2580_freq_regs_lut [ i ] . r63_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x67 , fc2580_freq_regs_lut [ i ] . r67_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x68 , fc2580_freq_regs_lut [ i ] . r68_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x69 , fc2580_freq_regs_lut [ i ] . r69_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6a , fc2580_freq_regs_lut [ i ] . r6a_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6b , fc2580_freq_regs_lut [ i ] . r6b_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6c , fc2580_freq_regs_lut [ i ] . r6c_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6d , fc2580_freq_regs_lut [ i ] . r6d_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6e , fc2580_freq_regs_lut [ i ] . r6e_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
2012-11-06 23:48:41 +04:00
ret = fc2580_wr_reg_ff ( priv , 0x6f , fc2580_freq_regs_lut [ i ] . r6f_val ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
/* IF filters */
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_if_filter_lut ) ; i + + ) {
if ( c - > bandwidth_hz < = fc2580_if_filter_lut [ i ] . freq )
break ;
}
if ( i = = ARRAY_SIZE ( fc2580_if_filter_lut ) )
goto err ;
ret = fc2580_wr_reg ( priv , 0x36 , fc2580_if_filter_lut [ i ] . r36_val ) ;
if ( ret < 0 )
goto err ;
2014-04-11 04:18:16 +04:00
ret = fc2580_wr_reg ( priv , 0x37 , div_u64 ( 1ULL * priv - > cfg - > clock *
fc2580_if_filter_lut [ i ] . mul , 1000000000 ) ) ;
2012-09-09 05:07:24 +04:00
if ( ret < 0 )
goto err ;
ret = fc2580_wr_reg ( priv , 0x39 , fc2580_if_filter_lut [ i ] . r39_val ) ;
if ( ret < 0 )
goto err ;
/* calibration? */
ret = fc2580_wr_reg ( priv , 0x2e , 0x09 ) ;
if ( ret < 0 )
goto err ;
for ( i = 0 ; i < 5 ; i + + ) {
ret = fc2580_rd_reg ( priv , 0x2f , & tmp_val ) ;
if ( ret < 0 )
goto err ;
/* done when [7:6] are set */
if ( ( tmp_val & 0xc0 ) = = 0xc0 )
break ;
ret = fc2580_wr_reg ( priv , 0x2e , 0x01 ) ;
if ( ret < 0 )
goto err ;
ret = fc2580_wr_reg ( priv , 0x2e , 0x09 ) ;
if ( ret < 0 )
goto err ;
usleep_range ( 5000 , 25000 ) ;
}
dev_dbg ( & priv - > i2c - > dev , " %s: loop=%i \n " , __func__ , i ) ;
ret = fc2580_wr_reg ( priv , 0x2e , 0x01 ) ;
if ( ret < 0 )
goto err ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
return 0 ;
err :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
return ret ;
}
static int fc2580_init ( struct dvb_frontend * fe )
{
struct fc2580_priv * priv = fe - > tuner_priv ;
int ret , i ;
dev_dbg ( & priv - > i2c - > dev , " %s: \n " , __func__ ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_init_reg_vals ) ; i + + ) {
ret = fc2580_wr_reg ( priv , fc2580_init_reg_vals [ i ] . reg ,
fc2580_init_reg_vals [ i ] . val ) ;
if ( ret < 0 )
goto err ;
}
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
return 0 ;
err :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
return ret ;
}
static int fc2580_sleep ( struct dvb_frontend * fe )
{
struct fc2580_priv * priv = fe - > tuner_priv ;
int ret ;
dev_dbg ( & priv - > i2c - > dev , " %s: \n " , __func__ ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
ret = fc2580_wr_reg ( priv , 0x02 , 0x0a ) ;
if ( ret < 0 )
goto err ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
return 0 ;
err :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
return ret ;
}
static int fc2580_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct fc2580_priv * priv = fe - > tuner_priv ;
dev_dbg ( & priv - > i2c - > dev , " %s: \n " , __func__ ) ;
* frequency = 0 ; /* Zero-IF */
return 0 ;
}
static int fc2580_release ( struct dvb_frontend * fe )
{
struct fc2580_priv * priv = fe - > tuner_priv ;
dev_dbg ( & priv - > i2c - > dev , " %s: \n " , __func__ ) ;
kfree ( fe - > tuner_priv ) ;
return 0 ;
}
static const struct dvb_tuner_ops fc2580_tuner_ops = {
. info = {
. name = " FCI FC2580 " ,
. frequency_min = 174000000 ,
. frequency_max = 862000000 ,
} ,
. release = fc2580_release ,
. init = fc2580_init ,
. sleep = fc2580_sleep ,
. set_params = fc2580_set_params ,
. get_if_frequency = fc2580_get_if_frequency ,
} ;
struct dvb_frontend * fc2580_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c , const struct fc2580_config * cfg )
{
struct fc2580_priv * priv ;
int ret ;
u8 chip_id ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
priv = kzalloc ( sizeof ( struct fc2580_priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
ret = - ENOMEM ;
dev_err ( & i2c - > dev , " %s: kzalloc() failed \n " , KBUILD_MODNAME ) ;
goto err ;
}
priv - > cfg = cfg ;
priv - > i2c = i2c ;
/* check if the tuner is there */
ret = fc2580_rd_reg ( priv , 0x01 , & chip_id ) ;
if ( ret < 0 )
goto err ;
dev_dbg ( & priv - > i2c - > dev , " %s: chip_id=%02x \n " , __func__ , chip_id ) ;
2012-09-22 03:28:43 +04:00
switch ( chip_id ) {
case 0x56 :
case 0x5a :
break ;
default :
2012-09-09 05:07:24 +04:00
goto err ;
2012-09-20 21:57:17 +04:00
}
2012-09-09 05:07:24 +04:00
dev_info ( & priv - > i2c - > dev ,
" %s: FCI FC2580 successfully identified \n " ,
KBUILD_MODNAME ) ;
2012-09-22 06:36:27 +04:00
fe - > tuner_priv = priv ;
memcpy ( & fe - > ops . tuner_ops , & fc2580_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
2012-09-09 05:07:24 +04:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
return fe ;
err :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
dev_dbg ( & i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
kfree ( priv ) ;
return NULL ;
}
EXPORT_SYMBOL ( fc2580_attach ) ;
MODULE_DESCRIPTION ( " FCI FC2580 silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;