The patch number 9017 was added via Igor M. Liplianin <[EMAIL PROTECTED]>
to http://linuxtv.org/hg/v4l-dvb master development tree.

Kernel patches in this development tree may be modified to be backward
compatible with older kernels. Compatibility modifications will be
removed before inclusion into the mainstream Kernel

If anyone has any objections, please let us know by sending a message to:
        [EMAIL PROTECTED]

------

From: Igor M. Liplianin  <[EMAIL PROTECTED]>
Add support for Silicon Laboratories SI2109/2110 demodulators.


Add support for Silicon Laboratories SI2109/2110 demodulator
and cards with it, such as DvbWorld PCI2002.

Signed-off-by: Igor M. Liplianin <[EMAIL PROTECTED]>


---

 linux/drivers/media/dvb/dm1105/Kconfig     |    1 
 linux/drivers/media/dvb/dm1105/dm1105.c    |    7 
 linux/drivers/media/dvb/frontends/Kconfig  |    7 
 linux/drivers/media/dvb/frontends/Makefile |    1 
 linux/drivers/media/dvb/frontends/si21xx.c | 1049 +++++++++++++++++++++
 linux/drivers/media/dvb/frontends/si21xx.h |   37 
 6 files changed, 1098 insertions(+), 4 deletions(-)

diff -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/dm1105/Kconfig
--- a/linux/drivers/media/dvb/dm1105/Kconfig    Sun Sep 21 23:54:59 2008 -0400
+++ b/linux/drivers/media/dvb/dm1105/Kconfig    Wed Sep 17 00:21:11 2008 +0300
@@ -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
diff -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/dm1105/dm1105.c
--- a/linux/drivers/media/dvb/dm1105/dm1105.c   Sun Sep 21 23:54:59 2008 -0400
+++ b/linux/drivers/media/dvb/dm1105/dm1105.c   Wed Sep 17 00:21:11 2008 +0300
@@ -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"
 
@@ -608,12 +608,12 @@ static struct stv0288_config earda_confi
        .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,
@@ -647,7 +647,7 @@ static int __devinit frontend_init(struc
                                                &dm1105dvb->i2c_adap);
                        }
                }
-
+#endif /* keep */
                if (!dm1105dvb->fe) {
                        dm1105dvb->fe = dvb_attach(
                                si21xx_attach, &serit_config,
@@ -656,7 +656,6 @@ static int __devinit frontend_init(struc
                                dm1105dvb->fe->ops.set_voltage =
                                                        dm1105dvb_set_voltage;
                }
-#endif /* keep */
                break;
        case PCI_DEVICE_ID_DW2004:
                dm1105dvb->fe = dvb_attach(
diff -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/frontends/Kconfig
--- a/linux/drivers/media/dvb/frontends/Kconfig Sun Sep 21 23:54:59 2008 -0400
+++ b/linux/drivers/media/dvb/frontends/Kconfig Wed Sep 17 00:21:11 2008 +0300
@@ -98,6 +98,13 @@ config DVB_CX24116
        default m if DVB_FE_CUSTOMISE
        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
diff -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/frontends/Makefile
--- a/linux/drivers/media/dvb/frontends/Makefile        Sun Sep 21 23:54:59 
2008 -0400
+++ b/linux/drivers/media/dvb/frontends/Makefile        Wed Sep 17 00:21:11 
2008 +0300
@@ -50,3 +50,4 @@ obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
 obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
 obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
 obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
diff -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/frontends/si21xx.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/si21xx.c        Wed Sep 17 00:21:11 
2008 +0300
@@ -0,0 +1,1049 @@
+/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
+*
+* Copyright (C) 2008 Igor M. Liplianin ([EMAIL PROTECTED])
+*
+*      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;
+}
+#if 0
+static int si21xx_wait_diseqc_fifo(struct si21xx_state *state, int timeout)
+{
+       unsigned long start = jiffies;
+
+       dprintk("%s\n", __func__);
+
+       while (((si21_readreg(state, 0xc4) >> 7) & 1) == 0) {
+               if (jiffies - start > timeout) {
+                       dprintk("%s: timeout!!\n", __func__);
+                       return -ETIMEDOUT;
+               }
+               msleep(10);
+       };
+
+       return 0;
+}
+#endif
+
+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;
+#if 0
+       u8 val;
+       int i;
+
+               if (si21xx_wait_diseqc_idle(state, 100) < 0)
+                       return -ETIMEDOUT;
+
+               val = si21_readreg(state, 0x08);
+               /* DiSEqC mode */
+               if (si21_writereg(state, 0x08, (val & ~0x7) | 0x6))
+                       return -EREMOTEIO;
+
+               for (i = 0; i < m->msg_len; i++) {
+                       if (si21xx_wait_diseqc_fifo(state, 100) < 0)
+                               return -ETIMEDOUT;
+
+                       if (si21_writereg(state, 0x09, m->msg[i]))
+                               return -EREMOTEIO;
+               }
+
+               if (si21xx_wait_diseqc_idle(state, 100) < 0)
+                       return -ETIMEDOUT;
+
+               return 0;
+       }
+
+int si21xx_set_lnb_msg(state, lnb_cmd)
+{
+#endif
+       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);
+#if 0
+       LNB_CTRL_1 |= voltage << 6;     /*voltage select*/
+       LNB_CTRL_1 |= tone << 5;        /*continuous tone selection*/
+       LNB_CTRL_1 |= burst << 4;       /*tone burst selection*/
+       LNB_CTRL_1 |= mmsg << 3;        /*more messages indicator*/
+#endif
+       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 0
+       /* burst mode */
+       if (si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x10))
+               return -EREMOTEIO;
+#endif
+       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__);
+#if 0
+       if (si21xx_wait_diseqc_idle(state, 100) < 0)
+               return -ETIMEDOUT;
+#endif
+       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__);
+
+#if 0
+       lnb_cmd.tone = ON; /* 22khz continuous */
+       lnb_cmd.mmsg = OFF; /* diseqc more message */
+       /* diseqc  command */
+       lnb_cmd.msg[6] = { "0xE0", "0x10", "0x38", "0xF0" };
+       lnb_cmd.msg_len = OFF; /* diseqc command length */
+       lnb_cmd.burst = OFF; /* tone burst a,b */
+       lnb_cmd.volt = OFF; /* 13v 18v select */
+
+       status |= si21xx_set_lnb_msg(state, lnb_cmd);
+       if (status != PASS)
+               dprintk("%s LNB Set Error\n", __func__);
+#endif
+       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 -r 180f24d10379 -r 5374a0db9c65 linux/drivers/media/dvb/frontends/si21xx.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/si21xx.h        Wed Sep 17 00:21:11 
2008 +0300
@@ -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


---

Patch is available at: 
http://linuxtv.org/hg/v4l-dvb/rev/5374a0db9c652e7170b36ae664ba6dbadb2fb677

_______________________________________________
linuxtv-commits mailing list
linuxtv-commits@linuxtv.org
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits

Reply via email to