blob: 181deac72008404ed8c9f8402f638d0e593ccca0 [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 * Parameter for selecting tuner mode
134 */
135enum MT2063_RCVR_MODES {
136 MT2063_CABLE_QAM = 0, /* Digital cable */
137 MT2063_CABLE_ANALOG, /* Analog cable */
138 MT2063_OFFAIR_COFDM, /* Digital offair */
139 MT2063_OFFAIR_COFDM_SAWLESS, /* Digital offair without SAW */
140 MT2063_OFFAIR_ANALOG, /* Analog offair */
141 MT2063_OFFAIR_8VSB, /* Analog offair */
142 MT2063_NUM_RCVR_MODES
143};
144
145/*
146 * Possible values for MT2063_DNC_OUTPUT
147 */
148enum MT2063_DNC_Output_Enable {
149 MT2063_DNC_NONE = 0,
150 MT2063_DNC_1,
151 MT2063_DNC_2,
152 MT2063_DNC_BOTH
153};
154
155/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300156 * Two-wire serial bus subaddresses of the tuner registers.
157 * Also known as the tuner's register addresses.
158 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300159enum MT2063_Register_Offsets {
160 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
161 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
162 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
163 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
164 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
165 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
166 MT2063_REG_RSVD_06, /* 0x06: Reserved */
167 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
168 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
169 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
170 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
171 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
172 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
173 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
174 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
175 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
176 MT2063_REG_RSVD_10, /* 0x10: Reserved */
177 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
178 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
179 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
180 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
181 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
182 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
183 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
184 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
185 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
186 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
187 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
188 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
189 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
190 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
191 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
192 MT2063_REG_RSVD_20, /* 0x20: Reserved */
193 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
194 MT2063_REG_RSVD_22, /* 0x22: Reserved */
195 MT2063_REG_RSVD_23, /* 0x23: Reserved */
196 MT2063_REG_RSVD_24, /* 0x24: Reserved */
197 MT2063_REG_RSVD_25, /* 0x25: Reserved */
198 MT2063_REG_RSVD_26, /* 0x26: Reserved */
199 MT2063_REG_RSVD_27, /* 0x27: Reserved */
200 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
201 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
202 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
203 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
204 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
205 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
206 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
207 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
208 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
209 MT2063_REG_RSVD_31, /* 0x31: Reserved */
210 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
211 MT2063_REG_RSVD_33, /* 0x33: Reserved */
212 MT2063_REG_RSVD_34, /* 0x34: Reserved */
213 MT2063_REG_RSVD_35, /* 0x35: Reserved */
214 MT2063_REG_RSVD_36, /* 0x36: Reserved */
215 MT2063_REG_RSVD_37, /* 0x37: Reserved */
216 MT2063_REG_RSVD_38, /* 0x38: Reserved */
217 MT2063_REG_RSVD_39, /* 0x39: Reserved */
218 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
219 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
220 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
221 MT2063_REG_END_REGS
222};
223
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300224struct mt2063_state {
225 struct i2c_adapter *i2c;
226
227 const struct mt2063_config *config;
228 struct dvb_tuner_ops ops;
229 struct dvb_frontend *frontend;
230 struct tuner_state status;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300231
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300232 u32 frequency;
233 u32 srate;
234 u32 bandwidth;
235 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300236
237 u32 tuner_id;
238 struct MT2063_AvoidSpursData_t AS_Data;
239 u32 f_IF1_actual;
240 u32 rcvr_mode;
241 u32 ctfilt_sw;
242 u32 CTFiltMax[31];
243 u32 num_regs;
244 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300245};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300246
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300247/*
248 * mt2063_write - Write data into the I2C bus
249 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300250static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300251{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300252 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300253 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300254 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300255 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300256 .addr = state->config->tuner_address,
257 .flags = 0,
258 .buf = buf,
259 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300260 };
261
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300262 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300263 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300264
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300265 if (fe->ops.i2c_gate_ctrl)
266 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300267 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300268 if (fe->ops.i2c_gate_ctrl)
269 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300270
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300271 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300272 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300273
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300274 return ret;
275}
276
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300277/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300278 * mt2063_write - Write register data into the I2C bus, caching the value
279 */
280static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
281{
282 u32 status;
283
284 if (reg >= MT2063_REG_END_REGS)
285 return -ERANGE;
286
287 status = mt2063_write(state, reg, &val, 1);
288 if (status < 0)
289 return status;
290
291 state->reg[reg] = val;
292
293 return 0;
294}
295
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300296/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300297 * mt2063_read - Read data from the I2C bus
298 */
299static u32 mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300300 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300301{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300302 u32 status = 0; /* Status to be returned */
303 struct dvb_frontend *fe = state->frontend;
304 u32 i = 0;
305
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300306 if (fe->ops.i2c_gate_ctrl)
307 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300308
309 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300310 int ret;
311 u8 b0[] = { subAddress + i };
312 struct i2c_msg msg[] = {
313 {
314 .addr = state->config->tuner_address,
315 .flags = I2C_M_RD,
316 .buf = b0,
317 .len = 1
318 }, {
319 .addr = state->config->tuner_address,
320 .flags = I2C_M_RD,
321 .buf = pData + 1,
322 .len = 1
323 }
324 };
325
326 ret = i2c_transfer(state->i2c, msg, 2);
327 if (ret < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300328 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300329 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300330 if (fe->ops.i2c_gate_ctrl)
331 fe->ops.i2c_gate_ctrl(fe, 0);
332
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300333 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300334}
335
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300336/*
337 * FIXME: Is this really needed?
338 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300339static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300340{
341 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300342 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300343 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300344 msleep(10);
345
346 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300347}
348
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300349/*
350 * Microtune spur avoidance
351 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300352
353/* Implement ceiling, floor functions. */
354#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300355#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300356
357struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300358 s32 min_;
359 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300360};
361
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300362static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
363 *pAS_Info,
364 struct MT2063_ExclZone_t *pPrevNode)
365{
366 struct MT2063_ExclZone_t *pNode;
367 /* Check for a node in the free list */
368 if (pAS_Info->freeZones != NULL) {
369 /* Use one from the free list */
370 pNode = pAS_Info->freeZones;
371 pAS_Info->freeZones = pNode->next_;
372 } else {
373 /* Grab a node from the array */
374 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
375 }
376
377 if (pPrevNode != NULL) {
378 pNode->next_ = pPrevNode->next_;
379 pPrevNode->next_ = pNode;
380 } else { /* insert at the beginning of the list */
381
382 pNode->next_ = pAS_Info->usedZones;
383 pAS_Info->usedZones = pNode;
384 }
385
386 pAS_Info->nZones++;
387 return pNode;
388}
389
390static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
391 *pAS_Info,
392 struct MT2063_ExclZone_t *pPrevNode,
393 struct MT2063_ExclZone_t
394 *pNodeToRemove)
395{
396 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
397
398 /* Make previous node point to the subsequent node */
399 if (pPrevNode != NULL)
400 pPrevNode->next_ = pNext;
401
402 /* Add pNodeToRemove to the beginning of the freeZones */
403 pNodeToRemove->next_ = pAS_Info->freeZones;
404 pAS_Info->freeZones = pNodeToRemove;
405
406 /* Decrement node count */
407 pAS_Info->nZones--;
408
409 return pNext;
410}
411
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300412/*
413 * MT_AddExclZone()
414 *
415 * Add (and merge) an exclusion zone into the list.
416 * If the range (f_min, f_max) is totally outside the
417 * 1st IF BW, ignore the entry.
418 * If the range (f_min, f_max) is negative, ignore the entry.
419 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300420static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300421 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300422{
423 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
424 struct MT2063_ExclZone_t *pPrev = NULL;
425 struct MT2063_ExclZone_t *pNext = NULL;
426
427 /* Check to see if this overlaps the 1st IF filter */
428 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
429 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
430 && (f_min < f_max)) {
431 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300432 * 1 2 3 4 5 6
433 *
434 * New entry: |---| |--| |--| |-| |---| |--|
435 * or or or or or
436 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300437 */
438
439 /* Check for our place in the list */
440 while ((pNode != NULL) && (pNode->max_ < f_min)) {
441 pPrev = pNode;
442 pNode = pNode->next_;
443 }
444
445 if ((pNode != NULL) && (pNode->min_ < f_max)) {
446 /* Combine me with pNode */
447 if (f_min < pNode->min_)
448 pNode->min_ = f_min;
449 if (f_max > pNode->max_)
450 pNode->max_ = f_max;
451 } else {
452 pNode = InsertNode(pAS_Info, pPrev);
453 pNode->min_ = f_min;
454 pNode->max_ = f_max;
455 }
456
457 /* Look for merging possibilities */
458 pNext = pNode->next_;
459 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
460 if (pNext->max_ > pNode->max_)
461 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300462 /* Remove pNext, return ptr to pNext->next */
463 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300464 }
465 }
466}
467
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300468/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300469 * Reset all exclusion zones.
470 * Add zones to protect the PLL FracN regions near zero
471 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300472static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
473{
474 u32 center;
475
476 pAS_Info->nZones = 0; /* this clears the used list */
477 pAS_Info->usedZones = NULL; /* reset ptr */
478 pAS_Info->freeZones = NULL; /* reset ptr */
479
480 center =
481 pAS_Info->f_ref *
482 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
483 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
484 while (center <
485 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
486 pAS_Info->f_LO1_FracN_Avoid) {
487 /* Exclude LO1 FracN */
488 MT2063_AddExclZone(pAS_Info,
489 center - pAS_Info->f_LO1_FracN_Avoid,
490 center - 1);
491 MT2063_AddExclZone(pAS_Info, center + 1,
492 center + pAS_Info->f_LO1_FracN_Avoid);
493 center += pAS_Info->f_ref;
494 }
495
496 center =
497 pAS_Info->f_ref *
498 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
499 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
500 while (center <
501 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
502 pAS_Info->f_LO2_FracN_Avoid) {
503 /* Exclude LO2 FracN */
504 MT2063_AddExclZone(pAS_Info,
505 center - pAS_Info->f_LO2_FracN_Avoid,
506 center - 1);
507 MT2063_AddExclZone(pAS_Info, center + 1,
508 center + pAS_Info->f_LO2_FracN_Avoid);
509 center += pAS_Info->f_ref;
510 }
511
512 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
513 /* Exclude LO1 values that conflict with DECT channels */
514 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
515 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
516 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
517 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
518 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
519 }
520
521 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
522 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
523 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
524 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
525 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
526 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
527 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
528 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
529 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
530 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
531 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
532 }
533}
534
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300535/*
536 * MT_ChooseFirstIF - Choose the best available 1st IF
537 * If f_Desired is not excluded, choose that first.
538 * Otherwise, return the value closest to f_Center that is
539 * not excluded
540 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300541static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300542{
543 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300544 * Update "f_Desired" to be the nearest "combinational-multiple" of
545 * "f_LO1_Step".
546 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
547 * And F_LO1 is the arithmetic sum of f_in + f_Center.
548 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
549 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300550 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300551 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300552 pAS_Info->f_LO1_Step *
553 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
554 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
555 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300556 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300557 (pAS_Info->f_LO1_Step >
558 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
559 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300560 u32 f_Center;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300561
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300562 s32 i;
563 s32 j = 0;
564 u32 bDesiredExcluded = 0;
565 u32 bZeroExcluded = 0;
566 s32 tmpMin, tmpMax;
567 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300568 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
569 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
570
571 if (pAS_Info->nZones == 0)
572 return f_Desired;
573
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300574 /*
575 * f_Center needs to be an integer multiple of f_Step away
576 * from f_Desired
577 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300578 if (pAS_Info->f_if1_Center > f_Desired)
579 f_Center =
580 f_Desired +
581 f_Step *
582 ((pAS_Info->f_if1_Center - f_Desired +
583 f_Step / 2) / f_Step);
584 else
585 f_Center =
586 f_Desired -
587 f_Step *
588 ((f_Desired - pAS_Info->f_if1_Center +
589 f_Step / 2) / f_Step);
590
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300591 /*
592 * Take MT_ExclZones, center around f_Center and change the
593 * resolution to f_Step
594 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300595 while (pNode != NULL) {
596 /* floor function */
597 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300598 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300599
600 /* ceil function */
601 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300602 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300603
604 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
605 bDesiredExcluded = 1;
606
607 if ((tmpMin < 0) && (tmpMax > 0))
608 bZeroExcluded = 1;
609
610 /* See if this zone overlaps the previous */
611 if ((j > 0) && (tmpMin < zones[j - 1].max_))
612 zones[j - 1].max_ = tmpMax;
613 else {
614 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300615 zones[j].min_ = tmpMin;
616 zones[j].max_ = tmpMax;
617 j++;
618 }
619 pNode = pNode->next_;
620 }
621
622 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300623 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300624 */
625 if (bDesiredExcluded == 0)
626 return f_Desired;
627
628 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300629 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300630 */
631 if (bZeroExcluded == 0)
632 return f_Center;
633
634 /* Find the value closest to 0 (f_Center) */
635 bestDiff = zones[0].min_;
636 for (i = 0; i < j; i++) {
637 if (abs(zones[i].min_) < abs(bestDiff))
638 bestDiff = zones[i].min_;
639 if (abs(zones[i].max_) < abs(bestDiff))
640 bestDiff = zones[i].max_;
641 }
642
643 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300644 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300645
646 return f_Center + (bestDiff * f_Step);
647}
648
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300649/**
650 * gcd() - Uses Euclid's algorithm
651 *
652 * @u, @v: Unsigned values whose GCD is desired.
653 *
654 * Returns THE greatest common divisor of u and v, if either value is 0,
655 * the other value is returned as the result.
656 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300657static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300658{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300659 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300660
661 while (v != 0) {
662 r = u % v;
663 u = v;
664 v = r;
665 }
666
667 return u;
668}
669
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300670/**
671 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
672 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
673 *
674 * ma mb mc md
675 * <--+-+-+-------------------+-------------------+-+-+-->
676 * | ^ 0 ^ |
677 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
678 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
679 *
680 * Note that some equations are doubled to prevent round-off
681 * problems when calculating fIFBW/2
682 *
683 * @pAS_Info: Avoid Spurs information block
684 * @fm: If spur, amount f_IF1 has to move negative
685 * @fp: If spur, amount f_IF1 has to move positive
686 *
687 * Returns 1 if an LO spur would be present, otherwise 0.
688 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300689static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300690 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300691{
692 /*
693 ** Calculate LO frequency settings.
694 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300695 u32 n, n0;
696 const u32 f_LO1 = pAS_Info->f_LO1;
697 const u32 f_LO2 = pAS_Info->f_LO2;
698 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
699 const u32 c = d - pAS_Info->f_out_bw;
700 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300701 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300702 s32 f_nsLO1, f_nsLO2;
703 s32 f_Spur;
704 u32 ma, mb, mc, md, me, mf;
705 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300706 *fm = 0;
707
708 /*
709 ** For each edge (d, c & f), calculate a scale, based on the gcd
710 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
711 ** gcd-based scale factor or f_Scale.
712 */
713 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300714 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300715 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300716 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300717 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300718 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300719 hgfs = gf_Scale / 2;
720
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300721 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300722
723 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
724 for (n = n0; n <= pAS_Info->maxH1; ++n) {
725 md = (n * ((f_LO1 + hgds) / gd_Scale) -
726 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
727
728 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
729 if (md >= pAS_Info->maxH1)
730 break;
731
732 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
733 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
734
735 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
736 if (md == ma)
737 continue;
738
739 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
740 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
741 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300742 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
743 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300744 f_Spur =
745 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
746 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
747
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300748 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
749 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300750 return 1;
751 }
752
753 /* Location of Zero-IF-spur to be checked */
754 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
755 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
756 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
757 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
758 if (me != mf) {
759 f_nsLO1 = n * (f_LO1 / gf_Scale);
760 f_nsLO2 = me * (f_LO2 / gf_Scale);
761 f_Spur =
762 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
763 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
764
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300765 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
766 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300767 return 1;
768 }
769
770 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
771 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
772 if (ma != mb) {
773 f_nsLO1 = n * (f_LO1 / gc_Scale);
774 f_nsLO2 = ma * (f_LO2 / gc_Scale);
775 f_Spur =
776 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
777 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
778
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300779 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
780 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300781 return 1;
782 }
783 }
784
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300785 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300786 return 0;
787}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300788
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300789/*
790 * MT_AvoidSpurs() - Main entry point to avoid spurs.
791 * Checks for existing spurs in present LO1, LO2 freqs
792 * and if present, chooses spur-free LO1, LO2 combination
793 * that tunes the same input/output frequencies.
794 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300795static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300796{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300797 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300798 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300799 pAS_Info->bSpurAvoided = 0;
800 pAS_Info->nSpursFound = 0;
801
802 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300803 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300804
805 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300806 * Avoid LO Generated Spurs
807 *
808 * Make sure that have no LO-related spurs within the IF output
809 * bandwidth.
810 *
811 * If there is an LO spur in this band, start at the current IF1 frequency
812 * and work out until we find a spur-free frequency or run up against the
813 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
814 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300815 */
816 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
817 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300818 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
819 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
820 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
821 u32 delta_IF1;
822 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300823
824 /*
825 ** Spur was found, attempt to find a spur-free 1st IF
826 */
827 do {
828 pAS_Info->nSpursFound++;
829
830 /* Raise f_IF1_upper, if needed */
831 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
832
833 /* Choose next IF1 that is closest to f_IF1_CENTER */
834 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
835
836 if (new_IF1 > zfIF1) {
837 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
838 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
839 } else {
840 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
841 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
842 }
843 zfIF1 = new_IF1;
844
845 if (zfIF1 > pAS_Info->f_if1_Center)
846 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
847 else
848 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300849
850 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300851 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300852 * Continue while the new 1st IF is still within the 1st IF bandwidth
853 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300854 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300855 } 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 -0300856
857 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300858 * Use the LO-spur free values found. If the search went all
859 * the way to the 1st IF band edge and always found spurs, just
860 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300861 */
862 if (pAS_Info->bSpurPresent == 1) {
863 status |= MT2063_SPUR_PRESENT_ERR;
864 pAS_Info->f_LO1 = zfLO1;
865 pAS_Info->f_LO2 = zfLO2;
866 } else
867 pAS_Info->bSpurAvoided = 1;
868 }
869
870 status |=
871 ((pAS_Info->
872 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
873
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300874 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300875}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300876
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300877/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300878 * Constants used by the tuning algorithm
879 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300880#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
881#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
882#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
883#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
884#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
885#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
886#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
887#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
888#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
889#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
890#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
891#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
892#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
893#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
894#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
895#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
896#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
897#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
898
899/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300900 * Define the supported Part/Rev codes for the MT2063
901 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300902#define MT2063_B0 (0x9B)
903#define MT2063_B1 (0x9C)
904#define MT2063_B2 (0x9D)
905#define MT2063_B3 (0x9E)
906
907/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300908 * Constants for setting receiver modes.
909 * (6 modes defined at this time, enumerated by MT2063_RCVR_MODES)
910 * (DNC1GC & DNC2GC are the values, which are used, when the specific
911 * DNC Output is selected, the other is always off)
912 *
913 * enum MT2063_RCVR_MODES
914 * -------------+----------------------------------------------
915 * Mode 0 : | MT2063_CABLE_QAM
916 * Mode 1 : | MT2063_CABLE_ANALOG
917 * Mode 2 : | MT2063_OFFAIR_COFDM
918 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
919 * Mode 4 : | MT2063_OFFAIR_ANALOG
920 * Mode 5 : | MT2063_OFFAIR_8VSB
921 * --------------+----------------------------------------------
922 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300923static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
924static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
925static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
926static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
927static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
928static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
929static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
930static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
931static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
932static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
933static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
934static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
935static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
936static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300937
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300938/**
939 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
940 *
941 * @state: struct mt2063_state pointer
942 *
943 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
944 */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300945static unsigned int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300946{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300947 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
948 const u32 nPollRate = 2; /* poll status bits every 2 ms */
949 const u32 nMaxLoops = nMaxWait / nPollRate;
950 const u8 LO1LK = 0x80;
951 u8 LO2LK = 0x08;
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300952 u32 status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300953 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300954
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300955 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300956 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300957 LO2LK = 0x40;
958
959 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300960 status = mt2063_read(state, MT2063_REG_LO_STATUS,
961 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300962
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300963 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300964 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300965
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300966 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300967 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300968 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300969 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300970 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300971 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300972
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300973 /*
974 * Got no lock or partial lock
975 */
976 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300977}
978
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300979/*
980 * mt2063_set_dnc_output_enable()
981 */
982static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300983 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300984{
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300985 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
986 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
987 *pValue = MT2063_DNC_NONE;
988 else
989 *pValue = MT2063_DNC_2;
990 } else { /* DNC1 is on */
991 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
992 *pValue = MT2063_DNC_1;
993 else
994 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300995 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300996 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300997}
998
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300999/*
1000 * mt2063_set_dnc_output_enable()
1001 */
1002static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001003 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001004{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001005 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001006 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001007
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001008 /* selects, which DNC output is used */
1009 switch (nValue) {
1010 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001011 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1012 if (state->reg[MT2063_REG_DNC_GAIN] !=
1013 val)
1014 status |=
1015 mt2063_setreg(state,
1016 MT2063_REG_DNC_GAIN,
1017 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001018
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001019 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1020 if (state->reg[MT2063_REG_VGA_GAIN] !=
1021 val)
1022 status |=
1023 mt2063_setreg(state,
1024 MT2063_REG_VGA_GAIN,
1025 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001026
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001027 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1028 if (state->reg[MT2063_REG_RSVD_20] !=
1029 val)
1030 status |=
1031 mt2063_setreg(state,
1032 MT2063_REG_RSVD_20,
1033 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001034
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001035 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001036 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001037 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1038 if (state->reg[MT2063_REG_DNC_GAIN] !=
1039 val)
1040 status |=
1041 mt2063_setreg(state,
1042 MT2063_REG_DNC_GAIN,
1043 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001044
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001045 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1046 if (state->reg[MT2063_REG_VGA_GAIN] !=
1047 val)
1048 status |=
1049 mt2063_setreg(state,
1050 MT2063_REG_VGA_GAIN,
1051 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001052
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001053 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1054 if (state->reg[MT2063_REG_RSVD_20] !=
1055 val)
1056 status |=
1057 mt2063_setreg(state,
1058 MT2063_REG_RSVD_20,
1059 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001060
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001061 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001062 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001063 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1064 if (state->reg[MT2063_REG_DNC_GAIN] !=
1065 val)
1066 status |=
1067 mt2063_setreg(state,
1068 MT2063_REG_DNC_GAIN,
1069 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001070
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001071 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1072 if (state->reg[MT2063_REG_VGA_GAIN] !=
1073 val)
1074 status |=
1075 mt2063_setreg(state,
1076 MT2063_REG_VGA_GAIN,
1077 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001078
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001079 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1080 if (state->reg[MT2063_REG_RSVD_20] !=
1081 val)
1082 status |=
1083 mt2063_setreg(state,
1084 MT2063_REG_RSVD_20,
1085 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001086
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001087 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001088 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001089 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1090 if (state->reg[MT2063_REG_DNC_GAIN] !=
1091 val)
1092 status |=
1093 mt2063_setreg(state,
1094 MT2063_REG_DNC_GAIN,
1095 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001096
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001097 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1098 if (state->reg[MT2063_REG_VGA_GAIN] !=
1099 val)
1100 status |=
1101 mt2063_setreg(state,
1102 MT2063_REG_VGA_GAIN,
1103 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001104
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001105 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1106 if (state->reg[MT2063_REG_RSVD_20] !=
1107 val)
1108 status |=
1109 mt2063_setreg(state,
1110 MT2063_REG_RSVD_20,
1111 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001112
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001113 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001114 default:
1115 break;
1116 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001117
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001118 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001119}
1120
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001121/*
1122 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001123**
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001124 * enum MT2063_RCVR_MODES
1125 * --------------+----------------------------------------------
1126 * Mode 0 : | MT2063_CABLE_QAM
1127 * Mode 1 : | MT2063_CABLE_ANALOG
1128 * Mode 2 : | MT2063_OFFAIR_COFDM
1129 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
1130 * Mode 4 : | MT2063_OFFAIR_ANALOG
1131 * Mode 5 : | MT2063_OFFAIR_8VSB
1132 * --------------+----------------------------------------------
1133 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1134 * DNC Output is selected, the other is always off)
1135 *
1136 * |<---------- Mode -------------->|
1137 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
1138 * ------------+-----+-----+-----+-----+-----+-----+
1139 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
1140 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
1141 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
1142 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
1143 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
1144 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
1145 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
1146 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
1147 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
1148 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1149 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
1150 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
1151 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1152 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
1153 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
1154 *
1155 *
1156 * @state: ptr to mt2063_state structure
1157 * @Mode: desired reciever mode
1158 *
1159 * Note: Register cache must be valid for it to work
1160 */
1161
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001162static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001163 enum MT2063_RCVR_MODES Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001164{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001165 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001166 u8 val;
1167 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001168
1169 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001170 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001171
1172 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001173 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001174 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001175 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001176 reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001177 ? 0x40 :
1178 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001179 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001180 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001181 }
1182
1183 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001184 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001185 u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001186 (LNARIN[Mode] & 0x03);
1187 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001188 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001189 }
1190
1191 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001192 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001193 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001194 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001195 reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001196 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001197 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001198 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001199 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001200 /* trigger FIFF calibration, needed after changing FIFFQ */
1201 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001202 (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001203 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001204 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001205 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001206 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001207 reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001208 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001209 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001210 }
1211 }
1212
1213 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001214 status |= mt2063_get_dnc_output_enable(state, &longval);
1215 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001216
1217 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001218 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001219 u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001220 (ACLNAMAX[Mode] & 0x1F);
1221 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001222 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001223 }
1224
1225 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001226 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001227 u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001228 (LNATGT[Mode] & 0x3F);
1229 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001230 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001231 }
1232
1233 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001234 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001235 u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
1236 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001237 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001238 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001239 }
1240
1241 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001242 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001243 u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001244 (PD1TGT[Mode] & 0x3F);
1245 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001246 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001247 }
1248
1249 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001250 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001251 u8 val = ACFIFMAX[Mode];
1252 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1253 val = 5;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001254 val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001255 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001256 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001257 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001258 }
1259
1260 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001261 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001262 u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001263 (PD2TGT[Mode] & 0x3F);
1264 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001265 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001266 }
1267
1268 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001269 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001270 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
1271 (RFOVDIS[Mode] ? 0x80 : 0x00);
1272 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001273 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001274 }
1275
1276 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001277 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001278 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
1279 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1280 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001281 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001282 }
1283
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001284 if (status >= 0)
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001285 state->rcvr_mode = Mode;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001286
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001287 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001288}
1289
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001290/*
1291 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1292 * sections of the MT2063
1293 *
1294 * @Bits: Mask bits to be cleared.
1295 *
1296 * See definition of MT2063_Mask_Bits type for description
1297 * of each of the power bits.
1298 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001299static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1300 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001301{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001302 u32 status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001303
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001304 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1305 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001306 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001307 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001308 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001309 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001310 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001311 }
1312 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001313 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001314 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001315 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001316 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001317 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001318 }
1319
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001320 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001321}
1322
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001323/*
1324 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1325 * When Shutdown is 1, any section whose power
1326 * mask is set will be shutdown.
1327 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001328static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001329{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001330 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001331
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001332 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001333 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001334 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001335 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001336
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001337 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001338 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001339 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001340
1341 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001342 state->reg[MT2063_REG_BYP_CTRL] =
1343 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001344 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001345 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001346 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001347 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001348 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001349 state->reg[MT2063_REG_BYP_CTRL] =
1350 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001351 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001352 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001353 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001354 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001355 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001356 }
1357
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001358 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001359}
1360
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001361static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001362{
1363 return f_ref * (f_LO / f_ref)
1364 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1365}
1366
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001367/**
1368 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1369 * This function preserves maximum precision without
1370 * risk of overflow. It accurately calculates
1371 * f_ref * num / denom to within 1 HZ with fixed math.
1372 *
1373 * @num : Fractional portion of the multiplier
1374 * @denom: denominator portion of the ratio
1375 * @f_Ref: SRO frequency.
1376 *
1377 * This calculation handles f_ref as two separate 14-bit fields.
1378 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1379 * This is the genesis of the magic number "14" and the magic mask value of
1380 * 0x03FFF.
1381 *
1382 * This routine successfully handles denom values up to and including 2^18.
1383 * Returns: f_ref * num / denom
1384 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001385static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001386{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001387 u32 t1 = (f_ref >> 14) * num;
1388 u32 term1 = t1 / denom;
1389 u32 loss = t1 % denom;
1390 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001391 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001392 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001393}
1394
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001395/*
1396 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1397 * value for a FracN PLL.
1398 *
1399 * This function assumes that the f_LO and f_Ref are
1400 * evenly divisible by f_LO_Step.
1401 *
1402 * @Div: OUTPUT: Whole number portion of the multiplier
1403 * @FracN: OUTPUT: Fractional portion of the multiplier
1404 * @f_LO: desired LO frequency.
1405 * @f_LO_Step: Minimum step size for the LO (in Hz).
1406 * @f_Ref: SRO frequency.
1407 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1408 * of f_Ref (in Hz).
1409 *
1410 * Returns: Recalculated LO frequency.
1411 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001412static u32 MT2063_CalcLO1Mult(u32 *Div,
1413 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001414 u32 f_LO,
1415 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001416{
1417 /* Calculate the whole number portion of the divider */
1418 *Div = f_LO / f_Ref;
1419
1420 /* Calculate the numerator value (round to nearest f_LO_Step) */
1421 *FracN =
1422 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1423 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1424
1425 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1426}
1427
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001428/**
1429 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1430 * value for a FracN PLL.
1431 *
1432 * This function assumes that the f_LO and f_Ref are
1433 * evenly divisible by f_LO_Step.
1434 *
1435 * @Div: OUTPUT: Whole number portion of the multiplier
1436 * @FracN: OUTPUT: Fractional portion of the multiplier
1437 * @f_LO: desired LO frequency.
1438 * @f_LO_Step: Minimum step size for the LO (in Hz).
1439 * @f_Ref: SRO frequency.
1440 * @f_Avoid: Range of PLL frequencies to avoid near
1441 * integer multiples of f_Ref (in Hz).
1442 *
1443 * Returns: Recalculated LO frequency.
1444 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001445static u32 MT2063_CalcLO2Mult(u32 *Div,
1446 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001447 u32 f_LO,
1448 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001449{
1450 /* Calculate the whole number portion of the divider */
1451 *Div = f_LO / f_Ref;
1452
1453 /* Calculate the numerator value (round to nearest f_LO_Step) */
1454 *FracN =
1455 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1456 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1457
1458 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1459 8191);
1460}
1461
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001462/*
1463 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1464 * used for a given input frequency.
1465 *
1466 * @state: ptr to tuner data structure
1467 * @f_in: RF input center frequency (in Hz).
1468 *
1469 * Returns: ClearTune filter number (0-31)
1470 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001471static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001472{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001473 u32 RFBand;
1474 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001475
1476 /*
1477 ** Find RF Band setting
1478 */
1479 RFBand = 31; /* def when f_in > all */
1480 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001481 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001482 RFBand = idx;
1483 break;
1484 }
1485 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001486 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001487}
1488
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001489/*
1490 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1491 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001492static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001493{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001494
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001495 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001496 u32 LO1; /* 1st LO register value */
1497 u32 Num1; /* Numerator for LO1 reg. value */
1498 u32 f_IF1; /* 1st IF requested */
1499 u32 LO2; /* 2nd LO register value */
1500 u32 Num2; /* Numerator for LO2 reg. value */
1501 u32 ofLO1, ofLO2; /* last time's LO frequencies */
1502 u32 ofin, ofout; /* last time's I/O frequencies */
1503 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1504 u32 fiffof; /* Offset from FIFF center freq */
1505 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1506 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1507 u8 val;
1508 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001509
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001510 /* Check the input and output frequency ranges */
1511 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001512 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001513
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001514 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1515 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001516 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001517
1518 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001519 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001520 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001521 ofLO1 = state->AS_Data.f_LO1;
1522 ofLO2 = state->AS_Data.f_LO2;
1523 ofin = state->AS_Data.f_in;
1524 ofout = state->AS_Data.f_out;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001525
1526 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001527 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001528 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001529 if (state->ctfilt_sw == 1) {
1530 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1531 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001532 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001533 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001534 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001535 val = state->reg[MT2063_REG_CTUNE_OV];
1536 RFBand = FindClearTuneFilter(state, f_in);
1537 state->reg[MT2063_REG_CTUNE_OV] =
1538 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001539 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001540 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001541 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001542 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001543 }
1544 }
1545
1546 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001547 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001548 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001549 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001550 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001551 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001552 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001553 &state->reg[MT2063_REG_FIFFC], 1);
1554 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001555 }
1556 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001557 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001558 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001559 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001560 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001561 state->AS_Data.f_if1_Request =
1562 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1563 state->AS_Data.f_LO1_Step,
1564 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001565
1566 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001567 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1568 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001569 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001570 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001571
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001572 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001573
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001574 state->AS_Data.f_LO1 =
1575 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1576 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001577
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001578 state->AS_Data.f_LO2 =
1579 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1580 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001581
1582 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001583 * Check for any LO spurs in the output bandwidth and adjust
1584 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001585 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001586 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001587 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001588 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1589 * Recalculate the LO frequencies and the values to be placed
1590 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001591 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001592 state->AS_Data.f_LO1 =
1593 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1594 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1595 state->AS_Data.f_LO2 =
1596 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1597 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1598 state->AS_Data.f_LO2 =
1599 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1600 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001601
1602 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001603 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001604 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001605 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1606 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001607 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001608 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1609 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001610 status |= MT2063_DNC_RANGE;
1611 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001612 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001613 LO2LK = 0x40;
1614
1615 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001616 * If we have the same LO frequencies and we're already locked,
1617 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001618 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001619 if ((ofLO1 != state->AS_Data.f_LO1)
1620 || (ofLO2 != state->AS_Data.f_LO2)
1621 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001622 (LO1LK | LO2LK))) {
1623 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001624 * Calculate the FIFFOF register value
1625 *
1626 * IF1_Actual
1627 * FIFFOF = ------------ - 8 * FIFFC - 4992
1628 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001629 */
1630 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001631 (state->AS_Data.f_LO1 -
1632 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001633 4992;
1634 if (fiffof > 0xFF)
1635 fiffof = 0xFF;
1636
1637 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001638 * Place all of the calculated values into the local tuner
1639 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001640 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001641 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001642 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1643 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1644 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001645 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001646 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1647 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001648
1649 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001650 * Now write out the computed register values
1651 * IMPORTANT: There is a required order for writing
1652 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001653 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001654 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 -03001655 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001656 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001657 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001658 }
1659 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001660 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001661 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001662 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001663 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001664 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001665 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001666 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001667 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001668 reg[MT2063_REG_FIFF_OFFSET],
1669 1);
1670 }
1671 }
1672
1673 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001674 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001675 */
1676
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001677 if (status < 0)
1678 return status;
1679
1680 status = mt2063_lockStatus(state);
1681 if (status < 0)
1682 return status;
1683 if (!status)
1684 return -EINVAL; /* Couldn't lock */
1685
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001686 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001687 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001688 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001689 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001690 }
1691
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001692 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001693}
1694
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001695static const u8 MT2063B0_defaults[] = {
1696 /* Reg, Value */
1697 0x19, 0x05,
1698 0x1B, 0x1D,
1699 0x1C, 0x1F,
1700 0x1D, 0x0F,
1701 0x1E, 0x3F,
1702 0x1F, 0x0F,
1703 0x20, 0x3F,
1704 0x22, 0x21,
1705 0x23, 0x3F,
1706 0x24, 0x20,
1707 0x25, 0x3F,
1708 0x27, 0xEE,
1709 0x2C, 0x27, /* bit at 0x20 is cleared below */
1710 0x30, 0x03,
1711 0x2C, 0x07, /* bit at 0x20 is cleared here */
1712 0x2D, 0x87,
1713 0x2E, 0xAA,
1714 0x28, 0xE1, /* Set the FIFCrst bit here */
1715 0x28, 0xE0, /* Clear the FIFCrst bit here */
1716 0x00
1717};
1718
1719/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1720static const u8 MT2063B1_defaults[] = {
1721 /* Reg, Value */
1722 0x05, 0xF0,
1723 0x11, 0x10, /* New Enable AFCsd */
1724 0x19, 0x05,
1725 0x1A, 0x6C,
1726 0x1B, 0x24,
1727 0x1C, 0x28,
1728 0x1D, 0x8F,
1729 0x1E, 0x14,
1730 0x1F, 0x8F,
1731 0x20, 0x57,
1732 0x22, 0x21, /* New - ver 1.03 */
1733 0x23, 0x3C, /* New - ver 1.10 */
1734 0x24, 0x20, /* New - ver 1.03 */
1735 0x2C, 0x24, /* bit at 0x20 is cleared below */
1736 0x2D, 0x87, /* FIFFQ=0 */
1737 0x2F, 0xF3,
1738 0x30, 0x0C, /* New - ver 1.11 */
1739 0x31, 0x1B, /* New - ver 1.11 */
1740 0x2C, 0x04, /* bit at 0x20 is cleared here */
1741 0x28, 0xE1, /* Set the FIFCrst bit here */
1742 0x28, 0xE0, /* Clear the FIFCrst bit here */
1743 0x00
1744};
1745
1746/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1747static const u8 MT2063B3_defaults[] = {
1748 /* Reg, Value */
1749 0x05, 0xF0,
1750 0x19, 0x3D,
1751 0x2C, 0x24, /* bit at 0x20 is cleared below */
1752 0x2C, 0x04, /* bit at 0x20 is cleared here */
1753 0x28, 0xE1, /* Set the FIFCrst bit here */
1754 0x28, 0xE0, /* Clear the FIFCrst bit here */
1755 0x00
1756};
1757
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001758static int mt2063_init(struct dvb_frontend *fe)
1759{
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001760 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001761 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001762 u8 all_resets = 0xF0; /* reset/load bits */
1763 const u8 *def = NULL;
1764 u32 FCRUN;
1765 s32 maxReads;
1766 u32 fcu_osc;
1767 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001768
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001769 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001770
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001771 /* Read the Part/Rev code from the tuner */
1772 status = mt2063_read(state, MT2063_REG_PART_REV, state->reg, 1);
1773 if (status < 0)
1774 return status;
1775
1776 /* Check the part/rev code */
1777 if (((state->reg[MT2063_REG_PART_REV] != MT2063_B0) /* MT2063 B0 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001778 && (state->reg[MT2063_REG_PART_REV] != MT2063_B1) /* MT2063 B1 */
1779 && (state->reg[MT2063_REG_PART_REV] != MT2063_B3))) /* MT2063 B3 */
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001780 return -ENODEV; /* Wrong tuner Part/Rev code */
1781
1782 /* Check the 2nd byte of the Part/Rev code from the tuner */
1783 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1784 &state->reg[MT2063_REG_RSVD_3B], 1);
1785
1786 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001787 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00))
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001788 return -ENODEV; /* Wrong tuner Part/Rev code */
1789
1790 /* Reset the tuner */
1791 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1792 if (status < 0)
1793 return status;
1794
1795 /* change all of the default values that vary from the HW reset values */
1796 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1797 switch (state->reg[MT2063_REG_PART_REV]) {
1798 case MT2063_B3:
1799 def = MT2063B3_defaults;
1800 break;
1801
1802 case MT2063_B1:
1803 def = MT2063B1_defaults;
1804 break;
1805
1806 case MT2063_B0:
1807 def = MT2063B0_defaults;
1808 break;
1809
1810 default:
1811 return -ENODEV;
1812 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001813 }
1814
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001815 while (status >= 0 && *def) {
1816 u8 reg = *def++;
1817 u8 val = *def++;
1818 status = mt2063_write(state, reg, &val, 1);
1819 }
1820 if (status < 0)
1821 return status;
1822
1823 /* Wait for FIFF location to complete. */
1824 FCRUN = 1;
1825 maxReads = 10;
1826 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1827 msleep(2);
1828 status = mt2063_read(state,
1829 MT2063_REG_XO_STATUS,
1830 &state->
1831 reg[MT2063_REG_XO_STATUS], 1);
1832 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1833 }
1834
1835 if (FCRUN != 0 || status < 0)
1836 return -ENODEV;
1837
1838 status = mt2063_read(state,
1839 MT2063_REG_FIFFC,
1840 &state->reg[MT2063_REG_FIFFC], 1);
1841 if (status < 0)
1842 return status;
1843
1844 /* Read back all the registers from the tuner */
1845 status = mt2063_read(state,
1846 MT2063_REG_PART_REV,
1847 state->reg, MT2063_REG_END_REGS);
1848 if (status < 0)
1849 return status;
1850
1851 /* Initialize the tuner state. */
1852 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1853 state->AS_Data.f_ref = MT2063_REF_FREQ;
1854 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1855 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1856 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1857 state->AS_Data.f_out = 43750000UL;
1858 state->AS_Data.f_out_bw = 6750000UL;
1859 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1860 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1861 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1862 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1863 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1864 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1865 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1866 state->AS_Data.f_LO1 = 2181000000UL;
1867 state->AS_Data.f_LO2 = 1486249786UL;
1868 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1869 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1870 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1871 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1872 state->num_regs = MT2063_REG_END_REGS;
1873 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1874 state->ctfilt_sw = 0;
1875
1876 state->CTFiltMax[0] = 69230000;
1877 state->CTFiltMax[1] = 105770000;
1878 state->CTFiltMax[2] = 140350000;
1879 state->CTFiltMax[3] = 177110000;
1880 state->CTFiltMax[4] = 212860000;
1881 state->CTFiltMax[5] = 241130000;
1882 state->CTFiltMax[6] = 274370000;
1883 state->CTFiltMax[7] = 309820000;
1884 state->CTFiltMax[8] = 342450000;
1885 state->CTFiltMax[9] = 378870000;
1886 state->CTFiltMax[10] = 416210000;
1887 state->CTFiltMax[11] = 456500000;
1888 state->CTFiltMax[12] = 495790000;
1889 state->CTFiltMax[13] = 534530000;
1890 state->CTFiltMax[14] = 572610000;
1891 state->CTFiltMax[15] = 598970000;
1892 state->CTFiltMax[16] = 635910000;
1893 state->CTFiltMax[17] = 672130000;
1894 state->CTFiltMax[18] = 714840000;
1895 state->CTFiltMax[19] = 739660000;
1896 state->CTFiltMax[20] = 770410000;
1897 state->CTFiltMax[21] = 814660000;
1898 state->CTFiltMax[22] = 846950000;
1899 state->CTFiltMax[23] = 867820000;
1900 state->CTFiltMax[24] = 915980000;
1901 state->CTFiltMax[25] = 947450000;
1902 state->CTFiltMax[26] = 983110000;
1903 state->CTFiltMax[27] = 1021630000;
1904 state->CTFiltMax[28] = 1061870000;
1905 state->CTFiltMax[29] = 1098330000;
1906 state->CTFiltMax[30] = 1138990000;
1907
1908 /*
1909 ** Fetch the FCU osc value and use it and the fRef value to
1910 ** scale all of the Band Max values
1911 */
1912
1913 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1914 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1915 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1916 if (status < 0)
1917 return status;
1918
1919 /* Read the ClearTune filter calibration value */
1920 status = mt2063_read(state, MT2063_REG_FIFFC,
1921 &state->reg[MT2063_REG_FIFFC], 1);
1922 if (status < 0)
1923 return status;
1924
1925 fcu_osc = state->reg[MT2063_REG_FIFFC];
1926
1927 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1928 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1929 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1930 if (status < 0)
1931 return status;
1932
1933 /* Adjust each of the values in the ClearTune filter cross-over table */
1934 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001935 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001936
1937 status = MT2063_SoftwareShutdown(state, 1);
1938 if (status < 0)
1939 return status;
1940 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1941 if (status < 0)
1942 return status;
1943
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001944 return 0;
1945}
1946
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001947static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001948{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001949 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001950 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001951
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001952 *tuner_status = 0;
1953 status = mt2063_lockStatus(state);
1954 if (status < 0)
1955 return status;
1956 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001957 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001958
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001959 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001960}
1961
1962static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001963{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001964 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001965
1966 fe->tuner_priv = NULL;
1967 kfree(state);
1968
1969 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001970}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03001971
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001972static int mt2063_set_analog_params(struct dvb_frontend *fe,
1973 struct analog_parameters *params)
1974{
1975 struct mt2063_state *state = fe->tuner_priv;
1976 s32 pict_car = 0;
1977 s32 pict2chanb_vsb = 0;
1978 s32 pict2chanb_snd = 0;
1979 s32 pict2snd1 = 0;
1980 s32 pict2snd2 = 0;
1981 s32 ch_bw = 0;
1982 s32 if_mid = 0;
1983 s32 rcvr_mode = 0;
1984 int status;
1985
1986 switch (params->mode) {
1987 case V4L2_TUNER_RADIO:
1988 pict_car = 38900000;
1989 ch_bw = 8000000;
1990 pict2chanb_vsb = -(ch_bw / 2);
1991 pict2snd1 = 0;
1992 pict2snd2 = 0;
1993 rcvr_mode = MT2063_OFFAIR_ANALOG;
1994 break;
1995 case V4L2_TUNER_ANALOG_TV:
1996 rcvr_mode = MT2063_CABLE_ANALOG;
1997 if (params->std & ~V4L2_STD_MN) {
1998 pict_car = 38900000;
1999 ch_bw = 6000000;
2000 pict2chanb_vsb = -1250000;
2001 pict2snd1 = 4500000;
2002 pict2snd2 = 0;
2003 } else if (params->std & V4L2_STD_PAL_I) {
2004 pict_car = 38900000;
2005 ch_bw = 8000000;
2006 pict2chanb_vsb = -1250000;
2007 pict2snd1 = 6000000;
2008 pict2snd2 = 0;
2009 } else if (params->std & V4L2_STD_PAL_B) {
2010 pict_car = 38900000;
2011 ch_bw = 8000000;
2012 pict2chanb_vsb = -1250000;
2013 pict2snd1 = 5500000;
2014 pict2snd2 = 5742000;
2015 } else if (params->std & V4L2_STD_PAL_G) {
2016 pict_car = 38900000;
2017 ch_bw = 7000000;
2018 pict2chanb_vsb = -1250000;
2019 pict2snd1 = 5500000;
2020 pict2snd2 = 0;
2021 } else if (params->std & V4L2_STD_PAL_DK) {
2022 pict_car = 38900000;
2023 ch_bw = 8000000;
2024 pict2chanb_vsb = -1250000;
2025 pict2snd1 = 6500000;
2026 pict2snd2 = 0;
2027 } else { /* PAL-L */
2028 pict_car = 38900000;
2029 ch_bw = 8000000;
2030 pict2chanb_vsb = -1250000;
2031 pict2snd1 = 6500000;
2032 pict2snd2 = 0;
2033 }
2034 break;
2035 }
2036 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2037 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2038
2039 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2040 state->AS_Data.f_out = if_mid;
2041 state->AS_Data.f_out_bw = ch_bw + 750000;
2042 status = MT2063_SetReceiverMode(state, rcvr_mode);
2043 if (status < 0)
2044 return status;
2045
2046 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2047 if (status < 0)
2048 return status;
2049
2050 state->frequency = params->frequency;
2051 return 0;
2052}
2053
2054/*
2055 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2056 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002057 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002058 * As such, the maximum symbol rate supported by 6 MHz is given by:
2059 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2060 */
2061#define MAX_SYMBOL_RATE_6MHz 5217391
2062
2063static int mt2063_set_params(struct dvb_frontend *fe,
2064 struct dvb_frontend_parameters *params)
2065{
2066 struct mt2063_state *state = fe->tuner_priv;
2067 int status;
2068 s32 pict_car = 0;
2069 s32 pict2chanb_vsb = 0;
2070 s32 pict2chanb_snd = 0;
2071 s32 pict2snd1 = 0;
2072 s32 pict2snd2 = 0;
2073 s32 ch_bw = 0;
2074 s32 if_mid = 0;
2075 s32 rcvr_mode = 0;
2076
2077 switch (fe->ops.info.type) {
2078 case FE_OFDM:
2079 switch (params->u.ofdm.bandwidth) {
2080 case BANDWIDTH_6_MHZ:
2081 ch_bw = 6000000;
2082 break;
2083 case BANDWIDTH_7_MHZ:
2084 ch_bw = 7000000;
2085 break;
2086 case BANDWIDTH_8_MHZ:
2087 ch_bw = 8000000;
2088 break;
2089 default:
2090 return -EINVAL;
2091 }
2092 rcvr_mode = MT2063_OFFAIR_COFDM;
2093 pict_car = 36125000;
2094 pict2chanb_vsb = -(ch_bw / 2);
2095 pict2snd1 = 0;
2096 pict2snd2 = 0;
2097 break;
2098 case FE_QAM:
2099 /*
2100 * Using a 8MHz bandwidth sometimes fail
2101 * with 6MHz-spaced channels, due to inter-carrier
2102 * interference. So, it is better to narrow-down the filter
2103 */
2104 if (params->u.qam.symbol_rate <= MAX_SYMBOL_RATE_6MHz)
2105 ch_bw = 6000000;
2106 else
2107 ch_bw = 8000000;
2108 rcvr_mode = MT2063_CABLE_QAM;
2109 pict_car = 36125000;
2110 pict2snd1 = 0;
2111 pict2snd2 = 0;
2112 pict2chanb_vsb = -(ch_bw / 2);
2113 break;
2114 default:
2115 return -EINVAL;
2116 }
2117 pict2chanb_snd = pict2chanb_vsb - ch_bw;
2118 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2119
2120 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2121 state->AS_Data.f_out = if_mid;
2122 state->AS_Data.f_out_bw = ch_bw + 750000;
2123 status = MT2063_SetReceiverMode(state, rcvr_mode);
2124 if (status < 0)
2125 return status;
2126
2127 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2128
2129 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002130 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002131
2132 state->frequency = params->frequency;
2133 return 0;
2134}
2135
2136static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2137{
2138 struct mt2063_state *state = fe->tuner_priv;
2139
2140 *freq = state->frequency;
2141 return 0;
2142}
2143
2144static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2145{
2146 struct mt2063_state *state = fe->tuner_priv;
2147
2148 *bw = state->AS_Data.f_out_bw - 750000;
2149 return 0;
2150}
2151
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002152static struct dvb_tuner_ops mt2063_ops = {
2153 .info = {
2154 .name = "MT2063 Silicon Tuner",
2155 .frequency_min = 45000000,
2156 .frequency_max = 850000000,
2157 .frequency_step = 0,
2158 },
2159
2160 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002161 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002162 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002163 .set_analog_params = mt2063_set_analog_params,
2164 .set_params = mt2063_set_params,
2165 .get_frequency = mt2063_get_frequency,
2166 .get_bandwidth = mt2063_get_bandwidth,
2167 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002168};
2169
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002170struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2171 struct mt2063_config *config,
2172 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002173{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002174 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002175
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002176 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002177 if (state == NULL)
2178 goto error;
2179
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002180 state->config = config;
2181 state->i2c = i2c;
2182 state->frontend = fe;
2183 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002184 fe->tuner_priv = state;
2185 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002186
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002187 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002188 return fe;
2189
2190error:
2191 kfree(state);
2192 return NULL;
2193}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002194EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002195
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002196/*
2197 * Ancillary routines visible outside mt2063
2198 * FIXME: Remove them in favor of using standard tuner callbacks
2199 */
2200unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2201{
2202 struct mt2063_state *state = fe->tuner_priv;
2203 int err = 0;
2204
2205 err = MT2063_SoftwareShutdown(state, 1);
2206 if (err < 0)
2207 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2208
2209 return err;
2210}
2211EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2212
2213unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2214{
2215 struct mt2063_state *state = fe->tuner_priv;
2216 int err = 0;
2217
2218 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2219 if (err < 0)
2220 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2221
2222 return err;
2223}
2224EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2225
2226
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002227MODULE_PARM_DESC(verbose, "Set Verbosity level");
2228
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002229MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002230MODULE_DESCRIPTION("MT2063 Silicon tuner");
2231MODULE_LICENSE("GPL");