2005-04-16 15:20:36 -07:00
/*
*
* i2c tv tuner chip device driver
* core core , i . e . kernel interfaces , registering and so on
*/
# 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>
2007-08-21 01:14:12 -03:00
# include <linux/videodev.h>
2005-04-16 15:20:36 -07:00
# include <media/tuner.h>
2007-08-27 21:59:08 -03:00
# include <media/tuner-types.h>
2008-11-24 18:21:40 -03:00
# include <media/v4l2-device.h>
2008-07-20 08:12:02 -03:00
# include <media/v4l2-ioctl.h>
2007-11-04 11:03:36 -03:00
# include <media/v4l2-i2c-drv-legacy.h>
2007-08-27 21:23:08 -03:00
# include "mt20xx.h"
2007-08-27 21:22:20 -03:00
# include "tda8290.h"
2007-08-27 21:23:40 -03:00
# include "tea5761.h"
2007-08-27 21:24:27 -03:00
# include "tea5767.h"
2007-10-23 15:24:06 -03:00
# include "tuner-xc2028.h"
2007-08-27 21:59:08 -03:00
# include "tuner-simple.h"
2007-10-21 20:48:48 -03:00
# include "tda9887.h"
2008-01-05 16:50:14 -03:00
# include "xc5000.h"
2005-04-16 15:20:36 -07:00
# define UNSET (-1U)
2007-11-04 11:03:36 -03:00
# define PREFIX t->i2c->driver->driver.name
2007-10-30 09:46:10 -03:00
2008-04-29 03:54:19 -03:00
/** This macro allows us to probe dynamically, avoiding static links */
2008-04-29 23:02:33 -03:00
# ifdef CONFIG_MEDIA_ATTACH
2008-04-29 03:54:19 -03: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 11:40:17 -03:00
symbol_put ( FUNCTION ) ; \
2008-04-29 03:54:19 -03: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-16 22:02:26 -03:00
struct tuner {
/* device */
struct dvb_frontend fe ;
struct i2c_client * i2c ;
2008-11-24 18:21:40 -03:00
struct v4l2_subdev sd ;
2007-12-16 22:02:26 -03:00
struct list_head list ;
unsigned int using_v4l2 : 1 ;
/* keep track of the current settings */
v4l2_std_id std ;
unsigned int tv_freq ;
unsigned int radio_freq ;
unsigned int audmode ;
unsigned int mode ;
unsigned int mode_mask ; /* Combination of allowable modes */
unsigned int type ; /* chip type id */
unsigned int config ;
2008-05-26 16:08:40 +02:00
const char * name ;
2007-12-16 22:02:26 -03:00
} ;
2008-11-24 18:21:40 -03:00
static inline struct tuner * to_tuner ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct tuner , sd ) ;
}
2005-04-16 15:20:36 -07:00
/* standard i2c insmod options */
static unsigned short normal_i2c [ ] = {
2008-04-29 21:38:46 -03:00
# if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
2007-04-08 01:09:11 -03:00
0x10 ,
# endif
2005-11-08 21:37:48 -08:00
0x42 , 0x43 , 0x4a , 0x4b , /* tda8290 */
2005-06-23 22:05:13 -07:00
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 ,
0x68 , 0x69 , 0x6a , 0x6b , 0x6c , 0x6d , 0x6e , 0x6f ,
2005-04-16 15:20:36 -07:00
I2C_CLIENT_END
} ;
2005-07-12 13:58:55 -07:00
2005-04-16 15:20:36 -07:00
I2C_CLIENT_INSMOD ;
/* insmod options used at init time => read/only */
2008-04-22 14:41:48 -03:00
static unsigned int addr ;
static unsigned int no_autodetect ;
static unsigned int show_i2c ;
2005-07-31 22:34:43 -07:00
2005-04-16 15:20:36 -07:00
/* insmod options used at runtime => read/write */
2007-12-09 02:26:48 -03:00
static int tuner_debug ;
# 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 )
/* ------------------------------------------------------------------------ */
2005-04-16 15:20:36 -07:00
2005-07-12 13:58:55 -07:00
static unsigned int tv_range [ 2 ] = { 44 , 958 } ;
2005-04-16 15:20:36 -07:00
static unsigned int radio_range [ 2 ] = { 65 , 108 } ;
2006-01-09 15:25:27 -02:00
static char pal [ ] = " -- " ;
static char secam [ ] = " -- " ;
static char ntsc [ ] = " - " ;
2006-01-11 19:01:01 -02:00
2006-01-09 15:25:27 -02:00
module_param ( addr , int , 0444 ) ;
module_param ( no_autodetect , int , 0444 ) ;
module_param ( show_i2c , int , 0444 ) ;
2006-01-11 19:01:01 -02:00
module_param_named ( debug , tuner_debug , int , 0644 ) ;
2006-01-09 15:25:27 -02:00
module_param_string ( pal , pal , sizeof ( pal ) , 0644 ) ;
module_param_string ( secam , secam , sizeof ( secam ) , 0644 ) ;
module_param_string ( ntsc , ntsc , sizeof ( ntsc ) , 0644 ) ;
2005-07-12 13:58:55 -07:00
module_param_array ( tv_range , int , NULL , 0644 ) ;
2005-04-16 15:20:36 -07:00
module_param_array ( radio_range , int , NULL , 0644 ) ;
MODULE_DESCRIPTION ( " device driver for various TV and TV+FM radio tuners " ) ;
MODULE_AUTHOR ( " Ralph Metzler, Gerd Knorr, Gunther Mayer " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* ---------------------------------------------------------------------- */
2007-12-08 17:06:30 -03:00
static void fe_set_params ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2007-08-21 01:25:48 -03:00
{
2007-10-21 19:39:50 -03:00
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
struct tuner * t = fe - > analog_demod_priv ;
2007-08-21 01:25:48 -03: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 17:06:30 -03:00
fe_tuner_ops - > set_analog_params ( fe , params ) ;
2007-08-21 01:25:48 -03:00
}
2007-10-21 19:39:50 -03:00
static void fe_standby ( struct dvb_frontend * fe )
2007-08-21 01:25:48 -03:00
{
2007-10-21 19:39:50 -03:00
struct dvb_tuner_ops * fe_tuner_ops = & fe - > ops . tuner_ops ;
2007-08-21 01:25:48 -03:00
if ( fe_tuner_ops - > sleep )
2007-10-21 19:39:50 -03:00
fe_tuner_ops - > sleep ( fe ) ;
2007-08-21 01:25:48 -03:00
}
2007-10-21 19:39:50 -03:00
static int fe_has_signal ( struct dvb_frontend * fe )
2007-08-31 17:38:02 -03:00
{
2007-10-14 18:11:53 -03:00
u16 strength = 0 ;
2007-08-31 17:38:02 -03:00
2007-10-21 19:39:50 -03:00
if ( fe - > ops . tuner_ops . get_rf_strength )
fe - > ops . tuner_ops . get_rf_strength ( fe , & strength ) ;
2007-08-31 17:38:02 -03:00
return strength ;
}
2007-12-16 19: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-21 19:39:50 -03:00
static void tuner_status ( struct dvb_frontend * fe ) ;
2007-10-21 13:40:56 -03:00
2008-11-24 18:21:40 -03:00
static struct analog_demod_ops tuner_analog_ops = {
2007-12-08 17:06:30 -03:00
. set_params = fe_set_params ,
2007-10-21 13:40:56 -03:00
. standby = fe_standby ,
. has_signal = fe_has_signal ,
2007-12-16 19:27:23 -03:00
. set_config = fe_set_config ,
2007-10-21 13:40:56 -03:00
. tuner_status = tuner_status
} ;
2005-06-23 22:05:07 -07:00
/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
2005-04-16 15:20:36 -07:00
static void set_tv_freq ( struct i2c_client * c , unsigned int freq )
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2007-12-21 11:18:32 -03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-16 15:20:36 -07:00
2007-12-08 17:06:30 -03:00
struct analog_parameters params = {
. mode = t - > mode ,
. audmode = t - > audmode ,
. std = t - > std
} ;
2005-04-16 15:20:36 -07:00
if ( t - > type = = UNSET ) {
2005-07-12 13:58:55 -07:00
tuner_warn ( " tuner type not set \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2007-12-21 11:18:32 -03:00
if ( NULL = = analog_ops - > set_params ) {
2005-07-12 13:58:55 -07:00
tuner_warn ( " Tuner has no way to set tv freq \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2005-07-12 13:58:55 -07:00
if ( freq < tv_range [ 0 ] * 16 | | freq > tv_range [ 1 ] * 16 ) {
tuner_dbg ( " TV freq (%d.%02d) out of range (%d-%d) \n " ,
freq / 16 , freq % 16 * 100 / 16 , tv_range [ 0 ] ,
tv_range [ 1 ] ) ;
2006-01-15 15:04:52 -02:00
/* 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 ;
2005-04-16 15:20:36 -07:00
}
2007-12-08 17:06:30 -03:00
params . frequency = freq ;
2007-12-21 11:18:32 -03:00
analog_ops - > set_params ( & t - > fe , & params ) ;
2005-04-16 15:20:36 -07:00
}
static void set_radio_freq ( struct i2c_client * c , unsigned int freq )
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2007-12-21 11:18:32 -03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-16 15:20:36 -07:00
2007-12-08 17:06:30 -03:00
struct analog_parameters params = {
. mode = t - > mode ,
. audmode = t - > audmode ,
. std = t - > std
} ;
2005-04-16 15:20:36 -07:00
if ( t - > type = = UNSET ) {
2005-07-12 13:58:55 -07:00
tuner_warn ( " tuner type not set \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2008-01-05 16:37:04 -03:00
if ( NULL = = analog_ops - > set_params ) {
2005-07-12 13:58:55 -07:00
tuner_warn ( " tuner has no way to set radio frequency \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2006-01-15 15:04:52 -02:00
if ( freq < radio_range [ 0 ] * 16000 | | freq > radio_range [ 1 ] * 16000 ) {
2005-07-12 13:58:55 -07:00
tuner_dbg ( " radio freq (%d.%02d) out of range (%d-%d) \n " ,
freq / 16000 , freq % 16000 * 100 / 16000 ,
radio_range [ 0 ] , radio_range [ 1 ] ) ;
2006-01-15 15:04:52 -02:00
/* 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 ;
2005-04-16 15:20:36 -07:00
}
2007-12-08 17:06:30 -03:00
params . frequency = freq ;
2005-06-28 20:45:21 -07:00
2007-12-21 11:18:32 -03:00
analog_ops - > set_params ( & t - > fe , & params ) ;
2005-04-16 15:20:36 -07:00
}
static void set_freq ( struct i2c_client * c , unsigned long freq )
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2005-04-16 15:20:36 -07:00
switch ( t - > mode ) {
case V4L2_TUNER_RADIO :
tuner_dbg ( " radio freq set to %lu.%02lu \n " ,
2005-07-12 13:58:55 -07:00
freq / 16000 , freq % 16000 * 100 / 16000 ) ;
set_radio_freq ( c , freq ) ;
2006-01-15 15:04:52 -02:00
t - > radio_freq = freq ;
2005-04-16 15:20:36 -07:00
break ;
case V4L2_TUNER_ANALOG_TV :
case V4L2_TUNER_DIGITAL_TV :
tuner_dbg ( " tv freq set to %lu.%02lu \n " ,
2005-07-12 13:58:55 -07:00
freq / 16 , freq % 16 * 100 / 16 ) ;
2005-04-16 15:20:36 -07:00
set_tv_freq ( c , freq ) ;
2006-01-15 15:04:52 -02:00
t - > tv_freq = freq ;
2005-04-16 15:20:36 -07:00
break ;
2007-10-02 11:57:03 -03:00
default :
tuner_dbg ( " freq set: unknown mode: 0x%04x! \n " , t - > mode ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-08-28 17:20:42 -03:00
static void tuner_i2c_address_check ( struct tuner * t )
{
if ( ( t - > type = = UNSET | | t - > type = = TUNER_ABSENT ) | |
2007-09-14 05:13:54 -03:00
( ( t - > i2c - > addr < 0x64 ) | | ( t - > i2c - > addr > 0x6f ) ) )
2007-08-28 17:20:42 -03:00
return ;
2008-01-21 12:01:34 -03:00
/* We already know that the XC5000 can only be located at
* i2c address 0x61 , 0x62 , 0x63 or 0x64 */
if ( ( t - > type = = TUNER_XC5000 ) & &
( ( t - > i2c - > addr < = 0x64 ) ) & & ( t - > i2c - > addr > = 0x61 ) )
return ;
2007-08-28 17:20:42 -03:00
tuner_warn ( " ====================== WARNING! ====================== \n " ) ;
tuner_warn ( " Support for tuners in i2c address range 0x64 thru 0x6f \n " ) ;
tuner_warn ( " will soon be dropped. This message indicates that your \n " ) ;
tuner_warn ( " hardware has a %s tuner at i2c address 0x%02x. \n " ,
2008-05-26 16:08:40 +02:00
t - > name , t - > i2c - > addr ) ;
2007-08-28 17:20:42 -03:00
tuner_warn ( " To ensure continued support for your device, please \n " ) ;
tuner_warn ( " send a copy of this message, along with full dmesg \n " ) ;
tuner_warn ( " output to v4l-dvb-maintainer@linuxtv.org \n " ) ;
tuner_warn ( " Please use subject line: \" obsolete tuner i2c address. \" \n " ) ;
tuner_warn ( " driver: %s, addr: 0x%02x, type: %d (%s) \n " ,
2008-05-26 16:08:40 +02:00
t - > i2c - > adapter - > name , t - > i2c - > addr , t - > type , t - > name ) ;
2007-08-28 17:20:42 -03:00
tuner_warn ( " ====================== WARNING! ====================== \n " ) ;
}
2008-01-05 16:50:14 -03:00
static struct xc5000_config xc5000_cfg ;
2005-07-12 13:58:55 -07:00
static void set_type ( struct i2c_client * c , unsigned int type ,
2007-04-27 12:31:12 -03:00
unsigned int new_mode_mask , unsigned int new_config ,
2008-09-12 13:31:45 -03:00
int ( * tuner_callback ) ( void * dev , int component , int cmd , int arg ) )
2005-04-16 15:20:36 -07:00
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2007-08-21 01:25:48 -03:00
struct dvb_tuner_ops * fe_tuner_ops = & t - > fe . ops . tuner_ops ;
2007-12-21 11:18:32 -03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-06-28 20:45:21 -07:00
unsigned char buffer [ 4 ] ;
2005-04-16 15:20:36 -07:00
2005-07-12 13:58:55 -07:00
if ( type = = UNSET | | type = = TUNER_ABSENT ) {
tuner_dbg ( " tuner 0x%02x: Tuner type absent \n " , c - > addr ) ;
2005-04-16 15:20:36 -07:00
return ;
2005-07-12 13:58:55 -07:00
}
2007-04-27 12:31:18 -03:00
t - > type = type ;
t - > config = new_config ;
if ( tuner_callback ! = NULL ) {
tuner_dbg ( " defining GPIO callback \n " ) ;
2008-09-12 13:31:45 -03:00
t - > fe . callback = tuner_callback ;
2007-04-27 12:31:18 -03:00
}
2007-10-29 11:33:18 -03:00
if ( t - > mode = = T_UNINITIALIZED ) {
2005-07-12 13:58:55 -07:00
tuner_dbg ( " tuner 0x%02x: called during i2c_client register by adapter's attach_inform \n " , c - > addr ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2005-06-23 22:05:07 -07:00
2007-05-29 22:54:06 -03:00
/* discard private data, in case set_type() was previously called */
2008-04-29 03:54:19 -03:00
tuner_detach ( & t - > fe ) ;
t - > fe . analog_demod_priv = NULL ;
2007-06-04 14:40:27 -03:00
2005-04-16 15:20:36 -07:00
switch ( t - > type ) {
case TUNER_MT2032 :
2008-04-30 15:29:57 -03:00
if ( ! dvb_attach ( microtune_attach ,
& t - > fe , t - > i2c - > adapter , t - > i2c - > addr ) )
goto attach_failed ;
2005-04-16 15:20:36 -07:00
break ;
case TUNER_PHILIPS_TDA8290 :
2007-10-22 09:56:38 -03:00
{
2008-04-30 15:29:57 -03:00
struct tda829x_config cfg = {
. lna_cfg = t - > config ,
} ;
if ( ! dvb_attach ( tda829x_attach , & t - > fe , t - > i2c - > adapter ,
t - > i2c - > addr , & cfg ) )
goto attach_failed ;
2007-10-22 09:56:38 -03:00
break ;
}
2005-06-28 20:45:21 -07:00
case TUNER_TEA5767 :
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( tea5767_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2005-07-12 13:58:55 -07:00
t - > mode_mask = T_RADIO ;
2005-06-28 20:45:21 -07:00
break ;
2007-04-08 01:09:11 -03:00
case TUNER_TEA5761 :
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( tea5761_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2007-04-08 01:09:11 -03:00
t - > mode_mask = T_RADIO ;
break ;
2005-06-28 20:45:21 -07:00
case TUNER_PHILIPS_FMD1216ME_MK3 :
buffer [ 0 ] = 0x0b ;
buffer [ 1 ] = 0xdc ;
buffer [ 2 ] = 0x9c ;
buffer [ 3 ] = 0x60 ;
2005-07-12 13:58:55 -07:00
i2c_master_send ( c , buffer , 4 ) ;
2005-06-28 20:45:21 -07:00
mdelay ( 1 ) ;
buffer [ 2 ] = 0x86 ;
buffer [ 3 ] = 0x54 ;
2005-07-12 13:58:55 -07:00
i2c_master_send ( c , buffer , 4 ) ;
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( simple_tuner_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr , t - > type ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2005-06-28 20:45:21 -07:00
break ;
2005-11-08 21:36:31 -08:00
case TUNER_PHILIPS_TD1316 :
buffer [ 0 ] = 0x0b ;
buffer [ 1 ] = 0xdc ;
buffer [ 2 ] = 0x86 ;
buffer [ 3 ] = 0xa4 ;
2008-04-29 03:54:19 -03: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 11:29:34 -03:00
goto attach_failed ;
2006-01-23 17:11:09 -02:00
break ;
2007-10-29 11:33:18 -03:00
case TUNER_XC2028 :
{
2007-11-16 07:46:14 -03:00
struct xc2028_config cfg = {
. i2c_adap = t - > i2c - > adapter ,
. i2c_addr = t - > i2c - > addr ,
} ;
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( xc2028_attach , & t - > fe , & cfg ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2007-10-29 11:33:18 -03:00
break ;
}
2006-06-23 16:13:56 -03:00
case TUNER_TDA9887 :
2008-04-30 15:29:57 -03:00
if ( ! dvb_attach ( tda9887_attach ,
& t - > fe , t - > i2c - > adapter , t - > i2c - > addr ) )
goto attach_failed ;
2006-06-23 16:13:56 -03:00
break ;
2008-01-05 16:50:14 -03:00
case TUNER_XC5000 :
2008-04-26 11:29:34 -03:00
{
struct dvb_tuner_ops * xc_tuner_ops ;
2008-01-05 16:50:14 -03:00
xc5000_cfg . i2c_address = t - > i2c - > addr ;
xc5000_cfg . if_khz = 5380 ;
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( xc5000_attach ,
2008-09-06 14:56:58 -03:00
& t - > fe , t - > i2c - > adapter , & xc5000_cfg ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2008-01-05 16:50:14 -03:00
xc_tuner_ops = & t - > fe . ops . tuner_ops ;
2008-04-26 11:29:34 -03:00
if ( xc_tuner_ops - > init )
2008-01-05 16:50:14 -03:00
xc_tuner_ops - > init ( & t - > fe ) ;
break ;
2008-04-26 11:29:34 -03:00
}
2005-04-16 15:20:36 -07:00
default :
2008-04-29 03:54:19 -03:00
if ( ! dvb_attach ( simple_tuner_attach , & t - > fe ,
t - > i2c - > adapter , t - > i2c - > addr , t - > type ) )
2008-04-26 11:29:34 -03:00
goto attach_failed ;
2005-04-16 15:20:36 -07:00
break ;
}
2005-07-12 13:58:55 -07:00
2007-12-21 11:18:32 -03:00
if ( ( NULL = = analog_ops - > set_params ) & &
2007-10-21 13:40:56 -03:00
( fe_tuner_ops - > set_analog_params ) ) {
2008-04-29 03:54:19 -03:00
2008-05-26 16:08:40 +02:00
t - > name = fe_tuner_ops - > info . name ;
2007-08-21 01:25:48 -03:00
2007-10-21 19:39:50 -03:00
t - > fe . analog_demod_priv = t ;
2008-11-24 18:21:40 -03:00
memcpy ( analog_ops , & tuner_analog_ops ,
2007-12-21 11:18:32 -03:00
sizeof ( struct analog_demod_ops ) ) ;
2008-04-29 03:54:19 -03:00
2007-12-09 13:52:51 -03:00
} else {
2008-05-26 16:08:40 +02:00
t - > name = analog_ops - > info . name ;
2007-08-21 01:25:48 -03:00
}
2008-05-26 16:08:40 +02:00
tuner_dbg ( " type set to %s \n " , t - > name ) ;
2007-08-21 01:25:48 -03:00
2005-07-12 13:58:55 -07:00
if ( t - > mode_mask = = T_UNINITIALIZED )
t - > mode_mask = new_mode_mask ;
2008-04-26 11:29:34 -03:00
/* xc2028/3028 and xc5000 requires a firmware to be set-up later
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 ( t - > type ! = TUNER_XC2028 & & t - > type ! = TUNER_XC5000 )
set_freq ( c , ( V4L2_TUNER_RADIO = = t - > mode ) ?
t - > radio_freq : t - > tv_freq ) ;
2005-07-12 13:58:55 -07:00
tuner_dbg ( " %s %s I2C addr 0x%02x with type %d used for 0x%02x \n " ,
2005-11-26 20:43:39 +01:00
c - > adapter - > name , c - > driver - > driver . name , c - > addr < < 1 , type ,
2005-07-12 13:58:55 -07:00
t - > mode_mask ) ;
2007-08-28 17:20:42 -03:00
tuner_i2c_address_check ( t ) ;
2008-04-26 11:29:34 -03:00
return ;
attach_failed :
tuner_dbg ( " Tuner attach for type = %d failed. \n " , t - > type ) ;
t - > type = TUNER_ABSENT ;
t - > mode_mask = T_UNINITIALIZED ;
return ;
2005-04-16 15:20:36 -07:00
}
2005-07-12 13:58:55 -07:00
/*
* This function apply tuner config to tuner specified
* by tun_setup structure . I addr is unset , then admin status
* and tun addr status is more precise then current status ,
* it ' s applied . Otherwise status and type are applied only to
* tuner with exactly the same addr .
*/
static void set_addr ( struct i2c_client * c , struct tuner_setup * tun_setup )
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2005-07-12 13:58:55 -07:00
2007-04-27 12:31:12 -03:00
if ( ( t - > type = = UNSET & & ( ( tun_setup - > addr = = ADDR_UNSET ) & &
( t - > mode_mask & tun_setup - > mode_mask ) ) ) | |
( tun_setup - > addr = = c - > addr ) ) {
set_type ( c , tun_setup - > type , tun_setup - > mode_mask ,
2007-04-27 12:31:17 -03:00
tun_setup - > config , tun_setup - > tuner_callback ) ;
2008-04-26 11:29:34 -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 ) ;
2005-07-12 13:58:55 -07:00
}
2005-06-23 22:05:07 -07:00
2005-07-12 13:58:55 -07:00
static inline int check_mode ( struct tuner * t , char * cmd )
2005-06-23 22:05:07 -07:00
{
2005-09-09 13:03:37 -07:00
if ( ( 1 < < t - > mode & t - > mode_mask ) = = 0 ) {
2008-05-26 14:03:02 -03:00
return - EINVAL ;
2005-09-09 13:03:37 -07:00
}
switch ( t - > mode ) {
case V4L2_TUNER_RADIO :
tuner_dbg ( " Cmd %s accepted for radio \n " , cmd ) ;
break ;
case V4L2_TUNER_ANALOG_TV :
tuner_dbg ( " Cmd %s accepted for analog TV \n " , cmd ) ;
break ;
case V4L2_TUNER_DIGITAL_TV :
tuner_dbg ( " Cmd %s accepted for digital TV \n " , cmd ) ;
break ;
2005-06-23 22:05:07 -07:00
}
2005-09-09 13:03:37 -07:00
return 0 ;
2005-06-23 22:05:07 -07:00
}
2005-07-12 13:58:55 -07:00
/* get more precise norm info from insmod option */
2005-04-16 15:20:36 -07:00
static int tuner_fixup_std ( struct tuner * t )
{
if ( ( t - > std & V4L2_STD_PAL ) = = V4L2_STD_PAL ) {
switch ( pal [ 0 ] ) {
2006-12-11 15:51:43 -03:00
case ' 6 ' :
tuner_dbg ( " insmod fixup: PAL => PAL-60 \n " ) ;
t - > std = V4L2_STD_PAL_60 ;
break ;
2005-04-16 15:20:36 -07:00
case ' b ' :
case ' B ' :
case ' g ' :
case ' G ' :
2005-07-12 13:58:55 -07:00
tuner_dbg ( " insmod fixup: PAL => PAL-BG \n " ) ;
2005-04-16 15:20:36 -07:00
t - > std = V4L2_STD_PAL_BG ;
break ;
case ' i ' :
case ' I ' :
2005-07-12 13:58:55 -07:00
tuner_dbg ( " insmod fixup: PAL => PAL-I \n " ) ;
2005-04-16 15:20:36 -07:00
t - > std = V4L2_STD_PAL_I ;
break ;
case ' d ' :
case ' D ' :
case ' k ' :
case ' K ' :
2005-07-12 13:58:55 -07:00
tuner_dbg ( " insmod fixup: PAL => PAL-DK \n " ) ;
2005-04-16 15:20:36 -07:00
t - > std = V4L2_STD_PAL_DK ;
break ;
2005-07-12 13:58:55 -07:00
case ' M ' :
case ' m ' :
tuner_dbg ( " insmod fixup: PAL => PAL-M \n " ) ;
t - > std = V4L2_STD_PAL_M ;
break ;
case ' N ' :
case ' n ' :
2006-01-09 15:25:27 -02:00
if ( pal [ 1 ] = = ' c ' | | pal [ 1 ] = = ' C ' ) {
tuner_dbg ( " insmod fixup: PAL => PAL-Nc \n " ) ;
t - > std = V4L2_STD_PAL_Nc ;
} else {
tuner_dbg ( " insmod fixup: PAL => PAL-N \n " ) ;
t - > std = V4L2_STD_PAL_N ;
}
2005-07-12 13:58:55 -07:00
break ;
2005-09-09 13:03:59 -07:00
case ' - ' :
/* default parameter, do nothing */
break ;
default :
tuner_warn ( " pal= argument not recognised \n " ) ;
break ;
2005-04-16 15:20:36 -07:00
}
}
2005-07-12 13:58:55 -07:00
if ( ( t - > std & V4L2_STD_SECAM ) = = V4L2_STD_SECAM ) {
switch ( secam [ 0 ] ) {
2006-01-09 15:25:27 -02:00
case ' b ' :
case ' B ' :
case ' g ' :
case ' G ' :
case ' h ' :
case ' H ' :
tuner_dbg ( " insmod fixup: SECAM => SECAM-BGH \n " ) ;
t - > std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H ;
break ;
2005-07-12 13:58:55 -07:00
case ' d ' :
case ' D ' :
case ' k ' :
case ' K ' :
tuner_dbg ( " insmod fixup: SECAM => SECAM-DK \n " ) ;
t - > std = V4L2_STD_SECAM_DK ;
break ;
case ' l ' :
case ' L ' :
2005-11-13 16:07:48 -08:00
if ( ( secam [ 1 ] = = ' C ' ) | | ( secam [ 1 ] = = ' c ' ) ) {
tuner_dbg ( " insmod fixup: SECAM => SECAM-L' \n " ) ;
t - > std = V4L2_STD_SECAM_LC ;
} else {
tuner_dbg ( " insmod fixup: SECAM => SECAM-L \n " ) ;
t - > std = V4L2_STD_SECAM_L ;
}
2005-07-12 13:58:55 -07:00
break ;
2005-09-09 13:03:59 -07:00
case ' - ' :
/* default parameter, do nothing */
break ;
default :
tuner_warn ( " secam= argument not recognised \n " ) ;
break ;
2005-07-12 13:58:55 -07:00
}
}
2006-01-09 15:25:27 -02:00
if ( ( t - > std & V4L2_STD_NTSC ) = = V4L2_STD_NTSC ) {
switch ( ntsc [ 0 ] ) {
case ' m ' :
case ' M ' :
tuner_dbg ( " insmod fixup: NTSC => NTSC-M \n " ) ;
t - > std = V4L2_STD_NTSC_M ;
break ;
case ' j ' :
case ' J ' :
tuner_dbg ( " insmod fixup: NTSC => NTSC_M_JP \n " ) ;
t - > std = V4L2_STD_NTSC_M_JP ;
break ;
2006-02-07 06:48:40 -02:00
case ' k ' :
case ' K ' :
tuner_dbg ( " insmod fixup: NTSC => NTSC_M_KR \n " ) ;
t - > std = V4L2_STD_NTSC_M_KR ;
break ;
2006-01-09 15:25:27 -02:00
case ' - ' :
/* default parameter, do nothing */
break ;
default :
tuner_info ( " ntsc= argument not recognised \n " ) ;
break ;
}
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-10-21 19:39:50 -03:00
static void tuner_status ( struct dvb_frontend * fe )
2006-01-09 15:25:27 -02:00
{
2007-10-21 19:39:50 -03:00
struct tuner * t = fe - > analog_demod_priv ;
2006-01-09 15:25:27 -02:00
unsigned long freq , freq_fraction ;
2008-04-29 03:54:19 -03: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 15:25:27 -02:00
const char * p ;
switch ( t - > mode ) {
case V4L2_TUNER_RADIO : p = " radio " ; break ;
case V4L2_TUNER_ANALOG_TV : p = " analog TV " ; break ;
case V4L2_TUNER_DIGITAL_TV : p = " digital TV " ; break ;
default : p = " undefined " ; break ;
}
if ( t - > mode = = V4L2_TUNER_RADIO ) {
2006-01-15 15:04:52 -02:00
freq = t - > radio_freq / 16000 ;
freq_fraction = ( t - > radio_freq % 16000 ) * 100 / 16000 ;
2006-01-09 15:25:27 -02:00
} else {
2006-01-15 15:04:52 -02:00
freq = t - > tv_freq / 16 ;
freq_fraction = ( t - > tv_freq % 16 ) * 100 / 16 ;
2006-01-09 15:25:27 -02:00
}
tuner_info ( " Tuner mode: %s \n " , p ) ;
tuner_info ( " Frequency: %lu.%02lu MHz \n " , freq , freq_fraction ) ;
2006-03-25 15:53:38 -03:00
tuner_info ( " Standard: 0x%08lx \n " , ( unsigned long ) t - > std ) ;
2006-01-23 17:11:09 -02:00
if ( t - > mode ! = V4L2_TUNER_RADIO )
return ;
2007-08-21 01:25:48 -03: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 " ) ;
}
2007-12-21 11:18:32 -03:00
if ( analog_ops - > has_signal )
tuner_info ( " Signal strength: %d \n " ,
analog_ops - > has_signal ( fe ) ) ;
if ( analog_ops - > is_stereo )
tuner_info ( " Stereo: %s \n " ,
analog_ops - > is_stereo ( fe ) ? " yes " : " no " ) ;
2006-01-09 15:25:27 -02:00
}
2006-01-23 17:11:09 -02:00
2005-04-16 15:20:36 -07:00
/* ---------------------------------------------------------------------- */
2005-07-12 13:58:55 -07:00
/*
* Switch tuner to other mode . If tuner support both tv and radio ,
* set another frequency to some value ( This is needed for some pal
* tuners to avoid locking ) . Otherwise , just put second tuner in
* standby mode .
*/
static inline int set_mode ( struct i2c_client * client , struct tuner * t , int mode , char * cmd )
{
2007-12-21 11:18:32 -03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2007-10-21 13:40:56 -03:00
2005-11-08 21:37:43 -08:00
if ( mode = = t - > mode )
return 0 ;
t - > mode = mode ;
2008-05-26 14:03:02 -03:00
if ( check_mode ( t , cmd ) = = - EINVAL ) {
2008-12-20 07:17:10 -03:00
tuner_dbg ( " Tuner doesn't support this mode. "
" Putting tuner to sleep \n " ) ;
2005-11-08 21:37:43 -08:00
t - > mode = T_STANDBY ;
2007-12-21 11:18:32 -03:00
if ( analog_ops - > standby )
analog_ops - > standby ( & t - > fe ) ;
2008-05-26 14:03:02 -03:00
return - EINVAL ;
2005-11-08 21:37:43 -08:00
}
return 0 ;
2005-07-12 13:58:55 -07:00
}
# define switch_v4l2() if (!t->using_v4l2) \
2005-11-08 21:37:43 -08:00
tuner_dbg ( " switching to v4l2 \n " ) ; \
t - > using_v4l2 = 1 ;
2005-07-12 13:58:55 -07:00
static inline int check_v4l2 ( struct tuner * t )
{
2006-04-01 15:27:52 -03:00
/* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
TV , v4l1 for radio ) , until that is fixed this code is disabled .
Otherwise the radio ( v4l1 ) wouldn ' t tune after using the TV ( v4l2 )
first . */
2005-07-12 13:58:55 -07:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2008-11-24 18:21:40 -03:00
static int tuner_s_type_addr ( struct v4l2_subdev * sd , struct tuner_setup * type )
2005-04-16 15:20:36 -07:00
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
tuner_dbg ( " Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x \n " ,
type - > type ,
type - > addr ,
type - > mode_mask ,
type - > config ) ;
set_addr ( client , type ) ;
return 0 ;
}
static int tuner_s_radio ( struct v4l2_subdev * sd )
{
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
if ( set_mode ( client , t , V4L2_TUNER_RADIO , " AUDC_SET_RADIO " )
= = - EINVAL )
return 0 ;
if ( t - > radio_freq )
set_freq ( client , t - > radio_freq ) ;
return 0 ;
}
static int tuner_s_standby ( struct v4l2_subdev * sd , u32 standby )
{
struct tuner * t = to_tuner ( sd ) ;
2007-12-21 11:18:32 -03:00
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-16 15:20:36 -07:00
2008-12-20 07:17:10 -03:00
tuner_dbg ( " Putting tuner to sleep \n " ) ;
2008-11-24 18:21:40 -03:00
if ( check_mode ( t , " TUNER_SET_STANDBY " ) = = - EINVAL )
return 0 ;
t - > mode = T_STANDBY ;
if ( analog_ops - > standby )
analog_ops - > standby ( & t - > fe ) ;
return 0 ;
}
2006-01-09 15:32:31 -02:00
2008-04-15 18:11:50 -03:00
# ifdef CONFIG_VIDEO_ALLOW_V4L1
2008-12-30 07:04:34 -03:00
static long tuner_ioctl ( struct v4l2_subdev * sd , unsigned int cmd , void * arg )
2008-11-24 18:21:40 -03:00
{
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
struct dvb_tuner_ops * fe_tuner_ops = & t - > fe . ops . tuner_ops ;
switch ( cmd ) {
2005-07-31 22:34:43 -07:00
case VIDIOCSAUDIO :
2008-05-26 14:03:02 -03:00
if ( check_mode ( t , " VIDIOCSAUDIO " ) = = - EINVAL )
2005-07-31 22:34:43 -07:00
return 0 ;
2008-05-11 19:58:59 -03:00
if ( check_v4l2 ( t ) = = - EINVAL )
2005-07-31 22:34:43 -07:00
return 0 ;
/* Should be implemented, since bttv calls it */
tuner_dbg ( " VIDIOCSAUDIO not implemented. \n " ) ;
2005-07-12 13:58:55 -07:00
break ;
2005-04-16 15:20:36 -07:00
case VIDIOCSCHAN :
2005-07-12 13:58:55 -07:00
{
static const v4l2_std_id map [ ] = {
[ VIDEO_MODE_PAL ] = V4L2_STD_PAL ,
[ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M ,
[ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM ,
[ 4 /* bttv */ ] = V4L2_STD_PAL_M ,
[ 5 /* bttv */ ] = V4L2_STD_PAL_N ,
[ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP ,
} ;
struct video_channel * vc = arg ;
2008-05-11 19:58:59 -03:00
if ( check_v4l2 ( t ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
2008-05-26 14:03:02 -03:00
if ( set_mode ( client , t , V4L2_TUNER_ANALOG_TV , " VIDIOCSCHAN " ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
if ( vc - > norm < ARRAY_SIZE ( map ) )
t - > std = map [ vc - > norm ] ;
tuner_fixup_std ( t ) ;
2006-01-15 15:04:52 -02:00
if ( t - > tv_freq )
set_tv_freq ( client , t - > tv_freq ) ;
2005-07-12 13:58:55 -07:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
case VIDIOCSFREQ :
2005-07-12 13:58:55 -07:00
{
unsigned long * v = arg ;
2005-04-16 15:20:36 -07:00
2008-05-26 14:03:02 -03:00
if ( check_mode ( t , " VIDIOCSFREQ " ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
2008-05-11 19:58:59 -03:00
if ( check_v4l2 ( t ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
set_freq ( client , * v ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
case VIDIOCGTUNER :
2005-07-12 13:58:55 -07:00
{
struct video_tuner * vt = arg ;
2008-05-26 14:03:02 -03:00
if ( check_mode ( t , " VIDIOCGTUNER " ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
2008-05-11 19:58:59 -03:00
if ( check_v4l2 ( t ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
if ( V4L2_TUNER_RADIO = = t - > mode ) {
2007-08-21 01:25:48 -03:00
if ( fe_tuner_ops - > get_status ) {
u32 tuner_status ;
fe_tuner_ops - > get_status ( & t - > fe , & tuner_status ) ;
2007-08-31 17:38:02 -03:00
if ( tuner_status & TUNER_STATUS_STEREO )
vt - > flags | = VIDEO_TUNER_STEREO_ON ;
else
vt - > flags & = ~ VIDEO_TUNER_STEREO_ON ;
2007-08-21 01:25:48 -03:00
} else {
2007-12-21 11:18:32 -03:00
if ( analog_ops - > is_stereo ) {
if ( analog_ops - > is_stereo ( & t - > fe ) )
2007-08-21 01:25:48 -03:00
vt - > flags | =
VIDEO_TUNER_STEREO_ON ;
else
vt - > flags & =
~ VIDEO_TUNER_STEREO_ON ;
}
2005-07-12 13:58:55 -07:00
}
2007-12-21 11:18:32 -03:00
if ( analog_ops - > has_signal )
vt - > signal =
analog_ops - > has_signal ( & t - > fe ) ;
2007-08-31 17:38:02 -03:00
2005-07-12 13:58:55 -07:00
vt - > flags | = VIDEO_TUNER_LOW ; /* Allow freqs at 62.5 Hz */
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
vt - > rangelow = radio_range [ 0 ] * 16000 ;
vt - > rangehigh = radio_range [ 1 ] * 16000 ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
} else {
vt - > rangelow = tv_range [ 0 ] * 16 ;
vt - > rangehigh = tv_range [ 1 ] * 16 ;
}
2005-06-23 22:05:07 -07:00
2005-07-12 13:58:55 -07:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
case VIDIOCGAUDIO :
2005-07-12 13:58:55 -07:00
{
struct video_audio * va = arg ;
2008-05-26 14:03:02 -03:00
if ( check_mode ( t , " VIDIOCGAUDIO " ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
2008-05-11 19:58:59 -03:00
if ( check_v4l2 ( t ) = = - EINVAL )
2005-07-12 13:58:55 -07:00
return 0 ;
2007-08-21 01:25:48 -03:00
if ( V4L2_TUNER_RADIO = = t - > mode ) {
if ( fe_tuner_ops - > get_status ) {
u32 tuner_status ;
fe_tuner_ops - > get_status ( & t - > fe , & tuner_status ) ;
va - > mode = ( tuner_status & TUNER_STATUS_STEREO )
? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO ;
2007-12-21 11:18:32 -03:00
} else if ( analog_ops - > is_stereo )
va - > mode = analog_ops - > is_stereo ( & t - > fe )
2007-08-21 01:25:48 -03:00
? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO ;
}
2005-07-12 13:58:55 -07:00
return 0 ;
}
2008-11-24 18:21:40 -03:00
}
return - ENOIOCTLCMD ;
}
2006-07-23 06:31:19 -03:00
# endif
2007-10-18 19:56:47 -03:00
2008-11-24 18:21:40 -03:00
static int tuner_s_config ( struct v4l2_subdev * sd , const struct v4l2_priv_tun_config * cfg )
{
struct tuner * t = to_tuner ( sd ) ;
struct analog_demod_ops * analog_ops = & t - > fe . ops . analog_ops ;
2005-04-16 15:20:36 -07:00
2008-11-24 18:21:40 -03:00
if ( t - > type ! = cfg - > tuner )
return 0 ;
2007-10-18 19:56:47 -03:00
2008-11-24 18:21:40 -03:00
if ( analog_ops - > set_config ) {
analog_ops - > set_config ( & t - > fe , cfg - > priv ) ;
return 0 ;
2007-10-18 19:56:47 -03:00
}
2005-04-16 15:20:36 -07:00
2008-11-24 18:21:40 -03:00
tuner_dbg ( " Tuner frontend module has no way to set config \n " ) ;
return 0 ;
}
2005-06-23 22:05:07 -07:00
2008-11-24 18:21:40 -03:00
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here . . . */
static int tuner_s_std ( struct v4l2_subdev * sd , v4l2_std_id std )
{
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-07-12 13:58:55 -07:00
2008-11-24 18:21:40 -03:00
if ( set_mode ( client , t , V4L2_TUNER_ANALOG_TV , " VIDIOC_S_STD " )
= = - EINVAL )
return 0 ;
2005-07-12 13:58:55 -07:00
2008-11-24 18:21:40 -03:00
switch_v4l2 ( ) ;
2005-05-06 21:30:42 -07:00
2008-11-24 18:21:40 -03:00
t - > std = std ;
tuner_fixup_std ( t ) ;
if ( t - > tv_freq )
set_freq ( client , t - > tv_freq ) ;
return 0 ;
}
2005-07-12 13:58:55 -07:00
2008-11-24 18:21:40 -03:00
static int tuner_s_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * f )
{
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-07-12 13:58:55 -07:00
2008-11-24 18:21:40 -03:00
if ( set_mode ( client , t , f - > type , " VIDIOC_S_FREQUENCY " )
= = - EINVAL )
return 0 ;
switch_v4l2 ( ) ;
set_freq ( client , f - > frequency ) ;
2006-01-23 17:11:09 -02:00
2008-11-24 18:21:40 -03:00
return 0 ;
}
2005-07-12 13:58:55 -07:00
2008-11-24 18: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-12 13:58:55 -07:00
2008-11-24 18:21:40 -03:00
if ( check_mode ( t , " VIDIOC_G_FREQUENCY " ) = = - EINVAL )
return 0 ;
switch_v4l2 ( ) ;
f - > type = t - > mode ;
if ( fe_tuner_ops - > get_frequency ) {
u32 abs_freq ;
fe_tuner_ops - > get_frequency ( & t - > fe , & abs_freq ) ;
f - > frequency = ( V4L2_TUNER_RADIO = = t - > mode ) ?
( abs_freq * 2 + 125 / 2 ) / 125 :
( abs_freq + 62500 / 2 ) / 62500 ;
return 0 ;
}
f - > frequency = ( V4L2_TUNER_RADIO = = t - > mode ) ?
t - > radio_freq : t - > tv_freq ;
return 0 ;
}
2005-07-12 13:58:55 -07:00
2008-11-24 18: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 ;
if ( check_mode ( t , " VIDIOC_G_TUNER " ) = = - EINVAL )
return 0 ;
switch_v4l2 ( ) ;
vt - > type = t - > mode ;
if ( analog_ops - > get_afc )
vt - > afc = analog_ops - > get_afc ( & t - > fe ) ;
if ( t - > mode = = V4L2_TUNER_ANALOG_TV )
vt - > capability | = V4L2_TUNER_CAP_NORM ;
if ( t - > mode ! = V4L2_TUNER_RADIO ) {
vt - > rangelow = tv_range [ 0 ] * 16 ;
vt - > rangehigh = tv_range [ 1 ] * 16 ;
return 0 ;
}
/* radio 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 ;
} else {
if ( analog_ops - > is_stereo ) {
vt - > rxsubchans =
analog_ops - > is_stereo ( & t - > fe ) ?
V4L2_TUNER_SUB_STEREO :
V4L2_TUNER_SUB_MONO ;
2005-06-23 22:05:07 -07:00
}
2005-04-16 15:20:36 -07:00
}
2008-11-24 18:21:40 -03:00
if ( analog_ops - > has_signal )
vt - > signal = analog_ops - > has_signal ( & t - > fe ) ;
vt - > capability | =
V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO ;
vt - > audmode = t - > audmode ;
vt - > rangelow = radio_range [ 0 ] * 16000 ;
vt - > rangehigh = radio_range [ 1 ] * 16000 ;
return 0 ;
}
static int tuner_s_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct tuner * t = to_tuner ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
if ( check_mode ( t , " VIDIOC_S_TUNER " ) = = - EINVAL )
return 0 ;
switch_v4l2 ( ) ;
/* do nothing unless we're a radio tuner */
if ( t - > mode ! = V4L2_TUNER_RADIO )
return 0 ;
t - > audmode = vt - > audmode ;
set_radio_freq ( client , t - > radio_freq ) ;
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-16 15:20:36 -07:00
2008-11-24 18:21:40 -03:00
if ( analog_ops - > tuner_status )
analog_ops - > tuner_status ( & t - > fe ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-11-24 18:21:40 -03:00
static int tuner_command ( struct i2c_client * client , unsigned cmd , void * arg )
{
return v4l2_subdev_command ( i2c_get_clientdata ( client ) , cmd , arg ) ;
}
2007-03-12 19:20:15 -03:00
static int tuner_suspend ( struct i2c_client * c , pm_message_t state )
2005-04-16 15:20:36 -07:00
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2005-04-16 15:20:36 -07:00
2007-11-04 11:03:36 -03:00
tuner_dbg ( " suspend \n " ) ;
2005-04-16 15:20:36 -07:00
/* FIXME: power down ??? */
return 0 ;
}
2007-03-12 19:20:15 -03:00
static int tuner_resume ( struct i2c_client * c )
2005-04-16 15:20:36 -07:00
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( c ) ) ;
2005-04-16 15:20:36 -07:00
2007-11-04 11:03:36 -03:00
tuner_dbg ( " resume \n " ) ;
2006-01-15 15:04:52 -02:00
if ( V4L2_TUNER_RADIO = = t - > mode ) {
if ( t - > radio_freq )
set_freq ( c , t - > radio_freq ) ;
} else {
if ( t - > tv_freq )
set_freq ( c , t - > tv_freq ) ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-11-24 18:21:40 -03:00
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops tuner_core_ops = {
. log_status = tuner_log_status ,
. s_standby = tuner_s_standby ,
2008-12-19 07:07:56 -03:00
# ifdef CONFIG_VIDEO_ALLOW_V4L1
2008-11-24 18:21:40 -03:00
. ioctl = tuner_ioctl ,
2008-12-19 07:07:56 -03:00
# endif
2008-11-24 18:21:40 -03:00
} ;
static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
. s_std = tuner_s_std ,
. 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 ,
} ;
static const struct v4l2_subdev_ops tuner_ops = {
. core = & tuner_core_ops ,
. tuner = & tuner_tuner_ops ,
} ;
2007-11-04 10:53:09 -03:00
/* ---------------------------------------------------------------------- */
2008-01-28 22:11:15 -03:00
static LIST_HEAD ( tuner_list ) ;
2007-11-04 10:53:09 -03:00
/* Search for existing radio and/or TV tuners on the given I2C adapter.
2007-11-04 11:03:36 -03:00
Note that when this function is called from tuner_probe you can be
2007-11-04 10:53:09 -03:00
certain no other devices will be added / deleted at the same time , I2C
core protects against that . */
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 | |
pos - > i2c - > driver - > id ! = I2C_DRIVERID_TUNER )
continue ;
mode_mask = pos - > mode_mask & ~ T_STANDBY ;
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 & &
( pos - > mode_mask & ( T_ANALOG_TV | T_DIGITAL_TV ) ) )
* tv = pos ;
}
}
/* During client attach, set_type is called by adapter's attach_inform callback.
2007-11-04 11:03:36 -03:00
set_type must then be completed by tuner_probe .
2007-11-04 10:53:09 -03:00
*/
2008-04-29 23:11:39 +02:00
static int tuner_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2007-11-04 10:53:09 -03:00
{
struct tuner * t ;
struct tuner * radio ;
struct tuner * tv ;
t = kzalloc ( sizeof ( struct tuner ) , GFP_KERNEL ) ;
2007-11-04 11:03:36 -03:00
if ( NULL = = t )
2007-11-04 10:53:09 -03:00
return - ENOMEM ;
2008-11-24 18:21:40 -03:00
v4l2_i2c_subdev_init ( & t - > sd , client , & tuner_ops ) ;
2007-11-04 10:53:09 -03:00
t - > i2c = client ;
2008-05-26 16:08:40 +02:00
t - > name = " (tuner unset) " ;
2007-11-04 10:53:09 -03:00
t - > type = UNSET ;
t - > audmode = V4L2_TUNER_MODE_STEREO ;
t - > mode_mask = T_UNINITIALIZED ;
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 " ) ;
}
2007-11-04 11:03:36 -03:00
/* HACK: This test was added to avoid tuner to probe tda9840 and
2007-11-04 10:53:09 -03:00
tea6415c on the MXB card */
2007-11-04 11:03:36 -03:00
if ( client - > adapter - > id = = I2C_HW_SAA7146 & & client - > addr < 0x4a ) {
kfree ( t ) ;
2007-11-04 10:53:09 -03:00
return - ENODEV ;
2007-11-04 11:03:36 -03:00
}
2007-11-04 10:53:09 -03:00
/* autodetection code based on the i2c addr */
if ( ! no_autodetect ) {
2007-11-04 11:03:36 -03:00
switch ( client - > addr ) {
2007-11-04 10:53:09 -03:00
case 0x10 :
2008-04-29 03:54:19 -03:00
if ( tuner_symbol_probe ( tea5761_autodetection ,
t - > i2c - > adapter ,
t - > i2c - > addr ) > = 0 ) {
2007-11-04 10:53:09 -03:00
t - > type = TUNER_TEA5761 ;
t - > mode_mask = T_RADIO ;
t - > mode = T_STANDBY ;
/* Sets freq to FM range */
t - > radio_freq = 87.5 * 16000 ;
tuner_lookup ( t - > i2c - > adapter , & radio , & tv ) ;
if ( tv )
tv - > mode_mask & = ~ T_RADIO ;
goto register_client ;
}
2008-04-23 17:27:27 -03:00
return - ENODEV ;
2007-11-04 10:53:09 -03:00
case 0x42 :
case 0x43 :
case 0x4a :
case 0x4b :
/* If chip is not tda8290, don't register.
since it can be tda9887 */
2008-04-29 03:54:19 -03:00
if ( tuner_symbol_probe ( tda829x_probe , t - > i2c - > adapter ,
2008-04-30 15:45:00 -03:00
t - > i2c - > addr ) > = 0 ) {
2007-11-04 10:53:09 -03:00
tuner_dbg ( " tda829x detected \n " ) ;
} else {
/* Default is being tda9887 */
t - > type = TUNER_TDA9887 ;
t - > mode_mask = T_RADIO | T_ANALOG_TV |
T_DIGITAL_TV ;
t - > mode = T_STANDBY ;
goto register_client ;
}
break ;
case 0x60 :
2008-04-29 03:54:19 -03:00
if ( tuner_symbol_probe ( tea5767_autodetection ,
t - > i2c - > adapter , t - > i2c - > addr )
2008-04-30 15:45:00 -03:00
> = 0 ) {
2007-11-04 10:53:09 -03:00
t - > type = TUNER_TEA5767 ;
t - > mode_mask = T_RADIO ;
t - > mode = T_STANDBY ;
/* Sets freq to FM range */
t - > radio_freq = 87.5 * 16000 ;
tuner_lookup ( t - > i2c - > adapter , & radio , & tv ) ;
if ( tv )
tv - > mode_mask & = ~ T_RADIO ;
goto register_client ;
}
break ;
}
}
/* 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 ) {
t - > mode_mask = T_ANALOG_TV | T_DIGITAL_TV ;
if ( radio = = NULL )
t - > mode_mask | = T_RADIO ;
tuner_dbg ( " Setting mode_mask to 0x%02x \n " , t - > mode_mask ) ;
t - > tv_freq = 400 * 16 ; /* Sets freq to VHF High */
t - > radio_freq = 87.5 * 16000 ; /* Sets freq to FM range */
}
/* Should be just before return */
register_client :
2007-11-04 11:03:36 -03:00
tuner_info ( " chip found @ 0x%x (%s) \n " , client - > addr < < 1 ,
client - > adapter - > name ) ;
2007-11-04 10:53:09 -03:00
/* Sets a default mode */
if ( t - > mode_mask & T_ANALOG_TV ) {
2007-12-09 17:21:54 -03:00
t - > mode = V4L2_TUNER_ANALOG_TV ;
2007-11-04 10:53:09 -03:00
} else if ( t - > mode_mask & T_RADIO ) {
2007-12-09 17:21:54 -03:00
t - > mode = V4L2_TUNER_RADIO ;
2007-11-04 10:53:09 -03:00
} else {
2007-12-09 17:21:54 -03:00
t - > mode = V4L2_TUNER_DIGITAL_TV ;
2007-11-04 10:53:09 -03:00
}
2008-09-12 13:31:45 -03:00
set_type ( client , t - > type , t - > mode_mask , t - > config , t - > fe . callback ) ;
2007-11-04 11:03:36 -03:00
list_add_tail ( & t - > list , & tuner_list ) ;
2007-11-04 10:53:09 -03:00
return 0 ;
}
2007-11-04 11:03:36 -03:00
static int tuner_legacy_probe ( struct i2c_adapter * adap )
2007-11-04 10:53:09 -03:00
{
if ( 0 ! = addr ) {
normal_i2c [ 0 ] = addr ;
normal_i2c [ 1 ] = I2C_CLIENT_END ;
}
2007-11-04 11:03:36 -03:00
if ( ( adap - > class & I2C_CLASS_TV_ANALOG ) = = 0 )
return 0 ;
2007-11-04 10:53:09 -03:00
/* HACK: Ignore 0x6b and 0x6f on cx88 boards.
* FusionHDTV5 RT Gold has an ir receiver at 0x6b
* and an RTC at 0x6f which can get corrupted if probed .
*/
if ( ( adap - > id = = I2C_HW_B_CX2388x ) | |
( adap - > id = = I2C_HW_B_CX23885 ) ) {
unsigned int i = 0 ;
while ( i < I2C_CLIENT_MAX_OPTS & & ignore [ i ] ! = I2C_CLIENT_END )
i + = 2 ;
if ( i + 4 < I2C_CLIENT_MAX_OPTS ) {
ignore [ i + 0 ] = adap - > nr ;
ignore [ i + 1 ] = 0x6b ;
ignore [ i + 2 ] = adap - > nr ;
ignore [ i + 3 ] = 0x6f ;
ignore [ i + 4 ] = I2C_CLIENT_END ;
} else
printk ( KERN_WARNING " tuner: "
" too many options specified "
" in i2c probe ignore list! \n " ) ;
}
2007-11-04 11:03:36 -03:00
return 1 ;
2007-11-04 10:53:09 -03:00
}
2007-11-04 11:03:36 -03:00
static int tuner_remove ( struct i2c_client * client )
2007-11-04 10:53:09 -03:00
{
2008-11-24 18:21:40 -03:00
struct tuner * t = to_tuner ( i2c_get_clientdata ( client ) ) ;
2007-11-04 10:53:09 -03:00
2008-11-24 18:21:40 -03:00
v4l2_device_unregister_subdev ( & t - > sd ) ;
2008-04-29 03:54:19 -03:00
tuner_detach ( & t - > fe ) ;
t - > fe . analog_demod_priv = NULL ;
2007-11-04 10:53:09 -03:00
list_del ( & t - > list ) ;
kfree ( t ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
/* ----------------------------------------------------------------------- */
2008-05-18 20:49:40 +02:00
/* 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 . */
static const struct i2c_device_id tuner_id [ ] = {
{ " tuner " , } , /* autodetect */
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tuner_id ) ;
2007-11-04 11:03:36 -03:00
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " tuner " ,
. driverid = I2C_DRIVERID_TUNER ,
2005-07-12 13:58:55 -07:00
. command = tuner_command ,
2007-11-04 11:03:36 -03:00
. probe = tuner_probe ,
. remove = tuner_remove ,
2007-03-12 19:20:15 -03:00
. suspend = tuner_suspend ,
2007-11-04 11:03:36 -03:00
. resume = tuner_resume ,
. legacy_probe = tuner_legacy_probe ,
2008-05-18 20:49:40 +02:00
. id_table = tuner_id ,
2005-04-16 15:20:36 -07:00
} ;
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - basic - offset : 8
* End :
*/