2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-01-07 16:37:30 +04:00
/*
2014-08-31 06:52:48 +04:00
* ITE IT913X silicon tuner driver
2013-01-07 16:37:30 +04:00
*
* Copyright ( C ) 2011 Malcolm Priestley ( tvboxspy @ gmail . com )
* IT9137 Copyright ( C ) ITE Tech Inc .
*/
2014-08-28 09:07:08 +04:00
# include "it913x.h"
2016-11-07 03:00:45 +03:00
# include <linux/platform_device.h>
2014-08-28 09:07:08 +04:00
# include <linux/regmap.h>
2013-01-07 16:37:30 +04:00
2014-08-27 03:11:08 +04:00
struct it913x_dev {
2016-11-07 03:00:45 +03:00
struct platform_device * pdev ;
2014-08-27 04:45:33 +04:00
struct regmap * regmap ;
2014-08-27 00:14:16 +04:00
struct dvb_frontend * fe ;
2014-08-27 10:59:27 +04:00
u8 chip_ver : 2 ;
u8 role : 2 ;
2014-08-31 06:52:48 +04:00
u16 xtal ;
u8 fdiv ;
u8 clk_mode ;
u32 fn_min ;
bool active ;
2013-01-07 16:37:30 +04:00
} ;
2013-02-27 03:57:01 +04:00
static int it913x_init ( struct dvb_frontend * fe )
2013-01-07 16:37:30 +04:00
{
2014-08-27 03:11:08 +04:00
struct it913x_dev * dev = fe - > tuner_priv ;
2016-11-07 03:00:45 +03:00
struct platform_device * pdev = dev - > pdev ;
2014-08-31 07:29:33 +04:00
int ret ;
2014-08-31 06:52:48 +04:00
unsigned int utmp ;
u8 iqik_m_cal , nv_val , buf [ 2 ] ;
static const u8 nv [ ] = { 48 , 32 , 24 , 16 , 12 , 8 , 6 , 4 , 2 } ;
2014-08-31 07:29:33 +04:00
unsigned long timeout ;
2013-01-07 16:37:30 +04:00
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " role %u \n " , dev - > role ) ;
2014-08-31 06:52:48 +04:00
ret = regmap_write ( dev - > regmap , 0x80ec4c , 0x68 ) ;
if ( ret )
goto err ;
usleep_range ( 10000 , 100000 ) ;
ret = regmap_read ( dev - > regmap , 0x80ec86 , & utmp ) ;
if ( ret )
goto err ;
switch ( utmp ) {
2013-01-07 16:37:30 +04:00
case 0 :
2014-08-31 06:52:48 +04:00
/* 12.000 MHz */
dev - > clk_mode = utmp ;
dev - > xtal = 2000 ;
dev - > fdiv = 3 ;
iqik_m_cal = 16 ;
2013-01-07 16:37:30 +04:00
break ;
case 1 :
2014-08-31 06:52:48 +04:00
/* 20.480 MHz */
dev - > clk_mode = utmp ;
dev - > xtal = 640 ;
dev - > fdiv = 1 ;
iqik_m_cal = 6 ;
2013-01-07 16:37:30 +04:00
break ;
2014-08-31 06:52:48 +04:00
default :
2016-11-07 03:00:45 +03:00
dev_err ( & pdev - > dev , " unknown clock identifier %d \n " , utmp ) ;
2021-01-20 11:39:20 +03:00
ret = - EINVAL ;
2014-08-31 06:52:48 +04:00
goto err ;
2013-01-07 16:37:30 +04:00
}
2014-08-31 06:52:48 +04:00
ret = regmap_read ( dev - > regmap , 0x80ed03 , & utmp ) ;
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
else if ( utmp < ARRAY_SIZE ( nv ) )
nv_val = nv [ utmp ] ;
2013-01-07 16:37:30 +04:00
else
nv_val = 2 ;
2014-08-31 07:29:33 +04:00
# define TIMEOUT 50
timeout = jiffies + msecs_to_jiffies ( TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
2014-08-31 06:52:48 +04:00
ret = regmap_bulk_read ( dev - > regmap , 0x80ed23 , buf , 2 ) ;
2014-08-27 04:45:33 +04:00
if ( ret )
2014-08-31 06:52:48 +04:00
goto err ;
utmp = ( buf [ 1 ] < < 8 ) | ( buf [ 0 ] < < 0 ) ;
if ( utmp )
break ;
2013-01-07 16:37:30 +04:00
}
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " r_fbc_m_bdry took %u ms, val %u \n " ,
2014-08-31 07:29:33 +04:00
jiffies_to_msecs ( jiffies ) -
( jiffies_to_msecs ( timeout ) - TIMEOUT ) , utmp ) ;
2014-08-31 06:52:48 +04:00
dev - > fn_min = dev - > xtal * utmp ;
dev - > fn_min / = ( dev - > fdiv * nv_val ) ;
dev - > fn_min * = 1000 ;
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " fn_min %u \n " , dev - > fn_min ) ;
2014-08-31 06:52:48 +04:00
2014-08-31 07:29:33 +04:00
/*
* Chip version BX never sets that flag so we just wait 50 ms in that
* case . It is possible poll BX similarly than AX and then timeout in
* order to get 50 ms delay , but that causes about 120 extra I2C
* messages . As for now , we just wait and reduce IO .
*/
2014-08-31 06:52:48 +04:00
if ( dev - > chip_ver = = 1 ) {
2014-08-31 07:29:33 +04:00
# define TIMEOUT 50
timeout = jiffies + msecs_to_jiffies ( TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
2014-08-31 06:52:48 +04:00
ret = regmap_read ( dev - > regmap , 0x80ec82 , & utmp ) ;
if ( ret )
goto err ;
if ( utmp )
2013-01-07 16:37:30 +04:00
break ;
}
2014-08-31 06:52:48 +04:00
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " p_tsm_init_mode took %u ms, val %u \n " ,
2014-08-31 07:29:33 +04:00
jiffies_to_msecs ( jiffies ) -
( jiffies_to_msecs ( timeout ) - TIMEOUT ) , utmp ) ;
2014-08-31 06:52:48 +04:00
} else {
msleep ( 50 ) ;
2013-01-07 16:37:30 +04:00
}
2014-08-31 06:52:48 +04:00
ret = regmap_write ( dev - > regmap , 0x80ed81 , iqik_m_cal ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap , 0x80ec57 , 0x00 ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap , 0x80ec58 , 0x00 ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap , 0x80ec40 , 0x01 ) ;
if ( ret )
goto err ;
dev - > active = true ;
2013-02-28 06:29:02 +04:00
2014-08-31 06:52:48 +04:00
return 0 ;
err :
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " failed %d \n " , ret ) ;
2014-08-31 06:52:48 +04:00
return ret ;
2013-01-07 16:37:30 +04:00
}
2014-08-27 10:59:27 +04:00
static int it913x_sleep ( struct dvb_frontend * fe )
{
struct it913x_dev * dev = fe - > tuner_priv ;
2016-11-07 03:00:45 +03:00
struct platform_device * pdev = dev - > pdev ;
2014-08-27 10:59:27 +04:00
int ret , len ;
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " role %u \n " , dev - > role ) ;
2014-08-31 06:52:48 +04:00
dev - > active = false ;
2014-08-27 10:59:27 +04:00
ret = regmap_bulk_write ( dev - > regmap , 0x80ec40 , " \x00 " , 1 ) ;
if ( ret )
goto err ;
/*
* Writing ' 0x00 ' to master tuner register ' 0x80ec08 ' causes slave tuner
* communication lost . Due to that , we cannot put master full sleep .
*/
if ( dev - > role = = IT913X_ROLE_DUAL_MASTER )
len = 4 ;
else
len = 15 ;
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " role %u, len %d \n " , dev - > role , len ) ;
2014-08-27 10:59:27 +04:00
ret = regmap_bulk_write ( dev - > regmap , 0x80ec02 ,
" \x3f \x1f \x3f \x3e \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " ,
len ) ;
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap , 0x80ec12 , " \x00 \x00 \x00 \x00 " , 4 ) ;
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap , 0x80ec17 ,
" \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 9 ) ;
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap , 0x80ec22 ,
" \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 10 ) ;
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap , 0x80ec20 , " \x00 " , 1 ) ;
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap , 0x80ec3f , " \x01 " , 1 ) ;
if ( ret )
goto err ;
return 0 ;
err :
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " failed %d \n " , ret ) ;
2014-08-27 10:59:27 +04:00
return ret ;
}
2014-08-31 06:52:48 +04:00
static int it913x_set_params ( struct dvb_frontend * fe )
2013-01-07 16:37:30 +04:00
{
2014-08-27 03:11:08 +04:00
struct it913x_dev * dev = fe - > tuner_priv ;
2016-11-07 03:00:45 +03:00
struct platform_device * pdev = dev - > pdev ;
2014-08-31 06:52:48 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2014-08-27 01:56:46 +04:00
int ret ;
2014-08-31 06:52:48 +04:00
unsigned int utmp ;
u32 pre_lo_freq , t_cal_freq ;
u16 iqik_m_cal , n_div ;
u8 u8tmp , n , l_band , lna_band ;
2014-08-28 09:07:08 +04:00
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " role=%u, frequency %u, bandwidth_hz %u \n " ,
2014-08-31 06:52:48 +04:00
dev - > role , c - > frequency , c - > bandwidth_hz ) ;
2014-08-28 09:07:08 +04:00
2014-08-31 06:52:48 +04:00
if ( ! dev - > active ) {
ret = - EINVAL ;
2014-08-28 09:07:08 +04:00
goto err ;
2014-08-31 06:52:48 +04:00
}
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
if ( c - > frequency < = 74000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 48 ;
n = 0 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 111000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 32 ;
n = 1 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 148000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 24 ;
n = 2 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 222000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 16 ;
n = 3 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 296000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 12 ;
n = 4 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 445000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 8 ;
n = 5 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = dev - > fn_min ) {
2013-01-07 16:37:30 +04:00
n_div = 6 ;
n = 6 ;
2014-08-31 06:52:48 +04:00
} else if ( c - > frequency < = 950000000 ) {
2013-01-07 16:37:30 +04:00
n_div = 4 ;
n = 7 ;
2014-08-31 06:52:48 +04:00
} else {
2013-01-07 16:37:30 +04:00
n_div = 2 ;
n = 0 ;
2014-08-31 06:52:48 +04:00
}
ret = regmap_read ( dev - > regmap , 0x80ed81 , & utmp ) ;
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
iqik_m_cal = utmp * n_div ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
if ( utmp < 0x20 ) {
if ( dev - > clk_mode = = 0 )
2013-01-07 16:37:30 +04:00
iqik_m_cal = ( iqik_m_cal * 9 ) > > 5 ;
else
iqik_m_cal > > = 1 ;
} else {
iqik_m_cal = 0x40 - iqik_m_cal ;
2014-08-31 06:52:48 +04:00
if ( dev - > clk_mode = = 0 )
2013-01-07 16:37:30 +04:00
iqik_m_cal = ~ ( ( iqik_m_cal * 9 ) > > 5 ) ;
else
iqik_m_cal = ~ ( iqik_m_cal > > 1 ) ;
}
2014-08-31 06:52:48 +04:00
t_cal_freq = ( c - > frequency / 1000 ) * n_div * dev - > fdiv ;
pre_lo_freq = t_cal_freq / dev - > xtal ;
utmp = pre_lo_freq * dev - > xtal ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
if ( ( t_cal_freq - utmp ) > = ( dev - > xtal > > 1 ) )
pre_lo_freq + + ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
pre_lo_freq + = ( u32 ) n < < 13 ;
2013-01-07 16:37:30 +04:00
/* Frequency OMEGA_IQIK_M_CAL_MID*/
2014-08-31 06:52:48 +04:00
t_cal_freq = pre_lo_freq + ( u32 ) iqik_m_cal ;
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " t_cal_freq %u, pre_lo_freq %u \n " ,
2014-08-31 06:52:48 +04:00
t_cal_freq , pre_lo_freq ) ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
if ( c - > frequency < = 440000000 ) {
l_band = 0 ;
lna_band = 0 ;
} else if ( c - > frequency < = 484000000 ) {
l_band = 1 ;
lna_band = 1 ;
} else if ( c - > frequency < = 533000000 ) {
l_band = 1 ;
lna_band = 2 ;
} else if ( c - > frequency < = 587000000 ) {
l_band = 1 ;
lna_band = 3 ;
} else if ( c - > frequency < = 645000000 ) {
l_band = 1 ;
lna_band = 4 ;
} else if ( c - > frequency < = 710000000 ) {
l_band = 1 ;
lna_band = 5 ;
} else if ( c - > frequency < = 782000000 ) {
l_band = 1 ;
lna_band = 6 ;
} else if ( c - > frequency < = 860000000 ) {
l_band = 1 ;
lna_band = 7 ;
} else if ( c - > frequency < = 1492000000 ) {
l_band = 1 ;
lna_band = 0 ;
} else if ( c - > frequency < = 1685000000 ) {
l_band = 1 ;
lna_band = 1 ;
} else {
ret = - EINVAL ;
goto err ;
}
/* XXX: latest windows driver does not set that at all */
ret = regmap_write ( dev - > regmap , 0x80ee06 , lna_band ) ;
2014-08-28 09:07:08 +04:00
if ( ret )
goto err ;
2014-08-31 06:52:48 +04:00
if ( c - > bandwidth_hz < = 5000000 )
u8tmp = 0 ;
else if ( c - > bandwidth_hz < = 6000000 )
u8tmp = 2 ;
else if ( c - > bandwidth_hz < = 7000000 )
u8tmp = 4 ;
else
u8tmp = 6 ; /* 8000000 */
ret = regmap_write ( dev - > regmap , 0x80ec56 , u8tmp ) ;
if ( ret )
goto err ;
/* XXX: latest windows driver sets different value (a8 != 68) */
ret = regmap_write ( dev - > regmap , 0x80ec4c , 0xa0 | ( l_band < < 3 ) ) ;
2014-08-28 09:07:08 +04:00
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
ret = regmap_write ( dev - > regmap , 0x80ec4d , ( t_cal_freq > > 0 ) & 0xff ) ;
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
ret = regmap_write ( dev - > regmap , 0x80ec4e , ( t_cal_freq > > 8 ) & 0xff ) ;
2014-08-28 09:07:08 +04:00
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
ret = regmap_write ( dev - > regmap , 0x80011e , ( pre_lo_freq > > 0 ) & 0xff ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap , 0x80011f , ( pre_lo_freq > > 8 ) & 0xff ) ;
2014-08-28 09:07:08 +04:00
if ( ret )
goto err ;
2013-01-07 16:37:30 +04:00
2014-08-28 09:07:08 +04:00
return 0 ;
err :
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " failed %d \n " , ret ) ;
2014-08-28 09:07:08 +04:00
return ret ;
2013-01-07 16:37:30 +04:00
}
static const struct dvb_tuner_ops it913x_tuner_ops = {
. info = {
2018-07-06 01:59:35 +03:00
. name = " ITE IT913X " ,
. frequency_min_hz = 174 * MHz ,
. frequency_max_hz = 862 * MHz ,
2013-01-07 16:37:30 +04:00
} ,
2013-02-27 03:57:01 +04:00
. init = it913x_init ,
. sleep = it913x_sleep ,
2014-08-31 06:52:48 +04:00
. set_params = it913x_set_params ,
2013-01-07 16:37:30 +04:00
} ;
2016-11-07 03:00:45 +03:00
static int it913x_probe ( struct platform_device * pdev )
2013-01-07 16:37:30 +04:00
{
2016-11-07 03:00:45 +03:00
struct it913x_platform_data * pdata = pdev - > dev . platform_data ;
struct dvb_frontend * fe = pdata - > fe ;
2014-08-27 03:11:08 +04:00
struct it913x_dev * dev ;
2016-11-10 06:24:50 +03:00
const struct platform_device_id * id = platform_get_device_id ( pdev ) ;
2014-08-05 07:14:47 +04:00
int ret ;
2014-08-27 00:14:16 +04:00
char * chip_ver_str ;
2013-01-07 16:37:30 +04:00
2014-08-27 03:11:08 +04:00
dev = kzalloc ( sizeof ( struct it913x_dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
2014-08-27 00:14:16 +04:00
ret = - ENOMEM ;
2016-11-07 03:00:45 +03:00
dev_err ( & pdev - > dev , " kzalloc() failed \n " ) ;
2014-08-27 00:14:16 +04:00
goto err ;
2013-01-07 16:37:30 +04:00
}
2016-11-07 03:00:45 +03:00
dev - > pdev = pdev ;
dev - > regmap = pdata - > regmap ;
dev - > fe = pdata - > fe ;
2016-11-10 06:24:50 +03:00
dev - > chip_ver = id - > driver_data ;
2016-11-07 03:00:45 +03:00
dev - > role = pdata - > role ;
2013-02-28 07:14:06 +04:00
2014-08-27 03:11:08 +04:00
fe - > tuner_priv = dev ;
2013-01-07 16:37:30 +04:00
memcpy ( & fe - > ops . tuner_ops , & it913x_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
2016-11-07 03:00:45 +03:00
platform_set_drvdata ( pdev , dev ) ;
2014-08-27 00:14:16 +04:00
2014-08-27 03:11:08 +04:00
if ( dev - > chip_ver = = 1 )
2014-08-27 00:14:16 +04:00
chip_ver_str = " AX " ;
2014-08-27 03:11:08 +04:00
else if ( dev - > chip_ver = = 2 )
2014-08-27 00:14:16 +04:00
chip_ver_str = " BX " ;
else
chip_ver_str = " ?? " ;
2016-11-07 03:00:45 +03:00
dev_info ( & pdev - > dev , " ITE IT913X %s successfully attached \n " ,
chip_ver_str ) ;
dev_dbg ( & pdev - > dev , " chip_ver %u, role %u \n " , dev - > chip_ver , dev - > role ) ;
2014-08-27 00:14:16 +04:00
return 0 ;
err :
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " failed %d \n " , ret ) ;
2014-08-27 00:14:16 +04:00
return ret ;
}
2013-01-07 16:37:30 +04:00
2023-03-26 17:32:24 +03:00
static void it913x_remove ( struct platform_device * pdev )
2014-08-27 00:14:16 +04:00
{
2016-11-07 03:00:45 +03:00
struct it913x_dev * dev = platform_get_drvdata ( pdev ) ;
2014-08-27 03:11:08 +04:00
struct dvb_frontend * fe = dev - > fe ;
2014-08-27 00:14:16 +04:00
2016-11-07 03:00:45 +03:00
dev_dbg ( & pdev - > dev , " \n " ) ;
2014-08-27 00:14:16 +04:00
memset ( & fe - > ops . tuner_ops , 0 , sizeof ( struct dvb_tuner_ops ) ) ;
fe - > tuner_priv = NULL ;
2014-08-27 03:11:08 +04:00
kfree ( dev ) ;
2013-01-07 16:37:30 +04:00
}
2014-08-27 00:14:16 +04:00
2016-11-10 06:24:50 +03:00
static const struct platform_device_id it913x_id_table [ ] = {
{ " it9133ax-tuner " , 1 } ,
{ " it9133bx-tuner " , 2 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , it913x_id_table ) ;
2016-11-07 03:00:45 +03:00
static struct platform_driver it913x_driver = {
2014-08-27 00:14:16 +04:00
. driver = {
. name = " it913x " ,
2016-06-30 02:40:23 +03:00
. suppress_bind_attrs = true ,
2014-08-27 00:14:16 +04:00
} ,
. probe = it913x_probe ,
2023-03-26 17:32:24 +03:00
. remove_new = it913x_remove ,
2016-11-10 06:24:50 +03:00
. id_table = it913x_id_table ,
2014-08-27 00:14:16 +04:00
} ;
2016-11-07 03:00:45 +03:00
module_platform_driver ( it913x_driver ) ;
2013-01-07 16:37:30 +04:00
2014-08-31 06:52:48 +04:00
MODULE_DESCRIPTION ( " ITE IT913X silicon tuner driver " ) ;
2013-01-07 16:37:30 +04:00
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;