2011-07-04 00:36:17 +08:00
|
|
|
/*
|
|
|
|
* tda18271c2dd: Driver for the TDA18271C2 tuner
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Digital Devices GmbH
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2016-10-28 19:31:20 +08:00
|
|
|
* To obtain the license, point your browser to
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
2011-07-04 00:36:17 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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 <asm/div64.h>
|
|
|
|
|
2017-12-29 02:03:51 +08:00
|
|
|
#include <media/dvb_frontend.h>
|
2012-10-27 22:28:06 +08:00
|
|
|
#include "tda18271c2dd.h"
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2013-11-02 16:05:18 +08:00
|
|
|
/* Max transfer size done by I2C transfer functions */
|
|
|
|
#define MAX_XFER_SIZE 64
|
|
|
|
|
2011-07-04 00:36:17 +08:00
|
|
|
struct SStandardParam {
|
|
|
|
s32 m_IFFrequency;
|
|
|
|
u32 m_BandWidth;
|
|
|
|
u8 m_EP3_4_0;
|
|
|
|
u8 m_EB22;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SMap {
|
|
|
|
u32 m_Frequency;
|
|
|
|
u8 m_Param;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SMapI {
|
|
|
|
u32 m_Frequency;
|
|
|
|
s32 m_Param;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SMap2 {
|
|
|
|
u32 m_Frequency;
|
|
|
|
u8 m_Param1;
|
|
|
|
u8 m_Param2;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SRFBandMap {
|
|
|
|
u32 m_RF_max;
|
|
|
|
u32 m_RF1_Default;
|
|
|
|
u32 m_RF2_Default;
|
|
|
|
u32 m_RF3_Default;
|
|
|
|
};
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
enum ERegister {
|
2011-07-04 00:36:17 +08:00
|
|
|
ID = 0,
|
|
|
|
TM,
|
|
|
|
PL,
|
|
|
|
EP1, EP2, EP3, EP4, EP5,
|
|
|
|
CPD, CD1, CD2, CD3,
|
|
|
|
MPD, MD1, MD2, MD3,
|
|
|
|
EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10,
|
|
|
|
EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20,
|
|
|
|
EB21, EB22, EB23,
|
|
|
|
NUM_REGS
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tda_state {
|
|
|
|
struct i2c_adapter *i2c;
|
|
|
|
u8 adr;
|
|
|
|
|
|
|
|
u32 m_Frequency;
|
|
|
|
u32 IF;
|
|
|
|
|
|
|
|
u8 m_IFLevelAnalog;
|
|
|
|
u8 m_IFLevelDigital;
|
|
|
|
u8 m_IFLevelDVBC;
|
|
|
|
u8 m_IFLevelDVBT;
|
|
|
|
|
|
|
|
u8 m_EP4;
|
|
|
|
u8 m_EP3_Standby;
|
|
|
|
|
|
|
|
bool m_bMaster;
|
|
|
|
|
|
|
|
s32 m_SettlingTime;
|
|
|
|
|
|
|
|
u8 m_Regs[NUM_REGS];
|
|
|
|
|
|
|
|
/* Tracking filter settings for band 0..6 */
|
|
|
|
u32 m_RF1[7];
|
|
|
|
s32 m_RF_A1[7];
|
|
|
|
s32 m_RF_B1[7];
|
|
|
|
u32 m_RF2[7];
|
|
|
|
s32 m_RF_A2[7];
|
|
|
|
s32 m_RF_B2[7];
|
|
|
|
u32 m_RF3[7];
|
|
|
|
|
|
|
|
u8 m_TMValue_RFCal; /* Calibration temperatur */
|
|
|
|
|
|
|
|
bool m_bFMInput; /* true to use Pin 8 for FM Radio */
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
static int PowerScan(struct tda_state *state,
|
2011-07-04 00:37:31 +08:00
|
|
|
u8 RFBand, u32 RF_in,
|
|
|
|
u32 *pRF_Out, bool *pbcal);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
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,
|
2011-07-04 00:37:31 +08:00
|
|
|
.buf = data, .len = len} };
|
2011-07-04 00:36:17 +08:00
|
|
|
return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2011-07-05 01:00:25 +08:00
|
|
|
printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr);
|
2011-07-04 00:36:17 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WriteRegs(struct tda_state *state,
|
|
|
|
u8 SubAddr, u8 *Regs, u16 nRegs)
|
|
|
|
{
|
2013-11-02 16:05:18 +08:00
|
|
|
u8 data[MAX_XFER_SIZE];
|
|
|
|
|
|
|
|
if (1 + nRegs > sizeof(data)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: i2c wr: len=%d is too big!\n",
|
|
|
|
KBUILD_MODNAME, nRegs);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
data[0] = SubAddr;
|
|
|
|
memcpy(data + 1, Regs, nRegs);
|
2013-11-02 16:05:18 +08:00
|
|
|
return i2c_write(state->i2c, state->adr, data, nRegs + 1);
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
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, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ReadExtented(struct tda_state *state, u8 * Regs)
|
|
|
|
{
|
|
|
|
return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS);
|
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
return WriteRegs(state, RegFrom,
|
|
|
|
&state->m_Regs[RegFrom], RegTo-RegFrom+1);
|
|
|
|
}
|
|
|
|
static int UpdateReg(struct tda_state *state, u8 Reg)
|
|
|
|
{
|
2011-07-04 00:37:31 +08:00
|
|
|
return WriteReg(state, Reg, state->m_Regs[Reg]);
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "tda18271c2dd_maps.h"
|
|
|
|
|
|
|
|
static void reset(struct tda_state *state)
|
|
|
|
{
|
|
|
|
u32 ulIFLevelAnalog = 0;
|
|
|
|
u32 ulIFLevelDigital = 2;
|
|
|
|
u32 ulIFLevelDVBC = 7;
|
|
|
|
u32 ulIFLevelDVBT = 6;
|
|
|
|
u32 ulXTOut = 0;
|
2011-07-04 00:37:31 +08:00
|
|
|
u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */
|
2011-07-04 00:36:17 +08:00
|
|
|
u32 ulSlave = 0;
|
|
|
|
u32 ulFMInput = 0;
|
|
|
|
u32 ulSettlingTime = 100;
|
|
|
|
|
|
|
|
state->m_Frequency = 0;
|
|
|
|
state->m_SettlingTime = 100;
|
|
|
|
state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2;
|
|
|
|
state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2;
|
|
|
|
state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2;
|
|
|
|
state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2;
|
|
|
|
|
|
|
|
state->m_EP4 = 0x20;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (ulXTOut != 0)
|
|
|
|
state->m_EP4 |= 0x40;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F;
|
|
|
|
state->m_bMaster = (ulSlave == 0);
|
|
|
|
|
|
|
|
state->m_SettlingTime = ulSettlingTime;
|
|
|
|
|
|
|
|
state->m_bFMInput = (ulFMInput == 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SearchMap1(struct SMap Map[],
|
|
|
|
u32 Frequency, u8 *pParam)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency))
|
2011-07-04 00:36:17 +08:00
|
|
|
i += 1;
|
|
|
|
if (Map[i].m_Frequency == 0)
|
|
|
|
return false;
|
|
|
|
*pParam = Map[i].m_Param;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SearchMap2(struct SMapI Map[],
|
|
|
|
u32 Frequency, s32 *pParam)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while ((Map[i].m_Frequency != 0) &&
|
2011-07-04 00:37:31 +08:00
|
|
|
(Frequency > Map[i].m_Frequency))
|
2011-07-04 00:36:17 +08:00
|
|
|
i += 1;
|
|
|
|
if (Map[i].m_Frequency == 0)
|
|
|
|
return false;
|
|
|
|
*pParam = Map[i].m_Param;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
static bool SearchMap3(struct SMap2 Map[], u32 Frequency,
|
2011-07-04 00:36:17 +08:00
|
|
|
u8 *pParam1, u8 *pParam2)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while ((Map[i].m_Frequency != 0) &&
|
2011-07-04 00:37:31 +08:00
|
|
|
(Frequency > Map[i].m_Frequency))
|
2011-07-04 00:36:17 +08:00
|
|
|
i += 1;
|
|
|
|
if (Map[i].m_Frequency == 0)
|
|
|
|
return false;
|
|
|
|
*pParam1 = Map[i].m_Param1;
|
|
|
|
*pParam2 = Map[i].m_Param2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SearchMap4(struct SRFBandMap Map[],
|
|
|
|
u32 Frequency, u8 *pRFBand)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (i < 7 && (Frequency > Map[i].m_RF_max))
|
|
|
|
i += 1;
|
|
|
|
if (i == 7)
|
|
|
|
return false;
|
|
|
|
*pRFBand = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ThermometerRead(struct tda_state *state, u8 *pTM_Value)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
u8 Regs[16];
|
|
|
|
state->m_Regs[TM] |= 0x10;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, TM);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = Read(state, Regs);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) ||
|
|
|
|
((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) {
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[TM] ^= 0x20;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, TM);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(10);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = Read(state, Regs);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
2011-07-04 00:37:31 +08:00
|
|
|
*pTM_Value = (Regs[TM] & 0x20)
|
|
|
|
? m_Thermometer_Map_2[Regs[TM] & 0x0F]
|
|
|
|
: m_Thermometer_Map_1[Regs[TM] & 0x0F] ;
|
|
|
|
state->m_Regs[TM] &= ~0x10; /* Thermometer off */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, TM);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int StandBy(struct tda_state *state)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
do {
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB12);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB18);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP3] = state->m_EP3_Standby;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EB21, EB23);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CalcMainPLL(struct tda_state *state, u32 freq)
|
|
|
|
{
|
|
|
|
|
|
|
|
u8 PostDiv;
|
|
|
|
u8 Div;
|
|
|
|
u64 OscFreq;
|
|
|
|
u32 MainDiv;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div))
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
OscFreq = (u64) freq * (u64) Div;
|
|
|
|
OscFreq *= (u64) 16384;
|
|
|
|
do_div(OscFreq, (u64)16000000);
|
|
|
|
MainDiv = OscFreq;
|
|
|
|
|
|
|
|
state->m_Regs[MPD] = PostDiv & 0x77;
|
|
|
|
state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F);
|
|
|
|
state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF);
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[MD3] = (MainDiv & 0xFF);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
return UpdateRegs(state, MPD, MD3);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CalcCalPLL(struct tda_state *state, u32 freq)
|
|
|
|
{
|
|
|
|
u8 PostDiv;
|
|
|
|
u8 Div;
|
|
|
|
u64 OscFreq;
|
|
|
|
u32 CalDiv;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div))
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
OscFreq = (u64)freq * (u64)Div;
|
2011-07-04 00:37:31 +08:00
|
|
|
/* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
|
|
|
|
OscFreq *= (u64)16384;
|
2011-07-04 00:36:17 +08:00
|
|
|
do_div(OscFreq, (u64)16000000);
|
2011-07-04 00:37:31 +08:00
|
|
|
CalDiv = OscFreq;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
state->m_Regs[CPD] = PostDiv;
|
|
|
|
state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF);
|
|
|
|
state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF);
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[CD3] = (CalDiv & 0xFF);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
return UpdateRegs(state, CPD, CD3);
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int CalibrateRF(struct tda_state *state,
|
2011-07-04 00:37:31 +08:00
|
|
|
u8 RFBand, u32 freq, s32 *pCprog)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
u8 Regs[NUM_REGS];
|
|
|
|
do {
|
2011-07-04 00:37:31 +08:00
|
|
|
u8 BP_Filter = 0;
|
|
|
|
u8 GainTaper = 0;
|
|
|
|
u8 RFC_K = 0;
|
|
|
|
u8 RFC_M = 0;
|
|
|
|
|
|
|
|
state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB18);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
|
|
|
|
/* Switching off LT (as datasheet says) causes calibration on C1 to fail */
|
|
|
|
/* (Readout of Cprog is allways 255) */
|
|
|
|
if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */
|
|
|
|
state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */
|
|
|
|
|
|
|
|
if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) &&
|
|
|
|
SearchMap1(m_GainTaper_Map, freq, &GainTaper) &&
|
|
|
|
SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M)))
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter;
|
|
|
|
state->m_Regs[EP2] = (RFBand << 5) | GainTaper;
|
|
|
|
|
|
|
|
state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2);
|
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP1, EP3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EB13);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB7);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB14);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB20);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP4, EP5);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalcCalPLL(state, freq);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = CalcMainPLL(state, freq + 1000000);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB7);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(10);
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB20);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(60);
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */
|
|
|
|
state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */
|
|
|
|
state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB18);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateRegs(state, EP3, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = ReadExtented(state, Regs);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
*pCprog = Regs[EB14];
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int RFTrackingFiltersInit(struct tda_state *state,
|
|
|
|
u8 RFBand)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default;
|
|
|
|
u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default;
|
|
|
|
u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default;
|
|
|
|
bool bcal = false;
|
|
|
|
|
|
|
|
s32 Cprog_cal1 = 0;
|
|
|
|
s32 Cprog_table1 = 0;
|
|
|
|
s32 Cprog_cal2 = 0;
|
|
|
|
s32 Cprog_table2 = 0;
|
|
|
|
s32 Cprog_cal3 = 0;
|
|
|
|
s32 Cprog_table3 = 0;
|
|
|
|
|
|
|
|
state->m_RF_A1[RFBand] = 0;
|
|
|
|
state->m_RF_B1[RFBand] = 0;
|
|
|
|
state->m_RF_A2[RFBand] = 0;
|
|
|
|
state->m_RF_B2[RFBand] = 0;
|
|
|
|
|
|
|
|
do {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = PowerScan(state, RFBand, RF1, &RF1, &bcal);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (bcal) {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
2011-07-04 00:37:31 +08:00
|
|
|
SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1);
|
|
|
|
if (!bcal)
|
2011-07-04 00:36:17 +08:00
|
|
|
Cprog_cal1 = Cprog_table1;
|
|
|
|
state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1;
|
2011-07-04 00:37:31 +08:00
|
|
|
/* state->m_RF_A1[RF_Band] = ???? */
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (RF2 == 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = PowerScan(state, RFBand, RF2, &RF2, &bcal);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (bcal) {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
2011-07-04 00:37:31 +08:00
|
|
|
SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2);
|
|
|
|
if (!bcal)
|
2011-07-04 00:36:17 +08:00
|
|
|
Cprog_cal2 = Cprog_table2;
|
|
|
|
|
|
|
|
state->m_RF_A1[RFBand] =
|
|
|
|
(Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) /
|
2011-07-04 00:37:31 +08:00
|
|
|
((s32)(RF2) - (s32)(RF1));
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (RF3 == 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = PowerScan(state, RFBand, RF3, &RF3, &bcal);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (bcal) {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
2011-07-04 00:37:31 +08:00
|
|
|
SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3);
|
|
|
|
if (!bcal)
|
2011-07-04 00:36:17 +08:00
|
|
|
Cprog_cal3 = Cprog_table3;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2));
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
state->m_RF1[RFBand] = RF1;
|
|
|
|
state->m_RF2[RFBand] = RF2;
|
|
|
|
state->m_RF3[RFBand] = RF3;
|
|
|
|
|
|
|
|
#if 0
|
2011-07-05 01:00:25 +08:00
|
|
|
printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__,
|
2011-07-04 00:37:31 +08:00
|
|
|
RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2,
|
|
|
|
state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3);
|
2011-07-04 00:36:17 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PowerScan(struct tda_state *state,
|
2011-07-04 00:37:31 +08:00
|
|
|
u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
2011-07-04 00:37:31 +08:00
|
|
|
int status = 0;
|
|
|
|
do {
|
|
|
|
u8 Gain_Taper = 0;
|
|
|
|
s32 RFC_Cprog = 0;
|
|
|
|
u8 CID_Target = 0;
|
|
|
|
u8 CountLimit = 0;
|
|
|
|
u32 freq_MainPLL;
|
|
|
|
u8 Regs[NUM_REGS];
|
|
|
|
u8 CID_Gain;
|
|
|
|
s32 Count = 0;
|
|
|
|
int sign = 1;
|
|
|
|
bool wait = false;
|
|
|
|
|
|
|
|
if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) &&
|
|
|
|
SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) &&
|
|
|
|
SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) {
|
|
|
|
|
2011-07-05 01:00:25 +08:00
|
|
|
printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__);
|
2011-07-04 00:37:31 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper;
|
|
|
|
state->m_Regs[EB14] = (RFC_Cprog);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EB14);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
|
|
|
|
freq_MainPLL = RF_in + 1000000;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalcMainPLL(state, freq_MainPLL);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
msleep(5);
|
|
|
|
state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP2); /* Launch power measurement */
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = ReadExtented(state, Regs);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
CID_Gain = Regs[EB10] & 0x3F;
|
|
|
|
state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workarround in CalibrateRF) */
|
|
|
|
|
|
|
|
*pRF_Out = RF_in;
|
|
|
|
|
|
|
|
while (CID_Gain < CID_Target) {
|
|
|
|
freq_MainPLL = RF_in + sign * Count + 1000000;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalcMainPLL(state, freq_MainPLL);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
msleep(wait ? 5 : 1);
|
|
|
|
wait = false;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2); /* Launch power measurement */
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = ReadExtented(state, Regs);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
CID_Gain = Regs[EB10] & 0x3F;
|
|
|
|
Count += 200000;
|
|
|
|
|
|
|
|
if (Count < CountLimit * 100000)
|
|
|
|
continue;
|
|
|
|
if (sign < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sign = -sign;
|
|
|
|
Count = 200000;
|
|
|
|
wait = true;
|
|
|
|
}
|
2011-07-04 05:12:26 +08:00
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (CID_Gain >= CID_Target) {
|
|
|
|
*pbcal = true;
|
|
|
|
*pRF_Out = freq_MainPLL - 1000000;
|
|
|
|
} else
|
|
|
|
*pbcal = false;
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
return status;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int PowerScanInit(struct tda_state *state)
|
|
|
|
{
|
|
|
|
int status = 0;
|
2011-07-04 00:37:31 +08:00
|
|
|
do {
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB18);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */
|
|
|
|
state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EB21, EB23);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CalcRFFilterCurve(struct tda_state *state)
|
|
|
|
{
|
|
|
|
int status = 0;
|
2011-07-04 00:37:31 +08:00
|
|
|
do {
|
|
|
|
msleep(200); /* Temperature stabilisation */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = PowerScanInit(state);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 0);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 5);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = RFTrackingFiltersInit(state, 6);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int FixedContentsI2CUpdate(struct tda_state *state)
|
|
|
|
{
|
|
|
|
static u8 InitRegs[] = {
|
2011-07-04 00:37:31 +08:00
|
|
|
0x08, 0x80, 0xC6,
|
|
|
|
0xDF, 0x16, 0x60, 0x80,
|
|
|
|
0x80, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00,
|
|
|
|
0xFC, 0x01, 0x84, 0x41,
|
|
|
|
0x01, 0x84, 0x40, 0x07,
|
|
|
|
0x00, 0x00, 0x96, 0x3F,
|
|
|
|
0xC1, 0x00, 0x8F, 0x00,
|
|
|
|
0x00, 0x8C, 0x00, 0x20,
|
|
|
|
0xB3, 0x48, 0xB0,
|
2011-07-04 00:36:17 +08:00
|
|
|
};
|
|
|
|
int status = 0;
|
2011-07-04 00:37:31 +08:00
|
|
|
memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1);
|
2011-07-04 00:36:17 +08:00
|
|
|
do {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, TM, EB23);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* AGC1 gain setup */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EB17] = 0x00;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB17);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EB17] = 0x03;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB17);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EB17] = 0x43;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB17);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EB17] = 0x4C;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB17);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* IRC Cal Low band */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP3] = 0x1F;
|
|
|
|
state->m_Regs[EP4] = 0x66;
|
|
|
|
state->m_Regs[EP5] = 0x81;
|
|
|
|
state->m_Regs[CPD] = 0xCC;
|
|
|
|
state->m_Regs[CD1] = 0x6C;
|
|
|
|
state->m_Regs[CD2] = 0x00;
|
|
|
|
state->m_Regs[CD3] = 0x00;
|
|
|
|
state->m_Regs[MPD] = 0xC5;
|
|
|
|
state->m_Regs[MD1] = 0x77;
|
|
|
|
state->m_Regs[MD2] = 0x08;
|
|
|
|
state->m_Regs[MD3] = 0x00;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
#if 0
|
|
|
|
state->m_Regs[EB4] = 0x61; /* missing in sw */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
msleep(1);
|
|
|
|
state->m_Regs[EB4] = 0x41;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
#endif
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
|
|
|
|
|
|
|
state->m_Regs[EP5] = 0x85;
|
|
|
|
state->m_Regs[CPD] = 0xCB;
|
|
|
|
state->m_Regs[CD1] = 0x66;
|
|
|
|
state->m_Regs[CD2] = 0x70;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, CD3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(30);
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* IRC Cal mid band */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP5] = 0x82;
|
|
|
|
state->m_Regs[CPD] = 0xA8;
|
|
|
|
state->m_Regs[CD2] = 0x00;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[MD1] = 0x73;
|
|
|
|
state->m_Regs[MD2] = 0x1A;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, MD3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
|
|
|
|
|
|
|
state->m_Regs[EP5] = 0x86;
|
|
|
|
state->m_Regs[CPD] = 0xA8;
|
|
|
|
state->m_Regs[CD1] = 0x66;
|
|
|
|
state->m_Regs[CD2] = 0xA0;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, CD3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(30);
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* IRC Cal high band */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP5] = 0x83;
|
|
|
|
state->m_Regs[CPD] = 0x98;
|
|
|
|
state->m_Regs[CD1] = 0x65;
|
|
|
|
state->m_Regs[CD2] = 0x00;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[MD1] = 0x71;
|
|
|
|
state->m_Regs[MD2] = 0xCD;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, MD3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
|
|
|
state->m_Regs[EP5] = 0x87;
|
|
|
|
state->m_Regs[CD1] = 0x65;
|
|
|
|
state->m_Regs[CD2] = 0x50;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EP3, CD3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(5);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP2);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(30);
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* Back to normal */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP4] = 0x64;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateReg(state, EP1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int InitCal(struct tda_state *state)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
do {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = FixedContentsI2CUpdate(state);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = CalcRFFilterCurve(state);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = StandBy(state);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
/* m_bInitDone = true; */
|
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int RFTrackingFiltersCorrection(struct tda_state *state,
|
|
|
|
u32 Frequency)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
s32 Cprog_table;
|
|
|
|
u8 RFBand;
|
|
|
|
u8 dCoverdT;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) ||
|
|
|
|
!SearchMap4(m_RF_Band_Map, Frequency, &RFBand) ||
|
|
|
|
!SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT))
|
|
|
|
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
do {
|
2011-07-04 00:36:17 +08:00
|
|
|
u8 TMValue_Current;
|
|
|
|
u32 RF1 = state->m_RF1[RFBand];
|
|
|
|
u32 RF2 = state->m_RF1[RFBand];
|
|
|
|
u32 RF3 = state->m_RF1[RFBand];
|
|
|
|
s32 RF_A1 = state->m_RF_A1[RFBand];
|
|
|
|
s32 RF_B1 = state->m_RF_B1[RFBand];
|
|
|
|
s32 RF_A2 = state->m_RF_A2[RFBand];
|
|
|
|
s32 RF_B2 = state->m_RF_B2[RFBand];
|
|
|
|
s32 Capprox = 0;
|
|
|
|
int TComp;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP3] &= ~0xE0; /* Power up */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = ThermometerRead(state, &TMValue_Current);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (RF3 == 0 || Frequency < RF2)
|
2011-07-04 00:36:17 +08:00
|
|
|
Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table;
|
|
|
|
else
|
|
|
|
Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table;
|
|
|
|
|
|
|
|
TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000;
|
|
|
|
|
|
|
|
Capprox += TComp;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (Capprox < 0)
|
|
|
|
Capprox = 0;
|
|
|
|
else if (Capprox > 255)
|
|
|
|
Capprox = 255;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* TODO Temperature compensation. There is defenitely a scale factor */
|
|
|
|
/* missing in the datasheet, so leave it out for now. */
|
|
|
|
state->m_Regs[EB14] = Capprox;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB14);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ChannelConfiguration(struct tda_state *state,
|
|
|
|
u32 Frequency, int Standard)
|
|
|
|
{
|
|
|
|
|
|
|
|
s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
u8 BP_Filter = 0;
|
|
|
|
u8 RF_Band = 0;
|
|
|
|
u8 GainTaper = 0;
|
2011-07-04 05:06:07 +08:00
|
|
|
u8 IR_Meas = 0;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->IF = IntermediateFrequency;
|
2011-07-05 01:00:25 +08:00
|
|
|
/* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */
|
2011-07-04 00:37:31 +08:00
|
|
|
/* get values from tables */
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) &&
|
|
|
|
SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) &&
|
|
|
|
SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) &&
|
|
|
|
SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) {
|
|
|
|
|
2011-07-05 01:00:25 +08:00
|
|
|
printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__);
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
do {
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */
|
|
|
|
|
|
|
|
/* m_EP4 default for XToutOn, CAL_Mode (0) */
|
|
|
|
state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog);
|
|
|
|
/* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */
|
|
|
|
if (Standard <= HF_AnalogMax)
|
|
|
|
state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog;
|
|
|
|
else if (Standard <= HF_ATSC)
|
|
|
|
state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT;
|
|
|
|
else if (Standard <= HF_DVBC)
|
|
|
|
state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC;
|
|
|
|
else
|
|
|
|
state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
if ((Standard == HF_FM_Radio) && state->m_bFMInput)
|
2014-02-06 15:49:47 +08:00
|
|
|
state->m_Regs[EP4] |= 0x80;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
state->m_Regs[MPD] &= ~0x80;
|
2011-07-04 00:37:31 +08:00
|
|
|
if (Standard > HF_AnalogMax)
|
|
|
|
state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */
|
2011-07-04 00:36:17 +08:00
|
|
|
|
|
|
|
state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22;
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
/* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */
|
|
|
|
if (Standard == HF_FM_Radio)
|
|
|
|
state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
|
|
|
|
else
|
|
|
|
state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateRegs(state, EB22, EB23);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas;
|
|
|
|
state->m_Regs[EP2] = (RF_Band << 5) | GainTaper;
|
|
|
|
|
|
|
|
state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) |
|
2011-07-04 00:37:31 +08:00
|
|
|
(state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */
|
|
|
|
/* AGC1_always_master = 0 */
|
|
|
|
/* AGC_firstn = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB1);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
|
|
|
|
if (state->m_bMaster) {
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalcMainPLL(state, Frequency + IntermediateFrequency);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateRegs(state, TM, EP5);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(1);
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB4);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:37:31 +08:00
|
|
|
} else {
|
2011-07-04 05:06:07 +08:00
|
|
|
u8 PostDiv = 0;
|
2011-07-04 00:36:17 +08:00
|
|
|
u8 Div;
|
2011-07-04 05:12:26 +08:00
|
|
|
status = CalcCalPLL(state, Frequency + IntermediateFrequency);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div);
|
2011-07-04 00:36:17 +08:00
|
|
|
state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77);
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, MPD);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
status = UpdateRegs(state, TM, EP5);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB7);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
msleep(1);
|
2011-07-04 00:37:31 +08:00
|
|
|
state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EB7);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
|
|
|
msleep(20);
|
2011-07-04 00:37:31 +08:00
|
|
|
if (Standard != HF_FM_Radio)
|
|
|
|
state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */
|
2011-07-04 05:12:26 +08:00
|
|
|
status = UpdateReg(state, EP3);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
static int sleep(struct dvb_frontend *fe)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
struct tda_state *state = fe->tuner_priv;
|
|
|
|
|
|
|
|
StandBy(state);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
static int init(struct dvb_frontend *fe)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-19 06:30:51 +08:00
|
|
|
static void release(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
kfree(fe->tuner_priv);
|
|
|
|
fe->tuner_priv = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-24 23:24:33 +08:00
|
|
|
static int set_params(struct dvb_frontend *fe)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
struct tda_state *state = fe->tuner_priv;
|
|
|
|
int status = 0;
|
|
|
|
int Standard;
|
2011-12-18 07:36:57 +08:00
|
|
|
u32 bw = fe->dtv_property_cache.bandwidth_hz;
|
|
|
|
u32 delsys = fe->dtv_property_cache.delivery_system;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-12-18 07:36:57 +08:00
|
|
|
state->m_Frequency = fe->dtv_property_cache.frequency;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-12-18 07:36:57 +08:00
|
|
|
switch (delsys) {
|
|
|
|
case SYS_DVBT:
|
|
|
|
case SYS_DVBT2:
|
|
|
|
switch (bw) {
|
|
|
|
case 6000000:
|
2011-07-04 00:36:17 +08:00
|
|
|
Standard = HF_DVBT_6MHZ;
|
|
|
|
break;
|
2011-12-18 07:36:57 +08:00
|
|
|
case 7000000:
|
2011-07-04 00:36:17 +08:00
|
|
|
Standard = HF_DVBT_7MHZ;
|
|
|
|
break;
|
2011-12-18 07:36:57 +08:00
|
|
|
case 8000000:
|
2011-07-04 00:36:17 +08:00
|
|
|
Standard = HF_DVBT_8MHZ;
|
|
|
|
break;
|
2011-12-18 07:36:57 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
2011-07-04 00:36:17 +08:00
|
|
|
}
|
2011-12-18 07:36:57 +08:00
|
|
|
case SYS_DVBC_ANNEX_A:
|
|
|
|
case SYS_DVBC_ANNEX_C:
|
2011-11-12 06:26:03 +08:00
|
|
|
if (bw <= 6000000)
|
[media] tda18271c2dd: Fix saw filter configuration for DVB-C @6MHz
Currently, the driver assumes that all QAM carriers are spaced with
8MHz. This is wrong, and may decrease QoS on Countries like Brazil,
that have DVB-C carriers with 6MHz-spaced.
Fortunately, both ITU-T J-83 and EN 300 429 specifies a way to
associate the symbol rate with the bandwidth needed for it.
For ITU-T J-83 2007 annex A, the maximum symbol rate for 6 MHz is:
6 MHz / 1.15 = 5217391 Bauds
For ITU-T J-83 2007 annex C, the maximum symbol rate for 6 MHz is:
6 MHz / 1.13 = 5309735 Bauds.
As this tuner is currently used only for DRX-K, and it is currently
hard-coded to annex A, I've opted to use the roll-off factor of 0.15,
instead of 0.13.
If we ever support annex C, the better would be to add a DVB S2API
call to allow changing between Annex A and C, and add the 0.13 roll-off
factor to it.
This code is currently being used on other frontends, so I think we
should later add a core function with this code, to warrant that
it will be properly implemented everywhere.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-07-29 02:49:43 +08:00
|
|
|
Standard = HF_DVBC_6MHZ;
|
2011-11-12 06:26:03 +08:00
|
|
|
else if (bw <= 7000000)
|
|
|
|
Standard = HF_DVBC_7MHZ;
|
[media] tda18271c2dd: Fix saw filter configuration for DVB-C @6MHz
Currently, the driver assumes that all QAM carriers are spaced with
8MHz. This is wrong, and may decrease QoS on Countries like Brazil,
that have DVB-C carriers with 6MHz-spaced.
Fortunately, both ITU-T J-83 and EN 300 429 specifies a way to
associate the symbol rate with the bandwidth needed for it.
For ITU-T J-83 2007 annex A, the maximum symbol rate for 6 MHz is:
6 MHz / 1.15 = 5217391 Bauds
For ITU-T J-83 2007 annex C, the maximum symbol rate for 6 MHz is:
6 MHz / 1.13 = 5309735 Bauds.
As this tuner is currently used only for DRX-K, and it is currently
hard-coded to annex A, I've opted to use the roll-off factor of 0.15,
instead of 0.13.
If we ever support annex C, the better would be to add a DVB S2API
call to allow changing between Annex A and C, and add the 0.13 roll-off
factor to it.
This code is currently being used on other frontends, so I think we
should later add a core function with this code, to warrant that
it will be properly implemented everywhere.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-07-29 02:49:43 +08:00
|
|
|
else
|
|
|
|
Standard = HF_DVBC_8MHZ;
|
2011-12-31 01:34:51 +08:00
|
|
|
break;
|
2011-12-18 07:36:57 +08:00
|
|
|
default:
|
2011-07-04 00:36:17 +08:00
|
|
|
return -EINVAL;
|
2011-12-18 07:36:57 +08:00
|
|
|
}
|
2011-07-04 00:36:17 +08:00
|
|
|
do {
|
2011-12-18 07:36:57 +08:00
|
|
|
status = RFTrackingFiltersCorrection(state, state->m_Frequency);
|
2011-07-04 05:12:26 +08:00
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-12-18 07:36:57 +08:00
|
|
|
status = ChannelConfiguration(state, state->m_Frequency,
|
|
|
|
Standard);
|
2011-07-04 05:12:26 +08:00
|
|
|
if (status < 0)
|
|
|
|
break;
|
2011-07-04 00:36:17 +08:00
|
|
|
|
2011-07-04 00:37:31 +08:00
|
|
|
msleep(state->m_SettlingTime); /* Allow AGC's to settle down */
|
|
|
|
} while (0);
|
2011-07-04 00:36:17 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
2011-07-04 00:37:31 +08:00
|
|
|
static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
2011-07-04 00:37:31 +08:00
|
|
|
if (IFAgc < 500) {
|
|
|
|
/* Scale this from 0 to 50000 */
|
2011-07-04 00:36:17 +08:00
|
|
|
*pSignalStrength = IFAgc * 100;
|
|
|
|
} else {
|
2011-07-04 00:37:31 +08:00
|
|
|
/* Scale range 500-1500 to 50000-80000 */
|
2011-07-04 00:36:17 +08:00
|
|
|
*pSignalStrength = 50000 + (IFAgc - 500) * 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
[media] dvb-core, tda18271c2dd: define get_if_frequency() callback
Tuners in general convert a high frequency carrier into an Intermediate
Frequency (IF).
Digital tuners like tda18271, xc3028, etc. generally allow changing the IF
frequency, although they generally have recommented settings for the IF.
Analog tuners, have a fixed IF frequency, that depends on the physical
characteristics of some analog components.
For digital tuners, it makes sense to have ways to configure IF,
via the tuner's configuration structure, like what's done inside the
tda18271-fe maps.
The demods need to know what IF is used by the tuner, as it will need
to convert internally from IF into baseband. Currently, the bridge driver
needs to fill a per-demod configuration struct for it, or pass it via
a dvb_attach parameter.
The tda18271 datasheet recommends to use different IF's for different
delivery system types and for different bandwidths.
The DRX-K demod also needs to know the IF frequency in order to work,
just like all other demods. However, as it accepts different delivery
systems (DVB-C and DVB-T), the IF may change if the standard and/or
bandwidth is changed.
So, the usual procedure of passing it via a config struct doesn't work.
One might try to code it as two separate IF frequencies, or even as a
table in function of the delivery system and the bandwidth, but this
will be messy.
So, it is better and simpler to just add a new callback for it and
require the tuners that can be used with MFE frontends like drx-k
to implement a new callback to return the used IF.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Antti Palosaari <crope@iki.fi>
2011-09-03 22:40:02 +08:00
|
|
|
static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
|
2011-07-04 00:36:17 +08:00
|
|
|
{
|
|
|
|
struct tda_state *state = fe->tuner_priv;
|
|
|
|
|
|
|
|
*frequency = state->IF;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
|
|
|
|
{
|
2011-07-04 00:37:31 +08:00
|
|
|
/* struct tda_state *state = fe->tuner_priv; */
|
|
|
|
/* *bandwidth = priv->bandwidth; */
|
2011-07-04 00:36:17 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-11 22:44:12 +08:00
|
|
|
static const struct dvb_tuner_ops tuner_ops = {
|
2011-07-04 00:36:17 +08:00
|
|
|
.info = {
|
|
|
|
.name = "NXP TDA18271C2D",
|
|
|
|
.frequency_min = 47125000,
|
|
|
|
.frequency_max = 865000000,
|
|
|
|
.frequency_step = 62500
|
|
|
|
},
|
|
|
|
.init = init,
|
|
|
|
.sleep = sleep,
|
|
|
|
.set_params = set_params,
|
2016-11-19 06:30:51 +08:00
|
|
|
.release = release,
|
[media] dvb-core, tda18271c2dd: define get_if_frequency() callback
Tuners in general convert a high frequency carrier into an Intermediate
Frequency (IF).
Digital tuners like tda18271, xc3028, etc. generally allow changing the IF
frequency, although they generally have recommented settings for the IF.
Analog tuners, have a fixed IF frequency, that depends on the physical
characteristics of some analog components.
For digital tuners, it makes sense to have ways to configure IF,
via the tuner's configuration structure, like what's done inside the
tda18271-fe maps.
The demods need to know what IF is used by the tuner, as it will need
to convert internally from IF into baseband. Currently, the bridge driver
needs to fill a per-demod configuration struct for it, or pass it via
a dvb_attach parameter.
The tda18271 datasheet recommends to use different IF's for different
delivery system types and for different bandwidths.
The DRX-K demod also needs to know the IF frequency in order to work,
just like all other demods. However, as it accepts different delivery
systems (DVB-C and DVB-T), the IF may change if the standard and/or
bandwidth is changed.
So, the usual procedure of passing it via a config struct doesn't work.
One might try to code it as two separate IF frequencies, or even as a
table in function of the delivery system and the bandwidth, but this
will be messy.
So, it is better and simpler to just add a new callback for it and
require the tuners that can be used with MFE frontends like drx-k
to implement a new callback to return the used IF.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Antti Palosaari <crope@iki.fi>
2011-09-03 22:40:02 +08:00
|
|
|
.get_if_frequency = get_if_frequency,
|
2011-07-04 00:36:17 +08:00
|
|
|
.get_bandwidth = get_bandwidth,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
|
|
|
|
struct i2c_adapter *i2c, u8 adr)
|
|
|
|
{
|
|
|
|
struct tda_state *state;
|
|
|
|
|
|
|
|
state = kzalloc(sizeof(struct tda_state), GFP_KERNEL);
|
|
|
|
if (!state)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
fe->tuner_priv = state;
|
|
|
|
state->adr = adr;
|
|
|
|
state->i2c = i2c;
|
|
|
|
memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops));
|
|
|
|
reset(state);
|
|
|
|
InitCal(state);
|
|
|
|
|
|
|
|
return fe;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tda18271c2dd_attach);
|
2011-07-04 00:37:31 +08:00
|
|
|
|
2011-07-04 00:36:17 +08:00
|
|
|
MODULE_DESCRIPTION("TDA18271C2 driver");
|
|
|
|
MODULE_AUTHOR("DD");
|
|
|
|
MODULE_LICENSE("GPL");
|