2011-04-10 03:07:30 +04:00
/*
* NXP TDA18212HN silicon tuner driver
*
* Copyright ( C ) 2011 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 .
*/
2011-07-31 11:30:10 +04:00
# include "tda18212.h"
2013-11-02 13:07:42 +04:00
/* Max transfer size done by I2C transfer functions */
# define MAX_XFER_SIZE 64
2011-07-31 11:30:10 +04:00
struct tda18212_priv {
struct tda18212_config * cfg ;
struct i2c_adapter * i2c ;
2011-11-13 18:20:08 +04:00
u32 if_frequency ;
2011-07-31 11:30:10 +04:00
} ;
2011-04-10 03:07:30 +04:00
/* write multiple registers */
static int tda18212_wr_regs ( struct tda18212_priv * priv , u8 reg , u8 * val ,
int len )
{
int ret ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ;
2011-04-10 03:07:30 +04:00
struct i2c_msg msg [ 1 ] = {
{
. addr = priv - > cfg - > i2c_address ,
. flags = 0 ,
2013-11-02 13:07:42 +04:00
. len = 1 + len ,
2011-04-10 03:07:30 +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 ;
}
2011-04-10 03:07:30 +04:00
buf [ 0 ] = reg ;
memcpy ( & buf [ 1 ] , val , len ) ;
ret = i2c_transfer ( priv - > i2c , msg , 1 ) ;
if ( ret = = 1 ) {
ret = 0 ;
} else {
2012-08-10 03:50:36 +04:00
dev_warn ( & priv - > i2c - > dev , " %s: i2c wr failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
2011-04-10 03:07:30 +04:00
ret = - EREMOTEIO ;
}
return ret ;
}
/* read multiple registers */
static int tda18212_rd_regs ( struct tda18212_priv * priv , u8 reg , u8 * val ,
int len )
{
int ret ;
2013-11-02 13:07:42 +04:00
u8 buf [ MAX_XFER_SIZE ] ;
2011-04-10 03:07:30 +04:00
struct i2c_msg msg [ 2 ] = {
{
. addr = priv - > cfg - > i2c_address ,
. flags = 0 ,
. len = 1 ,
. buf = & reg ,
} , {
. addr = priv - > cfg - > i2c_address ,
. flags = I2C_M_RD ,
2013-11-02 13:07:42 +04:00
. len = len ,
2011-04-10 03:07:30 +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 ;
}
2011-04-10 03:07:30 +04:00
ret = i2c_transfer ( priv - > i2c , msg , 2 ) ;
if ( ret = = 2 ) {
memcpy ( val , buf , len ) ;
ret = 0 ;
} else {
2012-08-10 03:50:36 +04:00
dev_warn ( & priv - > i2c - > dev , " %s: i2c rd failed=%d reg=%02x " \
" len=%d \n " , KBUILD_MODNAME , ret , reg , len ) ;
2011-04-10 03:07:30 +04:00
ret = - EREMOTEIO ;
}
return ret ;
}
/* write single register */
static int tda18212_wr_reg ( struct tda18212_priv * priv , u8 reg , u8 val )
{
return tda18212_wr_regs ( priv , reg , & val , 1 ) ;
}
/* read single register */
static int tda18212_rd_reg ( struct tda18212_priv * priv , u8 reg , u8 * val )
{
return tda18212_rd_regs ( priv , reg , val , 1 ) ;
}
#if 0 /* keep, useful when developing driver */
static void tda18212_dump_regs ( struct tda18212_priv * priv )
{
int i ;
u8 buf [ 256 ] ;
# define TDA18212_RD_LEN 32
for ( i = 0 ; i < sizeof ( buf ) ; i + = TDA18212_RD_LEN )
tda18212_rd_regs ( priv , i , & buf [ i ] , TDA18212_RD_LEN ) ;
print_hex_dump ( KERN_INFO , " " , DUMP_PREFIX_OFFSET , 32 , 1 , buf ,
sizeof ( buf ) , true ) ;
return ;
}
# endif
2011-12-24 19:24:33 +04:00
static int tda18212_set_params ( struct dvb_frontend * fe )
2011-04-10 03:07:30 +04:00
{
struct tda18212_priv * priv = fe - > tuner_priv ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
int ret , i ;
u32 if_khz ;
u8 buf [ 9 ] ;
2011-08-13 01:29:10 +04:00
# define DVBT_6 0
# define DVBT_7 1
# define DVBT_8 2
# define DVBT2_6 3
# define DVBT2_7 4
# define DVBT2_8 5
# define DVBC_6 6
# define DVBC_8 7
2014-03-03 23:27:38 +04:00
# define ATSC_VSB 8
# define ATSC_QAM 9
2011-04-10 03:07:30 +04:00
static const u8 bw_params [ ] [ 3 ] = {
2011-08-13 01:29:10 +04:00
/* reg: 0f 13 23 */
[ DVBT_6 ] = { 0xb3 , 0x20 , 0x03 } ,
[ DVBT_7 ] = { 0xb3 , 0x31 , 0x01 } ,
[ DVBT_8 ] = { 0xb3 , 0x22 , 0x01 } ,
[ DVBT2_6 ] = { 0xbc , 0x20 , 0x03 } ,
[ DVBT2_7 ] = { 0xbc , 0x72 , 0x03 } ,
[ DVBT2_8 ] = { 0xbc , 0x22 , 0x01 } ,
[ DVBC_6 ] = { 0x92 , 0x50 , 0x03 } ,
[ DVBC_8 ] = { 0x92 , 0x53 , 0x03 } ,
2014-03-03 23:27:38 +04:00
[ ATSC_VSB ] = { 0x7d , 0x20 , 0x63 } ,
[ ATSC_QAM ] = { 0x7d , 0x20 , 0x63 } ,
2011-04-10 03:07:30 +04:00
} ;
2012-08-10 03:50:36 +04:00
dev_dbg ( & priv - > i2c - > dev ,
" %s: delivery_system=%d frequency=%d bandwidth_hz=%d \n " ,
__func__ , c - > delivery_system , c - > frequency ,
c - > bandwidth_hz ) ;
2011-04-10 03:07:30 +04:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
switch ( c - > delivery_system ) {
2014-03-03 23:27:38 +04:00
case SYS_ATSC :
if_khz = priv - > cfg - > if_atsc_vsb ;
i = ATSC_VSB ;
break ;
case SYS_DVBC_ANNEX_B :
if_khz = priv - > cfg - > if_atsc_qam ;
i = ATSC_QAM ;
break ;
2011-04-10 03:07:30 +04:00
case SYS_DVBT :
switch ( c - > bandwidth_hz ) {
case 6000000 :
if_khz = priv - > cfg - > if_dvbt_6 ;
2011-08-13 01:29:10 +04:00
i = DVBT_6 ;
2011-04-10 03:07:30 +04:00
break ;
case 7000000 :
if_khz = priv - > cfg - > if_dvbt_7 ;
2011-08-13 01:29:10 +04:00
i = DVBT_7 ;
2011-04-10 03:07:30 +04:00
break ;
case 8000000 :
if_khz = priv - > cfg - > if_dvbt_8 ;
2011-08-13 01:29:10 +04:00
i = DVBT_8 ;
break ;
default :
ret = - EINVAL ;
goto error ;
}
break ;
case SYS_DVBT2 :
switch ( c - > bandwidth_hz ) {
case 6000000 :
if_khz = priv - > cfg - > if_dvbt2_6 ;
i = DVBT2_6 ;
break ;
case 7000000 :
if_khz = priv - > cfg - > if_dvbt2_7 ;
i = DVBT2_7 ;
break ;
case 8000000 :
if_khz = priv - > cfg - > if_dvbt2_8 ;
i = DVBT2_8 ;
2011-04-10 03:07:30 +04:00
break ;
default :
ret = - EINVAL ;
goto error ;
}
break ;
2011-12-23 01:11:39 +04:00
case SYS_DVBC_ANNEX_A :
case SYS_DVBC_ANNEX_C :
2011-04-10 03:07:30 +04:00
if_khz = priv - > cfg - > if_dvbc ;
2011-08-13 01:29:10 +04:00
i = DVBC_8 ;
2011-04-10 03:07:30 +04:00
break ;
default :
ret = - EINVAL ;
goto error ;
}
ret = tda18212_wr_reg ( priv , 0x23 , bw_params [ i ] [ 2 ] ) ;
if ( ret )
goto error ;
ret = tda18212_wr_reg ( priv , 0x06 , 0x00 ) ;
if ( ret )
goto error ;
ret = tda18212_wr_reg ( priv , 0x0f , bw_params [ i ] [ 0 ] ) ;
if ( ret )
goto error ;
buf [ 0 ] = 0x02 ;
buf [ 1 ] = bw_params [ i ] [ 1 ] ;
buf [ 2 ] = 0x03 ; /* default value */
2011-11-13 18:22:58 +04:00
buf [ 3 ] = DIV_ROUND_CLOSEST ( if_khz , 50 ) ;
2011-04-10 03:07:30 +04:00
buf [ 4 ] = ( ( c - > frequency / 1000 ) > > 16 ) & 0xff ;
buf [ 5 ] = ( ( c - > frequency / 1000 ) > > 8 ) & 0xff ;
buf [ 6 ] = ( ( c - > frequency / 1000 ) > > 0 ) & 0xff ;
buf [ 7 ] = 0xc1 ;
buf [ 8 ] = 0x01 ;
ret = tda18212_wr_regs ( priv , 0x12 , buf , sizeof ( buf ) ) ;
if ( ret )
goto error ;
2011-11-13 18:20:08 +04:00
/* actual IF rounded as it is on register */
priv - > if_frequency = buf [ 3 ] * 50 * 1000 ;
2011-04-10 03:07:30 +04:00
exit :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
return ret ;
error :
2012-08-10 03:50:36 +04:00
dev_dbg ( & priv - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2011-04-10 03:07:30 +04:00
goto exit ;
}
2011-11-13 18:20:08 +04:00
static int tda18212_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct tda18212_priv * priv = fe - > tuner_priv ;
* frequency = priv - > if_frequency ;
return 0 ;
}
2011-04-10 03:07:30 +04:00
static int tda18212_release ( struct dvb_frontend * fe )
{
kfree ( fe - > tuner_priv ) ;
fe - > tuner_priv = NULL ;
return 0 ;
}
static const struct dvb_tuner_ops tda18212_tuner_ops = {
. info = {
. name = " NXP TDA18212 " ,
. frequency_min = 48000000 ,
. frequency_max = 864000000 ,
. frequency_step = 1000 ,
} ,
. release = tda18212_release ,
. set_params = tda18212_set_params ,
2011-11-13 18:20:08 +04:00
. get_if_frequency = tda18212_get_if_frequency ,
2011-04-10 03:07:30 +04:00
} ;
struct dvb_frontend * tda18212_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c , struct tda18212_config * cfg )
{
struct tda18212_priv * priv = NULL ;
int ret ;
2012-11-02 00:00:09 +04:00
u8 val ;
2011-04-10 03:07:30 +04:00
priv = kzalloc ( sizeof ( struct tda18212_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 = tda18212_rd_reg ( priv , 0x00 , & val ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
2012-11-02 00:00:09 +04:00
if ( ! ret )
dev_dbg ( & priv - > i2c - > dev , " %s: chip id=%02x \n " , __func__ , val ) ;
2011-04-10 03:07:30 +04:00
if ( ret | | val ! = 0xc7 ) {
kfree ( priv ) ;
return NULL ;
}
2012-08-10 03:50:36 +04:00
dev_info ( & priv - > i2c - > dev ,
" %s: NXP TDA18212HN successfully identified \n " ,
KBUILD_MODNAME ) ;
2011-04-10 03:07:30 +04:00
memcpy ( & fe - > ops . tuner_ops , & tda18212_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
return fe ;
}
EXPORT_SYMBOL ( tda18212_attach ) ;
MODULE_DESCRIPTION ( " NXP TDA18212HN silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;