2005-04-17 02:20:36 +04:00
/*
* i2c tv tuner chip device driver
* controls all those simple 4 - control - bytes style tuners .
2007-08-28 04:59:08 +04:00
*
* This " tuner-simple " module was split apart from the original " tuner " module .
2005-04-17 02:20:36 +04:00
*/
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/videodev.h>
# include <media/tuner.h>
2006-06-25 22:34:39 +04:00
# include <media/v4l2-common.h>
2007-06-26 20:12:08 +04:00
# include <media/tuner-types.h>
2007-08-28 04:59:08 +04:00
# include "tuner-i2c.h"
# include "tuner-simple.h"
2008-04-22 21:41:44 +04:00
static int debug ;
2007-08-28 04:59:08 +04:00
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " enable verbose debug messages " ) ;
2008-04-22 21:41:44 +04:00
static int offset ;
2006-08-13 04:59:19 +04:00
module_param ( offset , int , 0664 ) ;
2008-04-22 21:41:44 +04:00
MODULE_PARM_DESC ( offset , " Allows to specify an offset for tuner " ) ;
2006-01-09 20:25:11 +03:00
2005-04-17 02:20:36 +04:00
/* ---------------------------------------------------------------------- */
/* tv standard selection for Temic 4046 FM5
this value takes the low bits of control byte 2
from datasheet Rev .01 , Feb .00
standard BG I L L2 D
picture IF 38.9 38.9 38.9 33.95 38.9
sound 1 33.4 32.9 32.4 40.45 32.4
sound 2 33.16
NICAM 33.05 32.348 33.05 33.05
*/
# define TEMIC_SET_PAL_I 0x05
# define TEMIC_SET_PAL_DK 0x09
2008-04-22 21:41:44 +04:00
# define TEMIC_SET_PAL_L 0x0a /* SECAM ? */
# define TEMIC_SET_PAL_L2 0x0b /* change IF ! */
2005-04-17 02:20:36 +04:00
# define TEMIC_SET_PAL_BG 0x0c
/* tv tuner system standard selection for Philips FQ1216ME
this value takes the low bits of control byte 2
from datasheet " 1999 Nov 16 " ( supersedes " 1999 Mar 23 " )
standard BG DK I L L `
picture carrier 38.90 38.90 38.90 38.90 33.95
colour 34.47 34.47 34.47 34.47 38.38
sound 1 33.40 32.40 32.90 32.40 40.45
sound 2 33.16 - - - -
NICAM 33.05 33.05 32.35 33.05 39.80
*/
# define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/
# define PHILIPS_SET_PAL_BGDK 0x09
# define PHILIPS_SET_PAL_L2 0x0a
# define PHILIPS_SET_PAL_L 0x0b
/* system switching for Philips FI1216MF MK2
from datasheet " 1996 Jul 09 " ,
standard BG L L '
picture carrier 38.90 38.90 33.95
colour 34.47 34.37 38.38
sound 1 33.40 32.40 40.45
sound 2 33.16 - -
NICAM 33.05 33.05 39.80
*/
2007-05-21 18:39:21 +04:00
# define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */
# define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */
# define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */
2005-04-17 02:20:36 +04:00
2005-07-13 00:58:55 +04:00
/* Control byte */
# define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */
# define TUNER_RATIO_SELECT_50 0x00
# define TUNER_RATIO_SELECT_32 0x02
# define TUNER_RATIO_SELECT_166 0x04
# define TUNER_RATIO_SELECT_62 0x06
# define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 */
/* Status byte */
# define TUNER_POR 0x80
# define TUNER_FL 0x40
# define TUNER_MODE 0x38
# define TUNER_AFC 0x07
# define TUNER_SIGNAL 0x07
# define TUNER_STEREO 0x10
# define TUNER_PLL_LOCKED 0x40
# define TUNER_STEREO_MK3 0x04
2005-04-17 02:20:36 +04:00
2008-04-22 21:45:52 +04:00
static DEFINE_MUTEX ( tuner_simple_list_mutex ) ;
static LIST_HEAD ( hybrid_tuner_instance_list ) ;
2007-08-11 22:42:12 +04:00
struct tuner_simple_priv {
u16 last_div ;
2008-04-22 21:45:52 +04:00
2007-08-21 08:24:42 +04:00
struct tuner_i2c_props i2c_props ;
2008-04-22 21:45:52 +04:00
struct list_head hybrid_tuner_instance_list ;
2007-08-28 04:59:08 +04:00
unsigned int type ;
struct tunertype * tun ;
u32 frequency ;
2008-04-22 21:45:52 +04:00
u32 bandwidth ;
2007-08-11 22:42:12 +04:00
} ;
2005-04-17 02:20:36 +04:00
/* ---------------------------------------------------------------------- */
2007-08-31 23:39:39 +04:00
static int tuner_read_status ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-08-28 04:59:08 +04:00
struct tuner_simple_priv * priv = fe - > tuner_priv ;
2005-04-17 02:20:36 +04:00
unsigned char byte ;
2008-04-22 21:41:44 +04:00
if ( 1 ! = tuner_i2c_xfer_recv ( & priv - > i2c_props , & byte , 1 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-06-24 09:05:07 +04:00
2005-04-17 02:20:36 +04:00
return byte ;
}
2007-08-31 23:39:39 +04:00
static inline int tuner_signal ( const int status )
2005-04-17 02:20:36 +04:00
{
2007-08-31 23:39:39 +04:00
return ( status & TUNER_SIGNAL ) < < 13 ;
2005-04-17 02:20:36 +04:00
}
2007-08-31 23:39:39 +04:00
static inline int tuner_stereo ( const int type , const int status )
2005-04-17 02:20:36 +04:00
{
2007-08-31 23:39:39 +04:00
switch ( type ) {
2008-04-22 21:41:44 +04:00
case TUNER_PHILIPS_FM1216ME_MK3 :
case TUNER_PHILIPS_FM1236_MK3 :
case TUNER_PHILIPS_FM1256_IH3 :
case TUNER_LG_NTSC_TAPE :
return ( ( status & TUNER_SIGNAL ) = = TUNER_STEREO_MK3 ) ;
default :
return status & TUNER_STEREO ;
2005-06-24 09:05:07 +04:00
}
2007-08-31 23:39:39 +04:00
}
2005-06-24 09:05:07 +04:00
2007-08-31 23:39:39 +04:00
static inline int tuner_islocked ( const int status )
{
return ( status & TUNER_FL ) ;
}
static inline int tuner_afcstatus ( const int status )
{
return ( status & TUNER_AFC ) - 2 ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 04:59:08 +04:00
static int simple_get_status ( struct dvb_frontend * fe , u32 * status )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
2008-04-22 21:45:52 +04:00
int tuner_status ;
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
tuner_status = tuner_read_status ( fe ) ;
2007-08-28 04:59:08 +04:00
* status = 0 ;
2007-08-31 23:39:39 +04:00
if ( tuner_islocked ( tuner_status ) )
2007-08-28 04:59:08 +04:00
* status = TUNER_STATUS_LOCKED ;
2007-08-31 23:39:39 +04:00
if ( tuner_stereo ( priv - > type , tuner_status ) )
2007-08-28 04:59:08 +04:00
* status | = TUNER_STATUS_STEREO ;
2007-08-31 23:39:39 +04:00
tuner_dbg ( " AFC Status: %d \n " , tuner_afcstatus ( tuner_status ) ) ;
return 0 ;
}
static int simple_get_rf_strength ( struct dvb_frontend * fe , u16 * strength )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
2008-04-22 21:45:52 +04:00
int signal ;
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
signal = tuner_signal ( tuner_read_status ( fe ) ) ;
2007-08-31 23:39:39 +04:00
* strength = signal ;
tuner_dbg ( " Signal strength: %d \n " , signal ) ;
2007-08-28 04:59:08 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* ---------------------------------------------------------------------- */
2008-04-22 21:41:48 +04:00
static inline char * tuner_param_name ( enum param_type type )
{
char * name ;
switch ( type ) {
case TUNER_PARAM_TYPE_RADIO :
name = " radio " ;
break ;
case TUNER_PARAM_TYPE_PAL :
name = " pal " ;
break ;
case TUNER_PARAM_TYPE_SECAM :
name = " secam " ;
break ;
case TUNER_PARAM_TYPE_NTSC :
name = " ntsc " ;
break ;
2008-04-22 21:45:52 +04:00
case TUNER_PARAM_TYPE_DIGITAL :
name = " digital " ;
break ;
2008-04-22 21:41:48 +04:00
default :
name = " unknown " ;
break ;
}
return name ;
}
static struct tuner_params * simple_tuner_params ( struct dvb_frontend * fe ,
enum param_type desired_type )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
struct tunertype * tun = priv - > tun ;
int i ;
for ( i = 0 ; i < tun - > count ; i + + )
if ( desired_type = = tun - > params [ i ] . type )
break ;
/* use default tuner params if desired_type not available */
if ( i = = tun - > count ) {
tuner_dbg ( " desired params (%s) undefined for tuner %d \n " ,
tuner_param_name ( desired_type ) , priv - > type ) ;
i = 0 ;
}
tuner_dbg ( " using tuner params #%d (%s) \n " , i ,
tuner_param_name ( tun - > params [ i ] . type ) ) ;
return & tun - > params [ i ] ;
}
static int simple_config_lookup ( struct dvb_frontend * fe ,
struct tuner_params * t_params ,
int * frequency , u8 * config , u8 * cb )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
int i ;
for ( i = 0 ; i < t_params - > count ; i + + ) {
if ( * frequency > t_params - > ranges [ i ] . limit )
continue ;
break ;
}
if ( i = = t_params - > count ) {
tuner_dbg ( " frequency out of range (%d > %d) \n " ,
* frequency , t_params - > ranges [ i - 1 ] . limit ) ;
* frequency = t_params - > ranges [ - - i ] . limit ;
}
* config = t_params - > ranges [ i ] . config ;
* cb = t_params - > ranges [ i ] . cb ;
2008-04-22 21:41:49 +04:00
tuner_dbg ( " freq = %d.%02d (%d), range = %d, "
" config = 0x%02x, cb = 0x%02x \n " ,
* frequency / 16 , * frequency % 16 * 100 / 16 , * frequency ,
i , * config , * cb ) ;
2008-04-22 21:41:48 +04:00
return i ;
}
/* ---------------------------------------------------------------------- */
2008-04-22 21:41:51 +04:00
static int simple_std_setup ( struct dvb_frontend * fe ,
struct analog_parameters * params ,
V4L/DVB (7235): tuner-simple: fix a buffer overflow
simple_set_tv() creates a buffer with 4 elements, and calls
simple_std_setup(), passing &buffer[1]. This makes the 5th element of buffer to
be initialized to 0, overriding some area outside the buffer.
Also, simple_std_setup() receives a buffer as parameter, but the buffer is
just overriden after the call, so, it doesn't make much sense to pass it as a
parameter.
This patch removes buffer[] from the function call, creating, instead, a local
var to be used internally.
Thanks to Axel Rometsch <axel.rometsch@freenet.de> for pointing the issue.
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-04-22 21:42:13 +04:00
u8 * config , u8 * cb )
2005-04-17 02:20:36 +04:00
{
2007-08-28 04:59:08 +04:00
struct tuner_simple_priv * priv = fe - > tuner_priv ;
2008-04-22 21:41:51 +04:00
u8 tuneraddr ;
int rc ;
2006-02-07 11:25:33 +03:00
2005-04-17 02:20:36 +04:00
/* tv norm specific stuff for multi-norm tuners */
2007-08-28 04:59:08 +04:00
switch ( priv - > type ) {
2008-04-22 21:41:44 +04:00
case TUNER_PHILIPS_SECAM : /* FI1216MF */
2005-04-17 02:20:36 +04:00
/* 0x01 -> ??? no change ??? */
/* 0x02 -> PAL BDGHI / SECAM L */
/* 0x04 -> ??? PAL others / SECAM others ??? */
2008-04-22 21:41:51 +04:00
* cb & = ~ 0x03 ;
2008-04-22 21:41:44 +04:00
if ( params - > std & V4L2_STD_SECAM_L )
/* also valid for V4L2_STD_SECAM */
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_MF_SET_STD_L ;
2007-08-28 04:59:08 +04:00
else if ( params - > std & V4L2_STD_SECAM_LC )
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_MF_SET_STD_LC ;
2007-05-21 18:15:09 +04:00
else /* V4L2_STD_B|V4L2_STD_GH */
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_MF_SET_STD_BG ;
2005-04-17 02:20:36 +04:00
break ;
case TUNER_TEMIC_4046FM5 :
2008-04-22 21:41:51 +04:00
* cb & = ~ 0x0f ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
if ( params - > std & V4L2_STD_PAL_BG ) {
2008-04-22 21:41:51 +04:00
* cb | = TEMIC_SET_PAL_BG ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
} else if ( params - > std & V4L2_STD_PAL_I ) {
2008-04-22 21:41:51 +04:00
* cb | = TEMIC_SET_PAL_I ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
} else if ( params - > std & V4L2_STD_PAL_DK ) {
2008-04-22 21:41:51 +04:00
* cb | = TEMIC_SET_PAL_DK ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
} else if ( params - > std & V4L2_STD_SECAM_L ) {
2008-04-22 21:41:51 +04:00
* cb | = TEMIC_SET_PAL_L ;
2005-04-17 02:20:36 +04:00
}
break ;
case TUNER_PHILIPS_FQ1216ME :
2008-04-22 21:41:51 +04:00
* cb & = ~ 0x0f ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
if ( params - > std & ( V4L2_STD_PAL_BG | V4L2_STD_PAL_DK ) ) {
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_SET_PAL_BGDK ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
} else if ( params - > std & V4L2_STD_PAL_I ) {
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_SET_PAL_I ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
} else if ( params - > std & V4L2_STD_SECAM_L ) {
2008-04-22 21:41:51 +04:00
* cb | = PHILIPS_SET_PAL_L ;
2005-04-17 02:20:36 +04:00
}
break ;
case TUNER_PHILIPS_ATSC :
/* 0x00 -> ATSC antenna input 1 */
/* 0x01 -> ATSC antenna input 2 */
/* 0x02 -> NTSC antenna input 1 */
/* 0x03 -> NTSC antenna input 2 */
2008-04-22 21:41:51 +04:00
* cb & = ~ 0x03 ;
2007-08-28 04:59:08 +04:00
if ( ! ( params - > std & V4L2_STD_ATSC ) )
2008-04-22 21:41:51 +04:00
* cb | = 2 ;
2005-04-17 02:20:36 +04:00
/* FIXME: input */
break ;
case TUNER_MICROTUNE_4042FI5 :
/* Set the charge pump for fast tuning */
2008-04-22 21:41:51 +04:00
* config | = TUNER_CHARGE_PUMP ;
2005-04-17 02:20:36 +04:00
break ;
2005-11-09 08:37:04 +03:00
case TUNER_PHILIPS_TUV1236D :
V4L/DVB (7235): tuner-simple: fix a buffer overflow
simple_set_tv() creates a buffer with 4 elements, and calls
simple_std_setup(), passing &buffer[1]. This makes the 5th element of buffer to
be initialized to 0, overriding some area outside the buffer.
Also, simple_std_setup() receives a buffer as parameter, but the buffer is
just overriden after the call, so, it doesn't make much sense to pass it as a
parameter.
This patch removes buffer[] from the function call, creating, instead, a local
var to be used internally.
Thanks to Axel Rometsch <axel.rometsch@freenet.de> for pointing the issue.
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-04-22 21:42:13 +04:00
{
2005-11-09 08:37:04 +03:00
/* 0x40 -> ATSC antenna input 1 */
/* 0x48 -> ATSC antenna input 2 */
/* 0x00 -> NTSC antenna input 1 */
/* 0x08 -> NTSC antenna input 2 */
V4L/DVB (7235): tuner-simple: fix a buffer overflow
simple_set_tv() creates a buffer with 4 elements, and calls
simple_std_setup(), passing &buffer[1]. This makes the 5th element of buffer to
be initialized to 0, overriding some area outside the buffer.
Also, simple_std_setup() receives a buffer as parameter, but the buffer is
just overriden after the call, so, it doesn't make much sense to pass it as a
parameter.
This patch removes buffer[] from the function call, creating, instead, a local
var to be used internally.
Thanks to Axel Rometsch <axel.rometsch@freenet.de> for pointing the issue.
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-04-22 21:42:13 +04:00
u8 buffer [ 4 ] = { 0x14 , 0x00 , 0x17 , 0x00 } ;
2008-04-22 21:41:51 +04:00
* cb & = ~ 0x40 ;
2007-08-28 04:59:08 +04:00
if ( params - > std & V4L2_STD_ATSC ) {
2008-04-22 21:41:51 +04:00
* cb | = 0x40 ;
2005-11-09 08:38:18 +03:00
buffer [ 1 ] = 0x04 ;
}
/* set to the correct mode (analog or digital) */
2007-08-21 08:24:42 +04:00
tuneraddr = priv - > i2c_props . addr ;
priv - > i2c_props . addr = 0x0a ;
2008-04-22 21:41:44 +04:00
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , & buffer [ 0 ] , 2 ) ;
if ( 2 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d "
" (should be 2) \n " , rc ) ;
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , & buffer [ 2 ] , 2 ) ;
if ( 2 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d "
" (should be 2) \n " , rc ) ;
2007-08-21 08:24:42 +04:00
priv - > i2c_props . addr = tuneraddr ;
2005-11-09 08:37:04 +03:00
/* FIXME: input */
break ;
2005-04-17 02:20:36 +04:00
}
V4L/DVB (7235): tuner-simple: fix a buffer overflow
simple_set_tv() creates a buffer with 4 elements, and calls
simple_std_setup(), passing &buffer[1]. This makes the 5th element of buffer to
be initialized to 0, overriding some area outside the buffer.
Also, simple_std_setup() receives a buffer as parameter, but the buffer is
just overriden after the call, so, it doesn't make much sense to pass it as a
parameter.
This patch removes buffer[] from the function call, creating, instead, a local
var to be used internally.
Thanks to Axel Rometsch <axel.rometsch@freenet.de> for pointing the issue.
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-04-22 21:42:13 +04:00
}
2005-04-17 02:20:36 +04:00
2008-04-22 21:41:51 +04:00
return 0 ;
}
static int simple_post_tune ( struct dvb_frontend * fe , u8 * buffer ,
u16 div , u8 config , u8 cb )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
int rc ;
switch ( priv - > type ) {
case TUNER_LG_TDVS_H06XF :
/* Set the Auxiliary Byte. */
buffer [ 0 ] = buffer [ 2 ] ;
buffer [ 0 ] & = ~ 0x20 ;
buffer [ 0 ] | = 0x18 ;
buffer [ 1 ] = 0x20 ;
tuner_dbg ( " tv 0x%02x 0x%02x \n " , buffer [ 0 ] , buffer [ 1 ] ) ;
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 2 ) ;
if ( 2 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d "
" (should be 2) \n " , rc ) ;
break ;
case TUNER_MICROTUNE_4042FI5 :
{
/* FIXME - this may also work for other tuners */
unsigned long timeout = jiffies + msecs_to_jiffies ( 1 ) ;
u8 status_byte = 0 ;
/* Wait until the PLL locks */
for ( ; ; ) {
if ( time_after ( jiffies , timeout ) )
return 0 ;
rc = tuner_i2c_xfer_recv ( & priv - > i2c_props ,
& status_byte , 1 ) ;
if ( 1 ! = rc ) {
tuner_warn ( " i2c i/o read error: rc == %d "
" (should be 1) \n " , rc ) ;
break ;
}
if ( status_byte & TUNER_PLL_LOCKED )
break ;
udelay ( 10 ) ;
}
/* Set the charge pump for optimized phase noise figure */
config & = ~ TUNER_CHARGE_PUMP ;
buffer [ 0 ] = ( div > > 8 ) & 0x7f ;
buffer [ 1 ] = div & 0xff ;
buffer [ 2 ] = config ;
buffer [ 3 ] = cb ;
tuner_dbg ( " tv 0x%02x 0x%02x 0x%02x 0x%02x \n " ,
buffer [ 0 ] , buffer [ 1 ] , buffer [ 2 ] , buffer [ 3 ] ) ;
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 4 ) ;
if ( 4 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d "
" (should be 4) \n " , rc ) ;
break ;
}
}
return 0 ;
}
static int simple_radio_bandswitch ( struct dvb_frontend * fe , u8 * buffer )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
switch ( priv - > type ) {
case TUNER_TENA_9533_DI :
case TUNER_YMEC_TVF_5533MF :
tuner_dbg ( " This tuner doesn't have FM. "
" Most cards have a TEA5767 for FM \n " ) ;
return 0 ;
case TUNER_PHILIPS_FM1216ME_MK3 :
case TUNER_PHILIPS_FM1236_MK3 :
case TUNER_PHILIPS_FMD1216ME_MK3 :
case TUNER_LG_NTSC_TAPE :
case TUNER_PHILIPS_FM1256_IH3 :
buffer [ 3 ] = 0x19 ;
break ;
case TUNER_TNF_5335MF :
buffer [ 3 ] = 0x11 ;
break ;
case TUNER_LG_PAL_FM :
buffer [ 3 ] = 0xa5 ;
break ;
case TUNER_THOMSON_DTT761X :
buffer [ 3 ] = 0x39 ;
break ;
case TUNER_MICROTUNE_4049FM5 :
default :
buffer [ 3 ] = 0xa4 ;
break ;
}
return 0 ;
}
/* ---------------------------------------------------------------------- */
static int simple_set_tv_freq ( struct dvb_frontend * fe ,
struct analog_parameters * params )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
u8 config , cb ;
u16 div ;
struct tunertype * tun ;
u8 buffer [ 4 ] ;
int rc , IFPCoff , i ;
enum param_type desired_type ;
struct tuner_params * t_params ;
tun = priv - > tun ;
/* IFPCoff = Video Intermediate Frequency - Vif:
940 = 16 * 58.75 NTSC / J ( Japan )
732 = 16 * 45.75 M / N STD
704 = 16 * 44 ATSC ( at DVB code )
632 = 16 * 39.50 I U . K .
622.4 = 16 * 38.90 B / G D / K I , L STD
592 = 16 * 37.00 D China
590 = 16.36 .875 B Australia
543.2 = 16 * 33.95 L ' STD
171.2 = 16 * 10.70 FM Radio ( at set_radio_freq )
*/
if ( params - > std = = V4L2_STD_NTSC_M_JP ) {
IFPCoff = 940 ;
desired_type = TUNER_PARAM_TYPE_NTSC ;
} else if ( ( params - > std & V4L2_STD_MN ) & &
! ( params - > std & ~ V4L2_STD_MN ) ) {
IFPCoff = 732 ;
desired_type = TUNER_PARAM_TYPE_NTSC ;
} else if ( params - > std = = V4L2_STD_SECAM_LC ) {
IFPCoff = 543 ;
desired_type = TUNER_PARAM_TYPE_SECAM ;
} else {
IFPCoff = 623 ;
desired_type = TUNER_PARAM_TYPE_PAL ;
}
t_params = simple_tuner_params ( fe , desired_type ) ;
i = simple_config_lookup ( fe , t_params , & params - > frequency ,
& config , & cb ) ;
div = params - > frequency + IFPCoff + offset ;
tuner_dbg ( " Freq= %d.%02d MHz, V_IF=%d.%02d MHz, "
" Offset=%d.%02d MHz, div=%0d \n " ,
params - > frequency / 16 , params - > frequency % 16 * 100 / 16 ,
IFPCoff / 16 , IFPCoff % 16 * 100 / 16 ,
offset / 16 , offset % 16 * 100 / 16 , div ) ;
/* tv norm specific stuff for multi-norm tuners */
V4L/DVB (7235): tuner-simple: fix a buffer overflow
simple_set_tv() creates a buffer with 4 elements, and calls
simple_std_setup(), passing &buffer[1]. This makes the 5th element of buffer to
be initialized to 0, overriding some area outside the buffer.
Also, simple_std_setup() receives a buffer as parameter, but the buffer is
just overriden after the call, so, it doesn't make much sense to pass it as a
parameter.
This patch removes buffer[] from the function call, creating, instead, a local
var to be used internally.
Thanks to Axel Rometsch <axel.rometsch@freenet.de> for pointing the issue.
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-04-22 21:42:13 +04:00
simple_std_setup ( fe , params , & config , & cb ) ;
2008-04-22 21:41:51 +04:00
2007-08-28 04:59:08 +04:00
if ( t_params - > cb_first_if_lower_freq & & div < priv - > last_div ) {
2006-01-23 22:11:11 +03:00
buffer [ 0 ] = config ;
2006-01-23 22:11:11 +03:00
buffer [ 1 ] = cb ;
2005-04-17 02:20:36 +04:00
buffer [ 2 ] = ( div > > 8 ) & 0x7f ;
buffer [ 3 ] = div & 0xff ;
} else {
buffer [ 0 ] = ( div > > 8 ) & 0x7f ;
buffer [ 1 ] = div & 0xff ;
2006-01-23 22:11:11 +03:00
buffer [ 2 ] = config ;
2006-01-23 22:11:11 +03:00
buffer [ 3 ] = cb ;
2005-04-17 02:20:36 +04:00
}
2007-08-11 22:42:12 +04:00
priv - > last_div = div ;
2007-08-28 04:59:08 +04:00
if ( t_params - > has_tda9887 ) {
2007-10-19 02:56:47 +04:00
struct v4l2_priv_tun_config tda9887_cfg ;
2006-06-25 22:34:39 +04:00
int config = 0 ;
2008-04-22 21:41:44 +04:00
int is_secam_l = ( params - > std & ( V4L2_STD_SECAM_L |
V4L2_STD_SECAM_LC ) ) & &
! ( params - > std & ~ ( V4L2_STD_SECAM_L |
V4L2_STD_SECAM_LC ) ) ;
2006-06-25 22:34:39 +04:00
2007-10-19 02:56:47 +04:00
tda9887_cfg . tuner = TUNER_TDA9887 ;
tda9887_cfg . priv = & config ;
2007-08-28 04:59:08 +04:00
if ( params - > std = = V4L2_STD_SECAM_LC ) {
if ( t_params - > port1_active ^ t_params - > port1_invert_for_secam_lc )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT1_ACTIVE ;
2007-08-28 04:59:08 +04:00
if ( t_params - > port2_active ^ t_params - > port2_invert_for_secam_lc )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT2_ACTIVE ;
2008-04-22 21:41:44 +04:00
} else {
2007-08-28 04:59:08 +04:00
if ( t_params - > port1_active )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT1_ACTIVE ;
2007-08-28 04:59:08 +04:00
if ( t_params - > port2_active )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT2_ACTIVE ;
}
2007-08-28 04:59:08 +04:00
if ( t_params - > intercarrier_mode )
2006-06-25 22:34:39 +04:00
config | = TDA9887_INTERCARRIER ;
if ( is_secam_l ) {
2007-08-28 04:59:08 +04:00
if ( i = = 0 & & t_params - > default_top_secam_low )
config | = TDA9887_TOP ( t_params - > default_top_secam_low ) ;
else if ( i = = 1 & & t_params - > default_top_secam_mid )
config | = TDA9887_TOP ( t_params - > default_top_secam_mid ) ;
else if ( t_params - > default_top_secam_high )
config | = TDA9887_TOP ( t_params - > default_top_secam_high ) ;
2008-04-22 21:41:44 +04:00
} else {
2007-08-28 04:59:08 +04:00
if ( i = = 0 & & t_params - > default_top_low )
config | = TDA9887_TOP ( t_params - > default_top_low ) ;
else if ( i = = 1 & & t_params - > default_top_mid )
config | = TDA9887_TOP ( t_params - > default_top_mid ) ;
else if ( t_params - > default_top_high )
config | = TDA9887_TOP ( t_params - > default_top_high ) ;
2006-06-25 22:34:39 +04:00
}
2007-08-28 04:59:08 +04:00
if ( t_params - > default_pll_gating_18 )
2006-08-25 05:43:45 +04:00
config | = TDA9887_GATING_18 ;
2007-10-19 02:56:47 +04:00
i2c_clients_command ( priv - > i2c_props . adap , TUNER_SET_CONFIG ,
& tda9887_cfg ) ;
2006-06-25 22:34:39 +04:00
}
2005-04-17 02:20:36 +04:00
tuner_dbg ( " tv 0x%02x 0x%02x 0x%02x 0x%02x \n " ,
2008-04-22 21:41:44 +04:00
buffer [ 0 ] , buffer [ 1 ] , buffer [ 2 ] , buffer [ 3 ] ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:41:44 +04:00
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 4 ) ;
if ( 4 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d (should be 4) \n " , rc ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:41:51 +04:00
simple_post_tune ( fe , & buffer [ 0 ] , div , config , cb ) ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 04:59:08 +04:00
static int simple_set_radio_freq ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2005-04-17 02:20:36 +04:00
{
struct tunertype * tun ;
2007-08-28 04:59:08 +04:00
struct tuner_simple_priv * priv = fe - > tuner_priv ;
2006-01-15 20:04:52 +03:00
u8 buffer [ 4 ] ;
u16 div ;
V4L/DVB (3359): Redesign tuners struct for maximum flexibility
- Tunertype struct redefined to allow one or more tuner_params structs
per tuner definition, one for each video standard.
- Each tuner_params struct has an element containing an arbitrary
amount of tuner_ranges.
(this is needed for dvb tuners - to be handled later)
- A tuner_range may be referenced by multiple tuner_params structs.
There are many duplicates in here. Reusing tuner_range structs,
rather than defining new ones for each tuner, will cut down on
memory usage, and is preferred when possible.
- tunertype struct contains an element, has_tda988x.
We must set this for all tunertypes that contain a tda988x
chip, and then we can remove this setting from the various
card structs.
- Improves tuners array memory usage efficiency.
- Right now, all tuners are using the first tuner_params[] array element
for analog mode. In the future, we will be merging similar tuner
definitions together, such that each tuner definition will have a
tuner_params struct for each available video standard. At that point,
the tuner_params[] array element will be chosen based on the video
standard in use.
Signed-off-by: Michael Krufky <mkrufky@m1k.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-01-13 19:10:25 +03:00
int rc , j ;
2007-08-28 04:59:08 +04:00
struct tuner_params * t_params ;
unsigned int freq = params - > frequency ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
tun = priv - > tun ;
2006-02-07 11:25:34 +03:00
2007-08-04 01:32:38 +04:00
for ( j = tun - > count - 1 ; j > 0 ; j - - )
if ( tun - > params [ j ] . type = = TUNER_PARAM_TYPE_RADIO )
break ;
2007-08-28 04:59:08 +04:00
/* default t_params (j=0) will be used if desired type wasn't found */
t_params = & tun - > params [ j ] ;
2007-08-04 01:32:38 +04:00
/* Select Radio 1st IF used */
2007-08-28 04:59:08 +04:00
switch ( t_params - > radio_if ) {
2007-08-04 01:32:38 +04:00
case 0 : /* 10.7 MHz */
freq + = ( unsigned int ) ( 10.7 * 16000 ) ;
2006-02-07 11:25:34 +03:00
break ;
2007-08-04 01:32:38 +04:00
case 1 : /* 33.3 MHz */
freq + = ( unsigned int ) ( 33.3 * 16000 ) ;
break ;
case 2 : /* 41.3 MHz */
freq + = ( unsigned int ) ( 41.3 * 16000 ) ;
break ;
default :
2008-04-22 21:41:44 +04:00
tuner_warn ( " Unsupported radio_if value %d \n " ,
t_params - > radio_if ) ;
2007-08-28 04:59:08 +04:00
return 0 ;
2006-02-07 11:25:34 +03:00
}
2005-04-17 02:20:36 +04:00
2007-08-04 01:32:38 +04:00
/* Bandswitch byte */
2008-04-22 21:41:51 +04:00
simple_radio_bandswitch ( fe , & buffer [ 0 ] ) ;
2007-08-04 01:32:38 +04:00
2007-08-28 04:59:08 +04:00
buffer [ 2 ] = ( t_params - > ranges [ 0 ] . config & ~ TUNER_RATIO_MASK ) |
2007-08-04 01:32:38 +04:00
TUNER_RATIO_SELECT_50 ; /* 50 kHz step */
/* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps
freq * ( 1 Mhz / 16000 V4L steps ) * ( 20 PLL steps / 1 MHz ) =
freq * ( 1 / 800 ) */
div = ( freq + 400 ) / 800 ;
2007-08-28 04:59:08 +04:00
if ( t_params - > cb_first_if_lower_freq & & div < priv - > last_div ) {
2006-01-15 20:04:52 +03:00
buffer [ 0 ] = buffer [ 2 ] ;
buffer [ 1 ] = buffer [ 3 ] ;
buffer [ 2 ] = ( div > > 8 ) & 0x7f ;
buffer [ 3 ] = div & 0xff ;
} else {
buffer [ 0 ] = ( div > > 8 ) & 0x7f ;
buffer [ 1 ] = div & 0xff ;
}
2005-04-17 02:20:36 +04:00
tuner_dbg ( " radio 0x%02x 0x%02x 0x%02x 0x%02x \n " ,
2008-04-22 21:41:44 +04:00
buffer [ 0 ] , buffer [ 1 ] , buffer [ 2 ] , buffer [ 3 ] ) ;
2007-08-11 22:42:12 +04:00
priv - > last_div = div ;
2005-04-17 02:20:36 +04:00
2007-08-28 04:59:08 +04:00
if ( t_params - > has_tda9887 ) {
2006-06-25 22:34:39 +04:00
int config = 0 ;
2007-10-19 02:56:47 +04:00
struct v4l2_priv_tun_config tda9887_cfg ;
tda9887_cfg . tuner = TUNER_TDA9887 ;
tda9887_cfg . priv = & config ;
2008-04-22 21:41:44 +04:00
if ( t_params - > port1_active & &
! t_params - > port1_fm_high_sensitivity )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT1_ACTIVE ;
2008-04-22 21:41:44 +04:00
if ( t_params - > port2_active & &
! t_params - > port2_fm_high_sensitivity )
2006-06-25 22:34:39 +04:00
config | = TDA9887_PORT2_ACTIVE ;
2007-08-28 04:59:08 +04:00
if ( t_params - > intercarrier_mode )
2006-06-25 22:34:39 +04:00
config | = TDA9887_INTERCARRIER ;
2007-08-28 04:59:08 +04:00
/* if (t_params->port1_set_for_fm_mono)
2006-06-25 22:34:39 +04:00
config & = ~ TDA9887_PORT1_ACTIVE ; */
2007-08-28 04:59:08 +04:00
if ( t_params - > fm_gain_normal )
2006-12-04 14:31:38 +03:00
config | = TDA9887_GAIN_NORMAL ;
2007-08-28 04:59:08 +04:00
if ( t_params - > radio_if = = 2 )
2007-08-04 01:32:38 +04:00
config | = TDA9887_RIF_41_3 ;
2007-10-19 02:56:47 +04:00
i2c_clients_command ( priv - > i2c_props . adap , TUNER_SET_CONFIG ,
2008-04-22 21:41:44 +04:00
& tda9887_cfg ) ;
2006-06-25 22:34:39 +04:00
}
2008-04-22 21:41:44 +04:00
rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 4 ) ;
if ( 4 ! = rc )
tuner_warn ( " i2c i/o error: rc == %d (should be 4) \n " , rc ) ;
2007-08-28 04:59:08 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 04:59:08 +04:00
static int simple_set_params ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2007-08-11 22:42:12 +04:00
{
2007-08-28 04:59:08 +04:00
struct tuner_simple_priv * priv = fe - > tuner_priv ;
int ret = - EINVAL ;
2008-04-22 21:45:52 +04:00
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
2007-08-28 04:59:08 +04:00
switch ( params - > mode ) {
case V4L2_TUNER_RADIO :
ret = simple_set_radio_freq ( fe , params ) ;
priv - > frequency = params - > frequency * 125 / 2 ;
break ;
case V4L2_TUNER_ANALOG_TV :
case V4L2_TUNER_DIGITAL_TV :
ret = simple_set_tv_freq ( fe , params ) ;
priv - > frequency = params - > frequency * 62500 ;
break ;
}
2008-04-22 21:45:52 +04:00
priv - > bandwidth = 0 ;
2007-08-28 04:59:08 +04:00
return ret ;
}
2008-04-22 21:45:53 +04:00
static void simple_set_dvb ( struct dvb_frontend * fe , u8 * buf ,
const struct dvb_frontend_parameters * params )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
switch ( priv - > type ) {
case TUNER_PHILIPS_FMD1216ME_MK3 :
if ( params - > u . ofdm . bandwidth = = BANDWIDTH_8_MHZ & &
params - > frequency > = 158870000 )
buf [ 3 ] | = 0x08 ;
break ;
default :
break ;
}
}
2008-04-22 21:45:52 +04:00
static int simple_dvb_configure ( struct dvb_frontend * fe , u8 * buf ,
const struct dvb_frontend_parameters * params )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
struct tunertype * tun = priv - > tun ;
static struct tuner_params * t_params ;
u8 config , cb ;
u32 div ;
int ret , frequency = params - > frequency / 62500 ;
t_params = simple_tuner_params ( fe , TUNER_PARAM_TYPE_DIGITAL ) ;
ret = simple_config_lookup ( fe , t_params , & frequency , & config , & cb ) ;
if ( ret < 0 )
return ret ;
div = ( ( frequency + t_params - > iffreq ) * 62500 + offset +
tun - > stepsize / 2 ) / tun - > stepsize ;
buf [ 0 ] = div > > 8 ;
buf [ 1 ] = div & 0xff ;
buf [ 2 ] = config ;
buf [ 3 ] = cb ;
2008-04-22 21:45:53 +04:00
simple_set_dvb ( fe , buf , params ) ;
2008-04-22 21:45:52 +04:00
tuner_dbg ( " %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x \n " ,
tun - > name , div , buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] ) ;
/* calculate the frequency we set it to */
return ( div * tun - > stepsize ) - t_params - > iffreq ;
}
static int simple_dvb_calc_regs ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * params ,
u8 * buf , int buf_len )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
int ret ;
u32 frequency ;
if ( buf_len < 5 )
return - EINVAL ;
ret = simple_dvb_configure ( fe , buf + 1 , params ) ;
if ( ret < 0 )
return ret ;
else
frequency = ret ;
buf [ 0 ] = priv - > i2c_props . addr ;
priv - > frequency = frequency ;
priv - > bandwidth = ( fe - > ops . info . type = = FE_OFDM ) ?
params - > u . ofdm . bandwidth : 0 ;
return 5 ;
}
static int simple_dvb_set_params ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * params )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
u32 prev_freq , prev_bw ;
int ret ;
u8 buf [ 5 ] ;
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
prev_freq = priv - > frequency ;
prev_bw = priv - > bandwidth ;
ret = simple_dvb_calc_regs ( fe , params , buf , 5 ) ;
if ( ret ! = 5 )
goto fail ;
/* put analog demod in standby when tuning digital */
if ( fe - > ops . analog_ops . standby )
fe - > ops . analog_ops . standby ( fe ) ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
/* buf[0] contains the i2c address, but *
* we already have it in i2c_props . addr */
ret = tuner_i2c_xfer_send ( & priv - > i2c_props , buf + 1 , 4 ) ;
if ( ret ! = 4 )
goto fail ;
return 0 ;
fail :
/* calc_regs sets frequency and bandwidth. if we failed, unset them */
priv - > frequency = prev_freq ;
priv - > bandwidth = prev_bw ;
return ret ;
}
2007-08-28 04:59:08 +04:00
2008-04-22 21:45:53 +04:00
static int simple_init ( struct dvb_frontend * fe )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
if ( priv - > tun - > initdata ) {
int ret ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
ret = tuner_i2c_xfer_send ( & priv - > i2c_props ,
priv - > tun - > initdata + 1 ,
priv - > tun - > initdata [ 0 ] ) ;
if ( ret ! = priv - > tun - > initdata [ 0 ] )
return ret ;
}
return 0 ;
}
static int simple_sleep ( struct dvb_frontend * fe )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
if ( priv - > i2c_props . adap = = NULL )
return - EINVAL ;
if ( priv - > tun - > sleepdata ) {
int ret ;
if ( fe - > ops . i2c_gate_ctrl )
fe - > ops . i2c_gate_ctrl ( fe , 1 ) ;
ret = tuner_i2c_xfer_send ( & priv - > i2c_props ,
priv - > tun - > sleepdata + 1 ,
priv - > tun - > sleepdata [ 0 ] ) ;
if ( ret ! = priv - > tun - > sleepdata [ 0 ] )
return ret ;
}
return 0 ;
}
2007-08-28 04:59:08 +04:00
static int simple_release ( struct dvb_frontend * fe )
{
2008-04-22 21:45:52 +04:00
struct tuner_simple_priv * priv = fe - > tuner_priv ;
mutex_lock ( & tuner_simple_list_mutex ) ;
if ( priv )
hybrid_tuner_release_state ( priv ) ;
mutex_unlock ( & tuner_simple_list_mutex ) ;
2007-08-28 04:59:08 +04:00
fe - > tuner_priv = NULL ;
return 0 ;
}
static int simple_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
* frequency = priv - > frequency ;
return 0 ;
2007-08-11 22:42:12 +04:00
}
2008-04-22 21:45:52 +04:00
static int simple_get_bandwidth ( struct dvb_frontend * fe , u32 * bandwidth )
{
struct tuner_simple_priv * priv = fe - > tuner_priv ;
* bandwidth = priv - > bandwidth ;
return 0 ;
}
2007-08-28 04:59:08 +04:00
static struct dvb_tuner_ops simple_tuner_ops = {
2008-04-22 21:45:53 +04:00
. init = simple_init ,
. sleep = simple_sleep ,
2007-08-28 04:59:08 +04:00
. set_analog_params = simple_set_params ,
2008-04-22 21:45:52 +04:00
. set_params = simple_dvb_set_params ,
. calc_regs = simple_dvb_calc_regs ,
2007-08-31 23:39:39 +04:00
. release = simple_release ,
. get_frequency = simple_get_frequency ,
2008-04-22 21:45:52 +04:00
. get_bandwidth = simple_get_bandwidth ,
2007-08-31 23:39:39 +04:00
. get_status = simple_get_status ,
. get_rf_strength = simple_get_rf_strength ,
2007-06-06 23:17:57 +04:00
} ;
2007-08-28 04:59:08 +04:00
struct dvb_frontend * simple_tuner_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c_adap ,
u8 i2c_addr ,
2008-04-22 21:41:51 +04:00
unsigned int type )
2005-04-17 02:20:36 +04:00
{
2007-08-11 22:42:12 +04:00
struct tuner_simple_priv * priv = NULL ;
2008-04-22 21:45:52 +04:00
int instance ;
2007-08-11 22:42:12 +04:00
2008-04-22 21:41:50 +04:00
if ( type > = tuner_count ) {
printk ( KERN_WARNING " %s: invalid tuner type: %d (max: %d) \n " ,
__FUNCTION__ , type , tuner_count - 1 ) ;
return NULL ;
}
2008-04-22 21:45:52 +04:00
mutex_lock ( & tuner_simple_list_mutex ) ;
instance = hybrid_tuner_request_state ( struct tuner_simple_priv , priv ,
hybrid_tuner_instance_list ,
i2c_adap , i2c_addr ,
" tuner-simple " ) ;
switch ( instance ) {
case 0 :
mutex_unlock ( & tuner_simple_list_mutex ) ;
2007-08-28 04:59:08 +04:00
return NULL ;
2008-04-22 21:45:52 +04:00
break ;
case 1 :
fe - > tuner_priv = priv ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:45:52 +04:00
priv - > type = type ;
priv - > tun = & tuners [ type ] ;
break ;
default :
fe - > tuner_priv = priv ;
break ;
}
2008-04-22 21:41:53 +04:00
2008-04-22 21:45:52 +04:00
mutex_unlock ( & tuner_simple_list_mutex ) ;
2007-08-21 08:24:42 +04:00
2008-04-22 21:41:44 +04:00
memcpy ( & fe - > ops . tuner_ops , & simple_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:45:52 +04:00
tuner_info ( " type set to %d (%s) \n " , type , priv - > tun - > name ) ;
2005-06-29 07:45:21 +04:00
2008-04-22 21:41:51 +04:00
strlcpy ( fe - > ops . tuner_ops . info . name , priv - > tun - > name ,
2008-04-22 21:41:44 +04:00
sizeof ( fe - > ops . tuner_ops . info . name ) ) ;
2007-08-28 04:59:08 +04:00
return fe ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 04:59:08 +04:00
EXPORT_SYMBOL_GPL ( simple_tuner_attach ) ;
MODULE_DESCRIPTION ( " Simple 4-control-bytes style tuner driver " ) ;
MODULE_AUTHOR ( " Ralph Metzler, Gerd Knorr, Gunther Mayer " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - basic - offset : 8
* End :
*/