2012-09-09 05:07:24 +04:00
/*
* FCI FC2580 silicon tuner driver
*
* Copyright ( C ) 2012 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 .
*/
# include "fc2580_priv.h"
/*
* TODO :
* I2C write and read works only for one single register . Multiple registers
* could not be accessed using normal register address auto - increment .
* There could be ( very likely ) register to change that behavior . . . .
*/
2012-11-06 23:48:41 +04:00
/* write single register conditionally only when value differs from 0xff
* XXX : This is special routine meant only for writing fc2580_freq_regs_lut [ ]
* values . Do not use for the other purposes . */
2015-04-15 18:35:47 +03:00
static int fc2580_wr_reg_ff ( struct fc2580_dev * dev , u8 reg , u8 val )
2012-11-06 23:48:41 +04:00
{
if ( val = = 0xff )
return 0 ;
else
2015-04-15 19:51:29 +03:00
return regmap_write ( dev - > regmap , reg , val ) ;
2012-11-06 23:48:41 +04:00
}
2015-05-04 03:42:02 +03:00
static int fc2580_set_params ( struct fc2580_dev * dev )
2012-09-09 05:07:24 +04:00
{
2015-04-15 18:35:47 +03:00
struct i2c_client * client = dev - > client ;
2015-04-15 18:10:54 +03:00
int ret , i ;
unsigned int uitmp , div_ref , div_ref_val , div_n , k , k_cw , div_out ;
2012-09-09 05:07:24 +04:00
u64 f_vco ;
2015-05-03 21:16:52 +03:00
u8 synth_config ;
2015-04-15 18:10:54 +03:00
unsigned long timeout ;
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
if ( ! dev - > active ) {
dev_dbg ( & client - > dev , " tuner is sleeping \n " ) ;
return 0 ;
}
2012-09-09 05:07:24 +04:00
2015-04-15 18:10:54 +03:00
/*
* Fractional - N synthesizer
*
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* v |
* Fref + - - - - + + - - - - + + - - - - - - - + + - - - - + + - - - - - - + + - - - +
* - - - - - - > | / R | - - > | PD | - - > | VCO | - - - - - - > | / 2 | - - > | / N . F | < - - | K |
* + - - - - + + - - - - + + - - - - - - - + + - - - - + + - - - - - - + + - - - +
* |
* |
* v
* + - - - - - - - + Fout
* | / Rout | - - - - - - >
* + - - - - - - - +
*/
2012-09-09 05:07:24 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_pll_lut ) ; i + + ) {
2015-05-04 03:42:02 +03:00
if ( dev - > f_frequency < = fc2580_pll_lut [ i ] . freq )
2012-09-09 05:07:24 +04:00
break ;
}
2015-04-15 18:10:54 +03:00
if ( i = = ARRAY_SIZE ( fc2580_pll_lut ) ) {
ret = - EINVAL ;
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:10:54 +03:00
}
2012-09-09 05:07:24 +04:00
2015-04-15 18:10:54 +03:00
# define DIV_PRE_N 2
2015-04-15 18:35:47 +03:00
# define F_REF dev->clk
2015-04-15 18:10:54 +03:00
div_out = fc2580_pll_lut [ i ] . div_out ;
2015-05-04 03:42:02 +03:00
f_vco = ( u64 ) dev - > f_frequency * div_out ;
2015-04-15 18:10:54 +03:00
synth_config = fc2580_pll_lut [ i ] . band ;
if ( f_vco < 2600000000ULL )
synth_config | = 0x06 ;
2012-09-09 05:07:24 +04:00
else
2015-04-15 18:10:54 +03:00
synth_config | = 0x0e ;
/* select reference divider R (keep PLL div N in valid range) */
# define DIV_N_MIN 76
if ( f_vco > = div_u64 ( ( u64 ) DIV_PRE_N * DIV_N_MIN * F_REF , 1 ) ) {
div_ref = 1 ;
div_ref_val = 0x00 ;
} else if ( f_vco > = div_u64 ( ( u64 ) DIV_PRE_N * DIV_N_MIN * F_REF , 2 ) ) {
div_ref = 2 ;
div_ref_val = 0x10 ;
2012-09-09 05:07:24 +04:00
} else {
2015-04-15 18:10:54 +03:00
div_ref = 4 ;
div_ref_val = 0x20 ;
2012-09-09 05:07:24 +04:00
}
2015-04-15 18:10:54 +03:00
/* calculate PLL integer and fractional control word */
uitmp = DIV_PRE_N * F_REF / div_ref ;
div_n = div_u64_rem ( f_vco , uitmp , & k ) ;
k_cw = div_u64 ( ( u64 ) k * 0x100000 , uitmp ) ;
dev_dbg ( & client - > dev ,
2015-05-04 03:42:02 +03:00
" frequency=%u bandwidth=%u f_vco=%llu F_REF=%u div_ref=%u div_n=%u k=%u div_out=%u k_cw=%0x \n " ,
dev - > f_frequency , dev - > f_bandwidth , f_vco , F_REF , div_ref ,
div_n , k , div_out , k_cw ) ;
2012-09-09 05:07:24 +04:00
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x02 , synth_config ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x18 , div_ref_val < < 0 | k_cw > > 16 ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x1a , ( k_cw > > 8 ) & 0xff ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x1b , ( k_cw > > 0 ) & 0xff ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x1c , div_n ) ;
if ( ret )
2015-04-15 18:10:54 +03:00
goto err ;
2012-09-09 05:07:24 +04:00
/* registers */
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_freq_regs_lut ) ; i + + ) {
2015-05-04 03:42:02 +03:00
if ( dev - > f_frequency < = fc2580_freq_regs_lut [ i ] . freq )
2012-09-09 05:07:24 +04:00
break ;
}
2015-04-15 18:10:54 +03:00
if ( i = = ARRAY_SIZE ( fc2580_freq_regs_lut ) ) {
ret = - EINVAL ;
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:10:54 +03:00
}
2012-09-09 05:07:24 +04:00
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x25 , fc2580_freq_regs_lut [ i ] . r25_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x27 , fc2580_freq_regs_lut [ i ] . r27_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x28 , fc2580_freq_regs_lut [ i ] . r28_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x29 , fc2580_freq_regs_lut [ i ] . r29_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x2b , fc2580_freq_regs_lut [ i ] . r2b_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x2c , fc2580_freq_regs_lut [ i ] . r2c_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x2d , fc2580_freq_regs_lut [ i ] . r2d_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x30 , fc2580_freq_regs_lut [ i ] . r30_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x44 , fc2580_freq_regs_lut [ i ] . r44_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x50 , fc2580_freq_regs_lut [ i ] . r50_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x53 , fc2580_freq_regs_lut [ i ] . r53_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x5f , fc2580_freq_regs_lut [ i ] . r5f_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x61 , fc2580_freq_regs_lut [ i ] . r61_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x62 , fc2580_freq_regs_lut [ i ] . r62_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x63 , fc2580_freq_regs_lut [ i ] . r63_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x67 , fc2580_freq_regs_lut [ i ] . r67_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x68 , fc2580_freq_regs_lut [ i ] . r68_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x69 , fc2580_freq_regs_lut [ i ] . r69_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6a , fc2580_freq_regs_lut [ i ] . r6a_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6b , fc2580_freq_regs_lut [ i ] . r6b_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6c , fc2580_freq_regs_lut [ i ] . r6c_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6d , fc2580_freq_regs_lut [ i ] . r6d_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6e , fc2580_freq_regs_lut [ i ] . r6e_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:35:47 +03:00
ret = fc2580_wr_reg_ff ( dev , 0x6f , fc2580_freq_regs_lut [ i ] . r6f_val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
/* IF filters */
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_if_filter_lut ) ; i + + ) {
2015-05-04 03:42:02 +03:00
if ( dev - > f_bandwidth < = fc2580_if_filter_lut [ i ] . freq )
2012-09-09 05:07:24 +04:00
break ;
}
2015-04-15 18:10:54 +03:00
if ( i = = ARRAY_SIZE ( fc2580_if_filter_lut ) ) {
ret = - EINVAL ;
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:10:54 +03:00
}
2012-09-09 05:07:24 +04:00
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x36 , fc2580_if_filter_lut [ i ] . r36_val ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-05-04 03:42:02 +03:00
uitmp = ( unsigned int ) 8058000 - ( dev - > f_bandwidth * 122 / 100 / 2 ) ;
2015-05-03 21:16:52 +03:00
uitmp = div64_u64 ( ( u64 ) dev - > clk * uitmp , 1000000000000ULL ) ;
ret = regmap_write ( dev - > regmap , 0x37 , uitmp ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x39 , fc2580_if_filter_lut [ i ] . r39_val ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:10:54 +03:00
timeout = jiffies + msecs_to_jiffies ( 30 ) ;
for ( uitmp = ~ 0xc0 ; ! time_after ( jiffies , timeout ) & & uitmp ! = 0xc0 ; ) {
/* trigger filter */
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x2e , 0x09 ) ;
2015-04-15 18:10:54 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 18:10:54 +03:00
/* locked when [7:6] are set (val: d7 6MHz, d5 7MHz, cd 8MHz) */
2015-04-15 19:51:29 +03:00
ret = regmap_read ( dev - > regmap , 0x2f , & uitmp ) ;
2015-04-15 18:10:54 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
2015-04-15 19:51:29 +03:00
uitmp & = 0xc0 ;
2012-09-09 05:07:24 +04:00
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x2e , 0x01 ) ;
2015-04-15 18:10:54 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
}
2015-04-15 18:10:54 +03:00
if ( uitmp ! = 0xc0 )
dev_dbg ( & client - > dev , " filter did not lock %02x \n " , uitmp ) ;
2012-09-09 05:07:24 +04:00
return 0 ;
err :
2015-04-15 18:10:54 +03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2012-09-09 05:07:24 +04:00
return ret ;
}
2015-05-04 03:42:02 +03:00
static int fc2580_init ( struct fc2580_dev * dev )
2012-09-09 05:07:24 +04:00
{
2015-04-15 18:35:47 +03:00
struct i2c_client * client = dev - > client ;
2012-09-09 05:07:24 +04:00
int ret , i ;
2015-04-15 18:35:47 +03:00
dev_dbg ( & client - > dev , " \n " ) ;
2012-09-09 05:07:24 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( fc2580_init_reg_vals ) ; i + + ) {
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , fc2580_init_reg_vals [ i ] . reg ,
2012-09-09 05:07:24 +04:00
fc2580_init_reg_vals [ i ] . val ) ;
2015-04-15 19:51:29 +03:00
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
}
2015-05-04 03:42:02 +03:00
dev - > active = true ;
2012-09-09 05:07:24 +04:00
return 0 ;
err :
2015-04-15 18:35:47 +03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2012-09-09 05:07:24 +04:00
return ret ;
}
2015-05-04 03:42:02 +03:00
static int fc2580_sleep ( struct fc2580_dev * dev )
2012-09-09 05:07:24 +04:00
{
2015-04-15 18:35:47 +03:00
struct i2c_client * client = dev - > client ;
2012-09-09 05:07:24 +04:00
int ret ;
2015-04-15 18:35:47 +03:00
dev_dbg ( & client - > dev , " \n " ) ;
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
dev - > active = false ;
2015-04-15 19:51:29 +03:00
ret = regmap_write ( dev - > regmap , 0x02 , 0x0a ) ;
if ( ret )
2012-09-09 05:07:24 +04:00
goto err ;
return 0 ;
err :
2015-04-15 18:35:47 +03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2012-09-09 05:07:24 +04:00
return ret ;
}
2015-05-04 03:42:02 +03:00
/*
* DVB API
*/
static int fc2580_dvb_set_params ( struct dvb_frontend * fe )
2012-09-09 05:07:24 +04:00
{
2015-04-15 18:35:47 +03:00
struct fc2580_dev * dev = fe - > tuner_priv ;
2015-05-04 03:42:02 +03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
dev - > f_frequency = c - > frequency ;
dev - > f_bandwidth = c - > bandwidth_hz ;
return fc2580_set_params ( dev ) ;
}
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
static int fc2580_dvb_init ( struct dvb_frontend * fe )
{
return fc2580_init ( fe - > tuner_priv ) ;
}
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
static int fc2580_dvb_sleep ( struct dvb_frontend * fe )
{
return fc2580_sleep ( fe - > tuner_priv ) ;
}
static int fc2580_dvb_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
* frequency = 0 ; /* Zero-IF */
2012-09-09 05:07:24 +04:00
return 0 ;
}
2015-05-04 03:42:02 +03:00
static const struct dvb_tuner_ops fc2580_dvb_tuner_ops = {
2012-09-09 05:07:24 +04:00
. info = {
. name = " FCI FC2580 " ,
. frequency_min = 174000000 ,
. frequency_max = 862000000 ,
} ,
2015-05-04 03:42:02 +03:00
. init = fc2580_dvb_init ,
. sleep = fc2580_dvb_sleep ,
. set_params = fc2580_dvb_set_params ,
2012-09-09 05:07:24 +04:00
2015-05-04 03:42:02 +03:00
. get_if_frequency = fc2580_dvb_get_if_frequency ,
2012-09-09 05:07:24 +04:00
} ;
2015-05-04 03:42:02 +03:00
/*
* V4L2 API
*/
# if IS_ENABLED(CONFIG_VIDEO_V4L2)
static const struct v4l2_frequency_band bands [ ] = {
{
. type = V4L2_TUNER_RF ,
. index = 0 ,
. capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ,
. rangelow = 130000000 ,
. rangehigh = 2000000000 ,
} ,
} ;
static inline struct fc2580_dev * fc2580_subdev_to_dev ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct fc2580_dev , subdev ) ;
}
static int fc2580_s_power ( struct v4l2_subdev * sd , int on )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
int ret ;
dev_dbg ( & client - > dev , " on=%d \n " , on ) ;
if ( on )
ret = fc2580_init ( dev ) ;
else
ret = fc2580_sleep ( dev ) ;
if ( ret )
return ret ;
return fc2580_set_params ( dev ) ;
}
static const struct v4l2_subdev_core_ops fc2580_subdev_core_ops = {
. s_power = fc2580_s_power ,
} ;
static int fc2580_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * v )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
dev_dbg ( & client - > dev , " index=%d \n " , v - > index ) ;
strlcpy ( v - > name , " FCI FC2580 " , sizeof ( v - > name ) ) ;
v - > type = V4L2_TUNER_RF ;
v - > capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ;
v - > rangelow = bands [ 0 ] . rangelow ;
v - > rangehigh = bands [ 0 ] . rangehigh ;
return 0 ;
}
static int fc2580_s_tuner ( struct v4l2_subdev * sd , const struct v4l2_tuner * v )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
dev_dbg ( & client - > dev , " index=%d \n " , v - > index ) ;
return 0 ;
}
static int fc2580_g_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * f )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
dev_dbg ( & client - > dev , " tuner=%d \n " , f - > tuner ) ;
f - > frequency = dev - > f_frequency ;
return 0 ;
}
static int fc2580_s_frequency ( struct v4l2_subdev * sd ,
const struct v4l2_frequency * f )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
dev_dbg ( & client - > dev , " tuner=%d type=%d frequency=%u \n " ,
f - > tuner , f - > type , f - > frequency ) ;
dev - > f_frequency = clamp_t ( unsigned int , f - > frequency ,
bands [ 0 ] . rangelow , bands [ 0 ] . rangehigh ) ;
return fc2580_set_params ( dev ) ;
}
static int fc2580_enum_freq_bands ( struct v4l2_subdev * sd ,
struct v4l2_frequency_band * band )
{
struct fc2580_dev * dev = fc2580_subdev_to_dev ( sd ) ;
struct i2c_client * client = dev - > client ;
dev_dbg ( & client - > dev , " tuner=%d type=%d index=%d \n " ,
band - > tuner , band - > type , band - > index ) ;
if ( band - > index > = ARRAY_SIZE ( bands ) )
return - EINVAL ;
band - > capability = bands [ band - > index ] . capability ;
band - > rangelow = bands [ band - > index ] . rangelow ;
band - > rangehigh = bands [ band - > index ] . rangehigh ;
return 0 ;
}
static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = {
. g_tuner = fc2580_g_tuner ,
. s_tuner = fc2580_s_tuner ,
. g_frequency = fc2580_g_frequency ,
. s_frequency = fc2580_s_frequency ,
. enum_freq_bands = fc2580_enum_freq_bands ,
} ;
static const struct v4l2_subdev_ops fc2580_subdev_ops = {
. core = & fc2580_subdev_core_ops ,
. tuner = & fc2580_subdev_tuner_ops ,
} ;
static int fc2580_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct fc2580_dev * dev = container_of ( ctrl - > handler , struct fc2580_dev , hdl ) ;
struct i2c_client * client = dev - > client ;
int ret ;
dev_dbg ( & client - > dev , " ctrl: id=%d name=%s cur.val=%d val=%d \n " ,
ctrl - > id , ctrl - > name , ctrl - > cur . val , ctrl - > val ) ;
switch ( ctrl - > id ) {
case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO :
case V4L2_CID_RF_TUNER_BANDWIDTH :
/*
* TODO : Auto logic does not work 100 % correctly as tuner driver
* do not have information to calculate maximum suitable
* bandwidth . Calculating it is responsible of master driver .
*/
dev - > f_bandwidth = dev - > bandwidth - > val ;
ret = fc2580_set_params ( dev ) ;
break ;
default :
dev_dbg ( & client - > dev , " unknown ctrl " ) ;
ret = - EINVAL ;
}
return ret ;
}
static const struct v4l2_ctrl_ops fc2580_ctrl_ops = {
. s_ctrl = fc2580_s_ctrl ,
} ;
# endif
static struct v4l2_subdev * fc2580_get_v4l2_subdev ( struct i2c_client * client )
{
struct fc2580_dev * dev = i2c_get_clientdata ( client ) ;
if ( dev - > subdev . ops )
return & dev - > subdev ;
else
return NULL ;
}
2015-04-15 01:52:16 +03:00
static int fc2580_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
2015-04-15 18:35:47 +03:00
struct fc2580_dev * dev ;
2015-04-15 01:52:16 +03:00
struct fc2580_platform_data * pdata = client - > dev . platform_data ;
struct dvb_frontend * fe = pdata - > dvb_frontend ;
int ret ;
2015-04-15 19:51:29 +03:00
unsigned int uitmp ;
static const struct regmap_config regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2015-04-15 01:52:16 +03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
ret = - ENOMEM ;
goto err ;
}
if ( pdata - > clk )
dev - > clk = pdata - > clk ;
else
dev - > clk = 16384000 ; /* internal clock */
dev - > client = client ;
2015-04-15 19:51:29 +03:00
dev - > regmap = devm_regmap_init_i2c ( client , & regmap_config ) ;
if ( IS_ERR ( dev - > regmap ) ) {
ret = PTR_ERR ( dev - > regmap ) ;
goto err_kfree ;
}
2015-04-15 01:52:16 +03:00
/* check if the tuner is there */
2015-04-15 19:51:29 +03:00
ret = regmap_read ( dev - > regmap , 0x01 , & uitmp ) ;
if ( ret )
2015-04-15 01:52:16 +03:00
goto err_kfree ;
2015-04-15 19:51:29 +03:00
dev_dbg ( & client - > dev , " chip_id=%02x \n " , uitmp ) ;
2015-04-15 01:52:16 +03:00
2015-04-15 19:51:29 +03:00
switch ( uitmp ) {
2015-04-15 01:52:16 +03:00
case 0x56 :
case 0x5a :
break ;
default :
2015-06-06 14:15:34 +03:00
ret = - ENODEV ;
2015-04-15 01:52:16 +03:00
goto err_kfree ;
}
2015-05-04 03:42:02 +03:00
# if IS_ENABLED(CONFIG_VIDEO_V4L2)
/* Register controls */
v4l2_ctrl_handler_init ( & dev - > hdl , 2 ) ;
dev - > bandwidth_auto = v4l2_ctrl_new_std ( & dev - > hdl , & fc2580_ctrl_ops ,
V4L2_CID_RF_TUNER_BANDWIDTH_AUTO ,
0 , 1 , 1 , 1 ) ;
dev - > bandwidth = v4l2_ctrl_new_std ( & dev - > hdl , & fc2580_ctrl_ops ,
V4L2_CID_RF_TUNER_BANDWIDTH ,
3000 , 10000000 , 1 , 3000 ) ;
v4l2_ctrl_auto_cluster ( 2 , & dev - > bandwidth_auto , 0 , false ) ;
if ( dev - > hdl . error ) {
ret = dev - > hdl . error ;
dev_err ( & client - > dev , " Could not initialize controls \n " ) ;
v4l2_ctrl_handler_free ( & dev - > hdl ) ;
goto err_kfree ;
}
dev - > subdev . ctrl_handler = & dev - > hdl ;
dev - > f_frequency = bands [ 0 ] . rangelow ;
dev - > f_bandwidth = dev - > bandwidth - > val ;
v4l2_i2c_subdev_init ( & dev - > subdev , client , & fc2580_subdev_ops ) ;
# endif
2015-04-15 01:52:16 +03:00
fe - > tuner_priv = dev ;
2015-05-04 03:42:02 +03:00
memcpy ( & fe - > ops . tuner_ops , & fc2580_dvb_tuner_ops ,
sizeof ( fe - > ops . tuner_ops ) ) ;
pdata - > get_v4l2_subdev = fc2580_get_v4l2_subdev ;
2015-04-15 01:52:16 +03:00
i2c_set_clientdata ( client , dev ) ;
dev_info ( & client - > dev , " FCI FC2580 successfully identified \n " ) ;
return 0 ;
err_kfree :
kfree ( dev ) ;
err :
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
return ret ;
}
static int fc2580_remove ( struct i2c_client * client )
{
2015-04-15 18:35:47 +03:00
struct fc2580_dev * dev = i2c_get_clientdata ( client ) ;
2015-04-15 01:52:16 +03:00
dev_dbg ( & client - > dev , " \n " ) ;
2015-05-04 03:42:02 +03:00
# if IS_ENABLED(CONFIG_VIDEO_V4L2)
v4l2_ctrl_handler_free ( & dev - > hdl ) ;
# endif
2015-04-15 01:52:16 +03:00
kfree ( dev ) ;
return 0 ;
}
static const struct i2c_device_id fc2580_id_table [ ] = {
{ " fc2580 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , fc2580_id_table ) ;
static struct i2c_driver fc2580_driver = {
. driver = {
. name = " fc2580 " ,
. suppress_bind_attrs = true ,
} ,
. probe = fc2580_probe ,
. remove = fc2580_remove ,
. id_table = fc2580_id_table ,
} ;
module_i2c_driver ( fc2580_driver ) ;
2012-09-09 05:07:24 +04:00
MODULE_DESCRIPTION ( " FCI FC2580 silicon tuner driver " ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;