[PATCH 04/12] tda18212dd: Support for NXP TDA18212 (DD) silicon tuner

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <mbroemme@xxxxxxxxxxxxx>
---
 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 <mbroemme@xxxxxxxxxxxxx>
+ *
+ *  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 <mbroemme@xxxxxxxxxxxxx>
+ *
+ *  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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux