2005-04-17 02:20:36 +04:00
/*
* i2c tv tuner chip device driver
* core core , i . e . kernel interfaces , registering and so on
2011-02-15 15:15:19 +03:00
*
* Copyright ( c ) by Ralph Metzler , Gerd Knorr , Gunther Mayer
*
* Copyright ( c ) 2005 - 2011 by Mauro Carvalho Chehab
* - Added support for a separate Radio tuner
* - Major rework and cleanups at the code
*
* This driver supports many devices and the idea is to let the driver
* detect which device is present . So rather than listing all supported
* devices here , we pretend to support a single , fake device type that will
* handle both radio and analog TV tuning .
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/i2c.h>
# include <linux/types.h>
# include <linux/init.h>
2009-04-01 10:32:22 +04:00
# include <linux/videodev2.h>
2005-04-17 02:20:36 +04:00
# include <media/tuner.h>
2007-08-28 04:59:08 +04:00
# include <media/tuner-types.h>
2008-11-25 00:21:40 +03:00
# include <media/v4l2-device.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2007-08-28 04:23:08 +04:00
# include "mt20xx.h"
2007-08-28 04:22:20 +04:00
# include "tda8290.h"
2007-08-28 04:23:40 +04:00
# include "tea5761.h"
2007-08-28 04:24:27 +04:00
# include "tea5767.h"
2007-10-23 22:24:06 +04:00
# include "tuner-xc2028.h"
2007-08-28 04:59:08 +04:00
# include "tuner-simple.h"
2007-10-22 03:48:48 +04:00
# include "tda9887.h"
2008-01-05 22:50:14 +03:00
# include "xc5000.h"
2009-09-16 06:04:18 +04:00
# include "tda18271.h"
2009-06-24 05:34:06 +04:00
# include "xc4000.h"
2005-04-17 02:20:36 +04:00
# define UNSET (-1U)
2013-09-29 12:51:01 +04:00
# define PREFIX (t->i2c->dev.driver->name)
2007-10-30 15:46:10 +03:00
2011-02-04 05:32:07 +03:00
/*
* Driver modprobe parameters
*/
/* insmod options used at init time => read/only */
static unsigned int addr ;
static unsigned int no_autodetect ;
static unsigned int show_i2c ;
module_param ( addr , int , 0444 ) ;
module_param ( no_autodetect , int , 0444 ) ;
module_param ( show_i2c , int , 0444 ) ;
/* insmod options used at runtime => read/write */
static int tuner_debug ;
static unsigned int tv_range [ 2 ] = { 44 , 958 } ;
static unsigned int radio_range [ 2 ] = { 65 , 108 } ;
static char pal [ ] = " -- " ;
static char secam [ ] = " -- " ;
static char ntsc [ ] = " - " ;
2011-02-04 17:28:00 +03:00
module_param_named ( debug , tuner_debug , int , 0644 ) ;
2011-02-04 05:32:07 +03:00
module_param_array ( tv_range , int , NULL , 0644 ) ;
module_param_array ( radio_range , int , NULL , 0644 ) ;
module_param_string ( pal , pal , sizeof ( pal ) , 0644 ) ;
module_param_string ( secam , secam , sizeof ( secam ) , 0644 ) ;
module_param_string ( ntsc , ntsc , sizeof ( ntsc ) , 0644 ) ;
/*
* Static vars
*/
static LIST_HEAD ( tuner_list ) ;
2011-02-15 15:15:19 +03:00
static const struct v4l2_subdev_ops tuner_ops ;
2011-02-04 05:32:07 +03:00
/*
* Debug macros
*/
# define tuner_warn(fmt, arg...) do { \
printk ( KERN_WARNING " %s %d-%04x: " fmt , PREFIX , \
i2c_adapter_id ( t - > i2c - > adapter ) , \
t - > i2c - > addr , # # arg ) ; \
} while ( 0 )
# define tuner_info(fmt, arg...) do { \
printk ( KERN_INFO " %s %d-%04x: " fmt , PREFIX , \
i2c_adapter_id ( t - > i2c - > adapter ) , \
t - > i2c - > addr , # # arg ) ; \
} while ( 0 )
# define tuner_err(fmt, arg...) do { \
printk ( KERN_ERR " %s %d-%04x: " fmt , PREFIX , \
i2c_adapter_id ( t - > i2c - > adapter ) , \
t - > i2c - > addr , # # arg ) ; \
} while ( 0 )
# define tuner_dbg(fmt, arg...) do { \
if ( tuner_debug ) \
printk ( KERN_DEBUG " %s %d-%04x: " fmt , PREFIX , \
i2c_adapter_id ( t - > i2c - > adapter ) , \
t - > i2c - > addr , # # arg ) ; \
} while ( 0 )
/*
* Internal struct used inside the driver
*/
struct tuner {
/* device */
struct dvb_frontend fe ;
struct i2c_client * i2c ;
struct v4l2_subdev sd ;
struct list_head list ;
/* keep track of the current settings */
v4l2_std_id std ;
unsigned int tv_freq ;
unsigned int radio_freq ;
unsigned int audmode ;
2011-02-04 16:42:09 +03:00
enum v4l2_tuner_type mode ;
2011-02-04 05:32:07 +03:00
unsigned int mode_mask ; /* Combination of allowable modes */
2011-02-04 16:42:09 +03:00
bool standby ; /* Standby mode */
2011-02-04 05:32:07 +03:00
unsigned int type ; /* chip type id */
2013-04-06 21:21:36 +04:00
void * config ;
2011-02-04 05:32:07 +03:00
const char * name ;
2015-01-03 18:44:40 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
struct media_pad pad ;
# endif
2011-02-04 05:32:07 +03:00
} ;
2011-02-15 15:15:19 +03:00
/*
* Function prototypes
*/
static void set_tv_freq ( struct i2c_client * c , unsigned int freq ) ;
static void set_radio_freq ( struct i2c_client * c , unsigned int freq ) ;
2011-02-04 05:32:07 +03:00
/*
* tuner attach / detach logic
*/
2011-02-15 07:10:20 +03:00
/* This macro allows us to probe dynamically, avoiding static links */
2008-04-30 06:02:33 +04:00
# ifdef CONFIG_MEDIA_ATTACH
2008-04-29 10:54:19 +04:00
# define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
int __r = - EINVAL ; \
typeof ( & FUNCTION ) __a = symbol_request ( FUNCTION ) ; \
if ( __a ) { \
__r = ( int ) __a ( ARGS ) ; \
2008-04-30 18:40:17 +04:00
symbol_put ( FUNCTION ) ; \
2008-04-29 10:54:19 +04:00
} else { \
printk ( KERN_ERR " TUNER: Unable to find " \
" symbol " # FUNCTION " () \n " ) ; \
} \
__r ; \
} )
static void tuner_detach ( struct dvb_frontend * fe )
{
if ( fe - > ops . tuner_ops . release ) {
fe - > ops . tuner_ops . release ( fe ) ;
symbol_put_addr ( fe - > ops . tuner_ops . release ) ;
}
if ( fe - > ops . analog_ops . release ) {
fe - > ops . analog_ops . release ( fe ) ;
symbol_put_addr ( fe - > ops . analog_ops . release ) ;
}
}
# else
# define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
FUNCTION ( ARGS ) ; \
} )
static void tuner_detach ( struct dvb_frontend * fe )
{
if ( fe - > ops . tuner_ops . release )
fe - > ops . tuner_ops . release ( fe ) ;
if ( fe - > ops . analog_ops . release )
fe - > ops . analog_ops . release ( fe ) ;
}
# endif
2007-12-17 04:02:26 +03:00
2008-11-25 00:21:40 +03:00
static inline struct tuner * to_tuner ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct tuner , sd ) ;
}
2011-02-04 05:32:07 +03:00
/*
* struct analog_demod_ops callbacks
*/
2005-04-17 02:20:36 +04:00
2007-12-08 23:06:30 +03:00
static void fe_set_params ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2007-08-21 08:25:48 +04:00
{
2007-10-22 02:39:50 +04:00
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
struct tuner * t = fe - > analog_demod_priv ;
2007-08-21 08:25:48 +04:00
if ( NULL = = fe_tuner_ops - > set_analog_params ) {
tuner_warn ( " Tuner frontend module has no way to set freq \n " ) ;
return ;
}
2007-12-08 23:06:30 +03:00
fe_tuner_ops - > set_analog_params ( fe , params ) ;
2007-08-21 08:25:48 +04:00
}
2007-10-22 02:39:50 +04:00
static void fe_standby ( struct dvb_frontend * fe )
2007-08-21 08:25:48 +04:00
{
2007-10-22 02:39:50 +04:00
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
2007-08-21 08:25:48 +04:00
if ( fe_tuner_ops - > sleep )
2007-10-22 02:39:50 +04:00
fe_tuner_ops - > sleep ( fe ) ;
2007-08-21 08:25:48 +04:00
}
2007-12-17 01:27:23 +03:00
static int fe_set_config ( struct dvb_frontend * fe , void * priv_cfg )
{
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
struct tuner * t = fe - > analog_demod_priv ;
if ( fe_tuner_ops - > set_config )
return fe_tuner_ops - > set_config ( fe , priv_cfg ) ;
tuner_warn ( " Tuner frontend module has no way to set config \n " ) ;
return 0 ;
}
2007-10-22 02:39:50 +04:00
static void tuner_status ( struct dvb_frontend * fe ) ;
2007-10-21 20:40:56 +04:00
2013-03-25 15:14:13 +04:00
static const struct analog_demod_ops tuner_analog_ops = {
2007-12-08 23:06:30 +03:00
. set_params = fe_set_params ,
2007-10-21 20:40:56 +04:00
. standby = fe_standby ,
2007-12-17 01:27:23 +03:00
. set_config = fe_set_config ,
2007-10-21 20:40:56 +04:00
. tuner_status = tuner_status
} ;
2011-02-04 05:32:07 +03:00
/*
2011-02-15 15:27:03 +03:00
* Functions to select between radio and TV and tuner probe / remove functions
2011-02-04 05:32:07 +03:00
*/
2005-04-17 02:20:36 +04:00
2011-02-15 15:15:19 +03:00
/**
* set_type - Sets the tuner type for a given device
*
2013-10-10 17:39:32 +04:00
* @ c : i2c_client descriptor
2011-02-15 15:15:19 +03:00
* @ type : type of the tuner ( e . g . tuner number )
* @ new_mode_mask : Indicates if tuner supports TV and / or Radio
2013-04-06 21:21:36 +04:00
* @ new_config : an optional parameter used by a few tuners to adjust
internal parameters , like LNA mode
2011-02-15 15:15:19 +03:00
* @ tuner_callback : an optional function to be called when switching
* to analog mode
*
* This function applys the tuner config to tuner specified
* by tun_setup structure . It contains several per - tuner initialization " magic "
*/
2005-07-13 00:58:55 +04:00
static void set_type ( struct i2c_client * c , unsigned int type ,
2013-04-06 21:21:36 +04:00
unsigned int new_mode_mask , void * new_config ,
2008-09-12 20:31:45 +04:00
int ( * tuner_callback ) ( void * dev , int component , int cmd , int arg ) )
2005-04-17 02:20:36 +04:00
{
2008-11-25 00:21:40 +03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2007-08-21 08:25:48 +04:00
struct dvb_tuner_ops * fe_tuner_ops = & t - > fe . ops . tuner_ops ;
2007-12-21 17:18:32 +03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-06-29 07:45:21 +04:00
unsigned char buffer [ 4 ] ;
2009-10-24 23:42:16 +04:00
int tune_now = 1 ;
2005-04-17 02:20:36 +04:00
2005-07-13 00:58:55 +04:00
if ( type = = UNSET | | type = = TUNER_ABSENT ) {
2011-02-04 17:28:00 +03:00
tuner_dbg ( " tuner 0x%02x: Tuner type absent \n " , c - > addr ) ;
2005-04-17 02:20:36 +04:00
return ;
2005-07-13 00:58:55 +04:00
}
2007-04-27 19:31:18 +04:00
t - > type = type ;
2013-04-06 21:21:36 +04:00
t - > config = new_config ;
2007-04-27 19:31:18 +04:00
if ( tuner_callback ! = NULL ) {
tuner_dbg ( " defining GPIO callback \n " ) ;
2008-09-12 20:31:45 +04:00
t - > fe . callback = tuner_callback ;
2007-04-27 19:31:18 +04:00
}
2007-05-30 05:54:06 +04:00
/* discard private data, in case set_type() was previously called */
2008-04-29 10:54:19 +04:00
tuner_detach ( & t - > fe ) ;
t - > fe . analog_demod_priv = NULL ;
2007-06-04 21:40:27 +04:00
2005-04-17 02:20:36 +04:00
switch ( t - > type ) {
case TUNER_MT2032 :
2008-04-30 22:29:57 +04:00
if ( ! dvb_attach ( microtune_attach ,
& t - > fe , t - > i2c - > adapter , t - > i2c - > addr ) )
goto attach_failed ;
2005-04-17 02:20:36 +04:00
break ;
case TUNER_PHILIPS_TDA8290 :
2007-10-22 16:56:38 +04:00
{
2008-04-30 22:29:57 +04:00
if ( ! dvb_attach ( tda829x_attach , & t - > fe , t - > i2c - > adapter ,
2013-04-06 21:21:36 +04:00
t - > i2c - > addr , t - > config ) )
2008-04-30 22:29:57 +04:00
goto attach_failed ;
2007-10-22 16:56:38 +04:00
break ;
}
2005-06-29 07:45:21 +04:00
case TUNER_TEA5767 :
2008-04-29 10:54:19 +04:00
if ( ! dvb_attach ( tea5767_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2005-07-13 00:58:55 +04:00
t - > mode_mask = T_RADIO ;
2005-06-29 07:45:21 +04:00
break ;
2007-04-08 08:09:11 +04:00
case TUNER_TEA5761 :
2008-04-29 10:54:19 +04:00
if ( ! dvb_attach ( tea5761_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2007-04-08 08:09:11 +04:00
t - > mode_mask = T_RADIO ;
break ;
2005-06-29 07:45:21 +04:00
case TUNER_PHILIPS_FMD1216ME_MK3 :
2011-12-13 22:36:15 +04:00
case TUNER_PHILIPS_FMD1216MEX_MK3 :
2005-06-29 07:45:21 +04:00
buffer [ 0 ] = 0x0b ;
buffer [ 1 ] = 0xdc ;
buffer [ 2 ] = 0x9c ;
buffer [ 3 ] = 0x60 ;
2005-07-13 00:58:55 +04:00
i2c_master_send ( c , buffer , 4 ) ;
2005-06-29 07:45:21 +04:00
mdelay ( 1 ) ;
buffer [ 2 ] = 0x86 ;
buffer [ 3 ] = 0x54 ;
2005-07-13 00:58:55 +04:00
i2c_master_send ( c , buffer , 4 ) ;
2008-04-29 10:54:19 +04:00
if ( ! dvb_attach ( simple_tuner_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr , t - > type ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2005-06-29 07:45:21 +04:00
break ;
2005-11-09 08:36:31 +03:00
case TUNER_PHILIPS_TD1316 :
buffer [ 0 ] = 0x0b ;
buffer [ 1 ] = 0xdc ;
buffer [ 2 ] = 0x86 ;
buffer [ 3 ] = 0xa4 ;
2008-04-29 10:54:19 +04:00
i2c_master_send ( c , buffer , 4 ) ;
if ( ! dvb_attach ( simple_tuner_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr , t - > type ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2006-01-23 22:11:09 +03:00
break ;
2007-10-29 17:33:18 +03:00
case TUNER_XC2028 :
{
2007-11-16 13:46:14 +03:00
struct xc2028_config cfg = {
. i2c_adap = t - > i2c - > adapter ,
. i2c_addr = t - > i2c - > addr ,
} ;
2008-04-29 10:54:19 +04:00
if ( ! dvb_attach ( xc2028_attach , & t - > fe , & cfg ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2009-10-24 23:42:16 +04:00
tune_now = 0 ;
2007-10-29 17:33:18 +03:00
break ;
}
2006-06-23 23:13:56 +04:00
case TUNER_TDA9887 :
2008-04-30 22:29:57 +04:00
if ( ! dvb_attach ( tda9887_attach ,
& t - > fe , t - > i2c - > adapter , t - > i2c - > addr ) )
goto attach_failed ;
2006-06-23 23:13:56 +04:00
break ;
2008-01-05 22:50:14 +03:00
case TUNER_XC5000 :
2008-04-26 18:29:34 +04:00
{
2011-02-04 18:56:39 +03:00
struct xc5000_config xc5000_cfg = {
. i2c_address = t - > i2c - > addr ,
/* if_khz will be set at dvb_attach() */
. if_khz = 0 ,
} ;
2008-04-29 10:54:19 +04:00
if ( ! dvb_attach ( xc5000_attach ,
2008-09-06 21:56:58 +04:00
& t - > fe , t - > i2c - > adapter , & xc5000_cfg ) )
2008-04-26 18:29:34 +04:00
goto attach_failed ;
2009-10-24 23:42:16 +04:00
tune_now = 0 ;
2008-01-05 22:50:14 +03:00
break ;
2008-04-26 18:29:34 +04:00
}
2012-02-07 08:22:34 +04:00
case TUNER_XC5000C :
{
struct xc5000_config xc5000c_cfg = {
. i2c_address = t - > i2c - > addr ,
/* if_khz will be set at dvb_attach() */
. if_khz = 0 ,
2012-02-08 21:57:39 +04:00
. chip_id = XC5000C ,
2012-02-07 08:22:34 +04:00
} ;
if ( ! dvb_attach ( xc5000_attach ,
& t - > fe , t - > i2c - > adapter , & xc5000c_cfg ) )
goto attach_failed ;
tune_now = 0 ;
break ;
}
2009-09-16 06:04:18 +04:00
case TUNER_NXP_TDA18271 :
{
struct tda18271_config cfg = {
2010-09-27 05:58:28 +04:00
. small_i2c = TDA18271_03_BYTE_CHUNK_INIT ,
2009-09-16 06:04:18 +04:00
} ;
2011-02-04 05:32:07 +03:00
if ( ! dvb_attach ( tda18271_attach , & t - > fe , t - > i2c - > addr ,
t - > i2c - > adapter , & cfg ) )
goto attach_failed ;
tune_now = 0 ;
break ;
}
2009-06-24 05:34:06 +04:00
case TUNER_XC4000 :
{
struct xc4000_config xc4000_cfg = {
. i2c_address = t - > i2c - > addr ,
2011-06-06 20:03:44 +04:00
/* FIXME: the correct parameters will be set */
/* only when the digital dvb_attach() occurs */
. default_pm = 0 ,
. dvb_amplitude = 0 ,
. set_smoothedcvbs = 0 ,
. if_khz = 0
2009-06-24 05:34:06 +04:00
} ;
if ( ! dvb_attach ( xc4000_attach ,
& t - > fe , t - > i2c - > adapter , & xc4000_cfg ) )
goto attach_failed ;
tune_now = 0 ;
break ;
}
2011-02-04 05:32:07 +03:00
default :
if ( ! dvb_attach ( simple_tuner_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr , t - > type ) )
goto attach_failed ;
break ;
}
if ( ( NULL = = analog_ops - > set_params ) & &
( fe_tuner_ops - > set_analog_params ) ) {
t - > name = fe_tuner_ops - > info . name ;
t - > fe . analog_demod_priv = t ;
memcpy ( analog_ops , & tuner_analog_ops ,
sizeof ( struct analog_demod_ops ) ) ;
2013-03-25 16:55:58 +04:00
if ( fe_tuner_ops - > get_rf_strength )
2013-04-06 11:41:29 +04:00
analog_ops - > has_signal = fe_tuner_ops - > get_rf_strength ;
2013-03-25 16:55:58 +04:00
if ( fe_tuner_ops - > get_afc )
2013-04-06 11:35:27 +04:00
analog_ops - > get_afc = fe_tuner_ops - > get_afc ;
2013-03-25 15:14:13 +04:00
2011-02-04 05:32:07 +03:00
} else {
t - > name = analog_ops - > info . name ;
}
2015-02-18 18:04:05 +03:00
# ifdef CONFIG_MEDIA_CONTROLLER
2015-01-03 18:44:40 +03:00
t - > sd . entity . name = t - > name ;
2015-02-18 18:04:05 +03:00
# endif
2015-01-03 18:44:40 +03:00
2011-02-04 05:32:07 +03:00
tuner_dbg ( " type set to %s \n " , t - > name ) ;
2011-02-04 16:42:09 +03:00
t - > mode_mask = new_mode_mask ;
2011-02-04 05:32:07 +03:00
/* Some tuners require more initialization setup before use,
such as firmware download or device calibration .
trying to set a frequency here will just fail
FIXME : better to move set_freq to the tuner code . This is needed
on analog tuners for PLL to properly work
*/
if ( tune_now ) {
if ( V4L2_TUNER_RADIO = = t - > mode )
set_radio_freq ( c , t - > radio_freq ) ;
else
set_tv_freq ( c , t - > tv_freq ) ;
}
tuner_dbg ( " %s %s I2C addr 0x%02x with type %d used for 0x%02x \n " ,
2013-09-29 12:51:01 +04:00
c - > adapter - > name , c - > dev . driver - > name , c - > addr < < 1 , type ,
2011-02-04 05:32:07 +03:00
t - > mode_mask ) ;
return ;
attach_failed :
tuner_dbg ( " Tuner attach for type = %d failed. \n " , t - > type ) ;
t - > type = TUNER_ABSENT ;
return ;
}
2011-02-15 07:10:20 +03:00
/**
* tuner_s_type_addr - Sets the tuner type for a device
*
* @ sd : subdev descriptor
* @ tun_setup : type to be associated to a given tuner i2c address
*
* This function applys the tuner config to tuner specified
* by tun_setup structure .
* If tuner I2C address is UNSET , then it will only set the device
* if the tuner supports the mode specified in the call .
* If the address is specified , the change will be applied only if
* tuner I2C address matches .
* The call can change the tuner number and the tuner mode .
*/
2011-02-15 06:55:18 +03:00
static int tuner_s_type_addr ( struct v4l2_subdev * sd ,
struct tuner_setup * tun_setup )
2011-02-04 05:32:07 +03:00
{
2011-02-15 06:55:18 +03:00
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * c = v4l2_get_subdevdata ( sd ) ;
2013-04-06 21:21:36 +04:00
tuner_dbg ( " Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=%p \n " ,
2011-02-15 06:55:18 +03:00
tun_setup - > type ,
tun_setup - > addr ,
tun_setup - > mode_mask ,
tun_setup - > config ) ;
2011-02-04 05:32:07 +03:00
2011-02-04 17:28:00 +03:00
if ( ( t - > type = = UNSET & & ( ( tun_setup - > addr = = ADDR_UNSET ) & &
( t - > mode_mask & tun_setup - > mode_mask ) ) ) | |
( tun_setup - > addr = = c - > addr ) ) {
2011-02-04 16:42:09 +03:00
set_type ( c , tun_setup - > type , tun_setup - > mode_mask ,
tun_setup - > config , tun_setup - > tuner_callback ) ;
2011-02-04 05:32:07 +03:00
} else
tuner_dbg ( " set addr discarded for type %i, mask %x. "
" Asked to change tuner at addr 0x%02x, with mask %x \n " ,
t - > type , t - > mode_mask ,
tun_setup - > addr , tun_setup - > mode_mask ) ;
return 0 ;
}
2011-02-15 15:15:19 +03:00
/**
* tuner_s_config - Sets tuner configuration
*
* @ sd : subdev descriptor
* @ cfg : tuner configuration
*
* Calls tuner set_config ( ) private function to set some tuner - internal
* parameters
*/
2011-02-04 17:28:00 +03:00
static int tuner_s_config ( struct v4l2_subdev * sd ,
const struct v4l2_priv_tun_config * cfg )
2011-02-04 05:32:07 +03:00
{
struct tuner * t = to_tuner ( sd ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
if ( t - > type ! = cfg - > tuner )
return 0 ;
if ( analog_ops - > set_config ) {
analog_ops - > set_config ( & t - > fe , cfg - > priv ) ;
return 0 ;
}
tuner_dbg ( " Tuner frontend module has no way to set config \n " ) ;
return 0 ;
}
2011-02-15 15:15:19 +03:00
/**
* tuner_lookup - Seek for tuner adapters
*
* @ adap : i2c_adapter struct
* @ radio : pointer to be filled if the adapter is radio
* @ tv : pointer to be filled if the adapter is TV
*
* Search for existing radio and / or TV tuners on the given I2C adapter ,
* discarding demod - only adapters ( tda9887 ) .
*
* Note that when this function is called from tuner_probe you can be
* certain no other devices will be added / deleted at the same time , I2C
* core protects against that .
*/
2011-02-04 05:32:07 +03:00
static void tuner_lookup ( struct i2c_adapter * adap ,
struct tuner * * radio , struct tuner * * tv )
{
struct tuner * pos ;
* radio = NULL ;
* tv = NULL ;
list_for_each_entry ( pos , & tuner_list , list ) {
int mode_mask ;
if ( pos - > i2c - > adapter ! = adap | |
2013-09-29 12:51:01 +04:00
strcmp ( pos - > i2c - > dev . driver - > name , " tuner " ) )
2011-02-04 05:32:07 +03:00
continue ;
2011-02-04 16:42:09 +03:00
mode_mask = pos - > mode_mask ;
2011-02-04 05:32:07 +03:00
if ( * radio = = NULL & & mode_mask = = T_RADIO )
* radio = pos ;
/* Note: currently TDA9887 is the only demod-only
device . If other devices appear then we need to
make this test more general . */
else if ( * tv = = NULL & & pos - > type ! = TUNER_TDA9887 & &
2011-02-15 14:30:50 +03:00
( pos - > mode_mask & T_ANALOG_TV ) )
2011-02-04 05:32:07 +03:00
* tv = pos ;
}
}
2011-02-15 15:15:19 +03:00
/**
* tuner_probe - Probes the existing tuners on an I2C bus
*
* @ client : i2c_client descriptor
* @ id : not used
*
* This routine probes for tuners at the expected I2C addresses . On most
* cases , if a device answers to a given I2C address , it assumes that the
* device is a tuner . On a few cases , however , an additional logic is needed
* to double check if the device is really a tuner , or to identify the tuner
* type , like on tea5767 / 5761 devices .
*
* During client attach , set_type is called by adapter ' s attach_inform callback .
* set_type must then be completed by tuner_probe .
2011-02-04 05:32:07 +03:00
*/
static int tuner_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct tuner * t ;
struct tuner * radio ;
struct tuner * tv ;
2015-01-03 18:44:40 +03:00
# ifdef CONFIG_MEDIA_CONTROLLER
int ret ;
# endif
2011-02-04 05:32:07 +03:00
t = kzalloc ( sizeof ( struct tuner ) , GFP_KERNEL ) ;
if ( NULL = = t )
return - ENOMEM ;
v4l2_i2c_subdev_init ( & t - > sd , client , & tuner_ops ) ;
t - > i2c = client ;
t - > name = " (tuner unset) " ;
t - > type = UNSET ;
t - > audmode = V4L2_TUNER_MODE_STEREO ;
2014-09-03 22:08:11 +04:00
t - > standby = true ;
2011-02-04 16:42:09 +03:00
t - > radio_freq = 87.5 * 16000 ; /* Initial freq range */
t - > tv_freq = 400 * 16 ; /* Sets freq to VHF High - needed for some PLL's to properly start */
2011-02-04 05:32:07 +03:00
if ( show_i2c ) {
unsigned char buffer [ 16 ] ;
int i , rc ;
memset ( buffer , 0 , sizeof ( buffer ) ) ;
rc = i2c_master_recv ( client , buffer , sizeof ( buffer ) ) ;
tuner_info ( " I2C RECV = " ) ;
for ( i = 0 ; i < rc ; i + + )
printk ( KERN_CONT " %02x " , buffer [ i ] ) ;
printk ( " \n " ) ;
}
/* autodetection code based on the i2c addr */
if ( ! no_autodetect ) {
switch ( client - > addr ) {
case 0x10 :
if ( tuner_symbol_probe ( tea5761_autodetection ,
t - > i2c - > adapter ,
t - > i2c - > addr ) > = 0 ) {
t - > type = TUNER_TEA5761 ;
t - > mode_mask = T_RADIO ;
tuner_lookup ( t - > i2c - > adapter , & radio , & tv ) ;
if ( tv )
tv - > mode_mask & = ~ T_RADIO ;
goto register_client ;
}
kfree ( t ) ;
return - ENODEV ;
case 0x42 :
case 0x43 :
case 0x4a :
case 0x4b :
/* If chip is not tda8290, don't register.
since it can be tda9887 */
if ( tuner_symbol_probe ( tda829x_probe , t - > i2c - > adapter ,
t - > i2c - > addr ) > = 0 ) {
tuner_dbg ( " tda829x detected \n " ) ;
} else {
/* Default is being tda9887 */
t - > type = TUNER_TDA9887 ;
2011-02-15 14:30:50 +03:00
t - > mode_mask = T_RADIO | T_ANALOG_TV ;
2011-02-04 05:32:07 +03:00
goto register_client ;
}
break ;
case 0x60 :
if ( tuner_symbol_probe ( tea5767_autodetection ,
t - > i2c - > adapter , t - > i2c - > addr )
> = 0 ) {
t - > type = TUNER_TEA5767 ;
t - > mode_mask = T_RADIO ;
/* Sets freq to FM range */
tuner_lookup ( t - > i2c - > adapter , & radio , & tv ) ;
if ( tv )
tv - > mode_mask & = ~ T_RADIO ;
goto register_client ;
}
break ;
}
2009-09-16 06:04:18 +04:00
}
2008-04-26 18:29:34 +04:00
2011-02-04 05:32:07 +03:00
/* Initializes only the first TV tuner on this adapter. Why only the
first ? Because there are some devices ( notably the ones with TI
tuners ) that have more than one i2c address for the * same * device .
Experience shows that , except for just one case , the first
address is the right one . The exception is a Russian tuner
( ACORP_Y878F ) . So , the desired behavior is just to enable the
first found TV tuner . */
tuner_lookup ( t - > i2c - > adapter , & radio , & tv ) ;
if ( tv = = NULL ) {
2011-02-15 14:30:50 +03:00
t - > mode_mask = T_ANALOG_TV ;
2011-02-04 05:32:07 +03:00
if ( radio = = NULL )
t - > mode_mask | = T_RADIO ;
tuner_dbg ( " Setting mode_mask to 0x%02x \n " , t - > mode_mask ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-13 00:58:55 +04:00
2011-02-04 05:32:07 +03:00
/* Should be just before return */
register_client :
2015-01-03 18:44:40 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
t - > pad . flags = MEDIA_PAD_FL_SOURCE ;
t - > sd . entity . type = MEDIA_ENT_T_V4L2_SUBDEV_TUNER ;
t - > sd . entity . name = t - > name ;
ret = media_entity_init ( & t - > sd . entity , 1 , & t - > pad , 0 ) ;
if ( ret < 0 ) {
tuner_err ( " failed to initialize media entity! \n " ) ;
kfree ( t ) ;
return - ENODEV ;
}
# endif
2011-02-04 05:32:07 +03:00
/* Sets a default mode */
2011-02-04 17:28:00 +03:00
if ( t - > mode_mask & T_ANALOG_TV )
2011-02-04 05:32:07 +03:00
t - > mode = V4L2_TUNER_ANALOG_TV ;
2011-02-04 17:28:00 +03:00
else
2011-02-15 14:30:50 +03:00
t - > mode = V4L2_TUNER_RADIO ;
2011-02-04 05:32:07 +03:00
set_type ( client , t - > type , t - > mode_mask , t - > config , t - > fe . callback ) ;
list_add_tail ( & t - > list , & tuner_list ) ;
2011-02-04 16:42:09 +03:00
2011-02-15 14:30:50 +03:00
tuner_info ( " Tuner %d found with type(s)%s%s. \n " ,
2011-02-04 16:42:09 +03:00
t - > type ,
2011-02-15 14:30:50 +03:00
t - > mode_mask & T_RADIO ? " Radio " : " " ,
t - > mode_mask & T_ANALOG_TV ? " TV " : " " ) ;
2011-02-04 05:32:07 +03:00
return 0 ;
}
2007-08-21 08:25:48 +04:00
2011-02-15 15:15:19 +03:00
/**
* tuner_remove - detaches a tuner
*
* @ client : i2c_client descriptor
*/
2011-02-04 05:32:07 +03:00
static int tuner_remove ( struct i2c_client * client )
{
struct tuner * t = to_tuner ( i2c_get_clientdata ( client ) ) ;
2008-04-26 18:29:34 +04:00
2011-02-04 05:32:07 +03:00
v4l2_device_unregister_subdev ( & t - > sd ) ;
tuner_detach ( & t - > fe ) ;
t - > fe . analog_demod_priv = NULL ;
2008-04-26 18:29:34 +04:00
2011-02-04 05:32:07 +03:00
list_del ( & t - > list ) ;
kfree ( t ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2011-02-15 15:27:03 +03:00
/*
* Functions to switch between Radio and TV
*
* A few cards have a separate I2C tuner for radio . Those routines
* take care of switching between TV / Radio mode , filtering only the
* commands that apply to the Radio or TV tuner .
*/
/**
* check_mode - Verify if tuner supports the requested mode
* @ t : a pointer to the module ' s internal struct_tuner
*
* This function checks if the tuner is capable of tuning analog TV ,
* digital TV or radio , depending on what the caller wants . If the
* tuner can ' t support that mode , it returns - EINVAL . Otherwise , it
* returns 0.
* This function is needed for boards that have a separate tuner for
* radio ( like devices with tea5767 ) .
2011-07-13 08:23:11 +04:00
* NOTE : mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to
* select a TV frequency . So , t_mode = T_ANALOG_TV could actually
* be used to represent a Digital TV too .
2011-02-15 15:27:03 +03:00
*/
static inline int check_mode ( struct tuner * t , enum v4l2_tuner_type mode )
{
2011-07-13 08:23:11 +04:00
int t_mode ;
if ( mode = = V4L2_TUNER_RADIO )
t_mode = T_RADIO ;
else
t_mode = T_ANALOG_TV ;
if ( ( t_mode & t - > mode_mask ) = = 0 )
2011-02-15 15:27:03 +03:00
return - EINVAL ;
return 0 ;
}
/**
2011-06-14 10:56:09 +04:00
* set_mode - Switch tuner to other mode .
2011-02-15 15:27:03 +03:00
* @ t : a pointer to the module ' s internal struct_tuner
* @ mode : enum v4l2_type ( radio or TV )
*
* If tuner doesn ' t support the needed mode ( radio or TV ) , prints a
* debug message and returns - EINVAL , changing its state to standby .
2011-06-14 10:56:09 +04:00
* Otherwise , changes the mode and returns 0.
2011-02-15 15:27:03 +03:00
*/
2011-06-14 10:56:09 +04:00
static int set_mode ( struct tuner * t , enum v4l2_tuner_type mode )
2011-02-15 15:27:03 +03:00
{
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
if ( mode ! = t - > mode ) {
if ( check_mode ( t , mode ) = = - EINVAL ) {
tuner_dbg ( " Tuner doesn't support mode %d. "
" Putting tuner to sleep \n " , mode ) ;
t - > standby = true ;
if ( analog_ops - > standby )
analog_ops - > standby ( & t - > fe ) ;
return - EINVAL ;
}
t - > mode = mode ;
tuner_dbg ( " Changing to mode %d \n " , mode ) ;
}
2011-06-14 10:56:09 +04:00
return 0 ;
}
/**
* set_freq - Set the tuner to the desired frequency .
* @ t : a pointer to the module ' s internal struct_tuner
* @ freq : frequency to set ( 0 means to use the current frequency )
*/
static void set_freq ( struct tuner * t , unsigned int freq )
{
struct i2c_client * client = v4l2_get_subdevdata ( & t - > sd ) ;
2011-02-15 15:27:03 +03:00
if ( t - > mode = = V4L2_TUNER_RADIO ) {
2011-06-14 10:56:09 +04:00
if ( ! freq )
freq = t - > radio_freq ;
set_radio_freq ( client , freq ) ;
2011-02-15 15:27:03 +03:00
} else {
2011-06-14 10:56:09 +04:00
if ( ! freq )
freq = t - > tv_freq ;
set_tv_freq ( client , freq ) ;
2011-02-15 15:27:03 +03:00
}
}
2005-07-13 00:58:55 +04:00
/*
2011-02-04 05:32:07 +03:00
* Functions that are specific for TV mode
*/
2005-07-13 00:58:55 +04:00
2011-02-15 15:15:19 +03:00
/**
* set_tv_freq - Set tuner frequency , freq in Units of 62.5 kHz = 1 / 16 MHz
*
* @ c : i2c_client descriptor
* @ freq : frequency
*/
2011-02-04 05:32:07 +03:00
static void set_tv_freq ( struct i2c_client * c , unsigned int freq )
2005-07-13 00:58:55 +04:00
{
2008-11-25 00:21:40 +03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2011-02-04 05:32:07 +03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-07-13 00:58:55 +04:00
2011-02-04 05:32:07 +03:00
struct analog_parameters params = {
. mode = t - > mode ,
. audmode = t - > audmode ,
. std = t - > std
} ;
2005-06-24 09:05:07 +04:00
2011-02-04 05:32:07 +03:00
if ( t - > type = = UNSET ) {
2011-02-04 17:28:00 +03:00
tuner_warn ( " tuner type not set \n " ) ;
2011-02-04 05:32:07 +03:00
return ;
2005-09-10 00:03:37 +04:00
}
2011-02-04 05:32:07 +03:00
if ( NULL = = analog_ops - > set_params ) {
2011-02-04 17:28:00 +03:00
tuner_warn ( " Tuner has no way to set tv freq \n " ) ;
2011-02-04 05:32:07 +03:00
return ;
2005-06-24 09:05:07 +04:00
}
2011-02-04 05:32:07 +03:00
if ( freq < tv_range [ 0 ] * 16 | | freq > tv_range [ 1 ] * 16 ) {
2011-02-04 17:28:00 +03:00
tuner_dbg ( " TV freq (%d.%02d) out of range (%d-%d) \n " ,
2011-02-04 05:32:07 +03:00
freq / 16 , freq % 16 * 100 / 16 , tv_range [ 0 ] ,
tv_range [ 1 ] ) ;
/* V4L2 spec: if the freq is not possible then the closest
possible value should be selected */
if ( freq < tv_range [ 0 ] * 16 )
freq = tv_range [ 0 ] * 16 ;
else
freq = tv_range [ 1 ] * 16 ;
}
params . frequency = freq ;
tuner_dbg ( " tv freq set to %d.%02d \n " ,
freq / 16 , freq % 16 * 100 / 16 ) ;
t - > tv_freq = freq ;
2011-02-04 16:42:09 +03:00
t - > standby = false ;
2011-02-04 05:32:07 +03:00
analog_ops - > set_params ( & t - > fe , & params ) ;
2005-06-24 09:05:07 +04:00
}
2011-02-15 15:15:19 +03:00
/**
* tuner_fixup_std - force a given video standard variant
*
2011-06-13 16:47:56 +04:00
* @ t : tuner internal struct
* @ std : TV standard
2011-02-15 15:15:19 +03:00
*
* A few devices or drivers have problem to detect some standard variations .
* On other operational systems , the drivers generally have a per - country
* code , and some logic to apply per - country hacks . V4L2 API doesn ' t provide
* such hacks . Instead , it relies on a proper video standard selection from
* the userspace application . However , as some apps are buggy , not allowing
* to distinguish all video standard variations , a modprobe parameter can
* be used to force a video standard match .
*/
2011-06-13 16:47:56 +04:00
static v4l2_std_id tuner_fixup_std ( struct tuner * t , v4l2_std_id std )
2005-04-17 02:20:36 +04:00
{
2011-06-13 16:47:56 +04:00
if ( pal [ 0 ] ! = ' - ' & & ( std & V4L2_STD_PAL ) = = V4L2_STD_PAL ) {
2005-04-17 02:20:36 +04:00
switch ( pal [ 0 ] ) {
2006-12-11 21:51:43 +03:00
case ' 6 ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_PAL_60 ;
2005-04-17 02:20:36 +04:00
case ' b ' :
case ' B ' :
case ' g ' :
case ' G ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_PAL_BG ;
2005-04-17 02:20:36 +04:00
case ' i ' :
case ' I ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_PAL_I ;
2005-04-17 02:20:36 +04:00
case ' d ' :
case ' D ' :
case ' k ' :
case ' K ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_PAL_DK ;
2005-07-13 00:58:55 +04:00
case ' M ' :
case ' m ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_PAL_M ;
2005-07-13 00:58:55 +04:00
case ' N ' :
case ' n ' :
2011-06-13 16:47:56 +04:00
if ( pal [ 1 ] = = ' c ' | | pal [ 1 ] = = ' C ' )
return V4L2_STD_PAL_Nc ;
return V4L2_STD_PAL_N ;
2005-09-10 00:03:59 +04:00
default :
2011-02-04 17:28:00 +03:00
tuner_warn ( " pal= argument not recognised \n " ) ;
2005-09-10 00:03:59 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
2011-06-13 16:47:56 +04:00
if ( secam [ 0 ] ! = ' - ' & & ( std & V4L2_STD_SECAM ) = = V4L2_STD_SECAM ) {
2005-07-13 00:58:55 +04:00
switch ( secam [ 0 ] ) {
2006-01-09 20:25:27 +03:00
case ' b ' :
case ' B ' :
case ' g ' :
case ' G ' :
case ' h ' :
case ' H ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_SECAM_B |
V4L2_STD_SECAM_G |
V4L2_STD_SECAM_H ;
2005-07-13 00:58:55 +04:00
case ' d ' :
case ' D ' :
case ' k ' :
case ' K ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_SECAM_DK ;
2005-07-13 00:58:55 +04:00
case ' l ' :
case ' L ' :
2011-06-13 16:47:56 +04:00
if ( ( secam [ 1 ] = = ' C ' ) | | ( secam [ 1 ] = = ' c ' ) )
return V4L2_STD_SECAM_LC ;
return V4L2_STD_SECAM_L ;
2005-09-10 00:03:59 +04:00
default :
2011-02-04 17:28:00 +03:00
tuner_warn ( " secam= argument not recognised \n " ) ;
2005-09-10 00:03:59 +04:00
break ;
2005-07-13 00:58:55 +04:00
}
}
2011-06-13 16:47:56 +04:00
if ( ntsc [ 0 ] ! = ' - ' & & ( std & V4L2_STD_NTSC ) = = V4L2_STD_NTSC ) {
2006-01-09 20:25:27 +03:00
switch ( ntsc [ 0 ] ) {
case ' m ' :
case ' M ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_NTSC_M ;
2006-01-09 20:25:27 +03:00
case ' j ' :
case ' J ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_NTSC_M_JP ;
2006-02-07 11:48:40 +03:00
case ' k ' :
case ' K ' :
2011-06-13 16:47:56 +04:00
return V4L2_STD_NTSC_M_KR ;
2006-01-09 20:25:27 +03:00
default :
tuner_info ( " ntsc= argument not recognised \n " ) ;
break ;
}
}
2011-06-13 16:47:56 +04:00
return std ;
2011-02-04 05:32:07 +03:00
}
/*
* Functions that are specific for Radio mode
*/
2011-02-15 15:15:19 +03:00
/**
* set_radio_freq - Set tuner frequency , freq in Units of 62.5 Hz = 1 / 16 kHz
*
* @ c : i2c_client descriptor
* @ freq : frequency
*/
2011-02-04 05:32:07 +03:00
static void set_radio_freq ( struct i2c_client * c , unsigned int freq )
{
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
struct analog_parameters params = {
. mode = t - > mode ,
. audmode = t - > audmode ,
. std = t - > std
} ;
if ( t - > type = = UNSET ) {
2011-02-04 17:28:00 +03:00
tuner_warn ( " tuner type not set \n " ) ;
2011-02-04 05:32:07 +03:00
return ;
}
if ( NULL = = analog_ops - > set_params ) {
2011-02-04 17:28:00 +03:00
tuner_warn ( " tuner has no way to set radio frequency \n " ) ;
2011-02-04 05:32:07 +03:00
return ;
}
if ( freq < radio_range [ 0 ] * 16000 | | freq > radio_range [ 1 ] * 16000 ) {
2011-02-04 17:28:00 +03:00
tuner_dbg ( " radio freq (%d.%02d) out of range (%d-%d) \n " ,
2011-02-04 05:32:07 +03:00
freq / 16000 , freq % 16000 * 100 / 16000 ,
radio_range [ 0 ] , radio_range [ 1 ] ) ;
/* V4L2 spec: if the freq is not possible then the closest
possible value should be selected */
if ( freq < radio_range [ 0 ] * 16000 )
freq = radio_range [ 0 ] * 16000 ;
else
freq = radio_range [ 1 ] * 16000 ;
}
params . frequency = freq ;
tuner_dbg ( " radio freq set to %d.%02d \n " ,
freq / 16000 , freq % 16000 * 100 / 16000 ) ;
t - > radio_freq = freq ;
2011-02-04 16:42:09 +03:00
t - > standby = false ;
2011-02-04 05:32:07 +03:00
analog_ops - > set_params ( & t - > fe , & params ) ;
2012-09-28 15:18:07 +04:00
/*
* The tuner driver might decide to change the audmode if it only
* supports stereo , so update t - > audmode .
*/
t - > audmode = params . audmode ;
2011-02-04 05:32:07 +03:00
}
2011-02-15 15:27:03 +03:00
/*
* Debug function for reporting tuner status to userspace
2011-02-04 05:32:07 +03:00
*/
2011-02-04 16:42:09 +03:00
/**
* tuner_status - Dumps the current tuner status at dmesg
* @ fe : pointer to struct dvb_frontend
*
* This callback is used only for driver debug purposes , answering to
* VIDIOC_LOG_STATUS . No changes should happen on this call .
*/
2007-10-22 02:39:50 +04:00
static void tuner_status ( struct dvb_frontend * fe )
2006-01-09 20:25:27 +03:00
{
2007-10-22 02:39:50 +04:00
struct tuner * t = fe - > analog_demod_priv ;
2006-01-09 20:25:27 +03:00
unsigned long freq , freq_fraction ;
2008-04-29 10:54:19 +04:00
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
struct analog_demod_ops * analog_ops = & fe - > ops . analog_ops ;
2006-01-09 20:25:27 +03:00
const char * p ;
switch ( t - > mode ) {
2011-02-04 17:28:00 +03:00
case V4L2_TUNER_RADIO :
p = " radio " ;
break ;
2011-07-13 08:23:11 +04:00
case V4L2_TUNER_DIGITAL_TV : /* Used by mt20xx */
2011-02-04 17:28:00 +03:00
p = " digital TV " ;
break ;
case V4L2_TUNER_ANALOG_TV :
default :
p = " analog TV " ;
break ;
2006-01-09 20:25:27 +03:00
}
if ( t - > mode = = V4L2_TUNER_RADIO ) {
2006-01-15 20:04:52 +03:00
freq = t - > radio_freq / 16000 ;
freq_fraction = ( t - > radio_freq % 16000 ) * 100 / 16000 ;
2006-01-09 20:25:27 +03:00
} else {
2006-01-15 20:04:52 +03:00
freq = t - > tv_freq / 16 ;
freq_fraction = ( t - > tv_freq % 16 ) * 100 / 16 ;
2006-01-09 20:25:27 +03:00
}
2011-02-04 16:42:09 +03:00
tuner_info ( " Tuner mode: %s%s \n " , p ,
t - > standby ? " on standby mode " : " " ) ;
2006-01-09 20:25:27 +03:00
tuner_info ( " Frequency: %lu.%02lu MHz \n " , freq , freq_fraction ) ;
2006-03-25 21:53:38 +03:00
tuner_info ( " Standard: 0x%08lx \n " , ( unsigned long ) t - > std ) ;
2006-01-23 22:11:09 +03:00
if ( t - > mode ! = V4L2_TUNER_RADIO )
2011-02-04 17:28:00 +03:00
return ;
2007-08-21 08:25:48 +04:00
if ( fe_tuner_ops - > get_status ) {
u32 tuner_status ;
fe_tuner_ops - > get_status ( & t - > fe , & tuner_status ) ;
if ( tuner_status & TUNER_STATUS_LOCKED )
tuner_info ( " Tuner is locked. \n " ) ;
if ( tuner_status & TUNER_STATUS_STEREO )
tuner_info ( " Stereo: yes \n " ) ;
}
2013-04-06 11:41:29 +04:00
if ( analog_ops - > has_signal ) {
u16 signal ;
if ( ! analog_ops - > has_signal ( fe , & signal ) )
tuner_info ( " Signal strength: %hu \n " , signal ) ;
}
2006-01-09 20:25:27 +03:00
}
2006-01-23 22:11:09 +03:00
2011-02-15 15:27:03 +03:00
/*
* Function to splicitly change mode to radio . Probably not needed anymore
*/
static int tuner_s_radio ( struct v4l2_subdev * sd )
{
struct tuner * t = to_tuner ( sd ) ;
2011-06-14 10:56:09 +04:00
if ( set_mode ( t , V4L2_TUNER_RADIO ) = = 0 )
set_freq ( t , 0 ) ;
2011-02-15 15:27:03 +03:00
return 0 ;
}
/*
* Tuner callbacks to handle userspace ioctl ' s
*/
2011-02-04 16:42:09 +03:00
/**
* tuner_s_power - controls the power state of the tuner
* @ sd : pointer to struct v4l2_subdev
2011-06-25 17:24:49 +04:00
* @ on : a zero value puts the tuner to sleep , non - zero wakes it up
2011-02-04 16:42:09 +03:00
*/
2009-10-05 17:48:17 +04:00
static int tuner_s_power ( struct v4l2_subdev * sd , int on )
2008-11-25 00:21:40 +03:00
{
struct tuner * t = to_tuner ( sd ) ;
2007-12-21 17:18:32 +03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-17 02:20:36 +04:00
2011-06-25 17:24:49 +04:00
if ( on ) {
if ( t - > standby & & set_mode ( t , t - > mode ) = = 0 ) {
tuner_dbg ( " Waking up tuner \n " ) ;
set_freq ( t , 0 ) ;
}
2009-10-05 17:48:17 +04:00
return 0 ;
2011-06-25 17:24:49 +04:00
}
2009-10-05 17:48:17 +04:00
2008-12-20 13:17:10 +03:00
tuner_dbg ( " Putting tuner to sleep \n " ) ;
2011-02-04 16:42:09 +03:00
t - > standby = true ;
2008-11-25 00:21:40 +03:00
if ( analog_ops - > standby )
analog_ops - > standby ( & t - > fe ) ;
return 0 ;
}
2006-01-09 20:32:31 +03:00
2008-11-25 00:21:40 +03:00
static int tuner_s_std ( struct v4l2_subdev * sd , v4l2_std_id std )
{
struct tuner * t = to_tuner ( sd ) ;
2005-07-13 00:58:55 +04:00
2011-06-14 10:56:09 +04:00
if ( set_mode ( t , V4L2_TUNER_ANALOG_TV ) )
2008-11-25 00:21:40 +03:00
return 0 ;
2005-07-13 00:58:55 +04:00
2011-06-13 16:47:56 +04:00
t - > std = tuner_fixup_std ( t , std ) ;
if ( t - > std ! = std )
tuner_dbg ( " Fixup standard %llx to %llx \n " , std , t - > std ) ;
2011-06-14 10:56:09 +04:00
set_freq ( t , 0 ) ;
2008-11-25 00:21:40 +03:00
return 0 ;
}
2005-07-13 00:58:55 +04:00
2013-03-19 11:09:26 +04:00
static int tuner_s_frequency ( struct v4l2_subdev * sd , const struct v4l2_frequency * f )
2008-11-25 00:21:40 +03:00
{
struct tuner * t = to_tuner ( sd ) ;
2006-01-23 22:11:09 +03:00
2011-06-14 10:56:09 +04:00
if ( set_mode ( t , f - > type ) = = 0 )
set_freq ( t , f - > frequency ) ;
2008-11-25 00:21:40 +03:00
return 0 ;
}
2005-07-13 00:58:55 +04:00
2011-06-13 16:35:56 +04:00
/**
* tuner_g_frequency - Get the tuned frequency for the tuner
* @ sd : pointer to struct v4l2_subdev
* @ f : pointer to struct v4l2_frequency
*
* At return , the structure f will be filled with tuner frequency
* if the tuner matches the f - > type .
* Note : f - > type should be initialized before calling it .
* This is done by either video_ioctl2 or by the bridge driver .
*/
2008-11-25 00:21:40 +03:00
static int tuner_g_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * f )
{
struct tuner * t = to_tuner ( sd ) ;
struct dvb_tuner_ops * fe_tuner_ops = & t - > fe . ops . tuner_ops ;
2005-07-13 00:58:55 +04:00
2011-02-04 16:42:09 +03:00
if ( check_mode ( t , f - > type ) = = - EINVAL )
2008-11-25 00:21:40 +03:00
return 0 ;
2011-06-26 12:35:34 +04:00
if ( f - > type = = t - > mode & & fe_tuner_ops - > get_frequency & & ! t - > standby ) {
2008-11-25 00:21:40 +03:00
u32 abs_freq ;
fe_tuner_ops - > get_frequency ( & t - > fe , & abs_freq ) ;
f - > frequency = ( V4L2_TUNER_RADIO = = t - > mode ) ?
2009-08-01 23:48:41 +04:00
DIV_ROUND_CLOSEST ( abs_freq * 2 , 125 ) :
DIV_ROUND_CLOSEST ( abs_freq , 62500 ) ;
2011-02-04 16:42:09 +03:00
} else {
2011-06-26 12:35:34 +04:00
f - > frequency = ( V4L2_TUNER_RADIO = = f - > type ) ?
2011-02-04 16:42:09 +03:00
t - > radio_freq : t - > tv_freq ;
2008-11-25 00:21:40 +03:00
}
return 0 ;
}
2005-07-13 00:58:55 +04:00
2011-06-13 16:35:56 +04:00
/**
* tuner_g_tuner - Fill in tuner information
* @ sd : pointer to struct v4l2_subdev
* @ vt : pointer to struct v4l2_tuner
*
* At return , the structure vt will be filled with tuner information
* if the tuner matches vt - > type .
* Note : vt - > type should be initialized before calling it .
* This is done by either video_ioctl2 or by the bridge driver .
*/
2008-11-25 00:21:40 +03:00
static int tuner_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct tuner * t = to_tuner ( sd ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
struct dvb_tuner_ops * fe_tuner_ops = & t - > fe . ops . tuner_ops ;
2011-02-04 16:42:09 +03:00
if ( check_mode ( t , vt - > type ) = = - EINVAL )
2008-11-25 00:21:40 +03:00
return 0 ;
2011-06-26 12:35:34 +04:00
if ( vt - > type = = t - > mode & & analog_ops - > get_afc )
2013-04-06 11:35:27 +04:00
analog_ops - > get_afc ( & t - > fe , & vt - > afc ) ;
2013-04-06 11:41:29 +04:00
if ( vt - > type = = t - > mode & & analog_ops - > has_signal ) {
u16 signal = ( u16 ) vt - > signal ;
if ( ! analog_ops - > has_signal ( & t - > fe , & signal ) )
vt - > signal = signal ;
}
2012-05-25 19:01:40 +04:00
if ( vt - > type ! = V4L2_TUNER_RADIO ) {
2008-11-25 00:21:40 +03:00
vt - > capability | = V4L2_TUNER_CAP_NORM ;
vt - > rangelow = tv_range [ 0 ] * 16 ;
vt - > rangehigh = tv_range [ 1 ] * 16 ;
return 0 ;
}
/* radio mode */
2011-06-26 12:35:34 +04:00
if ( vt - > type = = t - > mode ) {
vt - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
if ( fe_tuner_ops - > get_status ) {
u32 tuner_status ;
fe_tuner_ops - > get_status ( & t - > fe , & tuner_status ) ;
vt - > rxsubchans =
( tuner_status & TUNER_STATUS_STEREO ) ?
V4L2_TUNER_SUB_STEREO :
V4L2_TUNER_SUB_MONO ;
}
vt - > audmode = t - > audmode ;
2005-04-17 02:20:36 +04:00
}
2011-02-04 16:42:09 +03:00
vt - > capability | = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO ;
2008-11-25 00:21:40 +03:00
vt - > rangelow = radio_range [ 0 ] * 16000 ;
vt - > rangehigh = radio_range [ 1 ] * 16000 ;
2011-02-04 16:42:09 +03:00
2008-11-25 00:21:40 +03:00
return 0 ;
}
2011-06-13 16:35:56 +04:00
/**
* tuner_s_tuner - Set the tuner ' s audio mode
* @ sd : pointer to struct v4l2_subdev
* @ vt : pointer to struct v4l2_tuner
*
* Sets the audio mode if the tuner matches vt - > type .
* Note : vt - > type should be initialized before calling it .
* This is done by either video_ioctl2 or by the bridge driver .
*/
2013-03-15 13:10:06 +04:00
static int tuner_s_tuner ( struct v4l2_subdev * sd , const struct v4l2_tuner * vt )
2008-11-25 00:21:40 +03:00
{
struct tuner * t = to_tuner ( sd ) ;
2011-06-14 10:56:09 +04:00
if ( set_mode ( t , vt - > type ) )
2008-11-25 00:21:40 +03:00
return 0 ;
2012-09-28 15:18:07 +04:00
if ( t - > mode = = V4L2_TUNER_RADIO ) {
2011-02-04 16:42:09 +03:00
t - > audmode = vt - > audmode ;
2012-09-28 15:18:07 +04:00
/*
* For radio audmode can only be mono or stereo . Map any
* other values to stereo . The actual tuner driver that is
* called in set_radio_freq can decide to limit the audmode to
* mono if only mono is supported .
*/
if ( t - > audmode ! = V4L2_TUNER_MODE_MONO & &
t - > audmode ! = V4L2_TUNER_MODE_STEREO )
t - > audmode = V4L2_TUNER_MODE_STEREO ;
}
2011-06-14 10:56:09 +04:00
set_freq ( t , 0 ) ;
2011-02-04 16:42:09 +03:00
2008-11-25 00:21:40 +03:00
return 0 ;
}
static int tuner_log_status ( struct v4l2_subdev * sd )
{
struct tuner * t = to_tuner ( sd ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-17 02:20:36 +04:00
2008-11-25 00:21:40 +03:00
if ( analog_ops - > tuner_status )
analog_ops - > tuner_status ( & t - > fe ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-05-03 13:54:51 +04:00
# ifdef CONFIG_PM_SLEEP
static int tuner_suspend ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2012-05-03 13:54:51 +04:00
struct i2c_client * c = to_i2c_client ( dev ) ;
2008-11-25 00:21:40 +03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2011-02-04 17:15:21 +03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-17 02:20:36 +04:00
2007-11-04 17:03:36 +03:00
tuner_dbg ( " suspend \n " ) ;
2011-02-04 17:15:21 +03:00
2014-08-10 04:47:19 +04:00
if ( t - > fe . ops . tuner_ops . suspend )
t - > fe . ops . tuner_ops . suspend ( & t - > fe ) ;
else if ( ! t - > standby & & analog_ops - > standby )
2011-02-04 17:15:21 +03:00
analog_ops - > standby ( & t - > fe ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-05-03 13:54:51 +04:00
static int tuner_resume ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2012-05-03 13:54:51 +04:00
struct i2c_client * c = to_i2c_client ( dev ) ;
2008-11-25 00:21:40 +03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2005-04-17 02:20:36 +04:00
2007-11-04 17:03:36 +03:00
tuner_dbg ( " resume \n " ) ;
2011-02-04 17:15:21 +03:00
2014-08-10 04:47:19 +04:00
if ( t - > fe . ops . tuner_ops . resume )
t - > fe . ops . tuner_ops . resume ( & t - > fe ) ;
else if ( ! t - > standby )
2011-06-13 16:21:56 +04:00
if ( set_mode ( t , t - > mode ) = = 0 )
2011-06-14 10:56:09 +04:00
set_freq ( t , 0 ) ;
2011-02-04 17:15:21 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-05-03 13:54:51 +04:00
# endif
2005-04-17 02:20:36 +04:00
2009-04-01 10:32:22 +04:00
static int tuner_command ( struct i2c_client * client , unsigned cmd , void * arg )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
/* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have
to handle it here .
There must be a better way of doing this . . . */
switch ( cmd ) {
case TUNER_SET_CONFIG :
return tuner_s_config ( sd , arg ) ;
}
return - ENOIOCTLCMD ;
}
2011-02-15 15:15:19 +03:00
/*
* Callback structs
*/
2008-11-25 00:21:40 +03:00
static const struct v4l2_subdev_core_ops tuner_core_ops = {
. log_status = tuner_log_status ,
2009-10-05 17:48:17 +04:00
. s_power = tuner_s_power ,
2008-11-25 00:21:40 +03:00
} ;
static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
. s_radio = tuner_s_radio ,
. g_tuner = tuner_g_tuner ,
. s_tuner = tuner_s_tuner ,
. s_frequency = tuner_s_frequency ,
. g_frequency = tuner_g_frequency ,
. s_type_addr = tuner_s_type_addr ,
. s_config = tuner_s_config ,
} ;
2014-04-28 23:53:01 +04:00
static const struct v4l2_subdev_video_ops tuner_video_ops = {
. s_std = tuner_s_std ,
} ;
2008-11-25 00:21:40 +03:00
static const struct v4l2_subdev_ops tuner_ops = {
. core = & tuner_core_ops ,
. tuner = & tuner_tuner_ops ,
2014-04-28 23:53:01 +04:00
. video = & tuner_video_ops ,
2008-11-25 00:21:40 +03:00
} ;
2011-02-15 15:15:19 +03:00
/*
* I2C structs and module init functions
*/
2005-04-17 02:20:36 +04:00
2012-05-03 13:54:51 +04:00
static const struct dev_pm_ops tuner_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( tuner_suspend , tuner_resume )
} ;
2008-05-18 22:49:40 +04:00
static const struct i2c_device_id tuner_id [ ] = {
{ " tuner " , } , /* autodetect */
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tuner_id ) ;
2010-09-15 22:36:23 +04:00
static struct i2c_driver tuner_driver = {
. driver = {
. name = " tuner " ,
2012-05-03 13:54:51 +04:00
. pm = & tuner_pm_ops ,
2010-09-15 22:36:23 +04:00
} ,
. probe = tuner_probe ,
. remove = tuner_remove ,
. command = tuner_command ,
. id_table = tuner_id ,
2005-04-17 02:20:36 +04:00
} ;
2012-02-12 13:56:32 +04:00
module_i2c_driver ( tuner_driver ) ;
2010-09-15 22:36:23 +04:00
2011-02-04 05:32:07 +03:00
MODULE_DESCRIPTION ( " device driver for various TV and TV+FM radio tuners " ) ;
MODULE_AUTHOR ( " Ralph Metzler, Gerd Knorr, Gunther Mayer " ) ;
MODULE_LICENSE ( " GPL " ) ;