2014-02-02 23:34:23 -03:00
/*
* Mirics MSi001 silicon tuner driver
*
* Copyright ( C ) 2013 Antti Palosaari < crope @ iki . fi >
* Copyright ( C ) 2014 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 .
*/
# include <linux/module.h>
# include <linux/gcd.h>
# include <media/v4l2-device.h>
# include <media/v4l2-ctrls.h>
static const struct v4l2_frequency_band bands [ ] = {
{
. type = V4L2_TUNER_RF ,
. index = 0 ,
. capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ,
. rangelow = 49000000 ,
. rangehigh = 263000000 ,
} , {
. type = V4L2_TUNER_RF ,
. index = 1 ,
. capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ,
. rangelow = 390000000 ,
. rangehigh = 960000000 ,
} ,
} ;
struct msi001 {
struct spi_device * spi ;
struct v4l2_subdev sd ;
/* Controls */
struct v4l2_ctrl_handler hdl ;
struct v4l2_ctrl * bandwidth_auto ;
struct v4l2_ctrl * bandwidth ;
struct v4l2_ctrl * lna_gain ;
struct v4l2_ctrl * mixer_gain ;
struct v4l2_ctrl * if_gain ;
unsigned int f_tuner ;
} ;
static inline struct msi001 * sd_to_msi001 ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct msi001 , sd ) ;
}
static int msi001_wreg ( struct msi001 * s , u32 data )
{
/* Register format: 4 bits addr + 20 bits value */
return spi_write ( s - > spi , & data , 3 ) ;
} ;
static int msi001_set_gain ( struct msi001 * s , int lna_gain , int mixer_gain ,
int if_gain )
{
int ret ;
u32 reg ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " lna=%d mixer=%d if=%d \n " ,
2014-02-02 23:34:23 -03:00
lna_gain , mixer_gain , if_gain ) ;
reg = 1 < < 0 ;
reg | = ( 59 - if_gain ) < < 4 ;
reg | = 0 < < 10 ;
reg | = ( 1 - mixer_gain ) < < 12 ;
reg | = ( 1 - lna_gain ) < < 13 ;
reg | = 4 < < 14 ;
reg | = 0 < < 17 ;
ret = msi001_wreg ( s , reg ) ;
if ( ret )
goto err ;
return 0 ;
err :
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " failed %d \n " , ret ) ;
2014-02-02 23:34:23 -03:00
return ret ;
} ;
static int msi001_set_tuner ( struct msi001 * s )
{
int ret , i ;
unsigned int n , m , thresh , frac , vco_step , tmp , f_if1 ;
u32 reg ;
u64 f_vco , tmp64 ;
u8 mode , filter_mode , lo_div ;
2014-08-24 23:12:13 -03:00
2014-02-02 23:34:23 -03:00
static const struct {
u32 rf ;
u8 mode ;
u8 lo_div ;
} band_lut [ ] = {
{ 50000000 , 0xe1 , 16 } , /* AM_MODE2, antenna 2 */
{ 108000000 , 0x42 , 32 } , /* VHF_MODE */
{ 330000000 , 0x44 , 16 } , /* B3_MODE */
{ 960000000 , 0x48 , 4 } , /* B45_MODE */
{ ~ 0U , 0x50 , 2 } , /* BL_MODE */
} ;
static const struct {
u32 freq ;
u8 filter_mode ;
} if_freq_lut [ ] = {
{ 0 , 0x03 } , /* Zero IF */
{ 450000 , 0x02 } , /* 450 kHz IF */
{ 1620000 , 0x01 } , /* 1.62 MHz IF */
{ 2048000 , 0x00 } , /* 2.048 MHz IF */
} ;
static const struct {
u32 freq ;
u8 val ;
} bandwidth_lut [ ] = {
{ 200000 , 0x00 } , /* 200 kHz */
{ 300000 , 0x01 } , /* 300 kHz */
{ 600000 , 0x02 } , /* 600 kHz */
{ 1536000 , 0x03 } , /* 1.536 MHz */
{ 5000000 , 0x04 } , /* 5 MHz */
{ 6000000 , 0x05 } , /* 6 MHz */
{ 7000000 , 0x06 } , /* 7 MHz */
{ 8000000 , 0x07 } , /* 8 MHz */
} ;
unsigned int f_rf = s - > f_tuner ;
/*
* bandwidth ( Hz )
* 200000 , 300000 , 600000 , 1536000 , 5000000 , 6000000 , 7000000 , 8000000
*/
unsigned int bandwidth ;
/*
* intermediate frequency ( Hz )
* 0 , 450000 , 1620000 , 2048000
*/
unsigned int f_if = 0 ;
# define F_REF 24000000
# define R_REF 4
# define F_OUT_STEP 1
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " f_rf=%d f_if=%d \n " , f_rf , f_if ) ;
2014-02-02 23:34:23 -03:00
for ( i = 0 ; i < ARRAY_SIZE ( band_lut ) ; i + + ) {
if ( f_rf < = band_lut [ i ] . rf ) {
mode = band_lut [ i ] . mode ;
lo_div = band_lut [ i ] . lo_div ;
break ;
}
}
if ( i = = ARRAY_SIZE ( band_lut ) ) {
ret = - EINVAL ;
goto err ;
}
/* AM_MODE is upconverted */
if ( ( mode > > 0 ) & 0x1 )
f_if1 = 5 * F_REF ;
else
f_if1 = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( if_freq_lut ) ; i + + ) {
if ( f_if = = if_freq_lut [ i ] . freq ) {
filter_mode = if_freq_lut [ i ] . filter_mode ;
break ;
}
}
if ( i = = ARRAY_SIZE ( if_freq_lut ) ) {
ret = - EINVAL ;
goto err ;
}
/* filters */
bandwidth = s - > bandwidth - > val ;
bandwidth = clamp ( bandwidth , 200000U , 8000000U ) ;
for ( i = 0 ; i < ARRAY_SIZE ( bandwidth_lut ) ; i + + ) {
if ( bandwidth < = bandwidth_lut [ i ] . freq ) {
bandwidth = bandwidth_lut [ i ] . val ;
break ;
}
}
if ( i = = ARRAY_SIZE ( bandwidth_lut ) ) {
ret = - EINVAL ;
goto err ;
}
s - > bandwidth - > val = bandwidth_lut [ i ] . freq ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " bandwidth selected=%d \n " , bandwidth_lut [ i ] . freq ) ;
2014-02-02 23:34:23 -03:00
2014-04-05 17:23:41 -03:00
f_vco = ( u64 ) ( f_rf + f_if + f_if1 ) * lo_div ;
2014-02-02 23:34:23 -03:00
tmp64 = f_vco ;
m = do_div ( tmp64 , F_REF * R_REF ) ;
n = ( unsigned int ) tmp64 ;
vco_step = F_OUT_STEP * lo_div ;
thresh = ( F_REF * R_REF ) / vco_step ;
frac = 1ul * thresh * m / ( F_REF * R_REF ) ;
/* Find out greatest common divisor and divide to smaller. */
tmp = gcd ( thresh , frac ) ;
thresh / = tmp ;
frac / = tmp ;
/* Force divide to reg max. Resolution will be reduced. */
tmp = DIV_ROUND_UP ( thresh , 4095 ) ;
thresh = DIV_ROUND_CLOSEST ( thresh , tmp ) ;
frac = DIV_ROUND_CLOSEST ( frac , tmp ) ;
/* calc real RF set */
tmp = 1ul * F_REF * R_REF * n ;
tmp + = 1ul * F_REF * R_REF * frac / thresh ;
tmp / = lo_div ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " rf=%u:%u n=%d thresh=%d frac=%d \n " ,
f_rf , tmp , n , thresh , frac ) ;
2014-02-02 23:34:23 -03:00
ret = msi001_wreg ( s , 0x00000e ) ;
if ( ret )
goto err ;
ret = msi001_wreg ( s , 0x000003 ) ;
if ( ret )
goto err ;
reg = 0 < < 0 ;
reg | = mode < < 4 ;
reg | = filter_mode < < 12 ;
reg | = bandwidth < < 14 ;
reg | = 0x02 < < 17 ;
reg | = 0x00 < < 20 ;
ret = msi001_wreg ( s , reg ) ;
if ( ret )
goto err ;
reg = 5 < < 0 ;
reg | = thresh < < 4 ;
reg | = 1 < < 19 ;
reg | = 1 < < 21 ;
ret = msi001_wreg ( s , reg ) ;
if ( ret )
goto err ;
reg = 2 < < 0 ;
reg | = frac < < 4 ;
reg | = n < < 16 ;
ret = msi001_wreg ( s , reg ) ;
if ( ret )
goto err ;
ret = msi001_set_gain ( s , s - > lna_gain - > cur . val , s - > mixer_gain - > cur . val ,
s - > if_gain - > cur . val ) ;
if ( ret )
goto err ;
reg = 6 < < 0 ;
reg | = 63 < < 4 ;
reg | = 4095 < < 10 ;
ret = msi001_wreg ( s , reg ) ;
if ( ret )
goto err ;
return 0 ;
err :
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " failed %d \n " , ret ) ;
2014-02-02 23:34:23 -03:00
return ret ;
} ;
static int msi001_s_power ( struct v4l2_subdev * sd , int on )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
int ret ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " on=%d \n " , on ) ;
2014-02-02 23:34:23 -03:00
if ( on )
ret = 0 ;
else
ret = msi001_wreg ( s , 0x000000 ) ;
return ret ;
}
static const struct v4l2_subdev_core_ops msi001_core_ops = {
. s_power = msi001_s_power ,
} ;
static int msi001_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * v )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " index=%d \n " , v - > index ) ;
2014-02-02 23:34:23 -03:00
strlcpy ( v - > name , " Mirics MSi001 " , sizeof ( v - > name ) ) ;
v - > type = V4L2_TUNER_RF ;
v - > capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ;
v - > rangelow = 49000000 ;
v - > rangehigh = 960000000 ;
return 0 ;
}
static int msi001_s_tuner ( struct v4l2_subdev * sd , const struct v4l2_tuner * v )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " index=%d \n " , v - > index ) ;
2014-02-02 23:34:23 -03:00
return 0 ;
}
static int msi001_g_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * f )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " tuner=%d \n " , f - > tuner ) ;
2014-02-02 23:34:23 -03:00
f - > frequency = s - > f_tuner ;
return 0 ;
}
static int msi001_s_frequency ( struct v4l2_subdev * sd ,
const struct v4l2_frequency * f )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
unsigned int band ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " tuner=%d type=%d frequency=%u \n " ,
f - > tuner , f - > type , f - > frequency ) ;
2014-02-02 23:34:23 -03:00
if ( f - > frequency < ( ( bands [ 0 ] . rangehigh + bands [ 1 ] . rangelow ) / 2 ) )
band = 0 ;
else
band = 1 ;
s - > f_tuner = clamp_t ( unsigned int , f - > frequency ,
bands [ band ] . rangelow , bands [ band ] . rangehigh ) ;
return msi001_set_tuner ( s ) ;
}
static int msi001_enum_freq_bands ( struct v4l2_subdev * sd ,
struct v4l2_frequency_band * band )
{
struct msi001 * s = sd_to_msi001 ( sd ) ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " tuner=%d type=%d index=%d \n " ,
band - > tuner , band - > type , band - > index ) ;
2014-02-02 23:34:23 -03:00
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 msi001_tuner_ops = {
. g_tuner = msi001_g_tuner ,
. s_tuner = msi001_s_tuner ,
. g_frequency = msi001_g_frequency ,
. s_frequency = msi001_s_frequency ,
. enum_freq_bands = msi001_enum_freq_bands ,
} ;
static const struct v4l2_subdev_ops msi001_ops = {
. core = & msi001_core_ops ,
. tuner = & msi001_tuner_ops ,
} ;
static int msi001_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct msi001 * s = container_of ( ctrl - > handler , struct msi001 , hdl ) ;
int ret ;
2014-08-24 23:12:13 -03:00
2014-02-02 23:34:23 -03:00
dev_dbg ( & s - > spi - > dev ,
2014-08-24 23:12:13 -03:00
" id=%d name=%s val=%d min=%lld max=%lld step=%lld \n " ,
ctrl - > id , ctrl - > name , ctrl - > val ,
2014-02-02 23:34:23 -03:00
ctrl - > minimum , ctrl - > maximum , ctrl - > step ) ;
switch ( ctrl - > id ) {
case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO :
case V4L2_CID_RF_TUNER_BANDWIDTH :
ret = msi001_set_tuner ( s ) ;
break ;
case V4L2_CID_RF_TUNER_LNA_GAIN :
ret = msi001_set_gain ( s , s - > lna_gain - > val ,
s - > mixer_gain - > cur . val , s - > if_gain - > cur . val ) ;
break ;
case V4L2_CID_RF_TUNER_MIXER_GAIN :
ret = msi001_set_gain ( s , s - > lna_gain - > cur . val ,
s - > mixer_gain - > val , s - > if_gain - > cur . val ) ;
break ;
case V4L2_CID_RF_TUNER_IF_GAIN :
ret = msi001_set_gain ( s , s - > lna_gain - > cur . val ,
s - > mixer_gain - > cur . val , s - > if_gain - > val ) ;
break ;
default :
2014-08-24 23:12:13 -03:00
dev_dbg ( & s - > spi - > dev , " unkown control %d \n " , ctrl - > id ) ;
2014-02-02 23:34:23 -03:00
ret = - EINVAL ;
}
return ret ;
}
static const struct v4l2_ctrl_ops msi001_ctrl_ops = {
. s_ctrl = msi001_s_ctrl ,
} ;
static int msi001_probe ( struct spi_device * spi )
{
struct msi001 * s ;
int ret ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & spi - > dev , " \n " ) ;
2014-02-02 23:34:23 -03:00
s = kzalloc ( sizeof ( struct msi001 ) , GFP_KERNEL ) ;
if ( s = = NULL ) {
ret = - ENOMEM ;
dev_dbg ( & spi - > dev , " Could not allocate memory for msi001 \n " ) ;
goto err_kfree ;
}
s - > spi = spi ;
2014-03-10 16:28:18 -03:00
s - > f_tuner = bands [ 0 ] . rangelow ;
2014-02-02 23:34:23 -03:00
v4l2_spi_subdev_init ( & s - > sd , spi , & msi001_ops ) ;
/* Register controls */
v4l2_ctrl_handler_init ( & s - > hdl , 5 ) ;
s - > bandwidth_auto = v4l2_ctrl_new_std ( & s - > hdl , & msi001_ctrl_ops ,
V4L2_CID_RF_TUNER_BANDWIDTH_AUTO , 0 , 1 , 1 , 1 ) ;
s - > bandwidth = v4l2_ctrl_new_std ( & s - > hdl , & msi001_ctrl_ops ,
V4L2_CID_RF_TUNER_BANDWIDTH , 200000 , 8000000 , 1 , 200000 ) ;
v4l2_ctrl_auto_cluster ( 2 , & s - > bandwidth_auto , 0 , false ) ;
s - > lna_gain = v4l2_ctrl_new_std ( & s - > hdl , & msi001_ctrl_ops ,
V4L2_CID_RF_TUNER_LNA_GAIN , 0 , 1 , 1 , 1 ) ;
s - > mixer_gain = v4l2_ctrl_new_std ( & s - > hdl , & msi001_ctrl_ops ,
V4L2_CID_RF_TUNER_MIXER_GAIN , 0 , 1 , 1 , 1 ) ;
s - > if_gain = v4l2_ctrl_new_std ( & s - > hdl , & msi001_ctrl_ops ,
V4L2_CID_RF_TUNER_IF_GAIN , 0 , 59 , 1 , 0 ) ;
if ( s - > hdl . error ) {
ret = s - > hdl . error ;
dev_err ( & s - > spi - > dev , " Could not initialize controls \n " ) ;
/* control init failed, free handler */
goto err_ctrl_handler_free ;
}
s - > sd . ctrl_handler = & s - > hdl ;
return 0 ;
err_ctrl_handler_free :
v4l2_ctrl_handler_free ( & s - > hdl ) ;
err_kfree :
kfree ( s ) ;
return ret ;
}
static int msi001_remove ( struct spi_device * spi )
{
struct v4l2_subdev * sd = spi_get_drvdata ( spi ) ;
struct msi001 * s = sd_to_msi001 ( sd ) ;
2014-08-24 23:12:13 -03:00
dev_dbg ( & spi - > dev , " \n " ) ;
2014-02-02 23:34:23 -03:00
/*
* Registered by v4l2_spi_new_subdev ( ) from master driver , but we must
* unregister it from here . Weird .
*/
v4l2_device_unregister_subdev ( & s - > sd ) ;
v4l2_ctrl_handler_free ( & s - > hdl ) ;
kfree ( s ) ;
return 0 ;
}
static const struct spi_device_id msi001_id [ ] = {
{ " msi001 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , msi001_id ) ;
static struct spi_driver msi001_driver = {
. driver = {
. name = " msi001 " ,
. owner = THIS_MODULE ,
} ,
. probe = msi001_probe ,
. remove = msi001_remove ,
. id_table = msi001_id ,
} ;
module_spi_driver ( msi001_driver ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_DESCRIPTION ( " Mirics MSi001 " ) ;
MODULE_LICENSE ( " GPL " ) ;