blob: 5e9655a21e128dda33701b936add3646a41e5295 [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 *
6 * This driver came from a driver originally written by Henry, made available
7 * by Terratec, at:
8 * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation under version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030020#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/string.h>
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -030024#include <linux/videodev2.h>
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030025
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030026#include "mt2063.h"
27
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030028static unsigned int verbose;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030029module_param(verbose, int, 0644);
30
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -030031/* positive error codes used internally */
Mauro Carvalho Chehab29a0a4f2011-07-20 23:44:10 -030032
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -030033/* Info: Unavoidable LO-related spur may be present in the output */
Mauro Carvalho Chehab29a0a4f2011-07-20 23:44:10 -030034#define MT2063_SPUR_PRESENT_ERR (0x00800000)
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030035
36/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */
37#define MT2063_SPUR_CNT_MASK (0x001f0000)
38#define MT2063_SPUR_SHIFT (16)
39
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030040/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */
41#define MT2063_UPC_RANGE (0x04000000)
42
43/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */
44#define MT2063_DNC_RANGE (0x08000000)
45
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030046/*
47 * Constant defining the version of the following structure
48 * and therefore the API for this code.
49 *
50 * When compiling the tuner driver, the preprocessor will
51 * check against this version number to make sure that
52 * it matches the version that the tuner driver knows about.
53 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030054
55/* DECT Frequency Avoidance */
56#define MT2063_DECT_AVOID_US_FREQS 0x00000001
57
58#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002
59
60#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0)
61
62#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0)
63
64enum MT2063_DECT_Avoid_Type {
65 MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */
66 MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */
67 MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */
68 MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */
69};
70
71#define MT2063_MAX_ZONES 48
72
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030073struct MT2063_ExclZone_t {
74 u32 min_;
75 u32 max_;
76 struct MT2063_ExclZone_t *next_;
77};
78
79/*
80 * Structure of data needed for Spur Avoidance
81 */
82struct MT2063_AvoidSpursData_t {
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030083 u32 f_ref;
84 u32 f_in;
85 u32 f_LO1;
86 u32 f_if1_Center;
87 u32 f_if1_Request;
88 u32 f_if1_bw;
89 u32 f_LO2;
90 u32 f_out;
91 u32 f_out_bw;
92 u32 f_LO1_Step;
93 u32 f_LO2_Step;
94 u32 f_LO1_FracN_Avoid;
95 u32 f_LO2_FracN_Avoid;
96 u32 f_zif_bw;
97 u32 f_min_LO_Separation;
98 u32 maxH1;
99 u32 maxH2;
100 enum MT2063_DECT_Avoid_Type avoidDECT;
101 u32 bSpurPresent;
102 u32 bSpurAvoided;
103 u32 nSpursFound;
104 u32 nZones;
105 struct MT2063_ExclZone_t *freeZones;
106 struct MT2063_ExclZone_t *usedZones;
107 struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES];
108};
109
110/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300111 * Parameter for function MT2063_SetPowerMask that specifies the power down
112 * of various sections of the MT2063.
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300113 */
114enum MT2063_Mask_Bits {
115 MT2063_REG_SD = 0x0040, /* Shutdown regulator */
116 MT2063_SRO_SD = 0x0020, /* Shutdown SRO */
117 MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */
118 MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */
119 MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */
120 MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */
121 MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */
122 MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */
123 MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */
124 MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */
125 MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */
126 MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */
127 MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */
128 MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */
129 MT2063_NONE_SD = 0x0000 /* No shutdown bits */
130};
131
132/*
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300133 * Possible values for MT2063_DNC_OUTPUT
134 */
135enum MT2063_DNC_Output_Enable {
136 MT2063_DNC_NONE = 0,
137 MT2063_DNC_1,
138 MT2063_DNC_2,
139 MT2063_DNC_BOTH
140};
141
142/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300143 * Two-wire serial bus subaddresses of the tuner registers.
144 * Also known as the tuner's register addresses.
145 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300146enum MT2063_Register_Offsets {
147 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
148 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
149 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
150 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
151 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
152 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
153 MT2063_REG_RSVD_06, /* 0x06: Reserved */
154 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
155 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
156 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
157 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
158 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
159 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
160 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
161 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
162 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
163 MT2063_REG_RSVD_10, /* 0x10: Reserved */
164 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
165 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
166 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
167 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
168 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
169 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
170 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
171 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
172 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
173 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
174 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
175 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
176 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
177 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
178 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
179 MT2063_REG_RSVD_20, /* 0x20: Reserved */
180 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
181 MT2063_REG_RSVD_22, /* 0x22: Reserved */
182 MT2063_REG_RSVD_23, /* 0x23: Reserved */
183 MT2063_REG_RSVD_24, /* 0x24: Reserved */
184 MT2063_REG_RSVD_25, /* 0x25: Reserved */
185 MT2063_REG_RSVD_26, /* 0x26: Reserved */
186 MT2063_REG_RSVD_27, /* 0x27: Reserved */
187 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
188 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
189 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
190 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
191 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
192 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
193 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
194 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
195 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
196 MT2063_REG_RSVD_31, /* 0x31: Reserved */
197 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
198 MT2063_REG_RSVD_33, /* 0x33: Reserved */
199 MT2063_REG_RSVD_34, /* 0x34: Reserved */
200 MT2063_REG_RSVD_35, /* 0x35: Reserved */
201 MT2063_REG_RSVD_36, /* 0x36: Reserved */
202 MT2063_REG_RSVD_37, /* 0x37: Reserved */
203 MT2063_REG_RSVD_38, /* 0x38: Reserved */
204 MT2063_REG_RSVD_39, /* 0x39: Reserved */
205 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
206 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
207 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
208 MT2063_REG_END_REGS
209};
210
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300211struct mt2063_state {
212 struct i2c_adapter *i2c;
213
214 const struct mt2063_config *config;
215 struct dvb_tuner_ops ops;
216 struct dvb_frontend *frontend;
217 struct tuner_state status;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300218
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300219 u32 frequency;
220 u32 srate;
221 u32 bandwidth;
222 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300223
224 u32 tuner_id;
225 struct MT2063_AvoidSpursData_t AS_Data;
226 u32 f_IF1_actual;
227 u32 rcvr_mode;
228 u32 ctfilt_sw;
229 u32 CTFiltMax[31];
230 u32 num_regs;
231 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300232};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300233
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300234/*
235 * mt2063_write - Write data into the I2C bus
236 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300237static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300238{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300239 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300240 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300241 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300242 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300243 .addr = state->config->tuner_address,
244 .flags = 0,
245 .buf = buf,
246 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300247 };
248
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300249 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300250 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300251
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300252 if (fe->ops.i2c_gate_ctrl)
253 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300254 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300255 if (fe->ops.i2c_gate_ctrl)
256 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300257
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300258 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300259 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300260
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300261 return ret;
262}
263
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300264/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300265 * mt2063_write - Write register data into the I2C bus, caching the value
266 */
267static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
268{
269 u32 status;
270
271 if (reg >= MT2063_REG_END_REGS)
272 return -ERANGE;
273
274 status = mt2063_write(state, reg, &val, 1);
275 if (status < 0)
276 return status;
277
278 state->reg[reg] = val;
279
280 return 0;
281}
282
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300283/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300284 * mt2063_read - Read data from the I2C bus
285 */
286static u32 mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300287 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300288{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300289 u32 status = 0; /* Status to be returned */
290 struct dvb_frontend *fe = state->frontend;
291 u32 i = 0;
292
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300293 if (fe->ops.i2c_gate_ctrl)
294 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300295
296 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300297 int ret;
298 u8 b0[] = { subAddress + i };
299 struct i2c_msg msg[] = {
300 {
301 .addr = state->config->tuner_address,
302 .flags = I2C_M_RD,
303 .buf = b0,
304 .len = 1
305 }, {
306 .addr = state->config->tuner_address,
307 .flags = I2C_M_RD,
308 .buf = pData + 1,
309 .len = 1
310 }
311 };
312
313 ret = i2c_transfer(state->i2c, msg, 2);
314 if (ret < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300315 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300316 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300317 if (fe->ops.i2c_gate_ctrl)
318 fe->ops.i2c_gate_ctrl(fe, 0);
319
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300320 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300321}
322
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300323/*
324 * FIXME: Is this really needed?
325 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300326static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300327{
328 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300329 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300330 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300331 msleep(10);
332
333 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300334}
335
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300336/*
337 * Microtune spur avoidance
338 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300339
340/* Implement ceiling, floor functions. */
341#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300342#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300343
344struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300345 s32 min_;
346 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300347};
348
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300349static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
350 *pAS_Info,
351 struct MT2063_ExclZone_t *pPrevNode)
352{
353 struct MT2063_ExclZone_t *pNode;
354 /* Check for a node in the free list */
355 if (pAS_Info->freeZones != NULL) {
356 /* Use one from the free list */
357 pNode = pAS_Info->freeZones;
358 pAS_Info->freeZones = pNode->next_;
359 } else {
360 /* Grab a node from the array */
361 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
362 }
363
364 if (pPrevNode != NULL) {
365 pNode->next_ = pPrevNode->next_;
366 pPrevNode->next_ = pNode;
367 } else { /* insert at the beginning of the list */
368
369 pNode->next_ = pAS_Info->usedZones;
370 pAS_Info->usedZones = pNode;
371 }
372
373 pAS_Info->nZones++;
374 return pNode;
375}
376
377static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
378 *pAS_Info,
379 struct MT2063_ExclZone_t *pPrevNode,
380 struct MT2063_ExclZone_t
381 *pNodeToRemove)
382{
383 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
384
385 /* Make previous node point to the subsequent node */
386 if (pPrevNode != NULL)
387 pPrevNode->next_ = pNext;
388
389 /* Add pNodeToRemove to the beginning of the freeZones */
390 pNodeToRemove->next_ = pAS_Info->freeZones;
391 pAS_Info->freeZones = pNodeToRemove;
392
393 /* Decrement node count */
394 pAS_Info->nZones--;
395
396 return pNext;
397}
398
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300399/*
400 * MT_AddExclZone()
401 *
402 * Add (and merge) an exclusion zone into the list.
403 * If the range (f_min, f_max) is totally outside the
404 * 1st IF BW, ignore the entry.
405 * If the range (f_min, f_max) is negative, ignore the entry.
406 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300407static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300408 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300409{
410 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
411 struct MT2063_ExclZone_t *pPrev = NULL;
412 struct MT2063_ExclZone_t *pNext = NULL;
413
414 /* Check to see if this overlaps the 1st IF filter */
415 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
416 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
417 && (f_min < f_max)) {
418 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300419 * 1 2 3 4 5 6
420 *
421 * New entry: |---| |--| |--| |-| |---| |--|
422 * or or or or or
423 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300424 */
425
426 /* Check for our place in the list */
427 while ((pNode != NULL) && (pNode->max_ < f_min)) {
428 pPrev = pNode;
429 pNode = pNode->next_;
430 }
431
432 if ((pNode != NULL) && (pNode->min_ < f_max)) {
433 /* Combine me with pNode */
434 if (f_min < pNode->min_)
435 pNode->min_ = f_min;
436 if (f_max > pNode->max_)
437 pNode->max_ = f_max;
438 } else {
439 pNode = InsertNode(pAS_Info, pPrev);
440 pNode->min_ = f_min;
441 pNode->max_ = f_max;
442 }
443
444 /* Look for merging possibilities */
445 pNext = pNode->next_;
446 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
447 if (pNext->max_ > pNode->max_)
448 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300449 /* Remove pNext, return ptr to pNext->next */
450 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300451 }
452 }
453}
454
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300455/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300456 * Reset all exclusion zones.
457 * Add zones to protect the PLL FracN regions near zero
458 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300459static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
460{
461 u32 center;
462
463 pAS_Info->nZones = 0; /* this clears the used list */
464 pAS_Info->usedZones = NULL; /* reset ptr */
465 pAS_Info->freeZones = NULL; /* reset ptr */
466
467 center =
468 pAS_Info->f_ref *
469 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
470 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
471 while (center <
472 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
473 pAS_Info->f_LO1_FracN_Avoid) {
474 /* Exclude LO1 FracN */
475 MT2063_AddExclZone(pAS_Info,
476 center - pAS_Info->f_LO1_FracN_Avoid,
477 center - 1);
478 MT2063_AddExclZone(pAS_Info, center + 1,
479 center + pAS_Info->f_LO1_FracN_Avoid);
480 center += pAS_Info->f_ref;
481 }
482
483 center =
484 pAS_Info->f_ref *
485 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
486 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
487 while (center <
488 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
489 pAS_Info->f_LO2_FracN_Avoid) {
490 /* Exclude LO2 FracN */
491 MT2063_AddExclZone(pAS_Info,
492 center - pAS_Info->f_LO2_FracN_Avoid,
493 center - 1);
494 MT2063_AddExclZone(pAS_Info, center + 1,
495 center + pAS_Info->f_LO2_FracN_Avoid);
496 center += pAS_Info->f_ref;
497 }
498
499 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
500 /* Exclude LO1 values that conflict with DECT channels */
501 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
502 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
503 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
504 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
505 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
506 }
507
508 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
509 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
510 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
511 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
512 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
513 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
514 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
515 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
516 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
517 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
518 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
519 }
520}
521
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300522/*
523 * MT_ChooseFirstIF - Choose the best available 1st IF
524 * If f_Desired is not excluded, choose that first.
525 * Otherwise, return the value closest to f_Center that is
526 * not excluded
527 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300528static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300529{
530 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300531 * Update "f_Desired" to be the nearest "combinational-multiple" of
532 * "f_LO1_Step".
533 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
534 * And F_LO1 is the arithmetic sum of f_in + f_Center.
535 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
536 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300537 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300538 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300539 pAS_Info->f_LO1_Step *
540 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
541 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
542 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300543 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300544 (pAS_Info->f_LO1_Step >
545 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
546 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300547 u32 f_Center;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300548
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300549 s32 i;
550 s32 j = 0;
551 u32 bDesiredExcluded = 0;
552 u32 bZeroExcluded = 0;
553 s32 tmpMin, tmpMax;
554 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300555 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
556 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
557
558 if (pAS_Info->nZones == 0)
559 return f_Desired;
560
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300561 /*
562 * f_Center needs to be an integer multiple of f_Step away
563 * from f_Desired
564 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300565 if (pAS_Info->f_if1_Center > f_Desired)
566 f_Center =
567 f_Desired +
568 f_Step *
569 ((pAS_Info->f_if1_Center - f_Desired +
570 f_Step / 2) / f_Step);
571 else
572 f_Center =
573 f_Desired -
574 f_Step *
575 ((f_Desired - pAS_Info->f_if1_Center +
576 f_Step / 2) / f_Step);
577
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300578 /*
579 * Take MT_ExclZones, center around f_Center and change the
580 * resolution to f_Step
581 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300582 while (pNode != NULL) {
583 /* floor function */
584 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300585 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300586
587 /* ceil function */
588 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300589 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300590
591 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
592 bDesiredExcluded = 1;
593
594 if ((tmpMin < 0) && (tmpMax > 0))
595 bZeroExcluded = 1;
596
597 /* See if this zone overlaps the previous */
598 if ((j > 0) && (tmpMin < zones[j - 1].max_))
599 zones[j - 1].max_ = tmpMax;
600 else {
601 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300602 zones[j].min_ = tmpMin;
603 zones[j].max_ = tmpMax;
604 j++;
605 }
606 pNode = pNode->next_;
607 }
608
609 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300610 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300611 */
612 if (bDesiredExcluded == 0)
613 return f_Desired;
614
615 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300616 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300617 */
618 if (bZeroExcluded == 0)
619 return f_Center;
620
621 /* Find the value closest to 0 (f_Center) */
622 bestDiff = zones[0].min_;
623 for (i = 0; i < j; i++) {
624 if (abs(zones[i].min_) < abs(bestDiff))
625 bestDiff = zones[i].min_;
626 if (abs(zones[i].max_) < abs(bestDiff))
627 bestDiff = zones[i].max_;
628 }
629
630 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300631 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300632
633 return f_Center + (bestDiff * f_Step);
634}
635
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300636/**
637 * gcd() - Uses Euclid's algorithm
638 *
639 * @u, @v: Unsigned values whose GCD is desired.
640 *
641 * Returns THE greatest common divisor of u and v, if either value is 0,
642 * the other value is returned as the result.
643 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300644static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300645{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300646 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300647
648 while (v != 0) {
649 r = u % v;
650 u = v;
651 v = r;
652 }
653
654 return u;
655}
656
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300657/**
658 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
659 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
660 *
661 * ma mb mc md
662 * <--+-+-+-------------------+-------------------+-+-+-->
663 * | ^ 0 ^ |
664 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
665 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
666 *
667 * Note that some equations are doubled to prevent round-off
668 * problems when calculating fIFBW/2
669 *
670 * @pAS_Info: Avoid Spurs information block
671 * @fm: If spur, amount f_IF1 has to move negative
672 * @fp: If spur, amount f_IF1 has to move positive
673 *
674 * Returns 1 if an LO spur would be present, otherwise 0.
675 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300676static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300677 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300678{
679 /*
680 ** Calculate LO frequency settings.
681 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300682 u32 n, n0;
683 const u32 f_LO1 = pAS_Info->f_LO1;
684 const u32 f_LO2 = pAS_Info->f_LO2;
685 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
686 const u32 c = d - pAS_Info->f_out_bw;
687 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300688 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300689 s32 f_nsLO1, f_nsLO2;
690 s32 f_Spur;
691 u32 ma, mb, mc, md, me, mf;
692 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300693 *fm = 0;
694
695 /*
696 ** For each edge (d, c & f), calculate a scale, based on the gcd
697 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
698 ** gcd-based scale factor or f_Scale.
699 */
700 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300701 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300702 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300703 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300704 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300705 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300706 hgfs = gf_Scale / 2;
707
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300708 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300709
710 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
711 for (n = n0; n <= pAS_Info->maxH1; ++n) {
712 md = (n * ((f_LO1 + hgds) / gd_Scale) -
713 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
714
715 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
716 if (md >= pAS_Info->maxH1)
717 break;
718
719 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
720 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
721
722 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
723 if (md == ma)
724 continue;
725
726 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
727 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
728 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300729 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
730 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300731 f_Spur =
732 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
733 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
734
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300735 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
736 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300737 return 1;
738 }
739
740 /* Location of Zero-IF-spur to be checked */
741 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
742 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
743 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
744 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
745 if (me != mf) {
746 f_nsLO1 = n * (f_LO1 / gf_Scale);
747 f_nsLO2 = me * (f_LO2 / gf_Scale);
748 f_Spur =
749 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
750 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
751
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300752 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
753 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300754 return 1;
755 }
756
757 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
758 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
759 if (ma != mb) {
760 f_nsLO1 = n * (f_LO1 / gc_Scale);
761 f_nsLO2 = ma * (f_LO2 / gc_Scale);
762 f_Spur =
763 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
764 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
765
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300766 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
767 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300768 return 1;
769 }
770 }
771
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300772 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300773 return 0;
774}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300775
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300776/*
777 * MT_AvoidSpurs() - Main entry point to avoid spurs.
778 * Checks for existing spurs in present LO1, LO2 freqs
779 * and if present, chooses spur-free LO1, LO2 combination
780 * that tunes the same input/output frequencies.
781 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300782static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300783{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300784 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300785 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300786 pAS_Info->bSpurAvoided = 0;
787 pAS_Info->nSpursFound = 0;
788
789 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300790 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300791
792 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300793 * Avoid LO Generated Spurs
794 *
795 * Make sure that have no LO-related spurs within the IF output
796 * bandwidth.
797 *
798 * If there is an LO spur in this band, start at the current IF1 frequency
799 * and work out until we find a spur-free frequency or run up against the
800 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
801 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300802 */
803 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
804 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300805 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
806 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
807 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
808 u32 delta_IF1;
809 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300810
811 /*
812 ** Spur was found, attempt to find a spur-free 1st IF
813 */
814 do {
815 pAS_Info->nSpursFound++;
816
817 /* Raise f_IF1_upper, if needed */
818 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
819
820 /* Choose next IF1 that is closest to f_IF1_CENTER */
821 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
822
823 if (new_IF1 > zfIF1) {
824 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
825 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
826 } else {
827 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
828 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
829 }
830 zfIF1 = new_IF1;
831
832 if (zfIF1 > pAS_Info->f_if1_Center)
833 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
834 else
835 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300836
837 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300838 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300839 * Continue while the new 1st IF is still within the 1st IF bandwidth
840 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300841 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300842 } 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 -0300843
844 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300845 * Use the LO-spur free values found. If the search went all
846 * the way to the 1st IF band edge and always found spurs, just
847 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300848 */
849 if (pAS_Info->bSpurPresent == 1) {
850 status |= MT2063_SPUR_PRESENT_ERR;
851 pAS_Info->f_LO1 = zfLO1;
852 pAS_Info->f_LO2 = zfLO2;
853 } else
854 pAS_Info->bSpurAvoided = 1;
855 }
856
857 status |=
858 ((pAS_Info->
859 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
860
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300861 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300862}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300863
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300864/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300865 * Constants used by the tuning algorithm
866 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300867#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
868#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
869#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
870#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
871#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
872#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
873#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
874#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
875#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
876#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
877#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
878#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
879#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
880#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
881#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
882#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
883#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
884#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
885
886/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300887 * Define the supported Part/Rev codes for the MT2063
888 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300889#define MT2063_B0 (0x9B)
890#define MT2063_B1 (0x9C)
891#define MT2063_B2 (0x9D)
892#define MT2063_B3 (0x9E)
893
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300894/**
895 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
896 *
897 * @state: struct mt2063_state pointer
898 *
899 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
900 */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300901static unsigned int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300902{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300903 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
904 const u32 nPollRate = 2; /* poll status bits every 2 ms */
905 const u32 nMaxLoops = nMaxWait / nPollRate;
906 const u8 LO1LK = 0x80;
907 u8 LO2LK = 0x08;
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300908 u32 status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300909 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300910
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300911 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300912 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300913 LO2LK = 0x40;
914
915 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300916 status = mt2063_read(state, MT2063_REG_LO_STATUS,
917 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300918
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300919 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300920 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300921
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300922 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300923 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300924 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300925 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300926 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300927 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300928
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300929 /*
930 * Got no lock or partial lock
931 */
932 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300933}
934
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300935/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300936 * Constants for setting receiver modes.
937 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
938 * (DNC1GC & DNC2GC are the values, which are used, when the specific
939 * DNC Output is selected, the other is always off)
940 *
941 * enum mt2063_delivery_sys
942 * -------------+----------------------------------------------
943 * Mode 0 : | MT2063_CABLE_QAM
944 * Mode 1 : | MT2063_CABLE_ANALOG
945 * Mode 2 : | MT2063_OFFAIR_COFDM
946 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
947 * Mode 4 : | MT2063_OFFAIR_ANALOG
948 * Mode 5 : | MT2063_OFFAIR_8VSB
949 * --------------+----------------------------------------------
950 *
951 * |<---------- Mode -------------->|
952 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
953 * ------------+-----+-----+-----+-----+-----+-----+
954 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
955 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
956 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
957 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
958 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
959 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
960 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
961 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
962 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
963 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
964 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
965 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
966 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
967 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
968 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
969 */
970
971enum mt2063_delivery_sys {
972 MT2063_CABLE_QAM = 0, /* Digital cable */
973 MT2063_CABLE_ANALOG, /* Analog cable */
974 MT2063_OFFAIR_COFDM, /* Digital offair */
975 MT2063_OFFAIR_COFDM_SAWLESS, /* Digital offair without SAW */
976 MT2063_OFFAIR_ANALOG, /* Analog offair */
977 MT2063_OFFAIR_8VSB, /* Analog offair */
978 MT2063_NUM_RCVR_MODES
979};
980
981static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
982static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
983static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
984static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
985static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
986static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
987static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
988static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
989static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
990static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
991static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
992static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
993static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
994static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
995
996/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300997 * mt2063_set_dnc_output_enable()
998 */
999static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001000 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001001{
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001002 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1003 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1004 *pValue = MT2063_DNC_NONE;
1005 else
1006 *pValue = MT2063_DNC_2;
1007 } else { /* DNC1 is on */
1008 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1009 *pValue = MT2063_DNC_1;
1010 else
1011 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001012 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001013 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001014}
1015
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001016/*
1017 * mt2063_set_dnc_output_enable()
1018 */
1019static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001020 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001021{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001022 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001023 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001024
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001025 /* selects, which DNC output is used */
1026 switch (nValue) {
1027 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001028 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1029 if (state->reg[MT2063_REG_DNC_GAIN] !=
1030 val)
1031 status |=
1032 mt2063_setreg(state,
1033 MT2063_REG_DNC_GAIN,
1034 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001035
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001036 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1037 if (state->reg[MT2063_REG_VGA_GAIN] !=
1038 val)
1039 status |=
1040 mt2063_setreg(state,
1041 MT2063_REG_VGA_GAIN,
1042 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001043
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001044 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1045 if (state->reg[MT2063_REG_RSVD_20] !=
1046 val)
1047 status |=
1048 mt2063_setreg(state,
1049 MT2063_REG_RSVD_20,
1050 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001051
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001052 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001053 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001054 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1055 if (state->reg[MT2063_REG_DNC_GAIN] !=
1056 val)
1057 status |=
1058 mt2063_setreg(state,
1059 MT2063_REG_DNC_GAIN,
1060 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001061
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001062 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1063 if (state->reg[MT2063_REG_VGA_GAIN] !=
1064 val)
1065 status |=
1066 mt2063_setreg(state,
1067 MT2063_REG_VGA_GAIN,
1068 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001069
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001070 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1071 if (state->reg[MT2063_REG_RSVD_20] !=
1072 val)
1073 status |=
1074 mt2063_setreg(state,
1075 MT2063_REG_RSVD_20,
1076 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001077
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001078 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001079 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001080 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1081 if (state->reg[MT2063_REG_DNC_GAIN] !=
1082 val)
1083 status |=
1084 mt2063_setreg(state,
1085 MT2063_REG_DNC_GAIN,
1086 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001087
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001088 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1089 if (state->reg[MT2063_REG_VGA_GAIN] !=
1090 val)
1091 status |=
1092 mt2063_setreg(state,
1093 MT2063_REG_VGA_GAIN,
1094 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001095
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001096 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1097 if (state->reg[MT2063_REG_RSVD_20] !=
1098 val)
1099 status |=
1100 mt2063_setreg(state,
1101 MT2063_REG_RSVD_20,
1102 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001103
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001104 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001105 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001106 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1107 if (state->reg[MT2063_REG_DNC_GAIN] !=
1108 val)
1109 status |=
1110 mt2063_setreg(state,
1111 MT2063_REG_DNC_GAIN,
1112 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001113
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001114 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1115 if (state->reg[MT2063_REG_VGA_GAIN] !=
1116 val)
1117 status |=
1118 mt2063_setreg(state,
1119 MT2063_REG_VGA_GAIN,
1120 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001121
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001122 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1123 if (state->reg[MT2063_REG_RSVD_20] !=
1124 val)
1125 status |=
1126 mt2063_setreg(state,
1127 MT2063_REG_RSVD_20,
1128 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001129
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001130 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001131 default:
1132 break;
1133 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001134
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001135 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001136}
1137
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001138/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001139 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1140 * the selected enum mt2063_delivery_sys type.
1141 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001142 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1143 * DNC Output is selected, the other is always off)
1144 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001145 * @state: ptr to mt2063_state structure
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001146 * @Mode: desired reciever delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001147 *
1148 * Note: Register cache must be valid for it to work
1149 */
1150
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001151static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001152 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001153{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001154 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001155 u8 val;
1156 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001157
1158 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001159 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001160
1161 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001162 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001163 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001164 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001165 reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001166 ? 0x40 :
1167 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001168 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001169 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001170 }
1171
1172 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001173 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001174 u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001175 (LNARIN[Mode] & 0x03);
1176 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001177 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001178 }
1179
1180 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001181 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001182 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001183 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001184 reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001185 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001186 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001187 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001188 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001189 /* trigger FIFF calibration, needed after changing FIFFQ */
1190 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001191 (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001192 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001193 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001194 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001195 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001196 reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001197 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001198 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001199 }
1200 }
1201
1202 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001203 status |= mt2063_get_dnc_output_enable(state, &longval);
1204 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001205
1206 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001207 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001208 u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001209 (ACLNAMAX[Mode] & 0x1F);
1210 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001211 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001212 }
1213
1214 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001215 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001216 u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001217 (LNATGT[Mode] & 0x3F);
1218 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001219 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001220 }
1221
1222 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001223 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001224 u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
1225 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001226 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001227 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001228 }
1229
1230 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001231 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001232 u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001233 (PD1TGT[Mode] & 0x3F);
1234 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001235 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001236 }
1237
1238 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001239 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001240 u8 val = ACFIFMAX[Mode];
1241 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1242 val = 5;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001243 val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001244 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001245 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001246 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001247 }
1248
1249 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001250 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001251 u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001252 (PD2TGT[Mode] & 0x3F);
1253 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001254 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001255 }
1256
1257 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001258 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001259 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
1260 (RFOVDIS[Mode] ? 0x80 : 0x00);
1261 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001262 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001263 }
1264
1265 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001266 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001267 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
1268 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1269 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001270 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001271 }
1272
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001273 if (status >= 0)
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001274 state->rcvr_mode = Mode;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001275
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001276 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001277}
1278
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001279/*
1280 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1281 * sections of the MT2063
1282 *
1283 * @Bits: Mask bits to be cleared.
1284 *
1285 * See definition of MT2063_Mask_Bits type for description
1286 * of each of the power bits.
1287 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001288static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1289 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001290{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001291 u32 status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001292
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001293 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1294 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001295 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001296 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001297 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001298 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001299 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001300 }
1301 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001302 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001303 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001304 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001305 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001306 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001307 }
1308
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001309 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001310}
1311
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001312/*
1313 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1314 * When Shutdown is 1, any section whose power
1315 * mask is set will be shutdown.
1316 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001317static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001318{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001319 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001320
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001321 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001322 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001323 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001324 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001325
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001326 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001327 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001328 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001329
1330 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001331 state->reg[MT2063_REG_BYP_CTRL] =
1332 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001333 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001334 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001335 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001336 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001337 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001338 state->reg[MT2063_REG_BYP_CTRL] =
1339 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001340 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001341 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001342 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001343 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001344 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001345 }
1346
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001347 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001348}
1349
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001350static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001351{
1352 return f_ref * (f_LO / f_ref)
1353 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1354}
1355
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001356/**
1357 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1358 * This function preserves maximum precision without
1359 * risk of overflow. It accurately calculates
1360 * f_ref * num / denom to within 1 HZ with fixed math.
1361 *
1362 * @num : Fractional portion of the multiplier
1363 * @denom: denominator portion of the ratio
1364 * @f_Ref: SRO frequency.
1365 *
1366 * This calculation handles f_ref as two separate 14-bit fields.
1367 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1368 * This is the genesis of the magic number "14" and the magic mask value of
1369 * 0x03FFF.
1370 *
1371 * This routine successfully handles denom values up to and including 2^18.
1372 * Returns: f_ref * num / denom
1373 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001374static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001375{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001376 u32 t1 = (f_ref >> 14) * num;
1377 u32 term1 = t1 / denom;
1378 u32 loss = t1 % denom;
1379 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001380 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001381 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001382}
1383
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001384/*
1385 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1386 * value for a FracN PLL.
1387 *
1388 * This function assumes that the f_LO and f_Ref are
1389 * evenly divisible by f_LO_Step.
1390 *
1391 * @Div: OUTPUT: Whole number portion of the multiplier
1392 * @FracN: OUTPUT: Fractional portion of the multiplier
1393 * @f_LO: desired LO frequency.
1394 * @f_LO_Step: Minimum step size for the LO (in Hz).
1395 * @f_Ref: SRO frequency.
1396 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1397 * of f_Ref (in Hz).
1398 *
1399 * Returns: Recalculated LO frequency.
1400 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001401static u32 MT2063_CalcLO1Mult(u32 *Div,
1402 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001403 u32 f_LO,
1404 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001405{
1406 /* Calculate the whole number portion of the divider */
1407 *Div = f_LO / f_Ref;
1408
1409 /* Calculate the numerator value (round to nearest f_LO_Step) */
1410 *FracN =
1411 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1412 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1413
1414 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1415}
1416
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001417/**
1418 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1419 * value for a FracN PLL.
1420 *
1421 * This function assumes that the f_LO and f_Ref are
1422 * evenly divisible by f_LO_Step.
1423 *
1424 * @Div: OUTPUT: Whole number portion of the multiplier
1425 * @FracN: OUTPUT: Fractional portion of the multiplier
1426 * @f_LO: desired LO frequency.
1427 * @f_LO_Step: Minimum step size for the LO (in Hz).
1428 * @f_Ref: SRO frequency.
1429 * @f_Avoid: Range of PLL frequencies to avoid near
1430 * integer multiples of f_Ref (in Hz).
1431 *
1432 * Returns: Recalculated LO frequency.
1433 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001434static u32 MT2063_CalcLO2Mult(u32 *Div,
1435 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001436 u32 f_LO,
1437 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001438{
1439 /* Calculate the whole number portion of the divider */
1440 *Div = f_LO / f_Ref;
1441
1442 /* Calculate the numerator value (round to nearest f_LO_Step) */
1443 *FracN =
1444 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1445 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1446
1447 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1448 8191);
1449}
1450
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001451/*
1452 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1453 * used for a given input frequency.
1454 *
1455 * @state: ptr to tuner data structure
1456 * @f_in: RF input center frequency (in Hz).
1457 *
1458 * Returns: ClearTune filter number (0-31)
1459 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001460static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001461{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001462 u32 RFBand;
1463 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001464
1465 /*
1466 ** Find RF Band setting
1467 */
1468 RFBand = 31; /* def when f_in > all */
1469 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001470 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001471 RFBand = idx;
1472 break;
1473 }
1474 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001475 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001476}
1477
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001478/*
1479 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1480 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001481static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001482{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001483
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001484 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001485 u32 LO1; /* 1st LO register value */
1486 u32 Num1; /* Numerator for LO1 reg. value */
1487 u32 f_IF1; /* 1st IF requested */
1488 u32 LO2; /* 2nd LO register value */
1489 u32 Num2; /* Numerator for LO2 reg. value */
1490 u32 ofLO1, ofLO2; /* last time's LO frequencies */
1491 u32 ofin, ofout; /* last time's I/O frequencies */
1492 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1493 u32 fiffof; /* Offset from FIFF center freq */
1494 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1495 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1496 u8 val;
1497 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001498
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001499 /* Check the input and output frequency ranges */
1500 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001501 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001502
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001503 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1504 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001505 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001506
1507 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001508 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001509 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001510 ofLO1 = state->AS_Data.f_LO1;
1511 ofLO2 = state->AS_Data.f_LO2;
1512 ofin = state->AS_Data.f_in;
1513 ofout = state->AS_Data.f_out;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001514
1515 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001516 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001517 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001518 if (state->ctfilt_sw == 1) {
1519 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1520 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001521 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001522 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001523 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001524 val = state->reg[MT2063_REG_CTUNE_OV];
1525 RFBand = FindClearTuneFilter(state, f_in);
1526 state->reg[MT2063_REG_CTUNE_OV] =
1527 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001528 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001529 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001530 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001531 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001532 }
1533 }
1534
1535 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001536 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001537 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001538 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001539 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001540 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001541 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001542 &state->reg[MT2063_REG_FIFFC], 1);
1543 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001544 }
1545 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001546 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001547 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001548 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001549 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001550 state->AS_Data.f_if1_Request =
1551 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1552 state->AS_Data.f_LO1_Step,
1553 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001554
1555 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001556 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1557 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001558 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001559 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001560
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001561 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001562
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001563 state->AS_Data.f_LO1 =
1564 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1565 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001566
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001567 state->AS_Data.f_LO2 =
1568 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1569 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001570
1571 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001572 * Check for any LO spurs in the output bandwidth and adjust
1573 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001574 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001575 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001576 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001577 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1578 * Recalculate the LO frequencies and the values to be placed
1579 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001580 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001581 state->AS_Data.f_LO1 =
1582 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1583 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1584 state->AS_Data.f_LO2 =
1585 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1586 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1587 state->AS_Data.f_LO2 =
1588 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1589 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001590
1591 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001592 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001593 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001594 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1595 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001596 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001597 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1598 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001599 status |= MT2063_DNC_RANGE;
1600 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001601 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001602 LO2LK = 0x40;
1603
1604 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001605 * If we have the same LO frequencies and we're already locked,
1606 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001607 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001608 if ((ofLO1 != state->AS_Data.f_LO1)
1609 || (ofLO2 != state->AS_Data.f_LO2)
1610 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001611 (LO1LK | LO2LK))) {
1612 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001613 * Calculate the FIFFOF register value
1614 *
1615 * IF1_Actual
1616 * FIFFOF = ------------ - 8 * FIFFC - 4992
1617 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001618 */
1619 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001620 (state->AS_Data.f_LO1 -
1621 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001622 4992;
1623 if (fiffof > 0xFF)
1624 fiffof = 0xFF;
1625
1626 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001627 * Place all of the calculated values into the local tuner
1628 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001629 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001630 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001631 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1632 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1633 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001634 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001635 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1636 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001637
1638 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001639 * Now write out the computed register values
1640 * IMPORTANT: There is a required order for writing
1641 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001642 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001643 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 -03001644 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001645 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001646 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001647 }
1648 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001649 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001650 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001651 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001652 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001653 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001654 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001655 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001656 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001657 reg[MT2063_REG_FIFF_OFFSET],
1658 1);
1659 }
1660 }
1661
1662 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001663 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001664 */
1665
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001666 if (status < 0)
1667 return status;
1668
1669 status = mt2063_lockStatus(state);
1670 if (status < 0)
1671 return status;
1672 if (!status)
1673 return -EINVAL; /* Couldn't lock */
1674
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001675 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001676 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001677 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001678 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001679 }
1680
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001681 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001682}
1683
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001684static const u8 MT2063B0_defaults[] = {
1685 /* Reg, Value */
1686 0x19, 0x05,
1687 0x1B, 0x1D,
1688 0x1C, 0x1F,
1689 0x1D, 0x0F,
1690 0x1E, 0x3F,
1691 0x1F, 0x0F,
1692 0x20, 0x3F,
1693 0x22, 0x21,
1694 0x23, 0x3F,
1695 0x24, 0x20,
1696 0x25, 0x3F,
1697 0x27, 0xEE,
1698 0x2C, 0x27, /* bit at 0x20 is cleared below */
1699 0x30, 0x03,
1700 0x2C, 0x07, /* bit at 0x20 is cleared here */
1701 0x2D, 0x87,
1702 0x2E, 0xAA,
1703 0x28, 0xE1, /* Set the FIFCrst bit here */
1704 0x28, 0xE0, /* Clear the FIFCrst bit here */
1705 0x00
1706};
1707
1708/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1709static const u8 MT2063B1_defaults[] = {
1710 /* Reg, Value */
1711 0x05, 0xF0,
1712 0x11, 0x10, /* New Enable AFCsd */
1713 0x19, 0x05,
1714 0x1A, 0x6C,
1715 0x1B, 0x24,
1716 0x1C, 0x28,
1717 0x1D, 0x8F,
1718 0x1E, 0x14,
1719 0x1F, 0x8F,
1720 0x20, 0x57,
1721 0x22, 0x21, /* New - ver 1.03 */
1722 0x23, 0x3C, /* New - ver 1.10 */
1723 0x24, 0x20, /* New - ver 1.03 */
1724 0x2C, 0x24, /* bit at 0x20 is cleared below */
1725 0x2D, 0x87, /* FIFFQ=0 */
1726 0x2F, 0xF3,
1727 0x30, 0x0C, /* New - ver 1.11 */
1728 0x31, 0x1B, /* New - ver 1.11 */
1729 0x2C, 0x04, /* bit at 0x20 is cleared here */
1730 0x28, 0xE1, /* Set the FIFCrst bit here */
1731 0x28, 0xE0, /* Clear the FIFCrst bit here */
1732 0x00
1733};
1734
1735/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1736static const u8 MT2063B3_defaults[] = {
1737 /* Reg, Value */
1738 0x05, 0xF0,
1739 0x19, 0x3D,
1740 0x2C, 0x24, /* bit at 0x20 is cleared below */
1741 0x2C, 0x04, /* bit at 0x20 is cleared here */
1742 0x28, 0xE1, /* Set the FIFCrst bit here */
1743 0x28, 0xE0, /* Clear the FIFCrst bit here */
1744 0x00
1745};
1746
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001747static int mt2063_init(struct dvb_frontend *fe)
1748{
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001749 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001750 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001751 u8 all_resets = 0xF0; /* reset/load bits */
1752 const u8 *def = NULL;
1753 u32 FCRUN;
1754 s32 maxReads;
1755 u32 fcu_osc;
1756 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001757
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001758 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001759
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001760 /* Read the Part/Rev code from the tuner */
1761 status = mt2063_read(state, MT2063_REG_PART_REV, state->reg, 1);
1762 if (status < 0)
1763 return status;
1764
1765 /* Check the part/rev code */
1766 if (((state->reg[MT2063_REG_PART_REV] != MT2063_B0) /* MT2063 B0 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001767 && (state->reg[MT2063_REG_PART_REV] != MT2063_B1) /* MT2063 B1 */
1768 && (state->reg[MT2063_REG_PART_REV] != MT2063_B3))) /* MT2063 B3 */
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001769 return -ENODEV; /* Wrong tuner Part/Rev code */
1770
1771 /* Check the 2nd byte of the Part/Rev code from the tuner */
1772 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1773 &state->reg[MT2063_REG_RSVD_3B], 1);
1774
1775 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001776 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00))
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001777 return -ENODEV; /* Wrong tuner Part/Rev code */
1778
1779 /* Reset the tuner */
1780 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1781 if (status < 0)
1782 return status;
1783
1784 /* change all of the default values that vary from the HW reset values */
1785 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1786 switch (state->reg[MT2063_REG_PART_REV]) {
1787 case MT2063_B3:
1788 def = MT2063B3_defaults;
1789 break;
1790
1791 case MT2063_B1:
1792 def = MT2063B1_defaults;
1793 break;
1794
1795 case MT2063_B0:
1796 def = MT2063B0_defaults;
1797 break;
1798
1799 default:
1800 return -ENODEV;
1801 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001802 }
1803
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001804 while (status >= 0 && *def) {
1805 u8 reg = *def++;
1806 u8 val = *def++;
1807 status = mt2063_write(state, reg, &val, 1);
1808 }
1809 if (status < 0)
1810 return status;
1811
1812 /* Wait for FIFF location to complete. */
1813 FCRUN = 1;
1814 maxReads = 10;
1815 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1816 msleep(2);
1817 status = mt2063_read(state,
1818 MT2063_REG_XO_STATUS,
1819 &state->
1820 reg[MT2063_REG_XO_STATUS], 1);
1821 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1822 }
1823
1824 if (FCRUN != 0 || status < 0)
1825 return -ENODEV;
1826
1827 status = mt2063_read(state,
1828 MT2063_REG_FIFFC,
1829 &state->reg[MT2063_REG_FIFFC], 1);
1830 if (status < 0)
1831 return status;
1832
1833 /* Read back all the registers from the tuner */
1834 status = mt2063_read(state,
1835 MT2063_REG_PART_REV,
1836 state->reg, MT2063_REG_END_REGS);
1837 if (status < 0)
1838 return status;
1839
1840 /* Initialize the tuner state. */
1841 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1842 state->AS_Data.f_ref = MT2063_REF_FREQ;
1843 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1844 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1845 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1846 state->AS_Data.f_out = 43750000UL;
1847 state->AS_Data.f_out_bw = 6750000UL;
1848 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1849 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1850 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1851 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1852 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1853 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1854 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1855 state->AS_Data.f_LO1 = 2181000000UL;
1856 state->AS_Data.f_LO2 = 1486249786UL;
1857 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1858 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1859 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1860 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1861 state->num_regs = MT2063_REG_END_REGS;
1862 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1863 state->ctfilt_sw = 0;
1864
1865 state->CTFiltMax[0] = 69230000;
1866 state->CTFiltMax[1] = 105770000;
1867 state->CTFiltMax[2] = 140350000;
1868 state->CTFiltMax[3] = 177110000;
1869 state->CTFiltMax[4] = 212860000;
1870 state->CTFiltMax[5] = 241130000;
1871 state->CTFiltMax[6] = 274370000;
1872 state->CTFiltMax[7] = 309820000;
1873 state->CTFiltMax[8] = 342450000;
1874 state->CTFiltMax[9] = 378870000;
1875 state->CTFiltMax[10] = 416210000;
1876 state->CTFiltMax[11] = 456500000;
1877 state->CTFiltMax[12] = 495790000;
1878 state->CTFiltMax[13] = 534530000;
1879 state->CTFiltMax[14] = 572610000;
1880 state->CTFiltMax[15] = 598970000;
1881 state->CTFiltMax[16] = 635910000;
1882 state->CTFiltMax[17] = 672130000;
1883 state->CTFiltMax[18] = 714840000;
1884 state->CTFiltMax[19] = 739660000;
1885 state->CTFiltMax[20] = 770410000;
1886 state->CTFiltMax[21] = 814660000;
1887 state->CTFiltMax[22] = 846950000;
1888 state->CTFiltMax[23] = 867820000;
1889 state->CTFiltMax[24] = 915980000;
1890 state->CTFiltMax[25] = 947450000;
1891 state->CTFiltMax[26] = 983110000;
1892 state->CTFiltMax[27] = 1021630000;
1893 state->CTFiltMax[28] = 1061870000;
1894 state->CTFiltMax[29] = 1098330000;
1895 state->CTFiltMax[30] = 1138990000;
1896
1897 /*
1898 ** Fetch the FCU osc value and use it and the fRef value to
1899 ** scale all of the Band Max values
1900 */
1901
1902 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1903 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1904 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1905 if (status < 0)
1906 return status;
1907
1908 /* Read the ClearTune filter calibration value */
1909 status = mt2063_read(state, MT2063_REG_FIFFC,
1910 &state->reg[MT2063_REG_FIFFC], 1);
1911 if (status < 0)
1912 return status;
1913
1914 fcu_osc = state->reg[MT2063_REG_FIFFC];
1915
1916 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1917 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1918 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1919 if (status < 0)
1920 return status;
1921
1922 /* Adjust each of the values in the ClearTune filter cross-over table */
1923 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001924 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001925
1926 status = MT2063_SoftwareShutdown(state, 1);
1927 if (status < 0)
1928 return status;
1929 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1930 if (status < 0)
1931 return status;
1932
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001933 return 0;
1934}
1935
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001936static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001937{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001938 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001939 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001940
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001941 *tuner_status = 0;
1942 status = mt2063_lockStatus(state);
1943 if (status < 0)
1944 return status;
1945 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001946 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001947
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001948 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001949}
1950
1951static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001952{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001953 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001954
1955 fe->tuner_priv = NULL;
1956 kfree(state);
1957
1958 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001959}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001960
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001961static int mt2063_set_analog_params(struct dvb_frontend *fe,
1962 struct analog_parameters *params)
1963{
1964 struct mt2063_state *state = fe->tuner_priv;
1965 s32 pict_car = 0;
1966 s32 pict2chanb_vsb = 0;
1967 s32 pict2chanb_snd = 0;
1968 s32 pict2snd1 = 0;
1969 s32 pict2snd2 = 0;
1970 s32 ch_bw = 0;
1971 s32 if_mid = 0;
1972 s32 rcvr_mode = 0;
1973 int status;
1974
1975 switch (params->mode) {
1976 case V4L2_TUNER_RADIO:
1977 pict_car = 38900000;
1978 ch_bw = 8000000;
1979 pict2chanb_vsb = -(ch_bw / 2);
1980 pict2snd1 = 0;
1981 pict2snd2 = 0;
1982 rcvr_mode = MT2063_OFFAIR_ANALOG;
1983 break;
1984 case V4L2_TUNER_ANALOG_TV:
1985 rcvr_mode = MT2063_CABLE_ANALOG;
1986 if (params->std & ~V4L2_STD_MN) {
1987 pict_car = 38900000;
1988 ch_bw = 6000000;
1989 pict2chanb_vsb = -1250000;
1990 pict2snd1 = 4500000;
1991 pict2snd2 = 0;
1992 } else if (params->std & V4L2_STD_PAL_I) {
1993 pict_car = 38900000;
1994 ch_bw = 8000000;
1995 pict2chanb_vsb = -1250000;
1996 pict2snd1 = 6000000;
1997 pict2snd2 = 0;
1998 } else if (params->std & V4L2_STD_PAL_B) {
1999 pict_car = 38900000;
2000 ch_bw = 8000000;
2001 pict2chanb_vsb = -1250000;
2002 pict2snd1 = 5500000;
2003 pict2snd2 = 5742000;
2004 } else if (params->std & V4L2_STD_PAL_G) {
2005 pict_car = 38900000;
2006 ch_bw = 7000000;
2007 pict2chanb_vsb = -1250000;
2008 pict2snd1 = 5500000;
2009 pict2snd2 = 0;
2010 } else if (params->std & V4L2_STD_PAL_DK) {
2011 pict_car = 38900000;
2012 ch_bw = 8000000;
2013 pict2chanb_vsb = -1250000;
2014 pict2snd1 = 6500000;
2015 pict2snd2 = 0;
2016 } else { /* PAL-L */
2017 pict_car = 38900000;
2018 ch_bw = 8000000;
2019 pict2chanb_vsb = -1250000;
2020 pict2snd1 = 6500000;
2021 pict2snd2 = 0;
2022 }
2023 break;
2024 }
2025 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2026 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2027
2028 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2029 state->AS_Data.f_out = if_mid;
2030 state->AS_Data.f_out_bw = ch_bw + 750000;
2031 status = MT2063_SetReceiverMode(state, rcvr_mode);
2032 if (status < 0)
2033 return status;
2034
2035 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2036 if (status < 0)
2037 return status;
2038
2039 state->frequency = params->frequency;
2040 return 0;
2041}
2042
2043/*
2044 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2045 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002046 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002047 * As such, the maximum symbol rate supported by 6 MHz is given by:
2048 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2049 */
2050#define MAX_SYMBOL_RATE_6MHz 5217391
2051
2052static int mt2063_set_params(struct dvb_frontend *fe,
2053 struct dvb_frontend_parameters *params)
2054{
2055 struct mt2063_state *state = fe->tuner_priv;
2056 int status;
2057 s32 pict_car = 0;
2058 s32 pict2chanb_vsb = 0;
2059 s32 pict2chanb_snd = 0;
2060 s32 pict2snd1 = 0;
2061 s32 pict2snd2 = 0;
2062 s32 ch_bw = 0;
2063 s32 if_mid = 0;
2064 s32 rcvr_mode = 0;
2065
2066 switch (fe->ops.info.type) {
2067 case FE_OFDM:
2068 switch (params->u.ofdm.bandwidth) {
2069 case BANDWIDTH_6_MHZ:
2070 ch_bw = 6000000;
2071 break;
2072 case BANDWIDTH_7_MHZ:
2073 ch_bw = 7000000;
2074 break;
2075 case BANDWIDTH_8_MHZ:
2076 ch_bw = 8000000;
2077 break;
2078 default:
2079 return -EINVAL;
2080 }
2081 rcvr_mode = MT2063_OFFAIR_COFDM;
2082 pict_car = 36125000;
2083 pict2chanb_vsb = -(ch_bw / 2);
2084 pict2snd1 = 0;
2085 pict2snd2 = 0;
2086 break;
2087 case FE_QAM:
2088 /*
2089 * Using a 8MHz bandwidth sometimes fail
2090 * with 6MHz-spaced channels, due to inter-carrier
2091 * interference. So, it is better to narrow-down the filter
2092 */
2093 if (params->u.qam.symbol_rate <= MAX_SYMBOL_RATE_6MHz)
2094 ch_bw = 6000000;
2095 else
2096 ch_bw = 8000000;
2097 rcvr_mode = MT2063_CABLE_QAM;
2098 pict_car = 36125000;
2099 pict2snd1 = 0;
2100 pict2snd2 = 0;
2101 pict2chanb_vsb = -(ch_bw / 2);
2102 break;
2103 default:
2104 return -EINVAL;
2105 }
2106 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2107 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2108
2109 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2110 state->AS_Data.f_out = if_mid;
2111 state->AS_Data.f_out_bw = ch_bw + 750000;
2112 status = MT2063_SetReceiverMode(state, rcvr_mode);
2113 if (status < 0)
2114 return status;
2115
2116 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2117
2118 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002119 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002120
2121 state->frequency = params->frequency;
2122 return 0;
2123}
2124
2125static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2126{
2127 struct mt2063_state *state = fe->tuner_priv;
2128
2129 *freq = state->frequency;
2130 return 0;
2131}
2132
2133static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2134{
2135 struct mt2063_state *state = fe->tuner_priv;
2136
2137 *bw = state->AS_Data.f_out_bw - 750000;
2138 return 0;
2139}
2140
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002141static struct dvb_tuner_ops mt2063_ops = {
2142 .info = {
2143 .name = "MT2063 Silicon Tuner",
2144 .frequency_min = 45000000,
2145 .frequency_max = 850000000,
2146 .frequency_step = 0,
2147 },
2148
2149 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002150 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002151 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002152 .set_analog_params = mt2063_set_analog_params,
2153 .set_params = mt2063_set_params,
2154 .get_frequency = mt2063_get_frequency,
2155 .get_bandwidth = mt2063_get_bandwidth,
2156 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002157};
2158
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002159struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2160 struct mt2063_config *config,
2161 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002162{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002163 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002164
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002165 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002166 if (state == NULL)
2167 goto error;
2168
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002169 state->config = config;
2170 state->i2c = i2c;
2171 state->frontend = fe;
2172 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002173 fe->tuner_priv = state;
2174 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002175
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002176 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002177 return fe;
2178
2179error:
2180 kfree(state);
2181 return NULL;
2182}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002183EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002184
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002185/*
2186 * Ancillary routines visible outside mt2063
2187 * FIXME: Remove them in favor of using standard tuner callbacks
2188 */
2189unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2190{
2191 struct mt2063_state *state = fe->tuner_priv;
2192 int err = 0;
2193
2194 err = MT2063_SoftwareShutdown(state, 1);
2195 if (err < 0)
2196 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2197
2198 return err;
2199}
2200EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2201
2202unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2203{
2204 struct mt2063_state *state = fe->tuner_priv;
2205 int err = 0;
2206
2207 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2208 if (err < 0)
2209 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2210
2211 return err;
2212}
2213EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2214
2215
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002216MODULE_PARM_DESC(verbose, "Set Verbosity level");
2217
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002218MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002219MODULE_DESCRIPTION("MT2063 Silicon tuner");
2220MODULE_LICENSE("GPL");