189ee7f4fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29a0bf528SMauro Carvalho Chehab /* 39a0bf528SMauro Carvalho Chehab * tda18271c2dd: Driver for the TDA18271C2 tuner 49a0bf528SMauro Carvalho Chehab * 59a0bf528SMauro Carvalho Chehab * Copyright (C) 2010 Digital Devices GmbH 69a0bf528SMauro Carvalho Chehab */ 79a0bf528SMauro Carvalho Chehab 89a0bf528SMauro Carvalho Chehab #include <linux/kernel.h> 99a0bf528SMauro Carvalho Chehab #include <linux/module.h> 109a0bf528SMauro Carvalho Chehab #include <linux/init.h> 119a0bf528SMauro Carvalho Chehab #include <linux/delay.h> 129a0bf528SMauro Carvalho Chehab #include <linux/firmware.h> 139a0bf528SMauro Carvalho Chehab #include <linux/i2c.h> 149a0bf528SMauro Carvalho Chehab #include <asm/div64.h> 159a0bf528SMauro Carvalho Chehab 16fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h> 174e373217SMauro Carvalho Chehab #include "tda18271c2dd.h" 189a0bf528SMauro Carvalho Chehab 198393796dSMauro Carvalho Chehab /* Max transfer size done by I2C transfer functions */ 208393796dSMauro Carvalho Chehab #define MAX_XFER_SIZE 64 218393796dSMauro Carvalho Chehab 229a0bf528SMauro Carvalho Chehab struct SStandardParam { 239a0bf528SMauro Carvalho Chehab s32 m_IFFrequency; 249a0bf528SMauro Carvalho Chehab u32 m_BandWidth; 259a0bf528SMauro Carvalho Chehab u8 m_EP3_4_0; 269a0bf528SMauro Carvalho Chehab u8 m_EB22; 279a0bf528SMauro Carvalho Chehab }; 289a0bf528SMauro Carvalho Chehab 299a0bf528SMauro Carvalho Chehab struct SMap { 309a0bf528SMauro Carvalho Chehab u32 m_Frequency; 319a0bf528SMauro Carvalho Chehab u8 m_Param; 329a0bf528SMauro Carvalho Chehab }; 339a0bf528SMauro Carvalho Chehab 349a0bf528SMauro Carvalho Chehab struct SMapI { 359a0bf528SMauro Carvalho Chehab u32 m_Frequency; 369a0bf528SMauro Carvalho Chehab s32 m_Param; 379a0bf528SMauro Carvalho Chehab }; 389a0bf528SMauro Carvalho Chehab 399a0bf528SMauro Carvalho Chehab struct SMap2 { 409a0bf528SMauro Carvalho Chehab u32 m_Frequency; 419a0bf528SMauro Carvalho Chehab u8 m_Param1; 429a0bf528SMauro Carvalho Chehab u8 m_Param2; 439a0bf528SMauro Carvalho Chehab }; 449a0bf528SMauro Carvalho Chehab 459a0bf528SMauro Carvalho Chehab struct SRFBandMap { 469a0bf528SMauro Carvalho Chehab u32 m_RF_max; 479a0bf528SMauro Carvalho Chehab u32 m_RF1_Default; 489a0bf528SMauro Carvalho Chehab u32 m_RF2_Default; 499a0bf528SMauro Carvalho Chehab u32 m_RF3_Default; 509a0bf528SMauro Carvalho Chehab }; 519a0bf528SMauro Carvalho Chehab 529a0bf528SMauro Carvalho Chehab enum ERegister { 539a0bf528SMauro Carvalho Chehab ID = 0, 549a0bf528SMauro Carvalho Chehab TM, 559a0bf528SMauro Carvalho Chehab PL, 569a0bf528SMauro Carvalho Chehab EP1, EP2, EP3, EP4, EP5, 579a0bf528SMauro Carvalho Chehab CPD, CD1, CD2, CD3, 589a0bf528SMauro Carvalho Chehab MPD, MD1, MD2, MD3, 599a0bf528SMauro Carvalho Chehab EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10, 609a0bf528SMauro Carvalho Chehab EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20, 619a0bf528SMauro Carvalho Chehab EB21, EB22, EB23, 629a0bf528SMauro Carvalho Chehab NUM_REGS 639a0bf528SMauro Carvalho Chehab }; 649a0bf528SMauro Carvalho Chehab 659a0bf528SMauro Carvalho Chehab struct tda_state { 669a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c; 679a0bf528SMauro Carvalho Chehab u8 adr; 689a0bf528SMauro Carvalho Chehab 699a0bf528SMauro Carvalho Chehab u32 m_Frequency; 709a0bf528SMauro Carvalho Chehab u32 IF; 719a0bf528SMauro Carvalho Chehab 729a0bf528SMauro Carvalho Chehab u8 m_IFLevelAnalog; 739a0bf528SMauro Carvalho Chehab u8 m_IFLevelDigital; 749a0bf528SMauro Carvalho Chehab u8 m_IFLevelDVBC; 759a0bf528SMauro Carvalho Chehab u8 m_IFLevelDVBT; 769a0bf528SMauro Carvalho Chehab 779a0bf528SMauro Carvalho Chehab u8 m_EP4; 789a0bf528SMauro Carvalho Chehab u8 m_EP3_Standby; 799a0bf528SMauro Carvalho Chehab 809a0bf528SMauro Carvalho Chehab bool m_bMaster; 819a0bf528SMauro Carvalho Chehab 829a0bf528SMauro Carvalho Chehab s32 m_SettlingTime; 839a0bf528SMauro Carvalho Chehab 849a0bf528SMauro Carvalho Chehab u8 m_Regs[NUM_REGS]; 859a0bf528SMauro Carvalho Chehab 869a0bf528SMauro Carvalho Chehab /* Tracking filter settings for band 0..6 */ 879a0bf528SMauro Carvalho Chehab u32 m_RF1[7]; 889a0bf528SMauro Carvalho Chehab s32 m_RF_A1[7]; 899a0bf528SMauro Carvalho Chehab s32 m_RF_B1[7]; 909a0bf528SMauro Carvalho Chehab u32 m_RF2[7]; 919a0bf528SMauro Carvalho Chehab s32 m_RF_A2[7]; 929a0bf528SMauro Carvalho Chehab s32 m_RF_B2[7]; 939a0bf528SMauro Carvalho Chehab u32 m_RF3[7]; 949a0bf528SMauro Carvalho Chehab 95868c9a17SMauro Carvalho Chehab u8 m_TMValue_RFCal; /* Calibration temperature */ 969a0bf528SMauro Carvalho Chehab 979a0bf528SMauro Carvalho Chehab bool m_bFMInput; /* true to use Pin 8 for FM Radio */ 989a0bf528SMauro Carvalho Chehab 999a0bf528SMauro Carvalho Chehab }; 1009a0bf528SMauro Carvalho Chehab 1019a0bf528SMauro Carvalho Chehab static int PowerScan(struct tda_state *state, 1029a0bf528SMauro Carvalho Chehab u8 RFBand, u32 RF_in, 1039a0bf528SMauro Carvalho Chehab u32 *pRF_Out, bool *pbcal); 1049a0bf528SMauro Carvalho Chehab 1059a0bf528SMauro Carvalho Chehab static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len) 1069a0bf528SMauro Carvalho Chehab { 1079a0bf528SMauro Carvalho Chehab struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, 1089a0bf528SMauro Carvalho Chehab .buf = data, .len = len} }; 1099a0bf528SMauro Carvalho Chehab return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; 1109a0bf528SMauro Carvalho Chehab } 1119a0bf528SMauro Carvalho Chehab 1129a0bf528SMauro Carvalho Chehab static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) 1139a0bf528SMauro Carvalho Chehab { 1149a0bf528SMauro Carvalho Chehab struct i2c_msg msg = {.addr = adr, .flags = 0, 1159a0bf528SMauro Carvalho Chehab .buf = data, .len = len}; 1169a0bf528SMauro Carvalho Chehab 1179a0bf528SMauro Carvalho Chehab if (i2c_transfer(adap, &msg, 1) != 1) { 1189a0bf528SMauro Carvalho Chehab printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr); 1199a0bf528SMauro Carvalho Chehab return -1; 1209a0bf528SMauro Carvalho Chehab } 1219a0bf528SMauro Carvalho Chehab return 0; 1229a0bf528SMauro Carvalho Chehab } 1239a0bf528SMauro Carvalho Chehab 1249a0bf528SMauro Carvalho Chehab static int WriteRegs(struct tda_state *state, 1259a0bf528SMauro Carvalho Chehab u8 SubAddr, u8 *Regs, u16 nRegs) 1269a0bf528SMauro Carvalho Chehab { 1278393796dSMauro Carvalho Chehab u8 data[MAX_XFER_SIZE]; 1288393796dSMauro Carvalho Chehab 1298393796dSMauro Carvalho Chehab if (1 + nRegs > sizeof(data)) { 1308393796dSMauro Carvalho Chehab printk(KERN_WARNING 1318393796dSMauro Carvalho Chehab "%s: i2c wr: len=%d is too big!\n", 1328393796dSMauro Carvalho Chehab KBUILD_MODNAME, nRegs); 1338393796dSMauro Carvalho Chehab return -EINVAL; 1348393796dSMauro Carvalho Chehab } 1359a0bf528SMauro Carvalho Chehab 1369a0bf528SMauro Carvalho Chehab data[0] = SubAddr; 1379a0bf528SMauro Carvalho Chehab memcpy(data + 1, Regs, nRegs); 1389a0bf528SMauro Carvalho Chehab return i2c_write(state->i2c, state->adr, data, nRegs + 1); 1399a0bf528SMauro Carvalho Chehab } 1409a0bf528SMauro Carvalho Chehab 1419a0bf528SMauro Carvalho Chehab static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg) 1429a0bf528SMauro Carvalho Chehab { 1439a0bf528SMauro Carvalho Chehab u8 msg[2] = {SubAddr, Reg}; 1449a0bf528SMauro Carvalho Chehab 1459a0bf528SMauro Carvalho Chehab return i2c_write(state->i2c, state->adr, msg, 2); 1469a0bf528SMauro Carvalho Chehab } 1479a0bf528SMauro Carvalho Chehab 1489a0bf528SMauro Carvalho Chehab static int Read(struct tda_state *state, u8 * Regs) 1499a0bf528SMauro Carvalho Chehab { 1509a0bf528SMauro Carvalho Chehab return i2c_readn(state->i2c, state->adr, Regs, 16); 1519a0bf528SMauro Carvalho Chehab } 1529a0bf528SMauro Carvalho Chehab 1539a0bf528SMauro Carvalho Chehab static int ReadExtented(struct tda_state *state, u8 * Regs) 1549a0bf528SMauro Carvalho Chehab { 1559a0bf528SMauro Carvalho Chehab return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS); 1569a0bf528SMauro Carvalho Chehab } 1579a0bf528SMauro Carvalho Chehab 1589a0bf528SMauro Carvalho Chehab static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo) 1599a0bf528SMauro Carvalho Chehab { 1609a0bf528SMauro Carvalho Chehab return WriteRegs(state, RegFrom, 1619a0bf528SMauro Carvalho Chehab &state->m_Regs[RegFrom], RegTo-RegFrom+1); 1629a0bf528SMauro Carvalho Chehab } 1639a0bf528SMauro Carvalho Chehab static int UpdateReg(struct tda_state *state, u8 Reg) 1649a0bf528SMauro Carvalho Chehab { 1659a0bf528SMauro Carvalho Chehab return WriteReg(state, Reg, state->m_Regs[Reg]); 1669a0bf528SMauro Carvalho Chehab } 1679a0bf528SMauro Carvalho Chehab 1689a0bf528SMauro Carvalho Chehab #include "tda18271c2dd_maps.h" 1699a0bf528SMauro Carvalho Chehab 1709a0bf528SMauro Carvalho Chehab static void reset(struct tda_state *state) 1719a0bf528SMauro Carvalho Chehab { 1729a0bf528SMauro Carvalho Chehab u32 ulIFLevelAnalog = 0; 1739a0bf528SMauro Carvalho Chehab u32 ulIFLevelDigital = 2; 1749a0bf528SMauro Carvalho Chehab u32 ulIFLevelDVBC = 7; 1759a0bf528SMauro Carvalho Chehab u32 ulIFLevelDVBT = 6; 1769a0bf528SMauro Carvalho Chehab u32 ulXTOut = 0; 1779a0bf528SMauro Carvalho Chehab u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */ 1789a0bf528SMauro Carvalho Chehab u32 ulSlave = 0; 1799a0bf528SMauro Carvalho Chehab u32 ulFMInput = 0; 1809a0bf528SMauro Carvalho Chehab u32 ulSettlingTime = 100; 1819a0bf528SMauro Carvalho Chehab 1829a0bf528SMauro Carvalho Chehab state->m_Frequency = 0; 1839a0bf528SMauro Carvalho Chehab state->m_SettlingTime = 100; 1849a0bf528SMauro Carvalho Chehab state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2; 1859a0bf528SMauro Carvalho Chehab state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2; 1869a0bf528SMauro Carvalho Chehab state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2; 1879a0bf528SMauro Carvalho Chehab state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2; 1889a0bf528SMauro Carvalho Chehab 1899a0bf528SMauro Carvalho Chehab state->m_EP4 = 0x20; 1909a0bf528SMauro Carvalho Chehab if (ulXTOut != 0) 1919a0bf528SMauro Carvalho Chehab state->m_EP4 |= 0x40; 1929a0bf528SMauro Carvalho Chehab 1939a0bf528SMauro Carvalho Chehab state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F; 1949a0bf528SMauro Carvalho Chehab state->m_bMaster = (ulSlave == 0); 1959a0bf528SMauro Carvalho Chehab 1969a0bf528SMauro Carvalho Chehab state->m_SettlingTime = ulSettlingTime; 1979a0bf528SMauro Carvalho Chehab 1989a0bf528SMauro Carvalho Chehab state->m_bFMInput = (ulFMInput == 2); 1999a0bf528SMauro Carvalho Chehab } 2009a0bf528SMauro Carvalho Chehab 2012d4ffef1SRikard Falkeborn static bool SearchMap1(const struct SMap map[], u32 frequency, u8 *param) 2029a0bf528SMauro Carvalho Chehab { 2039a0bf528SMauro Carvalho Chehab int i = 0; 2049a0bf528SMauro Carvalho Chehab 2052d4ffef1SRikard Falkeborn while ((map[i].m_Frequency != 0) && (frequency > map[i].m_Frequency)) 2069a0bf528SMauro Carvalho Chehab i += 1; 2072d4ffef1SRikard Falkeborn if (map[i].m_Frequency == 0) 2089a0bf528SMauro Carvalho Chehab return false; 2092d4ffef1SRikard Falkeborn *param = map[i].m_Param; 2109a0bf528SMauro Carvalho Chehab return true; 2119a0bf528SMauro Carvalho Chehab } 2129a0bf528SMauro Carvalho Chehab 2132d4ffef1SRikard Falkeborn static bool SearchMap2(const struct SMapI map[], u32 frequency, s32 *param) 2149a0bf528SMauro Carvalho Chehab { 2159a0bf528SMauro Carvalho Chehab int i = 0; 2169a0bf528SMauro Carvalho Chehab 2172d4ffef1SRikard Falkeborn while ((map[i].m_Frequency != 0) && 2182d4ffef1SRikard Falkeborn (frequency > map[i].m_Frequency)) 2199a0bf528SMauro Carvalho Chehab i += 1; 2202d4ffef1SRikard Falkeborn if (map[i].m_Frequency == 0) 2219a0bf528SMauro Carvalho Chehab return false; 2222d4ffef1SRikard Falkeborn *param = map[i].m_Param; 2239a0bf528SMauro Carvalho Chehab return true; 2249a0bf528SMauro Carvalho Chehab } 2259a0bf528SMauro Carvalho Chehab 2262d4ffef1SRikard Falkeborn static bool SearchMap3(const struct SMap2 map[], u32 frequency, u8 *param1, 2272d4ffef1SRikard Falkeborn u8 *param2) 2289a0bf528SMauro Carvalho Chehab { 2299a0bf528SMauro Carvalho Chehab int i = 0; 2309a0bf528SMauro Carvalho Chehab 2312d4ffef1SRikard Falkeborn while ((map[i].m_Frequency != 0) && 2322d4ffef1SRikard Falkeborn (frequency > map[i].m_Frequency)) 2339a0bf528SMauro Carvalho Chehab i += 1; 2342d4ffef1SRikard Falkeborn if (map[i].m_Frequency == 0) 2359a0bf528SMauro Carvalho Chehab return false; 2362d4ffef1SRikard Falkeborn *param1 = map[i].m_Param1; 2372d4ffef1SRikard Falkeborn *param2 = map[i].m_Param2; 2389a0bf528SMauro Carvalho Chehab return true; 2399a0bf528SMauro Carvalho Chehab } 2409a0bf528SMauro Carvalho Chehab 2412d4ffef1SRikard Falkeborn static bool SearchMap4(const struct SRFBandMap map[], u32 frequency, u8 *rfband) 2429a0bf528SMauro Carvalho Chehab { 2439a0bf528SMauro Carvalho Chehab int i = 0; 2449a0bf528SMauro Carvalho Chehab 2452d4ffef1SRikard Falkeborn while (i < 7 && (frequency > map[i].m_RF_max)) 2469a0bf528SMauro Carvalho Chehab i += 1; 2479a0bf528SMauro Carvalho Chehab if (i == 7) 2489a0bf528SMauro Carvalho Chehab return false; 2492d4ffef1SRikard Falkeborn *rfband = i; 2509a0bf528SMauro Carvalho Chehab return true; 2519a0bf528SMauro Carvalho Chehab } 2529a0bf528SMauro Carvalho Chehab 2539a0bf528SMauro Carvalho Chehab static int ThermometerRead(struct tda_state *state, u8 *pTM_Value) 2549a0bf528SMauro Carvalho Chehab { 2559a0bf528SMauro Carvalho Chehab int status = 0; 2569a0bf528SMauro Carvalho Chehab 2579a0bf528SMauro Carvalho Chehab do { 2589a0bf528SMauro Carvalho Chehab u8 Regs[16]; 2599a0bf528SMauro Carvalho Chehab state->m_Regs[TM] |= 0x10; 2609a0bf528SMauro Carvalho Chehab status = UpdateReg(state, TM); 2619a0bf528SMauro Carvalho Chehab if (status < 0) 2629a0bf528SMauro Carvalho Chehab break; 2639a0bf528SMauro Carvalho Chehab status = Read(state, Regs); 2649a0bf528SMauro Carvalho Chehab if (status < 0) 2659a0bf528SMauro Carvalho Chehab break; 2669a0bf528SMauro Carvalho Chehab if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) || 2679a0bf528SMauro Carvalho Chehab ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) { 2689a0bf528SMauro Carvalho Chehab state->m_Regs[TM] ^= 0x20; 2699a0bf528SMauro Carvalho Chehab status = UpdateReg(state, TM); 2709a0bf528SMauro Carvalho Chehab if (status < 0) 2719a0bf528SMauro Carvalho Chehab break; 2729a0bf528SMauro Carvalho Chehab msleep(10); 2739a0bf528SMauro Carvalho Chehab status = Read(state, Regs); 2749a0bf528SMauro Carvalho Chehab if (status < 0) 2759a0bf528SMauro Carvalho Chehab break; 2769a0bf528SMauro Carvalho Chehab } 2779a0bf528SMauro Carvalho Chehab *pTM_Value = (Regs[TM] & 0x20) 2789a0bf528SMauro Carvalho Chehab ? m_Thermometer_Map_2[Regs[TM] & 0x0F] 2799a0bf528SMauro Carvalho Chehab : m_Thermometer_Map_1[Regs[TM] & 0x0F] ; 2809a0bf528SMauro Carvalho Chehab state->m_Regs[TM] &= ~0x10; /* Thermometer off */ 2819a0bf528SMauro Carvalho Chehab status = UpdateReg(state, TM); 2829a0bf528SMauro Carvalho Chehab if (status < 0) 2839a0bf528SMauro Carvalho Chehab break; 2849a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */ 2859a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP4); 2869a0bf528SMauro Carvalho Chehab if (status < 0) 2879a0bf528SMauro Carvalho Chehab break; 2889a0bf528SMauro Carvalho Chehab } while (0); 2899a0bf528SMauro Carvalho Chehab 2909a0bf528SMauro Carvalho Chehab return status; 2919a0bf528SMauro Carvalho Chehab } 2929a0bf528SMauro Carvalho Chehab 2939a0bf528SMauro Carvalho Chehab static int StandBy(struct tda_state *state) 2949a0bf528SMauro Carvalho Chehab { 2959a0bf528SMauro Carvalho Chehab int status = 0; 2969a0bf528SMauro Carvalho Chehab do { 2979a0bf528SMauro Carvalho Chehab state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */ 2989a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB12); 2999a0bf528SMauro Carvalho Chehab if (status < 0) 3009a0bf528SMauro Carvalho Chehab break; 3019a0bf528SMauro Carvalho Chehab state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */ 3029a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB18); 3039a0bf528SMauro Carvalho Chehab if (status < 0) 3049a0bf528SMauro Carvalho Chehab break; 3059a0bf528SMauro Carvalho Chehab state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */ 3069a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] = state->m_EP3_Standby; 3079a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP3); 3089a0bf528SMauro Carvalho Chehab if (status < 0) 3099a0bf528SMauro Carvalho Chehab break; 3109a0bf528SMauro Carvalho Chehab state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */ 3119a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EB21, EB23); 3129a0bf528SMauro Carvalho Chehab if (status < 0) 3139a0bf528SMauro Carvalho Chehab break; 3149a0bf528SMauro Carvalho Chehab } while (0); 3159a0bf528SMauro Carvalho Chehab return status; 3169a0bf528SMauro Carvalho Chehab } 3179a0bf528SMauro Carvalho Chehab 3189a0bf528SMauro Carvalho Chehab static int CalcMainPLL(struct tda_state *state, u32 freq) 3199a0bf528SMauro Carvalho Chehab { 3209a0bf528SMauro Carvalho Chehab 3219a0bf528SMauro Carvalho Chehab u8 PostDiv; 3229a0bf528SMauro Carvalho Chehab u8 Div; 3239a0bf528SMauro Carvalho Chehab u64 OscFreq; 3249a0bf528SMauro Carvalho Chehab u32 MainDiv; 3259a0bf528SMauro Carvalho Chehab 3269a0bf528SMauro Carvalho Chehab if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div)) 3279a0bf528SMauro Carvalho Chehab return -EINVAL; 3289a0bf528SMauro Carvalho Chehab 3299a0bf528SMauro Carvalho Chehab OscFreq = (u64) freq * (u64) Div; 3309a0bf528SMauro Carvalho Chehab OscFreq *= (u64) 16384; 331*e9a84463SRicardo Ribalda do_div(OscFreq, 16000000); 3329a0bf528SMauro Carvalho Chehab MainDiv = OscFreq; 3339a0bf528SMauro Carvalho Chehab 3349a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] = PostDiv & 0x77; 3359a0bf528SMauro Carvalho Chehab state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F); 3369a0bf528SMauro Carvalho Chehab state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF); 3379a0bf528SMauro Carvalho Chehab state->m_Regs[MD3] = (MainDiv & 0xFF); 3389a0bf528SMauro Carvalho Chehab 3399a0bf528SMauro Carvalho Chehab return UpdateRegs(state, MPD, MD3); 3409a0bf528SMauro Carvalho Chehab } 3419a0bf528SMauro Carvalho Chehab 3429a0bf528SMauro Carvalho Chehab static int CalcCalPLL(struct tda_state *state, u32 freq) 3439a0bf528SMauro Carvalho Chehab { 3449a0bf528SMauro Carvalho Chehab u8 PostDiv; 3459a0bf528SMauro Carvalho Chehab u8 Div; 3469a0bf528SMauro Carvalho Chehab u64 OscFreq; 3479a0bf528SMauro Carvalho Chehab u32 CalDiv; 3489a0bf528SMauro Carvalho Chehab 3499a0bf528SMauro Carvalho Chehab if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div)) 3509a0bf528SMauro Carvalho Chehab return -EINVAL; 3519a0bf528SMauro Carvalho Chehab 3529a0bf528SMauro Carvalho Chehab OscFreq = (u64)freq * (u64)Div; 3539a0bf528SMauro Carvalho Chehab /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ 3549a0bf528SMauro Carvalho Chehab OscFreq *= (u64)16384; 355*e9a84463SRicardo Ribalda do_div(OscFreq, 16000000); 3569a0bf528SMauro Carvalho Chehab CalDiv = OscFreq; 3579a0bf528SMauro Carvalho Chehab 3589a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = PostDiv; 3599a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF); 3609a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF); 3619a0bf528SMauro Carvalho Chehab state->m_Regs[CD3] = (CalDiv & 0xFF); 3629a0bf528SMauro Carvalho Chehab 3639a0bf528SMauro Carvalho Chehab return UpdateRegs(state, CPD, CD3); 3649a0bf528SMauro Carvalho Chehab } 3659a0bf528SMauro Carvalho Chehab 3669a0bf528SMauro Carvalho Chehab static int CalibrateRF(struct tda_state *state, 3679a0bf528SMauro Carvalho Chehab u8 RFBand, u32 freq, s32 *pCprog) 3689a0bf528SMauro Carvalho Chehab { 3699a0bf528SMauro Carvalho Chehab int status = 0; 3709a0bf528SMauro Carvalho Chehab u8 Regs[NUM_REGS]; 3719a0bf528SMauro Carvalho Chehab do { 3729a0bf528SMauro Carvalho Chehab u8 BP_Filter = 0; 3739a0bf528SMauro Carvalho Chehab u8 GainTaper = 0; 3749a0bf528SMauro Carvalho Chehab u8 RFC_K = 0; 3759a0bf528SMauro Carvalho Chehab u8 RFC_M = 0; 3769a0bf528SMauro Carvalho Chehab 3779a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */ 3789a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP4); 3799a0bf528SMauro Carvalho Chehab if (status < 0) 3809a0bf528SMauro Carvalho Chehab break; 3819a0bf528SMauro Carvalho Chehab state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */ 3829a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB18); 3839a0bf528SMauro Carvalho Chehab if (status < 0) 3849a0bf528SMauro Carvalho Chehab break; 3859a0bf528SMauro Carvalho Chehab 3869a0bf528SMauro Carvalho Chehab /* Switching off LT (as datasheet says) causes calibration on C1 to fail */ 387868c9a17SMauro Carvalho Chehab /* (Readout of Cprog is always 255) */ 3889a0bf528SMauro Carvalho Chehab if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */ 3899a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */ 3909a0bf528SMauro Carvalho Chehab 3919a0bf528SMauro Carvalho Chehab if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) && 3929a0bf528SMauro Carvalho Chehab SearchMap1(m_GainTaper_Map, freq, &GainTaper) && 3939a0bf528SMauro Carvalho Chehab SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M))) 3949a0bf528SMauro Carvalho Chehab return -EINVAL; 3959a0bf528SMauro Carvalho Chehab 3969a0bf528SMauro Carvalho Chehab state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter; 3979a0bf528SMauro Carvalho Chehab state->m_Regs[EP2] = (RFBand << 5) | GainTaper; 3989a0bf528SMauro Carvalho Chehab 3999a0bf528SMauro Carvalho Chehab state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2); 4009a0bf528SMauro Carvalho Chehab 4019a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP1, EP3); 4029a0bf528SMauro Carvalho Chehab if (status < 0) 4039a0bf528SMauro Carvalho Chehab break; 4049a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB13); 4059a0bf528SMauro Carvalho Chehab if (status < 0) 4069a0bf528SMauro Carvalho Chehab break; 4079a0bf528SMauro Carvalho Chehab 4089a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */ 4099a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 4109a0bf528SMauro Carvalho Chehab if (status < 0) 4119a0bf528SMauro Carvalho Chehab break; 4129a0bf528SMauro Carvalho Chehab 4139a0bf528SMauro Carvalho Chehab state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */ 4149a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB7); 4159a0bf528SMauro Carvalho Chehab if (status < 0) 4169a0bf528SMauro Carvalho Chehab break; 4179a0bf528SMauro Carvalho Chehab 4189a0bf528SMauro Carvalho Chehab state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */ 4199a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB14); 4209a0bf528SMauro Carvalho Chehab if (status < 0) 4219a0bf528SMauro Carvalho Chehab break; 4229a0bf528SMauro Carvalho Chehab 4239a0bf528SMauro Carvalho Chehab state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */ 4249a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB20); 4259a0bf528SMauro Carvalho Chehab if (status < 0) 4269a0bf528SMauro Carvalho Chehab break; 4279a0bf528SMauro Carvalho Chehab 4289a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */ 4299a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP4, EP5); 4309a0bf528SMauro Carvalho Chehab if (status < 0) 4319a0bf528SMauro Carvalho Chehab break; 4329a0bf528SMauro Carvalho Chehab 4339a0bf528SMauro Carvalho Chehab status = CalcCalPLL(state, freq); 4349a0bf528SMauro Carvalho Chehab if (status < 0) 4359a0bf528SMauro Carvalho Chehab break; 4369a0bf528SMauro Carvalho Chehab status = CalcMainPLL(state, freq + 1000000); 4379a0bf528SMauro Carvalho Chehab if (status < 0) 4389a0bf528SMauro Carvalho Chehab break; 4399a0bf528SMauro Carvalho Chehab 4409a0bf528SMauro Carvalho Chehab msleep(5); 4419a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 4429a0bf528SMauro Carvalho Chehab if (status < 0) 4439a0bf528SMauro Carvalho Chehab break; 4449a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 4459a0bf528SMauro Carvalho Chehab if (status < 0) 4469a0bf528SMauro Carvalho Chehab break; 4479a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 4489a0bf528SMauro Carvalho Chehab if (status < 0) 4499a0bf528SMauro Carvalho Chehab break; 4509a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 4519a0bf528SMauro Carvalho Chehab if (status < 0) 4529a0bf528SMauro Carvalho Chehab break; 4539a0bf528SMauro Carvalho Chehab 4549a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */ 4559a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 4569a0bf528SMauro Carvalho Chehab if (status < 0) 4579a0bf528SMauro Carvalho Chehab break; 4589a0bf528SMauro Carvalho Chehab 4599a0bf528SMauro Carvalho Chehab state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */ 4609a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB7); 4619a0bf528SMauro Carvalho Chehab if (status < 0) 4629a0bf528SMauro Carvalho Chehab break; 4639a0bf528SMauro Carvalho Chehab msleep(10); 4649a0bf528SMauro Carvalho Chehab 4659a0bf528SMauro Carvalho Chehab state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */ 4669a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB20); 4679a0bf528SMauro Carvalho Chehab if (status < 0) 4689a0bf528SMauro Carvalho Chehab break; 4699a0bf528SMauro Carvalho Chehab msleep(60); 4709a0bf528SMauro Carvalho Chehab 4719a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */ 4729a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */ 4739a0bf528SMauro Carvalho Chehab state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */ 4749a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB18); 4759a0bf528SMauro Carvalho Chehab if (status < 0) 4769a0bf528SMauro Carvalho Chehab break; 4779a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, EP4); 4789a0bf528SMauro Carvalho Chehab if (status < 0) 4799a0bf528SMauro Carvalho Chehab break; 4809a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 4819a0bf528SMauro Carvalho Chehab if (status < 0) 4829a0bf528SMauro Carvalho Chehab break; 4839a0bf528SMauro Carvalho Chehab 4849a0bf528SMauro Carvalho Chehab status = ReadExtented(state, Regs); 4859a0bf528SMauro Carvalho Chehab if (status < 0) 4869a0bf528SMauro Carvalho Chehab break; 4879a0bf528SMauro Carvalho Chehab 4889a0bf528SMauro Carvalho Chehab *pCprog = Regs[EB14]; 4899a0bf528SMauro Carvalho Chehab 4909a0bf528SMauro Carvalho Chehab } while (0); 4919a0bf528SMauro Carvalho Chehab return status; 4929a0bf528SMauro Carvalho Chehab } 4939a0bf528SMauro Carvalho Chehab 4949a0bf528SMauro Carvalho Chehab static int RFTrackingFiltersInit(struct tda_state *state, 4959a0bf528SMauro Carvalho Chehab u8 RFBand) 4969a0bf528SMauro Carvalho Chehab { 4979a0bf528SMauro Carvalho Chehab int status = 0; 4989a0bf528SMauro Carvalho Chehab 4999a0bf528SMauro Carvalho Chehab u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default; 5009a0bf528SMauro Carvalho Chehab u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default; 5019a0bf528SMauro Carvalho Chehab u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default; 5029a0bf528SMauro Carvalho Chehab bool bcal = false; 5039a0bf528SMauro Carvalho Chehab 5049a0bf528SMauro Carvalho Chehab s32 Cprog_cal1 = 0; 5059a0bf528SMauro Carvalho Chehab s32 Cprog_table1 = 0; 5069a0bf528SMauro Carvalho Chehab s32 Cprog_cal2 = 0; 5079a0bf528SMauro Carvalho Chehab s32 Cprog_table2 = 0; 5089a0bf528SMauro Carvalho Chehab s32 Cprog_cal3 = 0; 5099a0bf528SMauro Carvalho Chehab s32 Cprog_table3 = 0; 5109a0bf528SMauro Carvalho Chehab 5119a0bf528SMauro Carvalho Chehab state->m_RF_A1[RFBand] = 0; 5129a0bf528SMauro Carvalho Chehab state->m_RF_B1[RFBand] = 0; 5139a0bf528SMauro Carvalho Chehab state->m_RF_A2[RFBand] = 0; 5149a0bf528SMauro Carvalho Chehab state->m_RF_B2[RFBand] = 0; 5159a0bf528SMauro Carvalho Chehab 5169a0bf528SMauro Carvalho Chehab do { 5179a0bf528SMauro Carvalho Chehab status = PowerScan(state, RFBand, RF1, &RF1, &bcal); 5189a0bf528SMauro Carvalho Chehab if (status < 0) 5199a0bf528SMauro Carvalho Chehab break; 5209a0bf528SMauro Carvalho Chehab if (bcal) { 5219a0bf528SMauro Carvalho Chehab status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1); 5229a0bf528SMauro Carvalho Chehab if (status < 0) 5239a0bf528SMauro Carvalho Chehab break; 5249a0bf528SMauro Carvalho Chehab } 5259a0bf528SMauro Carvalho Chehab SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1); 5269a0bf528SMauro Carvalho Chehab if (!bcal) 5279a0bf528SMauro Carvalho Chehab Cprog_cal1 = Cprog_table1; 5289a0bf528SMauro Carvalho Chehab state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1; 5299a0bf528SMauro Carvalho Chehab /* state->m_RF_A1[RF_Band] = ???? */ 5309a0bf528SMauro Carvalho Chehab 5319a0bf528SMauro Carvalho Chehab if (RF2 == 0) 5329a0bf528SMauro Carvalho Chehab break; 5339a0bf528SMauro Carvalho Chehab 5349a0bf528SMauro Carvalho Chehab status = PowerScan(state, RFBand, RF2, &RF2, &bcal); 5359a0bf528SMauro Carvalho Chehab if (status < 0) 5369a0bf528SMauro Carvalho Chehab break; 5379a0bf528SMauro Carvalho Chehab if (bcal) { 5389a0bf528SMauro Carvalho Chehab status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2); 5399a0bf528SMauro Carvalho Chehab if (status < 0) 5409a0bf528SMauro Carvalho Chehab break; 5419a0bf528SMauro Carvalho Chehab } 5429a0bf528SMauro Carvalho Chehab SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2); 5439a0bf528SMauro Carvalho Chehab if (!bcal) 5449a0bf528SMauro Carvalho Chehab Cprog_cal2 = Cprog_table2; 5459a0bf528SMauro Carvalho Chehab 5469a0bf528SMauro Carvalho Chehab state->m_RF_A1[RFBand] = 5479a0bf528SMauro Carvalho Chehab (Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) / 5489a0bf528SMauro Carvalho Chehab ((s32)(RF2) - (s32)(RF1)); 5499a0bf528SMauro Carvalho Chehab 5509a0bf528SMauro Carvalho Chehab if (RF3 == 0) 5519a0bf528SMauro Carvalho Chehab break; 5529a0bf528SMauro Carvalho Chehab 5539a0bf528SMauro Carvalho Chehab status = PowerScan(state, RFBand, RF3, &RF3, &bcal); 5549a0bf528SMauro Carvalho Chehab if (status < 0) 5559a0bf528SMauro Carvalho Chehab break; 5569a0bf528SMauro Carvalho Chehab if (bcal) { 5579a0bf528SMauro Carvalho Chehab status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3); 5589a0bf528SMauro Carvalho Chehab if (status < 0) 5599a0bf528SMauro Carvalho Chehab break; 5609a0bf528SMauro Carvalho Chehab } 5619a0bf528SMauro Carvalho Chehab SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3); 5629a0bf528SMauro Carvalho Chehab if (!bcal) 5639a0bf528SMauro Carvalho Chehab Cprog_cal3 = Cprog_table3; 5649a0bf528SMauro Carvalho Chehab state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2)); 5659a0bf528SMauro Carvalho Chehab state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2; 5669a0bf528SMauro Carvalho Chehab 5679a0bf528SMauro Carvalho Chehab } while (0); 5689a0bf528SMauro Carvalho Chehab 5699a0bf528SMauro Carvalho Chehab state->m_RF1[RFBand] = RF1; 5709a0bf528SMauro Carvalho Chehab state->m_RF2[RFBand] = RF2; 5719a0bf528SMauro Carvalho Chehab state->m_RF3[RFBand] = RF3; 5729a0bf528SMauro Carvalho Chehab 5739a0bf528SMauro Carvalho Chehab #if 0 5749a0bf528SMauro Carvalho Chehab printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__, 5759a0bf528SMauro Carvalho Chehab RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2, 5769a0bf528SMauro Carvalho Chehab state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3); 5779a0bf528SMauro Carvalho Chehab #endif 5789a0bf528SMauro Carvalho Chehab 5799a0bf528SMauro Carvalho Chehab return status; 5809a0bf528SMauro Carvalho Chehab } 5819a0bf528SMauro Carvalho Chehab 5829a0bf528SMauro Carvalho Chehab static int PowerScan(struct tda_state *state, 5839a0bf528SMauro Carvalho Chehab u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal) 5849a0bf528SMauro Carvalho Chehab { 5859a0bf528SMauro Carvalho Chehab int status = 0; 5869a0bf528SMauro Carvalho Chehab do { 5879a0bf528SMauro Carvalho Chehab u8 Gain_Taper = 0; 5889a0bf528SMauro Carvalho Chehab s32 RFC_Cprog = 0; 5899a0bf528SMauro Carvalho Chehab u8 CID_Target = 0; 5909a0bf528SMauro Carvalho Chehab u8 CountLimit = 0; 5919a0bf528SMauro Carvalho Chehab u32 freq_MainPLL; 5929a0bf528SMauro Carvalho Chehab u8 Regs[NUM_REGS]; 5939a0bf528SMauro Carvalho Chehab u8 CID_Gain; 5949a0bf528SMauro Carvalho Chehab s32 Count = 0; 5959a0bf528SMauro Carvalho Chehab int sign = 1; 5969a0bf528SMauro Carvalho Chehab bool wait = false; 5979a0bf528SMauro Carvalho Chehab 5989a0bf528SMauro Carvalho Chehab if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) && 5999a0bf528SMauro Carvalho Chehab SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) && 6009a0bf528SMauro Carvalho Chehab SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) { 6019a0bf528SMauro Carvalho Chehab 6029a0bf528SMauro Carvalho Chehab printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__); 6039a0bf528SMauro Carvalho Chehab return -EINVAL; 6049a0bf528SMauro Carvalho Chehab } 6059a0bf528SMauro Carvalho Chehab 6069a0bf528SMauro Carvalho Chehab state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper; 6079a0bf528SMauro Carvalho Chehab state->m_Regs[EB14] = (RFC_Cprog); 6089a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 6099a0bf528SMauro Carvalho Chehab if (status < 0) 6109a0bf528SMauro Carvalho Chehab break; 6119a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB14); 6129a0bf528SMauro Carvalho Chehab if (status < 0) 6139a0bf528SMauro Carvalho Chehab break; 6149a0bf528SMauro Carvalho Chehab 6159a0bf528SMauro Carvalho Chehab freq_MainPLL = RF_in + 1000000; 6169a0bf528SMauro Carvalho Chehab status = CalcMainPLL(state, freq_MainPLL); 6179a0bf528SMauro Carvalho Chehab if (status < 0) 6189a0bf528SMauro Carvalho Chehab break; 6199a0bf528SMauro Carvalho Chehab msleep(5); 6209a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */ 6219a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP4); 6229a0bf528SMauro Carvalho Chehab if (status < 0) 6239a0bf528SMauro Carvalho Chehab break; 6249a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); /* Launch power measurement */ 6259a0bf528SMauro Carvalho Chehab if (status < 0) 6269a0bf528SMauro Carvalho Chehab break; 6279a0bf528SMauro Carvalho Chehab status = ReadExtented(state, Regs); 6289a0bf528SMauro Carvalho Chehab if (status < 0) 6299a0bf528SMauro Carvalho Chehab break; 6309a0bf528SMauro Carvalho Chehab CID_Gain = Regs[EB10] & 0x3F; 631868c9a17SMauro Carvalho Chehab state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workaround in CalibrateRF) */ 6329a0bf528SMauro Carvalho Chehab 6339a0bf528SMauro Carvalho Chehab *pRF_Out = RF_in; 6349a0bf528SMauro Carvalho Chehab 6359a0bf528SMauro Carvalho Chehab while (CID_Gain < CID_Target) { 6369a0bf528SMauro Carvalho Chehab freq_MainPLL = RF_in + sign * Count + 1000000; 6379a0bf528SMauro Carvalho Chehab status = CalcMainPLL(state, freq_MainPLL); 6389a0bf528SMauro Carvalho Chehab if (status < 0) 6399a0bf528SMauro Carvalho Chehab break; 6409a0bf528SMauro Carvalho Chehab msleep(wait ? 5 : 1); 6419a0bf528SMauro Carvalho Chehab wait = false; 6429a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); /* Launch power measurement */ 6439a0bf528SMauro Carvalho Chehab if (status < 0) 6449a0bf528SMauro Carvalho Chehab break; 6459a0bf528SMauro Carvalho Chehab status = ReadExtented(state, Regs); 6469a0bf528SMauro Carvalho Chehab if (status < 0) 6479a0bf528SMauro Carvalho Chehab break; 6489a0bf528SMauro Carvalho Chehab CID_Gain = Regs[EB10] & 0x3F; 6499a0bf528SMauro Carvalho Chehab Count += 200000; 6509a0bf528SMauro Carvalho Chehab 6519a0bf528SMauro Carvalho Chehab if (Count < CountLimit * 100000) 6529a0bf528SMauro Carvalho Chehab continue; 6539a0bf528SMauro Carvalho Chehab if (sign < 0) 6549a0bf528SMauro Carvalho Chehab break; 6559a0bf528SMauro Carvalho Chehab 6569a0bf528SMauro Carvalho Chehab sign = -sign; 6579a0bf528SMauro Carvalho Chehab Count = 200000; 6589a0bf528SMauro Carvalho Chehab wait = true; 6599a0bf528SMauro Carvalho Chehab } 6609a0bf528SMauro Carvalho Chehab if (status < 0) 6619a0bf528SMauro Carvalho Chehab break; 6629a0bf528SMauro Carvalho Chehab if (CID_Gain >= CID_Target) { 6639a0bf528SMauro Carvalho Chehab *pbcal = true; 6649a0bf528SMauro Carvalho Chehab *pRF_Out = freq_MainPLL - 1000000; 6659a0bf528SMauro Carvalho Chehab } else 6669a0bf528SMauro Carvalho Chehab *pbcal = false; 6679a0bf528SMauro Carvalho Chehab } while (0); 6689a0bf528SMauro Carvalho Chehab 6699a0bf528SMauro Carvalho Chehab return status; 6709a0bf528SMauro Carvalho Chehab } 6719a0bf528SMauro Carvalho Chehab 6729a0bf528SMauro Carvalho Chehab static int PowerScanInit(struct tda_state *state) 6739a0bf528SMauro Carvalho Chehab { 6749a0bf528SMauro Carvalho Chehab int status = 0; 6759a0bf528SMauro Carvalho Chehab do { 6769a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12; 6779a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */ 6789a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, EP4); 6799a0bf528SMauro Carvalho Chehab if (status < 0) 6809a0bf528SMauro Carvalho Chehab break; 6819a0bf528SMauro Carvalho Chehab state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */ 6829a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB18); 6839a0bf528SMauro Carvalho Chehab if (status < 0) 6849a0bf528SMauro Carvalho Chehab break; 6859a0bf528SMauro Carvalho Chehab state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */ 6869a0bf528SMauro Carvalho Chehab state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ 6879a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EB21, EB23); 6889a0bf528SMauro Carvalho Chehab if (status < 0) 6899a0bf528SMauro Carvalho Chehab break; 6909a0bf528SMauro Carvalho Chehab } while (0); 6919a0bf528SMauro Carvalho Chehab return status; 6929a0bf528SMauro Carvalho Chehab } 6939a0bf528SMauro Carvalho Chehab 6949a0bf528SMauro Carvalho Chehab static int CalcRFFilterCurve(struct tda_state *state) 6959a0bf528SMauro Carvalho Chehab { 6969a0bf528SMauro Carvalho Chehab int status = 0; 6979a0bf528SMauro Carvalho Chehab do { 6989a0bf528SMauro Carvalho Chehab msleep(200); /* Temperature stabilisation */ 6999a0bf528SMauro Carvalho Chehab status = PowerScanInit(state); 7009a0bf528SMauro Carvalho Chehab if (status < 0) 7019a0bf528SMauro Carvalho Chehab break; 7029a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 0); 7039a0bf528SMauro Carvalho Chehab if (status < 0) 7049a0bf528SMauro Carvalho Chehab break; 7059a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 1); 7069a0bf528SMauro Carvalho Chehab if (status < 0) 7079a0bf528SMauro Carvalho Chehab break; 7089a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 2); 7099a0bf528SMauro Carvalho Chehab if (status < 0) 7109a0bf528SMauro Carvalho Chehab break; 7119a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 3); 7129a0bf528SMauro Carvalho Chehab if (status < 0) 7139a0bf528SMauro Carvalho Chehab break; 7149a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 4); 7159a0bf528SMauro Carvalho Chehab if (status < 0) 7169a0bf528SMauro Carvalho Chehab break; 7179a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 5); 7189a0bf528SMauro Carvalho Chehab if (status < 0) 7199a0bf528SMauro Carvalho Chehab break; 7209a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersInit(state, 6); 7219a0bf528SMauro Carvalho Chehab if (status < 0) 7229a0bf528SMauro Carvalho Chehab break; 7239a0bf528SMauro Carvalho Chehab status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */ 7249a0bf528SMauro Carvalho Chehab if (status < 0) 7259a0bf528SMauro Carvalho Chehab break; 7269a0bf528SMauro Carvalho Chehab } while (0); 7279a0bf528SMauro Carvalho Chehab 7289a0bf528SMauro Carvalho Chehab return status; 7299a0bf528SMauro Carvalho Chehab } 7309a0bf528SMauro Carvalho Chehab 7319a0bf528SMauro Carvalho Chehab static int FixedContentsI2CUpdate(struct tda_state *state) 7329a0bf528SMauro Carvalho Chehab { 7339a0bf528SMauro Carvalho Chehab static u8 InitRegs[] = { 7349a0bf528SMauro Carvalho Chehab 0x08, 0x80, 0xC6, 7359a0bf528SMauro Carvalho Chehab 0xDF, 0x16, 0x60, 0x80, 7369a0bf528SMauro Carvalho Chehab 0x80, 0x00, 0x00, 0x00, 7379a0bf528SMauro Carvalho Chehab 0x00, 0x00, 0x00, 0x00, 7389a0bf528SMauro Carvalho Chehab 0xFC, 0x01, 0x84, 0x41, 7399a0bf528SMauro Carvalho Chehab 0x01, 0x84, 0x40, 0x07, 7409a0bf528SMauro Carvalho Chehab 0x00, 0x00, 0x96, 0x3F, 7419a0bf528SMauro Carvalho Chehab 0xC1, 0x00, 0x8F, 0x00, 7429a0bf528SMauro Carvalho Chehab 0x00, 0x8C, 0x00, 0x20, 7439a0bf528SMauro Carvalho Chehab 0xB3, 0x48, 0xB0, 7449a0bf528SMauro Carvalho Chehab }; 7459a0bf528SMauro Carvalho Chehab int status = 0; 7469a0bf528SMauro Carvalho Chehab memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1); 7479a0bf528SMauro Carvalho Chehab do { 7489a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, TM, EB23); 7499a0bf528SMauro Carvalho Chehab if (status < 0) 7509a0bf528SMauro Carvalho Chehab break; 7519a0bf528SMauro Carvalho Chehab 7529a0bf528SMauro Carvalho Chehab /* AGC1 gain setup */ 7539a0bf528SMauro Carvalho Chehab state->m_Regs[EB17] = 0x00; 7549a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB17); 7559a0bf528SMauro Carvalho Chehab if (status < 0) 7569a0bf528SMauro Carvalho Chehab break; 7579a0bf528SMauro Carvalho Chehab state->m_Regs[EB17] = 0x03; 7589a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB17); 7599a0bf528SMauro Carvalho Chehab if (status < 0) 7609a0bf528SMauro Carvalho Chehab break; 7619a0bf528SMauro Carvalho Chehab state->m_Regs[EB17] = 0x43; 7629a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB17); 7639a0bf528SMauro Carvalho Chehab if (status < 0) 7649a0bf528SMauro Carvalho Chehab break; 7659a0bf528SMauro Carvalho Chehab state->m_Regs[EB17] = 0x4C; 7669a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB17); 7679a0bf528SMauro Carvalho Chehab if (status < 0) 7689a0bf528SMauro Carvalho Chehab break; 7699a0bf528SMauro Carvalho Chehab 7709a0bf528SMauro Carvalho Chehab /* IRC Cal Low band */ 7719a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] = 0x1F; 7729a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = 0x66; 7739a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x81; 7749a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = 0xCC; 7759a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = 0x6C; 7769a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0x00; 7779a0bf528SMauro Carvalho Chehab state->m_Regs[CD3] = 0x00; 7789a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] = 0xC5; 7799a0bf528SMauro Carvalho Chehab state->m_Regs[MD1] = 0x77; 7809a0bf528SMauro Carvalho Chehab state->m_Regs[MD2] = 0x08; 7819a0bf528SMauro Carvalho Chehab state->m_Regs[MD3] = 0x00; 7829a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */ 7839a0bf528SMauro Carvalho Chehab if (status < 0) 7849a0bf528SMauro Carvalho Chehab break; 7859a0bf528SMauro Carvalho Chehab 7869a0bf528SMauro Carvalho Chehab #if 0 7879a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] = 0x61; /* missing in sw */ 7889a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 7899a0bf528SMauro Carvalho Chehab if (status < 0) 7909a0bf528SMauro Carvalho Chehab break; 7919a0bf528SMauro Carvalho Chehab msleep(1); 7929a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] = 0x41; 7939a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 7949a0bf528SMauro Carvalho Chehab if (status < 0) 7959a0bf528SMauro Carvalho Chehab break; 7969a0bf528SMauro Carvalho Chehab #endif 7979a0bf528SMauro Carvalho Chehab 7989a0bf528SMauro Carvalho Chehab msleep(5); 7999a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 8009a0bf528SMauro Carvalho Chehab if (status < 0) 8019a0bf528SMauro Carvalho Chehab break; 8029a0bf528SMauro Carvalho Chehab msleep(5); 8039a0bf528SMauro Carvalho Chehab 8049a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x85; 8059a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = 0xCB; 8069a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = 0x66; 8079a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0x70; 8089a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, CD3); 8099a0bf528SMauro Carvalho Chehab if (status < 0) 8109a0bf528SMauro Carvalho Chehab break; 8119a0bf528SMauro Carvalho Chehab msleep(5); 8129a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 8139a0bf528SMauro Carvalho Chehab if (status < 0) 8149a0bf528SMauro Carvalho Chehab break; 8159a0bf528SMauro Carvalho Chehab msleep(30); 8169a0bf528SMauro Carvalho Chehab 8179a0bf528SMauro Carvalho Chehab /* IRC Cal mid band */ 8189a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x82; 8199a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = 0xA8; 8209a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0x00; 8219a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */ 8229a0bf528SMauro Carvalho Chehab state->m_Regs[MD1] = 0x73; 8239a0bf528SMauro Carvalho Chehab state->m_Regs[MD2] = 0x1A; 8249a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, MD3); 8259a0bf528SMauro Carvalho Chehab if (status < 0) 8269a0bf528SMauro Carvalho Chehab break; 8279a0bf528SMauro Carvalho Chehab 8289a0bf528SMauro Carvalho Chehab msleep(5); 8299a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 8309a0bf528SMauro Carvalho Chehab if (status < 0) 8319a0bf528SMauro Carvalho Chehab break; 8329a0bf528SMauro Carvalho Chehab msleep(5); 8339a0bf528SMauro Carvalho Chehab 8349a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x86; 8359a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = 0xA8; 8369a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = 0x66; 8379a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0xA0; 8389a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, CD3); 8399a0bf528SMauro Carvalho Chehab if (status < 0) 8409a0bf528SMauro Carvalho Chehab break; 8419a0bf528SMauro Carvalho Chehab msleep(5); 8429a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 8439a0bf528SMauro Carvalho Chehab if (status < 0) 8449a0bf528SMauro Carvalho Chehab break; 8459a0bf528SMauro Carvalho Chehab msleep(30); 8469a0bf528SMauro Carvalho Chehab 8479a0bf528SMauro Carvalho Chehab /* IRC Cal high band */ 8489a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x83; 8499a0bf528SMauro Carvalho Chehab state->m_Regs[CPD] = 0x98; 8509a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = 0x65; 8519a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0x00; 8529a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */ 8539a0bf528SMauro Carvalho Chehab state->m_Regs[MD1] = 0x71; 8549a0bf528SMauro Carvalho Chehab state->m_Regs[MD2] = 0xCD; 8559a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, MD3); 8569a0bf528SMauro Carvalho Chehab if (status < 0) 8579a0bf528SMauro Carvalho Chehab break; 8589a0bf528SMauro Carvalho Chehab msleep(5); 8599a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 8609a0bf528SMauro Carvalho Chehab if (status < 0) 8619a0bf528SMauro Carvalho Chehab break; 8629a0bf528SMauro Carvalho Chehab msleep(5); 8639a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = 0x87; 8649a0bf528SMauro Carvalho Chehab state->m_Regs[CD1] = 0x65; 8659a0bf528SMauro Carvalho Chehab state->m_Regs[CD2] = 0x50; 8669a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EP3, CD3); 8679a0bf528SMauro Carvalho Chehab if (status < 0) 8689a0bf528SMauro Carvalho Chehab break; 8699a0bf528SMauro Carvalho Chehab msleep(5); 8709a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP2); 8719a0bf528SMauro Carvalho Chehab if (status < 0) 8729a0bf528SMauro Carvalho Chehab break; 8739a0bf528SMauro Carvalho Chehab msleep(30); 8749a0bf528SMauro Carvalho Chehab 8759a0bf528SMauro Carvalho Chehab /* Back to normal */ 8769a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = 0x64; 8779a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP4); 8789a0bf528SMauro Carvalho Chehab if (status < 0) 8799a0bf528SMauro Carvalho Chehab break; 8809a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP1); 8819a0bf528SMauro Carvalho Chehab if (status < 0) 8829a0bf528SMauro Carvalho Chehab break; 8839a0bf528SMauro Carvalho Chehab 8849a0bf528SMauro Carvalho Chehab } while (0); 8859a0bf528SMauro Carvalho Chehab return status; 8869a0bf528SMauro Carvalho Chehab } 8879a0bf528SMauro Carvalho Chehab 8889a0bf528SMauro Carvalho Chehab static int InitCal(struct tda_state *state) 8899a0bf528SMauro Carvalho Chehab { 8909a0bf528SMauro Carvalho Chehab int status = 0; 8919a0bf528SMauro Carvalho Chehab 8929a0bf528SMauro Carvalho Chehab do { 8939a0bf528SMauro Carvalho Chehab status = FixedContentsI2CUpdate(state); 8949a0bf528SMauro Carvalho Chehab if (status < 0) 8959a0bf528SMauro Carvalho Chehab break; 8969a0bf528SMauro Carvalho Chehab status = CalcRFFilterCurve(state); 8979a0bf528SMauro Carvalho Chehab if (status < 0) 8989a0bf528SMauro Carvalho Chehab break; 8999a0bf528SMauro Carvalho Chehab status = StandBy(state); 9009a0bf528SMauro Carvalho Chehab if (status < 0) 9019a0bf528SMauro Carvalho Chehab break; 9029a0bf528SMauro Carvalho Chehab /* m_bInitDone = true; */ 9039a0bf528SMauro Carvalho Chehab } while (0); 9049a0bf528SMauro Carvalho Chehab return status; 9059a0bf528SMauro Carvalho Chehab }; 9069a0bf528SMauro Carvalho Chehab 9079a0bf528SMauro Carvalho Chehab static int RFTrackingFiltersCorrection(struct tda_state *state, 9089a0bf528SMauro Carvalho Chehab u32 Frequency) 9099a0bf528SMauro Carvalho Chehab { 9109a0bf528SMauro Carvalho Chehab int status = 0; 9119a0bf528SMauro Carvalho Chehab s32 Cprog_table; 9129a0bf528SMauro Carvalho Chehab u8 RFBand; 9139a0bf528SMauro Carvalho Chehab u8 dCoverdT; 9149a0bf528SMauro Carvalho Chehab 9159a0bf528SMauro Carvalho Chehab if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) || 9169a0bf528SMauro Carvalho Chehab !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) || 9179a0bf528SMauro Carvalho Chehab !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT)) 9189a0bf528SMauro Carvalho Chehab 9199a0bf528SMauro Carvalho Chehab return -EINVAL; 9209a0bf528SMauro Carvalho Chehab 9219a0bf528SMauro Carvalho Chehab do { 9229a0bf528SMauro Carvalho Chehab u8 TMValue_Current; 9239a0bf528SMauro Carvalho Chehab u32 RF1 = state->m_RF1[RFBand]; 9249a0bf528SMauro Carvalho Chehab u32 RF2 = state->m_RF1[RFBand]; 9259a0bf528SMauro Carvalho Chehab u32 RF3 = state->m_RF1[RFBand]; 9269a0bf528SMauro Carvalho Chehab s32 RF_A1 = state->m_RF_A1[RFBand]; 9279a0bf528SMauro Carvalho Chehab s32 RF_B1 = state->m_RF_B1[RFBand]; 9289a0bf528SMauro Carvalho Chehab s32 RF_A2 = state->m_RF_A2[RFBand]; 9299a0bf528SMauro Carvalho Chehab s32 RF_B2 = state->m_RF_B2[RFBand]; 9309a0bf528SMauro Carvalho Chehab s32 Capprox = 0; 9319a0bf528SMauro Carvalho Chehab int TComp; 9329a0bf528SMauro Carvalho Chehab 9339a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] &= ~0xE0; /* Power up */ 9349a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP3); 9359a0bf528SMauro Carvalho Chehab if (status < 0) 9369a0bf528SMauro Carvalho Chehab break; 9379a0bf528SMauro Carvalho Chehab 9389a0bf528SMauro Carvalho Chehab status = ThermometerRead(state, &TMValue_Current); 9399a0bf528SMauro Carvalho Chehab if (status < 0) 9409a0bf528SMauro Carvalho Chehab break; 9419a0bf528SMauro Carvalho Chehab 9429a0bf528SMauro Carvalho Chehab if (RF3 == 0 || Frequency < RF2) 9439a0bf528SMauro Carvalho Chehab Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table; 9449a0bf528SMauro Carvalho Chehab else 9459a0bf528SMauro Carvalho Chehab Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table; 9469a0bf528SMauro Carvalho Chehab 9479a0bf528SMauro Carvalho Chehab TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000; 9489a0bf528SMauro Carvalho Chehab 9499a0bf528SMauro Carvalho Chehab Capprox += TComp; 9509a0bf528SMauro Carvalho Chehab 9519a0bf528SMauro Carvalho Chehab if (Capprox < 0) 9529a0bf528SMauro Carvalho Chehab Capprox = 0; 9539a0bf528SMauro Carvalho Chehab else if (Capprox > 255) 9549a0bf528SMauro Carvalho Chehab Capprox = 255; 9559a0bf528SMauro Carvalho Chehab 9569a0bf528SMauro Carvalho Chehab 9579a0bf528SMauro Carvalho Chehab /* TODO Temperature compensation. There is defenitely a scale factor */ 9589a0bf528SMauro Carvalho Chehab /* missing in the datasheet, so leave it out for now. */ 9599a0bf528SMauro Carvalho Chehab state->m_Regs[EB14] = Capprox; 9609a0bf528SMauro Carvalho Chehab 9619a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB14); 9629a0bf528SMauro Carvalho Chehab if (status < 0) 9639a0bf528SMauro Carvalho Chehab break; 9649a0bf528SMauro Carvalho Chehab 9659a0bf528SMauro Carvalho Chehab } while (0); 9669a0bf528SMauro Carvalho Chehab return status; 9679a0bf528SMauro Carvalho Chehab } 9689a0bf528SMauro Carvalho Chehab 9699a0bf528SMauro Carvalho Chehab static int ChannelConfiguration(struct tda_state *state, 9709a0bf528SMauro Carvalho Chehab u32 Frequency, int Standard) 9719a0bf528SMauro Carvalho Chehab { 9729a0bf528SMauro Carvalho Chehab 9739a0bf528SMauro Carvalho Chehab s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency; 9749a0bf528SMauro Carvalho Chehab int status = 0; 9759a0bf528SMauro Carvalho Chehab 9769a0bf528SMauro Carvalho Chehab u8 BP_Filter = 0; 9779a0bf528SMauro Carvalho Chehab u8 RF_Band = 0; 9789a0bf528SMauro Carvalho Chehab u8 GainTaper = 0; 9799a0bf528SMauro Carvalho Chehab u8 IR_Meas = 0; 9809a0bf528SMauro Carvalho Chehab 9819a0bf528SMauro Carvalho Chehab state->IF = IntermediateFrequency; 9829a0bf528SMauro Carvalho Chehab /* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */ 9839a0bf528SMauro Carvalho Chehab /* get values from tables */ 9849a0bf528SMauro Carvalho Chehab 9859a0bf528SMauro Carvalho Chehab if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) && 9869a0bf528SMauro Carvalho Chehab SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) && 9879a0bf528SMauro Carvalho Chehab SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) && 9889a0bf528SMauro Carvalho Chehab SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) { 9899a0bf528SMauro Carvalho Chehab 9909a0bf528SMauro Carvalho Chehab printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__); 9919a0bf528SMauro Carvalho Chehab return -EINVAL; 9929a0bf528SMauro Carvalho Chehab } 9939a0bf528SMauro Carvalho Chehab 9949a0bf528SMauro Carvalho Chehab do { 9959a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0; 9969a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */ 9979a0bf528SMauro Carvalho Chehab 9989a0bf528SMauro Carvalho Chehab /* m_EP4 default for XToutOn, CAL_Mode (0) */ 9999a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog); 10009a0bf528SMauro Carvalho Chehab /* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */ 10019a0bf528SMauro Carvalho Chehab if (Standard <= HF_AnalogMax) 10029a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog; 10039a0bf528SMauro Carvalho Chehab else if (Standard <= HF_ATSC) 10049a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT; 10059a0bf528SMauro Carvalho Chehab else if (Standard <= HF_DVBC) 10069a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC; 10079a0bf528SMauro Carvalho Chehab else 10089a0bf528SMauro Carvalho Chehab state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; 10099a0bf528SMauro Carvalho Chehab 10109a0bf528SMauro Carvalho Chehab if ((Standard == HF_FM_Radio) && state->m_bFMInput) 101158632818SDan Carpenter state->m_Regs[EP4] |= 0x80; 10129a0bf528SMauro Carvalho Chehab 10139a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] &= ~0x80; 10149a0bf528SMauro Carvalho Chehab if (Standard > HF_AnalogMax) 10159a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */ 10169a0bf528SMauro Carvalho Chehab 10179a0bf528SMauro Carvalho Chehab state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22; 10189a0bf528SMauro Carvalho Chehab 10199a0bf528SMauro Carvalho Chehab /* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */ 10209a0bf528SMauro Carvalho Chehab if (Standard == HF_FM_Radio) 10219a0bf528SMauro Carvalho Chehab state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ 10229a0bf528SMauro Carvalho Chehab else 10239a0bf528SMauro Carvalho Chehab state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */ 10249a0bf528SMauro Carvalho Chehab 10259a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, EB22, EB23); 10269a0bf528SMauro Carvalho Chehab if (status < 0) 10279a0bf528SMauro Carvalho Chehab break; 10289a0bf528SMauro Carvalho Chehab 10299a0bf528SMauro Carvalho Chehab state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */ 10309a0bf528SMauro Carvalho Chehab state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas; 10319a0bf528SMauro Carvalho Chehab state->m_Regs[EP2] = (RF_Band << 5) | GainTaper; 10329a0bf528SMauro Carvalho Chehab 10339a0bf528SMauro Carvalho Chehab state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) | 10349a0bf528SMauro Carvalho Chehab (state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */ 10359a0bf528SMauro Carvalho Chehab /* AGC1_always_master = 0 */ 10369a0bf528SMauro Carvalho Chehab /* AGC_firstn = 0 */ 10379a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB1); 10389a0bf528SMauro Carvalho Chehab if (status < 0) 10399a0bf528SMauro Carvalho Chehab break; 10409a0bf528SMauro Carvalho Chehab 10419a0bf528SMauro Carvalho Chehab if (state->m_bMaster) { 10429a0bf528SMauro Carvalho Chehab status = CalcMainPLL(state, Frequency + IntermediateFrequency); 10439a0bf528SMauro Carvalho Chehab if (status < 0) 10449a0bf528SMauro Carvalho Chehab break; 10459a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, TM, EP5); 10469a0bf528SMauro Carvalho Chehab if (status < 0) 10479a0bf528SMauro Carvalho Chehab break; 10489a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */ 10499a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 10509a0bf528SMauro Carvalho Chehab if (status < 0) 10519a0bf528SMauro Carvalho Chehab break; 10529a0bf528SMauro Carvalho Chehab msleep(1); 10539a0bf528SMauro Carvalho Chehab state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */ 10549a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB4); 10559a0bf528SMauro Carvalho Chehab if (status < 0) 10569a0bf528SMauro Carvalho Chehab break; 10579a0bf528SMauro Carvalho Chehab } else { 10589a0bf528SMauro Carvalho Chehab u8 PostDiv = 0; 10599a0bf528SMauro Carvalho Chehab u8 Div; 10609a0bf528SMauro Carvalho Chehab status = CalcCalPLL(state, Frequency + IntermediateFrequency); 10619a0bf528SMauro Carvalho Chehab if (status < 0) 10629a0bf528SMauro Carvalho Chehab break; 10639a0bf528SMauro Carvalho Chehab 10649a0bf528SMauro Carvalho Chehab SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div); 10659a0bf528SMauro Carvalho Chehab state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77); 10669a0bf528SMauro Carvalho Chehab status = UpdateReg(state, MPD); 10679a0bf528SMauro Carvalho Chehab if (status < 0) 10689a0bf528SMauro Carvalho Chehab break; 10699a0bf528SMauro Carvalho Chehab status = UpdateRegs(state, TM, EP5); 10709a0bf528SMauro Carvalho Chehab if (status < 0) 10719a0bf528SMauro Carvalho Chehab break; 10729a0bf528SMauro Carvalho Chehab 10739a0bf528SMauro Carvalho Chehab state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */ 10749a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB7); 10759a0bf528SMauro Carvalho Chehab if (status < 0) 10769a0bf528SMauro Carvalho Chehab break; 10779a0bf528SMauro Carvalho Chehab msleep(1); 10789a0bf528SMauro Carvalho Chehab state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */ 10799a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EB7); 10809a0bf528SMauro Carvalho Chehab if (status < 0) 10819a0bf528SMauro Carvalho Chehab break; 10829a0bf528SMauro Carvalho Chehab } 10839a0bf528SMauro Carvalho Chehab msleep(20); 10849a0bf528SMauro Carvalho Chehab if (Standard != HF_FM_Radio) 10859a0bf528SMauro Carvalho Chehab state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */ 10869a0bf528SMauro Carvalho Chehab status = UpdateReg(state, EP3); 10879a0bf528SMauro Carvalho Chehab if (status < 0) 10889a0bf528SMauro Carvalho Chehab break; 10899a0bf528SMauro Carvalho Chehab 10909a0bf528SMauro Carvalho Chehab } while (0); 10919a0bf528SMauro Carvalho Chehab return status; 10929a0bf528SMauro Carvalho Chehab } 10939a0bf528SMauro Carvalho Chehab 10949a0bf528SMauro Carvalho Chehab static int sleep(struct dvb_frontend *fe) 10959a0bf528SMauro Carvalho Chehab { 10969a0bf528SMauro Carvalho Chehab struct tda_state *state = fe->tuner_priv; 10979a0bf528SMauro Carvalho Chehab 10989a0bf528SMauro Carvalho Chehab StandBy(state); 10999a0bf528SMauro Carvalho Chehab return 0; 11009a0bf528SMauro Carvalho Chehab } 11019a0bf528SMauro Carvalho Chehab 11029a0bf528SMauro Carvalho Chehab static int init(struct dvb_frontend *fe) 11039a0bf528SMauro Carvalho Chehab { 11049a0bf528SMauro Carvalho Chehab return 0; 11059a0bf528SMauro Carvalho Chehab } 11069a0bf528SMauro Carvalho Chehab 1107f2709c20SMauro Carvalho Chehab static void release(struct dvb_frontend *fe) 1108f2709c20SMauro Carvalho Chehab { 1109f2709c20SMauro Carvalho Chehab kfree(fe->tuner_priv); 1110f2709c20SMauro Carvalho Chehab fe->tuner_priv = NULL; 1111f2709c20SMauro Carvalho Chehab } 1112f2709c20SMauro Carvalho Chehab 1113f2709c20SMauro Carvalho Chehab 11149a0bf528SMauro Carvalho Chehab static int set_params(struct dvb_frontend *fe) 11159a0bf528SMauro Carvalho Chehab { 11169a0bf528SMauro Carvalho Chehab struct tda_state *state = fe->tuner_priv; 11179a0bf528SMauro Carvalho Chehab int status = 0; 11189a0bf528SMauro Carvalho Chehab int Standard; 11199a0bf528SMauro Carvalho Chehab u32 bw = fe->dtv_property_cache.bandwidth_hz; 11209a0bf528SMauro Carvalho Chehab u32 delsys = fe->dtv_property_cache.delivery_system; 11219a0bf528SMauro Carvalho Chehab 11229a0bf528SMauro Carvalho Chehab state->m_Frequency = fe->dtv_property_cache.frequency; 11239a0bf528SMauro Carvalho Chehab 11249a0bf528SMauro Carvalho Chehab switch (delsys) { 11259a0bf528SMauro Carvalho Chehab case SYS_DVBT: 11269a0bf528SMauro Carvalho Chehab case SYS_DVBT2: 11279a0bf528SMauro Carvalho Chehab switch (bw) { 11289a0bf528SMauro Carvalho Chehab case 6000000: 11299a0bf528SMauro Carvalho Chehab Standard = HF_DVBT_6MHZ; 11309a0bf528SMauro Carvalho Chehab break; 11319a0bf528SMauro Carvalho Chehab case 7000000: 11329a0bf528SMauro Carvalho Chehab Standard = HF_DVBT_7MHZ; 11339a0bf528SMauro Carvalho Chehab break; 11349a0bf528SMauro Carvalho Chehab case 8000000: 11359a0bf528SMauro Carvalho Chehab Standard = HF_DVBT_8MHZ; 11369a0bf528SMauro Carvalho Chehab break; 11379a0bf528SMauro Carvalho Chehab default: 11389a0bf528SMauro Carvalho Chehab return -EINVAL; 11399a0bf528SMauro Carvalho Chehab } 1140ca747d04SDaniel Scheller break; 11419a0bf528SMauro Carvalho Chehab case SYS_DVBC_ANNEX_A: 11429a0bf528SMauro Carvalho Chehab case SYS_DVBC_ANNEX_C: 11439a0bf528SMauro Carvalho Chehab if (bw <= 6000000) 11449a0bf528SMauro Carvalho Chehab Standard = HF_DVBC_6MHZ; 11459a0bf528SMauro Carvalho Chehab else if (bw <= 7000000) 11469a0bf528SMauro Carvalho Chehab Standard = HF_DVBC_7MHZ; 11479a0bf528SMauro Carvalho Chehab else 11489a0bf528SMauro Carvalho Chehab Standard = HF_DVBC_8MHZ; 11499a0bf528SMauro Carvalho Chehab break; 11509a0bf528SMauro Carvalho Chehab default: 11519a0bf528SMauro Carvalho Chehab return -EINVAL; 11529a0bf528SMauro Carvalho Chehab } 11539a0bf528SMauro Carvalho Chehab do { 11549a0bf528SMauro Carvalho Chehab status = RFTrackingFiltersCorrection(state, state->m_Frequency); 11559a0bf528SMauro Carvalho Chehab if (status < 0) 11569a0bf528SMauro Carvalho Chehab break; 11579a0bf528SMauro Carvalho Chehab status = ChannelConfiguration(state, state->m_Frequency, 11589a0bf528SMauro Carvalho Chehab Standard); 11599a0bf528SMauro Carvalho Chehab if (status < 0) 11609a0bf528SMauro Carvalho Chehab break; 11619a0bf528SMauro Carvalho Chehab 11629a0bf528SMauro Carvalho Chehab msleep(state->m_SettlingTime); /* Allow AGC's to settle down */ 11639a0bf528SMauro Carvalho Chehab } while (0); 11649a0bf528SMauro Carvalho Chehab return status; 11659a0bf528SMauro Carvalho Chehab } 11669a0bf528SMauro Carvalho Chehab 11679a0bf528SMauro Carvalho Chehab #if 0 11689a0bf528SMauro Carvalho Chehab static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc) 11699a0bf528SMauro Carvalho Chehab { 11709a0bf528SMauro Carvalho Chehab if (IFAgc < 500) { 11719a0bf528SMauro Carvalho Chehab /* Scale this from 0 to 50000 */ 11729a0bf528SMauro Carvalho Chehab *pSignalStrength = IFAgc * 100; 11739a0bf528SMauro Carvalho Chehab } else { 11749a0bf528SMauro Carvalho Chehab /* Scale range 500-1500 to 50000-80000 */ 11759a0bf528SMauro Carvalho Chehab *pSignalStrength = 50000 + (IFAgc - 500) * 30; 11769a0bf528SMauro Carvalho Chehab } 11779a0bf528SMauro Carvalho Chehab 11789a0bf528SMauro Carvalho Chehab return 0; 11799a0bf528SMauro Carvalho Chehab } 11809a0bf528SMauro Carvalho Chehab #endif 11819a0bf528SMauro Carvalho Chehab 11829a0bf528SMauro Carvalho Chehab static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 11839a0bf528SMauro Carvalho Chehab { 11849a0bf528SMauro Carvalho Chehab struct tda_state *state = fe->tuner_priv; 11859a0bf528SMauro Carvalho Chehab 11869a0bf528SMauro Carvalho Chehab *frequency = state->IF; 11879a0bf528SMauro Carvalho Chehab return 0; 11889a0bf528SMauro Carvalho Chehab } 11899a0bf528SMauro Carvalho Chehab 11909a0bf528SMauro Carvalho Chehab static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 11919a0bf528SMauro Carvalho Chehab { 11929a0bf528SMauro Carvalho Chehab /* struct tda_state *state = fe->tuner_priv; */ 11939a0bf528SMauro Carvalho Chehab /* *bandwidth = priv->bandwidth; */ 11949a0bf528SMauro Carvalho Chehab return 0; 11959a0bf528SMauro Carvalho Chehab } 11969a0bf528SMauro Carvalho Chehab 11979a0bf528SMauro Carvalho Chehab 119814c4bf3cSJulia Lawall static const struct dvb_tuner_ops tuner_ops = { 11999a0bf528SMauro Carvalho Chehab .info = { 12009a0bf528SMauro Carvalho Chehab .name = "NXP TDA18271C2D", 1201a3f90c75SMauro Carvalho Chehab .frequency_min_hz = 47125 * kHz, 1202a3f90c75SMauro Carvalho Chehab .frequency_max_hz = 865 * MHz, 1203a3f90c75SMauro Carvalho Chehab .frequency_step_hz = 62500 12049a0bf528SMauro Carvalho Chehab }, 12059a0bf528SMauro Carvalho Chehab .init = init, 12069a0bf528SMauro Carvalho Chehab .sleep = sleep, 12079a0bf528SMauro Carvalho Chehab .set_params = set_params, 1208f2709c20SMauro Carvalho Chehab .release = release, 12099a0bf528SMauro Carvalho Chehab .get_if_frequency = get_if_frequency, 12109a0bf528SMauro Carvalho Chehab .get_bandwidth = get_bandwidth, 12119a0bf528SMauro Carvalho Chehab }; 12129a0bf528SMauro Carvalho Chehab 12139a0bf528SMauro Carvalho Chehab struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, 12149a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c, u8 adr) 12159a0bf528SMauro Carvalho Chehab { 12169a0bf528SMauro Carvalho Chehab struct tda_state *state; 12179a0bf528SMauro Carvalho Chehab 12189a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct tda_state), GFP_KERNEL); 12199a0bf528SMauro Carvalho Chehab if (!state) 12209a0bf528SMauro Carvalho Chehab return NULL; 12219a0bf528SMauro Carvalho Chehab 12229a0bf528SMauro Carvalho Chehab fe->tuner_priv = state; 12239a0bf528SMauro Carvalho Chehab state->adr = adr; 12249a0bf528SMauro Carvalho Chehab state->i2c = i2c; 12259a0bf528SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); 12269a0bf528SMauro Carvalho Chehab reset(state); 12279a0bf528SMauro Carvalho Chehab InitCal(state); 12289a0bf528SMauro Carvalho Chehab 12299a0bf528SMauro Carvalho Chehab return fe; 12309a0bf528SMauro Carvalho Chehab } 12319a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(tda18271c2dd_attach); 12329a0bf528SMauro Carvalho Chehab 12339a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("TDA18271C2 driver"); 12349a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("DD"); 12359a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 1236