2019-05-19 15:51:31 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-04-09 20:07:30 -03:00
/*
* NXP TDA18212HN silicon tuner driver
*
* Copyright ( C ) 2011 Antti Palosaari < crope @ iki . fi >
*/
2011-07-31 04:30:10 -03:00
# include "tda18212.h"
2014-08-04 01:00:46 -03:00
# include <linux/regmap.h>
2013-11-02 06:07:42 -03:00
2014-08-03 23:40:23 -03:00
struct tda18212_dev {
2014-08-03 23:05:31 -03:00
struct tda18212_config cfg ;
struct i2c_client * client ;
2014-08-04 01:00:46 -03:00
struct regmap * regmap ;
2011-11-13 11:20:08 -03:00
u32 if_frequency ;
2011-07-31 04:30:10 -03:00
} ;
2011-12-24 12:24:33 -03:00
static int tda18212_set_params ( struct dvb_frontend * fe )
2011-04-09 20:07:30 -03:00
{
2014-08-03 23:40:23 -03:00
struct tda18212_dev * dev = fe - > tuner_priv ;
2011-04-09 20:07:30 -03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
int ret , i ;
u32 if_khz ;
u8 buf [ 9 ] ;
2011-08-12 18:29:10 -03: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 16:27:38 -03:00
# define ATSC_VSB 8
# define ATSC_QAM 9
2011-04-09 20:07:30 -03:00
static const u8 bw_params [ ] [ 3 ] = {
2011-08-12 18:29:10 -03: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 16:27:38 -03:00
[ ATSC_VSB ] = { 0x7d , 0x20 , 0x63 } ,
[ ATSC_QAM ] = { 0x7d , 0x20 , 0x63 } ,
2011-04-09 20:07:30 -03:00
} ;
2014-08-03 23:40:23 -03:00
dev_dbg ( & dev - > client - > dev ,
2014-08-03 23:26:27 -03:00
" delivery_system=%d frequency=%d bandwidth_hz=%d \n " ,
c - > delivery_system , c - > frequency ,
2012-08-09 20:50:36 -03:00
c - > bandwidth_hz ) ;
2011-04-09 20:07:30 -03:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
switch ( c - > delivery_system ) {
2014-03-03 16:27:38 -03:00
case SYS_ATSC :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_atsc_vsb ;
2014-03-03 16:27:38 -03:00
i = ATSC_VSB ;
break ;
case SYS_DVBC_ANNEX_B :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_atsc_qam ;
2014-03-03 16:27:38 -03:00
i = ATSC_QAM ;
break ;
2011-04-09 20:07:30 -03:00
case SYS_DVBT :
switch ( c - > bandwidth_hz ) {
case 6000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt_6 ;
2011-08-12 18:29:10 -03:00
i = DVBT_6 ;
2011-04-09 20:07:30 -03:00
break ;
case 7000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt_7 ;
2011-08-12 18:29:10 -03:00
i = DVBT_7 ;
2011-04-09 20:07:30 -03:00
break ;
case 8000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt_8 ;
2011-08-12 18:29:10 -03:00
i = DVBT_8 ;
break ;
default :
ret = - EINVAL ;
goto error ;
}
break ;
case SYS_DVBT2 :
switch ( c - > bandwidth_hz ) {
case 6000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt2_6 ;
2011-08-12 18:29:10 -03:00
i = DVBT2_6 ;
break ;
case 7000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt2_7 ;
2011-08-12 18:29:10 -03:00
i = DVBT2_7 ;
break ;
case 8000000 :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbt2_8 ;
2011-08-12 18:29:10 -03:00
i = DVBT2_8 ;
2011-04-09 20:07:30 -03:00
break ;
default :
ret = - EINVAL ;
goto error ;
}
break ;
2011-12-22 18:11:39 -03:00
case SYS_DVBC_ANNEX_A :
case SYS_DVBC_ANNEX_C :
2014-08-03 23:40:23 -03:00
if_khz = dev - > cfg . if_dvbc ;
2011-08-12 18:29:10 -03:00
i = DVBC_8 ;
2011-04-09 20:07:30 -03:00
break ;
default :
ret = - EINVAL ;
goto error ;
}
2014-08-04 01:00:46 -03:00
ret = regmap_write ( dev - > regmap , 0x23 , bw_params [ i ] [ 2 ] ) ;
2011-04-09 20:07:30 -03:00
if ( ret )
goto error ;
2014-08-04 01:00:46 -03:00
ret = regmap_write ( dev - > regmap , 0x06 , 0x00 ) ;
2011-04-09 20:07:30 -03:00
if ( ret )
goto error ;
2014-08-04 01:00:46 -03:00
ret = regmap_write ( dev - > regmap , 0x0f , bw_params [ i ] [ 0 ] ) ;
2011-04-09 20:07:30 -03:00
if ( ret )
goto error ;
buf [ 0 ] = 0x02 ;
buf [ 1 ] = bw_params [ i ] [ 1 ] ;
buf [ 2 ] = 0x03 ; /* default value */
2011-11-13 11:22:58 -03:00
buf [ 3 ] = DIV_ROUND_CLOSEST ( if_khz , 50 ) ;
2011-04-09 20:07:30 -03: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 ;
2014-08-04 01:00:46 -03:00
ret = regmap_bulk_write ( dev - > regmap , 0x12 , buf , sizeof ( buf ) ) ;
2011-04-09 20:07:30 -03:00
if ( ret )
goto error ;
2011-11-13 11:20:08 -03:00
/* actual IF rounded as it is on register */
2014-08-03 23:40:23 -03:00
dev - > if_frequency = buf [ 3 ] * 50 * 1000 ;
2011-11-13 11:20:08 -03:00
2011-04-09 20:07:30 -03:00
exit :
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
return ret ;
error :
2014-08-03 23:40:23 -03:00
dev_dbg ( & dev - > client - > dev , " failed=%d \n " , ret ) ;
2011-04-09 20:07:30 -03:00
goto exit ;
}
2011-11-13 11:20:08 -03:00
static int tda18212_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
2014-08-03 23:40:23 -03:00
struct tda18212_dev * dev = fe - > tuner_priv ;
2011-11-13 11:20:08 -03:00
2014-08-03 23:40:23 -03:00
* frequency = dev - > if_frequency ;
2011-11-13 11:20:08 -03:00
return 0 ;
}
2011-04-09 20:07:30 -03:00
static const struct dvb_tuner_ops tda18212_tuner_ops = {
. info = {
2018-07-05 18:59:35 -04:00
. name = " NXP TDA18212 " ,
2011-04-09 20:07:30 -03:00
2018-07-05 18:59:35 -04:00
. frequency_min_hz = 48 * MHz ,
. frequency_max_hz = 864 * MHz ,
. frequency_step_hz = 1 * kHz ,
2011-04-09 20:07:30 -03:00
} ,
. set_params = tda18212_set_params ,
2011-11-13 11:20:08 -03:00
. get_if_frequency = tda18212_get_if_frequency ,
2011-04-09 20:07:30 -03:00
} ;
2014-08-03 23:05:31 -03:00
static int tda18212_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2011-04-09 20:07:30 -03:00
{
2014-08-03 23:05:31 -03:00
struct tda18212_config * cfg = client - > dev . platform_data ;
struct dvb_frontend * fe = cfg - > fe ;
2014-08-03 23:40:23 -03:00
struct tda18212_dev * dev ;
2011-04-09 20:07:30 -03:00
int ret ;
2014-08-04 01:00:46 -03:00
unsigned int chip_id ;
2014-07-31 16:35:56 -03:00
char * version ;
2014-08-04 01:00:46 -03:00
static const struct regmap_config regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2011-04-09 20:07:30 -03:00
2014-08-03 23:40:23 -03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
2014-08-03 23:05:31 -03:00
ret = - ENOMEM ;
2014-08-03 23:26:27 -03:00
dev_err ( & client - > dev , " kzalloc() failed \n " ) ;
2014-08-03 23:05:31 -03:00
goto err ;
}
2011-04-09 20:07:30 -03:00
2014-08-03 23:40:23 -03:00
memcpy ( & dev - > cfg , cfg , sizeof ( struct tda18212_config ) ) ;
dev - > client = client ;
2014-08-04 01:00:46 -03:00
dev - > regmap = devm_regmap_init_i2c ( client , & regmap_config ) ;
if ( IS_ERR ( dev - > regmap ) ) {
ret = PTR_ERR ( dev - > regmap ) ;
goto err ;
}
2011-04-09 20:07:30 -03:00
2014-08-03 23:05:31 -03:00
/* check if the tuner is there */
2011-04-09 20:07:30 -03:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ; /* open I2C-gate */
2014-08-04 01:00:46 -03:00
ret = regmap_read ( dev - > regmap , 0x00 , & chip_id ) ;
2014-08-03 23:40:23 -03:00
dev_dbg ( & dev - > client - > dev , " chip_id=%02x \n " , chip_id ) ;
2011-04-09 20:07:30 -03:00
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 0 ) ; /* close I2C-gate */
2014-07-31 16:35:56 -03:00
if ( ret )
goto err ;
switch ( chip_id ) {
case 0xc7 :
version = " M " ; /* master */
break ;
case 0x47 :
version = " S " ; /* slave */
break ;
default :
2014-08-03 23:05:31 -03:00
ret = - ENODEV ;
2014-07-31 16:35:56 -03:00
goto err ;
2011-04-09 20:07:30 -03:00
}
2014-08-03 23:40:23 -03:00
dev_info ( & dev - > client - > dev ,
2014-08-03 23:26:27 -03:00
" NXP TDA18212HN/%s successfully identified \n " , version ) ;
2011-04-09 20:07:30 -03:00
2014-08-03 23:40:23 -03:00
fe - > tuner_priv = dev ;
2011-04-09 20:07:30 -03:00
memcpy ( & fe - > ops . tuner_ops , & tda18212_tuner_ops ,
2014-08-03 23:05:31 -03:00
sizeof ( struct dvb_tuner_ops ) ) ;
2014-08-03 23:40:23 -03:00
i2c_set_clientdata ( client , dev ) ;
2011-04-09 20:07:30 -03:00
2014-08-03 23:05:31 -03:00
return 0 ;
2014-07-31 16:35:56 -03:00
err :
2014-08-03 23:26:27 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-08-03 23:40:23 -03:00
kfree ( dev ) ;
2014-08-03 23:05:31 -03:00
return ret ;
2011-04-09 20:07:30 -03:00
}
2014-08-03 23:05:31 -03:00
static int tda18212_remove ( struct i2c_client * client )
{
2014-08-03 23:40:23 -03:00
struct tda18212_dev * dev = i2c_get_clientdata ( client ) ;
struct dvb_frontend * fe = dev - > cfg . fe ;
2014-08-03 23:05:31 -03:00
2014-08-03 23:26:27 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-08-03 23:05:31 -03:00
memset ( & fe - > ops . tuner_ops , 0 , sizeof ( struct dvb_tuner_ops ) ) ;
fe - > tuner_priv = NULL ;
2014-08-03 23:40:23 -03:00
kfree ( dev ) ;
2014-08-03 23:05:31 -03:00
return 0 ;
}
static const struct i2c_device_id tda18212_id [ ] = {
{ " tda18212 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tda18212_id ) ;
static struct i2c_driver tda18212_driver = {
. driver = {
. name = " tda18212 " ,
} ,
. probe = tda18212_probe ,
. remove = tda18212_remove ,
. id_table = tda18212_id ,
} ;
module_i2c_driver ( tda18212_driver ) ;
2011-04-09 20:07:30 -03:00
MODULE_DESCRIPTION ( " NXP TDA18212HN silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;