root/drivers/media/dvb-frontends/si21xx.c
// 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                    0x00
#define SYSTEM_MODE_REG                 0x01
#define TS_CTRL_REG_1                   0x02
#define TS_CTRL_REG_2                   0x03
#define PIN_CTRL_REG_1                  0x04
#define PIN_CTRL_REG_2                  0x05
#define LOCK_STATUS_REG_1               0x0f
#define LOCK_STATUS_REG_2               0x10
#define ACQ_STATUS_REG                  0x11
#define ACQ_CTRL_REG_1                  0x13
#define ACQ_CTRL_REG_2                  0x14
#define PLL_DIVISOR_REG                 0x15
#define COARSE_TUNE_REG                 0x16
#define FINE_TUNE_REG_L                 0x17
#define FINE_TUNE_REG_H                 0x18

#define ANALOG_AGC_POWER_LEVEL_REG      0x28
#define CFO_ESTIMATOR_CTRL_REG_1        0x29
#define CFO_ESTIMATOR_CTRL_REG_2        0x2a
#define CFO_ESTIMATOR_CTRL_REG_3        0x2b

#define SYM_RATE_ESTIMATE_REG_L         0x31
#define SYM_RATE_ESTIMATE_REG_M         0x32
#define SYM_RATE_ESTIMATE_REG_H         0x33

#define CFO_ESTIMATOR_OFFSET_REG_L      0x36
#define CFO_ESTIMATOR_OFFSET_REG_H      0x37
#define CFO_ERROR_REG_L                 0x38
#define CFO_ERROR_REG_H                 0x39
#define SYM_RATE_ESTIMATOR_CTRL_REG     0x3a

#define SYM_RATE_REG_L                  0x3f
#define SYM_RATE_REG_M                  0x40
#define SYM_RATE_REG_H                  0x41
#define SYM_RATE_ESTIMATOR_MAXIMUM_REG  0x42
#define SYM_RATE_ESTIMATOR_MINIMUM_REG  0x43

#define C_N_ESTIMATOR_CTRL_REG          0x7c
#define C_N_ESTIMATOR_THRSHLD_REG       0x7d
#define C_N_ESTIMATOR_LEVEL_REG_L       0x7e
#define C_N_ESTIMATOR_LEVEL_REG_H       0x7f

#define BLIND_SCAN_CTRL_REG             0x80

#define LSA_CTRL_REG_1                  0x8D
#define SPCTRM_TILT_CORR_THRSHLD_REG    0x8f
#define ONE_DB_BNDWDTH_THRSHLD_REG      0x90
#define TWO_DB_BNDWDTH_THRSHLD_REG      0x91
#define THREE_DB_BNDWDTH_THRSHLD_REG    0x92
#define INBAND_POWER_THRSHLD_REG        0x93
#define REF_NOISE_LVL_MRGN_THRSHLD_REG  0x94

#define VIT_SRCH_CTRL_REG_1             0xa0
#define VIT_SRCH_CTRL_REG_2             0xa1
#define VIT_SRCH_CTRL_REG_3             0xa2
#define VIT_SRCH_STATUS_REG             0xa3
#define VITERBI_BER_COUNT_REG_L         0xab
#define REED_SOLOMON_CTRL_REG           0xb0
#define REED_SOLOMON_ERROR_COUNT_REG_L  0xb1
#define PRBS_CTRL_REG                   0xb5

#define LNB_CTRL_REG_1                  0xc0
#define LNB_CTRL_REG_2                  0xc1
#define LNB_CTRL_REG_3                  0xc2
#define LNB_CTRL_REG_4                  0xc3
#define LNB_CTRL_STATUS_REG             0xc4
#define LNB_FIFO_REGS_0                 0xc5
#define LNB_FIFO_REGS_1                 0xc6
#define LNB_FIFO_REGS_2                 0xc7
#define LNB_FIFO_REGS_3                 0xc8
#define LNB_FIFO_REGS_4                 0xc9
#define LNB_FIFO_REGS_5                 0xca
#define LNB_SUPPLY_CTRL_REG_1           0xcb
#define LNB_SUPPLY_CTRL_REG_2           0xcc
#define LNB_SUPPLY_CTRL_REG_3           0xcd
#define LNB_SUPPLY_CTRL_REG_4           0xce
#define LNB_SUPPLY_STATUS_REG           0xcf

