2018-03-27 13:08:21 -04:00
// SPDX-License-Identifier: GPL-2.0
2014-09-08 14:20:40 -03:00
/*
* MaxLinear MxL301RF OFDM tuner driver
*
* Copyright ( C ) 2014 Akihiro Tsukada < tskd08 @ gmail . com >
*/
/*
* NOTICE :
* This driver is incomplete and lacks init / config of the chips ,
* as the necessary info is not disclosed .
* Other features like get_if_frequency ( ) are missing as well .
* It assumes that users of this driver ( such as a PCI bridge of
* DTV receiver cards ) properly init and configure the chip
* via I2C * before * calling this driver ' s init ( ) function .
*
* Currently , PT3 driver is the only one that uses this driver ,
* and contains init / config code in its firmware .
* Thus some part of the code might be dependent on PT3 specific config .
*/
# include <linux/kernel.h>
# include "mxl301rf.h"
struct mxl301rf_state {
struct mxl301rf_config cfg ;
struct i2c_client * i2c ;
} ;
static struct mxl301rf_state * cfg_to_state ( struct mxl301rf_config * c )
{
return container_of ( c , struct mxl301rf_state , cfg ) ;
}
static int raw_write ( struct mxl301rf_state * state , const u8 * buf , int len )
{
int ret ;
ret = i2c_master_send ( state - > i2c , buf , len ) ;
if ( ret > = 0 & & ret < len )
ret = - EIO ;
return ( ret = = len ) ? 0 : ret ;
}
static int reg_write ( struct mxl301rf_state * state , u8 reg , u8 val )
{
u8 buf [ 2 ] = { reg , val } ;
return raw_write ( state , buf , 2 ) ;
}
static int reg_read ( struct mxl301rf_state * state , u8 reg , u8 * val )
{
u8 wbuf [ 2 ] = { 0xfb , reg } ;
int ret ;
ret = raw_write ( state , wbuf , sizeof ( wbuf ) ) ;
if ( ret = = 0 )
ret = i2c_master_recv ( state - > i2c , val , 1 ) ;
if ( ret > = 0 & & ret < 1 )
ret = - EIO ;
return ( ret = = 1 ) ? 0 : ret ;
}
/* tuner_ops */
/* get RSSI and update propery cache, set to *out in % */
static int mxl301rf_get_rf_strength ( struct dvb_frontend * fe , u16 * out )
{
struct mxl301rf_state * state ;
int ret ;
u8 rf_in1 , rf_in2 , rf_off1 , rf_off2 ;
u16 rf_in , rf_off ;
s64 level ;
struct dtv_fe_stats * rssi ;
rssi = & fe - > dtv_property_cache . strength ;
rssi - > len = 1 ;
rssi - > stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
* out = 0 ;
state = fe - > tuner_priv ;
ret = reg_write ( state , 0x14 , 0x01 ) ;
if ( ret < 0 )
return ret ;
usleep_range ( 1000 , 2000 ) ;
ret = reg_read ( state , 0x18 , & rf_in1 ) ;
if ( ret = = 0 )
ret = reg_read ( state , 0x19 , & rf_in2 ) ;
if ( ret = = 0 )
ret = reg_read ( state , 0xd6 , & rf_off1 ) ;
if ( ret = = 0 )
ret = reg_read ( state , 0xd7 , & rf_off2 ) ;
if ( ret ! = 0 )
return ret ;
rf_in = ( rf_in2 & 0x07 ) < < 8 | rf_in1 ;
rf_off = ( rf_off2 & 0x0f ) < < 5 | ( rf_off1 > > 3 ) ;
level = rf_in - rf_off - ( 113 < < 3 ) ; /* x8 dBm */
level = level * 1000 / 8 ;
rssi - > stat [ 0 ] . svalue = level ;
rssi - > stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
/* *out = (level - min) * 100 / (max - min) */
* out = ( rf_in - rf_off + ( 1 < < 9 ) - 1 ) * 100 / ( ( 5 < < 9 ) - 2 ) ;
return 0 ;
}
/* spur shift parameters */
struct shf {
u32 freq ; /* Channel center frequency */
u32 ofst_th ; /* Offset frequency threshold */
u8 shf_val ; /* Spur shift value */
u8 shf_dir ; /* Spur shift direction */
} ;
static const struct shf shf_tab [ ] = {
{ 64500 , 500 , 0x92 , 0x07 } ,
{ 191500 , 300 , 0xe2 , 0x07 } ,
{ 205500 , 500 , 0x2c , 0x04 } ,
{ 212500 , 500 , 0x1e , 0x04 } ,
{ 226500 , 500 , 0xd4 , 0x07 } ,
{ 99143 , 500 , 0x9c , 0x07 } ,
{ 173143 , 500 , 0xd4 , 0x07 } ,
{ 191143 , 300 , 0xd4 , 0x07 } ,
{ 207143 , 500 , 0xce , 0x07 } ,
{ 225143 , 500 , 0xce , 0x07 } ,
{ 243143 , 500 , 0xd4 , 0x07 } ,
{ 261143 , 500 , 0xd4 , 0x07 } ,
{ 291143 , 500 , 0xd4 , 0x07 } ,
{ 339143 , 500 , 0x2c , 0x04 } ,
{ 117143 , 500 , 0x7a , 0x07 } ,
{ 135143 , 300 , 0x7a , 0x07 } ,
{ 153143 , 500 , 0x01 , 0x07 }
} ;
struct reg_val {
u8 reg ;
u8 val ;
} __attribute__ ( ( __packed__ ) ) ;
static const struct reg_val set_idac [ ] = {
{ 0x0d , 0x00 } ,
{ 0x0c , 0x67 } ,
{ 0x6f , 0x89 } ,
{ 0x70 , 0x0c } ,
{ 0x6f , 0x8a } ,
{ 0x70 , 0x0e } ,
{ 0x6f , 0x8b } ,
{ 0x70 , 0x1c } ,
} ;
static int mxl301rf_set_params ( struct dvb_frontend * fe )
{
struct reg_val tune0 [ ] = {
{ 0x13 , 0x00 } , /* abort tuning */
{ 0x3b , 0xc0 } ,
{ 0x3b , 0x80 } ,
{ 0x10 , 0x95 } , /* BW */
{ 0x1a , 0x05 } ,
{ 0x61 , 0x00 } , /* spur shift value (placeholder) */
{ 0x62 , 0xa0 } /* spur shift direction (placeholder) */
} ;
struct reg_val tune1 [ ] = {
{ 0x11 , 0x40 } , /* RF frequency L (placeholder) */
{ 0x12 , 0x0e } , /* RF frequency H (placeholder) */
{ 0x13 , 0x01 } /* start tune */
} ;
struct mxl301rf_state * state ;
u32 freq ;
u16 f ;
u32 tmp , div ;
int i , ret ;
state = fe - > tuner_priv ;
freq = fe - > dtv_property_cache . frequency ;
/* spur shift function (for analog) */
for ( i = 0 ; i < ARRAY_SIZE ( shf_tab ) ; i + + ) {
if ( freq > = ( shf_tab [ i ] . freq - shf_tab [ i ] . ofst_th ) * 1000 & &
freq < = ( shf_tab [ i ] . freq + shf_tab [ i ] . ofst_th ) * 1000 ) {
tune0 [ 5 ] . val = shf_tab [ i ] . shf_val ;
tune0 [ 6 ] . val = 0xa0 | shf_tab [ i ] . shf_dir ;
break ;
}
}
ret = raw_write ( state , ( u8 * ) tune0 , sizeof ( tune0 ) ) ;
if ( ret < 0 )
goto failed ;
usleep_range ( 3000 , 4000 ) ;
/* convert freq to 10.6 fixed point float [MHz] */
f = freq / 1000000 ;
tmp = freq % 1000000 ;
div = 1000000 ;
for ( i = 0 ; i < 6 ; i + + ) {
f < < = 1 ;
div > > = 1 ;
if ( tmp > div ) {
tmp - = div ;
f | = 1 ;
}
}
if ( tmp > 7812 )
f + + ;
tune1 [ 0 ] . val = f & 0xff ;
tune1 [ 1 ] . val = f > > 8 ;
ret = raw_write ( state , ( u8 * ) tune1 , sizeof ( tune1 ) ) ;
if ( ret < 0 )
goto failed ;
msleep ( 31 ) ;
ret = reg_write ( state , 0x1a , 0x0d ) ;
if ( ret < 0 )
goto failed ;
ret = raw_write ( state , ( u8 * ) set_idac , sizeof ( set_idac ) ) ;
if ( ret < 0 )
goto failed ;
return 0 ;
failed :
dev_warn ( & state - > i2c - > dev , " (%s) failed. [adap%d-fe%d] \n " ,
__func__ , fe - > dvb - > num , fe - > id ) ;
return ret ;
}
static const struct reg_val standby_data [ ] = {
{ 0x01 , 0x00 } ,
{ 0x13 , 0x00 }
} ;
static int mxl301rf_sleep ( struct dvb_frontend * fe )
{
struct mxl301rf_state * state ;
int ret ;
state = fe - > tuner_priv ;
ret = raw_write ( state , ( u8 * ) standby_data , sizeof ( standby_data ) ) ;
if ( ret < 0 )
dev_warn ( & state - > i2c - > dev , " (%s) failed. [adap%d-fe%d] \n " ,
__func__ , fe - > dvb - > num , fe - > id ) ;
return ret ;
}
/* init sequence is not public.
* the parent must have init ' ed the device .
* just wake up here .
*/
static int mxl301rf_init ( struct dvb_frontend * fe )
{
struct mxl301rf_state * state ;
int ret ;
state = fe - > tuner_priv ;
ret = reg_write ( state , 0x01 , 0x01 ) ;
if ( ret < 0 ) {
dev_warn ( & state - > i2c - > dev , " (%s) failed. [adap%d-fe%d] \n " ,
__func__ , fe - > dvb - > num , fe - > id ) ;
return ret ;
}
return 0 ;
}
/* I2C driver functions */
static const struct dvb_tuner_ops mxl301rf_ops = {
. info = {
. name = " MaxLinear MxL301RF " ,
2018-07-05 18:59:35 -04:00
. frequency_min_hz = 93 * MHz ,
. frequency_max_hz = 803 * MHz + 142857 ,
2014-09-08 14:20:40 -03:00
} ,
. init = mxl301rf_init ,
. sleep = mxl301rf_sleep ,
. set_params = mxl301rf_set_params ,
. get_rf_strength = mxl301rf_get_rf_strength ,
} ;
2022-11-18 23:42:18 +01:00
static int mxl301rf_probe ( struct i2c_client * client )
2014-09-08 14:20:40 -03:00
{
struct mxl301rf_state * state ;
struct mxl301rf_config * cfg ;
struct dvb_frontend * fe ;
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return - ENOMEM ;
state - > i2c = client ;
cfg = client - > dev . platform_data ;
memcpy ( & state - > cfg , cfg , sizeof ( state - > cfg ) ) ;
fe = cfg - > fe ;
fe - > tuner_priv = state ;
memcpy ( & fe - > ops . tuner_ops , & mxl301rf_ops , sizeof ( mxl301rf_ops ) ) ;
i2c_set_clientdata ( client , & state - > cfg ) ;
dev_info ( & client - > dev , " MaxLinear MxL301RF attached. \n " ) ;
return 0 ;
}
2022-08-15 10:02:30 +02:00
static void mxl301rf_remove ( struct i2c_client * client )
2014-09-08 14:20:40 -03:00
{
struct mxl301rf_state * state ;
state = cfg_to_state ( i2c_get_clientdata ( client ) ) ;
state - > cfg . fe - > tuner_priv = NULL ;
kfree ( state ) ;
}
static const struct i2c_device_id mxl301rf_id [ ] = {
{ " mxl301rf " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mxl301rf_id ) ;
static struct i2c_driver mxl301rf_driver = {
. driver = {
. name = " mxl301rf " ,
} ,
2023-05-14 14:04:07 +02:00
. probe = mxl301rf_probe ,
2014-09-08 14:20:40 -03:00
. remove = mxl301rf_remove ,
. id_table = mxl301rf_id ,
} ;
module_i2c_driver ( mxl301rf_driver ) ;
MODULE_DESCRIPTION ( " MaxLinear MXL301RF tuner " ) ;
MODULE_AUTHOR ( " Akihiro TSUKADA " ) ;
MODULE_LICENSE ( " GPL " ) ;