2007-10-02 18:57:03 +04:00
/* tuner-xc2028
*
2008-04-21 13:58:48 +04:00
* Copyright ( c ) 2007 - 2008 Mauro Carvalho Chehab ( mchehab @ infradead . org )
2007-10-30 05:44:18 +03:00
*
2007-07-18 17:29:10 +04:00
* Copyright ( c ) 2007 Michel Ludwig ( michel . ludwig @ gmail . com )
* - frontend interface
2007-10-30 05:44:18 +03:00
*
2007-10-02 18:57:03 +04:00
* This code is placed under the terms of the GNU General Public License v2
*/
# include <linux/i2c.h>
# include <asm/div64.h>
# include <linux/firmware.h>
2007-11-01 23:47:42 +03:00
# include <linux/videodev2.h>
2007-10-02 18:57:03 +04:00
# include <linux/delay.h>
2007-07-18 17:29:10 +04:00
# include <media/tuner.h>
2007-09-28 01:27:03 +04:00
# include <linux/mutex.h>
2007-10-23 22:24:06 +04:00
# include "tuner-i2c.h"
2007-10-02 18:57:03 +04:00
# include "tuner-xc2028.h"
2007-10-24 16:22:08 +04:00
# include "tuner-xc2028-types.h"
2007-10-02 18:57:03 +04:00
2007-07-18 17:29:10 +04:00
# include <linux/dvb/frontend.h>
# include "dvb_frontend.h"
2007-11-16 22:28:21 +03:00
2007-11-15 15:44:30 +03:00
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " enable verbose debug messages " ) ;
2007-11-15 17:58:00 +03:00
static char audio_std [ 8 ] ;
module_param_string ( audio_std , audio_std , sizeof ( audio_std ) , 0 ) ;
MODULE_PARM_DESC ( audio_std ,
" Audio standard. XC3028 audio decoder explicitly "
" needs to know what audio \n "
" standard is needed for some video standards with audio A2 or NICAM. \n "
" The valid values are: \n "
" A2 \n "
" A2/A \n "
" A2/B \n "
" NICAM \n "
" NICAM/A \n "
" NICAM/B \n " ) ;
2008-04-22 21:46:24 +04:00
static char firmware_name [ FIRMWARE_NAME_MAX ] ;
module_param_string ( firmware_name , firmware_name , sizeof ( firmware_name ) , 0 ) ;
MODULE_PARM_DESC ( firmware_name , " Firmware file name. Allows overriding the "
" default firmware name \n " ) ;
2008-04-19 04:22:50 +04:00
static LIST_HEAD ( hybrid_tuner_instance_list ) ;
2007-11-19 10:45:38 +03:00
static DEFINE_MUTEX ( xc2028_list_mutex ) ;
2007-10-24 16:22:08 +04:00
/* struct for storing firmware table */
struct firmware_description {
unsigned int type ;
v4l2_std_id id ;
2007-11-26 01:26:36 +03:00
__u16 int_freq ;
2007-10-24 16:22:08 +04:00
unsigned char * ptr ;
unsigned int size ;
} ;
2007-10-02 18:57:03 +04:00
2007-11-19 17:22:03 +03:00
struct firmware_properties {
unsigned int type ;
v4l2_std_id id ;
v4l2_std_id std_req ;
2007-11-26 01:26:36 +03:00
__u16 int_freq ;
2007-11-19 17:22:03 +03:00
unsigned int scode_table ;
int scode_nr ;
} ;
2007-10-02 18:57:03 +04:00
struct xc2028_data {
2008-04-19 04:22:50 +04:00
struct list_head hybrid_tuner_instance_list ;
2007-10-23 22:24:06 +04:00
struct tuner_i2c_props i2c_props ;
int ( * tuner_callback ) ( void * dev ,
int command , int arg ) ;
void * video_dev ;
2007-10-24 16:22:08 +04:00
__u32 frequency ;
struct firmware_description * firm ;
int firm_size ;
2007-11-19 12:06:08 +03:00
__u16 firm_version ;
2007-10-24 16:22:08 +04:00
2007-11-19 17:35:45 +03:00
__u16 hwmodel ;
__u16 hwvers ;
2007-10-24 16:22:08 +04:00
struct xc2028_ctrl ctrl ;
2007-10-23 22:24:06 +04:00
2007-11-19 17:22:03 +03:00
struct firmware_properties cur_fw ;
2007-10-23 22:24:06 +04:00
struct mutex lock ;
2007-10-02 18:57:03 +04:00
} ;
2007-11-19 10:14:23 +03:00
# define i2c_send(priv, buf, size) ({ \
int _rc ; \
_rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buf , size ) ; \
if ( size ! = _rc ) \
tuner_info ( " i2c output error: rc = %d (should be %d) \n " , \
_rc , ( int ) size ) ; \
_rc ; \
} )
# define i2c_rcv(priv, buf, size) ({ \
int _rc ; \
_rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buf , size ) ; \
if ( size ! = _rc ) \
2007-11-15 15:44:30 +03:00
tuner_err ( " i2c input error: rc = %d (should be %d) \n " , \
2007-11-19 10:14:23 +03:00
_rc , ( int ) size ) ; \
_rc ; \
} )
2007-11-01 23:47:42 +03:00
2007-11-19 10:31:58 +03:00
# define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
int _rc ; \
_rc = tuner_i2c_xfer_send_recv ( & priv - > i2c_props , obuf , osize , \
ibuf , isize ) ; \
if ( isize ! = _rc ) \
tuner_err ( " i2c input error: rc = %d (should be %d) \n " , \
_rc , ( int ) isize ) ; \
_rc ; \
} )
2007-11-19 10:14:23 +03:00
# define send_seq(priv, data...) ({ \
2007-10-23 22:24:06 +04:00
static u8 _val [ ] = data ; \
2007-11-19 10:14:23 +03:00
int _rc ; \
2007-10-02 18:57:03 +04:00
if ( sizeof ( _val ) ! = \
2007-11-19 10:14:23 +03:00
( _rc = tuner_i2c_xfer_send ( & priv - > i2c_props , \
2007-10-23 22:24:06 +04:00
_val , sizeof ( _val ) ) ) ) { \
2007-11-19 10:14:23 +03:00
tuner_err ( " Error on line %d: %d \n " , __LINE__ , _rc ) ; \
} else \
msleep ( 10 ) ; \
_rc ; \
} )
2007-10-02 18:57:03 +04:00
2008-04-18 04:41:16 +04:00
static int xc2028_get_reg ( struct xc2028_data * priv , u16 reg , u16 * val )
2007-10-02 18:57:03 +04:00
{
2007-11-05 14:41:50 +03:00
unsigned char buf [ 2 ] ;
2007-11-19 10:31:58 +03:00
unsigned char ibuf [ 2 ] ;
2007-10-23 22:24:06 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s %04x called \n " , __func__ , reg ) ;
2007-10-02 18:57:03 +04:00
2007-11-19 10:31:58 +03:00
buf [ 0 ] = reg > > 8 ;
2007-11-05 15:07:13 +03:00
buf [ 1 ] = ( unsigned char ) reg ;
2007-10-02 18:57:03 +04:00
2007-11-19 10:31:58 +03:00
if ( i2c_send_recv ( priv , buf , 2 , ibuf , 2 ) ! = 2 )
return - EIO ;
2007-10-02 18:57:03 +04:00
2007-11-19 10:31:58 +03:00
* val = ( ibuf [ 1 ] ) | ( ibuf [ 0 ] < < 8 ) ;
return 0 ;
2007-10-02 18:57:03 +04:00
}
2007-12-02 12:30:50 +03:00
# define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
2008-04-22 21:41:45 +04:00
static void dump_firm_type_and_int_freq ( unsigned int type , u16 int_freq )
2007-11-15 01:30:28 +03:00
{
if ( type & BASE )
printk ( " BASE " ) ;
2007-11-15 14:43:53 +03:00
if ( type & INIT1 )
printk ( " INIT1 " ) ;
2007-11-15 01:30:28 +03:00
if ( type & F8MHZ )
printk ( " F8MHZ " ) ;
if ( type & MTS )
printk ( " MTS " ) ;
if ( type & D2620 )
printk ( " D2620 " ) ;
if ( type & D2633 )
printk ( " D2633 " ) ;
if ( type & DTV6 )
printk ( " DTV6 " ) ;
if ( type & QAM )
printk ( " QAM " ) ;
if ( type & DTV7 )
printk ( " DTV7 " ) ;
if ( type & DTV78 )
printk ( " DTV78 " ) ;
if ( type & DTV8 )
printk ( " DTV8 " ) ;
if ( type & FM )
printk ( " FM " ) ;
if ( type & INPUT1 )
printk ( " INPUT1 " ) ;
if ( type & LCD )
printk ( " LCD " ) ;
if ( type & NOGD )
printk ( " NOGD " ) ;
if ( type & MONO )
printk ( " MONO " ) ;
if ( type & ATSC )
printk ( " ATSC " ) ;
if ( type & IF )
printk ( " IF " ) ;
if ( type & LG60 )
printk ( " LG60 " ) ;
if ( type & ATI638 )
printk ( " ATI638 " ) ;
if ( type & OREN538 )
printk ( " OREN538 " ) ;
if ( type & OREN36 )
printk ( " OREN36 " ) ;
if ( type & TOYOTA388 )
printk ( " TOYOTA388 " ) ;
if ( type & TOYOTA794 )
printk ( " TOYOTA794 " ) ;
if ( type & DIBCOM52 )
printk ( " DIBCOM52 " ) ;
if ( type & ZARLINK456 )
printk ( " ZARLINK456 " ) ;
if ( type & CHINA )
printk ( " CHINA " ) ;
if ( type & F6MHZ )
printk ( " F6MHZ " ) ;
if ( type & INPUT2 )
printk ( " INPUT2 " ) ;
if ( type & SCODE )
printk ( " SCODE " ) ;
2007-12-02 12:30:50 +03:00
if ( type & HAS_IF )
printk ( " HAS_IF_%d " , int_freq ) ;
2007-11-15 01:30:28 +03:00
}
2007-11-16 22:28:21 +03:00
static v4l2_std_id parse_audio_std_option ( void )
2007-11-15 17:58:00 +03:00
{
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " A2 " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_A2 ;
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " A2/A " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_A2_A ;
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " A2/B " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_A2_B ;
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " NICAM " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_NICAM ;
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " NICAM/A " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_NICAM_A ;
2007-11-19 10:16:47 +03:00
if ( strcasecmp ( audio_std , " NICAM/B " ) = = 0 )
2007-11-15 17:58:00 +03:00
return V4L2_STD_NICAM_B ;
return 0 ;
}
2007-11-01 23:47:42 +03:00
static void free_firmware ( struct xc2028_data * priv )
2007-10-02 18:57:03 +04:00
{
2007-10-24 16:22:08 +04:00
int i ;
2008-04-18 04:40:53 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-24 16:22:08 +04:00
if ( ! priv - > firm )
return ;
2007-11-01 23:47:42 +03:00
for ( i = 0 ; i < priv - > firm_size ; i + + )
kfree ( priv - > firm [ i ] . ptr ) ;
2007-10-24 16:22:08 +04:00
kfree ( priv - > firm ) ;
2007-11-01 23:47:42 +03:00
priv - > firm = NULL ;
2007-11-19 12:06:08 +03:00
priv - > firm_size = 0 ;
2007-11-19 17:22:03 +03:00
memset ( & priv - > cur_fw , 0 , sizeof ( priv - > cur_fw ) ) ;
2007-10-24 16:22:08 +04:00
}
2007-11-01 23:47:42 +03:00
static int load_all_firmwares ( struct dvb_frontend * fe )
2007-10-24 16:22:08 +04:00
{
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-01 23:47:42 +03:00
const struct firmware * fw = NULL ;
2008-05-24 03:13:34 +04:00
const unsigned char * p , * endp ;
2007-11-01 23:47:42 +03:00
int rc = 0 ;
int n , n_array ;
2007-10-24 16:22:08 +04:00
char name [ 33 ] ;
2008-04-22 21:46:24 +04:00
char * fname ;
2007-10-02 18:57:03 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-23 22:24:06 +04:00
2008-04-22 21:46:24 +04:00
if ( ! firmware_name [ 0 ] )
fname = priv - > ctrl . fname ;
else
fname = firmware_name ;
tuner_dbg ( " Reading firmware %s \n " , fname ) ;
rc = request_firmware ( & fw , fname , & priv - > i2c_props . adap - > dev ) ;
2007-10-02 18:57:03 +04:00
if ( rc < 0 ) {
2007-11-01 23:47:42 +03:00
if ( rc = = - ENOENT )
2007-11-15 15:44:30 +03:00
tuner_err ( " Error: firmware %s not found. \n " ,
2008-04-22 21:46:24 +04:00
fname ) ;
2007-07-18 20:33:23 +04:00
else
2007-11-15 15:44:30 +03:00
tuner_err ( " Error %d while requesting firmware %s \n " ,
2008-04-22 21:46:24 +04:00
rc , fname ) ;
2007-07-18 20:33:23 +04:00
2007-10-02 18:57:03 +04:00
return rc ;
}
2007-11-01 23:47:42 +03:00
p = fw - > data ;
endp = p + fw - > size ;
2007-10-02 18:57:03 +04:00
2007-11-19 12:06:08 +03:00
if ( fw - > size < sizeof ( name ) - 1 + 2 + 2 ) {
tuner_err ( " Error: firmware file %s has invalid size! \n " ,
2008-04-22 21:46:24 +04:00
fname ) ;
2007-11-19 12:06:08 +03:00
goto corrupt ;
2007-10-02 18:57:03 +04:00
}
2007-10-24 16:22:08 +04:00
2007-11-01 23:47:42 +03:00
memcpy ( name , p , sizeof ( name ) - 1 ) ;
name [ sizeof ( name ) - 1 ] = 0 ;
p + = sizeof ( name ) - 1 ;
2007-10-24 16:22:08 +04:00
2007-11-19 12:06:08 +03:00
priv - > firm_version = le16_to_cpu ( * ( __u16 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = 2 ;
2007-11-01 23:47:42 +03:00
n_array = le16_to_cpu ( * ( __u16 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = 2 ;
2007-11-19 12:06:08 +03:00
tuner_info ( " Loading %d firmware images from %s, type: %s, ver %d.%d \n " ,
2008-04-22 21:46:24 +04:00
n_array , fname , name ,
2007-11-19 12:06:08 +03:00
priv - > firm_version > > 8 , priv - > firm_version & 0xff ) ;
2007-10-24 16:22:08 +04:00
2007-11-01 23:47:42 +03:00
priv - > firm = kzalloc ( sizeof ( * priv - > firm ) * n_array , GFP_KERNEL ) ;
2007-11-19 12:06:08 +03:00
if ( priv - > firm = = NULL ) {
tuner_err ( " Not enough memory to load firmware file. \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - ENOMEM ;
2007-11-19 12:06:08 +03:00
goto err ;
2007-10-02 18:57:03 +04:00
}
2007-10-24 16:22:08 +04:00
priv - > firm_size = n_array ;
2007-11-19 12:06:08 +03:00
2007-11-01 23:47:42 +03:00
n = - 1 ;
while ( p < endp ) {
2007-10-24 16:22:08 +04:00
__u32 type , size ;
v4l2_std_id id ;
2007-11-26 01:26:36 +03:00
__u16 int_freq = 0 ;
2007-10-24 16:22:08 +04:00
n + + ;
if ( n > = n_array ) {
2007-11-19 12:06:08 +03:00
tuner_err ( " More firmware images in file than "
" were expected! \n " ) ;
2007-10-24 16:22:08 +04:00
goto corrupt ;
}
/* Checks if there's enough bytes to read */
2007-11-01 23:47:42 +03:00
if ( p + sizeof ( type ) + sizeof ( id ) + sizeof ( size ) > endp ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware header is incomplete! \n " ) ;
2007-10-24 16:22:08 +04:00
goto corrupt ;
}
2007-11-01 23:47:42 +03:00
type = le32_to_cpu ( * ( __u32 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = sizeof ( type ) ;
2007-11-01 23:47:42 +03:00
id = le64_to_cpu ( * ( v4l2_std_id * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = sizeof ( id ) ;
2007-11-26 01:26:36 +03:00
if ( type & HAS_IF ) {
int_freq = le16_to_cpu ( * ( __u16 * ) p ) ;
p + = sizeof ( int_freq ) ;
}
2007-11-16 13:19:35 +03:00
size = le32_to_cpu ( * ( __u32 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = sizeof ( size ) ;
2007-11-01 23:47:42 +03:00
if ( ( ! size ) | | ( size + p > endp ) ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware type " ) ;
2007-11-15 01:30:28 +03:00
dump_firm_type ( type ) ;
2007-11-16 22:28:21 +03:00
printk ( " (%x), id %llx is corrupted "
" (size=%d, expected %d) \n " ,
2007-11-19 10:38:53 +03:00
type , ( unsigned long long ) id ,
2007-11-16 22:28:21 +03:00
( unsigned ) ( endp - p ) , size ) ;
2007-10-24 16:22:08 +04:00
goto corrupt ;
}
2007-11-01 23:47:42 +03:00
priv - > firm [ n ] . ptr = kzalloc ( size , GFP_KERNEL ) ;
2007-11-19 12:06:08 +03:00
if ( priv - > firm [ n ] . ptr = = NULL ) {
tuner_err ( " Not enough memory to load firmware file. \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - ENOMEM ;
2007-10-24 16:22:08 +04:00
goto err ;
}
2007-11-19 12:06:08 +03:00
tuner_dbg ( " Reading firmware type " ) ;
if ( debug ) {
2007-12-02 12:30:50 +03:00
dump_firm_type_and_int_freq ( type , int_freq ) ;
2007-11-19 12:06:08 +03:00
printk ( " (%x), id %llx, size=%d. \n " ,
2007-12-02 12:30:50 +03:00
type , ( unsigned long long ) id , size ) ;
2007-11-19 12:06:08 +03:00
}
2007-10-24 16:22:08 +04:00
memcpy ( priv - > firm [ n ] . ptr , p , size ) ;
priv - > firm [ n ] . type = type ;
priv - > firm [ n ] . id = id ;
priv - > firm [ n ] . size = size ;
2007-11-26 01:26:36 +03:00
priv - > firm [ n ] . int_freq = int_freq ;
2007-10-24 16:22:08 +04:00
p + = size ;
}
2007-11-01 23:47:42 +03:00
if ( n + 1 ! = priv - > firm_size ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware file is incomplete! \n " ) ;
2007-10-24 16:22:08 +04:00
goto corrupt ;
}
goto done ;
corrupt :
2007-11-01 23:47:42 +03:00
rc = - EINVAL ;
2007-11-15 15:44:30 +03:00
tuner_err ( " Error: firmware file is corrupted! \n " ) ;
2007-10-24 16:22:08 +04:00
err :
2007-11-19 12:06:08 +03:00
tuner_info ( " Releasing partially loaded firmware file. \n " ) ;
2007-10-24 16:22:08 +04:00
free_firmware ( priv ) ;
done :
release_firmware ( fw ) ;
2007-11-19 12:06:08 +03:00
if ( rc = = 0 )
tuner_dbg ( " Firmware files loaded. \n " ) ;
2007-10-24 16:22:08 +04:00
return rc ;
}
2007-11-15 14:43:53 +03:00
static int seek_firmware ( struct dvb_frontend * fe , unsigned int type ,
v4l2_std_id * id )
2007-10-24 16:22:08 +04:00
{
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-19 16:04:06 +03:00
int i , best_i = - 1 , best_nr_matches = 0 ;
2008-04-21 13:58:48 +04:00
unsigned int type_mask = 0 ;
2007-10-24 16:22:08 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called, want type= " , __func__ ) ;
2007-11-19 16:04:06 +03:00
if ( debug ) {
dump_firm_type ( type ) ;
printk ( " (%x), id %016llx. \n " , type , ( unsigned long long ) * id ) ;
}
2007-10-24 16:22:08 +04:00
if ( ! priv - > firm ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Error! firmware not loaded \n " ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
2007-11-15 14:43:53 +03:00
if ( ( ( type & ~ SCODE ) = = 0 ) & & ( * id = = 0 ) )
2007-11-01 23:47:42 +03:00
* id = V4L2_STD_PAL ;
2007-10-24 16:22:08 +04:00
2007-11-19 17:22:03 +03:00
if ( type & BASE )
2008-04-21 13:58:48 +04:00
type_mask = BASE_TYPES ;
2007-12-02 15:30:55 +03:00
else if ( type & SCODE ) {
2007-11-19 17:22:03 +03:00
type & = SCODE_TYPES ;
2008-04-21 13:58:48 +04:00
type_mask = SCODE_TYPES & ~ HAS_IF ;
2007-12-02 15:30:55 +03:00
} else if ( type & DTV_TYPES )
2008-04-21 13:58:48 +04:00
type_mask = DTV_TYPES ;
2007-11-20 05:18:36 +03:00
else if ( type & STD_SPECIFIC_TYPES )
2008-04-21 13:58:48 +04:00
type_mask = STD_SPECIFIC_TYPES ;
type & = type_mask ;
2008-04-25 08:28:10 +04:00
if ( ! ( type & SCODE ) )
2008-04-21 13:58:48 +04:00
type_mask = ~ 0 ;
2007-11-19 17:22:03 +03:00
2007-10-24 16:22:08 +04:00
/* Seek for exact match */
2007-11-01 23:47:42 +03:00
for ( i = 0 ; i < priv - > firm_size ; i + + ) {
2008-04-21 13:58:48 +04:00
if ( ( type = = ( priv - > firm [ i ] . type & type_mask ) ) & &
2007-12-02 15:30:55 +03:00
( * id = = priv - > firm [ i ] . id ) )
2007-10-24 16:22:08 +04:00
goto found ;
}
/* Seek for generic video standard match */
2007-11-01 23:47:42 +03:00
for ( i = 0 ; i < priv - > firm_size ; i + + ) {
2007-11-19 16:04:06 +03:00
v4l2_std_id match_mask ;
int nr_matches ;
2008-04-21 13:58:48 +04:00
if ( type ! = ( priv - > firm [ i ] . type & type_mask ) )
2007-11-19 16:04:06 +03:00
continue ;
match_mask = * id & priv - > firm [ i ] . id ;
if ( ! match_mask )
continue ;
if ( ( * id & match_mask ) = = * id )
goto found ; /* Supports all the requested standards */
nr_matches = hweight64 ( match_mask ) ;
if ( nr_matches > best_nr_matches ) {
best_nr_matches = nr_matches ;
best_i = i ;
}
}
if ( best_nr_matches > 0 ) {
tuner_dbg ( " Selecting best matching firmware (%d bits) for "
" type= " , best_nr_matches ) ;
dump_firm_type ( type ) ;
printk ( " (%x), id %016llx: \n " , type , ( unsigned long long ) * id ) ;
i = best_i ;
goto found ;
2007-10-24 16:22:08 +04:00
}
/*FIXME: Would make sense to seek for type "hint" match ? */
2007-11-19 16:04:06 +03:00
i = - ENOENT ;
2007-11-15 14:43:53 +03:00
goto ret ;
2007-10-24 16:22:08 +04:00
found :
* id = priv - > firm [ i ] . id ;
2007-11-15 14:43:53 +03:00
ret :
2007-11-19 16:04:06 +03:00
tuner_dbg ( " %s firmware for type= " , ( i < 0 ) ? " Can't find " : " Found " ) ;
2007-11-15 15:44:30 +03:00
if ( debug ) {
dump_firm_type ( type ) ;
2007-11-19 10:38:53 +03:00
printk ( " (%x), id %016llx. \n " , type , ( unsigned long long ) * id ) ;
2007-11-15 15:44:30 +03:00
}
2007-11-15 14:43:53 +03:00
return i ;
}
static int load_firmware ( struct dvb_frontend * fe , unsigned int type ,
v4l2_std_id * id )
{
struct xc2028_data * priv = fe - > tuner_priv ;
int pos , rc ;
2007-11-19 15:29:59 +03:00
unsigned char * p , * endp , buf [ priv - > ctrl . max_len ] ;
2007-11-15 14:43:53 +03:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-11-15 14:43:53 +03:00
pos = seek_firmware ( fe , type , id ) ;
if ( pos < 0 )
return pos ;
2007-11-15 15:44:30 +03:00
tuner_info ( " Loading firmware for type= " ) ;
2007-11-19 16:04:06 +03:00
dump_firm_type ( priv - > firm [ pos ] . type ) ;
printk ( " (%x), id %016llx. \n " , priv - > firm [ pos ] . type ,
( unsigned long long ) * id ) ;
2007-11-15 15:44:30 +03:00
2007-11-15 14:43:53 +03:00
p = priv - > firm [ pos ] . ptr ;
endp = p + priv - > firm [ pos ] . size ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
while ( p < endp ) {
2007-10-24 16:22:08 +04:00
__u16 size ;
/* Checks if there's enough bytes to read */
2007-11-01 23:47:42 +03:00
if ( p + sizeof ( size ) > endp ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware chunk size is wrong \n " ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
2007-11-01 23:47:42 +03:00
size = le16_to_cpu ( * ( __u16 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = sizeof ( size ) ;
if ( size = = 0xffff )
return 0 ;
if ( ! size ) {
2007-10-02 18:57:03 +04:00
/* Special callback command received */
2007-10-23 22:24:06 +04:00
rc = priv - > tuner_callback ( priv - > video_dev ,
2007-11-01 23:47:42 +03:00
XC2028_TUNER_RESET , 0 ) ;
if ( rc < 0 ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Error at RESET code %d \n " ,
2007-11-01 23:47:42 +03:00
( * p ) & 0x7f ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
2007-10-02 18:57:03 +04:00
}
continue ;
}
2007-11-16 13:49:49 +03:00
if ( size > = 0xff00 ) {
switch ( size ) {
case 0xff00 :
rc = priv - > tuner_callback ( priv - > video_dev ,
XC2028_RESET_CLK , 0 ) ;
if ( rc < 0 ) {
tuner_err ( " Error at RESET code %d \n " ,
( * p ) & 0x7f ) ;
return - EINVAL ;
}
2007-11-19 10:53:50 +03:00
break ;
2007-11-16 13:49:49 +03:00
default :
tuner_info ( " Invalid RESET code %d \n " ,
size & 0x7f ) ;
return - EINVAL ;
}
2007-11-16 15:43:19 +03:00
continue ;
2007-11-16 13:49:49 +03:00
}
2007-10-24 16:22:08 +04:00
/* Checks for a sleep command */
if ( size & 0x8000 ) {
2007-11-01 23:47:42 +03:00
msleep ( size & 0x7fff ) ;
2007-10-24 16:22:08 +04:00
continue ;
2007-10-02 18:57:03 +04:00
}
2007-10-24 16:22:08 +04:00
if ( ( size + p > endp ) ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " missing bytes: need %d, have %d \n " ,
2007-11-01 23:47:42 +03:00
size , ( int ) ( endp - p ) ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
2007-10-02 18:57:03 +04:00
2007-10-24 16:22:08 +04:00
buf [ 0 ] = * p ;
2007-10-02 18:57:03 +04:00
p + + ;
2007-10-24 16:22:08 +04:00
size - - ;
2007-10-02 18:57:03 +04:00
2007-10-24 16:22:08 +04:00
/* Sends message chunks */
2007-11-01 23:47:42 +03:00
while ( size > 0 ) {
2007-11-19 15:29:59 +03:00
int len = ( size < priv - > ctrl . max_len - 1 ) ?
size : priv - > ctrl . max_len - 1 ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
memcpy ( buf + 1 , p , len ) ;
2007-10-02 18:57:03 +04:00
2007-11-19 10:14:23 +03:00
rc = i2c_send ( priv , buf , len + 1 ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " %d returned from send \n " , rc ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
p + = len ;
size - = len ;
}
}
2007-11-15 01:30:28 +03:00
return 0 ;
2007-10-02 18:57:03 +04:00
}
2007-11-15 14:43:53 +03:00
static int load_scode ( struct dvb_frontend * fe , unsigned int type ,
2007-11-26 01:26:36 +03:00
v4l2_std_id * id , __u16 int_freq , int scode )
2007-11-15 14:43:53 +03:00
{
struct xc2028_data * priv = fe - > tuner_priv ;
int pos , rc ;
unsigned char * p ;
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-11-15 14:43:53 +03:00
2007-11-26 01:26:36 +03:00
if ( ! int_freq ) {
pos = seek_firmware ( fe , type , id ) ;
if ( pos < 0 )
return pos ;
} else {
for ( pos = 0 ; pos < priv - > firm_size ; pos + + ) {
if ( ( priv - > firm [ pos ] . int_freq = = int_freq ) & &
2007-12-02 12:54:17 +03:00
( priv - > firm [ pos ] . type & HAS_IF ) )
2007-11-26 01:26:36 +03:00
break ;
}
if ( pos = = priv - > firm_size )
return - ENOENT ;
}
2007-11-15 14:43:53 +03:00
p = priv - > firm [ pos ] . ptr ;
2007-12-02 12:54:17 +03:00
if ( priv - > firm [ pos ] . type & HAS_IF ) {
2007-11-26 01:26:36 +03:00
if ( priv - > firm [ pos ] . size ! = 12 * 16 | | scode > = 16 )
return - EINVAL ;
p + = 12 * scode ;
} else {
/* 16 SCODE entries per file; each SCODE entry is 12 bytes and
* has a 2 - byte size header in the firmware format . */
if ( priv - > firm [ pos ] . size ! = 14 * 16 | | scode > = 16 | |
le16_to_cpu ( * ( __u16 * ) ( p + 14 * scode ) ) ! = 12 )
return - EINVAL ;
p + = 14 * scode + 2 ;
}
2007-11-15 14:43:53 +03:00
2007-11-19 16:12:45 +03:00
tuner_info ( " Loading SCODE for type= " ) ;
2007-12-02 12:30:50 +03:00
dump_firm_type_and_int_freq ( priv - > firm [ pos ] . type ,
priv - > firm [ pos ] . int_freq ) ;
2007-11-19 16:12:45 +03:00
printk ( " (%x), id %016llx. \n " , priv - > firm [ pos ] . type ,
( unsigned long long ) * id ) ;
2007-11-19 12:06:08 +03:00
if ( priv - > firm_version < 0x0202 )
2007-11-19 10:14:23 +03:00
rc = send_seq ( priv , { 0x20 , 0x00 , 0x00 , 0x00 } ) ;
else
rc = send_seq ( priv , { 0xa0 , 0x00 , 0x00 , 0x00 } ) ;
if ( rc < 0 )
return - EIO ;
2007-11-15 14:43:53 +03:00
2007-11-26 01:26:36 +03:00
rc = i2c_send ( priv , p , 12 ) ;
2007-11-19 10:14:23 +03:00
if ( rc < 0 )
return - EIO ;
2007-11-15 14:43:53 +03:00
2007-11-19 10:14:23 +03:00
rc = send_seq ( priv , { 0x00 , 0x8c } ) ;
if ( rc < 0 )
return - EIO ;
2007-11-15 14:43:53 +03:00
return 0 ;
}
2007-11-24 16:13:42 +03:00
static int check_firmware ( struct dvb_frontend * fe , unsigned int type ,
2007-11-26 01:26:36 +03:00
v4l2_std_id std , __u16 int_freq )
2007-10-02 18:57:03 +04:00
{
2007-11-24 16:13:42 +03:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-19 17:22:03 +03:00
struct firmware_properties new_fw ;
2007-11-24 16:13:42 +03:00
int rc = 0 , is_retry = 0 ;
u16 version , hwmodel ;
v4l2_std_id std0 ;
2007-10-02 18:57:03 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-02 18:57:03 +04:00
2007-10-24 16:22:08 +04:00
if ( ! priv - > firm ) {
2007-11-16 13:46:14 +03:00
if ( ! priv - > ctrl . fname ) {
tuner_info ( " xc2028/3028 firmware name not set! \n " ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
2007-11-16 13:46:14 +03:00
}
2007-10-24 16:22:08 +04:00
2007-11-01 23:47:42 +03:00
rc = load_all_firmwares ( fe ) ;
if ( rc < 0 )
2007-10-24 16:22:08 +04:00
return rc ;
}
2008-01-05 22:47:16 +03:00
if ( priv - > ctrl . mts & & ! ( type & FM ) )
2007-11-19 17:22:03 +03:00
type | = MTS ;
2007-10-02 18:57:03 +04:00
2007-11-19 17:35:45 +03:00
retry :
2007-11-19 17:22:03 +03:00
new_fw . type = type ;
new_fw . id = std ;
new_fw . std_req = std ;
new_fw . scode_table = SCODE | priv - > ctrl . scode_table ;
new_fw . scode_nr = 0 ;
2007-11-26 01:26:36 +03:00
new_fw . int_freq = int_freq ;
2007-11-19 17:22:03 +03:00
tuner_dbg ( " checking firmware, user requested type= " ) ;
if ( debug ) {
dump_firm_type ( new_fw . type ) ;
2007-12-02 12:30:50 +03:00
printk ( " (%x), id %016llx, " , new_fw . type ,
2007-11-19 17:22:03 +03:00
( unsigned long long ) new_fw . std_req ) ;
2007-12-02 12:30:50 +03:00
if ( ! int_freq ) {
printk ( " scode_tbl " ) ;
dump_firm_type ( priv - > ctrl . scode_table ) ;
printk ( " (%x), " , priv - > ctrl . scode_table ) ;
} else
printk ( " int_freq %d, " , new_fw . int_freq ) ;
printk ( " scode_nr %d \n " , new_fw . scode_nr ) ;
2007-11-19 17:22:03 +03:00
}
/* No need to reload base firmware if it matches */
if ( ( ( BASE | new_fw . type ) & BASE_TYPES ) = =
( priv - > cur_fw . type & BASE_TYPES ) ) {
tuner_dbg ( " BASE firmware not changed. \n " ) ;
goto skip_base ;
}
/* Updating BASE - forget about all currently loaded firmware */
memset ( & priv - > cur_fw , 0 , sizeof ( priv - > cur_fw ) ) ;
/* Reset is needed before loading firmware */
rc = priv - > tuner_callback ( priv - > video_dev ,
XC2028_TUNER_RESET , 0 ) ;
if ( rc < 0 )
goto fail ;
2007-11-20 05:11:37 +03:00
/* BASE firmwares are all std0 */
std0 = 0 ;
rc = load_firmware ( fe , BASE | new_fw . type , & std0 ) ;
2007-11-19 17:22:03 +03:00
if ( rc < 0 ) {
tuner_err ( " Error %d while loading base firmware \n " ,
rc ) ;
goto fail ;
}
2007-11-16 13:49:49 +03:00
2007-10-24 16:22:08 +04:00
/* Load INIT1, if needed */
2007-11-15 15:44:30 +03:00
tuner_dbg ( " Load init1 firmware, if exists \n " ) ;
2007-10-24 16:22:08 +04:00
2007-11-20 05:11:37 +03:00
rc = load_firmware ( fe , BASE | INIT1 | new_fw . type , & std0 ) ;
2007-11-20 05:43:13 +03:00
if ( rc = = - ENOENT )
rc = load_firmware ( fe , ( BASE | INIT1 | new_fw . type ) & ~ F8MHZ ,
& std0 ) ;
2007-11-19 17:22:03 +03:00
if ( rc < 0 & & rc ! = - ENOENT ) {
tuner_err ( " Error %d while loading init1 firmware \n " ,
rc ) ;
goto fail ;
}
2007-10-24 16:22:08 +04:00
2007-11-19 17:22:03 +03:00
skip_base :
/*
* No need to reload standard specific firmware if base firmware
* was not reloaded and requested video standards have not changed .
2007-10-24 16:22:08 +04:00
*/
2007-11-19 17:22:03 +03:00
if ( priv - > cur_fw . type = = ( BASE | new_fw . type ) & &
priv - > cur_fw . std_req = = std ) {
2007-11-15 15:44:30 +03:00
tuner_dbg ( " Std-specific firmware already loaded. \n " ) ;
2007-11-19 17:22:03 +03:00
goto skip_std_specific ;
2007-07-18 20:33:23 +04:00
}
2007-10-02 18:57:03 +04:00
2007-11-19 17:22:03 +03:00
/* Reloading std-specific firmware forces a SCODE update */
priv - > cur_fw . scode_table = 0 ;
rc = load_firmware ( fe , new_fw . type , & new_fw . id ) ;
2007-11-22 17:47:18 +03:00
if ( rc = = - ENOENT )
rc = load_firmware ( fe , new_fw . type & ~ F8MHZ , & new_fw . id ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-11-19 17:22:03 +03:00
goto fail ;
skip_std_specific :
if ( priv - > cur_fw . scode_table = = new_fw . scode_table & &
priv - > cur_fw . scode_nr = = new_fw . scode_nr ) {
tuner_dbg ( " SCODE firmware already loaded. \n " ) ;
goto check_device ;
}
2007-10-02 18:57:03 +04:00
2008-02-14 07:52:48 +03:00
if ( new_fw . type & FM )
goto check_device ;
2007-11-15 14:43:53 +03:00
/* Load SCODE firmware, if exists */
2007-11-19 17:22:03 +03:00
tuner_dbg ( " Trying to load scode %d \n " , new_fw . scode_nr ) ;
2007-11-15 14:43:53 +03:00
2007-11-26 01:26:36 +03:00
rc = load_scode ( fe , new_fw . type | new_fw . scode_table , & new_fw . id ,
new_fw . int_freq , new_fw . scode_nr ) ;
2007-11-15 01:30:28 +03:00
2007-11-19 17:22:03 +03:00
check_device :
2007-11-19 17:35:45 +03:00
if ( xc2028_get_reg ( priv , 0x0004 , & version ) < 0 | |
xc2028_get_reg ( priv , 0x0008 , & hwmodel ) < 0 ) {
tuner_err ( " Unable to read tuner registers. \n " ) ;
goto fail ;
}
2007-11-05 15:07:13 +03:00
2008-04-21 14:02:09 +04:00
tuner_dbg ( " Device is Xceive %d version %d.%d, "
" firmware version %d.%d \n " ,
hwmodel , ( version & 0xf000 ) > > 12 , ( version & 0xf00 ) > > 8 ,
( version & 0xf0 ) > > 4 , version & 0xf ) ;
2007-10-02 18:57:03 +04:00
2007-11-19 17:35:45 +03:00
/* Check firmware version against what we downloaded. */
if ( priv - > firm_version ! = ( ( version & 0xf0 ) < < 4 | ( version & 0x0f ) ) ) {
tuner_err ( " Incorrect readback of firmware version. \n " ) ;
goto fail ;
}
/* Check that the tuner hardware model remains consistent over time. */
if ( priv - > hwmodel = = 0 & & ( hwmodel = = 2028 | | hwmodel = = 3028 ) ) {
priv - > hwmodel = hwmodel ;
priv - > hwvers = version & 0xff00 ;
} else if ( priv - > hwmodel = = 0 | | priv - > hwmodel ! = hwmodel | |
priv - > hwvers ! = ( version & 0xff00 ) ) {
tuner_err ( " Read invalid device hardware information - tuner "
" hung? \n " ) ;
goto fail ;
}
2007-11-19 17:22:03 +03:00
memcpy ( & priv - > cur_fw , & new_fw , sizeof ( priv - > cur_fw ) ) ;
/*
* By setting BASE in cur_fw . type only after successfully loading all
* firmwares , we can :
* 1. Identify that BASE firmware with type = 0 has been loaded ;
* 2. Tell whether BASE firmware was just changed the next time through .
*/
priv - > cur_fw . type | = BASE ;
2007-10-02 18:57:03 +04:00
return 0 ;
2007-11-19 17:22:03 +03:00
fail :
memset ( & priv - > cur_fw , 0 , sizeof ( priv - > cur_fw ) ) ;
2007-11-19 17:35:45 +03:00
if ( ! is_retry ) {
msleep ( 50 ) ;
is_retry = 1 ;
tuner_dbg ( " Retrying firmware load \n " ) ;
goto retry ;
}
2007-11-19 17:22:03 +03:00
if ( rc = = - ENOENT )
rc = - EINVAL ;
return rc ;
2007-10-02 18:57:03 +04:00
}
2007-10-23 22:24:06 +04:00
static int xc2028_signal ( struct dvb_frontend * fe , u16 * strength )
2007-10-02 18:57:03 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-19 10:31:58 +03:00
u16 frq_lock , signal = 0 ;
int rc ;
2007-09-28 01:27:03 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
mutex_lock ( & priv - > lock ) ;
2007-10-02 18:57:03 +04:00
2007-11-05 15:07:13 +03:00
/* Sync Lock Indicator */
2007-11-19 10:31:58 +03:00
rc = xc2028_get_reg ( priv , 0x0002 , & frq_lock ) ;
2008-04-24 18:19:55 +04:00
if ( rc < 0 )
2007-09-28 01:27:03 +04:00
goto ret ;
2007-10-02 18:57:03 +04:00
2008-04-24 18:19:55 +04:00
/* Frequency is locked */
if ( frq_lock = = 1 )
signal = 32768 ;
2007-10-02 18:57:03 +04:00
2007-11-05 15:07:13 +03:00
/* Get SNR of the video signal */
2007-11-19 10:31:58 +03:00
rc = xc2028_get_reg ( priv , 0x0040 , & signal ) ;
if ( rc < 0 )
2008-04-24 18:19:55 +04:00
goto ret ;
/* Use both frq_lock and signal to generate the result */
signal = signal | | ( ( signal & 0x07 ) < < 12 ) ;
2007-09-28 01:27:03 +04:00
ret :
2007-10-23 22:24:06 +04:00
mutex_unlock ( & priv - > lock ) ;
* strength = signal ;
2007-10-02 18:57:03 +04:00
2008-04-24 18:19:55 +04:00
tuner_dbg ( " signal strength is %d \n " , signal ) ;
2007-11-19 10:31:58 +03:00
return rc ;
2007-10-02 18:57:03 +04:00
}
# define DIV 15625
2007-11-24 16:13:42 +03:00
static int generic_set_freq ( struct dvb_frontend * fe , u32 freq /* in HZ */ ,
2007-11-26 01:26:36 +03:00
enum tuner_mode new_mode ,
unsigned int type ,
v4l2_std_id std ,
u16 int_freq )
2007-10-02 18:57:03 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-01 23:47:42 +03:00
int rc = - EINVAL ;
2007-11-19 12:20:17 +03:00
unsigned char buf [ 4 ] ;
2007-11-01 23:47:42 +03:00
u32 div , offset = 0 ;
2007-10-02 18:57:03 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-23 22:24:06 +04:00
2007-10-24 16:22:08 +04:00
mutex_lock ( & priv - > lock ) ;
2007-11-19 12:20:17 +03:00
tuner_dbg ( " should set frequency %d kHz \n " , freq / 1000 ) ;
2007-10-02 18:57:03 +04:00
2007-11-26 01:26:36 +03:00
if ( check_firmware ( fe , type , std , int_freq ) < 0 )
2007-09-28 01:27:03 +04:00
goto ret ;
2007-07-18 20:33:23 +04:00
2007-11-22 18:48:04 +03:00
/* On some cases xc2028 can disable video output, if
* very weak signals are received . By sending a soft
* reset , this is re - enabled . So , it is better to always
* send a soft reset before changing channels , to be sure
* that xc2028 will be in a safe state .
* Maybe this might also be needed for DTV .
*/
2007-11-24 16:13:42 +03:00
if ( new_mode = = T_ANALOG_TV ) {
2007-11-22 18:48:04 +03:00
rc = send_seq ( priv , { 0x00 , 0x00 } ) ;
2007-12-18 16:42:33 +03:00
} else if ( priv - > cur_fw . type & ATSC ) {
offset = 1750000 ;
} else {
2007-07-19 06:14:25 +04:00
offset = 2750000 ;
2007-12-02 15:39:18 +03:00
/*
* We must adjust the offset by 500 kHz in two cases in order
* to correctly center the IF output :
* 1 ) When the ZARLINK456 or DIBCOM52 tables were explicitly
* selected and a 7 MHz channel is tuned ;
* 2 ) When tuning a VHF channel with DTV78 firmware .
*/
if ( ( ( priv - > cur_fw . type & DTV7 ) & &
( priv - > cur_fw . scode_table & ( ZARLINK456 | DIBCOM52 ) ) ) | |
( ( priv - > cur_fw . type & DTV78 ) & & freq < 470000000 ) )
2007-11-19 12:35:26 +03:00
offset - = 500000 ;
}
2007-07-18 20:33:23 +04:00
2007-11-01 23:47:42 +03:00
div = ( freq - offset + DIV / 2 ) / DIV ;
2007-07-18 20:33:23 +04:00
2007-10-02 18:57:03 +04:00
/* CMD= Set frequency */
2007-11-19 12:06:08 +03:00
if ( priv - > firm_version < 0x0202 )
2007-11-19 10:14:23 +03:00
rc = send_seq ( priv , { 0x00 , 0x02 , 0x00 , 0x00 } ) ;
else
rc = send_seq ( priv , { 0x80 , 0x02 , 0x00 , 0x00 } ) ;
if ( rc < 0 )
goto ret ;
2007-10-24 16:22:08 +04:00
2008-04-22 21:45:20 +04:00
/* Return code shouldn't be checked.
The reset CLK is needed only with tm6000 .
Driver should work fine even if this fails .
*/
priv - > tuner_callback ( priv - > video_dev , XC2028_RESET_CLK , 1 ) ;
2007-10-02 18:57:03 +04:00
msleep ( 10 ) ;
2007-07-18 17:29:10 +04:00
2007-11-01 23:47:42 +03:00
buf [ 0 ] = 0xff & ( div > > 24 ) ;
buf [ 1 ] = 0xff & ( div > > 16 ) ;
buf [ 2 ] = 0xff & ( div > > 8 ) ;
buf [ 3 ] = 0xff & ( div ) ;
2007-10-02 18:57:03 +04:00
2007-11-19 10:14:23 +03:00
rc = i2c_send ( priv , buf , sizeof ( buf ) ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-09-28 01:27:03 +04:00
goto ret ;
2007-10-02 18:57:03 +04:00
msleep ( 100 ) ;
2007-11-01 23:47:42 +03:00
priv - > frequency = freq ;
2007-10-23 22:24:06 +04:00
2007-11-19 12:20:17 +03:00
tuner_dbg ( " divisor= %02x %02x %02x %02x (freq=%d.%03d) \n " ,
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] ,
freq / 1000000 , ( freq % 1000000 ) / 1000 ) ;
2007-09-28 01:27:03 +04:00
2007-11-01 23:47:42 +03:00
rc = 0 ;
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
ret :
mutex_unlock ( & priv - > lock ) ;
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
return rc ;
2007-07-18 17:29:10 +04:00
}
2007-11-24 16:13:42 +03:00
static int xc2028_set_analog_freq ( struct dvb_frontend * fe ,
2007-11-01 23:47:42 +03:00
struct analog_parameters * p )
2007-10-02 18:57:03 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-24 16:13:42 +03:00
unsigned int type = 0 ;
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-11-22 17:47:18 +03:00
2007-11-24 16:20:15 +03:00
if ( p - > mode = = V4L2_TUNER_RADIO ) {
type | = FM ;
if ( priv - > ctrl . input1 )
type | = INPUT1 ;
return generic_set_freq ( fe , ( 625l * p - > frequency ) / 10 ,
2007-11-26 01:26:36 +03:00
T_ANALOG_TV , type , 0 , 0 ) ;
2007-11-24 16:20:15 +03:00
}
2007-11-23 17:36:18 +03:00
/* if std is not defined, choose one */
if ( ! p - > std )
p - > std = V4L2_STD_MN ;
/* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
2007-11-24 16:13:42 +03:00
if ( ! ( p - > std & V4L2_STD_MN ) )
type | = F8MHZ ;
2007-10-02 18:57:03 +04:00
2007-11-24 16:13:42 +03:00
/* Add audio hack to std mask */
p - > std | = parse_audio_std_option ( ) ;
2007-10-02 18:57:03 +04:00
2007-11-24 16:13:42 +03:00
return generic_set_freq ( fe , 62500l * p - > frequency ,
2007-11-26 01:26:36 +03:00
T_ANALOG_TV , type , p - > std , 0 ) ;
2007-10-23 22:24:06 +04:00
}
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
static int xc2028_set_params ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * p )
2007-10-02 18:57:03 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-11-24 16:13:42 +03:00
unsigned int type = 0 ;
2007-11-24 16:47:03 +03:00
fe_bandwidth_t bw = BANDWIDTH_8_MHZ ;
2007-12-02 12:36:42 +03:00
u16 demod = 0 ;
2007-10-02 18:57:03 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-07-18 17:29:10 +04:00
2007-11-24 16:13:42 +03:00
if ( priv - > ctrl . d2633 )
type | = D2633 ;
else
type | = D2620 ;
2007-11-24 16:47:03 +03:00
switch ( fe - > ops . info . type ) {
case FE_OFDM :
bw = p - > u . ofdm . bandwidth ;
break ;
case FE_QAM :
2007-12-02 06:02:18 +03:00
tuner_info ( " WARN: There are some reports that "
" QAM 6 MHz doesn't work. \n "
" If this works for you, please report by "
" e-mail to: v4l-dvb-maintainer@linuxtv.org \n " ) ;
2007-11-24 16:47:03 +03:00
bw = BANDWIDTH_6_MHZ ;
type | = QAM ;
break ;
case FE_ATSC :
bw = BANDWIDTH_6_MHZ ;
2007-12-16 15:24:30 +03:00
/* The only ATSC firmware (at least on v2.7) is D2633,
so overrides ctrl - > d2633 */
2007-11-24 16:47:03 +03:00
type | = ATSC | D2633 ;
2007-12-16 15:24:30 +03:00
type & = ~ D2620 ;
2007-11-24 16:47:03 +03:00
break ;
2007-12-02 06:02:18 +03:00
/* DVB-S is not supported */
default :
return - EINVAL ;
2007-11-24 16:47:03 +03:00
}
2007-11-24 16:13:42 +03:00
switch ( bw ) {
case BANDWIDTH_8_MHZ :
2007-12-02 16:07:06 +03:00
if ( p - > frequency < 470000000 )
priv - > ctrl . vhfbw7 = 0 ;
else
priv - > ctrl . uhfbw8 = 1 ;
type | = ( priv - > ctrl . vhfbw7 & & priv - > ctrl . uhfbw8 ) ? DTV78 : DTV8 ;
type | = F8MHZ ;
2007-11-24 16:13:42 +03:00
break ;
case BANDWIDTH_7_MHZ :
2007-12-02 16:07:06 +03:00
if ( p - > frequency < 470000000 )
priv - > ctrl . vhfbw7 = 1 ;
else
priv - > ctrl . uhfbw8 = 0 ;
type | = ( priv - > ctrl . vhfbw7 & & priv - > ctrl . uhfbw8 ) ? DTV78 : DTV7 ;
type | = F8MHZ ;
2007-11-24 16:13:42 +03:00
break ;
case BANDWIDTH_6_MHZ :
2007-12-02 16:07:06 +03:00
type | = DTV6 ;
priv - > ctrl . vhfbw7 = 0 ;
priv - > ctrl . uhfbw8 = 0 ;
2007-11-24 16:13:42 +03:00
break ;
default :
tuner_err ( " error: bandwidth not supported. \n " ) ;
} ;
2007-11-26 01:26:36 +03:00
/* All S-code tables need a 200kHz shift */
if ( priv - > ctrl . demod )
2007-12-02 12:36:42 +03:00
demod = priv - > ctrl . demod + 200 ;
2007-11-24 17:07:12 +03:00
2007-11-24 16:13:42 +03:00
return generic_set_freq ( fe , p - > frequency ,
2007-12-02 12:36:42 +03:00
T_DIGITAL_TV , type , 0 , demod ) ;
2007-10-02 18:57:03 +04:00
}
2007-07-18 17:29:10 +04:00
2007-11-19 17:41:20 +03:00
2007-10-23 22:24:06 +04:00
static int xc2028_dvb_release ( struct dvb_frontend * fe )
2007-07-18 17:29:10 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-07-18 17:29:10 +04:00
2007-11-19 10:45:38 +03:00
mutex_lock ( & xc2028_list_mutex ) ;
2008-04-19 04:22:50 +04:00
/* only perform final cleanup if this is the last instance */
if ( hybrid_tuner_report_instance_count ( priv ) = = 1 ) {
2007-11-01 23:47:42 +03:00
kfree ( priv - > ctrl . fname ) ;
2007-10-24 16:22:08 +04:00
free_firmware ( priv ) ;
}
2007-07-18 17:29:10 +04:00
2008-04-19 04:22:50 +04:00
if ( priv )
hybrid_tuner_release_state ( priv ) ;
2007-11-19 10:45:38 +03:00
mutex_unlock ( & xc2028_list_mutex ) ;
2008-04-19 04:22:50 +04:00
fe - > tuner_priv = NULL ;
2007-07-18 17:29:10 +04:00
return 0 ;
}
2007-10-23 22:24:06 +04:00
static int xc2028_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
2007-07-18 17:29:10 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv = fe - > tuner_priv ;
2007-07-18 17:29:10 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
* frequency = priv - > frequency ;
2007-07-18 17:29:10 +04:00
return 0 ;
}
2007-11-01 23:47:42 +03:00
static int xc2028_set_config ( struct dvb_frontend * fe , void * priv_cfg )
2007-10-24 16:22:08 +04:00
{
struct xc2028_data * priv = fe - > tuner_priv ;
struct xc2028_ctrl * p = priv_cfg ;
2007-11-19 15:29:59 +03:00
int rc = 0 ;
2007-10-24 16:22:08 +04:00
2008-04-09 06:20:00 +04:00
tuner_dbg ( " %s called \n " , __func__ ) ;
2007-10-24 16:22:08 +04:00
2007-11-19 12:06:08 +03:00
mutex_lock ( & priv - > lock ) ;
2007-11-19 15:29:59 +03:00
memcpy ( & priv - > ctrl , p , sizeof ( priv - > ctrl ) ) ;
2008-04-18 04:40:53 +04:00
if ( priv - > ctrl . max_len < 9 )
priv - > ctrl . max_len = 13 ;
2007-10-24 16:22:08 +04:00
2007-11-19 15:29:59 +03:00
if ( p - > fname ) {
2008-04-18 04:40:53 +04:00
if ( priv - > ctrl . fname & & strcmp ( p - > fname , priv - > ctrl . fname ) ) {
kfree ( priv - > ctrl . fname ) ;
free_firmware ( priv ) ;
}
2007-11-19 15:29:59 +03:00
priv - > ctrl . fname = kstrdup ( p - > fname , GFP_KERNEL ) ;
if ( priv - > ctrl . fname = = NULL )
rc = - ENOMEM ;
2007-10-24 16:22:08 +04:00
}
2007-11-19 12:06:08 +03:00
mutex_unlock ( & priv - > lock ) ;
2007-11-19 15:29:59 +03:00
return rc ;
2007-10-24 16:22:08 +04:00
}
2007-10-23 22:24:06 +04:00
static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
2007-07-18 17:29:10 +04:00
. info = {
2007-11-01 23:47:42 +03:00
. name = " Xceive XC3028 " ,
. frequency_min = 42000000 ,
. frequency_max = 864000000 ,
. frequency_step = 50000 ,
} ,
2007-07-18 17:29:10 +04:00
2007-10-24 16:22:08 +04:00
. set_config = xc2028_set_config ,
2007-11-24 16:13:42 +03:00
. set_analog_params = xc2028_set_analog_freq ,
2007-10-23 22:24:06 +04:00
. release = xc2028_dvb_release ,
. get_frequency = xc2028_get_frequency ,
. get_rf_strength = xc2028_signal ,
. set_params = xc2028_set_params ,
2007-07-18 17:29:10 +04:00
} ;
2007-12-21 22:12:09 +03:00
struct dvb_frontend * xc2028_attach ( struct dvb_frontend * fe ,
struct xc2028_config * cfg )
2007-07-18 17:29:10 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv ;
2008-04-19 04:22:50 +04:00
int instance ;
2007-07-18 17:29:10 +04:00
2007-11-15 15:44:30 +03:00
if ( debug )
2008-04-22 21:41:53 +04:00
printk ( KERN_DEBUG " xc2028: Xcv2028/3028 init called! \n " ) ;
2007-07-18 17:29:10 +04:00
2008-04-22 21:46:11 +04:00
if ( NULL = = cfg )
2007-11-16 13:46:14 +03:00
return NULL ;
2007-10-23 22:24:06 +04:00
2007-11-16 13:46:14 +03:00
if ( ! fe ) {
2008-04-22 21:41:53 +04:00
printk ( KERN_ERR " xc2028: No frontend! \n " ) ;
2007-11-16 13:46:14 +03:00
return NULL ;
2007-10-23 22:24:06 +04:00
}
2007-11-19 10:45:38 +03:00
mutex_lock ( & xc2028_list_mutex ) ;
2008-04-19 04:22:50 +04:00
instance = hybrid_tuner_request_state ( struct xc2028_data , priv ,
hybrid_tuner_instance_list ,
cfg - > i2c_adap , cfg - > i2c_addr ,
" xc2028 " ) ;
switch ( instance ) {
case 0 :
/* memory allocation failure */
goto fail ;
break ;
case 1 :
/* new tuner instance */
2007-11-16 13:46:14 +03:00
priv - > tuner_callback = cfg - > callback ;
2007-11-19 15:29:59 +03:00
priv - > ctrl . max_len = 13 ;
2007-10-24 16:22:08 +04:00
2007-10-23 22:24:06 +04:00
mutex_init ( & priv - > lock ) ;
2008-04-19 04:22:50 +04:00
/* analog side (tuner-core) uses i2c_adap->algo_data.
* digital side is not guaranteed to have algo_data defined .
*
* digital side will always have fe - > dvb defined .
* analog side ( tuner - core ) doesn ' t ( yet ) define fe - > dvb .
*/
priv - > video_dev = ( ( fe - > dvb ) & & ( fe - > dvb - > priv ) ) ?
fe - > dvb - > priv : cfg - > i2c_adap - > algo_data ;
2007-10-23 22:24:06 +04:00
2008-04-19 04:22:50 +04:00
fe - > tuner_priv = priv ;
break ;
case 2 :
/* existing tuner instance */
fe - > tuner_priv = priv ;
break ;
}
2008-04-22 21:46:11 +04:00
2007-10-23 22:24:06 +04:00
memcpy ( & fe - > ops . tuner_ops , & xc2028_dvb_tuner_ops ,
2007-11-01 23:47:42 +03:00
sizeof ( xc2028_dvb_tuner_ops ) ) ;
2007-10-23 22:24:06 +04:00
tuner_info ( " type set to %s \n " , " XCeive xc2028/xc3028 tuner " ) ;
2007-11-22 18:19:37 +03:00
if ( cfg - > ctrl )
xc2028_set_config ( fe , cfg - > ctrl ) ;
2007-11-19 10:45:38 +03:00
mutex_unlock ( & xc2028_list_mutex ) ;
2007-11-16 13:46:14 +03:00
return fe ;
2008-04-19 04:22:50 +04:00
fail :
mutex_unlock ( & xc2028_list_mutex ) ;
xc2028_dvb_release ( fe ) ;
return NULL ;
2007-10-23 22:24:06 +04:00
}
2007-11-16 13:46:14 +03:00
2007-07-18 17:29:10 +04:00
EXPORT_SYMBOL ( xc2028_attach ) ;
2007-10-23 22:24:06 +04:00
MODULE_DESCRIPTION ( " Xceive xc2028/xc3028 tuner driver " ) ;
2007-10-30 05:44:18 +03:00
MODULE_AUTHOR ( " Michel Ludwig <michel.ludwig@gmail.com> " ) ;
2007-10-23 22:24:06 +04:00
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@infradead.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;