V4L/DVB (9017): Add support for Silicon Laboratories SI2109/2110 demodulators.
authorIgor M. Liplianin <liplianin@me.by>
Tue, 16 Sep 2008 21:21:11 +0000 (18:21 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 12 Oct 2008 11:37:06 +0000 (09:37 -0200)
Add support for Silicon Laboratories SI2109/2110 demodulator
and cards with it, such as DvbWorld PCI2002.

Signed-off-by: Igor M. Liplianin <liplianin@me.by>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dm1105/Kconfig
drivers/media/dvb/dm1105/dm1105.c
drivers/media/dvb/frontends/Kconfig
drivers/media/dvb/frontends/Makefile
drivers/media/dvb/frontends/si21xx.c [new file with mode: 0644]
drivers/media/dvb/frontends/si21xx.h [new file with mode: 0644]

index 4b96bbba9f42de4a90db6efb6b4840a9191d07a9..2af5fffe138e05815f232dd8febf385474ced659 100644 (file)
@@ -4,6 +4,7 @@ config DVB_DM1105
        select DVB_PLL if !DVB_FE_CUSTOMISE
        select DVB_STV0299 if !DVB_FE_CUSTOMISE
        select DVB_CX24116 if !DVB_FE_CUSTOMISE
+       select DVB_SI21XX if !DVB_FE_CUSTOMISE
        help
          Support for cards based on the SDMC DM1105 PCI chip like
          DvbWorld 2002
index 204763d2de4100900760098131443c24f96e36a1..0c1790fe2b56d463fa3f21dca4888c644c142db4 100644 (file)
@@ -40,8 +40,8 @@
 
 #include "stv0299.h"
 /*#include "stv0288.h"
- *#include "si21xx.h"
  *#include "stb6000.h"*/
+#include "si21xx.h"
 #include "cx24116.h"
 #include "z0194a.h"
 
@@ -600,12 +600,12 @@ static struct stv0288_config earda_config = {
        .min_delay_ms = 100,
 };
 
+#endif /* keep */
 static struct si21xx_config serit_config = {
        .demod_address = 0x68,
        .min_delay_ms = 100,
 
 };
-#endif /* keep */
 
 static struct cx24116_config serit_sp2633_config = {
        .demod_address = 0x55,
@@ -639,7 +639,7 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
                                                &dm1105dvb->i2c_adap);
                        }
                }
-
+#endif /* keep */
                if (!dm1105dvb->fe) {
                        dm1105dvb->fe = dvb_attach(
                                si21xx_attach, &serit_config,
@@ -648,7 +648,6 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
                                dm1105dvb->fe->ops.set_voltage =
                                                        dm1105dvb_set_voltage;
                }
-#endif /* keep */
                break;
        case PCI_DEVICE_ID_DW2004:
                dm1105dvb->fe = dvb_attach(
index d2f31da26cbc72b57e03c3cfc9d4e84bcc24c1c5..7697391ca86cc6a0b1f3dd93a562b021e6045080 100644 (file)
@@ -99,6 +99,13 @@ config DVB_CX24116
        help
          A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
 
+config DVB_SI21XX
+       tristate "Silicon Labs SI21XX based"
+       depends on DVB_CORE && I2C
+       default m if DVB_FE_CUSTOMISE
+       help
+         A DVB-S tuner module. Say Y when you want to support this frontend.
+
 comment "DVB-T (terrestrial) frontends"
        depends on DVB_CORE
 
index 5deb8a542abd53ba595053d28d40499af5fefb56..c719b7fdce4aea2a14e3aa031b70f08385f1c2d7 100644 (file)
@@ -52,3 +52,4 @@ obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
 obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
 obj-$(CONFIG_DVB_AF9013) += af9013.o
 obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
new file mode 100644 (file)
index 0000000..3ddbe69
--- /dev/null
@@ -0,0 +1,974 @@
+/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+*      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.
+*
+*/
+#include <linux/version.h>
+#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 "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 FALSE  0
+#define TRUE   1
+#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
+       };
+
+       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, 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 (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,
+                                               fe_sec_mini_cmd_t 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, fe_sec_tone_mode_t 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, fe_sec_voltage_t 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);
+               break;
+       case SEC_VOLTAGE_13:
+               return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
+               break;
+       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, fe_status_t *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,
+                                               fe_code_rate_t 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_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+       dprintk("%s(..)\n", __func__);
+       return 0;
+}
+
+static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+       dprintk("%s(..)\n", __func__);
+       return 0;
+}
+
+static int si21xx_set_frontend(struct dvb_frontend *fe,
+                                       struct dvb_frontend_parameters *dfp)
+{
+       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 */
+       unsigned int 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;
+       unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = {
+                       FALSE, FALSE, FALSE, FALSE, FALSE,
+                       FALSE, FALSE, FALSE, FALSE, FALSE
+       };
+       unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = {
+                       FALSE, FALSE, FALSE, FALSE, FALSE,
+                       FALSE, FALSE, FALSE, FALSE, FALSE
+       };
+
+       int status;
+
+       /* 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;
+
+       status = PASS;
+
+       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)
+               inband_interferer_ind &= inband_interferer_div2[i] |
+                                               inband_interferer_div4[i];
+
+       if (inband_interferer_ind) {
+               for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+                       if (inband_interferer_div2[i] == FALSE) {
+                               sample_rate = (u8) afs[i];
+                               break;
+                       }
+               }
+       } else {
+               for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+                       if ((inband_interferer_div2[i] |
+                                       inband_interferer_div4[i]) == FALSE) {
+                               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);
+
+       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 struct dvb_frontend_ops si21xx_ops = {
+
+       .info = {
+               .name                   = "SL SI21XX DVB-S",
+               .type                   = FE_QPSK,
+               .frequency_min          = 950000,
+               .frequency_max          = 2150000,
+               .frequency_stepsize     = 125,   /* kHz for QPSK frontends */
+               .frequency_tolerance    = 0,
+               .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_property = si21xx_set_property,
+       .get_property = si21xx_get_property,
+       .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 = kmalloc(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 | 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(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");
diff --git a/drivers/media/dvb/frontends/si21xx.h b/drivers/media/dvb/frontends/si21xx.h
new file mode 100644 (file)
index 0000000..141b5b8
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef SI21XX_H
+#define SI21XX_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct si21xx_config {
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* minimum delay before retuning */
+       int min_delay_ms;
+};
+
+#if defined(CONFIG_DVB_SI21XX) || \
+               (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE))
+extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+                                               struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *si21xx_attach(
+               const struct si21xx_config *config, struct i2c_adapter *i2c)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+       int r = 0;
+       u8 buf[] = {reg, val};
+       if (fe->ops.write)
+               r = fe->ops.write(fe, buf, 2);
+       return r;
+}
+
+#endif