2012-05-06 23:57:02 +04:00
/*
* Fitipower FC0013 tuner driver
*
* Copyright ( C ) 2012 Hans - Frieder Vogt < hfvogt @ gmx . net >
* partially based on driver code from Fitipower
* Copyright ( C ) 2010 Fitipower Integrated Technology Inc
*
* 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 "fc0013.h"
# include "fc0013-priv.h"
static int fc0013_writereg ( struct fc0013_priv * priv , u8 reg , u8 val )
{
u8 buf [ 2 ] = { reg , val } ;
struct i2c_msg msg = {
. addr = priv - > addr , . flags = 0 , . buf = buf , . len = 2
} ;
if ( i2c_transfer ( priv - > i2c , & msg , 1 ) ! = 1 ) {
err ( " I2C write reg failed, reg: %02x, val: %02x " , reg , val ) ;
return - EREMOTEIO ;
}
return 0 ;
}
static int fc0013_readreg ( struct fc0013_priv * priv , u8 reg , u8 * val )
{
struct i2c_msg msg [ 2 ] = {
{ . addr = priv - > addr , . flags = 0 , . buf = & reg , . len = 1 } ,
{ . addr = priv - > addr , . flags = I2C_M_RD , . buf = val , . len = 1 } ,
} ;
if ( i2c_transfer ( priv - > i2c , msg , 2 ) ! = 2 ) {
err ( " I2C read reg failed, reg: %02x " , reg ) ;
return - EREMOTEIO ;
}
return 0 ;
}
static int fc0013_release ( struct dvb_frontend * fe )
{
kfree ( fe - > tuner_priv ) ;
fe - > tuner_priv = NULL ;
return 0 ;
}
static int fc0013_init ( struct dvb_frontend * fe )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
int i , ret = 0 ;
unsigned char reg [ ] = {
0x00 , /* reg. 0x00: dummy */
0x09 , /* reg. 0x01 */
0x16 , /* reg. 0x02 */
0x00 , /* reg. 0x03 */
0x00 , /* reg. 0x04 */
0x17 , /* reg. 0x05 */
0x02 , /* reg. 0x06 */
0x0a , /* reg. 0x07: CHECK */
0xff , /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
Loop Bw 1 / 8 */
0x6f , /* reg. 0x09: enable LoopThrough */
0xb8 , /* reg. 0x0a: Disable LO Test Buffer */
0x82 , /* reg. 0x0b: CHECK */
0xfc , /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
0x01 , /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
0x00 , /* reg. 0x0e */
0x00 , /* reg. 0x0f */
0x00 , /* reg. 0x10 */
0x00 , /* reg. 0x11 */
0x00 , /* reg. 0x12 */
0x00 , /* reg. 0x13 */
0x50 , /* reg. 0x14: DVB-t High Gain, UHF.
Middle Gain : 0x48 , Low Gain : 0x40 */
0x01 , /* reg. 0x15 */
} ;
switch ( priv - > xtal_freq ) {
case FC_XTAL_27_MHZ :
case FC_XTAL_28_8_MHZ :
reg [ 0x07 ] | = 0x20 ;
break ;
case FC_XTAL_36_MHZ :
default :
break ;
}
if ( priv - > dual_master )
reg [ 0x0c ] | = 0x02 ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
for ( i = 1 ; i < sizeof ( reg ) ; i + + ) {
ret = fc0013_writereg ( priv , i , reg [ i ] ) ;
if ( ret )
break ;
}
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
if ( ret )
err ( " fc0013_writereg failed: %d " , ret ) ;
return ret ;
}
static int fc0013_sleep ( struct dvb_frontend * fe )
{
/* nothing to do here */
return 0 ;
}
int fc0013_rc_cal_add ( struct dvb_frontend * fe , int rc_val )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
int ret ;
u8 rc_cal ;
int val ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
/* push rc_cal value, get rc_cal value */
ret = fc0013_writereg ( priv , 0x10 , 0x00 ) ;
if ( ret )
goto error_out ;
/* get rc_cal value */
ret = fc0013_readreg ( priv , 0x10 , & rc_cal ) ;
if ( ret )
goto error_out ;
rc_cal & = 0x0f ;
val = ( int ) rc_cal + rc_val ;
/* forcing rc_cal */
ret = fc0013_writereg ( priv , 0x0d , 0x11 ) ;
if ( ret )
goto error_out ;
/* modify rc_cal value */
if ( val > 15 )
ret = fc0013_writereg ( priv , 0x10 , 0x0f ) ;
else if ( val < 0 )
ret = fc0013_writereg ( priv , 0x10 , 0x00 ) ;
else
ret = fc0013_writereg ( priv , 0x10 , ( u8 ) val ) ;
error_out :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
return ret ;
}
EXPORT_SYMBOL ( fc0013_rc_cal_add ) ;
int fc0013_rc_cal_reset ( struct dvb_frontend * fe )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
int ret ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
ret = fc0013_writereg ( priv , 0x0d , 0x01 ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x10 , 0x00 ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
return ret ;
}
EXPORT_SYMBOL ( fc0013_rc_cal_reset ) ;
static int fc0013_set_vhf_track ( struct fc0013_priv * priv , u32 freq )
{
int ret ;
u8 tmp ;
ret = fc0013_readreg ( priv , 0x1d , & tmp ) ;
if ( ret )
goto error_out ;
tmp & = 0xe3 ;
if ( freq < = 177500 ) { /* VHF Track: 7 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x1c ) ;
} else if ( freq < = 184500 ) { /* VHF Track: 6 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x18 ) ;
} else if ( freq < = 191500 ) { /* VHF Track: 5 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x14 ) ;
} else if ( freq < = 198500 ) { /* VHF Track: 4 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x10 ) ;
} else if ( freq < = 205500 ) { /* VHF Track: 3 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x0c ) ;
} else if ( freq < = 219500 ) { /* VHF Track: 2 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x08 ) ;
} else if ( freq < 300000 ) { /* VHF Track: 1 */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x04 ) ;
} else { /* UHF and GPS */
ret = fc0013_writereg ( priv , 0x1d , tmp | 0x1c ) ;
}
if ( ret )
goto error_out ;
error_out :
return ret ;
}
static int fc0013_set_params ( struct dvb_frontend * fe )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
int i , ret = 0 ;
struct dtv_frontend_properties * p = & fe - > dtv_property_cache ;
u32 freq = p - > frequency / 1000 ;
u32 delsys = p - > delivery_system ;
unsigned char reg [ 7 ] , am , pm , multi , tmp ;
unsigned long f_vco ;
unsigned short xtal_freq_khz_2 , xin , xdiv ;
int vco_select = false ;
if ( fe - > callback ) {
ret = fe - > callback ( priv - > i2c , DVB_FRONTEND_COMPONENT_TUNER ,
FC_FE_CALLBACK_VHF_ENABLE , ( freq > 300000 ? 0 : 1 ) ) ;
if ( ret )
goto exit ;
}
switch ( priv - > xtal_freq ) {
case FC_XTAL_27_MHZ :
xtal_freq_khz_2 = 27000 / 2 ;
break ;
case FC_XTAL_36_MHZ :
xtal_freq_khz_2 = 36000 / 2 ;
break ;
case FC_XTAL_28_8_MHZ :
default :
xtal_freq_khz_2 = 28800 / 2 ;
break ;
}
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
/* set VHF track */
ret = fc0013_set_vhf_track ( priv , freq ) ;
if ( ret )
goto exit ;
if ( freq < 300000 ) {
/* enable VHF filter */
ret = fc0013_readreg ( priv , 0x07 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x07 , tmp | 0x10 ) ;
if ( ret )
goto exit ;
/* disable UHF & disable GPS */
ret = fc0013_readreg ( priv , 0x14 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x14 , tmp & 0x1f ) ;
if ( ret )
goto exit ;
} else if ( freq < = 862000 ) {
/* disable VHF filter */
ret = fc0013_readreg ( priv , 0x07 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x07 , tmp & 0xef ) ;
if ( ret )
goto exit ;
/* enable UHF & disable GPS */
ret = fc0013_readreg ( priv , 0x14 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x14 , ( tmp & 0x1f ) | 0x40 ) ;
if ( ret )
goto exit ;
} else {
/* disable VHF filter */
ret = fc0013_readreg ( priv , 0x07 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x07 , tmp & 0xef ) ;
if ( ret )
goto exit ;
/* disable UHF & enable GPS */
ret = fc0013_readreg ( priv , 0x14 , & tmp ) ;
if ( ret )
goto exit ;
ret = fc0013_writereg ( priv , 0x14 , ( tmp & 0x1f ) | 0x20 ) ;
if ( ret )
goto exit ;
}
/* select frequency divider and the frequency of VCO */
if ( freq < 37084 ) { /* freq * 96 < 3560000 */
multi = 96 ;
reg [ 5 ] = 0x82 ;
reg [ 6 ] = 0x00 ;
} else if ( freq < 55625 ) { /* freq * 64 < 3560000 */
multi = 64 ;
reg [ 5 ] = 0x02 ;
reg [ 6 ] = 0x02 ;
} else if ( freq < 74167 ) { /* freq * 48 < 3560000 */
multi = 48 ;
reg [ 5 ] = 0x42 ;
reg [ 6 ] = 0x00 ;
} else if ( freq < 111250 ) { /* freq * 32 < 3560000 */
multi = 32 ;
reg [ 5 ] = 0x82 ;
reg [ 6 ] = 0x02 ;
} else if ( freq < 148334 ) { /* freq * 24 < 3560000 */
multi = 24 ;
reg [ 5 ] = 0x22 ;
reg [ 6 ] = 0x00 ;
} else if ( freq < 222500 ) { /* freq * 16 < 3560000 */
multi = 16 ;
reg [ 5 ] = 0x42 ;
reg [ 6 ] = 0x02 ;
} else if ( freq < 296667 ) { /* freq * 12 < 3560000 */
multi = 12 ;
reg [ 5 ] = 0x12 ;
reg [ 6 ] = 0x00 ;
} else if ( freq < 445000 ) { /* freq * 8 < 3560000 */
multi = 8 ;
reg [ 5 ] = 0x22 ;
reg [ 6 ] = 0x02 ;
} else if ( freq < 593334 ) { /* freq * 6 < 3560000 */
multi = 6 ;
reg [ 5 ] = 0x0a ;
reg [ 6 ] = 0x00 ;
} else if ( freq < 950000 ) { /* freq * 4 < 3800000 */
multi = 4 ;
reg [ 5 ] = 0x12 ;
reg [ 6 ] = 0x02 ;
} else {
multi = 2 ;
reg [ 5 ] = 0x0a ;
reg [ 6 ] = 0x02 ;
}
f_vco = freq * multi ;
if ( f_vco > = 3060000 ) {
reg [ 6 ] | = 0x08 ;
vco_select = true ;
}
if ( freq > = 45000 ) {
/* From divided value (XDIV) determined the FA and FP value */
xdiv = ( unsigned short ) ( f_vco / xtal_freq_khz_2 ) ;
if ( ( f_vco - xdiv * xtal_freq_khz_2 ) > = ( xtal_freq_khz_2 / 2 ) )
xdiv + + ;
pm = ( unsigned char ) ( xdiv / 8 ) ;
am = ( unsigned char ) ( xdiv - ( 8 * pm ) ) ;
if ( am < 2 ) {
reg [ 1 ] = am + 8 ;
reg [ 2 ] = pm - 1 ;
} else {
reg [ 1 ] = am ;
reg [ 2 ] = pm ;
}
} else {
/* fix for frequency less than 45 MHz */
reg [ 1 ] = 0x06 ;
reg [ 2 ] = 0x11 ;
}
/* fix clock out */
reg [ 6 ] | = 0x20 ;
/* From VCO frequency determines the XIN ( fractional part of Delta
Sigma PLL ) and divided value ( XDIV ) */
xin = ( unsigned short ) ( f_vco - ( f_vco / xtal_freq_khz_2 ) * xtal_freq_khz_2 ) ;
xin = ( xin < < 15 ) / xtal_freq_khz_2 ;
if ( xin > = 16384 )
xin + = 32768 ;
reg [ 3 ] = xin > > 8 ;
reg [ 4 ] = xin & 0xff ;
if ( delsys = = SYS_DVBT ) {
reg [ 6 ] & = 0x3f ; /* bits 6 and 7 describe the bandwidth */
switch ( p - > bandwidth_hz ) {
case 6000000 :
reg [ 6 ] | = 0x80 ;
break ;
case 7000000 :
reg [ 6 ] | = 0x40 ;
break ;
case 8000000 :
default :
break ;
}
} else {
err ( " %s: modulation type not supported! " , __func__ ) ;
return - EINVAL ;
}
/* modified for Realtek demod */
reg [ 5 ] | = 0x07 ;
for ( i = 1 ; i < = 6 ; i + + ) {
ret = fc0013_writereg ( priv , i , reg [ i ] ) ;
if ( ret )
goto exit ;
}
ret = fc0013_readreg ( priv , 0x11 , & tmp ) ;
if ( ret )
goto exit ;
if ( multi = = 64 )
ret = fc0013_writereg ( priv , 0x11 , tmp | 0x04 ) ;
else
ret = fc0013_writereg ( priv , 0x11 , tmp & 0xfb ) ;
if ( ret )
goto exit ;
/* VCO Calibration */
ret = fc0013_writereg ( priv , 0x0e , 0x80 ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x00 ) ;
/* VCO Re-Calibration if needed */
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x00 ) ;
if ( ! ret ) {
msleep ( 10 ) ;
ret = fc0013_readreg ( priv , 0x0e , & tmp ) ;
}
if ( ret )
goto exit ;
/* vco selection */
tmp & = 0x3f ;
if ( vco_select ) {
if ( tmp > 0x3c ) {
reg [ 6 ] & = ~ 0x08 ;
ret = fc0013_writereg ( priv , 0x06 , reg [ 6 ] ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x80 ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x00 ) ;
}
} else {
if ( tmp < 0x02 ) {
reg [ 6 ] | = 0x08 ;
ret = fc0013_writereg ( priv , 0x06 , reg [ 6 ] ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x80 ) ;
if ( ! ret )
ret = fc0013_writereg ( priv , 0x0e , 0x00 ) ;
}
}
priv - > frequency = p - > frequency ;
priv - > bandwidth = p - > bandwidth_hz ;
exit :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
2012-05-12 12:11:47 +04:00
if ( ret )
warn ( " %s: failed: %d " , __func__ , ret ) ;
2012-05-06 23:57:02 +04:00
return ret ;
}
static int fc0013_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
* frequency = priv - > frequency ;
return 0 ;
}
static int fc0013_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
/* always ? */
* frequency = 0 ;
return 0 ;
}
static int fc0013_get_bandwidth ( struct dvb_frontend * fe , u32 * bandwidth )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
* bandwidth = priv - > bandwidth ;
return 0 ;
}
2012-05-12 12:11:47 +04:00
# define INPUT_ADC_LEVEL -8
static int fc0013_get_rf_strength ( struct dvb_frontend * fe , u16 * strength )
{
struct fc0013_priv * priv = fe - > tuner_priv ;
int ret ;
unsigned char tmp ;
int int_temp , lna_gain , int_lna , tot_agc_gain , power ;
const int fc0013_lna_gain_table [ ] = {
/* low gain */
- 63 , - 58 , - 99 , - 73 ,
- 63 , - 65 , - 54 , - 60 ,
/* middle gain */
71 , 70 , 68 , 67 ,
65 , 63 , 61 , 58 ,
/* high gain */
197 , 191 , 188 , 186 ,
184 , 182 , 181 , 179 ,
} ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
ret = fc0013_writereg ( priv , 0x13 , 0x00 ) ;
if ( ret )
goto err ;
ret = fc0013_readreg ( priv , 0x13 , & tmp ) ;
if ( ret )
goto err ;
int_temp = tmp ;
ret = fc0013_readreg ( priv , 0x14 , & tmp ) ;
if ( ret )
goto err ;
lna_gain = tmp & 0x1f ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
if ( lna_gain < ARRAY_SIZE ( fc0013_lna_gain_table ) ) {
int_lna = fc0013_lna_gain_table [ lna_gain ] ;
tot_agc_gain = ( abs ( ( int_temp > > 5 ) - 7 ) - 2 +
( int_temp & 0x1f ) ) * 2 ;
power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10 ;
if ( power > = 45 )
* strength = 255 ; /* 100% */
else if ( power < - 95 )
* strength = 0 ;
else
* strength = ( power + 95 ) * 255 / 140 ;
* strength | = * strength < < 8 ;
} else {
ret = - 1 ;
}
goto exit ;
err :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
exit :
if ( ret )
warn ( " %s: failed: %d " , __func__ , ret ) ;
return ret ;
}
2012-05-06 23:57:02 +04:00
static const struct dvb_tuner_ops fc0013_tuner_ops = {
. info = {
. name = " Fitipower FC0013 " ,
. frequency_min = 37000000 , /* estimate */
. frequency_max = 1680000000 , /* CHECK */
. frequency_step = 0 ,
} ,
. release = fc0013_release ,
. init = fc0013_init ,
. sleep = fc0013_sleep ,
. set_params = fc0013_set_params ,
. get_frequency = fc0013_get_frequency ,
. get_if_frequency = fc0013_get_if_frequency ,
. get_bandwidth = fc0013_get_bandwidth ,
2012-05-12 12:11:47 +04:00
. get_rf_strength = fc0013_get_rf_strength ,
2012-05-06 23:57:02 +04:00
} ;
struct dvb_frontend * fc0013_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c , u8 i2c_address , int dual_master ,
enum fc001x_xtal_freq xtal_freq )
{
struct fc0013_priv * priv = NULL ;
priv = kzalloc ( sizeof ( struct fc0013_priv ) , GFP_KERNEL ) ;
if ( priv = = NULL )
return NULL ;
priv - > i2c = i2c ;
priv - > dual_master = dual_master ;
priv - > addr = i2c_address ;
priv - > xtal_freq = xtal_freq ;
info ( " Fitipower FC0013 successfully attached. " ) ;
fe - > tuner_priv = priv ;
memcpy ( & fe - > ops . tuner_ops , & fc0013_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
return fe ;
}
EXPORT_SYMBOL ( fc0013_attach ) ;
MODULE_DESCRIPTION ( " Fitipower FC0013 silicon tuner driver " ) ;
MODULE_AUTHOR ( " Hans-Frieder Vogt <hfvogt@gmx.net> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-05-12 12:11:47 +04:00
MODULE_VERSION ( " 0.2 " ) ;