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-04 17:03:36 +03:00
# define PREFIX "xc2028"
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 ) ;
struct device * dev ;
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-01 23:47:42 +03:00
# define i2c_send(rc, priv, buf, size) do { \
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 ) ; \
} while ( 0 )
# define i2c_rcv(rc, priv, buf, size) do { \
rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buf , size ) ; \
if ( size ! = rc ) \
tuner_info ( " i2c input error: rc = %d (should be %d) \n " , \
rc , ( int ) size ) ; \
} while ( 0 )
# define send_seq(priv, data...) do { \
int rc ; \
2007-10-23 22:24:06 +04:00
static u8 _val [ ] = data ; \
2007-10-02 18:57:03 +04:00
if ( sizeof ( _val ) ! = \
2007-11-01 23:47:42 +03:00
( rc = tuner_i2c_xfer_send ( & priv - > i2c_props , \
2007-10-23 22:24:06 +04:00
_val , sizeof ( _val ) ) ) ) { \
2007-11-01 23:47:42 +03:00
tuner_info ( " Error on line %d: %d \n " , __LINE__ , rc ) ; \
return - EINVAL ; \
2007-10-02 18:57:03 +04:00
} \
2007-11-01 23:47:42 +03:00
msleep ( 10 ) ; \
} while ( 0 )
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
static int xc2028_get_reg ( struct xc2028_data * priv , u16 reg )
2007-10-02 18:57:03 +04:00
{
int rc ;
unsigned char buf [ 1 ] ;
2007-10-23 22:24:06 +04:00
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
buf [ 0 ] = reg ;
2007-10-02 18:57:03 +04:00
2007-10-23 22:24:06 +04:00
i2c_send ( rc , priv , buf , sizeof ( buf ) ) ;
2007-11-01 23:47:42 +03:00
if ( rc < 0 )
2007-10-02 18:57:03 +04:00
return rc ;
2007-10-23 22:24:06 +04:00
i2c_rcv ( rc , priv , buf , 2 ) ;
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
return ( buf [ 1 ] ) | ( buf [ 0 ] < < 8 ) ;
2007-10-02 18:57:03 +04:00
}
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-10-23 22:24:06 +04:00
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
2007-10-24 16:22:08 +04:00
tuner_info ( " Loading firmware %s \n " , priv - > ctrl . fname ) ;
rc = request_firmware ( & fw , priv - > ctrl . fname , priv - > dev ) ;
2007-10-02 18:57:03 +04:00
if ( rc < 0 ) {
2007-11-01 23:47:42 +03:00
if ( rc = = - ENOENT )
2007-10-24 16:22:08 +04:00
tuner_info ( " Error: firmware %s not found. \n " ,
priv - > ctrl . fname ) ;
2007-07-18 20:33:23 +04:00
else
2007-10-24 16:22:08 +04:00
tuner_info ( " Error %d while requesting firmware %s \n " ,
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-10-02 18:57:03 +04:00
tuner_info ( " 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 ;
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 ;
tuner_info ( " there are %d firmwares at %s \n " , n_array , priv - > ctrl . fname ) ;
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 ) {
tuner_info ( " Not enough memory for loading 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 ) {
tuner_info ( " Too much firmwares at the file \n " ) ;
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-10-24 16:22:08 +04:00
tuner_info ( " Lost firmware! \n " ) ;
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-01 23:47:42 +03:00
size = le32_to_cpu ( * ( v4l2_std_id * ) 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-10-24 16:22:08 +04:00
tuner_info ( " Firmware type %x, id %lx corrupt \n " ,
2007-11-01 23:47:42 +03:00
type , ( unsigned long ) id ) ;
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 ) {
tuner_info ( " Not enough memory. \n " ) ;
2007-11-01 23:47:42 +03:00
rc = - ENOMEM ;
2007-10-24 16:22:08 +04:00
goto err ;
}
tuner_info ( " Loading firmware type %x, id %lx, size=%d. \n " ,
2007-11-01 23:47:42 +03:00
type , ( unsigned 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-10-24 16:22:08 +04:00
tuner_info ( " Firmware file is incomplete! \n " ) ;
goto corrupt ;
}
goto done ;
corrupt :
2007-11-01 23:47:42 +03:00
rc = - EINVAL ;
2007-10-24 16:22:08 +04:00
tuner_info ( " Error: firmware file is corrupted! \n " ) ;
err :
tuner_info ( " Releasing loaded firmware file. \n " ) ;
free_firmware ( priv ) ;
done :
release_firmware ( fw ) ;
tuner_info ( " Firmware files loaded. \n " ) ;
return rc ;
}
2007-11-01 23:47:42 +03:00
static int load_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-01 23:47:42 +03:00
int i , rc ;
unsigned char * p , * endp , buf [ priv - > max_len ] ;
2007-10-24 16:22:08 +04:00
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
if ( ! priv - > firm ) {
2007-11-01 23:47:42 +03:00
printk ( KERN_ERR PREFIX " Error! firmware not loaded \n " ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
if ( ( type = = 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-01 23:47:42 +03:00
tuner_info ( " Can't find firmware for type=%x, id=%lx \n " , type ,
( long int ) * id ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
found :
* id = priv - > firm [ i ] . id ;
2007-11-01 23:47:42 +03:00
tuner_info ( " Found firmware for type=%x, id=%lx \n " , type , ( long int ) * id ) ;
2007-10-24 16:22:08 +04:00
p = priv - > firm [ i ] . ptr ;
if ( ! p ) {
printk ( KERN_ERR PREFIX " Firmware pointer were freed! " ) ;
return - EINVAL ;
2007-10-02 18:57:03 +04:00
}
2007-11-01 23:47:42 +03:00
endp = p + priv - > firm [ i ] . 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-10-24 16:22:08 +04:00
tuner_info ( " missing bytes \n " ) ;
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-10-02 18:57:03 +04:00
tuner_info ( " 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-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 ) ) {
tuner_info ( " 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-01 23:47:42 +03:00
i2c_send ( rc , priv , buf , len + 1 ) ;
if ( rc < 0 ) {
tuner_info ( " %d returned from send \n " , rc ) ;
2007-10-24 16:22:08 +04:00
return - EINVAL ;
}
p + = len ;
size - = len ;
}
}
return - EINVAL ;
2007-10-02 18:57:03 +04:00
}
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-10-02 18:57:03 +04:00
int rc , version ;
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-10-23 22:24:06 +04:00
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
2007-10-02 18:57:03 +04:00
2007-10-24 16:22:08 +04:00
if ( ! priv - > firm ) {
if ( ! priv - > ctrl . fname )
return - EINVAL ;
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-01 23:47:42 +03:00
tuner_info ( " I am in mode %u and I should switch to mode %i \n " ,
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-10-23 22:24:06 +04:00
tuner_info ( " 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-10-24 16:22:08 +04:00
tuner_info ( " 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-01 23:47:42 +03:00
tuner_info ( " 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 */
type | = DTV6_QAM ;
2007-07-18 17:29:10 +04:00
break ;
2007-10-24 16:22:08 +04:00
default :
tuner_info ( " 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-10-24 16:22:08 +04:00
/* Load INIT1, if needed */
tuner_info ( " Trying to load init1 firmware \n " ) ;
type0 = BASE | INIT1 | priv - > ctrl . type ;
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-11-01 23:47:42 +03:00
tuner_info ( " firmware standard to load: %08lx \n " , ( unsigned long ) std ) ;
2007-10-23 22:24:06 +04:00
if ( priv - > firm_type & std ) {
2007-10-24 16:22:08 +04:00
tuner_info ( " no need to load a std-specific firmware. \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-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-10-23 22:24:06 +04:00
version = xc2028_get_reg ( priv , 0x4 ) ;
2007-10-02 18:57:03 +04:00
tuner_info ( " Firmware version is %d.%d \n " ,
2007-11-01 23:47:42 +03:00
( version > > 4 ) & 0x0f , ( version ) & 0x0f ) ;
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-01 23:47:42 +03:00
int frq_lock , signal = 0 ;
2007-09-28 01:27:03 +04:00
2007-10-23 22:24:06 +04:00
tuner_info ( " %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-10-23 22:24:06 +04:00
* strength = 0 ;
frq_lock = xc2028_get_reg ( priv , 0x2 ) ;
2007-11-01 23:47:42 +03:00
if ( 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-10-23 22:24:06 +04:00
signal = xc2028_get_reg ( priv , 0x40 ) ;
2007-10-02 18:57:03 +04:00
2007-11-01 23:47:42 +03:00
if ( signal < = 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-10-23 22:24:06 +04:00
return 0 ;
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-10-23 22:24:06 +04:00
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
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-10-23 22:24:06 +04:00
tuner_info ( " 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-01 23:47:42 +03:00
if ( priv - > version < 0x0202 ) {
2007-10-24 16:22:08 +04:00
send_seq ( priv , { 0x00 , 0x02 , 0x00 , 0x00 } ) ;
} else {
send_seq ( priv , { 0x80 , 0x02 , 0x00 , 0x00 } ) ;
}
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-10-23 22:24:06 +04:00
i2c_send ( rc , 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-10-02 18:57:03 +04:00
printk ( " 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-10-23 22:24:06 +04:00
tuner_info ( " %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-10-23 22:24:06 +04:00
tuner_info ( " %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-01 23:47:42 +03:00
tuner_info ( " 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 ;
tuner_info ( " %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-10-23 22:24:06 +04:00
tuner_info ( " %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 ;
tuner_info ( " %s called \n " , __FUNCTION__ ) ;
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
tuner_info ( " %s OK \n " , __FUNCTION__ ) ;
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-01 23:47:42 +03:00
int xc2028_attach ( struct dvb_frontend * fe , struct i2c_adapter * i2c_adap ,
2007-10-23 22:24:06 +04:00
u8 i2c_addr , struct device * dev , void * video_dev ,
2007-11-01 23:47:42 +03:00
int ( * tuner_callback ) ( void * dev , int command , int arg ) )
2007-07-18 17:29:10 +04:00
{
2007-10-23 22:24:06 +04:00
struct xc2028_data * priv ;
2007-07-18 17:29:10 +04:00
2007-11-01 23:47:42 +03:00
printk ( KERN_INFO PREFIX " Xcv2028/3028 init called! \n " ) ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
if ( NULL = = dev )
return - ENODEV ;
if ( NULL = = video_dev )
return - ENODEV ;
if ( ! tuner_callback ) {
2007-11-01 23:47:42 +03:00
printk ( KERN_ERR PREFIX " No tuner callback! \n " ) ;
2007-10-23 22:24:06 +04:00
return - EINVAL ;
}
list_for_each_entry ( priv , & xc2028_list , xc2028_list ) {
2007-11-01 23:47:42 +03:00
if ( priv - > dev = = dev )
2007-10-23 22:24:06 +04:00
dev = NULL ;
}
if ( dev ) {
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( priv = = NULL )
return - ENOMEM ;
2007-07-18 17:29:10 +04:00
2007-10-23 22:24:06 +04:00
fe - > tuner_priv = priv ;
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 ;
priv - > i2c_props . addr = i2c_addr ;
priv - > i2c_props . adap = i2c_adap ;
priv - > dev = dev ;
priv - > video_dev = video_dev ;
priv - > tuner_callback = tuner_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-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 " ) ;
return 0 ;
}
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 " ) ;