Added support for the NXP TDA18212 silicon tuner used by recent
Digital Devices hardware. This will allow update of ddbridge driver
to support newer devices.

Signed-off-by: Maik Broemme <mbroe...@parallels.com>
---
 drivers/media/dvb-frontends/Kconfig      |   9 +
 drivers/media/dvb-frontends/Makefile     |   1 +
 drivers/media/dvb-frontends/tda18212dd.c | 934 +++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/tda18212dd.h |  37 ++
 4 files changed, 981 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/tda18212dd.c
 create mode 100644 drivers/media/dvb-frontends/tda18212dd.h

diff --git a/drivers/media/dvb-frontends/Kconfig 
b/drivers/media/dvb-frontends/Kconfig
index 7cac015..a34c1c7 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -65,6 +65,15 @@ config DVB_STV0367DD
 
           Say Y when you want to support this frontend.
 
+config DVB_TDA18212DD
+       tristate "NXP TDA18212 silicon tuner (DD)"
+       depends on DVB_CORE && I2C
+       default m if DVB_FE_CUSTOMISE
+       help
+         NXP TDA18212 silicon tuner (Digital Devices driver).
+
+         Say Y when you want to support this tuner.
+
 comment "DVB-S (satellite) frontends"
        depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/Makefile 
b/drivers/media/dvb-frontends/Makefile
index de100f1..ed12424 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
 obj-$(CONFIG_DVB_DRXK) += drxk.o
 obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
 obj-$(CONFIG_DVB_STV0367DD) += stv0367dd.o
+obj-$(CONFIG_DVB_TDA18212DD) += tda18212dd.o
 obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
 obj-$(CONFIG_DVB_A8293) += a8293.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
