2007-10-02 18:57:03 +04:00
/* tuner-xc2028
*
* Copyright ( c ) 2007 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-04 17:03:36 +03:00
# define PREFIX "xc2028"
2007-10-23 22:24:06 +04: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 " ) ;
2007-10-23 22:24:06 +04:00
static LIST_HEAD ( xc2028_list ) ;
2007-10-24 16:22:08 +04:00
/* struct for storing firmware table */
struct firmware_description {
unsigned int type ;
v4l2_std_id id ;
unsigned char * ptr ;
unsigned int size ;
} ;
2007-10-02 18:57:03 +04:00
struct xc2028_data {
2007-10-23 22:24:06 +04:00
struct list_head xc2028_list ;
struct tuner_i2c_props i2c_props ;
int ( * tuner_callback ) ( void * dev ,
int command , int arg ) ;
void * video_dev ;
int count ;
2007-10-24 16:22:08 +04:00
__u32 frequency ;
struct firmware_description * firm ;
int firm_size ;
__u16 version ;
struct xc2028_ctrl ctrl ;
2007-10-23 22:24:06 +04:00
2007-07-18 17:29:10 +04:00
v4l2_std_id firm_type ; /* video stds supported
by current firmware */
fe_bandwidth_t bandwidth ; /* Firmware bandwidth:
6 M , 7 M or 8 M */
int need_load_generic ; /* The generic firmware
were loaded ? */
2007-10-24 16:22:08 +04:00
int max_len ; /* Max firmware chunk */
2007-07-18 17:29:10 +04:00
enum tuner_mode mode ;
struct i2c_client * i2c_client ;
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
2007-11-19 10:31:58 +03:00
static unsigned 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
2007-11-19 10:31:58 +03:00
tuner_dbg ( " %s %04x called \n " , __FUNCTION__ , 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-11-15 01:30:28 +03:00
void dump_firm_type ( unsigned int type )
{
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-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 ;
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-10-24 16:22:08 +04:00
priv - > need_load_generic = 1 ;
}
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 ;
2007-10-02 18:57:03 +04:00
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 ] ;
2007-10-02 18:57:03 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-10-23 22:24:06 +04:00
2007-11-15 14:43:53 +03:00
tuner_info ( " Reading firmware %s \n " , priv - > ctrl . fname ) ;
2007-11-16 13:46:14 +03:00
rc = request_firmware ( & fw , priv - > ctrl . 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 " ,
2007-10-24 16:22:08 +04:00
priv - > ctrl . 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 " ,
2007-10-24 16:22:08 +04:00
rc , priv - > ctrl . 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-01 23:47:42 +03:00
if ( fw - > size < sizeof ( name ) - 1 + 2 ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Error: firmware size is zero! \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - EINVAL ;
2007-10-24 16:22:08 +04:00
goto done ;
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-01 23:47:42 +03:00
priv - > version = le16_to_cpu ( * ( __u16 * ) p ) ;
2007-10-24 16:22:08 +04:00
p + = 2 ;
2007-11-15 15:44:30 +03:00
tuner_info ( " Firmware: %s, ver %d.%d \n " , name ,
2007-11-01 23:47:42 +03:00
priv - > version > > 8 , priv - > version & 0xff ) ;
2007-10-24 16:22:08 +04:00
2007-11-01 23:47:42 +03:00
if ( p + 2 > endp )
2007-10-24 16:22:08 +04:00
goto corrupt ;
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-15 15:44:30 +03:00
tuner_info ( " There are %d firmwares at %s \n " ,
n_array , priv - > ctrl . fname ) ;
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-10-24 16:22:08 +04:00
if ( ! fw ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Not enough memory for reading firmware. \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - ENOMEM ;
2007-10-24 16:22:08 +04:00
goto done ;
2007-10-02 18:57:03 +04:00
}
2007-10-24 16:22:08 +04:00
priv - > firm_size = n_array ;
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 ;
n + + ;
if ( n > = n_array ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Too much firmwares at the file \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-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-10-24 16:22:08 +04:00
if ( ! priv - > firm [ n ] . ptr ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Not enough memory. \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - ENOMEM ;
2007-10-24 16:22:08 +04:00
goto err ;
}
2007-11-15 15:44:30 +03:00
tuner_info ( " Reading firmware type " ) ;
2007-11-15 01:30:28 +03:00
dump_firm_type ( type ) ;
2007-11-19 10:38:53 +03:00
printk ( " (%x), id %llx, size=%d. \n " ,
type , ( unsigned long long ) id , size ) ;
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 ;
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 :
tuner_info ( " Releasing loaded firmware file. \n " ) ;
free_firmware ( priv ) ;
done :
release_firmware ( fw ) ;
2007-11-15 15:44:30 +03:00
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-15 14:43:53 +03:00
int i ;
2007-10-24 16:22:08 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
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
/* Seek for exact match */
2007-11-01 23:47:42 +03:00
for ( i = 0 ; i < priv - > firm_size ; i + + ) {
if ( ( type = = priv - > firm [ i ] . type ) & & ( * 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 + + ) {
if ( ( type = = priv - > firm [ i ] . type ) & & ( * id & priv - > firm [ i ] . id ) )
2007-10-24 16:22:08 +04:00
goto found ;
}
/*FIXME: Would make sense to seek for type "hint" match ? */
2007-11-15 14:43:53 +03:00
i = - EINVAL ;
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-15 15:44:30 +03:00
tuner_dbg ( " %s firmware for type= " , ( i < 0 ) ? " Can't find " : " Found " ) ;
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 ;
unsigned char * p , * endp , buf [ priv - > max_len ] ;
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
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= " ) ;
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
p = priv - > firm [ pos ] . ptr ;
2007-10-24 16:22:08 +04:00
if ( ! p ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware pointer were freed! " ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
2007-10-02 18:57:03 +04:00
}
2007-11-15 14:43:53 +03:00
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 ;
}
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 ) {
int len = ( size < priv - > max_len - 1 ) ?
size : priv - > 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 ,
v4l2_std_id * id , int scode )
{
struct xc2028_data * priv = fe - > tuner_priv ;
int pos , rc ;
unsigned char * p ;
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-11-15 14:43:53 +03:00
pos = seek_firmware ( fe , type , id ) ;
if ( pos < 0 )
return pos ;
p = priv - > firm [ pos ] . ptr ;
if ( ! p ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Firmware pointer were freed! " ) ;
2007-11-15 14:43:53 +03:00
return - EINVAL ;
}
if ( ( priv - > firm [ pos ] . size ! = 12 * 16 ) | | ( scode > = 16 ) )
return - EINVAL ;
2007-11-19 10:14:23 +03:00
if ( priv - > version < 0x0202 )
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-19 10:14:23 +03:00
rc = i2c_send ( priv , p + 12 * scode , 12 ) ;
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-10-23 22:24:06 +04:00
static int check_firmware ( struct dvb_frontend * fe , enum tuner_mode new_mode ,
2007-11-01 23:47:42 +03:00
v4l2_std_id std , fe_bandwidth_t bandwidth )
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
int rc ;
u16 version , hwmodel ;
2007-11-01 23:47:42 +03:00
v4l2_std_id std0 = 0 ;
unsigned int type0 = 0 , type = 0 ;
2007-10-24 16:22:08 +04:00
int change_digital_bandwidth ;
2007-10-02 18:57:03 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
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 ;
}
2007-11-15 15:44:30 +03:00
tuner_dbg ( " I am in mode %u and I should switch to mode %i \n " ,
2007-11-01 23:47:42 +03:00
priv - > mode , new_mode ) ;
2007-07-18 17:29:10 +04:00
/* first of all, determine whether we have switched the mode */
2007-11-01 23:47:42 +03:00
if ( new_mode ! = priv - > mode ) {
2007-10-23 22:24:06 +04:00
priv - > mode = new_mode ;
priv - > need_load_generic = 1 ;
2007-07-18 17:29:10 +04:00
}
2007-10-23 22:24:06 +04:00
change_digital_bandwidth = ( priv - > mode = = T_DIGITAL_TV
2007-11-01 23:47:42 +03:00
& & bandwidth ! = priv - > bandwidth ) ? 1 : 0 ;
2007-11-15 15:44:30 +03:00
tuner_dbg ( " old bandwidth %u, new bandwidth %u \n " , priv - > bandwidth ,
2007-11-01 23:47:42 +03:00
bandwidth ) ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
if ( priv - > need_load_generic ) {
2007-10-02 18:57:03 +04:00
/* Reset is needed before loading firmware */
2007-10-23 22:24:06 +04:00
rc = priv - > tuner_callback ( priv - > video_dev ,
XC2028_TUNER_RESET , 0 ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-10-02 18:57:03 +04:00
return rc ;
2007-11-01 23:47:42 +03:00
type0 = BASE ;
2007-10-24 16:22:08 +04:00
if ( priv - > ctrl . type = = XC2028_FIRM_MTS )
type0 | = MTS ;
2007-11-01 23:47:42 +03:00
if ( priv - > bandwidth = = 8 )
2007-10-24 16:22:08 +04:00
type0 | = F8MHZ ;
/* FIXME: How to load FM and FM|INPUT1 firmwares? */
rc = load_firmware ( fe , type0 , & std0 ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " Error %d while loading generic firmware \n " ,
rc ) ;
2007-10-02 18:57:03 +04:00
return rc ;
2007-10-24 16:22:08 +04:00
}
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
priv - > need_load_generic = 0 ;
priv - > firm_type = 0 ;
if ( priv - > mode = = T_DIGITAL_TV )
change_digital_bandwidth = 1 ;
2007-07-18 17:29:10 +04:00
}
2007-11-15 15:44:30 +03:00
tuner_dbg ( " I should change bandwidth %u \n " , change_digital_bandwidth ) ;
2007-07-18 17:29:10 +04:00
if ( change_digital_bandwidth ) {
2007-10-24 16:22:08 +04:00
/*FIXME: Should allow selecting between D2620 and D2633 */
type | = D2620 ;
/* FIXME: When should select a DTV78 firmware?
*/
2007-11-01 23:47:42 +03:00
switch ( bandwidth ) {
2007-10-24 16:22:08 +04:00
case BANDWIDTH_8_MHZ :
type | = DTV8 ;
2007-07-18 17:29:10 +04:00
break ;
2007-10-24 16:22:08 +04:00
case BANDWIDTH_7_MHZ :
type | = DTV7 ;
2007-07-18 17:29:10 +04:00
break ;
2007-10-24 16:22:08 +04:00
case BANDWIDTH_6_MHZ :
/* FIXME: Should allow select also ATSC */
2007-11-15 01:30:28 +03:00
type | = DTV6 | QAM ;
2007-07-18 17:29:10 +04:00
break ;
2007-10-24 16:22:08 +04:00
default :
2007-11-15 15:44:30 +03:00
tuner_err ( " error: bandwidth not supported. \n " ) ;
2007-07-18 17:29:10 +04:00
} ;
2007-10-23 22:24:06 +04:00
priv - > bandwidth = bandwidth ;
2007-10-02 18:57:03 +04:00
}
2007-11-16 13:49:49 +03:00
if ( ! change_digital_bandwidth & & priv - > mode = = T_DIGITAL_TV )
return 0 ;
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-11-15 14:43:53 +03:00
type0 = BASE | INIT1 ;
2007-10-24 16:22:08 +04:00
if ( priv - > ctrl . type = = XC2028_FIRM_MTS )
type0 | = MTS ;
/* FIXME: Should handle errors - if INIT1 found */
rc = load_firmware ( fe , type0 , & std0 ) ;
/* FIXME: Should add support for FM radio
*/
if ( priv - > ctrl . type = = XC2028_FIRM_MTS )
type | = MTS ;
2007-10-23 22:24:06 +04:00
if ( priv - > firm_type & std ) {
2007-11-15 15:44:30 +03:00
tuner_dbg ( " Std-specific firmware already loaded. \n " ) ;
2007-10-02 18:57:03 +04:00
return 0 ;
2007-07-18 20:33:23 +04:00
}
2007-10-02 18:57:03 +04:00
2007-11-15 17:58:00 +03:00
/* Add audio hack to std mask */
std | = parse_audio_std_option ( ) ;
2007-10-24 16:22:08 +04:00
rc = load_firmware ( fe , type , & std ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-10-02 18:57:03 +04:00
return rc ;
2007-11-15 14:43:53 +03:00
/* Load SCODE firmware, if exists */
2007-11-15 15:44:30 +03:00
tuner_dbg ( " Trying to load scode 0 \n " ) ;
2007-11-15 14:43:53 +03:00
type | = SCODE ;
rc = load_scode ( fe , type , & std , 0 ) ;
2007-11-15 01:30:28 +03:00
2007-11-19 10:31:58 +03:00
xc2028_get_reg ( priv , 0x0004 , & version ) ;
xc2028_get_reg ( priv , 0x0008 , & hwmodel ) ;
2007-11-05 15:07:13 +03:00
tuner_info ( " 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-01 23:47:42 +03:00
priv - > firm_type = std ;
2007-10-02 18:57:03 +04:00
return 0 ;
}
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
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
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 ) ;
if ( rc < 0 | | frq_lock = = 0 )
2007-09-28 01:27:03 +04:00
goto ret ;
2007-10-02 18:57:03 +04:00
/* Frequency is locked. Return signal quality */
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 )
signal = - frq_lock ;
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
2007-11-19 10:31:58 +03:00
return rc ;
2007-10-02 18:57:03 +04:00
}
# define DIV 15625
2007-11-01 23:47:42 +03:00
static int generic_set_tv_freq ( struct dvb_frontend * fe , u32 freq /* in Hz */ ,
enum tuner_mode new_mode ,
v4l2_std_id std , fe_bandwidth_t bandwidth )
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 ;
unsigned char buf [ 5 ] ;
u32 div , offset = 0 ;
2007-10-02 18:57:03 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-10-23 22:24:06 +04:00
2007-10-24 16:22:08 +04:00
mutex_lock ( & priv - > lock ) ;
2007-07-19 06:14:25 +04:00
/* HACK: It seems that specific firmware need to be reloaded
when freq is changed */
2007-07-18 17:29:10 +04:00
2007-11-01 23:47:42 +03:00
priv - > firm_type = 0 ;
2007-07-18 17:29:10 +04:00
2007-10-02 18:57:03 +04:00
/* Reset GPIO 1 */
2007-10-23 22:24:06 +04:00
rc = priv - > tuner_callback ( priv - > video_dev , XC2028_TUNER_RESET , 0 ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-10-23 22:24:06 +04:00
goto ret ;
2007-10-02 18:57:03 +04:00
msleep ( 10 ) ;
2007-11-15 15:44:30 +03:00
tuner_dbg ( " should set frequency %d kHz) \n " , freq / 1000 ) ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
if ( check_firmware ( fe , new_mode , std , bandwidth ) < 0 )
2007-09-28 01:27:03 +04:00
goto ret ;
2007-07-18 20:33:23 +04:00
2007-11-01 23:47:42 +03:00
if ( new_mode = = T_DIGITAL_TV )
2007-07-19 06:14:25 +04:00
offset = 2750000 ;
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-10-24 16:22:08 +04:00
2007-11-19 10:14:23 +03:00
if ( priv - > version < 0x0202 )
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
2007-10-23 22:24:06 +04:00
rc = priv - > tuner_callback ( priv - > video_dev , XC2028_RESET_CLK , 1 ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-10-23 22:24:06 +04:00
goto ret ;
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 ) ;
buf [ 4 ] = 0 ;
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-15 15:44:30 +03:00
tuner_dbg ( " divider= %02x %02x %02x %02x (freq=%d.%02d) \n " ,
2007-11-01 23:47:42 +03:00
buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , buf [ 4 ] ,
freq / 1000000 , ( freq % 1000000 ) / 10000 ) ;
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-10-23 22:24:06 +04:00
static int xc2028_set_tv_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-10-02 18:57:03 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
return generic_set_tv_freq ( fe , 62500l * p - > frequency , T_ANALOG_TV ,
p - > std , BANDWIDTH_8_MHZ /* NOT USED */ ) ;
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-10-02 18:57:03 +04:00
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
/* FIXME: Only OFDM implemented */
if ( fe - > ops . info . type ! = FE_OFDM ) {
2007-11-15 15:44:30 +03:00
tuner_err ( " DTV type not implemented. \n " ) ;
2007-10-23 22:24:06 +04:00
return - EINVAL ;
2007-10-02 18:57:03 +04:00
}
2007-10-23 22:24:06 +04:00
return generic_set_tv_freq ( fe , p - > frequency , T_DIGITAL_TV ,
2007-11-01 23:47:42 +03:00
0 /* NOT USED */ ,
p - > u . ofdm . bandwidth ) ;
2007-10-02 18:57:03 +04:00
}
2007-07-18 17:29:10 +04: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 ;
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
priv - > count - - ;
2007-07-18 17:29:10 +04:00
2007-10-24 16:22:08 +04:00
if ( ! priv - > count ) {
2007-10-29 23:38:59 +03:00
list_del ( & priv - > xc2028_list ) ;
2007-11-01 23:47:42 +03:00
kfree ( priv - > ctrl . fname ) ;
2007-10-24 16:22:08 +04:00
free_firmware ( priv ) ;
2007-11-01 23:47:42 +03:00
kfree ( priv ) ;
2007-10-24 16:22:08 +04:00
}
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
2007-11-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
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-15 15:44:30 +03:00
tuner_dbg ( " %s called \n " , __FUNCTION__ ) ;
2007-10-24 16:22:08 +04:00
priv - > ctrl . type = p - > type ;
if ( p - > fname ) {
2007-11-01 23:47:42 +03:00
kfree ( priv - > ctrl . fname ) ;
2007-10-24 16:22:08 +04:00
2007-11-01 23:47:42 +03:00
priv - > ctrl . fname = kmalloc ( strlen ( p - > fname ) + 1 , GFP_KERNEL ) ;
2007-10-24 16:22:08 +04:00
if ( ! priv - > ctrl . fname )
return - ENOMEM ;
free_firmware ( priv ) ;
strcpy ( priv - > ctrl . fname , p - > fname ) ;
}
2007-11-01 23:47:42 +03:00
if ( p - > max_len > 0 )
2007-11-01 22:56:26 +03:00
priv - > max_len = p - > max_len ;
2007-10-24 16:22:08 +04:00
return 0 ;
}
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-10-23 22:24:06 +04:00
. set_analog_params = xc2028_set_tv_freq ,
. 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-11-16 13:46:14 +03:00
void * 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 ;
2007-11-16 13:46:14 +03:00
void * video_dev ;
2007-07-18 17:29:10 +04:00
2007-11-15 15:44:30 +03:00
if ( debug )
2007-11-19 10:34:29 +03:00
printk ( KERN_DEBUG PREFIX " : Xcv2028/3028 init called! \n " ) ;
2007-07-18 17:29:10 +04:00
2007-11-16 13:46:14 +03:00
if ( NULL = = cfg - > video_dev )
return NULL ;
2007-10-23 22:24:06 +04:00
2007-11-16 13:46:14 +03:00
if ( ! fe ) {
2007-11-19 10:34:29 +03:00
printk ( KERN_ERR PREFIX " : No frontend! \n " ) ;
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
video_dev = cfg - > video_dev ;
2007-10-23 22:24:06 +04:00
list_for_each_entry ( priv , & xc2028_list , xc2028_list ) {
2007-11-16 13:46:14 +03:00
if ( priv - > video_dev = = cfg - > video_dev ) {
video_dev = NULL ;
break ;
}
2007-10-23 22:24:06 +04:00
}
2007-11-16 13:46:14 +03:00
if ( video_dev ) {
2007-10-23 22:24:06 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( priv = = NULL )
2007-11-16 13:46:14 +03:00
return NULL ;
2007-09-28 01:27:03 +04:00
2007-11-01 23:47:42 +03:00
priv - > bandwidth = BANDWIDTH_6_MHZ ;
priv - > need_load_generic = 1 ;
2007-10-23 22:24:06 +04:00
priv - > mode = T_UNINITIALIZED ;
2007-11-16 13:46:14 +03:00
priv - > i2c_props . addr = cfg - > i2c_addr ;
priv - > i2c_props . adap = cfg - > i2c_adap ;
2007-10-23 22:24:06 +04:00
priv - > video_dev = video_dev ;
2007-11-16 13:46:14 +03:00
priv - > tuner_callback = cfg - > callback ;
2007-10-24 16:22:08 +04:00
priv - > max_len = 13 ;
2007-10-23 22:24:06 +04:00
mutex_init ( & priv - > lock ) ;
2007-11-01 23:47:42 +03:00
list_add_tail ( & priv - > xc2028_list , & xc2028_list ) ;
2007-10-23 22:24:06 +04:00
}
2007-11-16 13:46:14 +03:00
fe - > tuner_priv = priv ;
2007-10-29 23:38:59 +03:00
priv - > count + + ;
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-16 13:46:14 +03:00
return fe ;
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 " ) ;