// SPDX-License-Identifier: GPL-2.0-or-later
/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
*
* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <asm /div64.h>
#include <media/dvb_frontend.h>
#include "si21xx.h"
#define REVISION_REG 0 x00
#define SYSTEM_MODE_REG 0 x01
#define TS_CTRL_REG_1 0 x02
#define TS_CTRL_REG_2 0 x03
#define PIN_CTRL_REG_1 0 x04
#define PIN_CTRL_REG_2 0 x05
#define LOCK_STATUS_REG_1 0 x0f
#define LOCK_STATUS_REG_2 0 x10
#define ACQ_STATUS_REG 0 x11
#define ACQ_CTRL_REG_1 0 x13
#define ACQ_CTRL_REG_2 0 x14
#define PLL_DIVISOR_REG 0 x15
#define COARSE_TUNE_REG 0 x16
#define FINE_TUNE_REG_L 0 x17
#define FINE_TUNE_REG_H 0 x18
#define ANALOG_AGC_POWER_LEVEL_REG 0 x28
#define CFO_ESTIMATOR_CTRL_REG_1 0 x29
#define CFO_ESTIMATOR_CTRL_REG_2 0 x2a
#define CFO_ESTIMATOR_CTRL_REG_3 0 x2b
#define SYM_RATE_ESTIMATE_REG_L 0 x31
#define SYM_RATE_ESTIMATE_REG_M 0 x32
#define SYM_RATE_ESTIMATE_REG_H 0 x33
#define CFO_ESTIMATOR_OFFSET_REG_L 0 x36
#define CFO_ESTIMATOR_OFFSET_REG_H 0 x37
#define CFO_ERROR_REG_L 0 x38
#define CFO_ERROR_REG_H 0 x39
#define SYM_RATE_ESTIMATOR_CTRL_REG 0 x3a
#define SYM_RATE_REG_L 0 x3f
#define SYM_RATE_REG_M 0 x40
#define SYM_RATE_REG_H 0 x41
#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0 x42
#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0 x43
#define C_N_ESTIMATOR_CTRL_REG 0 x7c
#define C_N_ESTIMATOR_THRSHLD_REG 0 x7d
#define C_N_ESTIMATOR_LEVEL_REG_L 0 x7e
#define C_N_ESTIMATOR_LEVEL_REG_H 0 x7f
#define BLIND_SCAN_CTRL_REG 0 x80
#define LSA_CTRL_REG_1 0 x8D
#define SPCTRM_TILT_CORR_THRSHLD_REG 0 x8f
#define ONE_DB_BNDWDTH_THRSHLD_REG 0 x90
#define TWO_DB_BNDWDTH_THRSHLD_REG 0 x91
#define THREE_DB_BNDWDTH_THRSHLD_REG 0 x92
#define INBAND_POWER_THRSHLD_REG 0 x93
#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0 x94
#define VIT_SRCH_CTRL_REG_1 0 xa0
#define VIT_SRCH_CTRL_REG_2 0 xa1
#define VIT_SRCH_CTRL_REG_3 0 xa2
#define VIT_SRCH_STATUS_REG 0 xa3
#define VITERBI_BER_COUNT_REG_L 0 xab
#define REED_SOLOMON_CTRL_REG 0 xb0
#define REED_SOLOMON_ERROR_COUNT_REG_L 0 xb1
#define PRBS_CTRL_REG 0 xb5
#define LNB_CTRL_REG_1 0 xc0
#define LNB_CTRL_REG_2 0 xc1
#define LNB_CTRL_REG_3 0 xc2
#define LNB_CTRL_REG_4 0 xc3
#define LNB_CTRL_STATUS_REG 0 xc4
#define LNB_FIFO_REGS_0 0 xc5
#define LNB_FIFO_REGS_1 0 xc6
#define LNB_FIFO_REGS_2 0 xc7
#define LNB_FIFO_REGS_3 0 xc8
#define LNB_FIFO_REGS_4 0 xc9
#define LNB_FIFO_REGS_5 0 xca
#define LNB_SUPPLY_CTRL_REG_1 0 xcb
#define LNB_SUPPLY_CTRL_REG_2 0 xcc
#define LNB_SUPPLY_CTRL_REG_3 0 xcd
#define LNB_SUPPLY_CTRL_REG_4 0 xce
#define LNB_SUPPLY_STATUS_REG 0 xcf
#define FAIL -1
#define PASS 0
#define ALLOWABLE_FS_COUNT 10
#define STATUS_BER 0
#define STATUS_UCBLOCKS 1
static int debug;
#define dprintk(args...) \
do { \
if (debug) \
printk(KERN_DEBUG "si21xx: " args); \
} while (0 )
enum {
ACTIVE_HIGH,
ACTIVE_LOW
};
enum {
BYTE_WIDE,
BIT_WIDE
};
enum {
CLK_GAPPED_MODE,
CLK_CONTINUOUS_MODE
};
enum {
RISING_EDGE,
FALLING_EDGE
};
enum {
MSB_FIRST,
LSB_FIRST
};
enum {
SERIAL,
PARALLEL
};
struct si21xx_state {
struct i2c_adapter *i2c;
const struct si21xx_config *config;
struct dvb_frontend frontend;
u8 initialised:1 ;
int errmode;
int fs; /*Sampling rate of the ADC in MHz*/
};
/* register default initialization */
static u8 serit_sp1511lhb_inittab[] = {
0 x01, 0 x28, /* set i2c_inc_disable */
0 x20, 0 x03,
0 x27, 0 x20,
0 xe0, 0 x45,
0 xe1, 0 x08,
0 xfe, 0 x01,
0 x01, 0 x28,
0 x89, 0 x09,
0 x04, 0 x80,
0 x05, 0 x01,
0 x06, 0 x00,
0 x20, 0 x03,
0 x24, 0 x88,
0 x29, 0 x09,
0 x2a, 0 x0f,
0 x2c, 0 x10,
0 x2d, 0 x19,
0 x2e, 0 x08,
0 x2f, 0 x10,
0 x30, 0 x19,
0 x34, 0 x20,
0 x35, 0 x03,
0 x45, 0 x02,
0 x46, 0 x45,
0 x47, 0 xd0,
0 x48, 0 x00,
0 x49, 0 x40,
0 x4a, 0 x03,
0 x4c, 0 xfd,
0 x4f, 0 x2e,
0 x50, 0 x2e,
0 x51, 0 x10,
0 x52, 0 x10,
0 x56, 0 x92,
0 x59, 0 x00,
0 x5a, 0 x2d,
0 x5b, 0 x33,
0 x5c, 0 x1f,
0 x5f, 0 x76,
0 x62, 0 xc0,
0 x63, 0 xc0,
0 x64, 0 xf3,
0 x65, 0 xf3,
0 x79, 0 x40,
0 x6a, 0 x40,
0 x6b, 0 x0a,
0 x6c, 0 x80,
0 x6d, 0 x27,
0 x71, 0 x06,
0 x75, 0 x60,
0 x78, 0 x00,
0 x79, 0 xb5,
0 x7c, 0 x05,
0 x7d, 0 x1a,
0 x87, 0 x55,
0 x88, 0 x72,
0 x8f, 0 x08,
0 x90, 0 xe0,
0 x94, 0 x40,
0 xa0, 0 x3f,
0 xa1, 0 xc0,
0 xa4, 0 xcc,
0 xa5, 0 x66,
0 xa6, 0 x66,
0 xa7, 0 x7b,
0 xa8, 0 x7b,
0 xa9, 0 x7b,
0 xaa, 0 x9a,
0 xed, 0 x04,
0 xad, 0 x00,
0 xae, 0 x03,
0 xcc, 0 xab,
0 x01, 0 x08,
0 xff, 0 xff
};
/* low level read/writes */
static int si21_writeregs(struct si21xx_state *state, u8 reg1,
u8 *data, int len)
{
int ret;
u8 buf[60 ];/* = { reg1, data };*/
struct i2c_msg msg = {
.addr = state->config->demod_address,
.flags = 0 ,
.buf = buf,
.len = len + 1
};
if (len > sizeof (buf) - 1 )
return -EINVAL;
msg.buf[0 ] = reg1;
memcpy(msg.buf + 1 , data, len);
ret = i2c_transfer(state->i2c, &msg, 1 );
if (ret != 1 )
dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, ret == %i)\n" ,
__func__, reg1, data[0 ], ret);
return (ret != 1 ) ? -EREMOTEIO : 0 ;
}
static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
{
int ret;
u8 buf[] = { reg, data };
struct i2c_msg msg = {
.addr = state->config->demod_address,
.flags = 0 ,
.buf = buf,
.len = 2
};
ret = i2c_transfer(state->i2c, &msg, 1 );
if (ret != 1 )
dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, ret == %i)\n" ,
__func__, reg, data, ret);
return (ret != 1 ) ? -EREMOTEIO : 0 ;
}
static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len)
{
struct si21xx_state *state = fe->demodulator_priv;
if (len != 2 )
return -EINVAL;
return si21_writereg(state, buf[0 ], buf[1 ]);
}
static u8 si21_readreg(struct si21xx_state *state, u8 reg)
{
int ret;
u8 b0[] = { reg };
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
{
.addr = state->config->demod_address,
.flags = 0 ,
.buf = b0,
.len = 1
}, {
.addr = state->config->demod_address,
.flags = I2C_M_RD,
.buf = b1,
.len = 1
}
};
ret = i2c_transfer(state->i2c, msg, 2 );
if (ret != 2 )
dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n" ,
__func__, reg, ret);
return b1[0 ];
}
static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
{
int ret;
struct i2c_msg msg[] = {
{
.addr = state->config->demod_address,
.flags = 0 ,
.buf = ®1,
.len = 1
}, {
.addr = state->config->demod_address,
.flags = I2C_M_RD,
.buf = b,
.len = len
}
};
ret = i2c_transfer(state->i2c, msg, 2 );
if (ret != 2 )
dprintk("%s: readreg error (ret == %i)\n" , __func__, ret);
return ret == 2 ? 0 : -1 ;
}
static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
{
unsigned long start = jiffies;
dprintk("%s\n" , __func__);
while ((si21_readreg(state, LNB_CTRL_REG_1) & 0 x8) == 8 ) {
if (time_is_before_jiffies(start + timeout)) {
dprintk("%s: timeout!!\n" , __func__);
return -ETIMEDOUT;
}
msleep(10 );
}
return 0 ;
}
static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
{
struct si21xx_state *state = fe->demodulator_priv;
u32 sym_rate, data_rate;
int i;
u8 sym_rate_bytes[3 ];
dprintk("%s : srate = %i\n" , __func__ , srate);
if ((srate < 1000000 ) || (srate > 45000000 ))
return -EINVAL;
data_rate = srate;
sym_rate = 0 ;
for (i = 0 ; i < 4 ; ++i) {
sym_rate /= 100 ;
sym_rate = sym_rate + ((data_rate % 100 ) * 0 x800000) /
state->fs;
data_rate /= 100 ;
}
for (i = 0 ; i < 3 ; ++i)
sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8 )) & 0 xff);
si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0 x03);
return 0 ;
}
static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *m)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 lnb_status;
u8 LNB_CTRL_1;
int status;
dprintk("%s\n" , __func__);
status = PASS;
LNB_CTRL_1 = 0 ;
status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0 x01);
status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0 x01);
/*fill the FIFO*/
status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
LNB_CTRL_1 = (lnb_status & 0 x70);
LNB_CTRL_1 |= m->msg_len;
LNB_CTRL_1 |= 0 x80; /* begin LNB signaling */
status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0 x01);
return status;
}
static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
enum fe_sec_mini_cmd burst)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 val;
dprintk("%s\n" , __func__);
if (si21xx_wait_diseqc_idle(state, 100 ) < 0 )
return -ETIMEDOUT;
val = (0 x80 | si21_readreg(state, 0 xc1));
if (si21_writereg(state, LNB_CTRL_REG_1,
burst == SEC_MINI_A ? (val & ~0 x10) : (val | 0 x10)))
return -EREMOTEIO;
if (si21xx_wait_diseqc_idle(state, 100 ) < 0 )
return -ETIMEDOUT;
if (si21_writereg(state, LNB_CTRL_REG_1, val))
return -EREMOTEIO;
return 0 ;
}
/* 30.06.2008 */
static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 val;
dprintk("%s\n" , __func__);
val = (0 x80 | si21_readreg(state, LNB_CTRL_REG_1));
switch (tone) {
case SEC_TONE_ON:
return si21_writereg(state, LNB_CTRL_REG_1, val | 0 x20);
case SEC_TONE_OFF:
return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0 x20));
default :
return -EINVAL;
}
}
static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 val;
dprintk("%s: %s\n" , __func__,
volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??" );
val = (0 x80 | si21_readreg(state, LNB_CTRL_REG_1));
switch (volt) {
case SEC_VOLTAGE_18:
return si21_writereg(state, LNB_CTRL_REG_1, val | 0 x40);
case SEC_VOLTAGE_13:
return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0 x40));
default :
return -EINVAL;
}
}
static int si21xx_init(struct dvb_frontend *fe)
{
struct si21xx_state *state = fe->demodulator_priv;
int i;
int status = 0 ;
u8 reg1;
u8 val;
u8 reg2[2 ];
dprintk("%s\n" , __func__);
for (i = 0 ; ; i += 2 ) {
reg1 = serit_sp1511lhb_inittab[i];
val = serit_sp1511lhb_inittab[i+1 ];
if (reg1 == 0 xff && val == 0 xff)
break ;
si21_writeregs(state, reg1, &val, 1 );
}
/*DVB QPSK SYSTEM MODE REG*/
reg1 = 0 x08;
si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0 x01);
/*transport stream config*/
/*
mode = PARALLEL;
sdata_form = LSB_FIRST;
clk_edge = FALLING_EDGE;
clk_mode = CLK_GAPPED_MODE;
strt_len = BYTE_WIDE;
sync_pol = ACTIVE_HIGH;
val_pol = ACTIVE_HIGH;
err_pol = ACTIVE_HIGH;
sclk_rate = 0x00;
parity = 0x00 ;
data_delay = 0x00;
clk_delay = 0x00;
pclk_smooth = 0x00;
*/
reg2[0 ] =
PARALLEL + (LSB_FIRST << 1 )
+ (FALLING_EDGE << 2 ) + (CLK_GAPPED_MODE << 3 )
+ (BYTE_WIDE << 4 ) + (ACTIVE_HIGH << 5 )
+ (ACTIVE_HIGH << 6 ) + (ACTIVE_HIGH << 7 );
reg2[1 ] = 0 ;
/* sclk_rate + (parity << 2)
+ (data_delay << 3) + (clk_delay << 4)
+ (pclk_smooth << 5);
*/
status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0 x02);
if (status != 0 )
dprintk(" %s : TS Set Error\n" , __func__);
return 0 ;
}
static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 regs_read[2 ];
u8 reg_read;
u8 i;
u8 lock;
u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0 x02);
reg_read = 0 ;
for (i = 0 ; i < 7 ; ++i)
reg_read |= ((regs_read[0 ] >> i) & 0 x01) << (6 - i);
lock = ((reg_read & 0 x7f) | (regs_read[1 ] & 0 x80));
dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n" , __func__, lock);
*status = 0 ;
if (signal > 10 )
*status |= FE_HAS_SIGNAL;
if (lock & 0 x2)
*status |= FE_HAS_CARRIER;
if (lock & 0 x20)
*status |= FE_HAS_VITERBI;
if (lock & 0 x40)
*status |= FE_HAS_SYNC;
if ((lock & 0 x7b) == 0 x7b)
*status |= FE_HAS_LOCK;
return 0 ;
}
static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct si21xx_state *state = fe->demodulator_priv;
/*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
(u8*)agclevel, 0x01);*/
u16 signal = (3 * si21_readreg(state, 0 x27) *
si21_readreg(state, 0 x28));
dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n" , __func__,
si21_readreg(state, 0 x27),
si21_readreg(state, 0 x28), (int ) signal);
signal <<= 4 ;
*strength = signal;
return 0 ;
}
static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct si21xx_state *state = fe->demodulator_priv;
dprintk("%s\n" , __func__);
if (state->errmode != STATUS_BER)
return 0 ;
*ber = (si21_readreg(state, 0 x1d) << 8 ) |
si21_readreg(state, 0 x1e);
return 0 ;
}
static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct si21xx_state *state = fe->demodulator_priv;
s32 xsnr = 0 xffff - ((si21_readreg(state, 0 x24) << 8 ) |
si21_readreg(state, 0 x25));
xsnr = 3 * (xsnr - 0 xa100);
*snr = (xsnr > 0 xffff) ? 0 xffff : (xsnr < 0 ) ? 0 : xsnr;
dprintk("%s\n" , __func__);
return 0 ;
}
static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
struct si21xx_state *state = fe->demodulator_priv;
dprintk("%s\n" , __func__);
if (state->errmode != STATUS_UCBLOCKS)
*ucblocks = 0 ;
else
*ucblocks = (si21_readreg(state, 0 x1d) << 8 ) |
si21_readreg(state, 0 x1e);
return 0 ;
}
/* initiates a channel acquisition sequence
using the specified symbol rate and code rate */
static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
enum fe_code_rate crate)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 coderates[] = {
0 x0, 0 x01, 0 x02, 0 x04, 0 x00,
0 x8, 0 x10, 0 x20, 0 x00, 0 x3f
};
u8 coderate_ptr;
int status;
u8 start_acq = 0 x80;
u8 reg, regs[3 ];
dprintk("%s\n" , __func__);
status = PASS;
coderate_ptr = coderates[crate];
si21xx_set_symbolrate(fe, symbrate);
/* write code rates to use in the Viterbi search */
status |= si21_writeregs(state,
VIT_SRCH_CTRL_REG_1,
&coderate_ptr, 0 x01);
/* clear acq_start bit */
status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0 x01);
reg &= ~start_acq;
status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0 x01);
/* use new Carrier Frequency Offset Estimator (QuickLock) */
regs[0 ] = 0 xCB;
regs[1 ] = 0 x40;
regs[2 ] = 0 xCB;
status |= si21_writeregs(state,
TWO_DB_BNDWDTH_THRSHLD_REG,
®s[0 ], 0 x03);
reg = 0 x56;
status |= si21_writeregs(state,
LSA_CTRL_REG_1, ®, 1 );
reg = 0 x05;
status |= si21_writeregs(state,
BLIND_SCAN_CTRL_REG, ®, 1 );
/* start automatic acq */
status |= si21_writeregs(state,
ACQ_CTRL_REG_2, &start_acq, 0 x01);
return status;
}
static int si21xx_set_frontend(struct dvb_frontend *fe)
{
struct si21xx_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
/* freq Channel carrier frequency in KHz (i.e. 1550000 KHz)
datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/
/* in MHz */
unsigned char coarse_tune_freq;
int fine_tune_freq;
unsigned char sample_rate = 0 ;
/* boolean */
bool inband_interferer_ind;
/* INTERMEDIATE VALUES */
int icoarse_tune_freq; /* MHz */
int ifine_tune_freq; /* MHz */
unsigned int band_high;
unsigned int band_low;
unsigned int x1;
unsigned int x2;
int i;
bool inband_interferer_div2[ALLOWABLE_FS_COUNT];
bool inband_interferer_div4[ALLOWABLE_FS_COUNT];
int status = 0 ;
/* allowable sample rates for ADC in MHz */
int afs[ALLOWABLE_FS_COUNT] = { 200 , 192 , 193 , 194 , 195 ,
196 , 204 , 205 , 206 , 207
};
/* in MHz */
int if_limit_high;
int if_limit_low;
int lnb_lo;
int lnb_uncertanity;
int rf_freq;
int data_rate;
unsigned char regs[4 ];
dprintk("%s : FE_SET_FRONTEND\n" , __func__);
if (c->delivery_system != SYS_DVBS) {
dprintk("%s: unsupported delivery system selected (%d)\n" ,
__func__, c->delivery_system);
return -EOPNOTSUPP;
}
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i)
inband_interferer_div2[i] = inband_interferer_div4[i] = false ;
if_limit_high = -700000 ;
if_limit_low = -100000 ;
/* in MHz */
lnb_lo = 0 ;
lnb_uncertanity = 0 ;
rf_freq = 10 * c->frequency ;
data_rate = c->symbol_rate / 100 ;
band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200 )
+ (data_rate * 135 )) / 200 ;
band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200 )
+ (data_rate * 135 )) / 200 ;
icoarse_tune_freq = 100000 *
(((rf_freq - lnb_lo) -
(if_limit_low + if_limit_high) / 2 )
/ 100000 );
ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i) {
x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500 )) *
(afs[i] * 2500 ) + afs[i] * 2500 ;
x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500 )) *
(afs[i] * 2500 );
if (((band_low < x1) && (x1 < band_high)) ||
((band_low < x2) && (x2 < band_high)))
inband_interferer_div4[i] = true ;
}
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i) {
x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000 )) *
(afs[i] * 5000 ) + afs[i] * 5000 ;
x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000 )) *
(afs[i] * 5000 );
if (((band_low < x1) && (x1 < band_high)) ||
((band_low < x2) && (x2 < band_high)))
inband_interferer_div2[i] = true ;
}
inband_interferer_ind = true ;
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i) {
if (inband_interferer_div2[i] || inband_interferer_div4[i]) {
inband_interferer_ind = false ;
break ;
}
}
if (inband_interferer_ind) {
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i) {
if (!inband_interferer_div2[i]) {
sample_rate = (u8) afs[i];
break ;
}
}
} else {
for (i = 0 ; i < ALLOWABLE_FS_COUNT; ++i) {
if ((inband_interferer_div2[i] ||
!inband_interferer_div4[i])) {
sample_rate = (u8) afs[i];
break ;
}
}
}
if (sample_rate > 207 || sample_rate < 192 )
sample_rate = 200 ;
fine_tune_freq = ((0 x4000 * (ifine_tune_freq / 10 )) /
((sample_rate) * 1000 ));
coarse_tune_freq = (u8)(icoarse_tune_freq / 100000 );
regs[0 ] = sample_rate;
regs[1 ] = coarse_tune_freq;
regs[2 ] = fine_tune_freq & 0 xFF;
regs[3 ] = fine_tune_freq >> 8 & 0 xFF;
status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0 ], 0 x04);
state->fs = sample_rate;/*ADC MHz*/
si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
if (status)
return -EREMOTEIO;
return 0 ;
}
static int si21xx_sleep(struct dvb_frontend *fe)
{
struct si21xx_state *state = fe->demodulator_priv;
u8 regdata;
dprintk("%s\n" , __func__);
si21_readregs(state, SYSTEM_MODE_REG, ®data, 0 x01);
regdata |= 1 << 6 ;
si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0 x01);
state->initialised = 0 ;
return 0 ;
}
static void si21xx_release(struct dvb_frontend *fe)
{
struct si21xx_state *state = fe->demodulator_priv;
dprintk("%s\n" , __func__);
kfree(state);
}
static const struct dvb_frontend_ops si21xx_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "SL SI21XX DVB-S" ,
.frequency_min_hz = 950 * MHz,
.frequency_max_hz = 2150 * MHz,
.frequency_stepsize_hz = 125 * kHz,
.symbol_rate_min = 1000000 ,
.symbol_rate_max = 45000000 ,
.symbol_rate_tolerance = 500 , /* ppm */
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_QPSK |
FE_CAN_FEC_AUTO
},
.release = si21xx_release,
.init = si21xx_init,
.sleep = si21xx_sleep,
.write = si21_write,
.read_status = si21_read_status,
.read_ber = si21_read_ber,
.read_signal_strength = si21_read_signal_strength,
.read_snr = si21_read_snr,
.read_ucblocks = si21_read_ucblocks,
.diseqc_send_master_cmd = si21xx_send_diseqc_msg,
.diseqc_send_burst = si21xx_send_diseqc_burst,
.set_tone = si21xx_set_tone,
.set_voltage = si21xx_set_voltage,
.set_frontend = si21xx_set_frontend,
};
struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
struct i2c_adapter *i2c)
{
struct si21xx_state *state = NULL;
int id;
dprintk("%s\n" , __func__);
/* allocate memory for the internal state */
state = kzalloc(sizeof (struct si21xx_state), GFP_KERNEL);
if (state == NULL)
goto error;
/* setup the state */
state->config = config;
state->i2c = i2c;
state->initialised = 0 ;
state->errmode = STATUS_BER;
/* check if the demod is there */
id = si21_readreg(state, SYSTEM_MODE_REG);
si21_writereg(state, SYSTEM_MODE_REG, id | 0 x40); /* standby off */
msleep(200 );
id = si21_readreg(state, 0 x00);
/* register 0x00 contains:
0x34 for SI2107
0x24 for SI2108
0x14 for SI2109
0x04 for SI2110
*/
if (id != 0 x04 && id != 0 x14)
goto error;
/* create dvb_frontend */
memcpy(&state->frontend.ops, &si21xx_ops,
sizeof (struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
error:
kfree(state);
return NULL;
}
EXPORT_SYMBOL_GPL(si21xx_attach);
module_param(debug, int , 0644 );
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)." );
MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver" );
MODULE_AUTHOR("Igor M. Liplianin" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland