2014-07-22 17:12:12 -03:00
/*
2015-11-13 20:54:56 -02:00
* Driver for Silicon Labs Si2161 DVB - T and Si2165 DVB - C / - T Demodulator
*
* Copyright ( C ) 2013 - 2014 Matthias Schwarzott < zzam @ gentoo . org >
*
* 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 .
*
* References :
* http : //www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
*/
2014-07-22 17:12:12 -03:00
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/firmware.h>
2016-07-26 04:09:08 -03:00
# include <linux/regmap.h>
2014-07-22 17:12:12 -03:00
# include "dvb_frontend.h"
# include "dvb_math.h"
# include "si2165_priv.h"
# include "si2165.h"
2015-11-13 20:54:56 -02:00
/*
* Hauppauge WinTV - HVR - 930 C - HD B130 / PCTV QuatroStick 521 e 1113 xx
* uses 16 MHz xtal
*
* Hauppauge WinTV - HVR - 930 C - HD B131 / PCTV QuatroStick 522 e 1114 xx
* uses 24 MHz clock provided by tuner
*/
2014-07-22 17:12:12 -03:00
struct si2165_state {
2016-07-26 04:09:02 -03:00
struct i2c_client * client ;
2016-07-26 04:09:08 -03:00
struct regmap * regmap ;
2014-07-22 17:12:12 -03:00
2015-11-19 18:03:53 -02:00
struct dvb_frontend fe ;
2014-07-22 17:12:12 -03:00
struct si2165_config config ;
2014-08-31 08:35:06 -03:00
u8 chip_revcode ;
2014-07-22 17:12:12 -03:00
u8 chip_type ;
/* calculated by xtal and div settings */
u32 fvco_hz ;
u32 sys_clk ;
u32 adc_clk ;
bool has_dvbc ;
bool has_dvbt ;
bool firmware_loaded ;
} ;
# define DEBUG_OTHER 0x01
# define DEBUG_I2C_WRITE 0x02
# define DEBUG_I2C_READ 0x04
# define DEBUG_REG_READ 0x08
# define DEBUG_REG_WRITE 0x10
# define DEBUG_FW_LOAD 0x20
static int debug = 0x00 ;
# define dprintk(args...) \
do { \
if ( debug & DEBUG_OTHER ) \
printk ( KERN_DEBUG " si2165: " args ) ; \
} while ( 0 )
# define deb_i2c_write(args...) \
do { \
if ( debug & DEBUG_I2C_WRITE ) \
printk ( KERN_DEBUG " si2165: i2c write: " args ) ; \
} while ( 0 )
# define deb_i2c_read(args...) \
do { \
if ( debug & DEBUG_I2C_READ ) \
printk ( KERN_DEBUG " si2165: i2c read: " args ) ; \
} while ( 0 )
# define deb_readreg(args...) \
do { \
if ( debug & DEBUG_REG_READ ) \
printk ( KERN_DEBUG " si2165: reg read: " args ) ; \
} while ( 0 )
# define deb_writereg(args...) \
do { \
if ( debug & DEBUG_REG_WRITE ) \
printk ( KERN_DEBUG " si2165: reg write: " args ) ; \
} while ( 0 )
# define deb_fw_load(args...) \
do { \
if ( debug & DEBUG_FW_LOAD ) \
printk ( KERN_DEBUG " si2165: fw load: " args ) ; \
} while ( 0 )
static int si2165_write ( struct si2165_state * state , const u16 reg ,
const u8 * src , const int count )
{
int ret ;
if ( debug & DEBUG_I2C_WRITE )
deb_i2c_write ( " reg: 0x%04x, data: %*ph \n " , reg , count , src ) ;
2016-07-26 04:09:08 -03:00
ret = regmap_bulk_write ( state - > regmap , reg , src , count ) ;
2014-07-22 17:12:12 -03:00
2016-07-26 04:09:08 -03:00
if ( ret )
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: ret == %d \n " , __func__ , ret ) ;
2014-07-22 17:12:12 -03:00
2016-07-26 04:09:08 -03:00
return ret ;
2014-07-22 17:12:12 -03:00
}
static int si2165_read ( struct si2165_state * state ,
const u16 reg , u8 * val , const int count )
{
2016-07-26 04:09:08 -03:00
int ret = regmap_bulk_read ( state - > regmap , reg , val , count ) ;
2014-07-22 17:12:12 -03:00
2016-07-26 04:09:08 -03:00
if ( ret ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: error (addr %02x reg %04x error (ret == %i) \n " ,
2014-07-22 17:12:12 -03:00
__func__ , state - > config . i2c_addr , reg , ret ) ;
2016-07-26 04:09:08 -03:00
return ret ;
2014-07-22 17:12:12 -03:00
}
if ( debug & DEBUG_I2C_READ )
deb_i2c_read ( " reg: 0x%04x, data: %*ph \n " , reg , count , val ) ;
return 0 ;
}
static int si2165_readreg8 ( struct si2165_state * state ,
const u16 reg , u8 * val )
{
2016-07-26 04:09:08 -03:00
unsigned int val_tmp ;
int ret = regmap_read ( state - > regmap , reg , & val_tmp ) ;
* val = ( u8 ) val_tmp ;
2014-07-22 17:12:12 -03:00
deb_readreg ( " R(0x%04x)=0x%02x \n " , reg , * val ) ;
return ret ;
}
static int si2165_readreg16 ( struct si2165_state * state ,
const u16 reg , u16 * val )
{
u8 buf [ 2 ] ;
int ret = si2165_read ( state , reg , buf , 2 ) ;
* val = buf [ 0 ] | buf [ 1 ] < < 8 ;
deb_readreg ( " R(0x%04x)=0x%04x \n " , reg , * val ) ;
return ret ;
}
static int si2165_writereg8 ( struct si2165_state * state , const u16 reg , u8 val )
{
2016-07-26 04:09:08 -03:00
return regmap_write ( state - > regmap , reg , val ) ;
2014-07-22 17:12:12 -03:00
}
static int si2165_writereg16 ( struct si2165_state * state , const u16 reg , u16 val )
{
u8 buf [ 2 ] = { val & 0xff , ( val > > 8 ) & 0xff } ;
return si2165_write ( state , reg , buf , 2 ) ;
}
static int si2165_writereg24 ( struct si2165_state * state , const u16 reg , u32 val )
{
u8 buf [ 3 ] = { val & 0xff , ( val > > 8 ) & 0xff , ( val > > 16 ) & 0xff } ;
return si2165_write ( state , reg , buf , 3 ) ;
}
static int si2165_writereg32 ( struct si2165_state * state , const u16 reg , u32 val )
{
u8 buf [ 4 ] = {
val & 0xff ,
( val > > 8 ) & 0xff ,
( val > > 16 ) & 0xff ,
( val > > 24 ) & 0xff
} ;
return si2165_write ( state , reg , buf , 4 ) ;
}
static int si2165_writereg_mask8 ( struct si2165_state * state , const u16 reg ,
u8 val , u8 mask )
{
if ( mask ! = 0xff ) {
2015-12-27 15:23:57 -02:00
u8 tmp ;
int ret = si2165_readreg8 ( state , reg , & tmp ) ;
2014-07-22 17:12:12 -03:00
if ( ret < 0 )
2015-12-27 15:23:57 -02:00
return ret ;
2014-07-22 17:12:12 -03:00
val & = mask ;
tmp & = ~ mask ;
val | = tmp ;
}
2015-12-27 15:23:57 -02:00
return si2165_writereg8 ( state , reg , val ) ;
2014-07-22 17:12:12 -03:00
}
2015-11-19 18:03:55 -02:00
# define REG16(reg, val) { (reg), (val) & 0xff }, { (reg)+1, (val)>>8 & 0xff }
struct si2165_reg_value_pair {
u16 reg ;
u8 val ;
} ;
static int si2165_write_reg_list ( struct si2165_state * state ,
const struct si2165_reg_value_pair * regs ,
int count )
{
int i ;
int ret ;
for ( i = 0 ; i < count ; i + + ) {
ret = si2165_writereg8 ( state , regs [ i ] . reg , regs [ i ] . val ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
2014-07-22 17:12:12 -03:00
static int si2165_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * s )
{
s - > min_delay_ms = 1000 ;
return 0 ;
}
static int si2165_init_pll ( struct si2165_state * state )
{
u32 ref_freq_Hz = state - > config . ref_freq_Hz ;
u8 divr = 1 ; /* 1..7 */
u8 divp = 1 ; /* only 1 or 4 */
u8 divn = 56 ; /* 1..63 */
u8 divm = 8 ;
u8 divl = 12 ;
u8 buf [ 4 ] ;
2015-11-13 20:54:56 -02:00
/*
* hardcoded values can be deleted if calculation is verified
* or it yields the same values as the windows driver
*/
2014-07-22 17:12:12 -03:00
switch ( ref_freq_Hz ) {
case 16000000u :
divn = 56 ;
break ;
case 24000000u :
divr = 2 ;
divp = 4 ;
divn = 19 ;
break ;
default :
/* ref_freq / divr must be between 4 and 16 MHz */
if ( ref_freq_Hz > 16000000u )
divr = 2 ;
2015-11-13 20:54:56 -02:00
/*
* now select divn and divp such that
* fvco is in 1624. .1824 MHz
*/
2014-07-22 17:12:12 -03:00
if ( 1624000000u * divr > ref_freq_Hz * 2u * 63u )
divp = 4 ;
/* is this already correct regarding rounding? */
divn = 1624000000u * divr / ( ref_freq_Hz * 2u * divp ) ;
break ;
}
/* adc_clk and sys_clk depend on xtal and pll settings */
state - > fvco_hz = ref_freq_Hz / divr
* 2u * divn * divp ;
state - > adc_clk = state - > fvco_hz / ( divm * 4u ) ;
state - > sys_clk = state - > fvco_hz / ( divl * 2u ) ;
/* write pll registers 0x00a0..0x00a3 at once */
buf [ 0 ] = divl ;
buf [ 1 ] = divm ;
buf [ 2 ] = ( divn & 0x3f ) | ( ( divp = = 1 ) ? 0x40 : 0x00 ) | 0x80 ;
buf [ 3 ] = divr ;
return si2165_write ( state , 0x00a0 , buf , 4 ) ;
}
static int si2165_adjust_pll_divl ( struct si2165_state * state , u8 divl )
{
state - > sys_clk = state - > fvco_hz / ( divl * 2u ) ;
return si2165_writereg8 ( state , 0x00a0 , divl ) ; /* pll_divl */
}
static u32 si2165_get_fe_clk ( struct si2165_state * state )
{
/* assume Oversampling mode Ovr4 is used */
return state - > adc_clk ;
}
2014-08-20 19:32:03 -03:00
static int si2165_wait_init_done ( struct si2165_state * state )
2014-07-22 17:12:12 -03:00
{
int ret = - EINVAL ;
u8 val = 0 ;
int i ;
for ( i = 0 ; i < 3 ; + + i ) {
si2165_readreg8 ( state , 0x0054 , & val ) ;
if ( val = = 0x01 )
return 0 ;
usleep_range ( 1000 , 50000 ) ;
}
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: init_done was not set \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
return ret ;
}
static int si2165_upload_firmware_block ( struct si2165_state * state ,
const u8 * data , u32 len , u32 * poffset , u32 block_count )
{
int ret ;
u8 buf_ctrl [ 4 ] = { 0x00 , 0x00 , 0x00 , 0xc0 } ;
u8 wordcount ;
u32 cur_block = 0 ;
u32 offset = poffset ? * poffset : 0 ;
if ( len < 4 )
return - EINVAL ;
if ( len % 4 ! = 0 )
return - EINVAL ;
2015-11-13 20:54:56 -02:00
deb_fw_load (
" si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x \n " ,
2014-07-22 17:12:12 -03:00
len , offset , block_count ) ;
while ( offset + 12 < = len & & cur_block < block_count ) {
2015-11-13 20:54:56 -02:00
deb_fw_load (
" si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x \n " ,
2014-07-22 17:12:12 -03:00
len , offset , cur_block , block_count ) ;
wordcount = data [ offset ] ;
if ( wordcount < 1 | | data [ offset + 1 ] | |
data [ offset + 2 ] | | data [ offset + 3 ] ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev ,
2014-07-22 17:12:12 -03:00
" %s: bad fw data[0..3] = %*ph \n " ,
KBUILD_MODNAME , 4 , data ) ;
return - EINVAL ;
}
if ( offset + 8 + wordcount * 4 > len ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev ,
2014-07-22 17:12:12 -03:00
" %s: len is too small for block len=%d, wordcount=%d \n " ,
KBUILD_MODNAME , len , wordcount ) ;
return - EINVAL ;
}
buf_ctrl [ 0 ] = wordcount - 1 ;
ret = si2165_write ( state , 0x0364 , buf_ctrl , 4 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_write ( state , 0x0368 , data + offset + 4 , 4 ) ;
if ( ret < 0 )
goto error ;
offset + = 8 ;
while ( wordcount > 0 ) {
ret = si2165_write ( state , 0x36c , data + offset , 4 ) ;
if ( ret < 0 )
goto error ;
wordcount - - ;
offset + = 4 ;
}
cur_block + + ;
}
2015-11-13 20:54:56 -02:00
deb_fw_load (
" si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x \n " ,
2014-07-22 17:12:12 -03:00
len , offset , cur_block , block_count ) ;
if ( poffset )
* poffset = offset ;
deb_fw_load ( " si2165_upload_firmware_block returned offset=0x%x \n " ,
offset ) ;
return 0 ;
error :
return ret ;
}
static int si2165_upload_firmware ( struct si2165_state * state )
{
/* int ret; */
u8 val [ 3 ] ;
u16 val16 ;
int ret ;
const struct firmware * fw = NULL ;
2014-08-31 08:35:06 -03:00
u8 * fw_file ;
2014-07-22 17:12:12 -03:00
const u8 * data ;
u32 len ;
u32 offset ;
u8 patch_version ;
u8 block_count ;
u16 crc_expected ;
2014-08-31 08:35:06 -03:00
switch ( state - > chip_revcode ) {
case 0x03 : /* revision D */
fw_file = SI2165_FIRMWARE_REV_D ;
break ;
default :
2016-07-26 04:09:07 -03:00
dev_info ( & state - > client - > dev , " %s: no firmware file for revision=%d \n " ,
2014-08-31 08:35:06 -03:00
KBUILD_MODNAME , state - > chip_revcode ) ;
return 0 ;
}
2014-07-22 17:12:12 -03:00
/* request the firmware, this will block and timeout */
2016-07-26 04:09:07 -03:00
ret = request_firmware ( & fw , fw_file , & state - > client - > dev ) ;
2014-07-22 17:12:12 -03:00
if ( ret ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev , " %s: firmware file '%s' not found \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME , fw_file ) ;
goto error ;
}
data = fw - > data ;
len = fw - > size ;
2016-07-26 04:09:07 -03:00
dev_info ( & state - > client - > dev , " %s: downloading firmware from file '%s' size=%d \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME , fw_file , len ) ;
if ( len % 4 ! = 0 ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev , " %s: firmware size is not multiple of 4 \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
ret = - EINVAL ;
goto error ;
}
/* check header (8 bytes) */
if ( len < 8 ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev , " %s: firmware header is missing \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
ret = - EINVAL ;
goto error ;
}
if ( data [ 0 ] ! = 1 | | data [ 1 ] ! = 0 ) {
2016-07-26 04:09:07 -03:00
dev_warn ( & state - > client - > dev , " %s: firmware file version is wrong \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
ret = - EINVAL ;
goto error ;
}
patch_version = data [ 2 ] ;
block_count = data [ 4 ] ;
crc_expected = data [ 7 ] < < 8 | data [ 6 ] ;
/* start uploading fw */
/* boot/wdog status */
ret = si2165_writereg8 ( state , 0x0341 , 0x00 ) ;
if ( ret < 0 )
goto error ;
/* reset */
ret = si2165_writereg8 ( state , 0x00c0 , 0x00 ) ;
if ( ret < 0 )
goto error ;
/* boot/wdog status */
ret = si2165_readreg8 ( state , 0x0341 , val ) ;
if ( ret < 0 )
goto error ;
/* enable reset on error */
ret = si2165_readreg8 ( state , 0x035c , val ) ;
if ( ret < 0 )
goto error ;
ret = si2165_readreg8 ( state , 0x035c , val ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x035c , 0x02 ) ;
if ( ret < 0 )
goto error ;
/* start right after the header */
offset = 8 ;
2016-07-26 04:09:07 -03:00
dev_info ( & state - > client - > dev , " %s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME , patch_version , block_count , crc_expected ) ;
ret = si2165_upload_firmware_block ( state , data , len , & offset , 1 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x0344 , patch_version ) ;
if ( ret < 0 )
goto error ;
/* reset crc */
ret = si2165_writereg8 ( state , 0x0379 , 0x01 ) ;
if ( ret )
2015-02-11 17:58:23 -03:00
goto error ;
2014-07-22 17:12:12 -03:00
ret = si2165_upload_firmware_block ( state , data , len ,
& offset , block_count ) ;
if ( ret < 0 ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev ,
2015-10-16 21:14:29 +09:00
" %s: firmware could not be uploaded \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
goto error ;
}
/* read crc */
ret = si2165_readreg16 ( state , 0x037a , & val16 ) ;
if ( ret )
goto error ;
if ( val16 ! = crc_expected ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev ,
2014-07-22 17:12:12 -03:00
" %s: firmware crc mismatch %04x != %04x \n " ,
KBUILD_MODNAME , val16 , crc_expected ) ;
ret = - EINVAL ;
goto error ;
}
ret = si2165_upload_firmware_block ( state , data , len , & offset , 5 ) ;
if ( ret )
goto error ;
if ( len ! = offset ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev ,
2015-10-16 21:14:29 +09:00
" %s: firmware len mismatch %04x != %04x \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME , len , offset ) ;
ret = - EINVAL ;
goto error ;
}
/* reset watchdog error register */
ret = si2165_writereg_mask8 ( state , 0x0341 , 0x02 , 0x02 ) ;
if ( ret < 0 )
goto error ;
/* enable reset on error */
ret = si2165_writereg_mask8 ( state , 0x035c , 0x01 , 0x01 ) ;
if ( ret < 0 )
goto error ;
2016-07-26 04:09:07 -03:00
dev_info ( & state - > client - > dev , " %s: fw load finished \n " , KBUILD_MODNAME ) ;
2014-07-22 17:12:12 -03:00
ret = 0 ;
state - > firmware_loaded = true ;
error :
if ( fw ) {
release_firmware ( fw ) ;
fw = NULL ;
}
return ret ;
}
static int si2165_init ( struct dvb_frontend * fe )
{
int ret = 0 ;
struct si2165_state * state = fe - > demodulator_priv ;
u8 val ;
u8 patch_version = 0x00 ;
dprintk ( " %s: called \n " , __func__ ) ;
/* powerup */
ret = si2165_writereg8 ( state , 0x0000 , state - > config . chip_mode ) ;
if ( ret < 0 )
goto error ;
/* dsp_clock_enable */
ret = si2165_writereg8 ( state , 0x0104 , 0x01 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_readreg8 ( state , 0x0000 , & val ) ; /* verify chip_mode */
if ( ret < 0 )
goto error ;
if ( val ! = state - > config . chip_mode ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: could not set chip_mode \n " ,
2014-07-22 17:12:12 -03:00
KBUILD_MODNAME ) ;
return - EINVAL ;
}
/* agc */
ret = si2165_writereg8 ( state , 0x018b , 0x00 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x0190 , 0x01 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x0170 , 0x00 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x0171 , 0x07 ) ;
if ( ret < 0 )
goto error ;
/* rssi pad */
ret = si2165_writereg8 ( state , 0x0646 , 0x00 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x0641 , 0x00 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_init_pll ( state ) ;
if ( ret < 0 )
goto error ;
/* enable chip_init */
ret = si2165_writereg8 ( state , 0x0050 , 0x01 ) ;
if ( ret < 0 )
goto error ;
/* set start_init */
ret = si2165_writereg8 ( state , 0x0096 , 0x01 ) ;
if ( ret < 0 )
goto error ;
ret = si2165_wait_init_done ( state ) ;
if ( ret < 0 )
goto error ;
/* disable chip_init */
ret = si2165_writereg8 ( state , 0x0050 , 0x00 ) ;
if ( ret < 0 )
goto error ;
/* ber_pkt */
2015-11-13 20:54:56 -02:00
ret = si2165_writereg16 ( state , 0x0470 , 0x7530 ) ;
2014-07-22 17:12:12 -03:00
if ( ret < 0 )
goto error ;
ret = si2165_readreg8 ( state , 0x0344 , & patch_version ) ;
if ( ret < 0 )
goto error ;
ret = si2165_writereg8 ( state , 0x00cb , 0x00 ) ;
if ( ret < 0 )
goto error ;
/* dsp_addr_jump */
ret = si2165_writereg32 ( state , 0x0348 , 0xf4000000 ) ;
if ( ret < 0 )
goto error ;
/* boot/wdog status */
ret = si2165_readreg8 ( state , 0x0341 , & val ) ;
if ( ret < 0 )
goto error ;
if ( patch_version = = 0x00 ) {
ret = si2165_upload_firmware ( state ) ;
if ( ret < 0 )
goto error ;
}
2015-11-19 18:03:57 -02:00
/* ts output config */
ret = si2165_writereg8 ( state , 0x04e4 , 0x20 ) ;
if ( ret < 0 )
return ret ;
ret = si2165_writereg16 ( state , 0x04ef , 0x00fe ) ;
if ( ret < 0 )
return ret ;
ret = si2165_writereg24 ( state , 0x04f4 , 0x555555 ) ;
if ( ret < 0 )
return ret ;
ret = si2165_writereg8 ( state , 0x04e5 , 0x01 ) ;
if ( ret < 0 )
return ret ;
2014-07-22 17:12:12 -03:00
return 0 ;
error :
return ret ;
}
static int si2165_sleep ( struct dvb_frontend * fe )
{
int ret ;
struct si2165_state * state = fe - > demodulator_priv ;
/* dsp clock disable */
ret = si2165_writereg8 ( state , 0x0104 , 0x00 ) ;
if ( ret < 0 )
return ret ;
/* chip mode */
ret = si2165_writereg8 ( state , 0x0000 , SI2165_MODE_OFF ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2015-06-07 14:53:52 -03:00
static int si2165_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2014-07-22 17:12:12 -03:00
{
int ret ;
u8 fec_lock = 0 ;
struct si2165_state * state = fe - > demodulator_priv ;
if ( ! state - > has_dvbt )
return - EINVAL ;
/* check fec_lock */
ret = si2165_readreg8 ( state , 0x4e0 , & fec_lock ) ;
if ( ret < 0 )
return ret ;
* status = 0 ;
if ( fec_lock & 0x01 ) {
* status | = FE_HAS_SIGNAL ;
* status | = FE_HAS_CARRIER ;
* status | = FE_HAS_VITERBI ;
* status | = FE_HAS_SYNC ;
* status | = FE_HAS_LOCK ;
}
return 0 ;
}
static int si2165_set_oversamp ( struct si2165_state * state , u32 dvb_rate )
{
u64 oversamp ;
u32 reg_value ;
2016-07-26 03:53:40 -03:00
if ( ! dvb_rate )
return - EINVAL ;
2014-07-22 17:12:12 -03:00
oversamp = si2165_get_fe_clk ( state ) ;
oversamp < < = 23 ;
do_div ( oversamp , dvb_rate ) ;
reg_value = oversamp & 0x3fffffff ;
2015-11-19 18:04:01 -02:00
dprintk ( " %s: Write oversamp=%#x \n " , __func__ , reg_value ) ;
2014-07-22 17:12:12 -03:00
return si2165_writereg32 ( state , 0x00e4 , reg_value ) ;
}
2015-11-19 18:03:58 -02:00
static int si2165_set_if_freq_shift ( struct si2165_state * state )
2014-07-22 17:12:12 -03:00
{
2015-11-19 18:03:58 -02:00
struct dvb_frontend * fe = & state - > fe ;
2014-07-22 17:12:12 -03:00
u64 if_freq_shift ;
s32 reg_value = 0 ;
u32 fe_clk = si2165_get_fe_clk ( state ) ;
2015-11-19 18:03:58 -02:00
u32 IF = 0 ;
2014-07-22 17:12:12 -03:00
2015-11-19 18:03:58 -02:00
if ( ! fe - > ops . tuner_ops . get_if_frequency ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev ,
2015-11-19 18:03:58 -02:00
" %s: Error: get_if_frequency() not defined at tuner. Can't work without it! \n " ,
KBUILD_MODNAME ) ;
return - EINVAL ;
}
2016-07-26 03:53:40 -03:00
if ( ! fe_clk )
return - EINVAL ;
2015-11-19 18:03:58 -02:00
fe - > ops . tuner_ops . get_if_frequency ( fe , & IF ) ;
2014-07-22 17:12:12 -03:00
if_freq_shift = IF ;
if_freq_shift < < = 29 ;
do_div ( if_freq_shift , fe_clk ) ;
reg_value = ( s32 ) if_freq_shift ;
if ( state - > config . inversion )
reg_value = - reg_value ;
reg_value = reg_value & 0x1fffffff ;
/* if_freq_shift, usbdump contained 0x023ee08f; */
return si2165_writereg32 ( state , 0x00e8 , reg_value ) ;
}
2015-11-19 18:04:00 -02:00
static const struct si2165_reg_value_pair dvbt_regs [ ] = {
/* standard = DVB-T */
{ 0x00ec , 0x01 } ,
{ 0x08f8 , 0x00 } ,
/* impulsive_noise_remover */
{ 0x031c , 0x01 } ,
{ 0x00cb , 0x00 } ,
/* agc2 */
{ 0x016e , 0x41 } ,
{ 0x016c , 0x0e } ,
{ 0x016d , 0x10 } ,
/* agc */
{ 0x015b , 0x03 } ,
{ 0x0150 , 0x78 } ,
/* agc */
{ 0x01a0 , 0x78 } ,
{ 0x01c8 , 0x68 } ,
/* freq_sync_range */
REG16 ( 0x030c , 0x0064 ) ,
/* gp_reg0 */
{ 0x0387 , 0x00 }
} ;
2015-11-19 18:04:01 -02:00
static int si2165_set_frontend_dvbt ( struct dvb_frontend * fe )
2014-07-22 17:12:12 -03:00
{
int ret ;
struct dtv_frontend_properties * p = & fe - > dtv_property_cache ;
struct si2165_state * state = fe - > demodulator_priv ;
u32 dvb_rate = 0 ;
u16 bw10k ;
2015-12-03 18:12:50 -02:00
u32 bw_hz = p - > bandwidth_hz ;
2014-07-22 17:12:12 -03:00
dprintk ( " %s: called \n " , __func__ ) ;
if ( ! state - > has_dvbt )
return - EINVAL ;
2015-12-03 18:12:50 -02:00
/* no bandwidth auto-detection */
if ( bw_hz = = 0 )
return - EINVAL ;
dvb_rate = bw_hz * 8 / 7 ;
bw10k = bw_hz / 10000 ;
2014-07-22 17:12:12 -03:00
ret = si2165_adjust_pll_divl ( state , 12 ) ;
if ( ret < 0 )
return ret ;
/* bandwidth in 10KHz steps */
ret = si2165_writereg16 ( state , 0x0308 , bw10k ) ;
if ( ret < 0 )
return ret ;
ret = si2165_set_oversamp ( state , dvb_rate ) ;
if ( ret < 0 )
return ret ;
2015-11-19 18:04:00 -02:00
ret = si2165_write_reg_list ( state , dvbt_regs , ARRAY_SIZE ( dvbt_regs ) ) ;
2014-07-22 17:12:12 -03:00
if ( ret < 0 )
return ret ;
2015-11-19 18:04:00 -02:00
2015-11-19 18:04:01 -02:00
return 0 ;
}
2015-11-19 18:04:02 -02:00
static const struct si2165_reg_value_pair dvbc_regs [ ] = {
/* standard = DVB-C */
{ 0x00ec , 0x05 } ,
{ 0x08f8 , 0x00 } ,
/* agc2 */
{ 0x016e , 0x50 } ,
{ 0x016c , 0x0e } ,
{ 0x016d , 0x10 } ,
/* agc */
{ 0x015b , 0x03 } ,
{ 0x0150 , 0x68 } ,
/* agc */
{ 0x01a0 , 0x68 } ,
{ 0x01c8 , 0x50 } ,
{ 0x0278 , 0x0d } ,
{ 0x023a , 0x05 } ,
{ 0x0261 , 0x09 } ,
REG16 ( 0x0350 , 0x3e80 ) ,
{ 0x02f4 , 0x00 } ,
{ 0x00cb , 0x01 } ,
REG16 ( 0x024c , 0x0000 ) ,
REG16 ( 0x027c , 0x0000 ) ,
{ 0x0232 , 0x03 } ,
{ 0x02f4 , 0x0b } ,
{ 0x018b , 0x00 } ,
} ;
static int si2165_set_frontend_dvbc ( struct dvb_frontend * fe )
{
struct si2165_state * state = fe - > demodulator_priv ;
int ret ;
struct dtv_frontend_properties * p = & fe - > dtv_property_cache ;
const u32 dvb_rate = p - > symbol_rate ;
const u32 bw_hz = p - > bandwidth_hz ;
if ( ! state - > has_dvbc )
return - EINVAL ;
if ( dvb_rate = = 0 )
return - EINVAL ;
ret = si2165_adjust_pll_divl ( state , 14 ) ;
if ( ret < 0 )
return ret ;
/* Oversampling */
ret = si2165_set_oversamp ( state , dvb_rate ) ;
if ( ret < 0 )
return ret ;
ret = si2165_writereg32 ( state , 0x00c4 , bw_hz ) ;
if ( ret < 0 )
return ret ;
ret = si2165_write_reg_list ( state , dvbc_regs , ARRAY_SIZE ( dvbc_regs ) ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2015-11-19 18:04:01 -02:00
static const struct si2165_reg_value_pair agc_rewrite [ ] = {
{ 0x012a , 0x46 } ,
{ 0x012c , 0x00 } ,
{ 0x012e , 0x0a } ,
{ 0x012f , 0xff } ,
{ 0x0123 , 0x70 }
} ;
static int si2165_set_frontend ( struct dvb_frontend * fe )
{
struct si2165_state * state = fe - > demodulator_priv ;
struct dtv_frontend_properties * p = & fe - > dtv_property_cache ;
u32 delsys = p - > delivery_system ;
int ret ;
u8 val [ 3 ] ;
/* initial setting of if freq shift */
ret = si2165_set_if_freq_shift ( state ) ;
if ( ret < 0 )
return ret ;
switch ( delsys ) {
case SYS_DVBT :
ret = si2165_set_frontend_dvbt ( fe ) ;
if ( ret < 0 )
return ret ;
break ;
2015-11-19 18:04:02 -02:00
case SYS_DVBC_ANNEX_A :
ret = si2165_set_frontend_dvbc ( fe ) ;
if ( ret < 0 )
return ret ;
break ;
2015-11-19 18:04:01 -02:00
default :
return - EINVAL ;
}
2014-07-22 17:12:12 -03:00
/* dsp_addr_jump */
ret = si2165_writereg32 ( state , 0x0348 , 0xf4000000 ) ;
if ( ret < 0 )
return ret ;
if ( fe - > ops . tuner_ops . set_params )
fe - > ops . tuner_ops . set_params ( fe ) ;
/* recalc if_freq_shift if IF might has changed */
2015-11-19 18:03:58 -02:00
ret = si2165_set_if_freq_shift ( state ) ;
2014-07-22 17:12:12 -03:00
if ( ret < 0 )
return ret ;
/* boot/wdog status */
ret = si2165_readreg8 ( state , 0x0341 , val ) ;
if ( ret < 0 )
return ret ;
ret = si2165_writereg8 ( state , 0x0341 , 0x00 ) ;
if ( ret < 0 )
return ret ;
2015-11-19 18:04:01 -02:00
2014-07-22 17:12:12 -03:00
/* reset all */
ret = si2165_writereg8 ( state , 0x00c0 , 0x00 ) ;
if ( ret < 0 )
return ret ;
/* gp_reg0 */
ret = si2165_writereg32 ( state , 0x0384 , 0x00000000 ) ;
if ( ret < 0 )
return ret ;
2015-11-19 18:03:56 -02:00
/* write adc values after each reset*/
ret = si2165_write_reg_list ( state , agc_rewrite ,
ARRAY_SIZE ( agc_rewrite ) ) ;
if ( ret < 0 )
return ret ;
2014-07-22 17:12:12 -03:00
/* start_synchro */
ret = si2165_writereg8 ( state , 0x02e0 , 0x01 ) ;
if ( ret < 0 )
return ret ;
/* boot/wdog status */
ret = si2165_readreg8 ( state , 0x0341 , val ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2016-08-09 18:32:21 -03:00
static const struct dvb_frontend_ops si2165_ops = {
2014-07-22 17:12:12 -03:00
. info = {
2014-08-31 08:35:07 -03:00
. name = " Silicon Labs " ,
2015-11-19 18:04:02 -02:00
/* For DVB-C */
. symbol_rate_min = 1000000 ,
. symbol_rate_max = 7200000 ,
/* For DVB-T */
. frequency_stepsize = 166667 ,
. caps = FE_CAN_FEC_1_2 |
2014-07-22 17:12:12 -03:00
FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 |
FE_CAN_FEC_7_8 |
FE_CAN_FEC_AUTO |
FE_CAN_QPSK |
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_QAM_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_RECOVER
} ,
. get_tune_settings = si2165_get_tune_settings ,
. init = si2165_init ,
. sleep = si2165_sleep ,
2015-11-19 18:03:54 -02:00
. set_frontend = si2165_set_frontend ,
2014-07-22 17:12:12 -03:00
. read_status = si2165_read_status ,
} ;
2016-07-26 04:09:02 -03:00
static int si2165_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct si2165_state * state = NULL ;
struct si2165_platform_data * pdata = client - > dev . platform_data ;
int n ;
int ret = 0 ;
u8 val ;
char rev_char ;
const char * chip_name ;
2016-07-26 04:09:08 -03:00
static const struct regmap_config regmap_config = {
. reg_bits = 16 ,
. val_bits = 8 ,
. max_register = 0x08ff ,
} ;
2016-07-26 04:09:02 -03:00
/* allocate memory for the internal state */
state = kzalloc ( sizeof ( struct si2165_state ) , GFP_KERNEL ) ;
if ( state = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
2016-07-26 04:09:08 -03:00
/* create regmap */
state - > regmap = devm_regmap_init_i2c ( client , & regmap_config ) ;
if ( IS_ERR ( state - > regmap ) ) {
ret = PTR_ERR ( state - > regmap ) ;
goto error ;
}
2016-07-26 04:09:02 -03:00
/* setup the state */
state - > client = client ;
state - > config . i2c_addr = client - > addr ;
state - > config . chip_mode = pdata - > chip_mode ;
state - > config . ref_freq_Hz = pdata - > ref_freq_Hz ;
state - > config . inversion = pdata - > inversion ;
if ( state - > config . ref_freq_Hz < 4000000
| | state - > config . ref_freq_Hz > 27000000 ) {
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: ref_freq of %d Hz not supported by this driver \n " ,
2016-07-26 04:09:02 -03:00
KBUILD_MODNAME , state - > config . ref_freq_Hz ) ;
ret = - EINVAL ;
goto error ;
}
/* create dvb_frontend */
memcpy ( & state - > fe . ops , & si2165_ops ,
sizeof ( struct dvb_frontend_ops ) ) ;
state - > fe . ops . release = NULL ;
state - > fe . demodulator_priv = state ;
i2c_set_clientdata ( client , state ) ;
/* powerup */
ret = si2165_writereg8 ( state , 0x0000 , state - > config . chip_mode ) ;
if ( ret < 0 )
goto nodev_error ;
ret = si2165_readreg8 ( state , 0x0000 , & val ) ;
if ( ret < 0 )
goto nodev_error ;
if ( val ! = state - > config . chip_mode )
goto nodev_error ;
ret = si2165_readreg8 ( state , 0x0023 , & state - > chip_revcode ) ;
if ( ret < 0 )
goto nodev_error ;
ret = si2165_readreg8 ( state , 0x0118 , & state - > chip_type ) ;
if ( ret < 0 )
goto nodev_error ;
/* powerdown */
ret = si2165_writereg8 ( state , 0x0000 , SI2165_MODE_OFF ) ;
if ( ret < 0 )
goto nodev_error ;
if ( state - > chip_revcode < 26 )
rev_char = ' A ' + state - > chip_revcode ;
else
rev_char = ' ? ' ;
switch ( state - > chip_type ) {
case 0x06 :
chip_name = " Si2161 " ;
state - > has_dvbt = true ;
break ;
case 0x07 :
chip_name = " Si2165 " ;
state - > has_dvbt = true ;
state - > has_dvbc = true ;
break ;
default :
2016-07-26 04:09:07 -03:00
dev_err ( & state - > client - > dev , " %s: Unsupported Silicon Labs chip (type %d, rev %d) \n " ,
2016-07-26 04:09:02 -03:00
KBUILD_MODNAME , state - > chip_type , state - > chip_revcode ) ;
goto nodev_error ;
}
2016-07-26 04:09:07 -03:00
dev_info ( & state - > client - > dev ,
2016-07-26 04:09:02 -03:00
" %s: Detected Silicon Labs %s-%c (type %d, rev %d) \n " ,
KBUILD_MODNAME , chip_name , rev_char , state - > chip_type ,
state - > chip_revcode ) ;
strlcat ( state - > fe . ops . info . name , chip_name ,
sizeof ( state - > fe . ops . info . name ) ) ;
n = 0 ;
if ( state - > has_dvbt ) {
state - > fe . ops . delsys [ n + + ] = SYS_DVBT ;
strlcat ( state - > fe . ops . info . name , " DVB-T " ,
sizeof ( state - > fe . ops . info . name ) ) ;
}
if ( state - > has_dvbc ) {
state - > fe . ops . delsys [ n + + ] = SYS_DVBC_ANNEX_A ;
strlcat ( state - > fe . ops . info . name , " DVB-C " ,
sizeof ( state - > fe . ops . info . name ) ) ;
}
/* return fe pointer */
* pdata - > fe = & state - > fe ;
return 0 ;
nodev_error :
ret = - ENODEV ;
error :
kfree ( state ) ;
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
return ret ;
}
static int si2165_remove ( struct i2c_client * client )
{
struct si2165_state * state = i2c_get_clientdata ( client ) ;
dev_dbg ( & client - > dev , " \n " ) ;
kfree ( state ) ;
return 0 ;
}
static const struct i2c_device_id si2165_id_table [ ] = {
{ " si2165 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , si2165_id_table ) ;
static struct i2c_driver si2165_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " si2165 " ,
} ,
. probe = si2165_probe ,
. remove = si2165_remove ,
. id_table = si2165_id_table ,
} ;
module_i2c_driver ( si2165_driver ) ;
2014-07-22 17:12:12 -03:00
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Turn on/off frontend debugging (default:off). " ) ;
MODULE_DESCRIPTION ( " Silicon Labs Si2165 DVB-C/-T Demodulator driver " ) ;
MODULE_AUTHOR ( " Matthias Schwarzott <zzam@gentoo.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-08-31 08:35:06 -03:00
MODULE_FIRMWARE ( SI2165_FIRMWARE_REV_D ) ;