blob: b2678a468a0e54486cc942c78b5db4ef64a42764 [file] [log] [blame]
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001/*
2 * Driver for mt2063 Micronas tuner
3 *
4 * Copyright (c) 2011 Mauro Carvalho Chehab <mchehab@redhat.com>
5 *
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>
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030027
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030028#include "mt2063.h"
29
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030030static unsigned int verbose;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030031module_param(verbose, int, 0644);
32
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -030033/* positive error codes used internally */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030034
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -030035/* Info: Unavoidable LO-related spur may be present in the output */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030036#define MT2063_SPUR_PRESENT_ERR (0x00800000)
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030037
38/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */
39#define MT2063_SPUR_CNT_MASK (0x001f0000)
40#define MT2063_SPUR_SHIFT (16)
41
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030042/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */
43#define MT2063_UPC_RANGE (0x04000000)
44
45/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */
46#define MT2063_DNC_RANGE (0x08000000)
47
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030048/*
49 * Constant defining the version of the following structure
50 * and therefore the API for this code.
51 *
52 * When compiling the tuner driver, the preprocessor will
53 * check against this version number to make sure that
54 * it matches the version that the tuner driver knows about.
55 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030056
57/* DECT Frequency Avoidance */
58#define MT2063_DECT_AVOID_US_FREQS 0x00000001
59
60#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002
61
62#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0)
63
64#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0)
65
66enum MT2063_DECT_Avoid_Type {
67 MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */
68 MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */
69 MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */
70 MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */
71};
72
73#define MT2063_MAX_ZONES 48
74
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030075struct MT2063_ExclZone_t {
76 u32 min_;
77 u32 max_;
78 struct MT2063_ExclZone_t *next_;
79};
80
81/*
82 * Structure of data needed for Spur Avoidance
83 */
84struct MT2063_AvoidSpursData_t {
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030085 u32 f_ref;
86 u32 f_in;
87 u32 f_LO1;
88 u32 f_if1_Center;
89 u32 f_if1_Request;
90 u32 f_if1_bw;
91 u32 f_LO2;
92 u32 f_out;
93 u32 f_out_bw;
94 u32 f_LO1_Step;
95 u32 f_LO2_Step;
96 u32 f_LO1_FracN_Avoid;
97 u32 f_LO2_FracN_Avoid;
98 u32 f_zif_bw;
99 u32 f_min_LO_Separation;
100 u32 maxH1;
101 u32 maxH2;
102 enum MT2063_DECT_Avoid_Type avoidDECT;
103 u32 bSpurPresent;
104 u32 bSpurAvoided;
105 u32 nSpursFound;
106 u32 nZones;
107 struct MT2063_ExclZone_t *freeZones;
108 struct MT2063_ExclZone_t *usedZones;
109 struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES];
110};
111
112/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300113 * Parameter for function MT2063_SetPowerMask that specifies the power down
114 * of various sections of the MT2063.
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300115 */
116enum MT2063_Mask_Bits {
117 MT2063_REG_SD = 0x0040, /* Shutdown regulator */
118 MT2063_SRO_SD = 0x0020, /* Shutdown SRO */
119 MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */
120 MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */
121 MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */
122 MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */
123 MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */
124 MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */
125 MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */
126 MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */
127 MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */
128 MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */
129 MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */
130 MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */
131 MT2063_NONE_SD = 0x0000 /* No shutdown bits */
132};
133
134/*
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300135 * Possible values for MT2063_DNC_OUTPUT
136 */
137enum MT2063_DNC_Output_Enable {
138 MT2063_DNC_NONE = 0,
139 MT2063_DNC_1,
140 MT2063_DNC_2,
141 MT2063_DNC_BOTH
142};
143
144/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300145 * Two-wire serial bus subaddresses of the tuner registers.
146 * Also known as the tuner's register addresses.
147 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300148enum MT2063_Register_Offsets {
149 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
150 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
151 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
152 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
153 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
154 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
155 MT2063_REG_RSVD_06, /* 0x06: Reserved */
156 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
157 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
158 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
159 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
160 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
161 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
162 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
163 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
164 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
165 MT2063_REG_RSVD_10, /* 0x10: Reserved */
166 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
167 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
168 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
169 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
170 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
171 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
172 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
173 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
174 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
175 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
176 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
177 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
178 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
179 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
180 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
181 MT2063_REG_RSVD_20, /* 0x20: Reserved */
182 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
183 MT2063_REG_RSVD_22, /* 0x22: Reserved */
184 MT2063_REG_RSVD_23, /* 0x23: Reserved */
185 MT2063_REG_RSVD_24, /* 0x24: Reserved */
186 MT2063_REG_RSVD_25, /* 0x25: Reserved */
187 MT2063_REG_RSVD_26, /* 0x26: Reserved */
188 MT2063_REG_RSVD_27, /* 0x27: Reserved */
189 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
190 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
191 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
192 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
193 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
194 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
195 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
196 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
197 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
198 MT2063_REG_RSVD_31, /* 0x31: Reserved */
199 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
200 MT2063_REG_RSVD_33, /* 0x33: Reserved */
201 MT2063_REG_RSVD_34, /* 0x34: Reserved */
202 MT2063_REG_RSVD_35, /* 0x35: Reserved */
203 MT2063_REG_RSVD_36, /* 0x36: Reserved */
204 MT2063_REG_RSVD_37, /* 0x37: Reserved */
205 MT2063_REG_RSVD_38, /* 0x38: Reserved */
206 MT2063_REG_RSVD_39, /* 0x39: Reserved */
207 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
208 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
209 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
210 MT2063_REG_END_REGS
211};
212
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300213struct mt2063_state {
214 struct i2c_adapter *i2c;
215
216 const struct mt2063_config *config;
217 struct dvb_tuner_ops ops;
218 struct dvb_frontend *frontend;
219 struct tuner_state status;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300220
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300221 u32 frequency;
222 u32 srate;
223 u32 bandwidth;
224 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300225
226 u32 tuner_id;
227 struct MT2063_AvoidSpursData_t AS_Data;
228 u32 f_IF1_actual;
229 u32 rcvr_mode;
230 u32 ctfilt_sw;
231 u32 CTFiltMax[31];
232 u32 num_regs;
233 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300234};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300235
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300236/*
237 * mt2063_write - Write data into the I2C bus
238 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300239static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300240{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300241 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300242 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300243 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300244 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300245 .addr = state->config->tuner_address,
246 .flags = 0,
247 .buf = buf,
248 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300249 };
250
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300251 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300252 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300253
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300254 if (fe->ops.i2c_gate_ctrl)
255 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300256 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300257 if (fe->ops.i2c_gate_ctrl)
258 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300259
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300260 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300261 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300262
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300263 return ret;
264}
265
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300266/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300267 * mt2063_write - Write register data into the I2C bus, caching the value
268 */
269static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
270{
271 u32 status;
272
273 if (reg >= MT2063_REG_END_REGS)
274 return -ERANGE;
275
276 status = mt2063_write(state, reg, &val, 1);
277 if (status < 0)
278 return status;
279
280 state->reg[reg] = val;
281
282 return 0;
283}
284
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300285/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300286 * mt2063_read - Read data from the I2C bus
287 */
288static u32 mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300289 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300290{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300291 u32 status = 0; /* Status to be returned */
292 struct dvb_frontend *fe = state->frontend;
293 u32 i = 0;
294
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300295 if (fe->ops.i2c_gate_ctrl)
296 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300297
298 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300299 int ret;
300 u8 b0[] = { subAddress + i };
301 struct i2c_msg msg[] = {
302 {
303 .addr = state->config->tuner_address,
304 .flags = I2C_M_RD,
305 .buf = b0,
306 .len = 1
307 }, {
308 .addr = state->config->tuner_address,
309 .flags = I2C_M_RD,
310 .buf = pData + 1,
311 .len = 1
312 }
313 };
314
315 ret = i2c_transfer(state->i2c, msg, 2);
316 if (ret < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300317 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300318 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300319 if (fe->ops.i2c_gate_ctrl)
320 fe->ops.i2c_gate_ctrl(fe, 0);
321
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300322 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300323}
324
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300325/*
326 * FIXME: Is this really needed?
327 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300328static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300329{
330 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300331 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300332 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300333 msleep(10);
334
335 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300336}
337
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300338/*
339 * Microtune spur avoidance
340 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300341
342/* Implement ceiling, floor functions. */
343#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300344#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300345
346struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300347 s32 min_;
348 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300349};
350
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300351static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
352 *pAS_Info,
353 struct MT2063_ExclZone_t *pPrevNode)
354{
355 struct MT2063_ExclZone_t *pNode;
356 /* Check for a node in the free list */
357 if (pAS_Info->freeZones != NULL) {
358 /* Use one from the free list */
359 pNode = pAS_Info->freeZones;
360 pAS_Info->freeZones = pNode->next_;
361 } else {
362 /* Grab a node from the array */
363 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
364 }
365
366 if (pPrevNode != NULL) {
367 pNode->next_ = pPrevNode->next_;
368 pPrevNode->next_ = pNode;
369 } else { /* insert at the beginning of the list */
370
371 pNode->next_ = pAS_Info->usedZones;
372 pAS_Info->usedZones = pNode;
373 }
374
375 pAS_Info->nZones++;
376 return pNode;
377}
378
379static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
380 *pAS_Info,
381 struct MT2063_ExclZone_t *pPrevNode,
382 struct MT2063_ExclZone_t
383 *pNodeToRemove)
384{
385 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
386
387 /* Make previous node point to the subsequent node */
388 if (pPrevNode != NULL)
389 pPrevNode->next_ = pNext;
390
391 /* Add pNodeToRemove to the beginning of the freeZones */
392 pNodeToRemove->next_ = pAS_Info->freeZones;
393 pAS_Info->freeZones = pNodeToRemove;
394
395 /* Decrement node count */
396 pAS_Info->nZones--;
397
398 return pNext;
399}
400
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300401/*
402 * MT_AddExclZone()
403 *
404 * Add (and merge) an exclusion zone into the list.
405 * If the range (f_min, f_max) is totally outside the
406 * 1st IF BW, ignore the entry.
407 * If the range (f_min, f_max) is negative, ignore the entry.
408 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300409static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300410 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300411{
412 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
413 struct MT2063_ExclZone_t *pPrev = NULL;
414 struct MT2063_ExclZone_t *pNext = NULL;
415
416 /* Check to see if this overlaps the 1st IF filter */
417 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
418 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
419 && (f_min < f_max)) {
420 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300421 * 1 2 3 4 5 6
422 *
423 * New entry: |---| |--| |--| |-| |---| |--|
424 * or or or or or
425 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300426 */
427
428 /* Check for our place in the list */
429 while ((pNode != NULL) && (pNode->max_ < f_min)) {
430 pPrev = pNode;
431 pNode = pNode->next_;
432 }
433
434 if ((pNode != NULL) && (pNode->min_ < f_max)) {
435 /* Combine me with pNode */
436 if (f_min < pNode->min_)
437 pNode->min_ = f_min;
438 if (f_max > pNode->max_)
439 pNode->max_ = f_max;
440 } else {
441 pNode = InsertNode(pAS_Info, pPrev);
442 pNode->min_ = f_min;
443 pNode->max_ = f_max;
444 }
445
446 /* Look for merging possibilities */
447 pNext = pNode->next_;
448 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
449 if (pNext->max_ > pNode->max_)
450 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300451 /* Remove pNext, return ptr to pNext->next */
452 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300453 }
454 }
455}
456
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300457/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300458 * Reset all exclusion zones.
459 * Add zones to protect the PLL FracN regions near zero
460 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300461static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
462{
463 u32 center;
464
465 pAS_Info->nZones = 0; /* this clears the used list */
466 pAS_Info->usedZones = NULL; /* reset ptr */
467 pAS_Info->freeZones = NULL; /* reset ptr */
468
469 center =
470 pAS_Info->f_ref *
471 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
472 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
473 while (center <
474 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
475 pAS_Info->f_LO1_FracN_Avoid) {
476 /* Exclude LO1 FracN */
477 MT2063_AddExclZone(pAS_Info,
478 center - pAS_Info->f_LO1_FracN_Avoid,
479 center - 1);
480 MT2063_AddExclZone(pAS_Info, center + 1,
481 center + pAS_Info->f_LO1_FracN_Avoid);
482 center += pAS_Info->f_ref;
483 }
484
485 center =
486 pAS_Info->f_ref *
487 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
488 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
489 while (center <
490 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
491 pAS_Info->f_LO2_FracN_Avoid) {
492 /* Exclude LO2 FracN */
493 MT2063_AddExclZone(pAS_Info,
494 center - pAS_Info->f_LO2_FracN_Avoid,
495 center - 1);
496 MT2063_AddExclZone(pAS_Info, center + 1,
497 center + pAS_Info->f_LO2_FracN_Avoid);
498 center += pAS_Info->f_ref;
499 }
500
501 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
502 /* Exclude LO1 values that conflict with DECT channels */
503 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
504 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
505 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
506 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
507 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
508 }
509
510 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
511 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
512 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
513 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
514 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
515 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
516 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
517 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
518 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
519 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
520 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
521 }
522}
523
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300524/*
525 * MT_ChooseFirstIF - Choose the best available 1st IF
526 * If f_Desired is not excluded, choose that first.
527 * Otherwise, return the value closest to f_Center that is
528 * not excluded
529 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300530static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300531{
532 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300533 * Update "f_Desired" to be the nearest "combinational-multiple" of
534 * "f_LO1_Step".
535 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
536 * And F_LO1 is the arithmetic sum of f_in + f_Center.
537 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
538 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300539 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300540 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300541 pAS_Info->f_LO1_Step *
542 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
543 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
544 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300545 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300546 (pAS_Info->f_LO1_Step >
547 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
548 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300549 u32 f_Center;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300550
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300551 s32 i;
552 s32 j = 0;
553 u32 bDesiredExcluded = 0;
554 u32 bZeroExcluded = 0;
555 s32 tmpMin, tmpMax;
556 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300557 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
558 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
559
560 if (pAS_Info->nZones == 0)
561 return f_Desired;
562
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300563 /*
564 * f_Center needs to be an integer multiple of f_Step away
565 * from f_Desired
566 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300567 if (pAS_Info->f_if1_Center > f_Desired)
568 f_Center =
569 f_Desired +
570 f_Step *
571 ((pAS_Info->f_if1_Center - f_Desired +
572 f_Step / 2) / f_Step);
573 else
574 f_Center =
575 f_Desired -
576 f_Step *
577 ((f_Desired - pAS_Info->f_if1_Center +
578 f_Step / 2) / f_Step);
579
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300580 /*
581 * Take MT_ExclZones, center around f_Center and change the
582 * resolution to f_Step
583 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300584 while (pNode != NULL) {
585 /* floor function */
586 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300587 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300588
589 /* ceil function */
590 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300591 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300592
593 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
594 bDesiredExcluded = 1;
595
596 if ((tmpMin < 0) && (tmpMax > 0))
597 bZeroExcluded = 1;
598
599 /* See if this zone overlaps the previous */
600 if ((j > 0) && (tmpMin < zones[j - 1].max_))
601 zones[j - 1].max_ = tmpMax;
602 else {
603 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300604 zones[j].min_ = tmpMin;
605 zones[j].max_ = tmpMax;
606 j++;
607 }
608 pNode = pNode->next_;
609 }
610
611 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300612 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300613 */
614 if (bDesiredExcluded == 0)
615 return f_Desired;
616
617 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300618 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300619 */
620 if (bZeroExcluded == 0)
621 return f_Center;
622
623 /* Find the value closest to 0 (f_Center) */
624 bestDiff = zones[0].min_;
625 for (i = 0; i < j; i++) {
626 if (abs(zones[i].min_) < abs(bestDiff))
627 bestDiff = zones[i].min_;
628 if (abs(zones[i].max_) < abs(bestDiff))
629 bestDiff = zones[i].max_;
630 }
631
632 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300633 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300634
635 return f_Center + (bestDiff * f_Step);
636}
637
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300638/**
639 * gcd() - Uses Euclid's algorithm
640 *
641 * @u, @v: Unsigned values whose GCD is desired.
642 *
643 * Returns THE greatest common divisor of u and v, if either value is 0,
644 * the other value is returned as the result.
645 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300646static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300647{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300648 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300649
650 while (v != 0) {
651 r = u % v;
652 u = v;
653 v = r;
654 }
655
656 return u;
657}
658
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300659/**
660 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
661 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
662 *
663 * ma mb mc md
664 * <--+-+-+-------------------+-------------------+-+-+-->
665 * | ^ 0 ^ |
666 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
667 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
668 *
669 * Note that some equations are doubled to prevent round-off
670 * problems when calculating fIFBW/2
671 *
672 * @pAS_Info: Avoid Spurs information block
673 * @fm: If spur, amount f_IF1 has to move negative
674 * @fp: If spur, amount f_IF1 has to move positive
675 *
676 * Returns 1 if an LO spur would be present, otherwise 0.
677 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300678static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300679 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300680{
681 /*
682 ** Calculate LO frequency settings.
683 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300684 u32 n, n0;
685 const u32 f_LO1 = pAS_Info->f_LO1;
686 const u32 f_LO2 = pAS_Info->f_LO2;
687 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
688 const u32 c = d - pAS_Info->f_out_bw;
689 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300690 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300691 s32 f_nsLO1, f_nsLO2;
692 s32 f_Spur;
693 u32 ma, mb, mc, md, me, mf;
694 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300695 *fm = 0;
696
697 /*
698 ** For each edge (d, c & f), calculate a scale, based on the gcd
699 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
700 ** gcd-based scale factor or f_Scale.
701 */
702 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300703 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300704 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300705 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300706 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300707 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300708 hgfs = gf_Scale / 2;
709
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300710 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300711
712 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
713 for (n = n0; n <= pAS_Info->maxH1; ++n) {
714 md = (n * ((f_LO1 + hgds) / gd_Scale) -
715 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
716
717 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
718 if (md >= pAS_Info->maxH1)
719 break;
720
721 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
722 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
723
724 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
725 if (md == ma)
726 continue;
727
728 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
729 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
730 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300731 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
732 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300733 f_Spur =
734 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
735 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
736
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300737 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
738 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300739 return 1;
740 }
741
742 /* Location of Zero-IF-spur to be checked */
743 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
744 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
745 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
746 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
747 if (me != mf) {
748 f_nsLO1 = n * (f_LO1 / gf_Scale);
749 f_nsLO2 = me * (f_LO2 / gf_Scale);
750 f_Spur =
751 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
752 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
753
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300754 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
755 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300756 return 1;
757 }
758
759 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
760 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
761 if (ma != mb) {
762 f_nsLO1 = n * (f_LO1 / gc_Scale);
763 f_nsLO2 = ma * (f_LO2 / gc_Scale);
764 f_Spur =
765 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
766 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
767
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300768 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
769 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300770 return 1;
771 }
772 }
773
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300774 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300775 return 0;
776}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300777
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300778/*
779 * MT_AvoidSpurs() - Main entry point to avoid spurs.
780 * Checks for existing spurs in present LO1, LO2 freqs
781 * and if present, chooses spur-free LO1, LO2 combination
782 * that tunes the same input/output frequencies.
783 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300784static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300785{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300786 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300787 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300788 pAS_Info->bSpurAvoided = 0;
789 pAS_Info->nSpursFound = 0;
790
791 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300792 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300793
794 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300795 * Avoid LO Generated Spurs
796 *
797 * Make sure that have no LO-related spurs within the IF output
798 * bandwidth.
799 *
800 * If there is an LO spur in this band, start at the current IF1 frequency
801 * and work out until we find a spur-free frequency or run up against the
802 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
803 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300804 */
805 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
806 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300807 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
808 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
809 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
810 u32 delta_IF1;
811 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300812
813 /*
814 ** Spur was found, attempt to find a spur-free 1st IF
815 */
816 do {
817 pAS_Info->nSpursFound++;
818
819 /* Raise f_IF1_upper, if needed */
820 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
821
822 /* Choose next IF1 that is closest to f_IF1_CENTER */
823 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
824
825 if (new_IF1 > zfIF1) {
826 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
827 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
828 } else {
829 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
830 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
831 }
832 zfIF1 = new_IF1;
833
834 if (zfIF1 > pAS_Info->f_if1_Center)
835 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
836 else
837 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300838
839 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300840 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300841 * Continue while the new 1st IF is still within the 1st IF bandwidth
842 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300843 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300844 } 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 -0300845
846 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300847 * Use the LO-spur free values found. If the search went all
848 * the way to the 1st IF band edge and always found spurs, just
849 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300850 */
851 if (pAS_Info->bSpurPresent == 1) {
852 status |= MT2063_SPUR_PRESENT_ERR;
853 pAS_Info->f_LO1 = zfLO1;
854 pAS_Info->f_LO2 = zfLO2;
855 } else
856 pAS_Info->bSpurAvoided = 1;
857 }
858
859 status |=
860 ((pAS_Info->
861 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
862
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300863 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300864}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300865
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300866/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300867 * Constants used by the tuning algorithm
868 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300869#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
870#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
871#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
872#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
873#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
874#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
875#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
876#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
877#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
878#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
879#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
880#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
881#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
882#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
883#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
884#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
885#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
886#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
887
888/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300889 * Define the supported Part/Rev codes for the MT2063
890 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300891#define MT2063_B0 (0x9B)
892#define MT2063_B1 (0x9C)
893#define MT2063_B2 (0x9D)
894#define MT2063_B3 (0x9E)
895
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300896/**
897 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
898 *
899 * @state: struct mt2063_state pointer
900 *
901 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
902 */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300903static unsigned int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300904{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300905 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
906 const u32 nPollRate = 2; /* poll status bits every 2 ms */
907 const u32 nMaxLoops = nMaxWait / nPollRate;
908 const u8 LO1LK = 0x80;
909 u8 LO2LK = 0x08;
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300910 u32 status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300911 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300912
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300913 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300914 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300915 LO2LK = 0x40;
916
917 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300918 status = mt2063_read(state, MT2063_REG_LO_STATUS,
919 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300920
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300921 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300922 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300923
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300924 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300925 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300926 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300927 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300928 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300929 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300930
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300931 /*
932 * Got no lock or partial lock
933 */
934 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300935}
936
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300937/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300938 * Constants for setting receiver modes.
939 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
940 * (DNC1GC & DNC2GC are the values, which are used, when the specific
941 * DNC Output is selected, the other is always off)
942 *
943 * enum mt2063_delivery_sys
944 * -------------+----------------------------------------------
945 * Mode 0 : | MT2063_CABLE_QAM
946 * Mode 1 : | MT2063_CABLE_ANALOG
947 * Mode 2 : | MT2063_OFFAIR_COFDM
948 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
949 * Mode 4 : | MT2063_OFFAIR_ANALOG
950 * Mode 5 : | MT2063_OFFAIR_8VSB
951 * --------------+----------------------------------------------
952 *
953 * |<---------- Mode -------------->|
954 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
955 * ------------+-----+-----+-----+-----+-----+-----+
956 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
957 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
958 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
959 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
960 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
961 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
962 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
963 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
964 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
965 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
966 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
967 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
968 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
969 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
970 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
971 */
972
973enum mt2063_delivery_sys {
974 MT2063_CABLE_QAM = 0, /* Digital cable */
975 MT2063_CABLE_ANALOG, /* Analog cable */
976 MT2063_OFFAIR_COFDM, /* Digital offair */
977 MT2063_OFFAIR_COFDM_SAWLESS, /* Digital offair without SAW */
978 MT2063_OFFAIR_ANALOG, /* Analog offair */
979 MT2063_OFFAIR_8VSB, /* Analog offair */
980 MT2063_NUM_RCVR_MODES
981};
982
983static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
984static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
985static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
986static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
987static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
988static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
989static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
990static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
991static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
992static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
993static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
994static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
995static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
996static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
997
998/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300999 * mt2063_set_dnc_output_enable()
1000 */
1001static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001002 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001003{
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001004 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1005 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1006 *pValue = MT2063_DNC_NONE;
1007 else
1008 *pValue = MT2063_DNC_2;
1009 } else { /* DNC1 is on */
1010 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1011 *pValue = MT2063_DNC_1;
1012 else
1013 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001014 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001015 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001016}
1017
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001018/*
1019 * mt2063_set_dnc_output_enable()
1020 */
1021static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001022 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001023{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001024 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001025 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001026
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001027 /* selects, which DNC output is used */
1028 switch (nValue) {
1029 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001030 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1031 if (state->reg[MT2063_REG_DNC_GAIN] !=
1032 val)
1033 status |=
1034 mt2063_setreg(state,
1035 MT2063_REG_DNC_GAIN,
1036 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001037
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001038 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1039 if (state->reg[MT2063_REG_VGA_GAIN] !=
1040 val)
1041 status |=
1042 mt2063_setreg(state,
1043 MT2063_REG_VGA_GAIN,
1044 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001045
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001046 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1047 if (state->reg[MT2063_REG_RSVD_20] !=
1048 val)
1049 status |=
1050 mt2063_setreg(state,
1051 MT2063_REG_RSVD_20,
1052 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001053
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001054 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001055 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001056 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1057 if (state->reg[MT2063_REG_DNC_GAIN] !=
1058 val)
1059 status |=
1060 mt2063_setreg(state,
1061 MT2063_REG_DNC_GAIN,
1062 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001063
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001064 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1065 if (state->reg[MT2063_REG_VGA_GAIN] !=
1066 val)
1067 status |=
1068 mt2063_setreg(state,
1069 MT2063_REG_VGA_GAIN,
1070 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001071
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001072 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1073 if (state->reg[MT2063_REG_RSVD_20] !=
1074 val)
1075 status |=
1076 mt2063_setreg(state,
1077 MT2063_REG_RSVD_20,
1078 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001079
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001080 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001081 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001082 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1083 if (state->reg[MT2063_REG_DNC_GAIN] !=
1084 val)
1085 status |=
1086 mt2063_setreg(state,
1087 MT2063_REG_DNC_GAIN,
1088 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001089
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001090 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1091 if (state->reg[MT2063_REG_VGA_GAIN] !=
1092 val)
1093 status |=
1094 mt2063_setreg(state,
1095 MT2063_REG_VGA_GAIN,
1096 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001097
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001098 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1099 if (state->reg[MT2063_REG_RSVD_20] !=
1100 val)
1101 status |=
1102 mt2063_setreg(state,
1103 MT2063_REG_RSVD_20,
1104 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001105
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001106 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001107 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001108 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1109 if (state->reg[MT2063_REG_DNC_GAIN] !=
1110 val)
1111 status |=
1112 mt2063_setreg(state,
1113 MT2063_REG_DNC_GAIN,
1114 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001115
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001116 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1117 if (state->reg[MT2063_REG_VGA_GAIN] !=
1118 val)
1119 status |=
1120 mt2063_setreg(state,
1121 MT2063_REG_VGA_GAIN,
1122 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001123
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001124 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1125 if (state->reg[MT2063_REG_RSVD_20] !=
1126 val)
1127 status |=
1128 mt2063_setreg(state,
1129 MT2063_REG_RSVD_20,
1130 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001131
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001132 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001133 default:
1134 break;
1135 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001136
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001137 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001138}
1139
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001140/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001141 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1142 * the selected enum mt2063_delivery_sys type.
1143 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001144 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1145 * DNC Output is selected, the other is always off)
1146 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001147 * @state: ptr to mt2063_state structure
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001148 * @Mode: desired reciever delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001149 *
1150 * Note: Register cache must be valid for it to work
1151 */
1152
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001153static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001154 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001155{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001156 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001157 u8 val;
1158 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001159
1160 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001161 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001162
1163 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001164 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001165 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001166 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001167 reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001168 ? 0x40 :
1169 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001170 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001171 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001172 }
1173
1174 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001175 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001176 u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001177 (LNARIN[Mode] & 0x03);
1178 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001179 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001180 }
1181
1182 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001183 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001184 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001185 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001186 reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001187 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001188 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001189 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001190 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001191 /* trigger FIFF calibration, needed after changing FIFFQ */
1192 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001193 (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001194 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001195 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001196 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001197 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001198 reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001199 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001200 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001201 }
1202 }
1203
1204 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001205 status |= mt2063_get_dnc_output_enable(state, &longval);
1206 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001207
1208 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001209 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001210 u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001211 (ACLNAMAX[Mode] & 0x1F);
1212 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001213 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001214 }
1215
1216 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001217 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001218 u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001219 (LNATGT[Mode] & 0x3F);
1220 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001221 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001222 }
1223
1224 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001225 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001226 u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
1227 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001228 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001229 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001230 }
1231
1232 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001233 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001234 u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001235 (PD1TGT[Mode] & 0x3F);
1236 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001237 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001238 }
1239
1240 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001241 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001242 u8 val = ACFIFMAX[Mode];
1243 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1244 val = 5;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001245 val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001246 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001247 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001248 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001249 }
1250
1251 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001252 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001253 u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001254 (PD2TGT[Mode] & 0x3F);
1255 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001256 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001257 }
1258
1259 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001260 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001261 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
1262 (RFOVDIS[Mode] ? 0x80 : 0x00);
1263 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001264 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001265 }
1266
1267 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001268 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001269 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
1270 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1271 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001272 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001273 }
1274
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001275 if (status >= 0)
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001276 state->rcvr_mode = Mode;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001277
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001278 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001279}
1280
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001281/*
1282 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1283 * sections of the MT2063
1284 *
1285 * @Bits: Mask bits to be cleared.
1286 *
1287 * See definition of MT2063_Mask_Bits type for description
1288 * of each of the power bits.
1289 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001290static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1291 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001292{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001293 u32 status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001294
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001295 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1296 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001297 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001298 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001299 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001300 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001301 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001302 }
1303 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001304 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001305 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001306 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001307 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001308 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001309 }
1310
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001311 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001312}
1313
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001314/*
1315 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1316 * When Shutdown is 1, any section whose power
1317 * mask is set will be shutdown.
1318 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001319static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001320{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001321 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001322
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001323 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001324 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001325 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001326 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001327
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001328 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001329 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001330 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001331
1332 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001333 state->reg[MT2063_REG_BYP_CTRL] =
1334 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001335 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001336 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001337 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001338 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001339 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001340 state->reg[MT2063_REG_BYP_CTRL] =
1341 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001342 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001343 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001344 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001345 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001346 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001347 }
1348
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001349 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001350}
1351
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001352static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001353{
1354 return f_ref * (f_LO / f_ref)
1355 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1356}
1357
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001358/**
1359 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1360 * This function preserves maximum precision without
1361 * risk of overflow. It accurately calculates
1362 * f_ref * num / denom to within 1 HZ with fixed math.
1363 *
1364 * @num : Fractional portion of the multiplier
1365 * @denom: denominator portion of the ratio
1366 * @f_Ref: SRO frequency.
1367 *
1368 * This calculation handles f_ref as two separate 14-bit fields.
1369 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1370 * This is the genesis of the magic number "14" and the magic mask value of
1371 * 0x03FFF.
1372 *
1373 * This routine successfully handles denom values up to and including 2^18.
1374 * Returns: f_ref * num / denom
1375 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001376static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001377{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001378 u32 t1 = (f_ref >> 14) * num;
1379 u32 term1 = t1 / denom;
1380 u32 loss = t1 % denom;
1381 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001382 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001383 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001384}
1385
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001386/*
1387 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1388 * value for a FracN PLL.
1389 *
1390 * This function assumes that the f_LO and f_Ref are
1391 * evenly divisible by f_LO_Step.
1392 *
1393 * @Div: OUTPUT: Whole number portion of the multiplier
1394 * @FracN: OUTPUT: Fractional portion of the multiplier
1395 * @f_LO: desired LO frequency.
1396 * @f_LO_Step: Minimum step size for the LO (in Hz).
1397 * @f_Ref: SRO frequency.
1398 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1399 * of f_Ref (in Hz).
1400 *
1401 * Returns: Recalculated LO frequency.
1402 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001403static u32 MT2063_CalcLO1Mult(u32 *Div,
1404 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001405 u32 f_LO,
1406 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001407{
1408 /* Calculate the whole number portion of the divider */
1409 *Div = f_LO / f_Ref;
1410
1411 /* Calculate the numerator value (round to nearest f_LO_Step) */
1412 *FracN =
1413 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1414 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1415
1416 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1417}
1418
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001419/**
1420 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1421 * value for a FracN PLL.
1422 *
1423 * This function assumes that the f_LO and f_Ref are
1424 * evenly divisible by f_LO_Step.
1425 *
1426 * @Div: OUTPUT: Whole number portion of the multiplier
1427 * @FracN: OUTPUT: Fractional portion of the multiplier
1428 * @f_LO: desired LO frequency.
1429 * @f_LO_Step: Minimum step size for the LO (in Hz).
1430 * @f_Ref: SRO frequency.
1431 * @f_Avoid: Range of PLL frequencies to avoid near
1432 * integer multiples of f_Ref (in Hz).
1433 *
1434 * Returns: Recalculated LO frequency.
1435 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001436static u32 MT2063_CalcLO2Mult(u32 *Div,
1437 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001438 u32 f_LO,
1439 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001440{
1441 /* Calculate the whole number portion of the divider */
1442 *Div = f_LO / f_Ref;
1443
1444 /* Calculate the numerator value (round to nearest f_LO_Step) */
1445 *FracN =
1446 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1447 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1448
1449 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1450 8191);
1451}
1452
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001453/*
1454 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1455 * used for a given input frequency.
1456 *
1457 * @state: ptr to tuner data structure
1458 * @f_in: RF input center frequency (in Hz).
1459 *
1460 * Returns: ClearTune filter number (0-31)
1461 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001462static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001463{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001464 u32 RFBand;
1465 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001466
1467 /*
1468 ** Find RF Band setting
1469 */
1470 RFBand = 31; /* def when f_in > all */
1471 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001472 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001473 RFBand = idx;
1474 break;
1475 }
1476 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001477 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001478}
1479
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001480/*
1481 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1482 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001483static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001484{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001485
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001486 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001487 u32 LO1; /* 1st LO register value */
1488 u32 Num1; /* Numerator for LO1 reg. value */
1489 u32 f_IF1; /* 1st IF requested */
1490 u32 LO2; /* 2nd LO register value */
1491 u32 Num2; /* Numerator for LO2 reg. value */
1492 u32 ofLO1, ofLO2; /* last time's LO frequencies */
1493 u32 ofin, ofout; /* last time's I/O frequencies */
1494 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1495 u32 fiffof; /* Offset from FIFF center freq */
1496 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1497 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1498 u8 val;
1499 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001500
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001501 /* Check the input and output frequency ranges */
1502 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001503 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001504
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001505 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1506 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001507 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001508
1509 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001510 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001511 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001512 ofLO1 = state->AS_Data.f_LO1;
1513 ofLO2 = state->AS_Data.f_LO2;
1514 ofin = state->AS_Data.f_in;
1515 ofout = state->AS_Data.f_out;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001516
1517 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001518 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001519 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001520 if (state->ctfilt_sw == 1) {
1521 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1522 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001523 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001524 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001525 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001526 val = state->reg[MT2063_REG_CTUNE_OV];
1527 RFBand = FindClearTuneFilter(state, f_in);
1528 state->reg[MT2063_REG_CTUNE_OV] =
1529 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001530 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001531 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001532 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001533 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001534 }
1535 }
1536
1537 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001538 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001539 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001540 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001541 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001542 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001543 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001544 &state->reg[MT2063_REG_FIFFC], 1);
1545 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001546 }
1547 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001548 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001549 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001550 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001551 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001552 state->AS_Data.f_if1_Request =
1553 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1554 state->AS_Data.f_LO1_Step,
1555 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001556
1557 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001558 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1559 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001560 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001561 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001562
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001563 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001564
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001565 state->AS_Data.f_LO1 =
1566 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1567 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001568
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001569 state->AS_Data.f_LO2 =
1570 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1571 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001572
1573 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001574 * Check for any LO spurs in the output bandwidth and adjust
1575 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001576 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001577 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001578 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001579 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1580 * Recalculate the LO frequencies and the values to be placed
1581 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001582 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001583 state->AS_Data.f_LO1 =
1584 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1585 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1586 state->AS_Data.f_LO2 =
1587 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1588 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1589 state->AS_Data.f_LO2 =
1590 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1591 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001592
1593 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001594 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001595 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001596 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1597 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001598 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001599 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1600 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001601 status |= MT2063_DNC_RANGE;
1602 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001603 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001604 LO2LK = 0x40;
1605
1606 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001607 * If we have the same LO frequencies and we're already locked,
1608 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001609 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001610 if ((ofLO1 != state->AS_Data.f_LO1)
1611 || (ofLO2 != state->AS_Data.f_LO2)
1612 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001613 (LO1LK | LO2LK))) {
1614 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001615 * Calculate the FIFFOF register value
1616 *
1617 * IF1_Actual
1618 * FIFFOF = ------------ - 8 * FIFFC - 4992
1619 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001620 */
1621 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001622 (state->AS_Data.f_LO1 -
1623 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001624 4992;
1625 if (fiffof > 0xFF)
1626 fiffof = 0xFF;
1627
1628 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001629 * Place all of the calculated values into the local tuner
1630 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001631 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001632 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001633 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1634 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1635 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001636 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001637 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1638 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001639
1640 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001641 * Now write out the computed register values
1642 * IMPORTANT: There is a required order for writing
1643 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001644 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001645 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 -03001646 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001647 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001648 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001649 }
1650 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001651 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001652 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001653 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001654 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001655 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001656 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001657 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001658 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001659 reg[MT2063_REG_FIFF_OFFSET],
1660 1);
1661 }
1662 }
1663
1664 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001665 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001666 */
1667
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001668 if (status < 0)
1669 return status;
1670
1671 status = mt2063_lockStatus(state);
1672 if (status < 0)
1673 return status;
1674 if (!status)
1675 return -EINVAL; /* Couldn't lock */
1676
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001677 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001678 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001679 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001680 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001681 }
1682
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001683 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001684}
1685
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001686static const u8 MT2063B0_defaults[] = {
1687 /* Reg, Value */
1688 0x19, 0x05,
1689 0x1B, 0x1D,
1690 0x1C, 0x1F,
1691 0x1D, 0x0F,
1692 0x1E, 0x3F,
1693 0x1F, 0x0F,
1694 0x20, 0x3F,
1695 0x22, 0x21,
1696 0x23, 0x3F,
1697 0x24, 0x20,
1698 0x25, 0x3F,
1699 0x27, 0xEE,
1700 0x2C, 0x27, /* bit at 0x20 is cleared below */
1701 0x30, 0x03,
1702 0x2C, 0x07, /* bit at 0x20 is cleared here */
1703 0x2D, 0x87,
1704 0x2E, 0xAA,
1705 0x28, 0xE1, /* Set the FIFCrst bit here */
1706 0x28, 0xE0, /* Clear the FIFCrst bit here */
1707 0x00
1708};
1709
1710/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1711static const u8 MT2063B1_defaults[] = {
1712 /* Reg, Value */
1713 0x05, 0xF0,
1714 0x11, 0x10, /* New Enable AFCsd */
1715 0x19, 0x05,
1716 0x1A, 0x6C,
1717 0x1B, 0x24,
1718 0x1C, 0x28,
1719 0x1D, 0x8F,
1720 0x1E, 0x14,
1721 0x1F, 0x8F,
1722 0x20, 0x57,
1723 0x22, 0x21, /* New - ver 1.03 */
1724 0x23, 0x3C, /* New - ver 1.10 */
1725 0x24, 0x20, /* New - ver 1.03 */
1726 0x2C, 0x24, /* bit at 0x20 is cleared below */
1727 0x2D, 0x87, /* FIFFQ=0 */
1728 0x2F, 0xF3,
1729 0x30, 0x0C, /* New - ver 1.11 */
1730 0x31, 0x1B, /* New - ver 1.11 */
1731 0x2C, 0x04, /* bit at 0x20 is cleared here */
1732 0x28, 0xE1, /* Set the FIFCrst bit here */
1733 0x28, 0xE0, /* Clear the FIFCrst bit here */
1734 0x00
1735};
1736
1737/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1738static const u8 MT2063B3_defaults[] = {
1739 /* Reg, Value */
1740 0x05, 0xF0,
1741 0x19, 0x3D,
1742 0x2C, 0x24, /* bit at 0x20 is cleared below */
1743 0x2C, 0x04, /* bit at 0x20 is cleared here */
1744 0x28, 0xE1, /* Set the FIFCrst bit here */
1745 0x28, 0xE0, /* Clear the FIFCrst bit here */
1746 0x00
1747};
1748
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001749static int mt2063_init(struct dvb_frontend *fe)
1750{
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001751 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001752 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001753 u8 all_resets = 0xF0; /* reset/load bits */
1754 const u8 *def = NULL;
1755 u32 FCRUN;
1756 s32 maxReads;
1757 u32 fcu_osc;
1758 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001759
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001760 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001761
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001762 /* Read the Part/Rev code from the tuner */
1763 status = mt2063_read(state, MT2063_REG_PART_REV, state->reg, 1);
1764 if (status < 0)
1765 return status;
1766
1767 /* Check the part/rev code */
1768 if (((state->reg[MT2063_REG_PART_REV] != MT2063_B0) /* MT2063 B0 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001769 && (state->reg[MT2063_REG_PART_REV] != MT2063_B1) /* MT2063 B1 */
1770 && (state->reg[MT2063_REG_PART_REV] != MT2063_B3))) /* MT2063 B3 */
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001771 return -ENODEV; /* Wrong tuner Part/Rev code */
1772
1773 /* Check the 2nd byte of the Part/Rev code from the tuner */
1774 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1775 &state->reg[MT2063_REG_RSVD_3B], 1);
1776
1777 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001778 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00))
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001779 return -ENODEV; /* Wrong tuner Part/Rev code */
1780
1781 /* Reset the tuner */
1782 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1783 if (status < 0)
1784 return status;
1785
1786 /* change all of the default values that vary from the HW reset values */
1787 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1788 switch (state->reg[MT2063_REG_PART_REV]) {
1789 case MT2063_B3:
1790 def = MT2063B3_defaults;
1791 break;
1792
1793 case MT2063_B1:
1794 def = MT2063B1_defaults;
1795 break;
1796
1797 case MT2063_B0:
1798 def = MT2063B0_defaults;
1799 break;
1800
1801 default:
1802 return -ENODEV;
1803 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001804 }
1805
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001806 while (status >= 0 && *def) {
1807 u8 reg = *def++;
1808 u8 val = *def++;
1809 status = mt2063_write(state, reg, &val, 1);
1810 }
1811 if (status < 0)
1812 return status;
1813
1814 /* Wait for FIFF location to complete. */
1815 FCRUN = 1;
1816 maxReads = 10;
1817 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1818 msleep(2);
1819 status = mt2063_read(state,
1820 MT2063_REG_XO_STATUS,
1821 &state->
1822 reg[MT2063_REG_XO_STATUS], 1);
1823 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1824 }
1825
1826 if (FCRUN != 0 || status < 0)
1827 return -ENODEV;
1828
1829 status = mt2063_read(state,
1830 MT2063_REG_FIFFC,
1831 &state->reg[MT2063_REG_FIFFC], 1);
1832 if (status < 0)
1833 return status;
1834
1835 /* Read back all the registers from the tuner */
1836 status = mt2063_read(state,
1837 MT2063_REG_PART_REV,
1838 state->reg, MT2063_REG_END_REGS);
1839 if (status < 0)
1840 return status;
1841
1842 /* Initialize the tuner state. */
1843 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1844 state->AS_Data.f_ref = MT2063_REF_FREQ;
1845 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1846 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1847 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1848 state->AS_Data.f_out = 43750000UL;
1849 state->AS_Data.f_out_bw = 6750000UL;
1850 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1851 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1852 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1853 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1854 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1855 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1856 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1857 state->AS_Data.f_LO1 = 2181000000UL;
1858 state->AS_Data.f_LO2 = 1486249786UL;
1859 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1860 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1861 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1862 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1863 state->num_regs = MT2063_REG_END_REGS;
1864 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1865 state->ctfilt_sw = 0;
1866
1867 state->CTFiltMax[0] = 69230000;
1868 state->CTFiltMax[1] = 105770000;
1869 state->CTFiltMax[2] = 140350000;
1870 state->CTFiltMax[3] = 177110000;
1871 state->CTFiltMax[4] = 212860000;
1872 state->CTFiltMax[5] = 241130000;
1873 state->CTFiltMax[6] = 274370000;
1874 state->CTFiltMax[7] = 309820000;
1875 state->CTFiltMax[8] = 342450000;
1876 state->CTFiltMax[9] = 378870000;
1877 state->CTFiltMax[10] = 416210000;
1878 state->CTFiltMax[11] = 456500000;
1879 state->CTFiltMax[12] = 495790000;
1880 state->CTFiltMax[13] = 534530000;
1881 state->CTFiltMax[14] = 572610000;
1882 state->CTFiltMax[15] = 598970000;
1883 state->CTFiltMax[16] = 635910000;
1884 state->CTFiltMax[17] = 672130000;
1885 state->CTFiltMax[18] = 714840000;
1886 state->CTFiltMax[19] = 739660000;
1887 state->CTFiltMax[20] = 770410000;
1888 state->CTFiltMax[21] = 814660000;
1889 state->CTFiltMax[22] = 846950000;
1890 state->CTFiltMax[23] = 867820000;
1891 state->CTFiltMax[24] = 915980000;
1892 state->CTFiltMax[25] = 947450000;
1893 state->CTFiltMax[26] = 983110000;
1894 state->CTFiltMax[27] = 1021630000;
1895 state->CTFiltMax[28] = 1061870000;
1896 state->CTFiltMax[29] = 1098330000;
1897 state->CTFiltMax[30] = 1138990000;
1898
1899 /*
1900 ** Fetch the FCU osc value and use it and the fRef value to
1901 ** scale all of the Band Max values
1902 */
1903
1904 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1905 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1906 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1907 if (status < 0)
1908 return status;
1909
1910 /* Read the ClearTune filter calibration value */
1911 status = mt2063_read(state, MT2063_REG_FIFFC,
1912 &state->reg[MT2063_REG_FIFFC], 1);
1913 if (status < 0)
1914 return status;
1915
1916 fcu_osc = state->reg[MT2063_REG_FIFFC];
1917
1918 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1919 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1920 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1921 if (status < 0)
1922 return status;
1923
1924 /* Adjust each of the values in the ClearTune filter cross-over table */
1925 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001926 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001927
1928 status = MT2063_SoftwareShutdown(state, 1);
1929 if (status < 0)
1930 return status;
1931 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1932 if (status < 0)
1933 return status;
1934
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001935 return 0;
1936}
1937
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001938static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001939{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001940 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001941 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001942
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001943 *tuner_status = 0;
1944 status = mt2063_lockStatus(state);
1945 if (status < 0)
1946 return status;
1947 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001948 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001949
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001950 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001951}
1952
1953static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001954{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001955 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001956
1957 fe->tuner_priv = NULL;
1958 kfree(state);
1959
1960 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001961}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001962
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001963static int mt2063_set_analog_params(struct dvb_frontend *fe,
1964 struct analog_parameters *params)
1965{
1966 struct mt2063_state *state = fe->tuner_priv;
1967 s32 pict_car = 0;
1968 s32 pict2chanb_vsb = 0;
1969 s32 pict2chanb_snd = 0;
1970 s32 pict2snd1 = 0;
1971 s32 pict2snd2 = 0;
1972 s32 ch_bw = 0;
1973 s32 if_mid = 0;
1974 s32 rcvr_mode = 0;
1975 int status;
1976
1977 switch (params->mode) {
1978 case V4L2_TUNER_RADIO:
1979 pict_car = 38900000;
1980 ch_bw = 8000000;
1981 pict2chanb_vsb = -(ch_bw / 2);
1982 pict2snd1 = 0;
1983 pict2snd2 = 0;
1984 rcvr_mode = MT2063_OFFAIR_ANALOG;
1985 break;
1986 case V4L2_TUNER_ANALOG_TV:
1987 rcvr_mode = MT2063_CABLE_ANALOG;
1988 if (params->std & ~V4L2_STD_MN) {
1989 pict_car = 38900000;
1990 ch_bw = 6000000;
1991 pict2chanb_vsb = -1250000;
1992 pict2snd1 = 4500000;
1993 pict2snd2 = 0;
1994 } else if (params->std & V4L2_STD_PAL_I) {
1995 pict_car = 38900000;
1996 ch_bw = 8000000;
1997 pict2chanb_vsb = -1250000;
1998 pict2snd1 = 6000000;
1999 pict2snd2 = 0;
2000 } else if (params->std & V4L2_STD_PAL_B) {
2001 pict_car = 38900000;
2002 ch_bw = 8000000;
2003 pict2chanb_vsb = -1250000;
2004 pict2snd1 = 5500000;
2005 pict2snd2 = 5742000;
2006 } else if (params->std & V4L2_STD_PAL_G) {
2007 pict_car = 38900000;
2008 ch_bw = 7000000;
2009 pict2chanb_vsb = -1250000;
2010 pict2snd1 = 5500000;
2011 pict2snd2 = 0;
2012 } else if (params->std & V4L2_STD_PAL_DK) {
2013 pict_car = 38900000;
2014 ch_bw = 8000000;
2015 pict2chanb_vsb = -1250000;
2016 pict2snd1 = 6500000;
2017 pict2snd2 = 0;
2018 } else { /* PAL-L */
2019 pict_car = 38900000;
2020 ch_bw = 8000000;
2021 pict2chanb_vsb = -1250000;
2022 pict2snd1 = 6500000;
2023 pict2snd2 = 0;
2024 }
2025 break;
2026 }
2027 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2028 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2029
2030 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2031 state->AS_Data.f_out = if_mid;
2032 state->AS_Data.f_out_bw = ch_bw + 750000;
2033 status = MT2063_SetReceiverMode(state, rcvr_mode);
2034 if (status < 0)
2035 return status;
2036
2037 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2038 if (status < 0)
2039 return status;
2040
2041 state->frequency = params->frequency;
2042 return 0;
2043}
2044
2045/*
2046 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2047 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002048 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002049 * As such, the maximum symbol rate supported by 6 MHz is given by:
2050 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2051 */
2052#define MAX_SYMBOL_RATE_6MHz 5217391
2053
2054static int mt2063_set_params(struct dvb_frontend *fe,
2055 struct dvb_frontend_parameters *params)
2056{
2057 struct mt2063_state *state = fe->tuner_priv;
2058 int status;
2059 s32 pict_car = 0;
2060 s32 pict2chanb_vsb = 0;
2061 s32 pict2chanb_snd = 0;
2062 s32 pict2snd1 = 0;
2063 s32 pict2snd2 = 0;
2064 s32 ch_bw = 0;
2065 s32 if_mid = 0;
2066 s32 rcvr_mode = 0;
2067
2068 switch (fe->ops.info.type) {
2069 case FE_OFDM:
2070 switch (params->u.ofdm.bandwidth) {
2071 case BANDWIDTH_6_MHZ:
2072 ch_bw = 6000000;
2073 break;
2074 case BANDWIDTH_7_MHZ:
2075 ch_bw = 7000000;
2076 break;
2077 case BANDWIDTH_8_MHZ:
2078 ch_bw = 8000000;
2079 break;
2080 default:
2081 return -EINVAL;
2082 }
2083 rcvr_mode = MT2063_OFFAIR_COFDM;
2084 pict_car = 36125000;
2085 pict2chanb_vsb = -(ch_bw / 2);
2086 pict2snd1 = 0;
2087 pict2snd2 = 0;
2088 break;
2089 case FE_QAM:
2090 /*
2091 * Using a 8MHz bandwidth sometimes fail
2092 * with 6MHz-spaced channels, due to inter-carrier
2093 * interference. So, it is better to narrow-down the filter
2094 */
2095 if (params->u.qam.symbol_rate <= MAX_SYMBOL_RATE_6MHz)
2096 ch_bw = 6000000;
2097 else
2098 ch_bw = 8000000;
2099 rcvr_mode = MT2063_CABLE_QAM;
2100 pict_car = 36125000;
2101 pict2snd1 = 0;
2102 pict2snd2 = 0;
2103 pict2chanb_vsb = -(ch_bw / 2);
2104 break;
2105 default:
2106 return -EINVAL;
2107 }
2108 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2109 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2110
2111 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2112 state->AS_Data.f_out = if_mid;
2113 state->AS_Data.f_out_bw = ch_bw + 750000;
2114 status = MT2063_SetReceiverMode(state, rcvr_mode);
2115 if (status < 0)
2116 return status;
2117
2118 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2119
2120 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002121 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002122
2123 state->frequency = params->frequency;
2124 return 0;
2125}
2126
2127static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2128{
2129 struct mt2063_state *state = fe->tuner_priv;
2130
2131 *freq = state->frequency;
2132 return 0;
2133}
2134
2135static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2136{
2137 struct mt2063_state *state = fe->tuner_priv;
2138
2139 *bw = state->AS_Data.f_out_bw - 750000;
2140 return 0;
2141}
2142
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002143static struct dvb_tuner_ops mt2063_ops = {
2144 .info = {
2145 .name = "MT2063 Silicon Tuner",
2146 .frequency_min = 45000000,
2147 .frequency_max = 850000000,
2148 .frequency_step = 0,
2149 },
2150
2151 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002152 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002153 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002154 .set_analog_params = mt2063_set_analog_params,
2155 .set_params = mt2063_set_params,
2156 .get_frequency = mt2063_get_frequency,
2157 .get_bandwidth = mt2063_get_bandwidth,
2158 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002159};
2160
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002161struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2162 struct mt2063_config *config,
2163 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002164{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002165 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002166
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002167 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002168 if (state == NULL)
2169 goto error;
2170
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002171 state->config = config;
2172 state->i2c = i2c;
2173 state->frontend = fe;
2174 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002175 fe->tuner_priv = state;
2176 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002177
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002178 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002179 return fe;
2180
2181error:
2182 kfree(state);
2183 return NULL;
2184}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002185EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002186
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002187/*
2188 * Ancillary routines visible outside mt2063
2189 * FIXME: Remove them in favor of using standard tuner callbacks
2190 */
2191unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2192{
2193 struct mt2063_state *state = fe->tuner_priv;
2194 int err = 0;
2195
2196 err = MT2063_SoftwareShutdown(state, 1);
2197 if (err < 0)
2198 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2199
2200 return err;
2201}
2202EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2203
2204unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2205{
2206 struct mt2063_state *state = fe->tuner_priv;
2207 int err = 0;
2208
2209 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2210 if (err < 0)
2211 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2212
2213 return err;
2214}
2215EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2216
2217
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002218MODULE_PARM_DESC(verbose, "Set Verbosity level");
2219
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002220MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002221MODULE_DESCRIPTION("MT2063 Silicon tuner");
2222MODULE_LICENSE("GPL");