2008-09-04 08:14:43 +04:00
/*
Conexant cx24116 / cx24118 - DVBS / S2 Satellite demod / tuner driver
Copyright ( C ) 2006 - 2008 Steven Toth < stoth @ hauppauge . com >
2008-09-14 02:42:16 +04:00
Copyright ( C ) 2006 - 2007 Georg Acher
Copyright ( C ) 2007 - 2008 Darron Broad
March 2007
Fixed some bugs .
Added diseqc support .
Added corrected signal strength support .
August 2007
Sync with legacy version .
Some clean ups .
Copyright ( C ) 2008 Igor Liplianin
September , 9 th 2008
2008-09-14 14:43:53 +04:00
Fixed locking on high symbol rates ( > 30000 ) .
Implement MPEG initialization parameter .
2008-09-04 08:14:43 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/firmware.h>
# include "dvb_frontend.h"
# include "cx24116.h"
2008-10-17 03:22:01 +04:00
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Activates frontend debugging (default:0) " ) ;
2008-09-14 02:42:16 +04:00
# define dprintk(args...) \
do { \
2008-10-17 03:22:01 +04:00
if ( debug ) \
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " cx24116: " args ) ; \
2008-09-14 02:42:16 +04:00
} while ( 0 )
2008-09-04 08:14:43 +04:00
# define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
# define CX24116_SEARCH_RANGE_KHZ 5000
2008-09-14 02:42:16 +04:00
/* known registers */
# define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */
# define CX24116_REG_EXECUTE (0x1f) /* execute command */
# define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */
# define CX24116_REG_RESET (0x20) /* reset status > 0 */
# define CX24116_REG_SIGNAL (0x9e) /* signal low */
# define CX24116_REG_SSTATUS (0x9d) /* signal high / status */
2008-10-07 04:20:21 +04:00
# define CX24116_REG_QUALITY8 (0xa3)
2008-09-14 02:42:16 +04:00
# define CX24116_REG_QSTATUS (0xbc)
2008-10-07 04:20:21 +04:00
# define CX24116_REG_QUALITY0 (0xd5)
2008-09-14 02:42:16 +04:00
# define CX24116_REG_BER0 (0xc9)
# define CX24116_REG_BER8 (0xc8)
# define CX24116_REG_BER16 (0xc7)
# define CX24116_REG_BER24 (0xc6)
# define CX24116_REG_UCB0 (0xcb)
# define CX24116_REG_UCB8 (0xca)
# define CX24116_REG_CLKDIV (0xf3)
# define CX24116_REG_RATEDIV (0xf9)
2008-10-17 03:24:42 +04:00
/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
# define CX24116_REG_FECSTATUS (0x9c)
2008-09-22 07:47:20 +04:00
/* FECSTATUS bits */
2008-10-17 03:24:42 +04:00
/* mask to determine configured fec (not tuned) or actual fec (tuned) */
# define CX24116_FEC_FECMASK (0x1f)
/* Select DVB-S demodulator, else DVB-S2 */
# define CX24116_FEC_DVBS (0x20)
2008-09-22 07:47:20 +04:00
# define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */
2008-10-17 03:24:42 +04:00
/* Pilot mode requested when tuning else always reset when tuned */
# define CX24116_FEC_PILOT (0x80)
2008-09-04 08:14:43 +04:00
/* arg buffer size */
# define CX24116_ARGLEN (0x1e)
2008-09-14 02:42:16 +04:00
/* rolloff */
# define CX24116_ROLLOFF_020 (0x00)
# define CX24116_ROLLOFF_025 (0x01)
# define CX24116_ROLLOFF_035 (0x02)
/* pilot bit */
2008-10-03 18:47:46 +04:00
# define CX24116_PILOT_OFF (0x00)
# define CX24116_PILOT_ON (0x40)
2008-09-14 02:42:16 +04:00
/* signal status */
# define CX24116_HAS_SIGNAL (0x01)
# define CX24116_HAS_CARRIER (0x02)
# define CX24116_HAS_VITERBI (0x04)
# define CX24116_HAS_SYNCLOCK (0x08)
# define CX24116_HAS_UNKNOWN1 (0x10)
# define CX24116_HAS_UNKNOWN2 (0x20)
# define CX24116_STATUS_MASK (0x3f)
# define CX24116_SIGNAL_MASK (0xc0)
# define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
# define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */
# define CX24116_DISEQC_MESGCACHE (2) /* message cached */
2008-09-04 08:14:43 +04:00
/* arg offset for DiSEqC */
# define CX24116_DISEQC_BURST (1)
# define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */
# define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */
# define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */
# define CX24116_DISEQC_MSGLEN (5)
# define CX24116_DISEQC_MSGOFS (6)
/* DiSEqC burst */
# define CX24116_DISEQC_MINI_A (0)
# define CX24116_DISEQC_MINI_B (1)
2008-09-14 02:42:16 +04:00
/* DiSEqC tone burst */
static int toneburst = 1 ;
2008-10-17 03:22:01 +04:00
module_param ( toneburst , int , 0644 ) ;
2008-10-17 03:24:42 +04:00
MODULE_PARM_DESC ( toneburst , " DiSEqC toneburst 0=OFF, 1=TONE CACHE, " \
" 2=MESSAGE CACHE (default:1) " ) ;
2008-09-14 02:42:16 +04:00
2008-10-07 04:20:21 +04:00
/* SNR measurements */
2008-10-17 03:22:01 +04:00
static int esno_snr ;
module_param ( esno_snr , int , 0644 ) ;
2008-10-17 03:24:42 +04:00
MODULE_PARM_DESC ( debug , " SNR return units, 0=PERCENTAGE 0-100, " \
" 1=ESNO(db * 10) (default:0) " ) ;
2008-10-07 04:20:21 +04:00
2008-10-17 03:22:01 +04:00
enum cmds {
2008-09-14 02:42:16 +04:00
CMD_SET_VCO = 0x10 ,
2008-09-04 08:14:43 +04:00
CMD_TUNEREQUEST = 0x11 ,
2008-09-14 02:42:16 +04:00
CMD_MPEGCONFIG = 0x13 ,
CMD_TUNERINIT = 0x14 ,
CMD_BANDWIDTH = 0x15 ,
CMD_GETAGC = 0x19 ,
CMD_LNBCONFIG = 0x20 ,
CMD_LNBSEND = 0x21 , /* Formerly CMD_SEND_DISEQC */
2008-09-04 08:14:43 +04:00
CMD_SET_TONEPRE = 0x22 ,
CMD_SET_TONE = 0x23 ,
2008-09-14 02:42:16 +04:00
CMD_UPDFWVERS = 0x35 ,
CMD_TUNERSLEEP = 0x36 ,
CMD_AGCCONTROL = 0x3b , /* Unknown */
2008-09-04 08:14:43 +04:00
} ;
/* The Demod/Tuner can't easily provide these, we cache them */
2008-10-17 03:22:01 +04:00
struct cx24116_tuning {
2008-09-04 08:14:43 +04:00
u32 frequency ;
u32 symbol_rate ;
fe_spectral_inversion_t inversion ;
fe_code_rate_t fec ;
2008-12-18 12:21:51 +03:00
fe_delivery_system_t delsys ;
2008-09-04 08:14:43 +04:00
fe_modulation_t modulation ;
2008-09-14 02:42:16 +04:00
fe_pilot_t pilot ;
fe_rolloff_t rolloff ;
2008-09-04 08:14:43 +04:00
/* Demod values */
u8 fec_val ;
u8 fec_mask ;
u8 inversion_val ;
2008-10-03 18:47:46 +04:00
u8 pilot_val ;
2008-09-14 02:42:16 +04:00
u8 rolloff_val ;
2008-09-04 08:14:43 +04:00
} ;
/* Basic commands that are sent to the firmware */
2008-10-17 03:22:01 +04:00
struct cx24116_cmd {
2008-09-04 08:14:43 +04:00
u8 len ;
u8 args [ CX24116_ARGLEN ] ;
} ;
2008-10-17 03:22:01 +04:00
struct cx24116_state {
struct i2c_adapter * i2c ;
const struct cx24116_config * config ;
2008-09-04 08:14:43 +04:00
struct dvb_frontend frontend ;
struct cx24116_tuning dcur ;
struct cx24116_tuning dnxt ;
u8 skip_fw_load ;
u8 burst ;
2008-09-14 02:42:16 +04:00
struct cx24116_cmd dsec_cmd ;
2008-09-04 08:14:43 +04:00
} ;
2008-10-17 03:22:01 +04:00
static int cx24116_writereg ( struct cx24116_state * state , int reg , int data )
2008-09-04 08:14:43 +04:00
{
u8 buf [ ] = { reg , data } ;
struct i2c_msg msg = { . addr = state - > config - > demod_address ,
. flags = 0 , . buf = buf , . len = 2 } ;
int err ;
2008-10-17 03:22:01 +04:00
if ( debug > 1 )
2008-09-04 08:14:43 +04:00
printk ( " cx24116: %s: write reg 0x%02x, value 0x%02x \n " ,
2008-10-17 03:22:01 +04:00
__func__ , reg , data ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
err = i2c_transfer ( state - > i2c , & msg , 1 ) ;
if ( err ! = 1 ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: writereg error(err == %i, reg == 0x%02x, "
2008-09-04 08:14:43 +04:00
" value == 0x%02x) \n " , __func__ , err , reg , data ) ;
return - EREMOTEIO ;
}
return 0 ;
}
/* Bulk byte writes to a single I2C address, for 32k firmware load */
2008-10-17 03:22:01 +04:00
static int cx24116_writeregN ( struct cx24116_state * state , int reg ,
2008-10-17 04:04:35 +04:00
const u8 * data , u16 len )
2008-09-04 08:14:43 +04:00
{
int ret = - EREMOTEIO ;
struct i2c_msg msg ;
u8 * buf ;
buf = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( buf = = NULL ) {
printk ( " Unable to kmalloc \n " ) ;
ret = - ENOMEM ;
goto error ;
}
* ( buf ) = reg ;
memcpy ( buf + 1 , data , len ) ;
msg . addr = state - > config - > demod_address ;
msg . flags = 0 ;
msg . buf = buf ;
msg . len = len + 1 ;
2008-10-17 03:22:01 +04:00
if ( debug > 1 )
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " cx24116: %s: write regN 0x%02x, len = %d \n " ,
2008-10-17 03:22:01 +04:00
__func__ , reg , len ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
ret = i2c_transfer ( state - > i2c , & msg , 1 ) ;
if ( ret ! = 1 ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: writereg error(err == %i, reg == 0x%02x \n " ,
2008-09-04 08:14:43 +04:00
__func__ , ret , reg ) ;
ret = - EREMOTEIO ;
}
error :
kfree ( buf ) ;
return ret ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_readreg ( struct cx24116_state * state , u8 reg )
2008-09-04 08:14:43 +04:00
{
int ret ;
u8 b0 [ ] = { reg } ;
u8 b1 [ ] = { 0 } ;
struct i2c_msg msg [ ] = {
2008-10-17 03:22:01 +04:00
{ . addr = state - > config - > demod_address , . flags = 0 ,
. buf = b0 , . len = 1 } ,
{ . addr = state - > config - > demod_address , . flags = I2C_M_RD ,
. buf = b1 , . len = 1 }
2008-09-04 08:14:43 +04:00
} ;
ret = i2c_transfer ( state - > i2c , msg , 2 ) ;
if ( ret ! = 2 ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: reg=0x%x (error=%d) \n " ,
__func__ , reg , ret ) ;
2008-09-04 08:14:43 +04:00
return ret ;
}
2008-10-17 03:22:01 +04:00
if ( debug > 1 )
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " cx24116: read reg 0x%02x, value 0x%02x \n " ,
2008-10-17 03:22:01 +04:00
reg , b1 [ 0 ] ) ;
2008-09-04 08:14:43 +04:00
return b1 [ 0 ] ;
}
2008-10-17 03:24:42 +04:00
static int cx24116_set_inversion ( struct cx24116_state * state ,
fe_spectral_inversion_t inversion )
2008-09-04 08:14:43 +04:00
{
dprintk ( " %s(%d) \n " , __func__ , inversion ) ;
switch ( inversion ) {
case INVERSION_OFF :
state - > dnxt . inversion_val = 0x00 ;
break ;
case INVERSION_ON :
state - > dnxt . inversion_val = 0x04 ;
break ;
case INVERSION_AUTO :
state - > dnxt . inversion_val = 0x0C ;
break ;
default :
return - EINVAL ;
}
state - > dnxt . inversion = inversion ;
return 0 ;
}
2008-09-14 02:42:16 +04:00
/*
* modfec ( modulation and FEC )
* = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* MOD FEC mask / val standard
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* QPSK FEC_1_2 0x02 0x02 + X DVB - S
* QPSK FEC_2_3 0x04 0x02 + X DVB - S
* QPSK FEC_3_4 0x08 0x02 + X DVB - S
* QPSK FEC_4_5 0x10 0x02 + X DVB - S ( ? )
* QPSK FEC_5_6 0x20 0x02 + X DVB - S
* QPSK FEC_6_7 0x40 0x02 + X DVB - S
* QPSK FEC_7_8 0x80 0x02 + X DVB - S
* QPSK FEC_8_9 0x01 0x02 + X DVB - S ( ? ) ( NOT SUPPORTED ? )
* QPSK AUTO 0xff 0x02 + X DVB - S
*
* For DVB - S high byte probably represents FEC
* and low byte selects the modulator . The high
* byte is search range mask . Bit 5 may turn
* on DVB - S and remaining bits represent some
* kind of calibration ( how / what i do not know ) .
*
* Eg . ( 2 / 3 ) szap " Zone Horror "
*
* mask / val = 0x04 , 0x20
2008-10-17 03:24:42 +04:00
* status 1f | signal c3c0 | snr a333 | ber 000000 98 | unc 0 | FE_HAS_LOCK
2008-09-14 02:42:16 +04:00
*
* mask / val = 0x04 , 0x30
2008-10-17 03:24:42 +04:00
* status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK
2008-09-14 02:42:16 +04:00
*
* After tuning FECSTATUS contains actual FEC
* in use numbered 1 through to 8 for 1 / 2 . . 2 / 3 etc
*
* NBC = NOT / NON BACKWARD COMPATIBLE WITH DVB - S ( DVB - S2 only )
*
* NBC - QPSK FEC_1_2 0x00 , 0x04 DVB - S2
* NBC - QPSK FEC_3_5 0x00 , 0x05 DVB - S2
* NBC - QPSK FEC_2_3 0x00 , 0x06 DVB - S2
* NBC - QPSK FEC_3_4 0x00 , 0x07 DVB - S2
* NBC - QPSK FEC_4_5 0x00 , 0x08 DVB - S2
* NBC - QPSK FEC_5_6 0x00 , 0x09 DVB - S2
* NBC - QPSK FEC_8_9 0x00 , 0x0a DVB - S2
* NBC - QPSK FEC_9_10 0x00 , 0x0b DVB - S2
*
* NBC - 8 PSK FEC_3_5 0x00 , 0x0c DVB - S2
* NBC - 8 PSK FEC_2_3 0x00 , 0x0d DVB - S2
* NBC - 8 PSK FEC_3_4 0x00 , 0x0e DVB - S2
* NBC - 8 PSK FEC_5_6 0x00 , 0x0f DVB - S2
* NBC - 8 PSK FEC_8_9 0x00 , 0x10 DVB - S2
* NBC - 8 PSK FEC_9_10 0x00 , 0x11 DVB - S2
*
* For DVB - S2 low bytes selects both modulator
* and FEC . High byte is meaningless here . To
* set pilot , bit 6 ( 0x40 ) is set . When inspecting
* FECSTATUS bit 7 ( 0x80 ) represents the pilot
* selection whilst not tuned . When tuned , actual FEC
* in use is found in FECSTATUS as per above . Pilot
* value is reset .
*/
2008-09-04 08:14:43 +04:00
/* A table of modulation, fec and configuration bytes for the demod.
* Not all S2 mmodulation schemes are support and not all rates with
* a scheme are support . Especially , no auto detect when in S2 mode .
*/
struct cx24116_modfec {
2008-10-07 04:06:48 +04:00
fe_delivery_system_t delivery_system ;
2008-09-04 08:14:43 +04:00
fe_modulation_t modulation ;
fe_code_rate_t fec ;
u8 mask ; /* In DVBS mode this is used to autodetect */
u8 val ; /* Passed to the firmware to indicate mode selection */
} CX24116_MODFEC_MODES [ ] = {
/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
2008-09-14 02:42:16 +04:00
/*mod fec mask val */
2008-10-07 04:06:48 +04:00
{ SYS_DVBS , QPSK , FEC_NONE , 0xfe , 0x30 } ,
{ SYS_DVBS , QPSK , FEC_1_2 , 0x02 , 0x2e } , /* 00000010 00101110 */
{ SYS_DVBS , QPSK , FEC_2_3 , 0x04 , 0x2f } , /* 00000100 00101111 */
{ SYS_DVBS , QPSK , FEC_3_4 , 0x08 , 0x30 } , /* 00001000 00110000 */
{ SYS_DVBS , QPSK , FEC_4_5 , 0xfe , 0x30 } , /* 000?0000 ? */
{ SYS_DVBS , QPSK , FEC_5_6 , 0x20 , 0x31 } , /* 00100000 00110001 */
{ SYS_DVBS , QPSK , FEC_6_7 , 0xfe , 0x30 } , /* 0?000000 ? */
{ SYS_DVBS , QPSK , FEC_7_8 , 0x80 , 0x32 } , /* 10000000 00110010 */
{ SYS_DVBS , QPSK , FEC_8_9 , 0xfe , 0x30 } , /* 0000000? ? */
{ SYS_DVBS , QPSK , FEC_AUTO , 0xfe , 0x30 } ,
2008-09-04 08:14:43 +04:00
/* NBC-QPSK */
2008-10-07 04:06:48 +04:00
{ SYS_DVBS2 , QPSK , FEC_1_2 , 0x00 , 0x04 } ,
{ SYS_DVBS2 , QPSK , FEC_3_5 , 0x00 , 0x05 } ,
{ SYS_DVBS2 , QPSK , FEC_2_3 , 0x00 , 0x06 } ,
{ SYS_DVBS2 , QPSK , FEC_3_4 , 0x00 , 0x07 } ,
{ SYS_DVBS2 , QPSK , FEC_4_5 , 0x00 , 0x08 } ,
{ SYS_DVBS2 , QPSK , FEC_5_6 , 0x00 , 0x09 } ,
{ SYS_DVBS2 , QPSK , FEC_8_9 , 0x00 , 0x0a } ,
{ SYS_DVBS2 , QPSK , FEC_9_10 , 0x00 , 0x0b } ,
2008-09-04 08:14:43 +04:00
/* 8PSK */
2008-10-07 04:06:48 +04:00
{ SYS_DVBS2 , PSK_8 , FEC_3_5 , 0x00 , 0x0c } ,
{ SYS_DVBS2 , PSK_8 , FEC_2_3 , 0x00 , 0x0d } ,
{ SYS_DVBS2 , PSK_8 , FEC_3_4 , 0x00 , 0x0e } ,
{ SYS_DVBS2 , PSK_8 , FEC_5_6 , 0x00 , 0x0f } ,
{ SYS_DVBS2 , PSK_8 , FEC_8_9 , 0x00 , 0x10 } ,
{ SYS_DVBS2 , PSK_8 , FEC_9_10 , 0x00 , 0x11 } ,
2008-09-14 02:42:16 +04:00
/*
* ` val ' can be found in the FECSTATUS register when tuning .
* FECSTATUS will give the actual FEC in use if tuning was successful .
*/
2008-09-04 08:14:43 +04:00
} ;
2008-10-17 03:22:01 +04:00
static int cx24116_lookup_fecmod ( struct cx24116_state * state ,
2008-12-18 12:21:51 +03:00
fe_delivery_system_t d , fe_modulation_t m , fe_code_rate_t f )
2008-09-04 08:14:43 +04:00
{
int i , ret = - EOPNOTSUPP ;
2008-09-14 02:42:16 +04:00
dprintk ( " %s(0x%02x,0x%02x) \n " , __func__ , m , f ) ;
2008-10-17 03:22:01 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( CX24116_MODFEC_MODES ) ; i + + ) {
2008-12-18 12:21:51 +03:00
if ( ( d = = CX24116_MODFEC_MODES [ i ] . delivery_system ) & &
( m = = CX24116_MODFEC_MODES [ i ] . modulation ) & &
2008-10-17 03:22:01 +04:00
( f = = CX24116_MODFEC_MODES [ i ] . fec ) ) {
2008-09-04 08:14:43 +04:00
ret = i ;
break ;
}
}
return ret ;
}
2008-10-17 03:24:42 +04:00
static int cx24116_set_fec ( struct cx24116_state * state ,
2008-12-18 12:21:51 +03:00
fe_delivery_system_t delsys , fe_modulation_t mod , fe_code_rate_t fec )
2008-09-04 08:14:43 +04:00
{
int ret = 0 ;
2008-09-14 02:42:16 +04:00
dprintk ( " %s(0x%02x,0x%02x) \n " , __func__ , mod , fec ) ;
2008-09-04 08:14:43 +04:00
2008-12-18 12:21:51 +03:00
ret = cx24116_lookup_fecmod ( state , delsys , mod , fec ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
if ( ret < 0 )
2008-09-04 08:14:43 +04:00
return ret ;
2008-09-14 02:42:16 +04:00
state - > dnxt . fec = fec ;
2008-09-04 08:14:43 +04:00
state - > dnxt . fec_val = CX24116_MODFEC_MODES [ ret ] . val ;
state - > dnxt . fec_mask = CX24116_MODFEC_MODES [ ret ] . mask ;
2008-09-14 02:42:16 +04:00
dprintk ( " %s() mask/val = 0x%02x/0x%02x \n " , __func__ ,
state - > dnxt . fec_mask , state - > dnxt . fec_val ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_set_symbolrate ( struct cx24116_state * state , u32 rate )
2008-09-04 08:14:43 +04:00
{
2008-09-14 02:42:16 +04:00
dprintk ( " %s(%d) \n " , __func__ , rate ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* check if symbol rate is within limits */
if ( ( rate > state - > frontend . ops . info . symbol_rate_max ) | |
( rate < state - > frontend . ops . info . symbol_rate_min ) ) {
dprintk ( " %s() unsupported symbol_rate = %d \n " , __func__ , rate ) ;
return - EOPNOTSUPP ;
}
2008-09-04 08:14:43 +04:00
state - > dnxt . symbol_rate = rate ;
2008-09-14 02:42:16 +04:00
dprintk ( " %s() symbol_rate = %d \n " , __func__ , rate ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
return 0 ;
2008-09-04 08:14:43 +04:00
}
2008-10-17 03:22:01 +04:00
static int cx24116_load_firmware ( struct dvb_frontend * fe ,
const struct firmware * fw ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
static int cx24116_firmware_ondemand ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
const struct firmware * fw ;
int ret = 0 ;
2008-10-17 03:22:01 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
if ( cx24116_readreg ( state , 0x20 ) > 0 ) {
2008-09-04 08:14:43 +04:00
if ( state - > skip_fw_load )
return 0 ;
/* Load firmware */
2008-10-17 03:24:42 +04:00
/* request the firmware, this will block until loaded */
printk ( KERN_INFO " %s: Waiting for firmware upload (%s)... \n " ,
__func__ , CX24116_DEFAULT_FIRMWARE ) ;
ret = request_firmware ( & fw , CX24116_DEFAULT_FIRMWARE ,
& state - > i2c - > dev ) ;
printk ( KERN_INFO " %s: Waiting for firmware upload(2)... \n " ,
__func__ ) ;
2008-09-04 08:14:43 +04:00
if ( ret ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: No firmware uploaded "
" (timeout or file not found?) \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
return ret ;
}
2008-10-17 03:24:42 +04:00
/* Make sure we don't recurse back through here
* during loading */
2008-09-04 08:14:43 +04:00
state - > skip_fw_load = 1 ;
ret = cx24116_load_firmware ( fe , fw ) ;
if ( ret )
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: Writing firmware to device failed \n " ,
__func__ ) ;
2008-09-04 08:14:43 +04:00
release_firmware ( fw ) ;
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " %s: Firmware upload %s \n " , __func__ ,
ret = = 0 ? " complete " : " failed " ) ;
2008-09-04 08:14:43 +04:00
/* Ensure firmware is always loaded if required */
state - > skip_fw_load = 0 ;
}
return ret ;
}
2008-10-17 03:24:42 +04:00
/* Take a basic firmware command structure, format it
* and forward it for processing
*/
2008-10-17 03:22:01 +04:00
static int cx24116_cmd_execute ( struct dvb_frontend * fe , struct cx24116_cmd * cmd )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
int i , ret ;
dprintk ( " %s() \n " , __func__ ) ;
/* Load the firmware if required */
2008-10-17 03:22:01 +04:00
ret = cx24116_firmware_ondemand ( fe ) ;
if ( ret ! = 0 ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s(): Unable initialise the firmware \n " ,
__func__ ) ;
2008-09-04 08:14:43 +04:00
return ret ;
}
/* Write the command */
2008-10-17 03:22:01 +04:00
for ( i = 0 ; i < cmd - > len ; i + + ) {
2008-09-04 08:14:43 +04:00
dprintk ( " %s: 0x%02x == 0x%02x \n " , __func__ , i , cmd - > args [ i ] ) ;
cx24116_writereg ( state , i , cmd - > args [ i ] ) ;
}
/* Start execution and wait for cmd to terminate */
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , CX24116_REG_EXECUTE , 0x01 ) ;
2008-10-17 03:22:01 +04:00
while ( cx24116_readreg ( state , CX24116_REG_EXECUTE ) ) {
2008-09-04 08:14:43 +04:00
msleep ( 10 ) ;
2008-10-17 03:22:01 +04:00
if ( i + + > 64 ) {
2008-10-17 03:24:42 +04:00
/* Avoid looping forever if the firmware does
not respond */
printk ( KERN_WARNING " %s() Firmware not responding \n " ,
__func__ ) ;
2008-09-04 08:14:43 +04:00
return - EREMOTEIO ;
}
}
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_load_firmware ( struct dvb_frontend * fe ,
const struct firmware * fw )
2008-09-04 08:14:43 +04:00
{
2008-10-17 03:22:01 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-04 08:14:43 +04:00
struct cx24116_cmd cmd ;
2008-09-14 02:42:16 +04:00
int i , ret ;
unsigned char vers [ 4 ] ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s \n " , __func__ ) ;
2008-10-17 03:22:01 +04:00
dprintk ( " Firmware is %zu bytes (%02x %02x .. %02x %02x) \n " ,
fw - > size ,
fw - > data [ 0 ] ,
fw - > data [ 1 ] ,
fw - > data [ fw - > size - 2 ] ,
fw - > data [ fw - > size - 1 ] ) ;
2008-09-04 08:14:43 +04:00
/* Toggle 88x SRST pin to reset demod */
if ( state - > config - > reset_device )
state - > config - > reset_device ( fe ) ;
/* Begin the firmware load process */
/* Prepare the demod, load the firmware, cleanup after load */
2008-09-14 02:42:16 +04:00
/* Init PLL */
cx24116_writereg ( state , 0xE5 , 0x00 ) ;
2008-09-04 08:14:43 +04:00
cx24116_writereg ( state , 0xF1 , 0x08 ) ;
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , 0xF2 , 0x13 ) ;
/* Start PLL */
cx24116_writereg ( state , 0xe0 , 0x03 ) ;
cx24116_writereg ( state , 0xe0 , 0x00 ) ;
/* Unknown */
cx24116_writereg ( state , CX24116_REG_CLKDIV , 0x46 ) ;
cx24116_writereg ( state , CX24116_REG_RATEDIV , 0x00 ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Unknown */
2008-09-04 08:14:43 +04:00
cx24116_writereg ( state , 0xF0 , 0x03 ) ;
cx24116_writereg ( state , 0xF4 , 0x81 ) ;
cx24116_writereg ( state , 0xF5 , 0x00 ) ;
cx24116_writereg ( state , 0xF6 , 0x00 ) ;
/* write the entire firmware as one transaction */
cx24116_writeregN ( state , 0xF7 , fw - > data , fw - > size ) ;
cx24116_writereg ( state , 0xF4 , 0x10 ) ;
cx24116_writereg ( state , 0xF0 , 0x00 ) ;
cx24116_writereg ( state , 0xF8 , 0x06 ) ;
2008-09-14 02:42:16 +04:00
/* Firmware CMD 10: VCO config */
cmd . args [ 0x00 ] = CMD_SET_VCO ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x01 ] = 0x05 ;
cmd . args [ 0x02 ] = 0xdc ;
cmd . args [ 0x03 ] = 0xda ;
cmd . args [ 0x04 ] = 0xae ;
cmd . args [ 0x05 ] = 0xaa ;
cmd . args [ 0x06 ] = 0x04 ;
cmd . args [ 0x07 ] = 0x9d ;
cmd . args [ 0x08 ] = 0xfc ;
cmd . args [ 0x09 ] = 0x06 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x0a ;
2008-09-04 08:14:43 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , CX24116_REG_SSTATUS , 0x00 ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Firmware CMD 14: Tuner config */
cmd . args [ 0x00 ] = CMD_TUNERINIT ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x01 ] = 0x00 ;
cmd . args [ 0x02 ] = 0x00 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x03 ;
2008-09-04 08:14:43 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
cx24116_writereg ( state , 0xe5 , 0x00 ) ;
2008-09-14 02:42:16 +04:00
/* Firmware CMD 13: MPEG config */
cmd . args [ 0x00 ] = CMD_MPEGCONFIG ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x01 ] = 0x01 ;
cmd . args [ 0x02 ] = 0x75 ;
cmd . args [ 0x03 ] = 0x00 ;
2008-09-09 20:57:47 +04:00
if ( state - > config - > mpg_clk_pos_pol )
cmd . args [ 0x04 ] = state - > config - > mpg_clk_pos_pol ;
else
cmd . args [ 0x04 ] = 0x02 ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x05 ] = 0x00 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x06 ;
2008-09-04 08:14:43 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-09-14 02:42:16 +04:00
/* Firmware CMD 35: Get firmware version */
cmd . args [ 0x00 ] = CMD_UPDFWVERS ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
for ( i = 0 ; i < 4 ; i + + ) {
2008-09-14 02:42:16 +04:00
cmd . args [ 0x01 ] = i ;
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-10-17 03:22:01 +04:00
vers [ i ] = cx24116_readreg ( state , CX24116_REG_MAILBOX ) ;
2008-09-14 02:42:16 +04:00
}
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " %s: FW version %i.%i.%i.%i \n " , __func__ ,
2008-09-14 02:42:16 +04:00
vers [ 0 ] , vers [ 1 ] , vers [ 2 ] , vers [ 3 ] ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_set_voltage ( struct dvb_frontend * fe ,
fe_sec_voltage_t voltage )
2008-09-04 08:14:43 +04:00
{
/* The isl6421 module will override this function in the fops. */
2008-10-17 03:22:01 +04:00
dprintk ( " %s() This should never appear if the isl6421 module "
" is loaded correctly \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
return - EOPNOTSUPP ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_read_status ( struct dvb_frontend * fe , fe_status_t * status )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-14 02:42:16 +04:00
int lock = cx24116_readreg ( state , CX24116_REG_SSTATUS ) ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s: status = 0x%02x \n " , __func__ , lock ) ;
* status = 0 ;
2008-09-14 02:42:16 +04:00
if ( lock & CX24116_HAS_SIGNAL )
2008-09-04 08:14:43 +04:00
* status | = FE_HAS_SIGNAL ;
2008-09-14 02:42:16 +04:00
if ( lock & CX24116_HAS_CARRIER )
2008-09-04 08:14:43 +04:00
* status | = FE_HAS_CARRIER ;
2008-09-14 02:42:16 +04:00
if ( lock & CX24116_HAS_VITERBI )
2008-09-04 08:14:43 +04:00
* status | = FE_HAS_VITERBI ;
2008-09-14 02:42:16 +04:00
if ( lock & CX24116_HAS_SYNCLOCK )
2008-09-04 08:14:43 +04:00
* status | = FE_HAS_SYNC | FE_HAS_LOCK ;
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_read_ber ( struct dvb_frontend * fe , u32 * ber )
2008-09-04 08:14:43 +04:00
{
2008-09-14 02:42:16 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
2008-10-17 03:22:01 +04:00
* ber = ( cx24116_readreg ( state , CX24116_REG_BER24 ) < < 24 ) |
( cx24116_readreg ( state , CX24116_REG_BER16 ) < < 16 ) |
( cx24116_readreg ( state , CX24116_REG_BER8 ) < < 8 ) |
cx24116_readreg ( state , CX24116_REG_BER0 ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
2008-09-14 02:42:16 +04:00
/* TODO Determine function and scale appropriately */
2008-10-17 03:22:01 +04:00
static int cx24116_read_signal_strength ( struct dvb_frontend * fe ,
u16 * signal_strength )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-14 02:42:16 +04:00
struct cx24116_cmd cmd ;
int ret ;
u16 sig_reading ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
/* Firmware CMD 19: Get AGC */
cmd . args [ 0x00 ] = CMD_GETAGC ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x01 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
sig_reading =
( cx24116_readreg ( state ,
CX24116_REG_SSTATUS ) & CX24116_SIGNAL_MASK ) |
( cx24116_readreg ( state , CX24116_REG_SIGNAL ) < < 6 ) ;
* signal_strength = 0 - sig_reading ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
dprintk ( " %s: raw / cooked = 0x%04x / 0x%04x \n " ,
__func__ , sig_reading , * signal_strength ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
2008-09-14 02:42:16 +04:00
/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
2008-10-17 03:22:01 +04:00
static int cx24116_read_snr_pct ( struct dvb_frontend * fe , u16 * snr )
2008-09-04 08:14:43 +04:00
{
2008-09-14 02:42:16 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
u8 snr_reading ;
static const u32 snr_tab [ ] = { /* 10 x Table (rounded up) */
2008-10-17 03:22:01 +04:00
0x00000 , 0x0199A , 0x03333 , 0x04ccD , 0x06667 ,
0x08000 , 0x0999A , 0x0b333 , 0x0cccD , 0x0e667 ,
0x10000 , 0x1199A , 0x13333 , 0x14ccD , 0x16667 ,
0x18000 } ;
2008-09-14 02:42:16 +04:00
2008-09-04 08:14:43 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
2008-10-07 04:20:21 +04:00
snr_reading = cx24116_readreg ( state , CX24116_REG_QUALITY0 ) ;
2008-09-14 02:42:16 +04:00
2008-10-17 03:22:01 +04:00
if ( snr_reading > = 0xa0 /* 100% */ )
2008-09-14 02:42:16 +04:00
* snr = 0xffff ;
else
2008-10-17 03:22:01 +04:00
* snr = snr_tab [ ( snr_reading & 0xf0 ) > > 4 ] +
( snr_tab [ ( snr_reading & 0x0f ) ] > > 4 ) ;
2008-09-14 02:42:16 +04:00
dprintk ( " %s: raw / cooked = 0x%02x / 0x%04x \n " , __func__ ,
snr_reading , * snr ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
2008-10-07 04:20:21 +04:00
/* The reelbox patches show the value in the registers represents
* ESNO , from 0 - > 30 db ( values 0 - > 300 ) . We provide this value by
* default .
*/
2008-10-17 03:22:01 +04:00
static int cx24116_read_snr_esno ( struct dvb_frontend * fe , u16 * snr )
2008-10-07 04:20:21 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
dprintk ( " %s() \n " , __func__ ) ;
* snr = cx24116_readreg ( state , CX24116_REG_QUALITY8 ) < < 8 |
cx24116_readreg ( state , CX24116_REG_QUALITY0 ) ;
dprintk ( " %s: raw 0x%04x \n " , __func__ , * snr ) ;
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_read_snr ( struct dvb_frontend * fe , u16 * snr )
2008-10-07 04:20:21 +04:00
{
if ( esno_snr = = 1 )
return cx24116_read_snr_esno ( fe , snr ) ;
else
return cx24116_read_snr_pct ( fe , snr ) ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_read_ucblocks ( struct dvb_frontend * fe , u32 * ucblocks )
2008-09-04 08:14:43 +04:00
{
2008-09-14 02:42:16 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
2008-10-17 03:22:01 +04:00
* ucblocks = ( cx24116_readreg ( state , CX24116_REG_UCB8 ) < < 8 ) |
2008-09-14 02:42:16 +04:00
cx24116_readreg ( state , CX24116_REG_UCB0 ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
/* Overwrite the current tuning params, we are about to tune */
2008-10-17 03:22:01 +04:00
static void cx24116_clone_params ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
memcpy ( & state - > dcur , & state - > dnxt , sizeof ( state - > dcur ) ) ;
}
2008-09-14 02:42:16 +04:00
/* Wait for LNB */
2008-10-17 03:22:01 +04:00
static int cx24116_wait_for_lnb ( struct dvb_frontend * fe )
2008-09-14 02:42:16 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
int i ;
dprintk ( " %s() qstatus = 0x%02x \n " , __func__ ,
cx24116_readreg ( state , CX24116_REG_QSTATUS ) ) ;
/* Wait for up to 300 ms */
2008-10-17 03:22:01 +04:00
for ( i = 0 ; i < 30 ; i + + ) {
2008-09-14 02:42:16 +04:00
if ( cx24116_readreg ( state , CX24116_REG_QSTATUS ) & 0x20 )
return 0 ;
msleep ( 10 ) ;
}
dprintk ( " %s(): LNB not ready \n " , __func__ ) ;
return - ETIMEDOUT ; /* -EBUSY ? */
}
2008-10-17 03:22:01 +04:00
static int cx24116_set_tone ( struct dvb_frontend * fe ,
fe_sec_tone_mode_t tone )
2008-09-04 08:14:43 +04:00
{
struct cx24116_cmd cmd ;
int ret ;
dprintk ( " %s(%d) \n " , __func__ , tone ) ;
2008-10-17 03:22:01 +04:00
if ( ( tone ! = SEC_TONE_ON ) & & ( tone ! = SEC_TONE_OFF ) ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_ERR " %s: Invalid, tone=%d \n " , __func__ , tone ) ;
2008-09-04 08:14:43 +04:00
return - EINVAL ;
}
2008-09-14 02:42:16 +04:00
/* Wait for LNB ready */
ret = cx24116_wait_for_lnb ( fe ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
/* Min delay time after DiSEqC send */
msleep ( 15 ) ; /* XXX determine is FW does this, see send_diseqc/burst */
2008-09-04 08:14:43 +04:00
/* This is always done before the tone is set */
cmd . args [ 0x00 ] = CMD_SET_TONEPRE ;
cmd . args [ 0x01 ] = 0x00 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
2008-09-04 08:14:43 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
/* Now we set the tone */
cmd . args [ 0x00 ] = CMD_SET_TONE ;
cmd . args [ 0x01 ] = 0x00 ;
cmd . args [ 0x02 ] = 0x00 ;
switch ( tone ) {
case SEC_TONE_ON :
dprintk ( " %s: setting tone on \n " , __func__ ) ;
cmd . args [ 0x03 ] = 0x01 ;
break ;
case SEC_TONE_OFF :
2008-10-17 03:22:01 +04:00
dprintk ( " %s: setting tone off \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x03 ] = 0x00 ;
break ;
}
2008-10-17 03:22:01 +04:00
cmd . len = 0x04 ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Min delay time before DiSEqC send */
msleep ( 15 ) ; /* XXX determine is FW does this, see send_diseqc/burst */
2008-09-04 08:14:43 +04:00
return cx24116_cmd_execute ( fe , & cmd ) ;
}
/* Initialise DiSEqC */
2008-10-17 03:22:01 +04:00
static int cx24116_diseqc_init ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-14 02:42:16 +04:00
struct cx24116_cmd cmd ;
int ret ;
/* Firmware CMD 20: LNB/DiSEqC config */
cmd . args [ 0x00 ] = CMD_LNBCONFIG ;
cmd . args [ 0x01 ] = 0x00 ;
cmd . args [ 0x02 ] = 0x10 ;
cmd . args [ 0x03 ] = 0x00 ;
cmd . args [ 0x04 ] = 0x8f ;
cmd . args [ 0x05 ] = 0x28 ;
cmd . args [ 0x06 ] = ( toneburst = = CX24116_DISEQC_TONEOFF ) ? 0x00 : 0x01 ;
cmd . args [ 0x07 ] = 0x01 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x08 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
/* Prepare a DiSEqC command */
state - > dsec_cmd . args [ 0x00 ] = CMD_LNBSEND ;
/* DiSEqC burst */
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] = CX24116_DISEQC_MINI_A ;
/* Unknown */
state - > dsec_cmd . args [ CX24116_DISEQC_ARG2_2 ] = 0x02 ;
state - > dsec_cmd . args [ CX24116_DISEQC_ARG3_0 ] = 0x00 ;
2008-10-17 03:24:42 +04:00
/* Continuation flag? */
state - > dsec_cmd . args [ CX24116_DISEQC_ARG4_0 ] = 0x00 ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* DiSEqC message length */
state - > dsec_cmd . args [ CX24116_DISEQC_MSGLEN ] = 0x00 ;
/* Command length */
2008-10-17 03:22:01 +04:00
state - > dsec_cmd . len = CX24116_DISEQC_MSGOFS ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
/* Send DiSEqC message with derived burst (hack) || previous burst */
2008-10-17 03:22:01 +04:00
static int cx24116_send_diseqc_msg ( struct dvb_frontend * fe ,
struct dvb_diseqc_master_cmd * d )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
int i , ret ;
/* Dump DiSEqC message */
if ( debug ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " cx24116: %s( " , __func__ ) ;
2008-10-17 03:22:01 +04:00
for ( i = 0 ; i < d - > msg_len ; ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " 0x%02x " , d - > msg [ i ] ) ;
2008-10-17 03:22:01 +04:00
if ( + + i < d - > msg_len )
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " , " ) ;
2008-10-17 03:22:01 +04:00
}
2008-09-14 02:42:16 +04:00
printk ( " ) toneburst=%d \n " , toneburst ) ;
2008-09-04 08:14:43 +04:00
}
2008-09-14 02:42:16 +04:00
/* Validate length */
2008-10-17 03:22:01 +04:00
if ( d - > msg_len > ( CX24116_ARGLEN - CX24116_DISEQC_MSGOFS ) )
2008-09-04 08:14:43 +04:00
return - EINVAL ;
/* DiSEqC message */
for ( i = 0 ; i < d - > msg_len ; i + + )
2008-09-14 02:42:16 +04:00
state - > dsec_cmd . args [ CX24116_DISEQC_MSGOFS + i ] = d - > msg [ i ] ;
/* DiSEqC message length */
state - > dsec_cmd . args [ CX24116_DISEQC_MSGLEN ] = d - > msg_len ;
/* Command length */
2008-10-17 03:22:01 +04:00
state - > dsec_cmd . len = CX24116_DISEQC_MSGOFS +
state - > dsec_cmd . args [ CX24116_DISEQC_MSGLEN ] ;
2008-09-14 02:42:16 +04:00
/* DiSEqC toneburst */
2008-10-17 03:22:01 +04:00
if ( toneburst = = CX24116_DISEQC_MESGCACHE )
2008-09-14 02:42:16 +04:00
/* Message is cached */
return 0 ;
2008-10-17 03:22:01 +04:00
else if ( toneburst = = CX24116_DISEQC_TONEOFF )
2008-09-14 02:42:16 +04:00
/* Message is sent without burst */
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] = 0 ;
2008-10-17 03:22:01 +04:00
else if ( toneburst = = CX24116_DISEQC_TONECACHE ) {
2008-09-14 02:42:16 +04:00
/*
* Message is sent with derived else cached burst
*
* WRITE PORT GROUP COMMAND 38
*
* 0 / A / A : E0 10 38 F0 . . F3
* 1 / B / B : E0 10 38 F4 . . F7
* 2 / C / A : E0 10 38 F8 . . FB
* 3 / D / B : E0 10 38 FC . . FF
*
2008-09-14 17:45:58 +04:00
* databyte [ 3 ] = 8421 : 8421
2008-09-14 02:42:16 +04:00
* ABCD : WXYZ
* CLR : SET
*
* WX = PORT SELECT 0. .3 ( X = TONEBURST )
* Y = VOLTAGE ( 0 = 13 V , 1 = 18 V )
* Z = BAND ( 0 = LOW , 1 = HIGH ( 22 K ) )
*/
2008-10-17 03:22:01 +04:00
if ( d - > msg_len > = 4 & & d - > msg [ 2 ] = = 0x38 )
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] =
( ( d - > msg [ 3 ] & 4 ) > > 2 ) ;
if ( debug )
dprintk ( " %s burst=%d \n " , __func__ ,
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] ) ;
2008-09-14 02:42:16 +04:00
}
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Wait for LNB ready */
ret = cx24116_wait_for_lnb ( fe ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Wait for voltage/min repeat delay */
msleep ( 100 ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Command */
ret = cx24116_cmd_execute ( fe , & state - > dsec_cmd ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
/*
* Wait for send
2008-09-04 08:14:43 +04:00
*
* Eutelsat spec :
2008-09-14 02:42:16 +04:00
* > 15 ms delay + ( XXX determine if FW does this , see set_tone )
* 13.5 ms per byte +
* > 15 ms delay +
* 12.5 ms burst +
* > 15 ms delay ( XXX determine if FW does this , see set_tone )
2008-09-04 08:14:43 +04:00
*/
2008-10-17 03:22:01 +04:00
msleep ( ( state - > dsec_cmd . args [ CX24116_DISEQC_MSGLEN ] < < 4 ) +
( ( toneburst = = CX24116_DISEQC_TONEOFF ) ? 30 : 60 ) ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
return 0 ;
2008-09-04 08:14:43 +04:00
}
/* Send DiSEqC burst */
2008-10-17 03:22:01 +04:00
static int cx24116_diseqc_send_burst ( struct dvb_frontend * fe ,
fe_sec_mini_cmd_t burst )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
int ret ;
2008-10-17 03:22:01 +04:00
dprintk ( " %s(%d) toneburst=%d \n " , __func__ , burst , toneburst ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* DiSEqC burst */
2008-09-04 08:14:43 +04:00
if ( burst = = SEC_MINI_A )
2008-10-17 03:22:01 +04:00
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] =
CX24116_DISEQC_MINI_A ;
else if ( burst = = SEC_MINI_B )
state - > dsec_cmd . args [ CX24116_DISEQC_BURST ] =
CX24116_DISEQC_MINI_B ;
2008-09-04 08:14:43 +04:00
else
return - EINVAL ;
2008-09-14 02:42:16 +04:00
/* DiSEqC toneburst */
2008-10-17 03:22:01 +04:00
if ( toneburst ! = CX24116_DISEQC_MESGCACHE )
2008-09-14 02:42:16 +04:00
/* Burst is cached */
return 0 ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Burst is to be sent with cached message */
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Wait for LNB ready */
ret = cx24116_wait_for_lnb ( fe ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Wait for voltage/min repeat delay */
msleep ( 100 ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Command */
ret = cx24116_cmd_execute ( fe , & state - > dsec_cmd ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
/*
* Wait for send
*
* Eutelsat spec :
* > 15 ms delay + ( XXX determine if FW does this , see set_tone )
* 13.5 ms per byte +
* > 15 ms delay +
* 12.5 ms burst +
* > 15 ms delay ( XXX determine if FW does this , see set_tone )
*/
2008-10-17 03:22:01 +04:00
msleep ( ( state - > dsec_cmd . args [ CX24116_DISEQC_MSGLEN ] < < 4 ) + 60 ) ;
2008-09-14 02:42:16 +04:00
return 0 ;
2008-09-04 08:14:43 +04:00
}
2008-10-17 03:22:01 +04:00
static void cx24116_release ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
2008-10-17 03:22:01 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
dprintk ( " %s \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
kfree ( state ) ;
}
static struct dvb_frontend_ops cx24116_ops ;
2008-10-17 03:22:01 +04:00
struct dvb_frontend * cx24116_attach ( const struct cx24116_config * config ,
struct i2c_adapter * i2c )
2008-09-04 08:14:43 +04:00
{
2008-10-17 03:22:01 +04:00
struct cx24116_state * state = NULL ;
2008-09-04 08:14:43 +04:00
int ret ;
2008-10-17 03:22:01 +04:00
dprintk ( " %s \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
/* allocate memory for the internal state */
state = kmalloc ( sizeof ( struct cx24116_state ) , GFP_KERNEL ) ;
2008-10-17 03:24:42 +04:00
if ( state = = NULL )
2008-09-14 17:45:58 +04:00
goto error1 ;
2008-09-04 08:14:43 +04:00
/* setup the state */
memset ( state , 0 , sizeof ( struct cx24116_state ) ) ;
state - > config = config ;
state - > i2c = i2c ;
/* check if the demod is present */
2008-10-17 03:24:42 +04:00
ret = ( cx24116_readreg ( state , 0xFF ) < < 8 ) |
cx24116_readreg ( state , 0xFE ) ;
2008-09-04 08:14:43 +04:00
if ( ret ! = 0x0501 ) {
2008-10-17 03:24:42 +04:00
printk ( KERN_INFO " Invalid probe, probably not a CX24116 device \n " ) ;
2008-09-14 17:45:58 +04:00
goto error2 ;
2008-09-04 08:14:43 +04:00
}
/* create dvb_frontend */
2008-10-17 03:24:42 +04:00
memcpy ( & state - > frontend . ops , & cx24116_ops ,
sizeof ( struct dvb_frontend_ops ) ) ;
2008-09-04 08:14:43 +04:00
state - > frontend . demodulator_priv = state ;
return & state - > frontend ;
2008-09-14 17:45:58 +04:00
error2 : kfree ( state ) ;
2008-09-14 02:42:16 +04:00
error1 : return NULL ;
2008-09-04 08:14:43 +04:00
}
2008-10-17 03:22:01 +04:00
EXPORT_SYMBOL ( cx24116_attach ) ;
2008-09-14 02:42:16 +04:00
/*
* Initialise or wake up device
*
* Power config will reset and load initial firmware if required
*/
2008-10-17 03:22:01 +04:00
static int cx24116_initfe ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
2008-10-17 03:22:01 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-14 02:42:16 +04:00
struct cx24116_cmd cmd ;
int ret ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Power on */
cx24116_writereg ( state , 0xe0 , 0 ) ;
cx24116_writereg ( state , 0xe1 , 0 ) ;
cx24116_writereg ( state , 0xea , 0 ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Firmware CMD 36: Power config */
cmd . args [ 0x00 ] = CMD_TUNERSLEEP ;
cmd . args [ 0x01 ] = 0 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
return cx24116_diseqc_init ( fe ) ;
2008-09-04 08:14:43 +04:00
}
2008-09-14 02:42:16 +04:00
/*
* Put device to sleep
*/
2008-10-17 03:22:01 +04:00
static int cx24116_sleep ( struct dvb_frontend * fe )
2008-09-04 08:14:43 +04:00
{
2008-10-17 03:22:01 +04:00
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-14 02:42:16 +04:00
struct cx24116_cmd cmd ;
int ret ;
2008-10-17 03:22:01 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
/* Firmware CMD 36: Power config */
cmd . args [ 0x00 ] = CMD_TUNERSLEEP ;
cmd . args [ 0x01 ] = 1 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-14 02:42:16 +04:00
return ret ;
/* Power off (Shutdown clocks) */
cx24116_writereg ( state , 0xea , 0xff ) ;
cx24116_writereg ( state , 0xe1 , 1 ) ;
cx24116_writereg ( state , 0xe0 , 1 ) ;
return 0 ;
2008-09-04 08:14:43 +04:00
}
2008-10-17 03:22:01 +04:00
static int cx24116_set_property ( struct dvb_frontend * fe ,
struct dtv_property * tvp )
2008-09-04 08:14:43 +04:00
{
dprintk ( " %s(..) \n " , __func__ ) ;
return 0 ;
}
2008-10-17 03:22:01 +04:00
static int cx24116_get_property ( struct dvb_frontend * fe ,
struct dtv_property * tvp )
2008-09-04 08:14:43 +04:00
{
2008-09-12 08:37:37 +04:00
dprintk ( " %s(..) \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
return 0 ;
}
/* dvb-core told us to tune, the tv property cache will be complete,
* it ' s safe for is to pull values and use them for tuning purposes .
*/
2008-10-17 03:22:01 +04:00
static int cx24116_set_frontend ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * p )
2008-09-04 08:14:43 +04:00
{
struct cx24116_state * state = fe - > demodulator_priv ;
2008-09-11 17:19:27 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2008-09-04 08:14:43 +04:00
struct cx24116_cmd cmd ;
fe_status_t tunerstat ;
2008-10-03 18:47:46 +04:00
int i , status , ret , retune ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
dprintk ( " %s() \n " , __func__ ) ;
2008-09-04 08:14:43 +04:00
2008-10-17 03:22:01 +04:00
switch ( c - > delivery_system ) {
case SYS_DVBS :
dprintk ( " %s: DVB-S delivery system selected \n " , __func__ ) ;
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
/* Only QPSK is supported for DVB-S */
if ( c - > modulation ! = QPSK ) {
dprintk ( " %s: unsupported modulation selected (%d) \n " ,
__func__ , c - > modulation ) ;
return - EOPNOTSUPP ;
}
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
/* Pilot doesn't exist in DVB-S, turn bit off */
state - > dnxt . pilot_val = CX24116_PILOT_OFF ;
retune = 1 ;
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
/* DVB-S only supports 0.35 */
if ( c - > rolloff ! = ROLLOFF_35 ) {
dprintk ( " %s: unsupported rolloff selected (%d) \n " ,
__func__ , c - > rolloff ) ;
return - EOPNOTSUPP ;
}
state - > dnxt . rolloff_val = CX24116_ROLLOFF_035 ;
break ;
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
case SYS_DVBS2 :
dprintk ( " %s: DVB-S2 delivery system selected \n " , __func__ ) ;
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
/*
* NBC 8 PSK / QPSK with DVB - S is supported for DVB - S2 ,
* but not hardware auto detection
*/
if ( c - > modulation ! = PSK_8 & & c - > modulation ! = QPSK ) {
dprintk ( " %s: unsupported modulation selected (%d) \n " ,
__func__ , c - > modulation ) ;
return - EOPNOTSUPP ;
}
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
switch ( c - > pilot ) {
case PILOT_AUTO : /* Not supported but emulated */
2008-10-16 03:01:32 +04:00
state - > dnxt . pilot_val = ( c - > modulation = = QPSK )
? CX24116_PILOT_OFF : CX24116_PILOT_ON ;
retune = 2 ;
break ;
2008-10-17 03:22:01 +04:00
case PILOT_OFF :
state - > dnxt . pilot_val = CX24116_PILOT_OFF ;
break ;
case PILOT_ON :
state - > dnxt . pilot_val = CX24116_PILOT_ON ;
2008-09-14 02:42:16 +04:00
break ;
2008-10-17 03:22:01 +04:00
default :
dprintk ( " %s: unsupported pilot mode selected (%d) \n " ,
__func__ , c - > pilot ) ;
return - EOPNOTSUPP ;
}
2008-10-03 18:47:46 +04:00
2008-10-17 03:22:01 +04:00
switch ( c - > rolloff ) {
case ROLLOFF_20 :
state - > dnxt . rolloff_val = CX24116_ROLLOFF_020 ;
break ;
case ROLLOFF_25 :
state - > dnxt . rolloff_val = CX24116_ROLLOFF_025 ;
break ;
case ROLLOFF_35 :
state - > dnxt . rolloff_val = CX24116_ROLLOFF_035 ;
break ;
case ROLLOFF_AUTO : /* Rolloff must be explicit */
2008-09-14 02:42:16 +04:00
default :
2008-10-17 03:22:01 +04:00
dprintk ( " %s: unsupported rolloff selected (%d) \n " ,
__func__ , c - > rolloff ) ;
2008-09-14 02:42:16 +04:00
return - EOPNOTSUPP ;
2008-10-17 03:22:01 +04:00
}
break ;
default :
dprintk ( " %s: unsupported delivery system selected (%d) \n " ,
__func__ , c - > delivery_system ) ;
return - EOPNOTSUPP ;
2008-09-14 02:42:16 +04:00
}
2008-12-18 12:21:51 +03:00
state - > dnxt . delsys = c - > delivery_system ;
2008-10-03 18:47:46 +04:00
state - > dnxt . modulation = c - > modulation ;
state - > dnxt . frequency = c - > frequency ;
state - > dnxt . pilot = c - > pilot ;
state - > dnxt . rolloff = c - > rolloff ;
2008-09-14 02:42:16 +04:00
2008-10-17 03:22:01 +04:00
ret = cx24116_set_inversion ( state , c - > inversion ) ;
if ( ret ! = 0 )
2008-09-04 08:14:43 +04:00
return ret ;
2008-10-03 18:47:46 +04:00
/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
2008-12-18 12:21:51 +03:00
ret = cx24116_set_fec ( state , c - > delivery_system , c - > modulation , c - > fec_inner ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-04 08:14:43 +04:00
return ret ;
2008-10-17 03:22:01 +04:00
ret = cx24116_set_symbolrate ( state , c - > symbol_rate ) ;
if ( ret ! = 0 )
2008-09-04 08:14:43 +04:00
return ret ;
/* discard the 'current' tuning parameters and prepare to tune */
cx24116_clone_params ( fe ) ;
2008-12-18 12:21:51 +03:00
dprintk ( " %s: delsys = %d \n " , __func__ , state - > dcur . delsys ) ;
2008-10-03 18:47:46 +04:00
dprintk ( " %s: modulation = %d \n " , __func__ , state - > dcur . modulation ) ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s: frequency = %d \n " , __func__ , state - > dcur . frequency ) ;
2008-10-03 18:47:46 +04:00
dprintk ( " %s: pilot = %d (val = 0x%02x) \n " , __func__ ,
state - > dcur . pilot , state - > dcur . pilot_val ) ;
dprintk ( " %s: retune = %d \n " , __func__ , retune ) ;
dprintk ( " %s: rolloff = %d (val = 0x%02x) \n " , __func__ ,
state - > dcur . rolloff , state - > dcur . rolloff_val ) ;
2008-09-04 08:14:43 +04:00
dprintk ( " %s: symbol_rate = %d \n " , __func__ , state - > dcur . symbol_rate ) ;
dprintk ( " %s: FEC = %d (mask/val = 0x%02x/0x%02x) \n " , __func__ ,
state - > dcur . fec , state - > dcur . fec_mask , state - > dcur . fec_val ) ;
dprintk ( " %s: Inversion = %d (val = 0x%02x) \n " , __func__ ,
state - > dcur . inversion , state - > dcur . inversion_val ) ;
2008-09-14 02:42:16 +04:00
/* This is also done in advise/acquire on HVR4000 but not on LITE */
2008-09-04 08:14:43 +04:00
if ( state - > config - > set_ts_params )
state - > config - > set_ts_params ( fe , 0 ) ;
2008-09-14 02:42:16 +04:00
/* Set/Reset B/W */
cmd . args [ 0x00 ] = CMD_BANDWIDTH ;
cmd . args [ 0x01 ] = 0x01 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-09-09 20:22:29 +04:00
2008-09-04 08:14:43 +04:00
/* Prepare a tune request */
cmd . args [ 0x00 ] = CMD_TUNEREQUEST ;
/* Frequency */
cmd . args [ 0x01 ] = ( state - > dcur . frequency & 0xff0000 ) > > 16 ;
cmd . args [ 0x02 ] = ( state - > dcur . frequency & 0x00ff00 ) > > 8 ;
cmd . args [ 0x03 ] = ( state - > dcur . frequency & 0x0000ff ) ;
/* Symbol Rate */
cmd . args [ 0x04 ] = ( ( state - > dcur . symbol_rate / 1000 ) & 0xff00 ) > > 8 ;
cmd . args [ 0x05 ] = ( ( state - > dcur . symbol_rate / 1000 ) & 0x00ff ) ;
/* Automatic Inversion */
cmd . args [ 0x06 ] = state - > dcur . inversion_val ;
2008-10-03 18:47:46 +04:00
/* Modulation / FEC / Pilot */
cmd . args [ 0x07 ] = state - > dcur . fec_val | state - > dcur . pilot_val ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x08 ] = CX24116_SEARCH_RANGE_KHZ > > 8 ;
cmd . args [ 0x09 ] = CX24116_SEARCH_RANGE_KHZ & 0xff ;
cmd . args [ 0x0a ] = 0x00 ;
cmd . args [ 0x0b ] = 0x00 ;
2008-09-14 02:42:16 +04:00
cmd . args [ 0x0c ] = state - > dcur . rolloff_val ;
2008-09-04 08:14:43 +04:00
cmd . args [ 0x0d ] = state - > dcur . fec_mask ;
2008-09-09 20:22:29 +04:00
2008-09-14 02:42:16 +04:00
if ( state - > dcur . symbol_rate > 30000000 ) {
2008-09-09 20:22:29 +04:00
cmd . args [ 0x0e ] = 0x04 ;
cmd . args [ 0x0f ] = 0x00 ;
cmd . args [ 0x10 ] = 0x01 ;
cmd . args [ 0x11 ] = 0x77 ;
cmd . args [ 0x12 ] = 0x36 ;
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , CX24116_REG_CLKDIV , 0x44 ) ;
cx24116_writereg ( state , CX24116_REG_RATEDIV , 0x01 ) ;
2008-09-09 20:22:29 +04:00
} else {
cmd . args [ 0x0e ] = 0x06 ;
cmd . args [ 0x0f ] = 0x00 ;
cmd . args [ 0x10 ] = 0x00 ;
cmd . args [ 0x11 ] = 0xFA ;
cmd . args [ 0x12 ] = 0x24 ;
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , CX24116_REG_CLKDIV , 0x46 ) ;
cx24116_writereg ( state , CX24116_REG_RATEDIV , 0x00 ) ;
2008-09-09 20:22:29 +04:00
}
2008-10-17 03:22:01 +04:00
cmd . len = 0x13 ;
2008-09-04 08:14:43 +04:00
/* We need to support pilot and non-pilot tuning in the
* driver automatically . This is a workaround for because
* the demod does not support autodetect .
*/
do {
2008-09-14 02:42:16 +04:00
/* Reset status register */
2008-10-17 03:22:01 +04:00
status = cx24116_readreg ( state , CX24116_REG_SSTATUS )
& CX24116_SIGNAL_MASK ;
2008-09-14 02:42:16 +04:00
cx24116_writereg ( state , CX24116_REG_SSTATUS , status ) ;
2008-09-04 08:14:43 +04:00
/* Tune */
ret = cx24116_cmd_execute ( fe , & cmd ) ;
2008-10-17 03:22:01 +04:00
if ( ret ! = 0 )
2008-09-04 08:14:43 +04:00
break ;
2008-09-14 02:42:16 +04:00
/*
* Wait for up to 500 ms before retrying
*
* If we are able to tune then generally it occurs within 100 ms .
* If it takes longer , try a different toneburst setting .
*/
2008-10-17 03:22:01 +04:00
for ( i = 0 ; i < 50 ; i + + ) {
2008-09-14 02:42:16 +04:00
cx24116_read_status ( fe , & tunerstat ) ;
status = tunerstat & ( FE_HAS_SIGNAL | FE_HAS_SYNC ) ;
2008-10-17 03:22:01 +04:00
if ( status = = ( FE_HAS_SIGNAL | FE_HAS_SYNC ) ) {
dprintk ( " %s: Tuned \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
goto tuned ;
}
msleep ( 10 ) ;
2008-09-04 08:14:43 +04:00
}
2008-09-14 02:42:16 +04:00
2008-10-17 03:22:01 +04:00
dprintk ( " %s: Not tuned \n " , __func__ ) ;
2008-09-14 02:42:16 +04:00
/* Toggle pilot bit when in auto-pilot */
2008-10-17 03:22:01 +04:00
if ( state - > dcur . pilot = = PILOT_AUTO )
2008-10-03 18:47:46 +04:00
cmd . args [ 0x07 ] ^ = CX24116_PILOT_ON ;
2008-10-17 03:22:01 +04:00
} while ( - - retune ) ;
2008-09-04 08:14:43 +04:00
2008-09-14 02:42:16 +04:00
tuned : /* Set/Reset B/W */
cmd . args [ 0x00 ] = CMD_BANDWIDTH ;
cmd . args [ 0x01 ] = 0x00 ;
2008-10-17 03:22:01 +04:00
cmd . len = 0x02 ;
2008-09-14 02:42:16 +04:00
ret = cx24116_cmd_execute ( fe , & cmd ) ;
if ( ret ! = 0 )
return ret ;
2008-09-04 08:14:43 +04:00
return ret ;
}
static struct dvb_frontend_ops cx24116_ops = {
. info = {
. name = " Conexant CX24116/CX24118 " ,
. type = FE_QPSK ,
. frequency_min = 950000 ,
. frequency_max = 2150000 ,
. frequency_stepsize = 1011 , /* kHz for QPSK frontends */
. frequency_tolerance = 5000 ,
. symbol_rate_min = 1000000 ,
. symbol_rate_max = 45000000 ,
. caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_RECOVER
} ,
. release = cx24116_release ,
. init = cx24116_initfe ,
2008-09-14 02:42:16 +04:00
. sleep = cx24116_sleep ,
2008-09-04 08:14:43 +04:00
. read_status = cx24116_read_status ,
. read_ber = cx24116_read_ber ,
. read_signal_strength = cx24116_read_signal_strength ,
. read_snr = cx24116_read_snr ,
. read_ucblocks = cx24116_read_ucblocks ,
. set_tone = cx24116_set_tone ,
. set_voltage = cx24116_set_voltage ,
. diseqc_send_master_cmd = cx24116_send_diseqc_msg ,
. diseqc_send_burst = cx24116_diseqc_send_burst ,
. set_property = cx24116_set_property ,
2008-09-12 08:37:37 +04:00
. get_property = cx24116_get_property ,
2008-09-04 08:14:43 +04:00
. set_frontend = cx24116_set_frontend ,
} ;
MODULE_DESCRIPTION ( " DVB Frontend module for Conexant cx24116/cx24118 hardware " ) ;
MODULE_AUTHOR ( " Steven Toth " ) ;
MODULE_LICENSE ( " GPL " ) ;