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