blob: de45c9db887d535b807f6b21344e7487ab7cbb9d [file] [log] [blame]
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001/*
2 * Driver for mt2063 Micronas tuner
3 *
4 * Copyright (c) 2011 Mauro Carvalho Chehab <mchehab@redhat.com>
5 *
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -03006 * This driver came from a driver originally written by:
7 * Henry Wang <Henry.wang@AzureWave.com>
8 * Made publicly available by Terratec, at:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03009 * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -030010 * The original driver's license is GPL, as declared with MODULE_LICENSE()
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -030011 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation under version 2 of the License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
21
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030022#include <linux/init.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/string.h>
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -030026#include <linux/videodev2.h>
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030027
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030028#include "mt2063.h"
29
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -030030static unsigned int debug;
31module_param(debug, int, 0644);
32MODULE_PARM_DESC(debug, "Set Verbosity level");
33
34#define dprintk(level, fmt, arg...) do { \
35if (debug >= level) \
36 printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \
37} while (0)
38
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030039
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -030040/* positive error codes used internally */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030041
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -030042/* Info: Unavoidable LO-related spur may be present in the output */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030043#define MT2063_SPUR_PRESENT_ERR (0x00800000)
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030044
45/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */
46#define MT2063_SPUR_CNT_MASK (0x001f0000)
47#define MT2063_SPUR_SHIFT (16)
48
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030049/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */
50#define MT2063_UPC_RANGE (0x04000000)
51
52/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */
53#define MT2063_DNC_RANGE (0x08000000)
54
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030055/*
56 * Constant defining the version of the following structure
57 * and therefore the API for this code.
58 *
59 * When compiling the tuner driver, the preprocessor will
60 * check against this version number to make sure that
61 * it matches the version that the tuner driver knows about.
62 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030063
64/* DECT Frequency Avoidance */
65#define MT2063_DECT_AVOID_US_FREQS 0x00000001
66
67#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002
68
69#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0)
70
71#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0)
72
73enum MT2063_DECT_Avoid_Type {
74 MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */
75 MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */
76 MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */
77 MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */
78};
79
80#define MT2063_MAX_ZONES 48
81
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030082struct MT2063_ExclZone_t {
83 u32 min_;
84 u32 max_;
85 struct MT2063_ExclZone_t *next_;
86};
87
88/*
89 * Structure of data needed for Spur Avoidance
90 */
91struct MT2063_AvoidSpursData_t {
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030092 u32 f_ref;
93 u32 f_in;
94 u32 f_LO1;
95 u32 f_if1_Center;
96 u32 f_if1_Request;
97 u32 f_if1_bw;
98 u32 f_LO2;
99 u32 f_out;
100 u32 f_out_bw;
101 u32 f_LO1_Step;
102 u32 f_LO2_Step;
103 u32 f_LO1_FracN_Avoid;
104 u32 f_LO2_FracN_Avoid;
105 u32 f_zif_bw;
106 u32 f_min_LO_Separation;
107 u32 maxH1;
108 u32 maxH2;
109 enum MT2063_DECT_Avoid_Type avoidDECT;
110 u32 bSpurPresent;
111 u32 bSpurAvoided;
112 u32 nSpursFound;
113 u32 nZones;
114 struct MT2063_ExclZone_t *freeZones;
115 struct MT2063_ExclZone_t *usedZones;
116 struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES];
117};
118
119/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300120 * Parameter for function MT2063_SetPowerMask that specifies the power down
121 * of various sections of the MT2063.
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300122 */
123enum MT2063_Mask_Bits {
124 MT2063_REG_SD = 0x0040, /* Shutdown regulator */
125 MT2063_SRO_SD = 0x0020, /* Shutdown SRO */
126 MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */
127 MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */
128 MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */
129 MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */
130 MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */
131 MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */
132 MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */
133 MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */
134 MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */
135 MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */
136 MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */
137 MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */
138 MT2063_NONE_SD = 0x0000 /* No shutdown bits */
139};
140
141/*
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300142 * Possible values for MT2063_DNC_OUTPUT
143 */
144enum MT2063_DNC_Output_Enable {
145 MT2063_DNC_NONE = 0,
146 MT2063_DNC_1,
147 MT2063_DNC_2,
148 MT2063_DNC_BOTH
149};
150
151/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300152 * Two-wire serial bus subaddresses of the tuner registers.
153 * Also known as the tuner's register addresses.
154 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300155enum MT2063_Register_Offsets {
156 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
157 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
158 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
159 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
160 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
161 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
162 MT2063_REG_RSVD_06, /* 0x06: Reserved */
163 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
164 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
165 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
166 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
167 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
168 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
169 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
170 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
171 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
172 MT2063_REG_RSVD_10, /* 0x10: Reserved */
173 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
174 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
175 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
176 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
177 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
178 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
179 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
180 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
181 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
182 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
183 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
184 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
185 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
186 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
187 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
188 MT2063_REG_RSVD_20, /* 0x20: Reserved */
189 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
190 MT2063_REG_RSVD_22, /* 0x22: Reserved */
191 MT2063_REG_RSVD_23, /* 0x23: Reserved */
192 MT2063_REG_RSVD_24, /* 0x24: Reserved */
193 MT2063_REG_RSVD_25, /* 0x25: Reserved */
194 MT2063_REG_RSVD_26, /* 0x26: Reserved */
195 MT2063_REG_RSVD_27, /* 0x27: Reserved */
196 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
197 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
198 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
199 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
200 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
201 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
202 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
203 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
204 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
205 MT2063_REG_RSVD_31, /* 0x31: Reserved */
206 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
207 MT2063_REG_RSVD_33, /* 0x33: Reserved */
208 MT2063_REG_RSVD_34, /* 0x34: Reserved */
209 MT2063_REG_RSVD_35, /* 0x35: Reserved */
210 MT2063_REG_RSVD_36, /* 0x36: Reserved */
211 MT2063_REG_RSVD_37, /* 0x37: Reserved */
212 MT2063_REG_RSVD_38, /* 0x38: Reserved */
213 MT2063_REG_RSVD_39, /* 0x39: Reserved */
214 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
215 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
216 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
217 MT2063_REG_END_REGS
218};
219
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300220struct mt2063_state {
221 struct i2c_adapter *i2c;
222
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -0300223 bool init;
224
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300225 const struct mt2063_config *config;
226 struct dvb_tuner_ops ops;
227 struct dvb_frontend *frontend;
228 struct tuner_state status;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300229
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300230 u32 frequency;
231 u32 srate;
232 u32 bandwidth;
233 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300234
235 u32 tuner_id;
236 struct MT2063_AvoidSpursData_t AS_Data;
237 u32 f_IF1_actual;
238 u32 rcvr_mode;
239 u32 ctfilt_sw;
240 u32 CTFiltMax[31];
241 u32 num_regs;
242 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300243};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300244
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300245/*
246 * mt2063_write - Write data into the I2C bus
247 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300248static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300249{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300250 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300251 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300252 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300253 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300254 .addr = state->config->tuner_address,
255 .flags = 0,
256 .buf = buf,
257 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300258 };
259
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300260 dprintk(2, "\n");
261
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300262 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300263 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300264
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300265 if (fe->ops.i2c_gate_ctrl)
266 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300267 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300268 if (fe->ops.i2c_gate_ctrl)
269 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300270
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300271 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300272 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300273
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300274 return ret;
275}
276
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300277/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300278 * mt2063_write - Write register data into the I2C bus, caching the value
279 */
280static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
281{
282 u32 status;
283
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300284 dprintk(2, "\n");
285
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300286 if (reg >= MT2063_REG_END_REGS)
287 return -ERANGE;
288
289 status = mt2063_write(state, reg, &val, 1);
290 if (status < 0)
291 return status;
292
293 state->reg[reg] = val;
294
295 return 0;
296}
297
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300298/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300299 * mt2063_read - Read data from the I2C bus
300 */
301static u32 mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300302 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300303{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300304 u32 status = 0; /* Status to be returned */
305 struct dvb_frontend *fe = state->frontend;
306 u32 i = 0;
307
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300308 dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300309
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300310 if (fe->ops.i2c_gate_ctrl)
311 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300312
313 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300314 u8 b0[] = { subAddress + i };
315 struct i2c_msg msg[] = {
316 {
317 .addr = state->config->tuner_address,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300318 .flags = 0,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300319 .buf = b0,
320 .len = 1
321 }, {
322 .addr = state->config->tuner_address,
323 .flags = I2C_M_RD,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300324 .buf = pData + i,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300325 .len = 1
326 }
327 };
328
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300329 status = i2c_transfer(state->i2c, msg, 2);
330 dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n",
331 subAddress + i, status, *(pData + i));
332 if (status < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300333 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300334 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300335 if (fe->ops.i2c_gate_ctrl)
336 fe->ops.i2c_gate_ctrl(fe, 0);
337
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300338 if (status < 0)
339 printk(KERN_ERR "Can't read from address 0x%02x,\n",
340 subAddress + i);
341
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300342 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300343}
344
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300345/*
346 * FIXME: Is this really needed?
347 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300348static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300349{
350 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300351 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300352 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300353 msleep(10);
354
355 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300356}
357
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300358/*
359 * Microtune spur avoidance
360 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300361
362/* Implement ceiling, floor functions. */
363#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300364#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300365
366struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300367 s32 min_;
368 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300369};
370
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300371static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
372 *pAS_Info,
373 struct MT2063_ExclZone_t *pPrevNode)
374{
375 struct MT2063_ExclZone_t *pNode;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300376
377 dprintk(2, "\n");
378
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300379 /* Check for a node in the free list */
380 if (pAS_Info->freeZones != NULL) {
381 /* Use one from the free list */
382 pNode = pAS_Info->freeZones;
383 pAS_Info->freeZones = pNode->next_;
384 } else {
385 /* Grab a node from the array */
386 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
387 }
388
389 if (pPrevNode != NULL) {
390 pNode->next_ = pPrevNode->next_;
391 pPrevNode->next_ = pNode;
392 } else { /* insert at the beginning of the list */
393
394 pNode->next_ = pAS_Info->usedZones;
395 pAS_Info->usedZones = pNode;
396 }
397
398 pAS_Info->nZones++;
399 return pNode;
400}
401
402static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
403 *pAS_Info,
404 struct MT2063_ExclZone_t *pPrevNode,
405 struct MT2063_ExclZone_t
406 *pNodeToRemove)
407{
408 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
409
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300410 dprintk(2, "\n");
411
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300412 /* Make previous node point to the subsequent node */
413 if (pPrevNode != NULL)
414 pPrevNode->next_ = pNext;
415
416 /* Add pNodeToRemove to the beginning of the freeZones */
417 pNodeToRemove->next_ = pAS_Info->freeZones;
418 pAS_Info->freeZones = pNodeToRemove;
419
420 /* Decrement node count */
421 pAS_Info->nZones--;
422
423 return pNext;
424}
425
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300426/*
427 * MT_AddExclZone()
428 *
429 * Add (and merge) an exclusion zone into the list.
430 * If the range (f_min, f_max) is totally outside the
431 * 1st IF BW, ignore the entry.
432 * If the range (f_min, f_max) is negative, ignore the entry.
433 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300434static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300435 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300436{
437 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
438 struct MT2063_ExclZone_t *pPrev = NULL;
439 struct MT2063_ExclZone_t *pNext = NULL;
440
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300441 dprintk(2, "\n");
442
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300443 /* Check to see if this overlaps the 1st IF filter */
444 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
445 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
446 && (f_min < f_max)) {
447 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300448 * 1 2 3 4 5 6
449 *
450 * New entry: |---| |--| |--| |-| |---| |--|
451 * or or or or or
452 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300453 */
454
455 /* Check for our place in the list */
456 while ((pNode != NULL) && (pNode->max_ < f_min)) {
457 pPrev = pNode;
458 pNode = pNode->next_;
459 }
460
461 if ((pNode != NULL) && (pNode->min_ < f_max)) {
462 /* Combine me with pNode */
463 if (f_min < pNode->min_)
464 pNode->min_ = f_min;
465 if (f_max > pNode->max_)
466 pNode->max_ = f_max;
467 } else {
468 pNode = InsertNode(pAS_Info, pPrev);
469 pNode->min_ = f_min;
470 pNode->max_ = f_max;
471 }
472
473 /* Look for merging possibilities */
474 pNext = pNode->next_;
475 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
476 if (pNext->max_ > pNode->max_)
477 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300478 /* Remove pNext, return ptr to pNext->next */
479 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300480 }
481 }
482}
483
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300484/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300485 * Reset all exclusion zones.
486 * Add zones to protect the PLL FracN regions near zero
487 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300488static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
489{
490 u32 center;
491
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300492 dprintk(2, "\n");
493
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300494 pAS_Info->nZones = 0; /* this clears the used list */
495 pAS_Info->usedZones = NULL; /* reset ptr */
496 pAS_Info->freeZones = NULL; /* reset ptr */
497
498 center =
499 pAS_Info->f_ref *
500 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
501 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
502 while (center <
503 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
504 pAS_Info->f_LO1_FracN_Avoid) {
505 /* Exclude LO1 FracN */
506 MT2063_AddExclZone(pAS_Info,
507 center - pAS_Info->f_LO1_FracN_Avoid,
508 center - 1);
509 MT2063_AddExclZone(pAS_Info, center + 1,
510 center + pAS_Info->f_LO1_FracN_Avoid);
511 center += pAS_Info->f_ref;
512 }
513
514 center =
515 pAS_Info->f_ref *
516 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
517 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
518 while (center <
519 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
520 pAS_Info->f_LO2_FracN_Avoid) {
521 /* Exclude LO2 FracN */
522 MT2063_AddExclZone(pAS_Info,
523 center - pAS_Info->f_LO2_FracN_Avoid,
524 center - 1);
525 MT2063_AddExclZone(pAS_Info, center + 1,
526 center + pAS_Info->f_LO2_FracN_Avoid);
527 center += pAS_Info->f_ref;
528 }
529
530 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
531 /* Exclude LO1 values that conflict with DECT channels */
532 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
533 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
534 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
535 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
536 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
537 }
538
539 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
540 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
541 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
542 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
543 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
544 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
545 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
546 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
547 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
548 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
549 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
550 }
551}
552
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300553/*
554 * MT_ChooseFirstIF - Choose the best available 1st IF
555 * If f_Desired is not excluded, choose that first.
556 * Otherwise, return the value closest to f_Center that is
557 * not excluded
558 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300559static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300560{
561 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300562 * Update "f_Desired" to be the nearest "combinational-multiple" of
563 * "f_LO1_Step".
564 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
565 * And F_LO1 is the arithmetic sum of f_in + f_Center.
566 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
567 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300568 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300569 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300570 pAS_Info->f_LO1_Step *
571 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
572 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
573 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300574 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300575 (pAS_Info->f_LO1_Step >
576 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
577 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300578 u32 f_Center;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300579 s32 i;
580 s32 j = 0;
581 u32 bDesiredExcluded = 0;
582 u32 bZeroExcluded = 0;
583 s32 tmpMin, tmpMax;
584 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300585 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
586 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
587
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300588 dprintk(2, "\n");
589
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300590 if (pAS_Info->nZones == 0)
591 return f_Desired;
592
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300593 /*
594 * f_Center needs to be an integer multiple of f_Step away
595 * from f_Desired
596 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300597 if (pAS_Info->f_if1_Center > f_Desired)
598 f_Center =
599 f_Desired +
600 f_Step *
601 ((pAS_Info->f_if1_Center - f_Desired +
602 f_Step / 2) / f_Step);
603 else
604 f_Center =
605 f_Desired -
606 f_Step *
607 ((f_Desired - pAS_Info->f_if1_Center +
608 f_Step / 2) / f_Step);
609
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300610 /*
611 * Take MT_ExclZones, center around f_Center and change the
612 * resolution to f_Step
613 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300614 while (pNode != NULL) {
615 /* floor function */
616 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300617 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300618
619 /* ceil function */
620 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300621 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300622
623 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
624 bDesiredExcluded = 1;
625
626 if ((tmpMin < 0) && (tmpMax > 0))
627 bZeroExcluded = 1;
628
629 /* See if this zone overlaps the previous */
630 if ((j > 0) && (tmpMin < zones[j - 1].max_))
631 zones[j - 1].max_ = tmpMax;
632 else {
633 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300634 zones[j].min_ = tmpMin;
635 zones[j].max_ = tmpMax;
636 j++;
637 }
638 pNode = pNode->next_;
639 }
640
641 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300642 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300643 */
644 if (bDesiredExcluded == 0)
645 return f_Desired;
646
647 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300648 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300649 */
650 if (bZeroExcluded == 0)
651 return f_Center;
652
653 /* Find the value closest to 0 (f_Center) */
654 bestDiff = zones[0].min_;
655 for (i = 0; i < j; i++) {
656 if (abs(zones[i].min_) < abs(bestDiff))
657 bestDiff = zones[i].min_;
658 if (abs(zones[i].max_) < abs(bestDiff))
659 bestDiff = zones[i].max_;
660 }
661
662 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300663 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300664
665 return f_Center + (bestDiff * f_Step);
666}
667
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300668/**
669 * gcd() - Uses Euclid's algorithm
670 *
671 * @u, @v: Unsigned values whose GCD is desired.
672 *
673 * Returns THE greatest common divisor of u and v, if either value is 0,
674 * the other value is returned as the result.
675 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300676static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300677{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300678 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300679
680 while (v != 0) {
681 r = u % v;
682 u = v;
683 v = r;
684 }
685
686 return u;
687}
688
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300689/**
690 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
691 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
692 *
693 * ma mb mc md
694 * <--+-+-+-------------------+-------------------+-+-+-->
695 * | ^ 0 ^ |
696 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
697 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
698 *
699 * Note that some equations are doubled to prevent round-off
700 * problems when calculating fIFBW/2
701 *
702 * @pAS_Info: Avoid Spurs information block
703 * @fm: If spur, amount f_IF1 has to move negative
704 * @fp: If spur, amount f_IF1 has to move positive
705 *
706 * Returns 1 if an LO spur would be present, otherwise 0.
707 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300708static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300709 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300710{
711 /*
712 ** Calculate LO frequency settings.
713 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300714 u32 n, n0;
715 const u32 f_LO1 = pAS_Info->f_LO1;
716 const u32 f_LO2 = pAS_Info->f_LO2;
717 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
718 const u32 c = d - pAS_Info->f_out_bw;
719 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300720 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300721 s32 f_nsLO1, f_nsLO2;
722 s32 f_Spur;
723 u32 ma, mb, mc, md, me, mf;
724 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300725
726 dprintk(2, "\n");
727
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300728 *fm = 0;
729
730 /*
731 ** For each edge (d, c & f), calculate a scale, based on the gcd
732 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
733 ** gcd-based scale factor or f_Scale.
734 */
735 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300736 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300737 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300738 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300739 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300740 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300741 hgfs = gf_Scale / 2;
742
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300743 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300744
745 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
746 for (n = n0; n <= pAS_Info->maxH1; ++n) {
747 md = (n * ((f_LO1 + hgds) / gd_Scale) -
748 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
749
750 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
751 if (md >= pAS_Info->maxH1)
752 break;
753
754 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
755 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
756
757 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
758 if (md == ma)
759 continue;
760
761 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
762 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
763 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300764 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
765 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300766 f_Spur =
767 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
768 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
769
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300770 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
771 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300772 return 1;
773 }
774
775 /* Location of Zero-IF-spur to be checked */
776 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
777 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
778 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
779 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
780 if (me != mf) {
781 f_nsLO1 = n * (f_LO1 / gf_Scale);
782 f_nsLO2 = me * (f_LO2 / gf_Scale);
783 f_Spur =
784 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
785 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
786
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300787 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
788 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300789 return 1;
790 }
791
792 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
793 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
794 if (ma != mb) {
795 f_nsLO1 = n * (f_LO1 / gc_Scale);
796 f_nsLO2 = ma * (f_LO2 / gc_Scale);
797 f_Spur =
798 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
799 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
800
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300801 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
802 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300803 return 1;
804 }
805 }
806
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300807 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300808 return 0;
809}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300810
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300811/*
812 * MT_AvoidSpurs() - Main entry point to avoid spurs.
813 * Checks for existing spurs in present LO1, LO2 freqs
814 * and if present, chooses spur-free LO1, LO2 combination
815 * that tunes the same input/output frequencies.
816 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300817static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300818{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300819 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300820 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300821 pAS_Info->bSpurAvoided = 0;
822 pAS_Info->nSpursFound = 0;
823
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300824 dprintk(2, "\n");
825
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300826 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300827 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300828
829 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300830 * Avoid LO Generated Spurs
831 *
832 * Make sure that have no LO-related spurs within the IF output
833 * bandwidth.
834 *
835 * If there is an LO spur in this band, start at the current IF1 frequency
836 * and work out until we find a spur-free frequency or run up against the
837 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
838 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300839 */
840 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
841 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300842 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
843 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
844 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
845 u32 delta_IF1;
846 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300847
848 /*
849 ** Spur was found, attempt to find a spur-free 1st IF
850 */
851 do {
852 pAS_Info->nSpursFound++;
853
854 /* Raise f_IF1_upper, if needed */
855 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
856
857 /* Choose next IF1 that is closest to f_IF1_CENTER */
858 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
859
860 if (new_IF1 > zfIF1) {
861 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
862 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
863 } else {
864 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
865 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
866 }
867 zfIF1 = new_IF1;
868
869 if (zfIF1 > pAS_Info->f_if1_Center)
870 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
871 else
872 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300873
874 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300875 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300876 * Continue while the new 1st IF is still within the 1st IF bandwidth
877 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300878 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300879 } 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 -0300880
881 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300882 * Use the LO-spur free values found. If the search went all
883 * the way to the 1st IF band edge and always found spurs, just
884 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300885 */
886 if (pAS_Info->bSpurPresent == 1) {
887 status |= MT2063_SPUR_PRESENT_ERR;
888 pAS_Info->f_LO1 = zfLO1;
889 pAS_Info->f_LO2 = zfLO2;
890 } else
891 pAS_Info->bSpurAvoided = 1;
892 }
893
894 status |=
895 ((pAS_Info->
896 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
897
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300898 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300899}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300900
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300901/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300902 * Constants used by the tuning algorithm
903 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300904#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
905#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
906#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
907#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
908#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
909#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
910#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
911#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
912#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
913#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
914#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
915#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
916#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
917#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
918#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
919#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
920#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
921#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
922
923/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300924 * Define the supported Part/Rev codes for the MT2063
925 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300926#define MT2063_B0 (0x9B)
927#define MT2063_B1 (0x9C)
928#define MT2063_B2 (0x9D)
929#define MT2063_B3 (0x9E)
930
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300931/**
932 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
933 *
934 * @state: struct mt2063_state pointer
935 *
936 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
937 */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300938static unsigned int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300939{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300940 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
941 const u32 nPollRate = 2; /* poll status bits every 2 ms */
942 const u32 nMaxLoops = nMaxWait / nPollRate;
943 const u8 LO1LK = 0x80;
944 u8 LO2LK = 0x08;
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300945 u32 status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300946 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300947
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300948 dprintk(2, "\n");
949
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300950 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300951 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300952 LO2LK = 0x40;
953
954 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300955 status = mt2063_read(state, MT2063_REG_LO_STATUS,
956 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300957
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300958 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300959 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300960
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300961 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300962 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300963 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300964 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300965 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300966 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300967
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300968 /*
969 * Got no lock or partial lock
970 */
971 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300972}
973
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300974/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300975 * Constants for setting receiver modes.
976 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
977 * (DNC1GC & DNC2GC are the values, which are used, when the specific
978 * DNC Output is selected, the other is always off)
979 *
980 * enum mt2063_delivery_sys
981 * -------------+----------------------------------------------
982 * Mode 0 : | MT2063_CABLE_QAM
983 * Mode 1 : | MT2063_CABLE_ANALOG
984 * Mode 2 : | MT2063_OFFAIR_COFDM
985 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
986 * Mode 4 : | MT2063_OFFAIR_ANALOG
987 * Mode 5 : | MT2063_OFFAIR_8VSB
988 * --------------+----------------------------------------------
989 *
990 * |<---------- Mode -------------->|
991 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
992 * ------------+-----+-----+-----+-----+-----+-----+
993 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
994 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
995 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
996 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
997 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
998 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
999 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
1000 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
1001 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
1002 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1003 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
1004 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
1005 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1006 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
1007 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
1008 */
1009
1010enum mt2063_delivery_sys {
1011 MT2063_CABLE_QAM = 0, /* Digital cable */
1012 MT2063_CABLE_ANALOG, /* Analog cable */
1013 MT2063_OFFAIR_COFDM, /* Digital offair */
1014 MT2063_OFFAIR_COFDM_SAWLESS, /* Digital offair without SAW */
1015 MT2063_OFFAIR_ANALOG, /* Analog offair */
1016 MT2063_OFFAIR_8VSB, /* Analog offair */
1017 MT2063_NUM_RCVR_MODES
1018};
1019
1020static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
1021static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
1022static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
1023static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
1024static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
1025static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
1026static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
1027static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
1028static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1029static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
1030static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
1031static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1032static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
1033static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
1034
1035/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001036 * mt2063_set_dnc_output_enable()
1037 */
1038static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001039 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001040{
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001041 dprintk(2, "\n");
1042
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001043 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1044 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1045 *pValue = MT2063_DNC_NONE;
1046 else
1047 *pValue = MT2063_DNC_2;
1048 } else { /* DNC1 is on */
1049 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1050 *pValue = MT2063_DNC_1;
1051 else
1052 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001053 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001054 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001055}
1056
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001057/*
1058 * mt2063_set_dnc_output_enable()
1059 */
1060static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001061 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001062{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001063 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001064 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001065
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001066 dprintk(2, "\n");
1067
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001068 /* selects, which DNC output is used */
1069 switch (nValue) {
1070 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001071 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1072 if (state->reg[MT2063_REG_DNC_GAIN] !=
1073 val)
1074 status |=
1075 mt2063_setreg(state,
1076 MT2063_REG_DNC_GAIN,
1077 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001078
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001079 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1080 if (state->reg[MT2063_REG_VGA_GAIN] !=
1081 val)
1082 status |=
1083 mt2063_setreg(state,
1084 MT2063_REG_VGA_GAIN,
1085 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001086
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001087 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1088 if (state->reg[MT2063_REG_RSVD_20] !=
1089 val)
1090 status |=
1091 mt2063_setreg(state,
1092 MT2063_REG_RSVD_20,
1093 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001094
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001095 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001096 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001097 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1098 if (state->reg[MT2063_REG_DNC_GAIN] !=
1099 val)
1100 status |=
1101 mt2063_setreg(state,
1102 MT2063_REG_DNC_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_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1106 if (state->reg[MT2063_REG_VGA_GAIN] !=
1107 val)
1108 status |=
1109 mt2063_setreg(state,
1110 MT2063_REG_VGA_GAIN,
1111 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001112
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001113 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1114 if (state->reg[MT2063_REG_RSVD_20] !=
1115 val)
1116 status |=
1117 mt2063_setreg(state,
1118 MT2063_REG_RSVD_20,
1119 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001120
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001121 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001122 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001123 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1124 if (state->reg[MT2063_REG_DNC_GAIN] !=
1125 val)
1126 status |=
1127 mt2063_setreg(state,
1128 MT2063_REG_DNC_GAIN,
1129 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001130
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001131 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1132 if (state->reg[MT2063_REG_VGA_GAIN] !=
1133 val)
1134 status |=
1135 mt2063_setreg(state,
1136 MT2063_REG_VGA_GAIN,
1137 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001138
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001139 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1140 if (state->reg[MT2063_REG_RSVD_20] !=
1141 val)
1142 status |=
1143 mt2063_setreg(state,
1144 MT2063_REG_RSVD_20,
1145 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001146
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001147 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001148 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001149 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1150 if (state->reg[MT2063_REG_DNC_GAIN] !=
1151 val)
1152 status |=
1153 mt2063_setreg(state,
1154 MT2063_REG_DNC_GAIN,
1155 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001156
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001157 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1158 if (state->reg[MT2063_REG_VGA_GAIN] !=
1159 val)
1160 status |=
1161 mt2063_setreg(state,
1162 MT2063_REG_VGA_GAIN,
1163 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001164
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001165 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1166 if (state->reg[MT2063_REG_RSVD_20] !=
1167 val)
1168 status |=
1169 mt2063_setreg(state,
1170 MT2063_REG_RSVD_20,
1171 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001172
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001173 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001174 default:
1175 break;
1176 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001177
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001178 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001179}
1180
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001181/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001182 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1183 * the selected enum mt2063_delivery_sys type.
1184 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001185 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1186 * DNC Output is selected, the other is always off)
1187 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001188 * @state: ptr to mt2063_state structure
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001189 * @Mode: desired reciever delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001190 *
1191 * Note: Register cache must be valid for it to work
1192 */
1193
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001194static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001195 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001196{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001197 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001198 u8 val;
1199 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001200
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001201 dprintk(2, "\n");
1202
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001203 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001204 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001205
1206 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001207 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001208 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001209 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001210 reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001211 ? 0x40 :
1212 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001213 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001214 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001215 }
1216
1217 /* LNARin */
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_CTRL_2C] & (u8) ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001220 (LNARIN[Mode] & 0x03);
1221 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001222 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001223 }
1224
1225 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001226 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001227 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001228 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001229 reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001230 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001231 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001232 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001233 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001234 /* trigger FIFF calibration, needed after changing FIFFQ */
1235 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001236 (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001237 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001238 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001239 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001240 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001241 reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001242 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001243 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001244 }
1245 }
1246
1247 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001248 status |= mt2063_get_dnc_output_enable(state, &longval);
1249 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001250
1251 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001252 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001253 u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001254 (ACLNAMAX[Mode] & 0x1F);
1255 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001256 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001257 }
1258
1259 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001260 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001261 u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001262 (LNATGT[Mode] & 0x3F);
1263 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001264 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001265 }
1266
1267 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001268 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001269 u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
1270 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001271 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001272 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001273 }
1274
1275 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001276 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001277 u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001278 (PD1TGT[Mode] & 0x3F);
1279 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001280 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001281 }
1282
1283 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001284 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001285 u8 val = ACFIFMAX[Mode];
1286 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1287 val = 5;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001288 val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001289 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001290 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001291 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001292 }
1293
1294 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001295 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001296 u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001297 (PD2TGT[Mode] & 0x3F);
1298 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001299 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001300 }
1301
1302 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001303 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001304 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
1305 (RFOVDIS[Mode] ? 0x80 : 0x00);
1306 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001307 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001308 }
1309
1310 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001311 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001312 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
1313 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1314 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001315 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001316 }
1317
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001318 if (status >= 0)
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001319 state->rcvr_mode = Mode;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001320
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001321 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001322}
1323
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001324/*
1325 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1326 * sections of the MT2063
1327 *
1328 * @Bits: Mask bits to be cleared.
1329 *
1330 * See definition of MT2063_Mask_Bits type for description
1331 * of each of the power bits.
1332 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001333static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1334 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001335{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001336 u32 status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001337
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001338 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001339 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1340 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001341 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001342 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001343 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001344 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001345 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001346 }
1347 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001348 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001349 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001350 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001351 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001352 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001353 }
1354
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001355 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001356}
1357
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001358/*
1359 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1360 * When Shutdown is 1, any section whose power
1361 * mask is set will be shutdown.
1362 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001363static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001364{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001365 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001366
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001367 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001368 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001369 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001370 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001371 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001372
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001373 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001374 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001375 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001376
1377 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001378 state->reg[MT2063_REG_BYP_CTRL] =
1379 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001380 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001381 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001382 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001383 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001384 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001385 state->reg[MT2063_REG_BYP_CTRL] =
1386 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001387 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001388 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001389 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001390 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001391 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001392 }
1393
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001394 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001395}
1396
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001397static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001398{
1399 return f_ref * (f_LO / f_ref)
1400 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1401}
1402
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001403/**
1404 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1405 * This function preserves maximum precision without
1406 * risk of overflow. It accurately calculates
1407 * f_ref * num / denom to within 1 HZ with fixed math.
1408 *
1409 * @num : Fractional portion of the multiplier
1410 * @denom: denominator portion of the ratio
1411 * @f_Ref: SRO frequency.
1412 *
1413 * This calculation handles f_ref as two separate 14-bit fields.
1414 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1415 * This is the genesis of the magic number "14" and the magic mask value of
1416 * 0x03FFF.
1417 *
1418 * This routine successfully handles denom values up to and including 2^18.
1419 * Returns: f_ref * num / denom
1420 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001421static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001422{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001423 u32 t1 = (f_ref >> 14) * num;
1424 u32 term1 = t1 / denom;
1425 u32 loss = t1 % denom;
1426 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001427 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001428 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001429}
1430
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001431/*
1432 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1433 * value for a FracN PLL.
1434 *
1435 * This function assumes that the f_LO and f_Ref are
1436 * evenly divisible by f_LO_Step.
1437 *
1438 * @Div: OUTPUT: Whole number portion of the multiplier
1439 * @FracN: OUTPUT: Fractional portion of the multiplier
1440 * @f_LO: desired LO frequency.
1441 * @f_LO_Step: Minimum step size for the LO (in Hz).
1442 * @f_Ref: SRO frequency.
1443 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1444 * of f_Ref (in Hz).
1445 *
1446 * Returns: Recalculated LO frequency.
1447 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001448static u32 MT2063_CalcLO1Mult(u32 *Div,
1449 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001450 u32 f_LO,
1451 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001452{
1453 /* Calculate the whole number portion of the divider */
1454 *Div = f_LO / f_Ref;
1455
1456 /* Calculate the numerator value (round to nearest f_LO_Step) */
1457 *FracN =
1458 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1459 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1460
1461 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1462}
1463
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001464/**
1465 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1466 * value for a FracN PLL.
1467 *
1468 * This function assumes that the f_LO and f_Ref are
1469 * evenly divisible by f_LO_Step.
1470 *
1471 * @Div: OUTPUT: Whole number portion of the multiplier
1472 * @FracN: OUTPUT: Fractional portion of the multiplier
1473 * @f_LO: desired LO frequency.
1474 * @f_LO_Step: Minimum step size for the LO (in Hz).
1475 * @f_Ref: SRO frequency.
1476 * @f_Avoid: Range of PLL frequencies to avoid near
1477 * integer multiples of f_Ref (in Hz).
1478 *
1479 * Returns: Recalculated LO frequency.
1480 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001481static u32 MT2063_CalcLO2Mult(u32 *Div,
1482 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001483 u32 f_LO,
1484 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001485{
1486 /* Calculate the whole number portion of the divider */
1487 *Div = f_LO / f_Ref;
1488
1489 /* Calculate the numerator value (round to nearest f_LO_Step) */
1490 *FracN =
1491 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1492 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1493
1494 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1495 8191);
1496}
1497
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001498/*
1499 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1500 * used for a given input frequency.
1501 *
1502 * @state: ptr to tuner data structure
1503 * @f_in: RF input center frequency (in Hz).
1504 *
1505 * Returns: ClearTune filter number (0-31)
1506 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001507static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001508{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001509 u32 RFBand;
1510 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001511
1512 /*
1513 ** Find RF Band setting
1514 */
1515 RFBand = 31; /* def when f_in > all */
1516 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001517 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001518 RFBand = idx;
1519 break;
1520 }
1521 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001522 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001523}
1524
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001525/*
1526 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1527 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001528static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001529{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001530
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001531 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001532 u32 LO1; /* 1st LO register value */
1533 u32 Num1; /* Numerator for LO1 reg. value */
1534 u32 f_IF1; /* 1st IF requested */
1535 u32 LO2; /* 2nd LO register value */
1536 u32 Num2; /* Numerator for LO2 reg. value */
1537 u32 ofLO1, ofLO2; /* last time's LO frequencies */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001538 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1539 u32 fiffof; /* Offset from FIFF center freq */
1540 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1541 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1542 u8 val;
1543 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001544
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001545 dprintk(2, "\n");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001546 /* Check the input and output frequency ranges */
1547 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001548 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001549
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001550 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1551 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001552 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001553
1554 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001555 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001556 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001557 ofLO1 = state->AS_Data.f_LO1;
Mauro Carvalho Chehabb5a91062011-07-22 17:07:17 -03001558 ofLO2 = state->AS_Data.f_LO2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001559
1560 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001561 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001562 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001563 if (state->ctfilt_sw == 1) {
1564 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1565 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001566 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001567 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001568 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001569 val = state->reg[MT2063_REG_CTUNE_OV];
1570 RFBand = FindClearTuneFilter(state, f_in);
1571 state->reg[MT2063_REG_CTUNE_OV] =
1572 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001573 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001574 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001575 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001576 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001577 }
1578 }
1579
1580 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001581 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001582 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001583 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001584 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001585 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001586 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001587 &state->reg[MT2063_REG_FIFFC], 1);
1588 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001589 }
1590 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001591 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001592 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001593 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001594 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001595 state->AS_Data.f_if1_Request =
1596 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1597 state->AS_Data.f_LO1_Step,
1598 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001599
1600 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001601 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1602 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001603 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001604 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001605
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001606 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001607
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001608 state->AS_Data.f_LO1 =
1609 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1610 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001611
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001612 state->AS_Data.f_LO2 =
1613 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1614 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001615
1616 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001617 * Check for any LO spurs in the output bandwidth and adjust
1618 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001619 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001620 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001621 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001622 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1623 * Recalculate the LO frequencies and the values to be placed
1624 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001625 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001626 state->AS_Data.f_LO1 =
1627 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1628 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1629 state->AS_Data.f_LO2 =
1630 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1631 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1632 state->AS_Data.f_LO2 =
1633 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1634 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001635
1636 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001637 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001638 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001639 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1640 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001641 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001642 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1643 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001644 status |= MT2063_DNC_RANGE;
1645 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001646 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001647 LO2LK = 0x40;
1648
1649 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001650 * If we have the same LO frequencies and we're already locked,
1651 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001652 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001653 if ((ofLO1 != state->AS_Data.f_LO1)
1654 || (ofLO2 != state->AS_Data.f_LO2)
1655 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001656 (LO1LK | LO2LK))) {
1657 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001658 * Calculate the FIFFOF register value
1659 *
1660 * IF1_Actual
1661 * FIFFOF = ------------ - 8 * FIFFC - 4992
1662 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001663 */
1664 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001665 (state->AS_Data.f_LO1 -
1666 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001667 4992;
1668 if (fiffof > 0xFF)
1669 fiffof = 0xFF;
1670
1671 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001672 * Place all of the calculated values into the local tuner
1673 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001674 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001675 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001676 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1677 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1678 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001679 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001680 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1681 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001682
1683 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001684 * Now write out the computed register values
1685 * IMPORTANT: There is a required order for writing
1686 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001687 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001688 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 -03001689 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001690 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001691 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001692 }
1693 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001694 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001695 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001696 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001697 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001698 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001699 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001700 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001701 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001702 reg[MT2063_REG_FIFF_OFFSET],
1703 1);
1704 }
1705 }
1706
1707 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001708 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001709 */
1710
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001711 if (status < 0)
1712 return status;
1713
1714 status = mt2063_lockStatus(state);
1715 if (status < 0)
1716 return status;
1717 if (!status)
1718 return -EINVAL; /* Couldn't lock */
1719
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001720 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001721 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001722 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001723 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001724 }
1725
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001726 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001727}
1728
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001729static const u8 MT2063B0_defaults[] = {
1730 /* Reg, Value */
1731 0x19, 0x05,
1732 0x1B, 0x1D,
1733 0x1C, 0x1F,
1734 0x1D, 0x0F,
1735 0x1E, 0x3F,
1736 0x1F, 0x0F,
1737 0x20, 0x3F,
1738 0x22, 0x21,
1739 0x23, 0x3F,
1740 0x24, 0x20,
1741 0x25, 0x3F,
1742 0x27, 0xEE,
1743 0x2C, 0x27, /* bit at 0x20 is cleared below */
1744 0x30, 0x03,
1745 0x2C, 0x07, /* bit at 0x20 is cleared here */
1746 0x2D, 0x87,
1747 0x2E, 0xAA,
1748 0x28, 0xE1, /* Set the FIFCrst bit here */
1749 0x28, 0xE0, /* Clear the FIFCrst bit here */
1750 0x00
1751};
1752
1753/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1754static const u8 MT2063B1_defaults[] = {
1755 /* Reg, Value */
1756 0x05, 0xF0,
1757 0x11, 0x10, /* New Enable AFCsd */
1758 0x19, 0x05,
1759 0x1A, 0x6C,
1760 0x1B, 0x24,
1761 0x1C, 0x28,
1762 0x1D, 0x8F,
1763 0x1E, 0x14,
1764 0x1F, 0x8F,
1765 0x20, 0x57,
1766 0x22, 0x21, /* New - ver 1.03 */
1767 0x23, 0x3C, /* New - ver 1.10 */
1768 0x24, 0x20, /* New - ver 1.03 */
1769 0x2C, 0x24, /* bit at 0x20 is cleared below */
1770 0x2D, 0x87, /* FIFFQ=0 */
1771 0x2F, 0xF3,
1772 0x30, 0x0C, /* New - ver 1.11 */
1773 0x31, 0x1B, /* New - ver 1.11 */
1774 0x2C, 0x04, /* bit at 0x20 is cleared here */
1775 0x28, 0xE1, /* Set the FIFCrst bit here */
1776 0x28, 0xE0, /* Clear the FIFCrst bit here */
1777 0x00
1778};
1779
1780/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1781static const u8 MT2063B3_defaults[] = {
1782 /* Reg, Value */
1783 0x05, 0xF0,
1784 0x19, 0x3D,
1785 0x2C, 0x24, /* bit at 0x20 is cleared below */
1786 0x2C, 0x04, /* bit at 0x20 is cleared here */
1787 0x28, 0xE1, /* Set the FIFCrst bit here */
1788 0x28, 0xE0, /* Clear the FIFCrst bit here */
1789 0x00
1790};
1791
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001792static int mt2063_init(struct dvb_frontend *fe)
1793{
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001794 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001795 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001796 u8 all_resets = 0xF0; /* reset/load bits */
1797 const u8 *def = NULL;
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001798 char *step;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001799 u32 FCRUN;
1800 s32 maxReads;
1801 u32 fcu_osc;
1802 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001803
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001804 dprintk(2, "\n");
1805
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001806 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001807
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001808 /* Read the Part/Rev code from the tuner */
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001809 status = mt2063_read(state, MT2063_REG_PART_REV,
1810 &state->reg[MT2063_REG_PART_REV], 1);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001811 if (status < 0) {
1812 printk(KERN_ERR "Can't read mt2063 part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001813 return status;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001814 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001815
1816 /* Check the part/rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001817 switch (state->reg[MT2063_REG_PART_REV]) {
1818 case MT2063_B0:
1819 step = "B0";
1820 break;
1821 case MT2063_B1:
1822 step = "B1";
1823 break;
1824 case MT2063_B2:
1825 step = "B2";
1826 break;
1827 case MT2063_B3:
1828 step = "B3";
1829 break;
1830 default:
1831 printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n",
1832 state->reg[MT2063_REG_PART_REV]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001833 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001834 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001835
1836 /* Check the 2nd byte of the Part/Rev code from the tuner */
1837 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1838 &state->reg[MT2063_REG_RSVD_3B], 1);
1839
1840 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001841 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001842 printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n",
1843 state->reg[MT2063_REG_PART_REV],
1844 state->reg[MT2063_REG_RSVD_3B]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001845 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001846 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001847
Mauro Carvalho Chehabd1244f72011-07-23 11:55:57 -03001848 printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step);
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001849
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001850 /* Reset the tuner */
1851 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1852 if (status < 0)
1853 return status;
1854
1855 /* change all of the default values that vary from the HW reset values */
1856 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1857 switch (state->reg[MT2063_REG_PART_REV]) {
1858 case MT2063_B3:
1859 def = MT2063B3_defaults;
1860 break;
1861
1862 case MT2063_B1:
1863 def = MT2063B1_defaults;
1864 break;
1865
1866 case MT2063_B0:
1867 def = MT2063B0_defaults;
1868 break;
1869
1870 default:
1871 return -ENODEV;
1872 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001873 }
1874
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001875 while (status >= 0 && *def) {
1876 u8 reg = *def++;
1877 u8 val = *def++;
1878 status = mt2063_write(state, reg, &val, 1);
1879 }
1880 if (status < 0)
1881 return status;
1882
1883 /* Wait for FIFF location to complete. */
1884 FCRUN = 1;
1885 maxReads = 10;
1886 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1887 msleep(2);
1888 status = mt2063_read(state,
1889 MT2063_REG_XO_STATUS,
1890 &state->
1891 reg[MT2063_REG_XO_STATUS], 1);
1892 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1893 }
1894
1895 if (FCRUN != 0 || status < 0)
1896 return -ENODEV;
1897
1898 status = mt2063_read(state,
1899 MT2063_REG_FIFFC,
1900 &state->reg[MT2063_REG_FIFFC], 1);
1901 if (status < 0)
1902 return status;
1903
1904 /* Read back all the registers from the tuner */
1905 status = mt2063_read(state,
1906 MT2063_REG_PART_REV,
1907 state->reg, MT2063_REG_END_REGS);
1908 if (status < 0)
1909 return status;
1910
1911 /* Initialize the tuner state. */
1912 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1913 state->AS_Data.f_ref = MT2063_REF_FREQ;
1914 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1915 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1916 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1917 state->AS_Data.f_out = 43750000UL;
1918 state->AS_Data.f_out_bw = 6750000UL;
1919 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1920 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1921 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1922 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1923 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1924 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1925 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1926 state->AS_Data.f_LO1 = 2181000000UL;
1927 state->AS_Data.f_LO2 = 1486249786UL;
1928 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1929 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1930 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1931 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1932 state->num_regs = MT2063_REG_END_REGS;
1933 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1934 state->ctfilt_sw = 0;
1935
1936 state->CTFiltMax[0] = 69230000;
1937 state->CTFiltMax[1] = 105770000;
1938 state->CTFiltMax[2] = 140350000;
1939 state->CTFiltMax[3] = 177110000;
1940 state->CTFiltMax[4] = 212860000;
1941 state->CTFiltMax[5] = 241130000;
1942 state->CTFiltMax[6] = 274370000;
1943 state->CTFiltMax[7] = 309820000;
1944 state->CTFiltMax[8] = 342450000;
1945 state->CTFiltMax[9] = 378870000;
1946 state->CTFiltMax[10] = 416210000;
1947 state->CTFiltMax[11] = 456500000;
1948 state->CTFiltMax[12] = 495790000;
1949 state->CTFiltMax[13] = 534530000;
1950 state->CTFiltMax[14] = 572610000;
1951 state->CTFiltMax[15] = 598970000;
1952 state->CTFiltMax[16] = 635910000;
1953 state->CTFiltMax[17] = 672130000;
1954 state->CTFiltMax[18] = 714840000;
1955 state->CTFiltMax[19] = 739660000;
1956 state->CTFiltMax[20] = 770410000;
1957 state->CTFiltMax[21] = 814660000;
1958 state->CTFiltMax[22] = 846950000;
1959 state->CTFiltMax[23] = 867820000;
1960 state->CTFiltMax[24] = 915980000;
1961 state->CTFiltMax[25] = 947450000;
1962 state->CTFiltMax[26] = 983110000;
1963 state->CTFiltMax[27] = 1021630000;
1964 state->CTFiltMax[28] = 1061870000;
1965 state->CTFiltMax[29] = 1098330000;
1966 state->CTFiltMax[30] = 1138990000;
1967
1968 /*
1969 ** Fetch the FCU osc value and use it and the fRef value to
1970 ** scale all of the Band Max values
1971 */
1972
1973 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1974 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1975 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1976 if (status < 0)
1977 return status;
1978
1979 /* Read the ClearTune filter calibration value */
1980 status = mt2063_read(state, MT2063_REG_FIFFC,
1981 &state->reg[MT2063_REG_FIFFC], 1);
1982 if (status < 0)
1983 return status;
1984
1985 fcu_osc = state->reg[MT2063_REG_FIFFC];
1986
1987 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1988 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1989 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1990 if (status < 0)
1991 return status;
1992
1993 /* Adjust each of the values in the ClearTune filter cross-over table */
1994 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001995 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001996
1997 status = MT2063_SoftwareShutdown(state, 1);
1998 if (status < 0)
1999 return status;
2000 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2001 if (status < 0)
2002 return status;
2003
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002004 state->init = true;
2005
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002006 return 0;
2007}
2008
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002009static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002010{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03002011 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002012 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002013
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002014 dprintk(2, "\n");
2015
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002016 if (!state->init)
2017 return -ENODEV;
2018
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002019 *tuner_status = 0;
2020 status = mt2063_lockStatus(state);
2021 if (status < 0)
2022 return status;
2023 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002024 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002025
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002026 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002027}
2028
2029static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002030{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002031 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002032
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002033 dprintk(2, "\n");
2034
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002035 fe->tuner_priv = NULL;
2036 kfree(state);
2037
2038 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002039}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002040
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002041static int mt2063_set_analog_params(struct dvb_frontend *fe,
2042 struct analog_parameters *params)
2043{
2044 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002045 s32 pict_car;
2046 s32 pict2chanb_vsb;
2047 s32 ch_bw;
2048 s32 if_mid;
2049 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002050 int status;
2051
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002052 dprintk(2, "\n");
2053
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002054 if (!state->init) {
2055 status = mt2063_init(fe);
2056 if (status < 0)
2057 return status;
2058 }
2059
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002060 switch (params->mode) {
2061 case V4L2_TUNER_RADIO:
2062 pict_car = 38900000;
2063 ch_bw = 8000000;
2064 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002065 rcvr_mode = MT2063_OFFAIR_ANALOG;
2066 break;
2067 case V4L2_TUNER_ANALOG_TV:
2068 rcvr_mode = MT2063_CABLE_ANALOG;
2069 if (params->std & ~V4L2_STD_MN) {
2070 pict_car = 38900000;
2071 ch_bw = 6000000;
2072 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002073 } else if (params->std & V4L2_STD_PAL_G) {
2074 pict_car = 38900000;
2075 ch_bw = 7000000;
2076 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002077 } else { /* PAL/SECAM standards */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002078 pict_car = 38900000;
2079 ch_bw = 8000000;
2080 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002081 }
2082 break;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002083 default:
2084 return -EINVAL;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002085 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002086 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2087
2088 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2089 state->AS_Data.f_out = if_mid;
2090 state->AS_Data.f_out_bw = ch_bw + 750000;
2091 status = MT2063_SetReceiverMode(state, rcvr_mode);
2092 if (status < 0)
2093 return status;
2094
2095 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2096 if (status < 0)
2097 return status;
2098
2099 state->frequency = params->frequency;
2100 return 0;
2101}
2102
2103/*
2104 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2105 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002106 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002107 * As such, the maximum symbol rate supported by 6 MHz is given by:
2108 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2109 */
2110#define MAX_SYMBOL_RATE_6MHz 5217391
2111
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002112static int mt2063_set_params(struct dvb_frontend *fe)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002113{
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002114 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002115 struct mt2063_state *state = fe->tuner_priv;
2116 int status;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002117 s32 pict_car;
2118 s32 pict2chanb_vsb;
2119 s32 ch_bw;
2120 s32 if_mid;
2121 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002122
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002123 if (!state->init) {
2124 status = mt2063_init(fe);
2125 if (status < 0)
2126 return status;
2127 }
2128
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002129 dprintk(2, "\n");
2130
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002131 if (c->bandwidth_hz == 0)
2132 return -EINVAL;
2133 if (c->bandwidth_hz <= 6000000)
2134 ch_bw = 6000000;
2135 else if (c->bandwidth_hz <= 7000000)
2136 ch_bw = 7000000;
2137 else
2138 ch_bw = 8000000;
2139
2140 switch (c->delivery_system) {
2141 case SYS_DVBT:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002142 rcvr_mode = MT2063_OFFAIR_COFDM;
2143 pict_car = 36125000;
2144 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002145 break;
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002146 case SYS_DVBC_ANNEX_A:
2147 case SYS_DVBC_ANNEX_C:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002148 rcvr_mode = MT2063_CABLE_QAM;
2149 pict_car = 36125000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002150 pict2chanb_vsb = -(ch_bw / 2);
2151 break;
2152 default:
2153 return -EINVAL;
2154 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002155 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2156
2157 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2158 state->AS_Data.f_out = if_mid;
2159 state->AS_Data.f_out_bw = ch_bw + 750000;
2160 status = MT2063_SetReceiverMode(state, rcvr_mode);
2161 if (status < 0)
2162 return status;
2163
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002164 status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2))));
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002165
2166 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002167 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002168
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002169 state->frequency = c->frequency;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002170 return 0;
2171}
2172
2173static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2174{
2175 struct mt2063_state *state = fe->tuner_priv;
2176
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002177 dprintk(2, "\n");
2178
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002179 if (!state->init)
2180 return -ENODEV;
2181
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002182 *freq = state->frequency;
2183 return 0;
2184}
2185
2186static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2187{
2188 struct mt2063_state *state = fe->tuner_priv;
2189
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002190 dprintk(2, "\n");
2191
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002192 if (!state->init)
2193 return -ENODEV;
2194
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002195 *bw = state->AS_Data.f_out_bw - 750000;
2196 return 0;
2197}
2198
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002199static struct dvb_tuner_ops mt2063_ops = {
2200 .info = {
2201 .name = "MT2063 Silicon Tuner",
2202 .frequency_min = 45000000,
2203 .frequency_max = 850000000,
2204 .frequency_step = 0,
2205 },
2206
2207 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002208 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002209 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002210 .set_analog_params = mt2063_set_analog_params,
2211 .set_params = mt2063_set_params,
2212 .get_frequency = mt2063_get_frequency,
2213 .get_bandwidth = mt2063_get_bandwidth,
2214 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002215};
2216
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002217struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2218 struct mt2063_config *config,
2219 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002220{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002221 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002222
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002223 dprintk(2, "\n");
2224
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002225 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002226 if (state == NULL)
2227 goto error;
2228
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002229 state->config = config;
2230 state->i2c = i2c;
2231 state->frontend = fe;
2232 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002233 fe->tuner_priv = state;
2234 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002235
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002236 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002237 return fe;
2238
2239error:
2240 kfree(state);
2241 return NULL;
2242}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002243EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002244
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002245/*
2246 * Ancillary routines visible outside mt2063
2247 * FIXME: Remove them in favor of using standard tuner callbacks
2248 */
2249unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2250{
2251 struct mt2063_state *state = fe->tuner_priv;
2252 int err = 0;
2253
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002254 dprintk(2, "\n");
2255
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002256 err = MT2063_SoftwareShutdown(state, 1);
2257 if (err < 0)
2258 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2259
2260 return err;
2261}
2262EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2263
2264unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2265{
2266 struct mt2063_state *state = fe->tuner_priv;
2267 int err = 0;
2268
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002269 dprintk(2, "\n");
2270
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002271 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2272 if (err < 0)
2273 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2274
2275 return err;
2276}
2277EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2278
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002279MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002280MODULE_DESCRIPTION("MT2063 Silicon tuner");
2281MODULE_LICENSE("GPL");