2013-02-25 15:43:32 +04:00
/*
* Montage M88TS2022 silicon tuner driver
*
* Copyright ( C ) 2013 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 .
*
* Some calculations are taken from existing TS2020 driver .
*/
# include "m88ts2022_priv.h"
/* write multiple registers */
static int m88ts2022_wr_regs ( struct m88ts2022_priv * priv ,
u8 reg , const u8 * val , int len )
{
2013-11-08 00:01:31 +04:00
# define MAX_WR_LEN 3
# define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
2013-02-25 15:43:32 +04:00
int ret ;
2013-11-08 00:01:31 +04:00
u8 buf [ MAX_WR_XFER_LEN ] ;
2013-02-25 15:43:32 +04:00
struct i2c_msg msg [ 1 ] = {
{
2013-12-03 01:38:41 +04:00
. addr = priv - > client - > addr ,
2013-02-25 15:43:32 +04:00
. flags = 0 ,
2013-11-08 00:01:31 +04:00
. len = 1 + len ,
2013-02-25 15:43:32 +04:00
. buf = buf ,
}
} ;
2013-11-08 00:01:31 +04:00
if ( WARN_ON ( len > MAX_WR_LEN ) )
return - EINVAL ;
2013-02-25 15:43:32 +04:00
buf [ 0 ] = reg ;
memcpy ( & buf [ 1 ] , val , len ) ;
2013-12-03 01:38:41 +04:00
ret = i2c_transfer ( priv - > client - > adapter , msg , 1 ) ;
2013-02-25 15:43:32 +04:00
if ( ret = = 1 ) {
ret = 0 ;
} else {
2013-12-03 01:38:41 +04:00
dev_warn ( & priv - > client - > dev ,
2013-02-25 15:43:32 +04:00
" %s: i2c wr failed=%d reg=%02x len=%d \n " ,
KBUILD_MODNAME , ret , reg , len ) ;
ret = - EREMOTEIO ;
}
return ret ;
}
/* read multiple registers */
static int m88ts2022_rd_regs ( struct m88ts2022_priv * priv , u8 reg ,
u8 * val , int len )
{
2013-11-08 00:01:31 +04:00
# define MAX_RD_LEN 1
# define MAX_RD_XFER_LEN (MAX_RD_LEN)
2013-02-25 15:43:32 +04:00
int ret ;
2013-11-08 00:01:31 +04:00
u8 buf [ MAX_RD_XFER_LEN ] ;
2013-02-25 15:43:32 +04:00
struct i2c_msg msg [ 2 ] = {
{
2013-12-03 01:38:41 +04:00
. addr = priv - > client - > addr ,
2013-02-25 15:43:32 +04:00
. flags = 0 ,
. len = 1 ,
. buf = & reg ,
} , {
2013-12-03 01:38:41 +04:00
. addr = priv - > client - > addr ,
2013-02-25 15:43:32 +04:00
. flags = I2C_M_RD ,
2013-11-08 00:01:31 +04:00
. len = len ,
2013-02-25 15:43:32 +04:00
. buf = buf ,
}
} ;
2013-11-08 00:01:31 +04:00
if ( WARN_ON ( len > MAX_RD_LEN ) )
return - EINVAL ;
2013-12-03 01:38:41 +04:00
ret = i2c_transfer ( priv - > client - > adapter , msg , 2 ) ;
2013-02-25 15:43:32 +04:00
if ( ret = = 2 ) {
memcpy ( val , buf , len ) ;
ret = 0 ;
} else {
2013-12-03 01:38:41 +04:00
dev_warn ( & priv - > client - > dev ,
2013-02-25 15:43:32 +04:00
" %s: i2c rd failed=%d reg=%02x len=%d \n " ,
KBUILD_MODNAME , ret , reg , len ) ;
ret = - EREMOTEIO ;
}
return ret ;
}
/* write single register */
static int m88ts2022_wr_reg ( struct m88ts2022_priv * priv , u8 reg , u8 val )
{
return m88ts2022_wr_regs ( priv , reg , & val , 1 ) ;
}
/* read single register */
static int m88ts2022_rd_reg ( struct m88ts2022_priv * priv , u8 reg , u8 * val )
{
return m88ts2022_rd_regs ( priv , reg , val , 1 ) ;
}
/* write single register with mask */
static int m88ts2022_wr_reg_mask ( struct m88ts2022_priv * priv ,
u8 reg , u8 val , u8 mask )
{
int ret ;
u8 u8tmp ;
/* no need for read if whole reg is written */
if ( mask ! = 0xff ) {
ret = m88ts2022_rd_regs ( priv , reg , & u8tmp , 1 ) ;
if ( ret )
return ret ;
val & = mask ;
u8tmp & = ~ mask ;
val | = u8tmp ;
}
return m88ts2022_wr_regs ( priv , reg , & val , 1 ) ;
}
static int m88ts2022_cmd ( struct dvb_frontend * fe ,
int op , int sleep , u8 reg , u8 mask , u8 val , u8 * reg_val )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
int ret , i ;
u8 u8tmp ;
struct m88ts2022_reg_val reg_vals [ ] = {
{ 0x51 , 0x1f - op } ,
{ 0x51 , 0x1f } ,
{ 0x50 , 0x00 + op } ,
{ 0x50 , 0x00 } ,
} ;
for ( i = 0 ; i < 2 ; i + + ) {
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev ,
2013-02-25 15:43:32 +04:00
" %s: i=%d op=%02x reg=%02x mask=%02x val=%02x \n " ,
__func__ , i , op , reg , mask , val ) ;
for ( i = 0 ; i < ARRAY_SIZE ( reg_vals ) ; i + + ) {
ret = m88ts2022_wr_reg ( priv , reg_vals [ i ] . reg ,
reg_vals [ i ] . val ) ;
if ( ret )
goto err ;
}
usleep_range ( sleep * 1000 , sleep * 10000 ) ;
ret = m88ts2022_rd_reg ( priv , reg , & u8tmp ) ;
if ( ret )
goto err ;
if ( ( u8tmp & mask ) ! = val )
break ;
}
if ( reg_val )
* reg_val = u8tmp ;
err :
return ret ;
}
static int m88ts2022_set_params ( struct dvb_frontend * fe )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2013-12-02 08:39:24 +04:00
int ret ;
unsigned int frequency_khz , frequency_offset_khz , f_3db_hz ;
unsigned int f_ref_khz , f_vco_khz , div_ref , div_out , pll_n , gdiv28 ;
u8 buf [ 3 ] , u8tmp , cap_code , lpf_gm , lpf_mxdiv , div_max , div_min ;
u16 u16tmp ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev ,
2013-02-25 15:43:32 +04:00
" %s: frequency=%d symbol_rate=%d rolloff=%d \n " ,
__func__ , c - > frequency , c - > symbol_rate , c - > rolloff ) ;
2013-12-02 08:39:24 +04:00
/*
* Integer - N PLL synthesizer
* kHz is used for all calculations to keep calculations within 32 - bit
*/
2013-12-03 01:38:41 +04:00
f_ref_khz = DIV_ROUND_CLOSEST ( priv - > cfg . clock , 1000 ) ;
2013-12-02 08:39:24 +04:00
div_ref = DIV_ROUND_CLOSEST ( f_ref_khz , 2000 ) ;
2013-02-25 15:43:32 +04:00
if ( c - > symbol_rate < 5000000 )
2013-12-02 08:39:24 +04:00
frequency_offset_khz = 3000 ; /* 3 MHz */
2013-02-25 15:43:32 +04:00
else
2013-12-02 08:39:24 +04:00
frequency_offset_khz = 0 ;
2013-02-25 15:43:32 +04:00
2013-12-02 08:39:24 +04:00
frequency_khz = c - > frequency + frequency_offset_khz ;
2013-02-25 15:43:32 +04:00
if ( frequency_khz < 1103000 ) {
2013-12-02 08:39:24 +04:00
div_out = 4 ;
2013-02-25 15:43:32 +04:00
u8tmp = 0x1b ;
} else {
2013-12-02 08:39:24 +04:00
div_out = 2 ;
2013-02-25 15:43:32 +04:00
u8tmp = 0x0b ;
}
buf [ 0 ] = u8tmp ;
buf [ 1 ] = 0x40 ;
ret = m88ts2022_wr_regs ( priv , 0x10 , buf , 2 ) ;
if ( ret )
goto err ;
2013-12-02 08:39:24 +04:00
f_vco_khz = frequency_khz * div_out ;
pll_n = f_vco_khz * div_ref / f_ref_khz ;
pll_n + = pll_n % 2 ;
priv - > frequency_khz = pll_n * f_ref_khz / div_ref / div_out ;
2013-02-25 15:43:32 +04:00
2013-12-02 08:39:24 +04:00
if ( pll_n < 4095 )
u16tmp = pll_n - 1024 ;
else if ( pll_n < 6143 )
u16tmp = pll_n + 1024 ;
2013-02-25 15:43:32 +04:00
else
2013-12-02 08:39:24 +04:00
u16tmp = pll_n + 3072 ;
2013-02-25 15:43:32 +04:00
2013-12-02 08:39:24 +04:00
buf [ 0 ] = ( u16tmp > > 8 ) & 0x3f ;
buf [ 1 ] = ( u16tmp > > 0 ) & 0xff ;
buf [ 2 ] = div_ref - 8 ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_wr_regs ( priv , 0x01 , buf , 3 ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev ,
2013-12-02 08:39:24 +04:00
" %s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u \n " ,
2013-02-25 15:43:32 +04:00
__func__ , priv - > frequency_khz ,
2013-12-02 08:39:24 +04:00
priv - > frequency_khz - c - > frequency , f_vco_khz , pll_n ,
div_ref , div_out ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_cmd ( fe , 0x10 , 5 , 0x15 , 0x40 , 0x00 , NULL ) ;
if ( ret )
goto err ;
ret = m88ts2022_rd_reg ( priv , 0x14 , & u8tmp ) ;
if ( ret )
goto err ;
u8tmp & = 0x7f ;
if ( u8tmp < 64 ) {
ret = m88ts2022_wr_reg_mask ( priv , 0x10 , 0x80 , 0x80 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x11 , 0x6f ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x10 , 5 , 0x15 , 0x40 , 0x00 , NULL ) ;
if ( ret )
goto err ;
}
ret = m88ts2022_rd_reg ( priv , 0x14 , & u8tmp ) ;
if ( ret )
goto err ;
u8tmp & = 0x1f ;
if ( u8tmp > 19 ) {
ret = m88ts2022_wr_reg_mask ( priv , 0x10 , 0x00 , 0x02 ) ;
if ( ret )
goto err ;
}
ret = m88ts2022_cmd ( fe , 0x08 , 5 , 0x3c , 0xff , 0x00 , NULL ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x25 , 0x00 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x27 , 0x70 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x41 , 0x09 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x08 , 0x0b ) ;
if ( ret )
goto err ;
2013-12-02 08:39:24 +04:00
/* filters */
gdiv28 = DIV_ROUND_CLOSEST ( f_ref_khz * 1694U , 1000000U ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_wr_reg ( priv , 0x04 , gdiv28 ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x04 , 2 , 0x26 , 0xff , 0x00 , & u8tmp ) ;
if ( ret )
goto err ;
cap_code = u8tmp & 0x3f ;
ret = m88ts2022_wr_reg ( priv , 0x41 , 0x0d ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x04 , 2 , 0x26 , 0xff , 0x00 , & u8tmp ) ;
if ( ret )
goto err ;
u8tmp & = 0x3f ;
cap_code = ( cap_code + u8tmp ) / 2 ;
gdiv28 = gdiv28 * 207 / ( cap_code * 2 + 151 ) ;
div_max = gdiv28 * 135 / 100 ;
div_min = gdiv28 * 78 / 100 ;
2013-12-02 08:39:24 +04:00
div_max = clamp_val ( div_max , 0U , 63U ) ;
2014-08-21 21:02:27 +04:00
f_3db_hz = mult_frac ( c - > symbol_rate , 135 , 200 ) ;
2013-12-02 08:39:24 +04:00
f_3db_hz + = 2000000U + ( frequency_offset_khz * 1000U ) ;
f_3db_hz = clamp ( f_3db_hz , 7000000U , 40000000U ) ;
2013-02-25 15:43:32 +04:00
2013-12-02 08:39:24 +04:00
# define LPF_COEFF 3200U
lpf_gm = DIV_ROUND_CLOSEST ( f_3db_hz * gdiv28 , LPF_COEFF * f_ref_khz ) ;
lpf_gm = clamp_val ( lpf_gm , 1U , 23U ) ;
lpf_mxdiv = DIV_ROUND_CLOSEST ( lpf_gm * LPF_COEFF * f_ref_khz , f_3db_hz ) ;
if ( lpf_mxdiv < div_min )
lpf_mxdiv = DIV_ROUND_CLOSEST ( + + lpf_gm * LPF_COEFF * f_ref_khz , f_3db_hz ) ;
lpf_mxdiv = clamp_val ( lpf_mxdiv , 0U , div_max ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_wr_reg ( priv , 0x04 , lpf_mxdiv ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x06 , lpf_gm ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x04 , 2 , 0x26 , 0xff , 0x00 , & u8tmp ) ;
if ( ret )
goto err ;
cap_code = u8tmp & 0x3f ;
ret = m88ts2022_wr_reg ( priv , 0x41 , 0x09 ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x04 , 2 , 0x26 , 0xff , 0x00 , & u8tmp ) ;
if ( ret )
goto err ;
u8tmp & = 0x3f ;
cap_code = ( cap_code + u8tmp ) / 2 ;
u8tmp = cap_code | 0x80 ;
ret = m88ts2022_wr_reg ( priv , 0x25 , u8tmp ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x27 , 0x30 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x08 , 0x09 ) ;
if ( ret )
goto err ;
ret = m88ts2022_cmd ( fe , 0x01 , 20 , 0x21 , 0xff , 0x00 , NULL ) ;
if ( ret )
goto err ;
err :
if ( ret )
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2013-02-25 15:43:32 +04:00
return ret ;
}
static int m88ts2022_init ( struct dvb_frontend * fe )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
int ret , i ;
u8 u8tmp ;
static const struct m88ts2022_reg_val reg_vals [ ] = {
{ 0x7d , 0x9d } ,
{ 0x7c , 0x9a } ,
{ 0x7a , 0x76 } ,
{ 0x3b , 0x01 } ,
{ 0x63 , 0x88 } ,
{ 0x61 , 0x85 } ,
{ 0x22 , 0x30 } ,
{ 0x30 , 0x40 } ,
{ 0x20 , 0x23 } ,
{ 0x24 , 0x02 } ,
{ 0x12 , 0xa0 } ,
} ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: \n " , __func__ ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x01 ) ;
if ( ret )
goto err ;
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x03 ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
switch ( priv - > cfg . clock_out ) {
2013-02-25 15:43:32 +04:00
case M88TS2022_CLOCK_OUT_DISABLED :
u8tmp = 0x60 ;
break ;
case M88TS2022_CLOCK_OUT_ENABLED :
u8tmp = 0x70 ;
2013-12-03 01:38:41 +04:00
ret = m88ts2022_wr_reg ( priv , 0x05 , priv - > cfg . clock_out_div ) ;
2013-02-25 15:43:32 +04:00
if ( ret )
goto err ;
break ;
case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT :
u8tmp = 0x6c ;
break ;
default :
goto err ;
}
ret = m88ts2022_wr_reg ( priv , 0x42 , u8tmp ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
if ( priv - > cfg . loop_through )
2013-02-25 15:43:32 +04:00
u8tmp = 0xec ;
else
u8tmp = 0x6c ;
ret = m88ts2022_wr_reg ( priv , 0x62 , u8tmp ) ;
if ( ret )
goto err ;
for ( i = 0 ; i < ARRAY_SIZE ( reg_vals ) ; i + + ) {
ret = m88ts2022_wr_reg ( priv , reg_vals [ i ] . reg , reg_vals [ i ] . val ) ;
if ( ret )
goto err ;
}
err :
if ( ret )
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2013-02-25 15:43:32 +04:00
return ret ;
}
static int m88ts2022_sleep ( struct dvb_frontend * fe )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
int ret ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: \n " , __func__ ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x00 ) ;
if ( ret )
goto err ;
err :
if ( ret )
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2013-02-25 15:43:32 +04:00
return ret ;
}
static int m88ts2022_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: \n " , __func__ ) ;
2013-02-25 15:43:32 +04:00
* frequency = priv - > frequency_khz ;
return 0 ;
}
static int m88ts2022_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: \n " , __func__ ) ;
2013-02-25 15:43:32 +04:00
* frequency = 0 ; /* Zero-IF */
return 0 ;
}
static int m88ts2022_get_rf_strength ( struct dvb_frontend * fe , u16 * strength )
{
struct m88ts2022_priv * priv = fe - > tuner_priv ;
int ret ;
2013-12-02 08:39:24 +04:00
u8 u8tmp ;
u16 gain , u16tmp ;
unsigned int gain1 , gain2 , gain3 ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_rd_reg ( priv , 0x3d , & u8tmp ) ;
if ( ret )
goto err ;
gain1 = ( u8tmp > > 0 ) & 0x1f ;
2013-12-02 08:39:24 +04:00
gain1 = clamp ( gain1 , 0U , 15U ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_rd_reg ( priv , 0x21 , & u8tmp ) ;
if ( ret )
goto err ;
gain2 = ( u8tmp > > 0 ) & 0x1f ;
2013-12-02 08:39:24 +04:00
gain2 = clamp ( gain2 , 2U , 16U ) ;
2013-02-25 15:43:32 +04:00
ret = m88ts2022_rd_reg ( priv , 0x66 , & u8tmp ) ;
if ( ret )
goto err ;
gain3 = ( u8tmp > > 3 ) & 0x07 ;
2013-12-02 08:39:24 +04:00
gain3 = clamp ( gain3 , 0U , 6U ) ;
2013-02-25 15:43:32 +04:00
gain = gain1 * 265 + gain2 * 338 + gain3 * 285 ;
/* scale value to 0x0000-0xffff */
u16tmp = ( 0xffff - gain ) ;
2013-12-02 08:39:24 +04:00
u16tmp = clamp_val ( u16tmp , 59000U , 61500U ) ;
2013-02-25 15:43:32 +04:00
* strength = ( u16tmp - 59000 ) * 0xffff / ( 61500 - 59000 ) ;
err :
if ( ret )
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2013-02-25 15:43:32 +04:00
return ret ;
}
static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
. info = {
. name = " Montage M88TS2022 " ,
. frequency_min = 950000 ,
. frequency_max = 2150000 ,
} ,
. init = m88ts2022_init ,
. sleep = m88ts2022_sleep ,
. set_params = m88ts2022_set_params ,
. get_frequency = m88ts2022_get_frequency ,
. get_if_frequency = m88ts2022_get_if_frequency ,
. get_rf_strength = m88ts2022_get_rf_strength ,
} ;
2013-12-03 01:38:41 +04:00
static int m88ts2022_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2013-02-25 15:43:32 +04:00
{
2013-12-03 01:38:41 +04:00
struct m88ts2022_config * cfg = client - > dev . platform_data ;
struct dvb_frontend * fe = cfg - > fe ;
2013-02-25 15:43:32 +04:00
struct m88ts2022_priv * priv ;
int ret ;
u8 chip_id , u8tmp ;
2013-12-18 19:46:45 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2013-02-25 15:43:32 +04:00
if ( ! priv ) {
ret = - ENOMEM ;
2013-12-03 01:38:41 +04:00
dev_err ( & client - > dev , " %s: kzalloc() failed \n " , KBUILD_MODNAME ) ;
2013-02-25 15:43:32 +04:00
goto err ;
}
2013-12-03 01:38:41 +04:00
memcpy ( & priv - > cfg , cfg , sizeof ( struct m88ts2022_config ) ) ;
priv - > client = client ;
2013-02-25 15:43:32 +04:00
/* check if the tuner is there */
ret = m88ts2022_rd_reg ( priv , 0x00 , & u8tmp ) ;
if ( ret )
goto err ;
if ( ( u8tmp & 0x03 ) = = 0x00 ) {
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x01 ) ;
if ( ret < 0 )
goto err ;
usleep_range ( 2000 , 50000 ) ;
}
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x03 ) ;
if ( ret )
goto err ;
usleep_range ( 2000 , 50000 ) ;
ret = m88ts2022_rd_reg ( priv , 0x00 , & chip_id ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
dev_dbg ( & priv - > client - > dev , " %s: chip_id=%02x \n " , __func__ , chip_id ) ;
2013-02-25 15:43:32 +04:00
switch ( chip_id ) {
case 0xc3 :
case 0x83 :
break ;
default :
goto err ;
}
2013-12-03 01:38:41 +04:00
switch ( priv - > cfg . clock_out ) {
2013-02-25 15:43:32 +04:00
case M88TS2022_CLOCK_OUT_DISABLED :
u8tmp = 0x60 ;
break ;
case M88TS2022_CLOCK_OUT_ENABLED :
u8tmp = 0x70 ;
2013-12-03 01:38:41 +04:00
ret = m88ts2022_wr_reg ( priv , 0x05 , priv - > cfg . clock_out_div ) ;
2013-02-25 15:43:32 +04:00
if ( ret )
goto err ;
break ;
case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT :
u8tmp = 0x6c ;
break ;
default :
goto err ;
}
ret = m88ts2022_wr_reg ( priv , 0x42 , u8tmp ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
if ( priv - > cfg . loop_through )
2013-02-25 15:43:32 +04:00
u8tmp = 0xec ;
else
u8tmp = 0x6c ;
ret = m88ts2022_wr_reg ( priv , 0x62 , u8tmp ) ;
if ( ret )
goto err ;
/* sleep */
ret = m88ts2022_wr_reg ( priv , 0x00 , 0x00 ) ;
if ( ret )
goto err ;
2013-12-03 01:38:41 +04:00
dev_info ( & priv - > client - > dev ,
2013-02-25 15:43:32 +04:00
" %s: Montage M88TS2022 successfully identified \n " ,
KBUILD_MODNAME ) ;
fe - > tuner_priv = priv ;
memcpy ( & fe - > ops . tuner_ops , & m88ts2022_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
2013-12-03 01:38:41 +04:00
i2c_set_clientdata ( client , priv ) ;
return 0 ;
2013-02-25 15:43:32 +04:00
err :
2013-12-03 01:38:41 +04:00
dev_dbg ( & client - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
kfree ( priv ) ;
return ret ;
}
static int m88ts2022_remove ( struct i2c_client * client )
{
struct m88ts2022_priv * priv = i2c_get_clientdata ( client ) ;
struct dvb_frontend * fe = priv - > cfg . fe ;
2014-08-21 23:22:02 +04:00
2013-12-03 01:38:41 +04:00
dev_dbg ( & client - > dev , " %s: \n " , __func__ ) ;
2013-02-25 15:43:32 +04:00
2013-12-03 01:38:41 +04:00
memset ( & fe - > ops . tuner_ops , 0 , sizeof ( struct dvb_tuner_ops ) ) ;
fe - > tuner_priv = NULL ;
kfree ( priv ) ;
return 0 ;
2013-02-25 15:43:32 +04:00
}
2013-12-03 01:38:41 +04:00
static const struct i2c_device_id m88ts2022_id [ ] = {
{ " m88ts2022 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , m88ts2022_id ) ;
static struct i2c_driver m88ts2022_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " m88ts2022 " ,
} ,
. probe = m88ts2022_probe ,
. remove = m88ts2022_remove ,
. id_table = m88ts2022_id ,
} ;
module_i2c_driver ( m88ts2022_driver ) ;
2013-02-25 15:43:32 +04:00
MODULE_DESCRIPTION ( " Montage M88TS2022 silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;