diff --git a/drivers/media/dvb-frontends/tda18212dd.c 
b/drivers/media/dvb-frontends/tda18212dd.c
new file mode 100644
index 0000000..3d2e04e
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda18212dd.c
@@ -0,0 +1,934 @@
+/*
+ *  tda18212dd.c: Driver for the TDA18212 tuner
+ *
+ *  Copyright (C) 2010-2013 Digital Devices GmbH
+ *  Copyright (C) 2013 Maik Broemme <mbroe...@parallels.com>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  version 2 only, as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+
+#ifndef CHK_ERROR
+    #define CHK_ERROR(s) if ((status = s) < 0) break
+#endif
+
+#define MASTER_PSM_AGC1     0
+#define MASTER_AGC1_6_15dB  1
+
+#define SLAVE_PSM_AGC1      1
+#define SLAVE_AGC1_6_15dB   0
+
+// 0 = 2 Vpp ... 2 = 1 Vpp,   7 = 0.5 Vpp
+#define IF_LEVEL_DVBC    2
+#define IF_LEVEL_DVBT    2
+
+enum {
+       ID_1                = 0x00,
+       ID_2                = 0x01,
+       ID_3                = 0x02,
+       THERMO_1,
+       THERMO_2,
+       POWER_STATE_1,
+       POWER_STATE_2,
+       INPUT_POWER_LEVEL,
+       IRQ_STATUS,
+       IRQ_ENABLE,
+       IRQ_CLEAR,
+       IRQ_SET,
+       AGC1_1,
+       AGC2_1,
+       AGCK_1,
+       RF_AGC_1,
+       IR_MIXER_1          = 0x10,
+       AGC5_1,
+       IF_AGC,
+       IF_1,
+       REFERENCE,
+       IF_FREQUENCY_1,
+       RF_FREQUENCY_1,
+       RF_FREQUENCY_2,
+       RF_FREQUENCY_3,
+       MSM_1,
+       MSM_2,
+       PSM_1,
+       DCC_1,
+       FLO_MAX,
+       IR_CAL_1,
+       IR_CAL_2,
+       IR_CAL_3            = 0x20,
+       IR_CAL_4,
+       VSYNC_MGT,
+       IR_MIXER_2,
+       AGC1_2,
+       AGC5_2,
+       RF_CAL_1,
+       RF_CAL_2,
+       RF_CAL_3,
+       RF_CAL_4,
+       RF_CAL_5,
+       RF_CAL_6,
+       RF_FILTER_1,
+       RF_FILTER_2,
+       RF_FILTER_3,
+       RF_BAND_PASS_FILTER,
+       CP_CURRENT          = 0x30,
+       AGC_DET_OUT         = 0x31,
+       RF_AGC_GAIN_1       = 0x32,
+       RF_AGC_GAIN_2       = 0x33,
+       IF_AGC_GAIN         = 0x34,
+       POWER_1             = 0x35,
+       POWER_2             = 0x36,
+       MISC_1,
+       RFCAL_LOG_1,
+       RFCAL_LOG_2,
+       RFCAL_LOG_3,
+       RFCAL_LOG_4,
+       RFCAL_LOG_5,
+       RFCAL_LOG_6,
+       RFCAL_LOG_7,
+       RFCAL_LOG_8,
+       RFCAL_LOG_9         = 0x40,
+       RFCAL_LOG_10        = 0x41,
+       RFCAL_LOG_11        = 0x42,
+       RFCAL_LOG_12        = 0x43,
+       REG_MAX,
+};
+
+enum HF_Standard {
+       HF_None=0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio,
+       HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ,
+       HF_DVBT, HF_ATSC,  HF_DVBC_6MHZ,  HF_DVBC_7MHZ,
+       HF_DVBC_8MHZ, HF_DVBC
+};
+
+struct SStandardParams {
+       s32   m_IFFrequency;
+       u32   m_BandWidth;
+       u8    m_IF_1;         // FF IF_HP_fc:2 IF_Notch:1 LP_FC_Offset:2 LP_FC:3
+       u8    m_IR_MIXER_2;   // 03 :6 HI_Pass:1 DC_Notch:1
+       u8    m_AGC1_1;       // 0F :4 AGC1_Top:4
+       u8    m_AGC2_1;       // 0F :4 AGC2_Top:4
+       u8    m_RF_AGC_1_Low; // EF RF_AGC_Adapt:1 RF_AGC_Adapt_Top:2 :1 
RF_Atten_3dB:1  RF_AGC_Top:3
+       u8    m_RF_AGC_1_High;// EF RF_AGC_Adapt:1 RF_AGC_Adapt_Top:2 :1 
RF_Atten_3dB:1  RF_AGC_Top:3
+       u8    m_IR_MIXER_1;   // 0F :4 IR_mixer_Top:4
+       u8    m_AGC5_1;       // 1F :3 AGC5_Ana AGC5_Top:4
+       u8    m_AGCK_1;       // 0F :4 AGCK_Step:2 AGCK_Mode:2
+       u8    m_PSM_1;        // 20 :2 PSM_StoB:1 :5
+       bool  m_AGC1_Freeze;
+       bool  m_LTO_STO_immune;
+};
+
+#if 0
+static struct SStandardParams m_StandardTable[HF_DVBC_8MHZ - HF_DVBT_6MHZ + 1] 
=
+{
+       { 3250000, 6000000, 0x20, 0x03, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 
0x02, 0x20, false, false },    // HF_DVBT_6MHZ
+       { 3500000, 7000000, 0x31, 0x01, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 
0x02, 0x20, false, false },    // HF_DVBT_7MHZ
+       { 4000000, 8000000, 0x22, 0x01, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 
0x02, 0x20, false, false },    // HF_DVBT_8MHZ
+       {       0,       0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, false, false },    // HF_DVBT (Unused)
+       { 3250000, 6000000, 0x20, 0x03, 0x0A, 0x07, 0x6D, 0x6D, 0x0E, 0x0E, 
0x02, 0x20, false, false },    // HF_ATSC
+       { 3600000, 6000000, 0x10, 0x01, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_6MHZ
+//    { 5000000, 7000000, 0x53, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_7MHZ (not documented by NXP, use same 
settings as 8 MHZ)
+//    { 5000000, 8000000, 0x53, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_8MHZ
+       { 5000000, 7000000, 0x93, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_7MHZ (not documented by NXP, use same 
settings as 8 MHZ)
+       { 5000000, 8000000, 0x43, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_8MHZ
+};
+#else
+static struct SStandardParams m_StandardTable[HF_DVBC_8MHZ - HF_DVBT_6MHZ + 1] 
=
+{
+     { 4000000, 6000000, 0x41, 0x03, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 0x02, 
0x20, false, false },    // HF_DVBT_6MHZ
+     { 4500000, 7000000, 0x42, 0x03, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 0x02, 
0x20, false, false },    // HF_DVBT_7MHZ
+     { 5000000, 8000000, 0x43, 0x03, 0x00, 0x07, 0x2B, 0x2C, 0x0B, 0x0B, 0x02, 
0x20, false, false },    // HF_DVBT_8MHZ
+     // ------------------------------
+     {       0,       0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, false, false },    // HF_DVBT (Unused)
+     { 3250000, 6000000, 0x20, 0x03, 0x0A, 0x07, 0x6D, 0x6D, 0x0E, 0x0E, 0x02, 
0x20, false, false },    // HF_ATSC
+     { 3600000, 6000000, 0x10, 0x01, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 0x02, 
0x00, true , true  },    // HF_DVBC_6MHZ
+//    { 5000000, 7000000, 0x53, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_7MHZ (not documented by NXP, use same 
settings as 8 MHZ)
+//    { 5000000, 8000000, 0x53, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 
0x02, 0x00, true , true  },    // HF_DVBC_8MHZ
+     { 5000000, 7000000, 0x93, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 0x02, 
0x00, true , true  },    // HF_DVBC_7MHZ (not documented by NXP, use same 
settings as 8 MHZ)
+     { 5000000, 8000000, 0x43, 0x03, 0x00, 0x07, 0x83, 0x83, 0x0B, 0x0B, 0x02, 
0x00, true , true  },    // HF_DVBC_8MHZ
+};
+#endif
+struct tda_state {
+       struct i2c_adapter *i2c;
+       u8 adr;
+
+       enum HF_Standard m_Standard;
+       u32   m_Frequency;
+       u32   IF;
+
+       bool    m_isMaster;
+       bool    m_bPowerMeasurement;
+       bool    m_bLTEnable;
+       bool    m_bEnableFreeze;
+
+       u16   m_ID;
+
+       s32    m_SettlingTime;
+
+       u8    m_IFLevelDVBC;
+       u8    m_IFLevelDVBT;
+       u8    m_Regs[REG_MAX];
+       u8    m_LastPowerLevel;
+};
+
+static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len)
+{
+       struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+                                  .buf  = data, .len   = len}};
+       return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read(struct i2c_adapter *adap,
+                   u8 adr, u8 *msg, int len, u8 *answ, int alen)
+{
+       struct i2c_msg msgs[2] = { { .addr = adr, .flags = 0,
+                                    .buf = msg, .len = len},
+                                  { .addr = adr, .flags = I2C_M_RD,
+                                    .buf = answ, .len = alen } };
+       if (i2c_transfer(adap, msgs, 2) != 2) {
+               printk("tda18212dd: i2c_read error\n");
+               return -1;
+       }
+       return 0;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+       struct i2c_msg msg = {.addr = adr, .flags = 0,
+                             .buf = data, .len = len};
+
+       if (i2c_transfer(adap, &msg, 1) != 1) {
+               printk("tda18212: i2c_write error\n");
+               return -1;
+       }
+       return 0;
+}
+
+static int write_regs(struct tda_state *state,
+                     u8 SubAddr, u8 *Regs, u16 nRegs)
+{
+       u8 data[nRegs+1];
+
+       data[0] = SubAddr;
+       memcpy(data + 1, Regs, nRegs);
+       return i2c_write(state->i2c, state->adr, data, nRegs+1);
+}
+
+static int write_reg(struct tda_state *state, u8 SubAddr,u8 Reg)
+{
+       u8 msg[2] = {SubAddr, Reg};
+
+       return i2c_write(state->i2c, state->adr, msg, 2);
+}
+
+static int Read(struct tda_state *state, u8 * Regs)
+{
+       return i2c_readn(state->i2c, state->adr, Regs, REG_MAX);
+}
+
+static int update_regs(struct tda_state *state, u8 RegFrom,u8 RegTo)
+{
+       return write_regs(state, RegFrom,
+                         &state->m_Regs[RegFrom], RegTo-RegFrom+1);
+}
+
+static int update_reg(struct tda_state *state, u8 Reg)
+{
+       return write_reg(state, Reg,state->m_Regs[Reg]);
+}
+
+
+static int read_regs(struct tda_state *state,
+                   u8 SubAddr, u8 *Regs, u16 nRegs)
+{
+       return i2c_read(state->i2c, state->adr,
+                       &SubAddr, 1, Regs, nRegs);
+}
+
+static int read_reg(struct tda_state *state,
+                  u8 SubAddr, u8 *Reg)
+{
+       return i2c_read(state->i2c, state->adr,
+                       &SubAddr, 1, Reg, 1);
+}
+
+static int read_reg1(struct tda_state *state, u8 Reg)
+{
+       return read_reg(state, Reg, &state->m_Regs[Reg]);
+}
+
+static void init_state(struct tda_state *state)
+{
+    u32   ulIFLevelDVBC = IF_LEVEL_DVBC;
+    u32   ulIFLevelDVBT = IF_LEVEL_DVBT;
+    u32   ulPowerMeasurement = 1;
+    u32   ulLTEnable = 1;
+    u32   ulEnableFreeze = 0;
+
+    state->m_Frequency    = 0;
+    state->m_isMaster = true;
+    state->m_ID = 0;
+    state->m_LastPowerLevel = 0xFF;
+    state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07);
+    state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07);
+    state->m_bPowerMeasurement = (ulPowerMeasurement != 0);
+    state->m_bLTEnable = (ulLTEnable != 0);
+    state->m_bEnableFreeze = (ulEnableFreeze != 0);
+}
+
+static int StartCalibration(struct tda_state *state)
+{
+       int  status = 0;
+       do {
+               state->m_Regs[POWER_2] &= ~0x02; // RSSI CK = 31.25 kHz
+               CHK_ERROR(update_reg(state, POWER_2));
+
+               state->m_Regs[AGC1_2] = (state->m_Regs[AGC1_2] & ~0x60) | 0x40; 
   // AGC1 Do Step = 2
+               CHK_ERROR(update_reg(state, AGC1_2));        // AGC
+
+               state->m_Regs[RF_FILTER_3] = (state->m_Regs[RF_FILTER_3] & 
~0xC0) | 0x40;    // AGC2 Do Step = 1
+               CHK_ERROR(update_reg(state, RF_FILTER_3));
+
+               state->m_Regs[AGCK_1] |= 0xC0; // AGCs Assym Up Step = 3      
// Datasheet sets all bits to 1!
+               CHK_ERROR(update_reg(state, AGCK_1));
+
+               state->m_Regs[AGC5_1] = (state->m_Regs[AGC5_1] & ~0x60) | 0x40; 
   // AGCs Assym Do Step = 2
+               CHK_ERROR(update_reg(state, AGC5_1));
+
+               state->m_Regs[IRQ_CLEAR] |= 0x80; // Reset IRQ
+               CHK_ERROR(update_reg(state, IRQ_CLEAR));
+
+               state->m_Regs[MSM_1] = 0x3B; // Set Calibration
+               state->m_Regs[MSM_2] = 0x01; // Start MSM
+               CHK_ERROR(update_regs(state, MSM_1,MSM_2));
+               state->m_Regs[MSM_2] = 0x00;
+
+       } while(0);
+       return status;
+}
+
+static int FinishCalibration(struct tda_state *state)
+{
+       int status = 0;
+       u8 RFCal_Log[12];
+
+       do {
+               u8 IRQ = 0;
+               int Timeout = 150; // 1.5 s
+               while(true) {
+                       CHK_ERROR(read_reg(state, IRQ_STATUS, &IRQ));
+                       if ((IRQ & 0x80) != 0 )
+                               break;
+                       Timeout -= 1;
+                       if (Timeout == 0) {
+                               status = -1;
+                               break;
+                       }
+                       msleep(10);
+               }
+               CHK_ERROR(status);
+
+               state->m_Regs[FLO_MAX] = 0x0A;
+               CHK_ERROR(update_reg(state, FLO_MAX));
+
+               state->m_Regs[AGC1_1] &= ~0xC0;
+               if( state->m_bLTEnable ) state->m_Regs[AGC1_1] |= 0x80;    // 
LTEnable
+
+               state->m_Regs[AGC1_1] |= (state->m_isMaster ? 
MASTER_AGC1_6_15dB : SLAVE_AGC1_6_15dB ) << 6;
+               CHK_ERROR(update_reg(state, AGC1_1));
+
+               state->m_Regs[PSM_1] &= ~0xC0;
+               state->m_Regs[PSM_1] |= (state->m_isMaster ? MASTER_PSM_AGC1 : 
SLAVE_PSM_AGC1 ) << 6;
+               CHK_ERROR(update_reg(state, PSM_1));
+
+               state->m_Regs[REFERENCE] |= 0x03; // XTOUT = 3
+               CHK_ERROR(update_reg(state, REFERENCE));
+
+               CHK_ERROR(read_regs(state, 
RFCAL_LOG_1,RFCal_Log,sizeof(RFCal_Log)));
+       } while(0);
+       return status;
+}
+
+static int PowerOn(struct tda_state *state)
+{
+       state->m_Regs[POWER_STATE_2] &= ~0x0F;
+       update_reg(state, POWER_STATE_2);
+       state->m_Regs[REFERENCE] |= 0x40;  // Digital clock source = Sigma Delta
+       update_reg(state, REFERENCE);
+       return 0;
+}
+
+static int Standby(struct tda_state *state)
+{
+       int status = 0;
+
+       do {
+               state->m_Regs[REFERENCE] &= ~0x40;  // Digital clock source = 
Quarz
+               CHK_ERROR(update_reg(state, REFERENCE));
+
+               state->m_Regs[POWER_STATE_2] &= ~0x0F;
+               state->m_Regs[POWER_STATE_2] |= state->m_isMaster ? 0x08 : 0x0E;
+               CHK_ERROR(update_reg(state, POWER_STATE_2));
+       } while(0);
+       return status;
+}
+
+static int attach_init(struct tda_state *state)
+{
+       int stat = 0;
+       u8 Id[2];
+       u8 PowerState = 0x00;
+
+       state->m_Standard = HF_None;
+
+       /* first read after cold reset sometimes fails on some cards,
+          try twice */
+       stat = read_regs(state, ID_1, Id, sizeof(Id));
+       stat = read_regs(state, ID_1, Id, sizeof(Id));
+       if (stat < 0)
+               return -1;
+
+       state->m_ID = ((Id[0] & 0x7F) << 8) | Id[1];
+       state->m_isMaster = ((Id[0] & 0x80) != 0);
+       if( !state->m_isMaster )
+               state->m_bLTEnable = false;
+
+       printk("tda18212dd: ChipID %04x\n", state->m_ID);
+
+       if( state->m_ID != 18212 )
+               return -1;
+
+       stat = read_reg(state, POWER_STATE_1 ,&PowerState);
+       if (stat < 0)
+               return stat;
+
+       printk("tda18212dd: PowerState %02x\n", PowerState);
+
+       if (state->m_isMaster) {
+               if( PowerState & 0x02 ) {
+                       // msleep for XTAL Calibration (on a PC this should be 
long done)
+                       u8 IRQStatus = 0;
+                       int Timeout = 10;
+
+                       while(Timeout > 0) {
+                               read_reg(state, IRQ_STATUS, &IRQStatus);
+                               if (IRQStatus & 0x20)
+                                       break;
+                               Timeout -= 1;
+                               msleep(10);
+                       }
+                       if( (IRQStatus & 0x20) == 0 ) {
+                               stat = -ETIMEDOUT;
+                       }
+               }
+       } else {
+               write_reg(state, FLO_MAX, 0x00);
+               write_reg(state, CP_CURRENT,0x68);
+       }
+       Read(state, state->m_Regs);
+
+       PowerOn(state);
+       StartCalibration(state);
+       FinishCalibration(state);
+       Standby(state);
+       return stat;
+}
+
+static int PowerMeasurement(struct tda_state *state, u8 *pPowerLevel)
+{
+       int status = 0;
+
+       do {
+               u8 IRQ = 0;
+               int Timeout = 70; // 700 ms
+
+               state->m_Regs[IRQ_CLEAR] |= 0x80; // Reset IRQ
+               CHK_ERROR(update_reg(state, IRQ_CLEAR));
+
+               state->m_Regs[MSM_1] = 0x80; // power measurement
+               state->m_Regs[MSM_2] = 0x01; // Start MSM
+               CHK_ERROR(update_regs(state, MSM_1,MSM_2));
+               state->m_Regs[MSM_2] = 0x00;
+
+               while(true) {
+                       CHK_ERROR(read_reg(state, IRQ_STATUS, &IRQ));
+                       if( (IRQ & 0x80) != 0 )
+                               break;
+                       Timeout -= 1;
+                       if( Timeout == 0 )
+                       {
+                               status = -1;
+                               break;
+                       }
+                       msleep(10);
+               }
+               CHK_ERROR(status);
+
+               CHK_ERROR(read_reg1(state, INPUT_POWER_LEVEL));
+               *pPowerLevel = state->m_Regs[INPUT_POWER_LEVEL] & 0x7F;
+
+
+               if( *pPowerLevel > 110 ) *pPowerLevel = 110;
+       } while(0);
+       /* printk("PL %d\n", *pPowerLevel); */
+       return status;
+}
+
+static int SetFrequency(struct tda_state *state, u32 Frequency, enum 
HF_Standard Standard)
+{
+       int status = 0;
+       struct SStandardParams *StandardParams;
+       u32   f = Frequency / 1000;
+       u8 IRQ = 0;
+       int Timeout = 25; // 250 ms
+       u32 fRatio = Frequency / 16000000;
+       u32 fDelta = Frequency - fRatio * 16000000;
+
+       if( Standard < HF_DVBT_6MHZ || Standard > HF_DVBC_8MHZ )
+               return -EINVAL;
+       StandardParams = &m_StandardTable[Standard - HF_DVBT_6MHZ];
+
+       if( StandardParams->m_IFFrequency == 0 )
+               return -EINVAL;
+       state->m_Standard = HF_None;
+       state->m_Frequency = 0;
+
+       do {
+               // IF Level
+               state->m_Regs[IF_AGC] = (Standard >= HF_DVBC_6MHZ) ? 
state->m_IFLevelDVBC : state->m_IFLevelDVBT;
+               CHK_ERROR(update_reg(state, IF_AGC));
+
+               // 
---------------------------------------------------------------------------------
+               // Standard setup
+
+               state->m_Regs[IF_1] = StandardParams->m_IF_1;
+               CHK_ERROR(update_reg(state, IF_1));
+
+               state->m_Regs[IR_MIXER_2] = (state->m_Regs[IR_MIXER_2] & ~0x03) 
| StandardParams->m_IR_MIXER_2;
+               CHK_ERROR(update_reg(state, IR_MIXER_2));
+
+               state->m_Regs[AGC1_1] = (state->m_Regs[AGC1_1] & ~0x0F) | 
StandardParams->m_AGC1_1;
+               CHK_ERROR(update_reg(state, AGC1_1));
+
+               state->m_Regs[AGC2_1] = (state->m_Regs[AGC2_1] & ~0x0F) | 
StandardParams->m_AGC2_1;
+               CHK_ERROR(update_reg(state, AGC2_1));
+
+               state->m_Regs[RF_AGC_1] &= ~0xEF;
+               if( Frequency < 291000000 )
+                       state->m_Regs[RF_AGC_1] |= 
StandardParams->m_RF_AGC_1_Low;
+               else
+                       state->m_Regs[RF_AGC_1] |= 
StandardParams->m_RF_AGC_1_High;
+               CHK_ERROR(update_reg(state, RF_AGC_1));
+
+               state->m_Regs[IR_MIXER_1] = (state->m_Regs[IR_MIXER_1] & ~0x0F) 
| StandardParams->m_IR_MIXER_1;
+               CHK_ERROR(update_reg(state, IR_MIXER_1));
+
+               state->m_Regs[AGC5_1] = (state->m_Regs[AGC5_1] & ~0x1F) | 
StandardParams->m_AGC5_1;
+               CHK_ERROR(update_reg(state, AGC5_1));
+
+               state->m_Regs[AGCK_1] = (state->m_Regs[AGCK_1] & ~0x0F) | 
StandardParams->m_AGCK_1;
+               CHK_ERROR(update_reg(state, AGCK_1));
+
+               state->m_Regs[PSM_1] = (state->m_Regs[PSM_1] & ~0x20) | 
StandardParams->m_PSM_1;
+               CHK_ERROR(update_reg(state, PSM_1));
+
+               state->m_Regs[IF_FREQUENCY_1] = ( StandardParams->m_IFFrequency 
/ 50000 );
+               CHK_ERROR(update_reg(state, IF_FREQUENCY_1));
+
+               if( state->m_isMaster && StandardParams->m_LTO_STO_immune )
+               {
+                       u8 tmp;
+                       u8 RF_Filter_Gain;
+
+                       CHK_ERROR(read_reg(state, RF_AGC_GAIN_1,&tmp));
+                       RF_Filter_Gain = (tmp & 0x30) >> 4;
+
+                       state->m_Regs[RF_FILTER_1] = 
(state->m_Regs[RF_FILTER_1] & ~0x0C) | (RF_Filter_Gain << 2);
+                       CHK_ERROR(update_reg(state, RF_FILTER_1));
+
+                       state->m_Regs[RF_FILTER_1] |= 0x10;    // Force
+                       CHK_ERROR(update_reg(state, RF_FILTER_1));
+
+                       while( RF_Filter_Gain != 0 )
+                       {
+                               RF_Filter_Gain -= 1;
+                               state->m_Regs[RF_FILTER_1] = 
(state->m_Regs[RF_FILTER_1] & ~0x0C) | (RF_Filter_Gain << 2);
+                               CHK_ERROR(update_reg(state, RF_FILTER_1));
+                               msleep(10);
+                       }
+                       CHK_ERROR(status);
+
+                       state->m_Regs[RF_AGC_1] |=  0x08;
+                       CHK_ERROR(update_reg(state, RF_AGC_1));
+               }
+
+               // 
---------------------------------------------------------------------------------
+
+               state->m_Regs[IRQ_CLEAR] |= 0x80; // Reset IRQ
+               CHK_ERROR(update_reg(state, IRQ_CLEAR));
+
+               CHK_ERROR(PowerOn(state));
+
+               state->m_Regs[RF_FREQUENCY_1] = ((f >> 16) & 0xFF);
+               state->m_Regs[RF_FREQUENCY_2] = ((f >>  8) & 0xFF);
+               state->m_Regs[RF_FREQUENCY_3] = ((f      ) & 0xFF);
+               CHK_ERROR(update_regs(state, RF_FREQUENCY_1,RF_FREQUENCY_3));
+
+               state->m_Regs[MSM_1] = 0x41; // Tune
+               state->m_Regs[MSM_2] = 0x01; // Start MSM
+               CHK_ERROR(update_regs(state, MSM_1, MSM_2));
+               state->m_Regs[MSM_2] = 0x00;
+
+               while(true)
+               {
+                       CHK_ERROR(read_reg(state, IRQ_STATUS, &IRQ));
+                       if( (IRQ & 0x80) != 0 ) break;
+                       Timeout -= 1;
+                       if (Timeout == 0) {
+                               status = -1;
+                               break;
+                       }
+                       msleep(10);
+               }
+               CHK_ERROR(status);
+
+               // 
---------------------------------------------------------------------------------
+
+               if( state->m_isMaster && StandardParams->m_LTO_STO_immune )
+               {
+                       state->m_Regs[RF_AGC_1] &=  ~0x08;
+                       CHK_ERROR(update_reg(state, RF_AGC_1));
+
+                       msleep(50);
+
+                       state->m_Regs[RF_FILTER_1] &= ~0x10;    // remove force
+                       CHK_ERROR(update_reg(state, RF_FILTER_1));
+               }
+
+               // 
---------------------------------------------------------------------------------
+               //  Spur reduction
+
+               if( Frequency < 72000000 )
+               {
+                       state->m_Regs[REFERENCE] |= 0x40;  // Set digital clock
+               }
+               else if( Frequency < 104000000 )
+               {
+                       state->m_Regs[REFERENCE] &= ~0x40;  // Clear digital 
clock
+               }
+               else if( Frequency < 120000000 )
+               {
+                       state->m_Regs[REFERENCE] |= 0x40;  // Set digital clock
+               }
+               else
+               {
+                       if( fDelta <= 8000000 )
+                       {
+                               if( fRatio & 1 ) state->m_Regs[REFERENCE] &= 
~0x40;  // Clear digital clock
+                               else             state->m_Regs[REFERENCE] |= 
0x40;  // Set digital clock
+                       }
+                       else
+                       {
+                               if( fRatio & 1 ) state->m_Regs[REFERENCE] |= 
0x40;  // Set digital clock
+                               else             state->m_Regs[REFERENCE] &= 
~0x40;  // Clear digital clock
+                       }
+
+               }
+               CHK_ERROR(update_reg(state, REFERENCE));
+
+               if( StandardParams->m_AGC1_Freeze && state->m_bEnableFreeze )
+               {
+                       u8 tmp;
+                       int AGC1GainMin = 0;
+                       int nSteps = 10;
+                       int Step  = 0;
+
+                       CHK_ERROR(read_reg(state, AGC1_2,&tmp));
+
+                       if( (tmp & 0x80) == 0 )
+                       {
+                               state->m_Regs[AGC1_2] |= 0x80;         // Loop 
off
+                               CHK_ERROR(update_reg(state, AGC1_2));
+                               state->m_Regs[AGC1_2] |= 0x10 ;        // Force 
gain
+                               CHK_ERROR(update_reg(state, AGC1_2));
+                       }
+                       // Adapt
+                       if( state->m_Regs[AGC1_1] & 0x40 ) // AGC1_6_15dB set
+                       {
+                               AGC1GainMin = 6;
+                               nSteps = 4;
+                       }
+                       while( Step < nSteps )
+                       {
+                               int Down = 0;
+                               int Up = 0, i;
+                               u8 AGC1_Gain;
+
+                               Step = Step + 1;
+
+                               for (i = 0; i < 40; i += 1) {
+                                       CHK_ERROR(read_reg(state, AGC_DET_OUT, 
&tmp));
+                                       Up   += (tmp & 0x02) ?  1 : -4;
+                                       Down += (tmp & 0x01) ? 14 : -1;
+                                       msleep(1);
+                               }
+                               CHK_ERROR(status);
+                               AGC1_Gain = (state->m_Regs[AGC1_2] & 0x0F);
+                               if( Up >= 15 && AGC1_Gain != 9 )
+                               {
+                                       state->m_Regs[AGC1_2] = ( 
state->m_Regs[AGC1_2] & ~0x0F ) | (AGC1_Gain + 1);
+                                       CHK_ERROR(update_reg(state, AGC1_2));
+                               }
+                               else if ( Down >= 10 && AGC1_Gain != 
AGC1GainMin )
+                               {
+                                       state->m_Regs[AGC1_2] = ( 
state->m_Regs[AGC1_2] & ~0x0F ) | (AGC1_Gain - 1);
+                                       CHK_ERROR(update_reg(state, AGC1_2));
+                               }
+                               else
+                               {
+                                       Step = nSteps;
+                               }
+                       }
+               }
+               else
+               {
+                       state->m_Regs[AGC1_2] &= ~0x10 ;       // unforce gain
+                       CHK_ERROR(update_reg(state, AGC1_2));
+                       state->m_Regs[AGC1_2] &= ~0x80;         // Loop on
+                       CHK_ERROR(update_reg(state, AGC1_2));
+               }
+
+               state->m_Standard = Standard;
+               state->m_Frequency = Frequency;
+
+               if( state->m_bPowerMeasurement )
+                       PowerMeasurement(state, &state->m_LastPowerLevel);
+       } while(0);
+
+       return status;
+}
+
+static int sleep(struct dvb_frontend* fe)
+{
+       struct tda_state *state = fe->tuner_priv;
+
+       Standby(state);
+       return 0;
+}
+
+static int init(struct dvb_frontend* fe)
+{
+       //struct tda_state *state = fe->tuner_priv;
+       return 0;
+}
+
+static int release(struct dvb_frontend* fe)
+{
+       kfree(fe->tuner_priv);
+       fe->tuner_priv = NULL;
+       return 0;
+}
+
+#ifndef USE_API3
+static int set_params(struct dvb_frontend *fe)
+{
+       struct tda_state *state = fe->tuner_priv;
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+       int status = 0;
+       int Standard;
+       u32 bw;
+
+       bw = (p->bandwidth_hz + 999999) / 1000000;
+       state->m_Frequency = p->frequency;
+       if (p->delivery_system == SYS_DVBT ||
+           p->delivery_system == SYS_DVBT2 ||
+           p->delivery_system == SYS_ISDBT ||
+           p->delivery_system == SYS_DVBC2) {
+               switch (bw) {
+               case 6:
+                       Standard = HF_DVBT_6MHZ;
+                       break;
+               case 7:
+                       Standard = HF_DVBT_7MHZ;
+                       break;
+               default:
+               case 8:
+                       Standard = HF_DVBT_8MHZ;
+                       break;
+               }
+       } else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
+               switch (bw) {
+               case 6:
+                       Standard = HF_DVBC_6MHZ;
+                       break;
+               case 7:
+                       Standard = HF_DVBC_7MHZ;
+                       break;
+               default:
+               case 8:
+                       Standard = HF_DVBC_8MHZ;
+                       break;
+               }
+       } else
+               return -EINVAL;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+       SetFrequency(state, state->m_Frequency, Standard);
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+
+       return status;
+}
+#else
+static int set_params(struct dvb_frontend *fe,
+                     struct dvb_frontend_parameters *params)
+{
+       struct tda_state *state = fe->tuner_priv;
+       int status = 0;
+       int Standard;
+
+       state->m_Frequency = params->frequency;
+
+       if (fe->ops.info.type == FE_OFDM)
+               switch (params->u.ofdm.bandwidth) {
+               case BANDWIDTH_6_MHZ:
+                       Standard = HF_DVBT_6MHZ;
+                       break;
+               case BANDWIDTH_7_MHZ:
+                       Standard = HF_DVBT_7MHZ;
+                       break;
+               default:
+               case BANDWIDTH_8_MHZ:
+                       Standard = HF_DVBT_8MHZ;
+                       break;
+               }
+       else if (fe->ops.info.type == FE_QAM) {
+               Standard = HF_DVBC_8MHZ;
+       } else
+               return -EINVAL;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+       SetFrequency(state, state->m_Frequency, Standard);
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+
+       return status;
+}
+#endif
+
+static int get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+       struct tda_state *state = fe->tuner_priv;
+
+       *frequency = state->IF;
+       return 0;
+}
+
+static int get_rf_strength(struct dvb_frontend *fe, u16 *st)
+{
+       struct tda_state *state = fe->tuner_priv;
+
+       *st = state->m_LastPowerLevel;
+       return 0;
+}
+
+static int get_if(struct dvb_frontend *fe, u32 *frequency)
+{
+       struct tda_state *state = fe->tuner_priv;
+
+       state->IF = 0;
+       if (state->m_Standard < HF_DVBT_6MHZ ||
+           state->m_Standard > HF_DVBC_8MHZ)
+               return 0;
+       state->IF = m_StandardTable[state->m_Standard - 
HF_DVBT_6MHZ].m_IFFrequency;
+       *frequency = state->IF;
+       return 0;
+}
+
+static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+       //struct tda_state *state = fe->tuner_priv;
+       //*bandwidth = priv->bandwidth;
+       return 0;
+}
+
+
+static struct dvb_tuner_ops tuner_ops = {
+       .info = {
+               .name = "NXP TDA18212",
+               .frequency_min  =  47125000,
+               .frequency_max  = 865000000,
+               .frequency_step =     62500
+       },
+       .init              = init,
+       .sleep             = sleep,
+       .set_params        = set_params,
+       .release           = release,
+       .get_frequency     = get_frequency,
+       .get_if_frequency  = get_if,
+       .get_bandwidth     = get_bandwidth,
+       .get_rf_strength   = get_rf_strength,
+};
+
+struct dvb_frontend *tda18212dd_attach(struct dvb_frontend *fe,
+                                      struct i2c_adapter *i2c, u8 adr)
+{
+       struct tda_state *state;
+       int stat;
+
+       state = kzalloc(sizeof(struct tda_state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+       state->adr = adr;
+       state->i2c = i2c;
+       memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops));
+       init_state(state);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+       stat = attach_init(state);
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+       if (stat < 0) {
+               kfree(state);
+               return 0;
+       }
+       fe->tuner_priv = state;
+       return fe;
+}
+
+EXPORT_SYMBOL_GPL(tda18212dd_attach);
+MODULE_DESCRIPTION("TDA18212 driver");
+MODULE_AUTHOR("DD");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb-frontends/tda18212dd.h 
b/drivers/media/dvb-frontends/tda18212dd.h
new file mode 100644
index 0000000..e276eff
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda18212dd.h
@@ -0,0 +1,37 @@
+/*
+ *  tda18212dd.h: Driver for the TDA18212 tuner
+ *
+ *  Copyright (C) 2010-2013 Digital Devices GmbH
+ *  Copyright (C) 2013 Maik Broemme <mbroe...@parallels.com>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  version 2 only, as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA
+ */
+
+#ifndef _TDA18212DD_H_
+#define _TDA18212DD_H_
+
+#if IS_ENABLED(CONFIG_DVB_TDA18212DD)
+struct dvb_frontend *tda18212dd_attach(struct dvb_frontend *fe,
+                                      struct i2c_adapter *i2c, u8 adr);
+#else
+static inline struct dvb_frontend *tda18212dd_attach(struct dvb_frontend *fe,
+                                                    struct i2c_adapter *i2c, 
u8 adr);
+{
+        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+        return NULL;
+}
+#endif
+
+#endif
-- 
1.8.4.2
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to