blob: 8b39d8dc97a075972c5f1824e2a286a6c675f4fd [file] [log] [blame]
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001/*
2 * Driver for mt2063 Micronas tuner
3 *
Mauro Carvalho Chehab37e59f82014-02-07 08:03:07 -02004 * Copyright (c) 2011 Mauro Carvalho Chehab
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03005 *
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -03006 * This driver came from a driver originally written by:
7 * Henry Wang <Henry.wang@AzureWave.com>
8 * Made publicly available by Terratec, at:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03009 * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -030010 * The original driver's license is GPL, as declared with MODULE_LICENSE()
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -030011 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation under version 2 of the License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
21
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030022#include <linux/init.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/string.h>
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -030026#include <linux/videodev2.h>
Zhaoxiu Zeng917d11a2016-04-27 04:07:08 -030027#include <linux/gcd.h>
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030028
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030029#include "mt2063.h"
30
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -030031static unsigned int debug;
32module_param(debug, int, 0644);
33MODULE_PARM_DESC(debug, "Set Verbosity level");
34
35#define dprintk(level, fmt, arg...) do { \
36if (debug >= level) \
37 printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \
38} while (0)
39
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030040
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -030041/* positive error codes used internally */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030042
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -030043/* Info: Unavoidable LO-related spur may be present in the output */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030044#define MT2063_SPUR_PRESENT_ERR (0x00800000)
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030045
46/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */
47#define MT2063_SPUR_CNT_MASK (0x001f0000)
48#define MT2063_SPUR_SHIFT (16)
49
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030050/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */
51#define MT2063_UPC_RANGE (0x04000000)
52
53/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */
54#define MT2063_DNC_RANGE (0x08000000)
55
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030056/*
57 * Constant defining the version of the following structure
58 * and therefore the API for this code.
59 *
60 * When compiling the tuner driver, the preprocessor will
61 * check against this version number to make sure that
62 * it matches the version that the tuner driver knows about.
63 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030064
65/* DECT Frequency Avoidance */
66#define MT2063_DECT_AVOID_US_FREQS 0x00000001
67
68#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002
69
70#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0)
71
72#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0)
73
74enum MT2063_DECT_Avoid_Type {
75 MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */
76 MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */
77 MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */
78 MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */
79};
80
81#define MT2063_MAX_ZONES 48
82
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030083struct MT2063_ExclZone_t {
84 u32 min_;
85 u32 max_;
86 struct MT2063_ExclZone_t *next_;
87};
88
89/*
90 * Structure of data needed for Spur Avoidance
91 */
92struct MT2063_AvoidSpursData_t {
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030093 u32 f_ref;
94 u32 f_in;
95 u32 f_LO1;
96 u32 f_if1_Center;
97 u32 f_if1_Request;
98 u32 f_if1_bw;
99 u32 f_LO2;
100 u32 f_out;
101 u32 f_out_bw;
102 u32 f_LO1_Step;
103 u32 f_LO2_Step;
104 u32 f_LO1_FracN_Avoid;
105 u32 f_LO2_FracN_Avoid;
106 u32 f_zif_bw;
107 u32 f_min_LO_Separation;
108 u32 maxH1;
109 u32 maxH2;
110 enum MT2063_DECT_Avoid_Type avoidDECT;
111 u32 bSpurPresent;
112 u32 bSpurAvoided;
113 u32 nSpursFound;
114 u32 nZones;
115 struct MT2063_ExclZone_t *freeZones;
116 struct MT2063_ExclZone_t *usedZones;
117 struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES];
118};
119
120/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300121 * Parameter for function MT2063_SetPowerMask that specifies the power down
122 * of various sections of the MT2063.
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300123 */
124enum MT2063_Mask_Bits {
125 MT2063_REG_SD = 0x0040, /* Shutdown regulator */
126 MT2063_SRO_SD = 0x0020, /* Shutdown SRO */
127 MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */
128 MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */
129 MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */
130 MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */
131 MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */
132 MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */
133 MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */
134 MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */
135 MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */
136 MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */
137 MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */
138 MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */
139 MT2063_NONE_SD = 0x0000 /* No shutdown bits */
140};
141
142/*
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300143 * Possible values for MT2063_DNC_OUTPUT
144 */
145enum MT2063_DNC_Output_Enable {
146 MT2063_DNC_NONE = 0,
147 MT2063_DNC_1,
148 MT2063_DNC_2,
149 MT2063_DNC_BOTH
150};
151
152/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300153 * Two-wire serial bus subaddresses of the tuner registers.
154 * Also known as the tuner's register addresses.
155 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300156enum MT2063_Register_Offsets {
157 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
158 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
159 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
160 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
161 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
162 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
163 MT2063_REG_RSVD_06, /* 0x06: Reserved */
164 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
165 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
166 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
167 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
168 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
169 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
170 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
171 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
172 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
173 MT2063_REG_RSVD_10, /* 0x10: Reserved */
174 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
175 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
176 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
177 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
178 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
179 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
180 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
181 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
182 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
183 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
184 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
185 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
186 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
187 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
188 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
189 MT2063_REG_RSVD_20, /* 0x20: Reserved */
190 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
191 MT2063_REG_RSVD_22, /* 0x22: Reserved */
192 MT2063_REG_RSVD_23, /* 0x23: Reserved */
193 MT2063_REG_RSVD_24, /* 0x24: Reserved */
194 MT2063_REG_RSVD_25, /* 0x25: Reserved */
195 MT2063_REG_RSVD_26, /* 0x26: Reserved */
196 MT2063_REG_RSVD_27, /* 0x27: Reserved */
197 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
198 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
199 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
200 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
201 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
202 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
203 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
204 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
205 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
206 MT2063_REG_RSVD_31, /* 0x31: Reserved */
207 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
208 MT2063_REG_RSVD_33, /* 0x33: Reserved */
209 MT2063_REG_RSVD_34, /* 0x34: Reserved */
210 MT2063_REG_RSVD_35, /* 0x35: Reserved */
211 MT2063_REG_RSVD_36, /* 0x36: Reserved */
212 MT2063_REG_RSVD_37, /* 0x37: Reserved */
213 MT2063_REG_RSVD_38, /* 0x38: Reserved */
214 MT2063_REG_RSVD_39, /* 0x39: Reserved */
215 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
216 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
217 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
218 MT2063_REG_END_REGS
219};
220
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300221struct mt2063_state {
222 struct i2c_adapter *i2c;
223
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -0300224 bool init;
225
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300226 const struct mt2063_config *config;
227 struct dvb_tuner_ops ops;
228 struct dvb_frontend *frontend;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300229
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300230 u32 frequency;
231 u32 srate;
232 u32 bandwidth;
233 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300234
235 u32 tuner_id;
236 struct MT2063_AvoidSpursData_t AS_Data;
237 u32 f_IF1_actual;
238 u32 rcvr_mode;
239 u32 ctfilt_sw;
240 u32 CTFiltMax[31];
241 u32 num_regs;
242 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300243};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300244
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300245/*
246 * mt2063_write - Write data into the I2C bus
247 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300248static int mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300249{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300250 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300251 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300252 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300253 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300254 .addr = state->config->tuner_address,
255 .flags = 0,
256 .buf = buf,
257 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300258 };
259
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300260 dprintk(2, "\n");
261
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300262 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300263 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300264
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300265 if (fe->ops.i2c_gate_ctrl)
266 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300267 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300268 if (fe->ops.i2c_gate_ctrl)
269 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300270
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300271 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300272 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300273
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300274 return ret;
275}
276
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300277/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300278 * mt2063_write - Write register data into the I2C bus, caching the value
279 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300280static int mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300281{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300282 int status;
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300283
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300284 dprintk(2, "\n");
285
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300286 if (reg >= MT2063_REG_END_REGS)
287 return -ERANGE;
288
289 status = mt2063_write(state, reg, &val, 1);
290 if (status < 0)
291 return status;
292
293 state->reg[reg] = val;
294
295 return 0;
296}
297
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300298/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300299 * mt2063_read - Read data from the I2C bus
300 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300301static int mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300302 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300303{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300304 int status = 0; /* Status to be returned */
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300305 struct dvb_frontend *fe = state->frontend;
306 u32 i = 0;
307
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300308 dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300309
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300310 if (fe->ops.i2c_gate_ctrl)
311 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300312
313 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300314 u8 b0[] = { subAddress + i };
315 struct i2c_msg msg[] = {
316 {
317 .addr = state->config->tuner_address,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300318 .flags = 0,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300319 .buf = b0,
320 .len = 1
321 }, {
322 .addr = state->config->tuner_address,
323 .flags = I2C_M_RD,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300324 .buf = pData + i,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300325 .len = 1
326 }
327 };
328
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300329 status = i2c_transfer(state->i2c, msg, 2);
330 dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n",
331 subAddress + i, status, *(pData + i));
332 if (status < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300333 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300334 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300335 if (fe->ops.i2c_gate_ctrl)
336 fe->ops.i2c_gate_ctrl(fe, 0);
337
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300338 if (status < 0)
339 printk(KERN_ERR "Can't read from address 0x%02x,\n",
340 subAddress + i);
341
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300342 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300343}
344
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300345/*
346 * FIXME: Is this really needed?
347 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300348static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300349{
350 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300351 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300352 */
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300353 msleep(100);
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300354
355 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300356}
357
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300358/*
359 * Microtune spur avoidance
360 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300361
362/* Implement ceiling, floor functions. */
363#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300364#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300365
366struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300367 s32 min_;
368 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300369};
370
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300371static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
372 *pAS_Info,
373 struct MT2063_ExclZone_t *pPrevNode)
374{
375 struct MT2063_ExclZone_t *pNode;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300376
377 dprintk(2, "\n");
378
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300379 /* Check for a node in the free list */
380 if (pAS_Info->freeZones != NULL) {
381 /* Use one from the free list */
382 pNode = pAS_Info->freeZones;
383 pAS_Info->freeZones = pNode->next_;
384 } else {
385 /* Grab a node from the array */
386 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
387 }
388
389 if (pPrevNode != NULL) {
390 pNode->next_ = pPrevNode->next_;
391 pPrevNode->next_ = pNode;
392 } else { /* insert at the beginning of the list */
393
394 pNode->next_ = pAS_Info->usedZones;
395 pAS_Info->usedZones = pNode;
396 }
397
398 pAS_Info->nZones++;
399 return pNode;
400}
401
402static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
403 *pAS_Info,
404 struct MT2063_ExclZone_t *pPrevNode,
405 struct MT2063_ExclZone_t
406 *pNodeToRemove)
407{
408 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
409
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300410 dprintk(2, "\n");
411
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300412 /* Make previous node point to the subsequent node */
413 if (pPrevNode != NULL)
414 pPrevNode->next_ = pNext;
415
416 /* Add pNodeToRemove to the beginning of the freeZones */
417 pNodeToRemove->next_ = pAS_Info->freeZones;
418 pAS_Info->freeZones = pNodeToRemove;
419
420 /* Decrement node count */
421 pAS_Info->nZones--;
422
423 return pNext;
424}
425
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300426/*
427 * MT_AddExclZone()
428 *
429 * Add (and merge) an exclusion zone into the list.
430 * If the range (f_min, f_max) is totally outside the
431 * 1st IF BW, ignore the entry.
432 * If the range (f_min, f_max) is negative, ignore the entry.
433 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300434static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300435 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300436{
437 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
438 struct MT2063_ExclZone_t *pPrev = NULL;
439 struct MT2063_ExclZone_t *pNext = NULL;
440
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300441 dprintk(2, "\n");
442
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300443 /* Check to see if this overlaps the 1st IF filter */
444 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
445 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
446 && (f_min < f_max)) {
447 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300448 * 1 2 3 4 5 6
449 *
450 * New entry: |---| |--| |--| |-| |---| |--|
451 * or or or or or
452 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300453 */
454
455 /* Check for our place in the list */
456 while ((pNode != NULL) && (pNode->max_ < f_min)) {
457 pPrev = pNode;
458 pNode = pNode->next_;
459 }
460
461 if ((pNode != NULL) && (pNode->min_ < f_max)) {
462 /* Combine me with pNode */
463 if (f_min < pNode->min_)
464 pNode->min_ = f_min;
465 if (f_max > pNode->max_)
466 pNode->max_ = f_max;
467 } else {
468 pNode = InsertNode(pAS_Info, pPrev);
469 pNode->min_ = f_min;
470 pNode->max_ = f_max;
471 }
472
473 /* Look for merging possibilities */
474 pNext = pNode->next_;
475 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
476 if (pNext->max_ > pNode->max_)
477 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300478 /* Remove pNext, return ptr to pNext->next */
479 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300480 }
481 }
482}
483
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300484/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300485 * Reset all exclusion zones.
486 * Add zones to protect the PLL FracN regions near zero
487 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300488static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
489{
490 u32 center;
491
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300492 dprintk(2, "\n");
493
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300494 pAS_Info->nZones = 0; /* this clears the used list */
495 pAS_Info->usedZones = NULL; /* reset ptr */
496 pAS_Info->freeZones = NULL; /* reset ptr */
497
498 center =
499 pAS_Info->f_ref *
500 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
501 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
502 while (center <
503 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
504 pAS_Info->f_LO1_FracN_Avoid) {
505 /* Exclude LO1 FracN */
506 MT2063_AddExclZone(pAS_Info,
507 center - pAS_Info->f_LO1_FracN_Avoid,
508 center - 1);
509 MT2063_AddExclZone(pAS_Info, center + 1,
510 center + pAS_Info->f_LO1_FracN_Avoid);
511 center += pAS_Info->f_ref;
512 }
513
514 center =
515 pAS_Info->f_ref *
516 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
517 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
518 while (center <
519 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
520 pAS_Info->f_LO2_FracN_Avoid) {
521 /* Exclude LO2 FracN */
522 MT2063_AddExclZone(pAS_Info,
523 center - pAS_Info->f_LO2_FracN_Avoid,
524 center - 1);
525 MT2063_AddExclZone(pAS_Info, center + 1,
526 center + pAS_Info->f_LO2_FracN_Avoid);
527 center += pAS_Info->f_ref;
528 }
529
530 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
531 /* Exclude LO1 values that conflict with DECT channels */
532 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
533 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
534 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
535 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
536 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
537 }
538
539 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
540 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
541 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
542 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
543 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
544 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
545 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
546 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
547 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
548 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
549 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
550 }
551}
552
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300553/*
554 * MT_ChooseFirstIF - Choose the best available 1st IF
555 * If f_Desired is not excluded, choose that first.
556 * Otherwise, return the value closest to f_Center that is
557 * not excluded
558 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300559static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300560{
561 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300562 * Update "f_Desired" to be the nearest "combinational-multiple" of
563 * "f_LO1_Step".
564 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
565 * And F_LO1 is the arithmetic sum of f_in + f_Center.
566 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
567 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300568 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300569 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300570 pAS_Info->f_LO1_Step *
571 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
572 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
573 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300574 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300575 (pAS_Info->f_LO1_Step >
576 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
577 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300578 u32 f_Center;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300579 s32 i;
580 s32 j = 0;
581 u32 bDesiredExcluded = 0;
582 u32 bZeroExcluded = 0;
583 s32 tmpMin, tmpMax;
584 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300585 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
586 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
587
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300588 dprintk(2, "\n");
589
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300590 if (pAS_Info->nZones == 0)
591 return f_Desired;
592
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300593 /*
594 * f_Center needs to be an integer multiple of f_Step away
595 * from f_Desired
596 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300597 if (pAS_Info->f_if1_Center > f_Desired)
598 f_Center =
599 f_Desired +
600 f_Step *
601 ((pAS_Info->f_if1_Center - f_Desired +
602 f_Step / 2) / f_Step);
603 else
604 f_Center =
605 f_Desired -
606 f_Step *
607 ((f_Desired - pAS_Info->f_if1_Center +
608 f_Step / 2) / f_Step);
609
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300610 /*
611 * Take MT_ExclZones, center around f_Center and change the
612 * resolution to f_Step
613 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300614 while (pNode != NULL) {
615 /* floor function */
616 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300617 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300618
619 /* ceil function */
620 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300621 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300622
623 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
624 bDesiredExcluded = 1;
625
626 if ((tmpMin < 0) && (tmpMax > 0))
627 bZeroExcluded = 1;
628
629 /* See if this zone overlaps the previous */
630 if ((j > 0) && (tmpMin < zones[j - 1].max_))
631 zones[j - 1].max_ = tmpMax;
632 else {
633 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300634 zones[j].min_ = tmpMin;
635 zones[j].max_ = tmpMax;
636 j++;
637 }
638 pNode = pNode->next_;
639 }
640
641 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300642 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300643 */
644 if (bDesiredExcluded == 0)
645 return f_Desired;
646
647 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300648 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300649 */
650 if (bZeroExcluded == 0)
651 return f_Center;
652
653 /* Find the value closest to 0 (f_Center) */
654 bestDiff = zones[0].min_;
655 for (i = 0; i < j; i++) {
656 if (abs(zones[i].min_) < abs(bestDiff))
657 bestDiff = zones[i].min_;
658 if (abs(zones[i].max_) < abs(bestDiff))
659 bestDiff = zones[i].max_;
660 }
661
662 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300663 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300664
665 return f_Center + (bestDiff * f_Step);
666}
667
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300668/**
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300669 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
670 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
671 *
672 * ma mb mc md
673 * <--+-+-+-------------------+-------------------+-+-+-->
674 * | ^ 0 ^ |
675 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
676 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
677 *
678 * Note that some equations are doubled to prevent round-off
679 * problems when calculating fIFBW/2
680 *
681 * @pAS_Info: Avoid Spurs information block
682 * @fm: If spur, amount f_IF1 has to move negative
683 * @fp: If spur, amount f_IF1 has to move positive
684 *
685 * Returns 1 if an LO spur would be present, otherwise 0.
686 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300687static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300688 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300689{
690 /*
691 ** Calculate LO frequency settings.
692 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300693 u32 n, n0;
694 const u32 f_LO1 = pAS_Info->f_LO1;
695 const u32 f_LO2 = pAS_Info->f_LO2;
696 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
697 const u32 c = d - pAS_Info->f_out_bw;
698 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300699 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300700 s32 f_nsLO1, f_nsLO2;
701 s32 f_Spur;
702 u32 ma, mb, mc, md, me, mf;
703 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300704
705 dprintk(2, "\n");
706
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300707 *fm = 0;
708
709 /*
710 ** For each edge (d, c & f), calculate a scale, based on the gcd
711 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
712 ** gcd-based scale factor or f_Scale.
713 */
Zhaoxiu Zeng917d11a2016-04-27 04:07:08 -0300714 lo_gcd = gcd(f_LO1, f_LO2);
715 gd_Scale = max((u32) gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300716 hgds = gd_Scale / 2;
Zhaoxiu Zeng917d11a2016-04-27 04:07:08 -0300717 gc_Scale = max((u32) gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300718 hgcs = gc_Scale / 2;
Zhaoxiu Zeng917d11a2016-04-27 04:07:08 -0300719 gf_Scale = max((u32) gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300720 hgfs = gf_Scale / 2;
721
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300722 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300723
724 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
725 for (n = n0; n <= pAS_Info->maxH1; ++n) {
726 md = (n * ((f_LO1 + hgds) / gd_Scale) -
727 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
728
729 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
730 if (md >= pAS_Info->maxH1)
731 break;
732
733 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
734 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
735
736 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
737 if (md == ma)
738 continue;
739
740 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
741 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
742 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300743 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
744 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300745 f_Spur =
746 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
747 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
748
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300749 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
750 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300751 return 1;
752 }
753
754 /* Location of Zero-IF-spur to be checked */
755 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
756 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
757 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
758 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
759 if (me != mf) {
760 f_nsLO1 = n * (f_LO1 / gf_Scale);
761 f_nsLO2 = me * (f_LO2 / gf_Scale);
762 f_Spur =
763 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
764 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
765
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300766 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
767 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300768 return 1;
769 }
770
771 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
772 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
773 if (ma != mb) {
774 f_nsLO1 = n * (f_LO1 / gc_Scale);
775 f_nsLO2 = ma * (f_LO2 / gc_Scale);
776 f_Spur =
777 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
778 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
779
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300780 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
781 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300782 return 1;
783 }
784 }
785
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300786 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300787 return 0;
788}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300789
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300790/*
791 * MT_AvoidSpurs() - Main entry point to avoid spurs.
792 * Checks for existing spurs in present LO1, LO2 freqs
793 * and if present, chooses spur-free LO1, LO2 combination
794 * that tunes the same input/output frequencies.
795 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300796static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300797{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300798 int status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300799 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300800 pAS_Info->bSpurAvoided = 0;
801 pAS_Info->nSpursFound = 0;
802
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300803 dprintk(2, "\n");
804
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300805 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300806 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300807
808 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300809 * Avoid LO Generated Spurs
810 *
811 * Make sure that have no LO-related spurs within the IF output
812 * bandwidth.
813 *
814 * If there is an LO spur in this band, start at the current IF1 frequency
815 * and work out until we find a spur-free frequency or run up against the
816 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
817 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300818 */
819 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
820 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300821 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
822 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
823 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
824 u32 delta_IF1;
825 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300826
827 /*
828 ** Spur was found, attempt to find a spur-free 1st IF
829 */
830 do {
831 pAS_Info->nSpursFound++;
832
833 /* Raise f_IF1_upper, if needed */
834 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
835
836 /* Choose next IF1 that is closest to f_IF1_CENTER */
837 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
838
839 if (new_IF1 > zfIF1) {
840 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
841 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
842 } else {
843 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
844 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
845 }
846 zfIF1 = new_IF1;
847
848 if (zfIF1 > pAS_Info->f_if1_Center)
849 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
850 else
851 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300852
853 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300854 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300855 * Continue while the new 1st IF is still within the 1st IF bandwidth
856 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300857 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300858 } while ((2 * delta_IF1 + pAS_Info->f_out_bw <= pAS_Info->f_if1_bw) && pAS_Info->bSpurPresent);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300859
860 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300861 * Use the LO-spur free values found. If the search went all
862 * the way to the 1st IF band edge and always found spurs, just
863 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300864 */
865 if (pAS_Info->bSpurPresent == 1) {
866 status |= MT2063_SPUR_PRESENT_ERR;
867 pAS_Info->f_LO1 = zfLO1;
868 pAS_Info->f_LO2 = zfLO2;
869 } else
870 pAS_Info->bSpurAvoided = 1;
871 }
872
873 status |=
874 ((pAS_Info->
875 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
876
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300877 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300878}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300879
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300880/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300881 * Constants used by the tuning algorithm
882 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300883#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
884#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
885#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
886#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
887#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
888#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
889#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
890#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
891#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
892#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
893#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
894#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
895#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
896#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
897#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
898#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
899#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
900#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
901
902/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300903 * Define the supported Part/Rev codes for the MT2063
904 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300905#define MT2063_B0 (0x9B)
906#define MT2063_B1 (0x9C)
907#define MT2063_B2 (0x9D)
908#define MT2063_B3 (0x9E)
909
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300910/**
911 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
912 *
913 * @state: struct mt2063_state pointer
914 *
915 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
916 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300917static int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300918{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300919 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
920 const u32 nPollRate = 2; /* poll status bits every 2 ms */
921 const u32 nMaxLoops = nMaxWait / nPollRate;
922 const u8 LO1LK = 0x80;
923 u8 LO2LK = 0x08;
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300924 int status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300925 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300926
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300927 dprintk(2, "\n");
928
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300929 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300930 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300931 LO2LK = 0x40;
932
933 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300934 status = mt2063_read(state, MT2063_REG_LO_STATUS,
935 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300936
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300937 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300938 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300939
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300940 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300941 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300942 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300943 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300944 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300945 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300946
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300947 /*
948 * Got no lock or partial lock
949 */
950 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300951}
952
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300953/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300954 * Constants for setting receiver modes.
955 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
956 * (DNC1GC & DNC2GC are the values, which are used, when the specific
957 * DNC Output is selected, the other is always off)
958 *
959 * enum mt2063_delivery_sys
960 * -------------+----------------------------------------------
961 * Mode 0 : | MT2063_CABLE_QAM
962 * Mode 1 : | MT2063_CABLE_ANALOG
963 * Mode 2 : | MT2063_OFFAIR_COFDM
964 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
965 * Mode 4 : | MT2063_OFFAIR_ANALOG
966 * Mode 5 : | MT2063_OFFAIR_8VSB
967 * --------------+----------------------------------------------
968 *
969 * |<---------- Mode -------------->|
970 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
971 * ------------+-----+-----+-----+-----+-----+-----+
972 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
973 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
974 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
975 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
976 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
977 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
978 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
979 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
980 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
981 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
982 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
983 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
984 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
985 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
986 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
987 */
988
989enum mt2063_delivery_sys {
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -0300990 MT2063_CABLE_QAM = 0,
991 MT2063_CABLE_ANALOG,
992 MT2063_OFFAIR_COFDM,
993 MT2063_OFFAIR_COFDM_SAWLESS,
994 MT2063_OFFAIR_ANALOG,
995 MT2063_OFFAIR_8VSB,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300996 MT2063_NUM_RCVR_MODES
997};
998
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -0300999static const char *mt2063_mode_name[] = {
1000 [MT2063_CABLE_QAM] = "digital cable",
1001 [MT2063_CABLE_ANALOG] = "analog cable",
1002 [MT2063_OFFAIR_COFDM] = "digital offair",
1003 [MT2063_OFFAIR_COFDM_SAWLESS] = "digital offair without SAW",
1004 [MT2063_OFFAIR_ANALOG] = "analog offair",
1005 [MT2063_OFFAIR_8VSB] = "analog offair 8vsb",
1006};
1007
1008static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
1009static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
1010static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
1011static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
1012static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
1013static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
1014static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
1015static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
1016static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1017static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
1018static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
1019static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1020static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
1021static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001022
1023/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001024 * mt2063_set_dnc_output_enable()
1025 */
1026static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001027 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001028{
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001029 dprintk(2, "\n");
1030
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001031 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1032 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1033 *pValue = MT2063_DNC_NONE;
1034 else
1035 *pValue = MT2063_DNC_2;
1036 } else { /* DNC1 is on */
1037 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1038 *pValue = MT2063_DNC_1;
1039 else
1040 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001041 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001042 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001043}
1044
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001045/*
1046 * mt2063_set_dnc_output_enable()
1047 */
1048static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001049 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001050{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001051 int status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001052 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001053
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001054 dprintk(2, "\n");
1055
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001056 /* selects, which DNC output is used */
1057 switch (nValue) {
1058 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001059 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1060 if (state->reg[MT2063_REG_DNC_GAIN] !=
1061 val)
1062 status |=
1063 mt2063_setreg(state,
1064 MT2063_REG_DNC_GAIN,
1065 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001066
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001067 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1068 if (state->reg[MT2063_REG_VGA_GAIN] !=
1069 val)
1070 status |=
1071 mt2063_setreg(state,
1072 MT2063_REG_VGA_GAIN,
1073 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001074
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001075 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1076 if (state->reg[MT2063_REG_RSVD_20] !=
1077 val)
1078 status |=
1079 mt2063_setreg(state,
1080 MT2063_REG_RSVD_20,
1081 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001082
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001083 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001084 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001085 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1086 if (state->reg[MT2063_REG_DNC_GAIN] !=
1087 val)
1088 status |=
1089 mt2063_setreg(state,
1090 MT2063_REG_DNC_GAIN,
1091 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001092
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001093 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1094 if (state->reg[MT2063_REG_VGA_GAIN] !=
1095 val)
1096 status |=
1097 mt2063_setreg(state,
1098 MT2063_REG_VGA_GAIN,
1099 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001100
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001101 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1102 if (state->reg[MT2063_REG_RSVD_20] !=
1103 val)
1104 status |=
1105 mt2063_setreg(state,
1106 MT2063_REG_RSVD_20,
1107 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001108
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001109 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001110 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001111 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1112 if (state->reg[MT2063_REG_DNC_GAIN] !=
1113 val)
1114 status |=
1115 mt2063_setreg(state,
1116 MT2063_REG_DNC_GAIN,
1117 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001118
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001119 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1120 if (state->reg[MT2063_REG_VGA_GAIN] !=
1121 val)
1122 status |=
1123 mt2063_setreg(state,
1124 MT2063_REG_VGA_GAIN,
1125 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001126
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001127 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1128 if (state->reg[MT2063_REG_RSVD_20] !=
1129 val)
1130 status |=
1131 mt2063_setreg(state,
1132 MT2063_REG_RSVD_20,
1133 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001134
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001135 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001136 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001137 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1138 if (state->reg[MT2063_REG_DNC_GAIN] !=
1139 val)
1140 status |=
1141 mt2063_setreg(state,
1142 MT2063_REG_DNC_GAIN,
1143 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001144
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001145 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1146 if (state->reg[MT2063_REG_VGA_GAIN] !=
1147 val)
1148 status |=
1149 mt2063_setreg(state,
1150 MT2063_REG_VGA_GAIN,
1151 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001152
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001153 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1154 if (state->reg[MT2063_REG_RSVD_20] !=
1155 val)
1156 status |=
1157 mt2063_setreg(state,
1158 MT2063_REG_RSVD_20,
1159 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001160
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001161 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001162 default:
1163 break;
1164 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001165
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001166 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001167}
1168
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001169/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001170 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1171 * the selected enum mt2063_delivery_sys type.
1172 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001173 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1174 * DNC Output is selected, the other is always off)
1175 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001176 * @state: ptr to mt2063_state structure
Jonathan McCrohan39c1cb22013-10-20 21:34:01 -03001177 * @Mode: desired receiver delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001178 *
1179 * Note: Register cache must be valid for it to work
1180 */
1181
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001182static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001183 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001184{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001185 int status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001186 u8 val;
1187 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001188
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001189 dprintk(2, "\n");
1190
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001191 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001192 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001193
1194 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001195 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001196 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001197 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001198 reg[MT2063_REG_PD1_TGT] & ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001199 ? 0x40 :
1200 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001201 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001202 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001203 }
1204
1205 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001206 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001207 u8 val = (state->reg[MT2063_REG_CTRL_2C] & ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001208 (LNARIN[Mode] & 0x03);
1209 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001210 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001211 }
1212
1213 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001214 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001215 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001216 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001217 reg[MT2063_REG_FIFF_CTRL2] & ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001218 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001219 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001220 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001221 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001222 /* trigger FIFF calibration, needed after changing FIFFQ */
1223 val =
Hans Verkuilfe10b842014-08-21 11:01:23 -03001224 (state->reg[MT2063_REG_FIFF_CTRL] | 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001225 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001226 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001227 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001228 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001229 reg[MT2063_REG_FIFF_CTRL] & ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001230 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001231 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001232 }
1233 }
1234
1235 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001236 status |= mt2063_get_dnc_output_enable(state, &longval);
1237 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001238
1239 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001240 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001241 u8 val = (state->reg[MT2063_REG_LNA_OV] & ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001242 (ACLNAMAX[Mode] & 0x1F);
1243 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001244 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001245 }
1246
1247 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001248 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001249 u8 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001250 (LNATGT[Mode] & 0x3F);
1251 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001252 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001253 }
1254
1255 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001256 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001257 u8 val = (state->reg[MT2063_REG_RF_OV] & ~0x1F) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001258 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001259 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001260 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001261 }
1262
1263 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001264 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001265 u8 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001266 (PD1TGT[Mode] & 0x3F);
1267 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001268 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001269 }
1270
1271 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001272 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001273 u8 val = ACFIFMAX[Mode];
1274 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1275 val = 5;
Hans Verkuilfe10b842014-08-21 11:01:23 -03001276 val = (state->reg[MT2063_REG_FIF_OV] & ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001277 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001278 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001279 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001280 }
1281
1282 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001283 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001284 u8 val = (state->reg[MT2063_REG_PD2_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001285 (PD2TGT[Mode] & 0x3F);
1286 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001287 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001288 }
1289
1290 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001291 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001292 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x80) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001293 (RFOVDIS[Mode] ? 0x80 : 0x00);
1294 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001295 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001296 }
1297
1298 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001299 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001300 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x80) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001301 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1302 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001303 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001304 }
1305
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001306 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001307 state->rcvr_mode = Mode;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001308 dprintk(1, "mt2063 mode changed to %s\n",
1309 mt2063_mode_name[state->rcvr_mode]);
1310 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001311
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001312 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001313}
1314
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001315/*
1316 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1317 * sections of the MT2063
1318 *
1319 * @Bits: Mask bits to be cleared.
1320 *
1321 * See definition of MT2063_Mask_Bits type for description
1322 * of each of the power bits.
1323 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001324static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1325 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001326{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001327 int status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001328
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001329 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001330 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1331 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001332 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001333 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001334 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001335 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001336 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001337 }
1338 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001339 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001340 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001341 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001342 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001343 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001344 }
1345
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001346 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001347}
1348
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001349/*
1350 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1351 * When Shutdown is 1, any section whose power
1352 * mask is set will be shutdown.
1353 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001354static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001355{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001356 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001357
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001358 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001359 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001360 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001361 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001362 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001363
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001364 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001365 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001366 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001367
1368 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001369 state->reg[MT2063_REG_BYP_CTRL] =
1370 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001371 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001372 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001373 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001374 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001375 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001376 state->reg[MT2063_REG_BYP_CTRL] =
1377 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001378 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001379 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001380 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001381 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001382 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001383 }
1384
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001385 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001386}
1387
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001388static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001389{
1390 return f_ref * (f_LO / f_ref)
1391 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1392}
1393
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001394/**
1395 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1396 * This function preserves maximum precision without
1397 * risk of overflow. It accurately calculates
1398 * f_ref * num / denom to within 1 HZ with fixed math.
1399 *
1400 * @num : Fractional portion of the multiplier
1401 * @denom: denominator portion of the ratio
1402 * @f_Ref: SRO frequency.
1403 *
1404 * This calculation handles f_ref as two separate 14-bit fields.
1405 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1406 * This is the genesis of the magic number "14" and the magic mask value of
1407 * 0x03FFF.
1408 *
1409 * This routine successfully handles denom values up to and including 2^18.
1410 * Returns: f_ref * num / denom
1411 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001412static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001413{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001414 u32 t1 = (f_ref >> 14) * num;
1415 u32 term1 = t1 / denom;
1416 u32 loss = t1 % denom;
1417 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001418 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001419 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001420}
1421
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001422/*
1423 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1424 * value for a FracN PLL.
1425 *
1426 * This function assumes that the f_LO and f_Ref are
1427 * evenly divisible by f_LO_Step.
1428 *
1429 * @Div: OUTPUT: Whole number portion of the multiplier
1430 * @FracN: OUTPUT: Fractional portion of the multiplier
1431 * @f_LO: desired LO frequency.
1432 * @f_LO_Step: Minimum step size for the LO (in Hz).
1433 * @f_Ref: SRO frequency.
1434 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1435 * of f_Ref (in Hz).
1436 *
1437 * Returns: Recalculated LO frequency.
1438 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001439static u32 MT2063_CalcLO1Mult(u32 *Div,
1440 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001441 u32 f_LO,
1442 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001443{
1444 /* Calculate the whole number portion of the divider */
1445 *Div = f_LO / f_Ref;
1446
1447 /* Calculate the numerator value (round to nearest f_LO_Step) */
1448 *FracN =
1449 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1450 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1451
1452 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1453}
1454
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001455/**
1456 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1457 * value for a FracN PLL.
1458 *
1459 * This function assumes that the f_LO and f_Ref are
1460 * evenly divisible by f_LO_Step.
1461 *
1462 * @Div: OUTPUT: Whole number portion of the multiplier
1463 * @FracN: OUTPUT: Fractional portion of the multiplier
1464 * @f_LO: desired LO frequency.
1465 * @f_LO_Step: Minimum step size for the LO (in Hz).
1466 * @f_Ref: SRO frequency.
1467 * @f_Avoid: Range of PLL frequencies to avoid near
1468 * integer multiples of f_Ref (in Hz).
1469 *
1470 * Returns: Recalculated LO frequency.
1471 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001472static u32 MT2063_CalcLO2Mult(u32 *Div,
1473 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001474 u32 f_LO,
1475 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001476{
1477 /* Calculate the whole number portion of the divider */
1478 *Div = f_LO / f_Ref;
1479
1480 /* Calculate the numerator value (round to nearest f_LO_Step) */
1481 *FracN =
1482 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1483 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1484
1485 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1486 8191);
1487}
1488
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001489/*
1490 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1491 * used for a given input frequency.
1492 *
1493 * @state: ptr to tuner data structure
1494 * @f_in: RF input center frequency (in Hz).
1495 *
1496 * Returns: ClearTune filter number (0-31)
1497 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001498static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001499{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001500 u32 RFBand;
1501 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001502
1503 /*
1504 ** Find RF Band setting
1505 */
1506 RFBand = 31; /* def when f_in > all */
1507 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001508 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001509 RFBand = idx;
1510 break;
1511 }
1512 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001513 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001514}
1515
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001516/*
1517 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1518 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001519static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001520{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001521
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001522 int status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001523 u32 LO1; /* 1st LO register value */
1524 u32 Num1; /* Numerator for LO1 reg. value */
1525 u32 f_IF1; /* 1st IF requested */
1526 u32 LO2; /* 2nd LO register value */
1527 u32 Num2; /* Numerator for LO2 reg. value */
1528 u32 ofLO1, ofLO2; /* last time's LO frequencies */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001529 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1530 u32 fiffof; /* Offset from FIFF center freq */
1531 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1532 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1533 u8 val;
1534 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001535
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001536 dprintk(2, "\n");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001537 /* Check the input and output frequency ranges */
1538 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001539 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001540
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001541 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1542 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001543 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001544
1545 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001546 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001547 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001548 ofLO1 = state->AS_Data.f_LO1;
Mauro Carvalho Chehabb5a91062011-07-22 17:07:17 -03001549 ofLO2 = state->AS_Data.f_LO2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001550
1551 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001552 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001553 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001554 if (state->ctfilt_sw == 1) {
1555 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1556 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001557 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001558 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001559 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001560 val = state->reg[MT2063_REG_CTUNE_OV];
1561 RFBand = FindClearTuneFilter(state, f_in);
1562 state->reg[MT2063_REG_CTUNE_OV] =
1563 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001564 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001565 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001566 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001567 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001568 }
1569 }
1570
1571 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001572 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001573 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001574 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001575 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001576 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001577 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001578 &state->reg[MT2063_REG_FIFFC], 1);
1579 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001580 }
1581 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001582 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001583 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001584 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001585 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001586 state->AS_Data.f_if1_Request =
1587 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1588 state->AS_Data.f_LO1_Step,
1589 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001590
1591 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001592 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1593 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001594 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001595 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001596
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001597 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001598
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001599 state->AS_Data.f_LO1 =
1600 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1601 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001602
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001603 state->AS_Data.f_LO2 =
1604 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1605 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001606
1607 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001608 * Check for any LO spurs in the output bandwidth and adjust
1609 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001610 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001611 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001612 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001613 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1614 * Recalculate the LO frequencies and the values to be placed
1615 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001616 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001617 state->AS_Data.f_LO1 =
1618 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1619 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1620 state->AS_Data.f_LO2 =
1621 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1622 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1623 state->AS_Data.f_LO2 =
1624 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1625 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001626
1627 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001628 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001629 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001630 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1631 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001632 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001633 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1634 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001635 status |= MT2063_DNC_RANGE;
1636 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001637 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001638 LO2LK = 0x40;
1639
1640 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001641 * If we have the same LO frequencies and we're already locked,
1642 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001643 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001644 if ((ofLO1 != state->AS_Data.f_LO1)
1645 || (ofLO2 != state->AS_Data.f_LO2)
1646 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001647 (LO1LK | LO2LK))) {
1648 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001649 * Calculate the FIFFOF register value
1650 *
1651 * IF1_Actual
1652 * FIFFOF = ------------ - 8 * FIFFC - 4992
1653 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001654 */
1655 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001656 (state->AS_Data.f_LO1 -
1657 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001658 4992;
1659 if (fiffof > 0xFF)
1660 fiffof = 0xFF;
1661
1662 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001663 * Place all of the calculated values into the local tuner
1664 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001665 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001666 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001667 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1668 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1669 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001670 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001671 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1672 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001673
1674 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001675 * Now write out the computed register values
1676 * IMPORTANT: There is a required order for writing
1677 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001678 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001679 status |= mt2063_write(state, MT2063_REG_LO1CQ_1, &state->reg[MT2063_REG_LO1CQ_1], 5); /* 0x01 - 0x05 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001680 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001681 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001682 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001683 }
1684 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001685 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001686 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001687 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001688 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001689 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001690 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001691 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001692 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001693 reg[MT2063_REG_FIFF_OFFSET],
1694 1);
1695 }
1696 }
1697
1698 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001699 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001700 */
1701
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001702 if (status < 0)
1703 return status;
1704
1705 status = mt2063_lockStatus(state);
1706 if (status < 0)
1707 return status;
1708 if (!status)
1709 return -EINVAL; /* Couldn't lock */
1710
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001711 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001712 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001713 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001714 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001715 }
1716
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001717 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001718}
1719
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001720static const u8 MT2063B0_defaults[] = {
1721 /* Reg, Value */
1722 0x19, 0x05,
1723 0x1B, 0x1D,
1724 0x1C, 0x1F,
1725 0x1D, 0x0F,
1726 0x1E, 0x3F,
1727 0x1F, 0x0F,
1728 0x20, 0x3F,
1729 0x22, 0x21,
1730 0x23, 0x3F,
1731 0x24, 0x20,
1732 0x25, 0x3F,
1733 0x27, 0xEE,
1734 0x2C, 0x27, /* bit at 0x20 is cleared below */
1735 0x30, 0x03,
1736 0x2C, 0x07, /* bit at 0x20 is cleared here */
1737 0x2D, 0x87,
1738 0x2E, 0xAA,
1739 0x28, 0xE1, /* Set the FIFCrst bit here */
1740 0x28, 0xE0, /* Clear the FIFCrst bit here */
1741 0x00
1742};
1743
1744/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1745static const u8 MT2063B1_defaults[] = {
1746 /* Reg, Value */
1747 0x05, 0xF0,
1748 0x11, 0x10, /* New Enable AFCsd */
1749 0x19, 0x05,
1750 0x1A, 0x6C,
1751 0x1B, 0x24,
1752 0x1C, 0x28,
1753 0x1D, 0x8F,
1754 0x1E, 0x14,
1755 0x1F, 0x8F,
1756 0x20, 0x57,
1757 0x22, 0x21, /* New - ver 1.03 */
1758 0x23, 0x3C, /* New - ver 1.10 */
1759 0x24, 0x20, /* New - ver 1.03 */
1760 0x2C, 0x24, /* bit at 0x20 is cleared below */
1761 0x2D, 0x87, /* FIFFQ=0 */
1762 0x2F, 0xF3,
1763 0x30, 0x0C, /* New - ver 1.11 */
1764 0x31, 0x1B, /* New - ver 1.11 */
1765 0x2C, 0x04, /* bit at 0x20 is cleared here */
1766 0x28, 0xE1, /* Set the FIFCrst bit here */
1767 0x28, 0xE0, /* Clear the FIFCrst bit here */
1768 0x00
1769};
1770
1771/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1772static const u8 MT2063B3_defaults[] = {
1773 /* Reg, Value */
1774 0x05, 0xF0,
1775 0x19, 0x3D,
1776 0x2C, 0x24, /* bit at 0x20 is cleared below */
1777 0x2C, 0x04, /* bit at 0x20 is cleared here */
1778 0x28, 0xE1, /* Set the FIFCrst bit here */
1779 0x28, 0xE0, /* Clear the FIFCrst bit here */
1780 0x00
1781};
1782
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001783static int mt2063_init(struct dvb_frontend *fe)
1784{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001785 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001786 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001787 u8 all_resets = 0xF0; /* reset/load bits */
1788 const u8 *def = NULL;
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001789 char *step;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001790 u32 FCRUN;
1791 s32 maxReads;
1792 u32 fcu_osc;
1793 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001794
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001795 dprintk(2, "\n");
1796
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001797 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001798
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001799 /* Read the Part/Rev code from the tuner */
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001800 status = mt2063_read(state, MT2063_REG_PART_REV,
1801 &state->reg[MT2063_REG_PART_REV], 1);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001802 if (status < 0) {
1803 printk(KERN_ERR "Can't read mt2063 part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001804 return status;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001805 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001806
1807 /* Check the part/rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001808 switch (state->reg[MT2063_REG_PART_REV]) {
1809 case MT2063_B0:
1810 step = "B0";
1811 break;
1812 case MT2063_B1:
1813 step = "B1";
1814 break;
1815 case MT2063_B2:
1816 step = "B2";
1817 break;
1818 case MT2063_B3:
1819 step = "B3";
1820 break;
1821 default:
1822 printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n",
1823 state->reg[MT2063_REG_PART_REV]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001824 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001825 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001826
1827 /* Check the 2nd byte of the Part/Rev code from the tuner */
1828 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1829 &state->reg[MT2063_REG_RSVD_3B], 1);
1830
1831 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001832 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001833 printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n",
1834 state->reg[MT2063_REG_PART_REV],
1835 state->reg[MT2063_REG_RSVD_3B]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001836 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001837 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001838
Mauro Carvalho Chehabd1244f72011-07-23 11:55:57 -03001839 printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step);
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001840
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001841 /* Reset the tuner */
1842 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1843 if (status < 0)
1844 return status;
1845
1846 /* change all of the default values that vary from the HW reset values */
1847 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1848 switch (state->reg[MT2063_REG_PART_REV]) {
1849 case MT2063_B3:
1850 def = MT2063B3_defaults;
1851 break;
1852
1853 case MT2063_B1:
1854 def = MT2063B1_defaults;
1855 break;
1856
1857 case MT2063_B0:
1858 def = MT2063B0_defaults;
1859 break;
1860
1861 default:
1862 return -ENODEV;
1863 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001864 }
1865
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001866 while (status >= 0 && *def) {
1867 u8 reg = *def++;
1868 u8 val = *def++;
1869 status = mt2063_write(state, reg, &val, 1);
1870 }
1871 if (status < 0)
1872 return status;
1873
1874 /* Wait for FIFF location to complete. */
1875 FCRUN = 1;
1876 maxReads = 10;
1877 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1878 msleep(2);
1879 status = mt2063_read(state,
1880 MT2063_REG_XO_STATUS,
1881 &state->
1882 reg[MT2063_REG_XO_STATUS], 1);
1883 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1884 }
1885
1886 if (FCRUN != 0 || status < 0)
1887 return -ENODEV;
1888
1889 status = mt2063_read(state,
1890 MT2063_REG_FIFFC,
1891 &state->reg[MT2063_REG_FIFFC], 1);
1892 if (status < 0)
1893 return status;
1894
1895 /* Read back all the registers from the tuner */
1896 status = mt2063_read(state,
1897 MT2063_REG_PART_REV,
1898 state->reg, MT2063_REG_END_REGS);
1899 if (status < 0)
1900 return status;
1901
1902 /* Initialize the tuner state. */
1903 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1904 state->AS_Data.f_ref = MT2063_REF_FREQ;
1905 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1906 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1907 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1908 state->AS_Data.f_out = 43750000UL;
1909 state->AS_Data.f_out_bw = 6750000UL;
1910 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1911 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1912 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1913 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1914 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1915 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1916 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1917 state->AS_Data.f_LO1 = 2181000000UL;
1918 state->AS_Data.f_LO2 = 1486249786UL;
1919 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1920 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1921 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1922 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1923 state->num_regs = MT2063_REG_END_REGS;
1924 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1925 state->ctfilt_sw = 0;
1926
1927 state->CTFiltMax[0] = 69230000;
1928 state->CTFiltMax[1] = 105770000;
1929 state->CTFiltMax[2] = 140350000;
1930 state->CTFiltMax[3] = 177110000;
1931 state->CTFiltMax[4] = 212860000;
1932 state->CTFiltMax[5] = 241130000;
1933 state->CTFiltMax[6] = 274370000;
1934 state->CTFiltMax[7] = 309820000;
1935 state->CTFiltMax[8] = 342450000;
1936 state->CTFiltMax[9] = 378870000;
1937 state->CTFiltMax[10] = 416210000;
1938 state->CTFiltMax[11] = 456500000;
1939 state->CTFiltMax[12] = 495790000;
1940 state->CTFiltMax[13] = 534530000;
1941 state->CTFiltMax[14] = 572610000;
1942 state->CTFiltMax[15] = 598970000;
1943 state->CTFiltMax[16] = 635910000;
1944 state->CTFiltMax[17] = 672130000;
1945 state->CTFiltMax[18] = 714840000;
1946 state->CTFiltMax[19] = 739660000;
1947 state->CTFiltMax[20] = 770410000;
1948 state->CTFiltMax[21] = 814660000;
1949 state->CTFiltMax[22] = 846950000;
1950 state->CTFiltMax[23] = 867820000;
1951 state->CTFiltMax[24] = 915980000;
1952 state->CTFiltMax[25] = 947450000;
1953 state->CTFiltMax[26] = 983110000;
1954 state->CTFiltMax[27] = 1021630000;
1955 state->CTFiltMax[28] = 1061870000;
1956 state->CTFiltMax[29] = 1098330000;
1957 state->CTFiltMax[30] = 1138990000;
1958
1959 /*
1960 ** Fetch the FCU osc value and use it and the fRef value to
1961 ** scale all of the Band Max values
1962 */
1963
1964 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1965 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1966 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1967 if (status < 0)
1968 return status;
1969
1970 /* Read the ClearTune filter calibration value */
1971 status = mt2063_read(state, MT2063_REG_FIFFC,
1972 &state->reg[MT2063_REG_FIFFC], 1);
1973 if (status < 0)
1974 return status;
1975
1976 fcu_osc = state->reg[MT2063_REG_FIFFC];
1977
1978 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1979 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1980 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1981 if (status < 0)
1982 return status;
1983
1984 /* Adjust each of the values in the ClearTune filter cross-over table */
1985 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001986 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001987
1988 status = MT2063_SoftwareShutdown(state, 1);
1989 if (status < 0)
1990 return status;
1991 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1992 if (status < 0)
1993 return status;
1994
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03001995 state->init = true;
1996
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001997 return 0;
1998}
1999
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002000static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002001{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03002002 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002003 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002004
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002005 dprintk(2, "\n");
2006
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002007 if (!state->init)
2008 return -ENODEV;
2009
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002010 *tuner_status = 0;
2011 status = mt2063_lockStatus(state);
2012 if (status < 0)
2013 return status;
2014 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002015 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002016
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002017 dprintk(1, "Tuner status: %d", *tuner_status);
2018
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002019 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002020}
2021
Max Kellermann194ced72016-08-09 18:32:31 -03002022static void mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002023{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002024 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002025
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002026 dprintk(2, "\n");
2027
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002028 fe->tuner_priv = NULL;
2029 kfree(state);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002030}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002031
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002032static int mt2063_set_analog_params(struct dvb_frontend *fe,
2033 struct analog_parameters *params)
2034{
2035 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002036 s32 pict_car;
2037 s32 pict2chanb_vsb;
2038 s32 ch_bw;
2039 s32 if_mid;
2040 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002041 int status;
2042
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002043 dprintk(2, "\n");
2044
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002045 if (!state->init) {
2046 status = mt2063_init(fe);
2047 if (status < 0)
2048 return status;
2049 }
2050
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002051 switch (params->mode) {
2052 case V4L2_TUNER_RADIO:
2053 pict_car = 38900000;
2054 ch_bw = 8000000;
2055 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002056 rcvr_mode = MT2063_OFFAIR_ANALOG;
2057 break;
2058 case V4L2_TUNER_ANALOG_TV:
2059 rcvr_mode = MT2063_CABLE_ANALOG;
2060 if (params->std & ~V4L2_STD_MN) {
2061 pict_car = 38900000;
2062 ch_bw = 6000000;
2063 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002064 } else if (params->std & V4L2_STD_PAL_G) {
2065 pict_car = 38900000;
2066 ch_bw = 7000000;
2067 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002068 } else { /* PAL/SECAM standards */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002069 pict_car = 38900000;
2070 ch_bw = 8000000;
2071 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002072 }
2073 break;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002074 default:
2075 return -EINVAL;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002076 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002077 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2078
2079 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2080 state->AS_Data.f_out = if_mid;
2081 state->AS_Data.f_out_bw = ch_bw + 750000;
2082 status = MT2063_SetReceiverMode(state, rcvr_mode);
2083 if (status < 0)
2084 return status;
2085
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002086 dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n",
2087 params->frequency, ch_bw, pict2chanb_vsb);
2088
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002089 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2090 if (status < 0)
2091 return status;
2092
2093 state->frequency = params->frequency;
2094 return 0;
2095}
2096
2097/*
2098 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
Jonathan McCrohan39c1cb22013-10-20 21:34:01 -03002099 * So, the amount of the needed bandwidth is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002100 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002101 * As such, the maximum symbol rate supported by 6 MHz is given by:
2102 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2103 */
2104#define MAX_SYMBOL_RATE_6MHz 5217391
2105
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002106static int mt2063_set_params(struct dvb_frontend *fe)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002107{
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002108 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002109 struct mt2063_state *state = fe->tuner_priv;
2110 int status;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002111 s32 pict_car;
2112 s32 pict2chanb_vsb;
2113 s32 ch_bw;
2114 s32 if_mid;
2115 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002116
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002117 if (!state->init) {
2118 status = mt2063_init(fe);
2119 if (status < 0)
2120 return status;
2121 }
2122
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002123 dprintk(2, "\n");
2124
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002125 if (c->bandwidth_hz == 0)
2126 return -EINVAL;
2127 if (c->bandwidth_hz <= 6000000)
2128 ch_bw = 6000000;
2129 else if (c->bandwidth_hz <= 7000000)
2130 ch_bw = 7000000;
2131 else
2132 ch_bw = 8000000;
2133
2134 switch (c->delivery_system) {
2135 case SYS_DVBT:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002136 rcvr_mode = MT2063_OFFAIR_COFDM;
2137 pict_car = 36125000;
2138 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002139 break;
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002140 case SYS_DVBC_ANNEX_A:
2141 case SYS_DVBC_ANNEX_C:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002142 rcvr_mode = MT2063_CABLE_QAM;
2143 pict_car = 36125000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002144 pict2chanb_vsb = -(ch_bw / 2);
2145 break;
2146 default:
2147 return -EINVAL;
2148 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002149 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2150
2151 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2152 state->AS_Data.f_out = if_mid;
2153 state->AS_Data.f_out_bw = ch_bw + 750000;
2154 status = MT2063_SetReceiverMode(state, rcvr_mode);
2155 if (status < 0)
2156 return status;
2157
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002158 dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n",
2159 c->frequency, ch_bw, pict2chanb_vsb);
2160
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002161 status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2))));
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002162
2163 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002164 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002165
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002166 state->frequency = c->frequency;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002167 return 0;
2168}
2169
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002170static int mt2063_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002171{
2172 struct mt2063_state *state = fe->tuner_priv;
2173
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002174 dprintk(2, "\n");
2175
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002176 if (!state->init)
2177 return -ENODEV;
2178
Stefan Ringeleeecd0c2011-12-17 16:57:15 -03002179 *freq = state->AS_Data.f_out;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002180
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002181 dprintk(1, "IF frequency: %d\n", *freq);
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002182
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002183 return 0;
2184}
2185
2186static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2187{
2188 struct mt2063_state *state = fe->tuner_priv;
2189
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002190 dprintk(2, "\n");
2191
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002192 if (!state->init)
2193 return -ENODEV;
2194
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002195 *bw = state->AS_Data.f_out_bw - 750000;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002196
2197 dprintk(1, "bandwidth: %d\n", *bw);
2198
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002199 return 0;
2200}
2201
Julia Lawall96105142016-09-11 11:44:13 -03002202static const struct dvb_tuner_ops mt2063_ops = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002203 .info = {
2204 .name = "MT2063 Silicon Tuner",
2205 .frequency_min = 45000000,
Jose Alberto Reguerodb5823b2012-01-27 18:43:28 -03002206 .frequency_max = 865000000,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002207 .frequency_step = 0,
2208 },
2209
2210 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002211 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002212 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002213 .set_analog_params = mt2063_set_analog_params,
2214 .set_params = mt2063_set_params,
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002215 .get_if_frequency = mt2063_get_if_frequency,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002216 .get_bandwidth = mt2063_get_bandwidth,
2217 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002218};
2219
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002220struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2221 struct mt2063_config *config,
2222 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002223{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002224 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002225
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002226 dprintk(2, "\n");
2227
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002228 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Peter Senna Tschudin6f0fdc42012-09-12 08:55:57 -03002229 if (!state)
2230 return NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002231
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002232 state->config = config;
2233 state->i2c = i2c;
2234 state->frontend = fe;
2235 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002236 fe->tuner_priv = state;
2237 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002238
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002239 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002240 return fe;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002241}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002242EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002243
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002244#if 0
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002245/*
2246 * Ancillary routines visible outside mt2063
2247 * FIXME: Remove them in favor of using standard tuner callbacks
2248 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002249static int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002250{
2251 struct mt2063_state *state = fe->tuner_priv;
2252 int err = 0;
2253
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002254 dprintk(2, "\n");
2255
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002256 err = MT2063_SoftwareShutdown(state, 1);
2257 if (err < 0)
2258 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2259
2260 return err;
2261}
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002262
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002263static int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002264{
2265 struct mt2063_state *state = fe->tuner_priv;
2266 int err = 0;
2267
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002268 dprintk(2, "\n");
2269
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002270 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2271 if (err < 0)
2272 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2273
2274 return err;
2275}
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002276#endif
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002277
Mauro Carvalho Chehab37e59f82014-02-07 08:03:07 -02002278MODULE_AUTHOR("Mauro Carvalho Chehab");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002279MODULE_DESCRIPTION("MT2063 Silicon tuner");
2280MODULE_LICENSE("GPL");