2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-08-13 10:41:02 +04:00
/*
* NXP TDA18218HN silicon tuner driver
*
* Copyright ( C ) 2010 Antti Palosaari < crope @ iki . fi >
*/
# include "tda18218_priv.h"
2013-11-02 13:07:42 +04:00
/* Max transfer size done by I2C transfer functions */
# define MAX_XFER_SIZE 64
2010-08-13 10:41:02 +04:00
/* write multiple registers */
static int tda18218_wr_regs ( struct tda18218_priv * priv , u8 reg , u8 * val , u8 len )
{
2012-08-21 19:12:50 +04:00
int ret = 0 , len2 , remaining ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ;
2010-08-13 10:41:02 +04:00
struct i2c_msg msg [ 1 ] = {
{
. addr = priv - > cfg - > i2c_address ,
. flags = 0 ,
. 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-08-21 19:12:50 +04:00
for ( remaining = len ; remaining > 0 ;
remaining - = ( priv - > cfg - > i2c_wr_max - 1 ) ) {
len2 = remaining ;
if ( len2 > ( priv - > cfg - > i2c_wr_max - 1 ) )
len2 = ( priv - > cfg - > i2c_wr_max - 1 ) ;
2010-08-13 10:41:02 +04:00
2012-08-21 19:12:50 +04:00
msg [ 0 ] . len = 1 + len2 ;
buf [ 0 ] = reg + len - remaining ;
memcpy ( & buf [ 1 ] , & val [ len - remaining ] , len2 ) ;
2010-08-13 10:41:02 +04:00
ret = i2c_transfer ( priv - > i2c , msg , 1 ) ;
if ( ret ! = 1 )
break ;
}
if ( ret = = 1 ) {
ret = 0 ;
} else {
2012-08-21 19:12:49 +04:00
dev_warn ( & priv - > i2c - > dev , " %s: i2c wr failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
2010-08-13 10:41:02 +04:00
ret = - EREMOTEIO ;
}
return ret ;
}
/* read multiple registers */
static int tda18218_rd_regs ( struct tda18218_priv * priv , u8 reg , u8 * val , u8 len )
{
int ret ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ; /* we must start read always from reg 0x00 */
2010-08-13 10:41:02 +04:00
struct i2c_msg msg [ 2 ] = {
{
. addr = priv - > cfg - > i2c_address ,
. flags = 0 ,
. len = 1 ,
. buf = " \x00 " ,
} , {
. addr = priv - > cfg - > i2c_address ,
. flags = I2C_M_RD ,
2013-11-02 13:07:42 +04:00
. len = reg + len ,
2010-08-13 10:41:02 +04:00
. buf = buf ,
}
} ;
2013-11-02 13:07:42 +04:00
if ( reg + 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 ;
}
2010-08-13 10:41:02 +04:00
ret = i2c_transfer ( priv - > i2c , msg , 2 ) ;
if ( ret = = 2 ) {
memcpy ( val , & buf [ reg ] , len ) ;
ret = 0 ;
} else {
2012-08-21 19:12:49 +04:00
dev_warn ( & priv - > i2c - > dev , " %s: i2c rd failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
2010-08-13 10:41:02 +04:00
ret = - EREMOTEIO ;
}
return ret ;
}
/* write single register */
static int tda18218_wr_reg ( struct tda18218_priv * priv , u8 reg , u8 val )
{
return tda18218_wr_regs ( priv , reg , & val , 1 ) ;
}
/* read single register */
static int tda18218_rd_reg ( struct tda18218_priv * priv , u8 reg , u8 * val )
{
return tda18218_rd_regs ( priv , reg , val , 1 ) ;
}
2011-12-24 19:24:33 +04:00
static int tda18218_set_params ( struct dvb_frontend * fe )
2010-08-13 10:41:02 +04:00
{
struct tda18218_priv * priv = fe - > tuner_priv ;
2011-12-21 14:47:27 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
u32 bw = c - > bandwidth_hz ;
2010-08-13 10:41:02 +04:00
int ret ;
u8 buf [ 3 ] , i , BP_Filter , LP_Fc ;
u32 LO_Frac ;
/* TODO: find out correct AGC algorithm */
u8 agc [ ] [ 2 ] = {
{ R20_AGC11 , 0x60 } ,
{ R23_AGC21 , 0x02 } ,
{ R20_AGC11 , 0xa0 } ,
{ R23_AGC21 , 0x09 } ,
{ R20_AGC11 , 0xe0 } ,
{ R23_AGC21 , 0x0c } ,
{ R20_AGC11 , 0x40 } ,
{ R23_AGC21 , 0x01 } ,
{ R20_AGC11 , 0x80 } ,
{ R23_AGC21 , 0x08 } ,
{ R20_AGC11 , 0xc0 } ,
{ R23_AGC21 , 0x0b } ,
{ R24_AGC22 , 0x1c } ,
{ R24_AGC22 , 0x0c } ,
} ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
/* low-pass filter cut-off frequency */
2011-12-21 14:47:27 +04:00
if ( bw < = 6000000 ) {
2010-08-13 10:41:02 +04:00
LP_Fc = 0 ;
2011-12-30 19:59:37 +04:00
priv - > if_frequency = 3000000 ;
2011-12-21 14:47:27 +04:00
} else if ( bw < = 7000000 ) {
2010-08-13 10:41:02 +04:00
LP_Fc = 1 ;
2011-11-13 07:19:56 +04:00
priv - > if_frequency = 3500000 ;
2011-12-21 14:47:27 +04:00
} else {
2010-08-13 10:41:02 +04:00
LP_Fc = 2 ;
2011-11-13 07:19:56 +04:00
priv - > if_frequency = 4000000 ;
2010-08-13 10:41:02 +04:00
}
2011-12-21 14:47:27 +04:00
LO_Frac = c - > frequency + priv - > if_frequency ;
2011-11-13 07:19:56 +04:00
2010-08-13 10:41:02 +04:00
/* band-pass filter */
if ( LO_Frac < 188000000 )
BP_Filter = 3 ;
else if ( LO_Frac < 253000000 )
BP_Filter = 4 ;
else if ( LO_Frac < 343000000 )
BP_Filter = 5 ;
else
BP_Filter = 6 ;
buf [ 0 ] = ( priv - > regs [ R1A_IF1 ] & ~ 7 ) | BP_Filter ; /* BP_Filter */
buf [ 1 ] = ( priv - > regs [ R1B_IF2 ] & ~ 3 ) | LP_Fc ; /* LP_Fc */
buf [ 2 ] = priv - > regs [ R1C_AGC2B ] ;
ret = tda18218_wr_regs ( priv , R1A_IF1 , buf , 3 ) ;
if ( ret )
goto error ;
buf [ 0 ] = ( LO_Frac / 1000 ) > > 12 ; /* LO_Frac_0 */
buf [ 1 ] = ( LO_Frac / 1000 ) > > 4 ; /* LO_Frac_1 */
buf [ 2 ] = ( LO_Frac / 1000 ) < < 4 |
( priv - > regs [ R0C_MD5 ] & 0x0f ) ; /* LO_Frac_2 */
ret = tda18218_wr_regs ( priv , R0A_MD3 , buf , 3 ) ;
if ( ret )
goto error ;
buf [ 0 ] = priv - > regs [ R0F_MD8 ] | ( 1 < < 6 ) ; /* Freq_prog_Start */
ret = tda18218_wr_regs ( priv , R0F_MD8 , buf , 1 ) ;
if ( ret )
goto error ;
buf [ 0 ] = priv - > regs [ R0F_MD8 ] & ~ ( 1 < < 6 ) ; /* Freq_prog_Start */
ret = tda18218_wr_regs ( priv , R0F_MD8 , buf , 1 ) ;
if ( ret )
goto error ;
/* trigger AGC */
for ( i = 0 ; i < ARRAY_SIZE ( agc ) ; i + + ) {
ret = tda18218_wr_reg ( priv , agc [ i ] [ 0 ] , agc [ i ] [ 1 ] ) ;
if ( ret )
goto error ;
}
error :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
if ( ret )
2012-08-21 19:12:49 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2010-08-13 10:41:02 +04:00
return ret ;
}
2011-11-13 07:19:56 +04:00
static int tda18218_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct tda18218_priv * priv = fe - > tuner_priv ;
* frequency = priv - > if_frequency ;
2012-08-21 19:12:49 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: if_frequency=%d \n " , __func__ , * frequency ) ;
2011-11-13 07:19:56 +04:00
return 0 ;
}
2010-08-13 10:41:02 +04:00
static int tda18218_sleep ( struct dvb_frontend * fe )
{
struct tda18218_priv * priv = fe - > tuner_priv ;
int ret ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
/* standby */
ret = tda18218_wr_reg ( priv , R17_PD1 , priv - > regs [ R17_PD1 ] | ( 1 < < 0 ) ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
if ( ret )
2012-08-21 19:12:49 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2010-08-13 10:41:02 +04:00
return ret ;
}
static int tda18218_init ( struct dvb_frontend * fe )
{
struct tda18218_priv * priv = fe - > tuner_priv ;
int ret ;
/* TODO: calibrations */
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
ret = tda18218_wr_regs ( priv , R00_ID , priv - > regs , TDA18218_NUM_REGS ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
if ( ret )
2012-08-21 19:12:49 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2010-08-13 10:41:02 +04:00
return ret ;
}
2016-11-19 01:30:51 +03:00
static void tda18218_release ( struct dvb_frontend * fe )
{
kfree ( fe - > tuner_priv ) ;
fe - > tuner_priv = NULL ;
}
2010-08-13 10:41:02 +04:00
static const struct dvb_tuner_ops tda18218_tuner_ops = {
. info = {
2018-07-06 01:59:35 +03:00
. name = " NXP TDA18218 " ,
2010-08-13 10:41:02 +04:00
2018-07-06 01:59:35 +03:00
. frequency_min_hz = 174 * MHz ,
. frequency_max_hz = 864 * MHz ,
. frequency_step_hz = 1 * kHz ,
2010-08-13 10:41:02 +04:00
} ,
2016-11-19 01:30:51 +03:00
. release = tda18218_release ,
2010-08-13 10:41:02 +04:00
. init = tda18218_init ,
. sleep = tda18218_sleep ,
. set_params = tda18218_set_params ,
2011-11-13 07:19:56 +04:00
. get_if_frequency = tda18218_get_if_frequency ,
2010-08-13 10:41:02 +04:00
} ;
struct dvb_frontend * tda18218_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c , struct tda18218_config * cfg )
{
struct tda18218_priv * priv = NULL ;
2012-11-02 00:00:09 +04:00
u8 val ;
2010-08-13 10:41:02 +04:00
int ret ;
/* chip default registers values */
static u8 def_regs [ ] = {
0xc0 , 0x88 , 0x00 , 0x8e , 0x03 , 0x00 , 0x00 , 0xd0 , 0x00 , 0x40 ,
0x00 , 0x00 , 0x07 , 0xff , 0x84 , 0x09 , 0x00 , 0x13 , 0x00 , 0x00 ,
0x01 , 0x84 , 0x09 , 0xf0 , 0x19 , 0x0a , 0x8e , 0x69 , 0x98 , 0x01 ,
0x00 , 0x58 , 0x10 , 0x40 , 0x8c , 0x00 , 0x0c , 0x48 , 0x85 , 0xc9 ,
0xa7 , 0x00 , 0x00 , 0x00 , 0x30 , 0x81 , 0x80 , 0x00 , 0x39 , 0x00 ,
0x8a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xf6 , 0xf6
} ;
priv = kzalloc ( sizeof ( struct tda18218_priv ) , GFP_KERNEL ) ;
if ( priv = = NULL )
return NULL ;
priv - > cfg = cfg ;
priv - > i2c = i2c ;
fe - > tuner_priv = priv ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
/* check if the tuner is there */
ret = tda18218_rd_reg ( priv , R00_ID , & val ) ;
2012-11-02 00:00:09 +04:00
if ( ! ret )
dev_dbg ( & priv - > i2c - > dev , " %s: chip id=%02x \n " , __func__ , val ) ;
2010-08-13 10:41:02 +04:00
if ( ret | | val ! = def_regs [ R00_ID ] ) {
kfree ( priv ) ;
return NULL ;
}
2012-08-21 19:12:49 +04:00
dev_info ( & priv - > i2c - > dev ,
" %s: NXP TDA18218HN successfully identified \n " ,
KBUILD_MODNAME ) ;
2010-08-13 10:41:02 +04:00
memcpy ( & fe - > ops . tuner_ops , & tda18218_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
memcpy ( priv - > regs , def_regs , sizeof ( def_regs ) ) ;
/* loop-through enabled chip default register values */
if ( priv - > cfg - > loop_through ) {
priv - > regs [ R17_PD1 ] = 0xb0 ;
priv - > regs [ R18_PD2 ] = 0x59 ;
}
/* standby */
ret = tda18218_wr_reg ( priv , R17_PD1 , priv - > regs [ R17_PD1 ] | ( 1 < < 0 ) ) ;
if ( ret )
2012-08-21 19:12:49 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2010-08-13 10:41:02 +04:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
return fe ;
}
EXPORT_SYMBOL ( tda18218_attach ) ;
MODULE_DESCRIPTION ( " NXP TDA18218HN silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;