blob: db347d9e7d4577373d34303f9e7c5de95a0e9921 [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 Chehabdb6587b2011-07-22 16:54:05 -0300308 dprintk(2, "\n");
309
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 int ret;
315 u8 b0[] = { subAddress + i };
316 struct i2c_msg msg[] = {
317 {
318 .addr = state->config->tuner_address,
319 .flags = I2C_M_RD,
320 .buf = b0,
321 .len = 1
322 }, {
323 .addr = state->config->tuner_address,
324 .flags = I2C_M_RD,
325 .buf = pData + 1,
326 .len = 1
327 }
328 };
329
330 ret = i2c_transfer(state->i2c, msg, 2);
331 if (ret < 0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300332 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300333 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300334 if (fe->ops.i2c_gate_ctrl)
335 fe->ops.i2c_gate_ctrl(fe, 0);
336
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300337 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300338}
339
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300340/*
341 * FIXME: Is this really needed?
342 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300343static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300344{
345 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300346 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300347 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300348 msleep(10);
349
350 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300351}
352
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300353/*
354 * Microtune spur avoidance
355 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300356
357/* Implement ceiling, floor functions. */
358#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300359#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300360
361struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300362 s32 min_;
363 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300364};
365
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300366static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
367 *pAS_Info,
368 struct MT2063_ExclZone_t *pPrevNode)
369{
370 struct MT2063_ExclZone_t *pNode;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300371
372 dprintk(2, "\n");
373
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300374 /* Check for a node in the free list */
375 if (pAS_Info->freeZones != NULL) {
376 /* Use one from the free list */
377 pNode = pAS_Info->freeZones;
378 pAS_Info->freeZones = pNode->next_;
379 } else {
380 /* Grab a node from the array */
381 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
382 }
383
384 if (pPrevNode != NULL) {
385 pNode->next_ = pPrevNode->next_;
386 pPrevNode->next_ = pNode;
387 } else { /* insert at the beginning of the list */
388
389 pNode->next_ = pAS_Info->usedZones;
390 pAS_Info->usedZones = pNode;
391 }
392
393 pAS_Info->nZones++;
394 return pNode;
395}
396
397static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
398 *pAS_Info,
399 struct MT2063_ExclZone_t *pPrevNode,
400 struct MT2063_ExclZone_t
401 *pNodeToRemove)
402{
403 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
404
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300405 dprintk(2, "\n");
406
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300407 /* Make previous node point to the subsequent node */
408 if (pPrevNode != NULL)
409 pPrevNode->next_ = pNext;
410
411 /* Add pNodeToRemove to the beginning of the freeZones */
412 pNodeToRemove->next_ = pAS_Info->freeZones;
413 pAS_Info->freeZones = pNodeToRemove;
414
415 /* Decrement node count */
416 pAS_Info->nZones--;
417
418 return pNext;
419}
420
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300421/*
422 * MT_AddExclZone()
423 *
424 * Add (and merge) an exclusion zone into the list.
425 * If the range (f_min, f_max) is totally outside the
426 * 1st IF BW, ignore the entry.
427 * If the range (f_min, f_max) is negative, ignore the entry.
428 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300429static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300430 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300431{
432 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
433 struct MT2063_ExclZone_t *pPrev = NULL;
434 struct MT2063_ExclZone_t *pNext = NULL;
435
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300436 dprintk(2, "\n");
437
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300438 /* Check to see if this overlaps the 1st IF filter */
439 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
440 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
441 && (f_min < f_max)) {
442 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300443 * 1 2 3 4 5 6
444 *
445 * New entry: |---| |--| |--| |-| |---| |--|
446 * or or or or or
447 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300448 */
449
450 /* Check for our place in the list */
451 while ((pNode != NULL) && (pNode->max_ < f_min)) {
452 pPrev = pNode;
453 pNode = pNode->next_;
454 }
455
456 if ((pNode != NULL) && (pNode->min_ < f_max)) {
457 /* Combine me with pNode */
458 if (f_min < pNode->min_)
459 pNode->min_ = f_min;
460 if (f_max > pNode->max_)
461 pNode->max_ = f_max;
462 } else {
463 pNode = InsertNode(pAS_Info, pPrev);
464 pNode->min_ = f_min;
465 pNode->max_ = f_max;
466 }
467
468 /* Look for merging possibilities */
469 pNext = pNode->next_;
470 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
471 if (pNext->max_ > pNode->max_)
472 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300473 /* Remove pNext, return ptr to pNext->next */
474 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300475 }
476 }
477}
478
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300479/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300480 * Reset all exclusion zones.
481 * Add zones to protect the PLL FracN regions near zero
482 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300483static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
484{
485 u32 center;
486
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300487 dprintk(2, "\n");
488
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300489 pAS_Info->nZones = 0; /* this clears the used list */
490 pAS_Info->usedZones = NULL; /* reset ptr */
491 pAS_Info->freeZones = NULL; /* reset ptr */
492
493 center =
494 pAS_Info->f_ref *
495 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
496 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
497 while (center <
498 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
499 pAS_Info->f_LO1_FracN_Avoid) {
500 /* Exclude LO1 FracN */
501 MT2063_AddExclZone(pAS_Info,
502 center - pAS_Info->f_LO1_FracN_Avoid,
503 center - 1);
504 MT2063_AddExclZone(pAS_Info, center + 1,
505 center + pAS_Info->f_LO1_FracN_Avoid);
506 center += pAS_Info->f_ref;
507 }
508
509 center =
510 pAS_Info->f_ref *
511 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
512 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
513 while (center <
514 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
515 pAS_Info->f_LO2_FracN_Avoid) {
516 /* Exclude LO2 FracN */
517 MT2063_AddExclZone(pAS_Info,
518 center - pAS_Info->f_LO2_FracN_Avoid,
519 center - 1);
520 MT2063_AddExclZone(pAS_Info, center + 1,
521 center + pAS_Info->f_LO2_FracN_Avoid);
522 center += pAS_Info->f_ref;
523 }
524
525 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
526 /* Exclude LO1 values that conflict with DECT channels */
527 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
528 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
529 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
530 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
531 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
532 }
533
534 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
535 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
536 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
537 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
538 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
539 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
540 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
541 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
542 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
543 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
544 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
545 }
546}
547
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300548/*
549 * MT_ChooseFirstIF - Choose the best available 1st IF
550 * If f_Desired is not excluded, choose that first.
551 * Otherwise, return the value closest to f_Center that is
552 * not excluded
553 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300554static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300555{
556 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300557 * Update "f_Desired" to be the nearest "combinational-multiple" of
558 * "f_LO1_Step".
559 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
560 * And F_LO1 is the arithmetic sum of f_in + f_Center.
561 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
562 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300563 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300564 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300565 pAS_Info->f_LO1_Step *
566 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
567 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
568 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300569 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300570 (pAS_Info->f_LO1_Step >
571 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
572 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300573 u32 f_Center;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300574 s32 i;
575 s32 j = 0;
576 u32 bDesiredExcluded = 0;
577 u32 bZeroExcluded = 0;
578 s32 tmpMin, tmpMax;
579 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300580 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
581 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
582
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300583 dprintk(2, "\n");
584
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300585 if (pAS_Info->nZones == 0)
586 return f_Desired;
587
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300588 /*
589 * f_Center needs to be an integer multiple of f_Step away
590 * from f_Desired
591 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300592 if (pAS_Info->f_if1_Center > f_Desired)
593 f_Center =
594 f_Desired +
595 f_Step *
596 ((pAS_Info->f_if1_Center - f_Desired +
597 f_Step / 2) / f_Step);
598 else
599 f_Center =
600 f_Desired -
601 f_Step *
602 ((f_Desired - pAS_Info->f_if1_Center +
603 f_Step / 2) / f_Step);
604
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300605 /*
606 * Take MT_ExclZones, center around f_Center and change the
607 * resolution to f_Step
608 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300609 while (pNode != NULL) {
610 /* floor function */
611 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300612 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300613
614 /* ceil function */
615 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300616 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300617
618 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
619 bDesiredExcluded = 1;
620
621 if ((tmpMin < 0) && (tmpMax > 0))
622 bZeroExcluded = 1;
623
624 /* See if this zone overlaps the previous */
625 if ((j > 0) && (tmpMin < zones[j - 1].max_))
626 zones[j - 1].max_ = tmpMax;
627 else {
628 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300629 zones[j].min_ = tmpMin;
630 zones[j].max_ = tmpMax;
631 j++;
632 }
633 pNode = pNode->next_;
634 }
635
636 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300637 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300638 */
639 if (bDesiredExcluded == 0)
640 return f_Desired;
641
642 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300643 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300644 */
645 if (bZeroExcluded == 0)
646 return f_Center;
647
648 /* Find the value closest to 0 (f_Center) */
649 bestDiff = zones[0].min_;
650 for (i = 0; i < j; i++) {
651 if (abs(zones[i].min_) < abs(bestDiff))
652 bestDiff = zones[i].min_;
653 if (abs(zones[i].max_) < abs(bestDiff))
654 bestDiff = zones[i].max_;
655 }
656
657 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300658 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300659
660 return f_Center + (bestDiff * f_Step);
661}
662
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300663/**
664 * gcd() - Uses Euclid's algorithm
665 *
666 * @u, @v: Unsigned values whose GCD is desired.
667 *
668 * Returns THE greatest common divisor of u and v, if either value is 0,
669 * the other value is returned as the result.
670 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300671static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300672{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300673 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300674
675 while (v != 0) {
676 r = u % v;
677 u = v;
678 v = r;
679 }
680
681 return u;
682}
683
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300684/**
685 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
686 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
687 *
688 * ma mb mc md
689 * <--+-+-+-------------------+-------------------+-+-+-->
690 * | ^ 0 ^ |
691 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
692 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
693 *
694 * Note that some equations are doubled to prevent round-off
695 * problems when calculating fIFBW/2
696 *
697 * @pAS_Info: Avoid Spurs information block
698 * @fm: If spur, amount f_IF1 has to move negative
699 * @fp: If spur, amount f_IF1 has to move positive
700 *
701 * Returns 1 if an LO spur would be present, otherwise 0.
702 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300703static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300704 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300705{
706 /*
707 ** Calculate LO frequency settings.
708 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300709 u32 n, n0;
710 const u32 f_LO1 = pAS_Info->f_LO1;
711 const u32 f_LO2 = pAS_Info->f_LO2;
712 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
713 const u32 c = d - pAS_Info->f_out_bw;
714 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300715 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300716 s32 f_nsLO1, f_nsLO2;
717 s32 f_Spur;
718 u32 ma, mb, mc, md, me, mf;
719 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300720
721 dprintk(2, "\n");
722
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300723 *fm = 0;
724
725 /*
726 ** For each edge (d, c & f), calculate a scale, based on the gcd
727 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
728 ** gcd-based scale factor or f_Scale.
729 */
730 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300731 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300732 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300733 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300734 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300735 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300736 hgfs = gf_Scale / 2;
737
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300738 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300739
740 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
741 for (n = n0; n <= pAS_Info->maxH1; ++n) {
742 md = (n * ((f_LO1 + hgds) / gd_Scale) -
743 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
744
745 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
746 if (md >= pAS_Info->maxH1)
747 break;
748
749 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
750 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
751
752 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
753 if (md == ma)
754 continue;
755
756 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
757 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
758 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300759 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
760 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300761 f_Spur =
762 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
763 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
764
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300765 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
766 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300767 return 1;
768 }
769
770 /* Location of Zero-IF-spur to be checked */
771 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
772 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
773 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
774 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
775 if (me != mf) {
776 f_nsLO1 = n * (f_LO1 / gf_Scale);
777 f_nsLO2 = me * (f_LO2 / gf_Scale);
778 f_Spur =
779 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
780 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
781
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300782 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
783 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300784 return 1;
785 }
786
787 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
788 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
789 if (ma != mb) {
790 f_nsLO1 = n * (f_LO1 / gc_Scale);
791 f_nsLO2 = ma * (f_LO2 / gc_Scale);
792 f_Spur =
793 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
794 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
795
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300796 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
797 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300798 return 1;
799 }
800 }
801
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300802 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300803 return 0;
804}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300805
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300806/*
807 * MT_AvoidSpurs() - Main entry point to avoid spurs.
808 * Checks for existing spurs in present LO1, LO2 freqs
809 * and if present, chooses spur-free LO1, LO2 combination
810 * that tunes the same input/output frequencies.
811 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300812static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300813{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300814 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300815 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300816 pAS_Info->bSpurAvoided = 0;
817 pAS_Info->nSpursFound = 0;
818
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300819 dprintk(2, "\n");
820
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300821 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300822 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300823
824 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300825 * Avoid LO Generated Spurs
826 *
827 * Make sure that have no LO-related spurs within the IF output
828 * bandwidth.
829 *
830 * If there is an LO spur in this band, start at the current IF1 frequency
831 * and work out until we find a spur-free frequency or run up against the
832 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
833 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300834 */
835 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
836 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300837 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
838 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
839 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
840 u32 delta_IF1;
841 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300842
843 /*
844 ** Spur was found, attempt to find a spur-free 1st IF
845 */
846 do {
847 pAS_Info->nSpursFound++;
848
849 /* Raise f_IF1_upper, if needed */
850 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
851
852 /* Choose next IF1 that is closest to f_IF1_CENTER */
853 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
854
855 if (new_IF1 > zfIF1) {
856 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
857 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
858 } else {
859 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
860 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
861 }
862 zfIF1 = new_IF1;
863
864 if (zfIF1 > pAS_Info->f_if1_Center)
865 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
866 else
867 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300868
869 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300870 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300871 * Continue while the new 1st IF is still within the 1st IF bandwidth
872 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300873 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300874 } 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 -0300875
876 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300877 * Use the LO-spur free values found. If the search went all
878 * the way to the 1st IF band edge and always found spurs, just
879 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300880 */
881 if (pAS_Info->bSpurPresent == 1) {
882 status |= MT2063_SPUR_PRESENT_ERR;
883 pAS_Info->f_LO1 = zfLO1;
884 pAS_Info->f_LO2 = zfLO2;
885 } else
886 pAS_Info->bSpurAvoided = 1;
887 }
888
889 status |=
890 ((pAS_Info->
891 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
892
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300893 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300894}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300895
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300896/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300897 * Constants used by the tuning algorithm
898 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300899#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
900#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
901#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
902#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
903#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
904#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
905#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
906#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
907#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
908#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
909#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
910#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
911#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
912#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
913#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
914#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
915#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
916#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
917
918/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300919 * Define the supported Part/Rev codes for the MT2063
920 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300921#define MT2063_B0 (0x9B)
922#define MT2063_B1 (0x9C)
923#define MT2063_B2 (0x9D)
924#define MT2063_B3 (0x9E)
925
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300926/**
927 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
928 *
929 * @state: struct mt2063_state pointer
930 *
931 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
932 */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300933static unsigned int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300934{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300935 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
936 const u32 nPollRate = 2; /* poll status bits every 2 ms */
937 const u32 nMaxLoops = nMaxWait / nPollRate;
938 const u8 LO1LK = 0x80;
939 u8 LO2LK = 0x08;
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300940 u32 status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300941 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300942
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300943 dprintk(2, "\n");
944
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300945 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300946 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300947 LO2LK = 0x40;
948
949 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300950 status = mt2063_read(state, MT2063_REG_LO_STATUS,
951 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300952
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300953 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300954 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300955
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300956 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300957 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300958 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300959 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300960 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300961 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300962
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300963 /*
964 * Got no lock or partial lock
965 */
966 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300967}
968
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300969/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300970 * Constants for setting receiver modes.
971 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
972 * (DNC1GC & DNC2GC are the values, which are used, when the specific
973 * DNC Output is selected, the other is always off)
974 *
975 * enum mt2063_delivery_sys
976 * -------------+----------------------------------------------
977 * Mode 0 : | MT2063_CABLE_QAM
978 * Mode 1 : | MT2063_CABLE_ANALOG
979 * Mode 2 : | MT2063_OFFAIR_COFDM
980 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
981 * Mode 4 : | MT2063_OFFAIR_ANALOG
982 * Mode 5 : | MT2063_OFFAIR_8VSB
983 * --------------+----------------------------------------------
984 *
985 * |<---------- Mode -------------->|
986 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
987 * ------------+-----+-----+-----+-----+-----+-----+
988 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
989 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
990 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
991 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
992 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
993 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
994 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
995 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
996 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
997 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
998 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
999 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
1000 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1001 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
1002 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
1003 */
1004
1005enum mt2063_delivery_sys {
1006 MT2063_CABLE_QAM = 0, /* Digital cable */
1007 MT2063_CABLE_ANALOG, /* Analog cable */
1008 MT2063_OFFAIR_COFDM, /* Digital offair */
1009 MT2063_OFFAIR_COFDM_SAWLESS, /* Digital offair without SAW */
1010 MT2063_OFFAIR_ANALOG, /* Analog offair */
1011 MT2063_OFFAIR_8VSB, /* Analog offair */
1012 MT2063_NUM_RCVR_MODES
1013};
1014
1015static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
1016static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
1017static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
1018static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
1019static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
1020static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
1021static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
1022static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
1023static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1024static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
1025static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
1026static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1027static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
1028static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
1029
1030/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001031 * mt2063_set_dnc_output_enable()
1032 */
1033static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001034 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001035{
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001036 dprintk(2, "\n");
1037
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001038 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1039 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1040 *pValue = MT2063_DNC_NONE;
1041 else
1042 *pValue = MT2063_DNC_2;
1043 } else { /* DNC1 is on */
1044 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1045 *pValue = MT2063_DNC_1;
1046 else
1047 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001048 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001049 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001050}
1051
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001052/*
1053 * mt2063_set_dnc_output_enable()
1054 */
1055static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001056 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001057{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001058 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001059 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001060
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001061 dprintk(2, "\n");
1062
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001063 /* selects, which DNC output is used */
1064 switch (nValue) {
1065 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001066 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1067 if (state->reg[MT2063_REG_DNC_GAIN] !=
1068 val)
1069 status |=
1070 mt2063_setreg(state,
1071 MT2063_REG_DNC_GAIN,
1072 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001073
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001074 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1075 if (state->reg[MT2063_REG_VGA_GAIN] !=
1076 val)
1077 status |=
1078 mt2063_setreg(state,
1079 MT2063_REG_VGA_GAIN,
1080 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001081
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001082 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1083 if (state->reg[MT2063_REG_RSVD_20] !=
1084 val)
1085 status |=
1086 mt2063_setreg(state,
1087 MT2063_REG_RSVD_20,
1088 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001089
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001090 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001091 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001092 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1093 if (state->reg[MT2063_REG_DNC_GAIN] !=
1094 val)
1095 status |=
1096 mt2063_setreg(state,
1097 MT2063_REG_DNC_GAIN,
1098 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001099
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001100 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1101 if (state->reg[MT2063_REG_VGA_GAIN] !=
1102 val)
1103 status |=
1104 mt2063_setreg(state,
1105 MT2063_REG_VGA_GAIN,
1106 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001107
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001108 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1109 if (state->reg[MT2063_REG_RSVD_20] !=
1110 val)
1111 status |=
1112 mt2063_setreg(state,
1113 MT2063_REG_RSVD_20,
1114 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001115
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001116 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001117 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001118 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1119 if (state->reg[MT2063_REG_DNC_GAIN] !=
1120 val)
1121 status |=
1122 mt2063_setreg(state,
1123 MT2063_REG_DNC_GAIN,
1124 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001125
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001126 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1127 if (state->reg[MT2063_REG_VGA_GAIN] !=
1128 val)
1129 status |=
1130 mt2063_setreg(state,
1131 MT2063_REG_VGA_GAIN,
1132 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001133
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001134 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1135 if (state->reg[MT2063_REG_RSVD_20] !=
1136 val)
1137 status |=
1138 mt2063_setreg(state,
1139 MT2063_REG_RSVD_20,
1140 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001141
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001142 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001143 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001144 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1145 if (state->reg[MT2063_REG_DNC_GAIN] !=
1146 val)
1147 status |=
1148 mt2063_setreg(state,
1149 MT2063_REG_DNC_GAIN,
1150 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001151
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001152 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1153 if (state->reg[MT2063_REG_VGA_GAIN] !=
1154 val)
1155 status |=
1156 mt2063_setreg(state,
1157 MT2063_REG_VGA_GAIN,
1158 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001159
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001160 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1161 if (state->reg[MT2063_REG_RSVD_20] !=
1162 val)
1163 status |=
1164 mt2063_setreg(state,
1165 MT2063_REG_RSVD_20,
1166 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001167
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001168 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001169 default:
1170 break;
1171 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001172
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001173 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001174}
1175
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001176/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001177 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1178 * the selected enum mt2063_delivery_sys type.
1179 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001180 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1181 * DNC Output is selected, the other is always off)
1182 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001183 * @state: ptr to mt2063_state structure
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001184 * @Mode: desired reciever delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001185 *
1186 * Note: Register cache must be valid for it to work
1187 */
1188
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001189static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001190 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001191{
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001192 u32 status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001193 u8 val;
1194 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001195
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001196 dprintk(2, "\n");
1197
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001198 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001199 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001200
1201 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001202 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001203 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001204 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001205 reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001206 ? 0x40 :
1207 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001208 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001209 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001210 }
1211
1212 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001213 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001214 u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001215 (LNARIN[Mode] & 0x03);
1216 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001217 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001218 }
1219
1220 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001221 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001222 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001223 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001224 reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001225 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001226 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001227 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001228 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001229 /* trigger FIFF calibration, needed after changing FIFFQ */
1230 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001231 (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
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_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001234 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001235 (state->
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001236 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 }
1240 }
1241
1242 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001243 status |= mt2063_get_dnc_output_enable(state, &longval);
1244 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001245
1246 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001247 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001248 u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001249 (ACLNAMAX[Mode] & 0x1F);
1250 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001251 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001252 }
1253
1254 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001255 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001256 u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001257 (LNATGT[Mode] & 0x3F);
1258 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001259 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001260 }
1261
1262 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001263 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001264 u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
1265 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001266 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001267 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001268 }
1269
1270 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001271 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001272 u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001273 (PD1TGT[Mode] & 0x3F);
1274 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001275 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001276 }
1277
1278 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001279 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001280 u8 val = ACFIFMAX[Mode];
1281 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1282 val = 5;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001283 val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001284 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001285 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001286 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001287 }
1288
1289 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001290 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001291 u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001292 (PD2TGT[Mode] & 0x3F);
1293 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001294 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001295 }
1296
1297 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001298 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001299 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
1300 (RFOVDIS[Mode] ? 0x80 : 0x00);
1301 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001302 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001303 }
1304
1305 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001306 if (status >= 0) {
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001307 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
1308 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1309 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001310 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001311 }
1312
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001313 if (status >= 0)
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001314 state->rcvr_mode = Mode;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001315
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001316 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001317}
1318
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001319/*
1320 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1321 * sections of the MT2063
1322 *
1323 * @Bits: Mask bits to be cleared.
1324 *
1325 * See definition of MT2063_Mask_Bits type for description
1326 * of each of the power bits.
1327 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001328static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1329 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001330{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001331 u32 status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001332
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001333 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001334 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1335 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001336 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001337 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001338 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001339 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001340 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001341 }
1342 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001343 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001344 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001345 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001346 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001347 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001348 }
1349
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001350 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001351}
1352
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001353/*
1354 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1355 * When Shutdown is 1, any section whose power
1356 * mask is set will be shutdown.
1357 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001358static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001359{
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001360 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001361
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001362 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001363 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001364 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001365 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001366 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001367
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001368 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001369 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001370 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001371
1372 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001373 state->reg[MT2063_REG_BYP_CTRL] =
1374 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001375 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001376 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001377 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001378 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001379 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001380 state->reg[MT2063_REG_BYP_CTRL] =
1381 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001382 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001383 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001384 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001385 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001386 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001387 }
1388
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001389 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001390}
1391
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001392static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001393{
1394 return f_ref * (f_LO / f_ref)
1395 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1396}
1397
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001398/**
1399 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1400 * This function preserves maximum precision without
1401 * risk of overflow. It accurately calculates
1402 * f_ref * num / denom to within 1 HZ with fixed math.
1403 *
1404 * @num : Fractional portion of the multiplier
1405 * @denom: denominator portion of the ratio
1406 * @f_Ref: SRO frequency.
1407 *
1408 * This calculation handles f_ref as two separate 14-bit fields.
1409 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1410 * This is the genesis of the magic number "14" and the magic mask value of
1411 * 0x03FFF.
1412 *
1413 * This routine successfully handles denom values up to and including 2^18.
1414 * Returns: f_ref * num / denom
1415 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001416static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001417{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001418 u32 t1 = (f_ref >> 14) * num;
1419 u32 term1 = t1 / denom;
1420 u32 loss = t1 % denom;
1421 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001422 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001423 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001424}
1425
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001426/*
1427 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1428 * value for a FracN PLL.
1429 *
1430 * This function assumes that the f_LO and f_Ref are
1431 * evenly divisible by f_LO_Step.
1432 *
1433 * @Div: OUTPUT: Whole number portion of the multiplier
1434 * @FracN: OUTPUT: Fractional portion of the multiplier
1435 * @f_LO: desired LO frequency.
1436 * @f_LO_Step: Minimum step size for the LO (in Hz).
1437 * @f_Ref: SRO frequency.
1438 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1439 * of f_Ref (in Hz).
1440 *
1441 * Returns: Recalculated LO frequency.
1442 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001443static u32 MT2063_CalcLO1Mult(u32 *Div,
1444 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001445 u32 f_LO,
1446 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001447{
1448 /* Calculate the whole number portion of the divider */
1449 *Div = f_LO / f_Ref;
1450
1451 /* Calculate the numerator value (round to nearest f_LO_Step) */
1452 *FracN =
1453 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1454 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1455
1456 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1457}
1458
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001459/**
1460 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1461 * value for a FracN PLL.
1462 *
1463 * This function assumes that the f_LO and f_Ref are
1464 * evenly divisible by f_LO_Step.
1465 *
1466 * @Div: OUTPUT: Whole number portion of the multiplier
1467 * @FracN: OUTPUT: Fractional portion of the multiplier
1468 * @f_LO: desired LO frequency.
1469 * @f_LO_Step: Minimum step size for the LO (in Hz).
1470 * @f_Ref: SRO frequency.
1471 * @f_Avoid: Range of PLL frequencies to avoid near
1472 * integer multiples of f_Ref (in Hz).
1473 *
1474 * Returns: Recalculated LO frequency.
1475 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001476static u32 MT2063_CalcLO2Mult(u32 *Div,
1477 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001478 u32 f_LO,
1479 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001480{
1481 /* Calculate the whole number portion of the divider */
1482 *Div = f_LO / f_Ref;
1483
1484 /* Calculate the numerator value (round to nearest f_LO_Step) */
1485 *FracN =
1486 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1487 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1488
1489 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1490 8191);
1491}
1492
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001493/*
1494 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1495 * used for a given input frequency.
1496 *
1497 * @state: ptr to tuner data structure
1498 * @f_in: RF input center frequency (in Hz).
1499 *
1500 * Returns: ClearTune filter number (0-31)
1501 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001502static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001503{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001504 u32 RFBand;
1505 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001506
1507 /*
1508 ** Find RF Band setting
1509 */
1510 RFBand = 31; /* def when f_in > all */
1511 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001512 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001513 RFBand = idx;
1514 break;
1515 }
1516 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001517 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001518}
1519
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001520/*
1521 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1522 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001523static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001524{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001525
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001526 u32 status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001527 u32 LO1; /* 1st LO register value */
1528 u32 Num1; /* Numerator for LO1 reg. value */
1529 u32 f_IF1; /* 1st IF requested */
1530 u32 LO2; /* 2nd LO register value */
1531 u32 Num2; /* Numerator for LO2 reg. value */
1532 u32 ofLO1, ofLO2; /* last time's LO frequencies */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001533 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1534 u32 fiffof; /* Offset from FIFF center freq */
1535 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1536 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1537 u8 val;
1538 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001539
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001540 dprintk(2, "\n");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001541 /* Check the input and output frequency ranges */
1542 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001543 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001544
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001545 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1546 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001547 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001548
1549 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001550 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001551 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001552 ofLO1 = state->AS_Data.f_LO1;
Mauro Carvalho Chehabb5a91062011-07-22 17:07:17 -03001553 ofLO2 = state->AS_Data.f_LO2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001554
1555 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001556 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001557 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001558 if (state->ctfilt_sw == 1) {
1559 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1560 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001561 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001562 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001563 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001564 val = state->reg[MT2063_REG_CTUNE_OV];
1565 RFBand = FindClearTuneFilter(state, f_in);
1566 state->reg[MT2063_REG_CTUNE_OV] =
1567 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001568 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001569 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001570 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001571 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001572 }
1573 }
1574
1575 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001576 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001577 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001578 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001579 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001580 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001581 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001582 &state->reg[MT2063_REG_FIFFC], 1);
1583 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001584 }
1585 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001586 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001587 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001588 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001589 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001590 state->AS_Data.f_if1_Request =
1591 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1592 state->AS_Data.f_LO1_Step,
1593 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001594
1595 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001596 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1597 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001598 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001599 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001600
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001601 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001602
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001603 state->AS_Data.f_LO1 =
1604 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1605 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001606
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001607 state->AS_Data.f_LO2 =
1608 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1609 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001610
1611 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001612 * Check for any LO spurs in the output bandwidth and adjust
1613 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001614 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001615 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001616 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001617 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1618 * Recalculate the LO frequencies and the values to be placed
1619 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001620 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001621 state->AS_Data.f_LO1 =
1622 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1623 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1624 state->AS_Data.f_LO2 =
1625 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1626 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1627 state->AS_Data.f_LO2 =
1628 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1629 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001630
1631 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001632 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001633 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001634 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1635 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001636 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001637 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1638 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001639 status |= MT2063_DNC_RANGE;
1640 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001641 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001642 LO2LK = 0x40;
1643
1644 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001645 * If we have the same LO frequencies and we're already locked,
1646 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001647 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001648 if ((ofLO1 != state->AS_Data.f_LO1)
1649 || (ofLO2 != state->AS_Data.f_LO2)
1650 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001651 (LO1LK | LO2LK))) {
1652 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001653 * Calculate the FIFFOF register value
1654 *
1655 * IF1_Actual
1656 * FIFFOF = ------------ - 8 * FIFFC - 4992
1657 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001658 */
1659 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001660 (state->AS_Data.f_LO1 -
1661 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001662 4992;
1663 if (fiffof > 0xFF)
1664 fiffof = 0xFF;
1665
1666 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001667 * Place all of the calculated values into the local tuner
1668 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001669 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001670 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001671 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1672 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1673 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001674 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001675 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1676 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001677
1678 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001679 * Now write out the computed register values
1680 * IMPORTANT: There is a required order for writing
1681 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001682 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001683 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 -03001684 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001685 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001686 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001687 }
1688 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001689 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001690 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001691 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001692 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001693 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001694 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001695 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001696 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001697 reg[MT2063_REG_FIFF_OFFSET],
1698 1);
1699 }
1700 }
1701
1702 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001703 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001704 */
1705
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001706 if (status < 0)
1707 return status;
1708
1709 status = mt2063_lockStatus(state);
1710 if (status < 0)
1711 return status;
1712 if (!status)
1713 return -EINVAL; /* Couldn't lock */
1714
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001715 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001716 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001717 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001718 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001719 }
1720
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001721 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001722}
1723
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001724static const u8 MT2063B0_defaults[] = {
1725 /* Reg, Value */
1726 0x19, 0x05,
1727 0x1B, 0x1D,
1728 0x1C, 0x1F,
1729 0x1D, 0x0F,
1730 0x1E, 0x3F,
1731 0x1F, 0x0F,
1732 0x20, 0x3F,
1733 0x22, 0x21,
1734 0x23, 0x3F,
1735 0x24, 0x20,
1736 0x25, 0x3F,
1737 0x27, 0xEE,
1738 0x2C, 0x27, /* bit at 0x20 is cleared below */
1739 0x30, 0x03,
1740 0x2C, 0x07, /* bit at 0x20 is cleared here */
1741 0x2D, 0x87,
1742 0x2E, 0xAA,
1743 0x28, 0xE1, /* Set the FIFCrst bit here */
1744 0x28, 0xE0, /* Clear the FIFCrst bit here */
1745 0x00
1746};
1747
1748/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1749static const u8 MT2063B1_defaults[] = {
1750 /* Reg, Value */
1751 0x05, 0xF0,
1752 0x11, 0x10, /* New Enable AFCsd */
1753 0x19, 0x05,
1754 0x1A, 0x6C,
1755 0x1B, 0x24,
1756 0x1C, 0x28,
1757 0x1D, 0x8F,
1758 0x1E, 0x14,
1759 0x1F, 0x8F,
1760 0x20, 0x57,
1761 0x22, 0x21, /* New - ver 1.03 */
1762 0x23, 0x3C, /* New - ver 1.10 */
1763 0x24, 0x20, /* New - ver 1.03 */
1764 0x2C, 0x24, /* bit at 0x20 is cleared below */
1765 0x2D, 0x87, /* FIFFQ=0 */
1766 0x2F, 0xF3,
1767 0x30, 0x0C, /* New - ver 1.11 */
1768 0x31, 0x1B, /* New - ver 1.11 */
1769 0x2C, 0x04, /* bit at 0x20 is cleared here */
1770 0x28, 0xE1, /* Set the FIFCrst bit here */
1771 0x28, 0xE0, /* Clear the FIFCrst bit here */
1772 0x00
1773};
1774
1775/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1776static const u8 MT2063B3_defaults[] = {
1777 /* Reg, Value */
1778 0x05, 0xF0,
1779 0x19, 0x3D,
1780 0x2C, 0x24, /* bit at 0x20 is cleared below */
1781 0x2C, 0x04, /* bit at 0x20 is cleared here */
1782 0x28, 0xE1, /* Set the FIFCrst bit here */
1783 0x28, 0xE0, /* Clear the FIFCrst bit here */
1784 0x00
1785};
1786
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001787static int mt2063_init(struct dvb_frontend *fe)
1788{
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001789 u32 status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001790 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001791 u8 all_resets = 0xF0; /* reset/load bits */
1792 const u8 *def = NULL;
1793 u32 FCRUN;
1794 s32 maxReads;
1795 u32 fcu_osc;
1796 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001797
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001798 dprintk(2, "\n");
1799
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001800 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001801
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001802 /* Read the Part/Rev code from the tuner */
1803 status = mt2063_read(state, MT2063_REG_PART_REV, state->reg, 1);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001804 if (status < 0) {
1805 printk(KERN_ERR "Can't read mt2063 part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001806 return status;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001807 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001808
1809 /* Check the part/rev code */
1810 if (((state->reg[MT2063_REG_PART_REV] != MT2063_B0) /* MT2063 B0 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001811 && (state->reg[MT2063_REG_PART_REV] != MT2063_B1) /* MT2063 B1 */
1812 && (state->reg[MT2063_REG_PART_REV] != MT2063_B3))) /* MT2063 B3 */
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001813 return -ENODEV; /* Wrong tuner Part/Rev code */
1814
1815 /* Check the 2nd byte of the Part/Rev code from the tuner */
1816 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1817 &state->reg[MT2063_REG_RSVD_3B], 1);
1818
1819 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001820 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
1821 printk(KERN_ERR "Can't read mt2063 2nd part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001822 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001823 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001824
1825 /* Reset the tuner */
1826 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1827 if (status < 0)
1828 return status;
1829
1830 /* change all of the default values that vary from the HW reset values */
1831 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1832 switch (state->reg[MT2063_REG_PART_REV]) {
1833 case MT2063_B3:
1834 def = MT2063B3_defaults;
1835 break;
1836
1837 case MT2063_B1:
1838 def = MT2063B1_defaults;
1839 break;
1840
1841 case MT2063_B0:
1842 def = MT2063B0_defaults;
1843 break;
1844
1845 default:
1846 return -ENODEV;
1847 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001848 }
1849
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001850 while (status >= 0 && *def) {
1851 u8 reg = *def++;
1852 u8 val = *def++;
1853 status = mt2063_write(state, reg, &val, 1);
1854 }
1855 if (status < 0)
1856 return status;
1857
1858 /* Wait for FIFF location to complete. */
1859 FCRUN = 1;
1860 maxReads = 10;
1861 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1862 msleep(2);
1863 status = mt2063_read(state,
1864 MT2063_REG_XO_STATUS,
1865 &state->
1866 reg[MT2063_REG_XO_STATUS], 1);
1867 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1868 }
1869
1870 if (FCRUN != 0 || status < 0)
1871 return -ENODEV;
1872
1873 status = mt2063_read(state,
1874 MT2063_REG_FIFFC,
1875 &state->reg[MT2063_REG_FIFFC], 1);
1876 if (status < 0)
1877 return status;
1878
1879 /* Read back all the registers from the tuner */
1880 status = mt2063_read(state,
1881 MT2063_REG_PART_REV,
1882 state->reg, MT2063_REG_END_REGS);
1883 if (status < 0)
1884 return status;
1885
1886 /* Initialize the tuner state. */
1887 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1888 state->AS_Data.f_ref = MT2063_REF_FREQ;
1889 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1890 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1891 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1892 state->AS_Data.f_out = 43750000UL;
1893 state->AS_Data.f_out_bw = 6750000UL;
1894 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1895 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1896 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1897 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1898 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1899 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1900 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1901 state->AS_Data.f_LO1 = 2181000000UL;
1902 state->AS_Data.f_LO2 = 1486249786UL;
1903 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1904 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1905 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1906 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1907 state->num_regs = MT2063_REG_END_REGS;
1908 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1909 state->ctfilt_sw = 0;
1910
1911 state->CTFiltMax[0] = 69230000;
1912 state->CTFiltMax[1] = 105770000;
1913 state->CTFiltMax[2] = 140350000;
1914 state->CTFiltMax[3] = 177110000;
1915 state->CTFiltMax[4] = 212860000;
1916 state->CTFiltMax[5] = 241130000;
1917 state->CTFiltMax[6] = 274370000;
1918 state->CTFiltMax[7] = 309820000;
1919 state->CTFiltMax[8] = 342450000;
1920 state->CTFiltMax[9] = 378870000;
1921 state->CTFiltMax[10] = 416210000;
1922 state->CTFiltMax[11] = 456500000;
1923 state->CTFiltMax[12] = 495790000;
1924 state->CTFiltMax[13] = 534530000;
1925 state->CTFiltMax[14] = 572610000;
1926 state->CTFiltMax[15] = 598970000;
1927 state->CTFiltMax[16] = 635910000;
1928 state->CTFiltMax[17] = 672130000;
1929 state->CTFiltMax[18] = 714840000;
1930 state->CTFiltMax[19] = 739660000;
1931 state->CTFiltMax[20] = 770410000;
1932 state->CTFiltMax[21] = 814660000;
1933 state->CTFiltMax[22] = 846950000;
1934 state->CTFiltMax[23] = 867820000;
1935 state->CTFiltMax[24] = 915980000;
1936 state->CTFiltMax[25] = 947450000;
1937 state->CTFiltMax[26] = 983110000;
1938 state->CTFiltMax[27] = 1021630000;
1939 state->CTFiltMax[28] = 1061870000;
1940 state->CTFiltMax[29] = 1098330000;
1941 state->CTFiltMax[30] = 1138990000;
1942
1943 /*
1944 ** Fetch the FCU osc value and use it and the fRef value to
1945 ** scale all of the Band Max values
1946 */
1947
1948 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1949 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1950 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1951 if (status < 0)
1952 return status;
1953
1954 /* Read the ClearTune filter calibration value */
1955 status = mt2063_read(state, MT2063_REG_FIFFC,
1956 &state->reg[MT2063_REG_FIFFC], 1);
1957 if (status < 0)
1958 return status;
1959
1960 fcu_osc = state->reg[MT2063_REG_FIFFC];
1961
1962 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1963 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1964 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1965 if (status < 0)
1966 return status;
1967
1968 /* Adjust each of the values in the ClearTune filter cross-over table */
1969 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001970 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001971
1972 status = MT2063_SoftwareShutdown(state, 1);
1973 if (status < 0)
1974 return status;
1975 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1976 if (status < 0)
1977 return status;
1978
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03001979 state->init = true;
1980
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001981 return 0;
1982}
1983
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001984static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001985{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001986 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001987 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001988
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001989 dprintk(2, "\n");
1990
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03001991 if (!state->init)
1992 return -ENODEV;
1993
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03001994 *tuner_status = 0;
1995 status = mt2063_lockStatus(state);
1996 if (status < 0)
1997 return status;
1998 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001999 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002000
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002001 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002002}
2003
2004static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002005{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002006 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002007
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002008 dprintk(2, "\n");
2009
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002010 fe->tuner_priv = NULL;
2011 kfree(state);
2012
2013 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002014}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002015
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002016static int mt2063_set_analog_params(struct dvb_frontend *fe,
2017 struct analog_parameters *params)
2018{
2019 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002020 s32 pict_car;
2021 s32 pict2chanb_vsb;
2022 s32 ch_bw;
2023 s32 if_mid;
2024 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002025 int status;
2026
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002027 dprintk(2, "\n");
2028
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002029 if (!state->init) {
2030 status = mt2063_init(fe);
2031 if (status < 0)
2032 return status;
2033 }
2034
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002035 switch (params->mode) {
2036 case V4L2_TUNER_RADIO:
2037 pict_car = 38900000;
2038 ch_bw = 8000000;
2039 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002040 rcvr_mode = MT2063_OFFAIR_ANALOG;
2041 break;
2042 case V4L2_TUNER_ANALOG_TV:
2043 rcvr_mode = MT2063_CABLE_ANALOG;
2044 if (params->std & ~V4L2_STD_MN) {
2045 pict_car = 38900000;
2046 ch_bw = 6000000;
2047 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002048 } else if (params->std & V4L2_STD_PAL_G) {
2049 pict_car = 38900000;
2050 ch_bw = 7000000;
2051 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002052 } else { /* PAL/SECAM standards */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002053 pict_car = 38900000;
2054 ch_bw = 8000000;
2055 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002056 }
2057 break;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002058 default:
2059 return -EINVAL;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002060 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002061 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2062
2063 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2064 state->AS_Data.f_out = if_mid;
2065 state->AS_Data.f_out_bw = ch_bw + 750000;
2066 status = MT2063_SetReceiverMode(state, rcvr_mode);
2067 if (status < 0)
2068 return status;
2069
2070 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2071 if (status < 0)
2072 return status;
2073
2074 state->frequency = params->frequency;
2075 return 0;
2076}
2077
2078/*
2079 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2080 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002081 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002082 * As such, the maximum symbol rate supported by 6 MHz is given by:
2083 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2084 */
2085#define MAX_SYMBOL_RATE_6MHz 5217391
2086
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002087static int mt2063_set_params(struct dvb_frontend *fe)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002088{
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002089 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002090 struct mt2063_state *state = fe->tuner_priv;
2091 int status;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002092 s32 pict_car;
2093 s32 pict2chanb_vsb;
2094 s32 ch_bw;
2095 s32 if_mid;
2096 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002097
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002098 if (!state->init) {
2099 status = mt2063_init(fe);
2100 if (status < 0)
2101 return status;
2102 }
2103
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002104 dprintk(2, "\n");
2105
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002106 if (c->bandwidth_hz == 0)
2107 return -EINVAL;
2108 if (c->bandwidth_hz <= 6000000)
2109 ch_bw = 6000000;
2110 else if (c->bandwidth_hz <= 7000000)
2111 ch_bw = 7000000;
2112 else
2113 ch_bw = 8000000;
2114
2115 switch (c->delivery_system) {
2116 case SYS_DVBT:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002117 rcvr_mode = MT2063_OFFAIR_COFDM;
2118 pict_car = 36125000;
2119 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002120 break;
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002121 case SYS_DVBC_ANNEX_A:
2122 case SYS_DVBC_ANNEX_C:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002123 rcvr_mode = MT2063_CABLE_QAM;
2124 pict_car = 36125000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002125 pict2chanb_vsb = -(ch_bw / 2);
2126 break;
2127 default:
2128 return -EINVAL;
2129 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002130 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2131
2132 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2133 state->AS_Data.f_out = if_mid;
2134 state->AS_Data.f_out_bw = ch_bw + 750000;
2135 status = MT2063_SetReceiverMode(state, rcvr_mode);
2136 if (status < 0)
2137 return status;
2138
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002139 status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2))));
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002140
2141 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002142 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002143
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002144 state->frequency = c->frequency;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002145 return 0;
2146}
2147
2148static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2149{
2150 struct mt2063_state *state = fe->tuner_priv;
2151
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002152 dprintk(2, "\n");
2153
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002154 if (!state->init)
2155 return -ENODEV;
2156
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002157 *freq = state->frequency;
2158 return 0;
2159}
2160
2161static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2162{
2163 struct mt2063_state *state = fe->tuner_priv;
2164
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002165 dprintk(2, "\n");
2166
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002167 if (!state->init)
2168 return -ENODEV;
2169
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002170 *bw = state->AS_Data.f_out_bw - 750000;
2171 return 0;
2172}
2173
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002174static struct dvb_tuner_ops mt2063_ops = {
2175 .info = {
2176 .name = "MT2063 Silicon Tuner",
2177 .frequency_min = 45000000,
2178 .frequency_max = 850000000,
2179 .frequency_step = 0,
2180 },
2181
2182 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002183 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002184 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002185 .set_analog_params = mt2063_set_analog_params,
2186 .set_params = mt2063_set_params,
2187 .get_frequency = mt2063_get_frequency,
2188 .get_bandwidth = mt2063_get_bandwidth,
2189 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002190};
2191
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002192struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2193 struct mt2063_config *config,
2194 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002195{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002196 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002197
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002198 dprintk(2, "\n");
2199
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002200 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002201 if (state == NULL)
2202 goto error;
2203
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002204 state->config = config;
2205 state->i2c = i2c;
2206 state->frontend = fe;
2207 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002208 fe->tuner_priv = state;
2209 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002210
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002211 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002212 return fe;
2213
2214error:
2215 kfree(state);
2216 return NULL;
2217}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002218EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002219
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002220/*
2221 * Ancillary routines visible outside mt2063
2222 * FIXME: Remove them in favor of using standard tuner callbacks
2223 */
2224unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2225{
2226 struct mt2063_state *state = fe->tuner_priv;
2227 int err = 0;
2228
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002229 dprintk(2, "\n");
2230
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002231 err = MT2063_SoftwareShutdown(state, 1);
2232 if (err < 0)
2233 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2234
2235 return err;
2236}
2237EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2238
2239unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2240{
2241 struct mt2063_state *state = fe->tuner_priv;
2242 int err = 0;
2243
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002244 dprintk(2, "\n");
2245
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002246 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2247 if (err < 0)
2248 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2249
2250 return err;
2251}
2252EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2253
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002254MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002255MODULE_DESCRIPTION("MT2063 Silicon tuner");
2256MODULE_LICENSE("GPL");