#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[] = {
        0x01, 0x28,     /* set i2c_inc_disable */
        0x20, 0x03,
        0x27, 0x20,
        0xe0, 0x45,
        0xe1, 0x08,
        0xfe, 0x01,
        0x01, 0x28,
        0x89, 0x09,
        0x04, 0x80,
        0x05, 0x01,
        0x06, 0x00,
        0x20, 0x03,
        0x24, 0x88,
        0x29, 0x09,
        0x2a, 0x0f,
        0x2c, 0x10,
        0x2d, 0x19,
        0x2e, 0x08,
        0x2f, 0x10,
        0x30, 0x19,
        0x34, 0x20,
        0x35, 0x03,
        0x45, 0x02,
        0x46, 0x45,
        0x47, 0xd0,
        0x48, 0x00,
        0x49, 0x40,
        0x4a, 0x03,
        0x4c, 0xfd,
        0x4f, 0x2e,
        0x50, 0x2e,
        0x51, 0x10,
        0x52, 0x10,
        0x56, 0x92,
        0x59, 0x00,
        0x5a, 0x2d,
        0x5b, 0x33,
        0x5c, 0x1f,
        0x5f, 0x76,
        0x62, 0xc0,
        0x63, 0xc0,
        0x64, 0xf3,
        0x65, 0xf3,
        0x79, 0x40,
        0x6a, 0x40,
        0x6b, 0x0a,
        0x6c, 0x80,
        0x6d, 0x27,
        0x71, 0x06,
        0x75, 0x60,
        0x78, 0x00,
        0x79, 0xb5,
        0x7c, 0x05,
        0x7d, 0x1a,
        0x87, 0x55,
        0x88, 0x72,
        0x8f, 0x08,
        0x90, 0xe0,
        0x94, 0x40,
        0xa0, 0x3f,
        0xa1, 0xc0,
        0xa4, 0xcc,
        0xa5, 0x66,
        0xa6, 0x66,
        0xa7, 0x7b,
        0xa8, 0x7b,
        0xa9, 0x7b,
        0xaa, 0x9a,
        0xed, 0x04,
        0xad, 0x00,
        0xae, 0x03,
        0xcc, 0xab,
        0x01, 0x08,
        0xff, 0xff
};

/*      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 = &reg1,
                        .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) & 0x8) == 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) * 0x800000) /
                                                                state->fs;
                data_rate /= 100;
        }
        for (i = 0; i < 3; ++i)
                sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);

        si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);

        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, 0x01);
        status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);

        /*fill the FIFO*/
        status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);

        LNB_CTRL_1 = (lnb_status & 0x70);
        LNB_CTRL_1 |= m->msg_len;

        LNB_CTRL_1 |= 0x80;     /* begin LNB signaling */

        status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);

        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 = (0x80 | si21_readreg(state, 0xc1));
        if (si21_writereg(state, LNB_CTRL_REG_1,
                        burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
                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 = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));

        switch (tone) {
        case SEC_TONE_ON:
                return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);

        case SEC_TONE_OFF:
                return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));

        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 = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));

        switch (volt) {
        case SEC_VOLTAGE_18:
                return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
        case SEC_VOLTAGE_13:
                return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
        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 == 0xff && val == 0xff)
                        break;
                si21_writeregs(state, reg1, &val, 1);
        }

        /*DVB QPSK SYSTEM MODE REG*/
        reg1 = 0x08;
        si21_writeregs(state, SYSTEM_MODE_REG, &reg1, 0x01);

        /*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, 0x02);
        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, 0x02);
        reg_read = 0;

        for (i = 0; i < 7; ++i)
                reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);

        lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));

        dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
        *status = 0;

        if (signal > 10)
                *status |= FE_HAS_SIGNAL;

        if (lock & 0x2)
                *status |= FE_HAS_CARRIER;

        if (lock & 0x20)
                *status |= FE_HAS_VITERBI;

        if (lock & 0x40)
                *status |= FE_HAS_SYNC;

        if ((lock & 0x7b) == 0x7b)
                *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, 0x27) *
                                        si21_readreg(state, 0x28));

        dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
                si21_readreg(state, 0x27),
                si21_readreg(state, 0x28), (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, 0x1d) << 8) |
                                si21_readreg(state, 0x1e);

        return 0;
}

static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
{
        struct si21xx_state *state = fe->demodulator_priv;

        s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
                                        si21_readreg(state, 0x25));
        xsnr = 3 * (xsnr - 0xa100);
        *snr = (xsnr > 0xffff) ? 0xffff : (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, 0x1d) << 8) |
                                        si21_readreg(state, 0x1e);

        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[] = {
                                0x0, 0x01, 0x02, 0x04, 0x00,
                                0x8, 0x10, 0x20, 0x00, 0x3f
        };

        u8 coderate_ptr;
        int status;
        u8 start_acq = 0x80;
        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, 0x01);

        /* clear acq_start bit */
        status |= si21_readregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
        reg &= ~start_acq;
        status |= si21_writeregs(state, ACQ_CTRL_REG_2, &reg, 0x01);

        /* use new Carrier Frequency Offset Estimator (QuickLock) */
        regs[0] = 0xCB;
        regs[1] = 0x40;
        regs[2] = 0xCB;

        status |= si21_writeregs(state,
                                TWO_DB_BNDWDTH_THRSHLD_REG,
                                &regs[0], 0x03);
        reg = 0x56;
        status |= si21_writeregs(state,
                                LSA_CTRL_REG_1, &reg, 1);
        reg = 0x05;
        status |= si21_writeregs(state,
                                BLIND_SCAN_CTRL_REG, &reg, 1);
        /* start automatic acq */
        status |= si21_writeregs(state,
                                ACQ_CTRL_REG_2, &start_acq, 0x01);

        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 = ((0x4000 * (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 & 0xFF;
        regs[3] = fine_tune_freq >> 8 & 0xFF;

        status |= si21_writeregs(state, PLL_DIVISOR_REG, &regs[0], 0x04);

        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, &regdata, 0x01);
        regdata |= 1 << 6;
        si21_writeregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
        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_obj(struct si21xx_state);
        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 | 0x40); /* standby off */
        msleep(200);
        id = si21_readreg(state, 0x00);

        /* register 0x00 contains:
                0x34 for SI2107
                0x24 for SI2108
                0x14 for SI2109
                0x04 for SI2110
        */
        if (id != 0x04 && id != 0x14)
                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");