blob: fdf605034ca6f08ddde967c72d3d7a9fef67b4d1 [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 Chehab29a0a4f2011-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 Chehab29a0a4f2011-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;
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001793 char *step;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001794 u32 FCRUN;
1795 s32 maxReads;
1796 u32 fcu_osc;
1797 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001798
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001799 dprintk(2, "\n");
1800
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001801 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001802
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001803 /* Read the Part/Rev code from the tuner */
1804 status = mt2063_read(state, MT2063_REG_PART_REV, state->reg, 1);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001805 if (status < 0) {
1806 printk(KERN_ERR "Can't read mt2063 part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001807 return status;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001808 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001809
1810 /* Check the part/rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001811 switch (state->reg[MT2063_REG_PART_REV]) {
1812 case MT2063_B0:
1813 step = "B0";
1814 break;
1815 case MT2063_B1:
1816 step = "B1";
1817 break;
1818 case MT2063_B2:
1819 step = "B2";
1820 break;
1821 case MT2063_B3:
1822 step = "B3";
1823 break;
1824 default:
1825 printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n",
1826 state->reg[MT2063_REG_PART_REV]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001827 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001828 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001829
1830 /* Check the 2nd byte of the Part/Rev code from the tuner */
1831 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1832 &state->reg[MT2063_REG_RSVD_3B], 1);
1833
1834 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001835 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001836 printk(KERN_ERR "mt2063: Unknown 2nd part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001837 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001838 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001839
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001840 dprintk(1, "Discovered a mt2063 %s (2nd part number 0x%02x)\n",
1841 step, state->reg[MT2063_REG_RSVD_3B]);
1842
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001843 /* Reset the tuner */
1844 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1845 if (status < 0)
1846 return status;
1847
1848 /* change all of the default values that vary from the HW reset values */
1849 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1850 switch (state->reg[MT2063_REG_PART_REV]) {
1851 case MT2063_B3:
1852 def = MT2063B3_defaults;
1853 break;
1854
1855 case MT2063_B1:
1856 def = MT2063B1_defaults;
1857 break;
1858
1859 case MT2063_B0:
1860 def = MT2063B0_defaults;
1861 break;
1862
1863 default:
1864 return -ENODEV;
1865 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001866 }
1867
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001868 while (status >= 0 && *def) {
1869 u8 reg = *def++;
1870 u8 val = *def++;
1871 status = mt2063_write(state, reg, &val, 1);
1872 }
1873 if (status < 0)
1874 return status;
1875
1876 /* Wait for FIFF location to complete. */
1877 FCRUN = 1;
1878 maxReads = 10;
1879 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1880 msleep(2);
1881 status = mt2063_read(state,
1882 MT2063_REG_XO_STATUS,
1883 &state->
1884 reg[MT2063_REG_XO_STATUS], 1);
1885 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1886 }
1887
1888 if (FCRUN != 0 || status < 0)
1889 return -ENODEV;
1890
1891 status = mt2063_read(state,
1892 MT2063_REG_FIFFC,
1893 &state->reg[MT2063_REG_FIFFC], 1);
1894 if (status < 0)
1895 return status;
1896
1897 /* Read back all the registers from the tuner */
1898 status = mt2063_read(state,
1899 MT2063_REG_PART_REV,
1900 state->reg, MT2063_REG_END_REGS);
1901 if (status < 0)
1902 return status;
1903
1904 /* Initialize the tuner state. */
1905 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1906 state->AS_Data.f_ref = MT2063_REF_FREQ;
1907 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1908 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1909 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1910 state->AS_Data.f_out = 43750000UL;
1911 state->AS_Data.f_out_bw = 6750000UL;
1912 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1913 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1914 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1915 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1916 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1917 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1918 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1919 state->AS_Data.f_LO1 = 2181000000UL;
1920 state->AS_Data.f_LO2 = 1486249786UL;
1921 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1922 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1923 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1924 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1925 state->num_regs = MT2063_REG_END_REGS;
1926 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1927 state->ctfilt_sw = 0;
1928
1929 state->CTFiltMax[0] = 69230000;
1930 state->CTFiltMax[1] = 105770000;
1931 state->CTFiltMax[2] = 140350000;
1932 state->CTFiltMax[3] = 177110000;
1933 state->CTFiltMax[4] = 212860000;
1934 state->CTFiltMax[5] = 241130000;
1935 state->CTFiltMax[6] = 274370000;
1936 state->CTFiltMax[7] = 309820000;
1937 state->CTFiltMax[8] = 342450000;
1938 state->CTFiltMax[9] = 378870000;
1939 state->CTFiltMax[10] = 416210000;
1940 state->CTFiltMax[11] = 456500000;
1941 state->CTFiltMax[12] = 495790000;
1942 state->CTFiltMax[13] = 534530000;
1943 state->CTFiltMax[14] = 572610000;
1944 state->CTFiltMax[15] = 598970000;
1945 state->CTFiltMax[16] = 635910000;
1946 state->CTFiltMax[17] = 672130000;
1947 state->CTFiltMax[18] = 714840000;
1948 state->CTFiltMax[19] = 739660000;
1949 state->CTFiltMax[20] = 770410000;
1950 state->CTFiltMax[21] = 814660000;
1951 state->CTFiltMax[22] = 846950000;
1952 state->CTFiltMax[23] = 867820000;
1953 state->CTFiltMax[24] = 915980000;
1954 state->CTFiltMax[25] = 947450000;
1955 state->CTFiltMax[26] = 983110000;
1956 state->CTFiltMax[27] = 1021630000;
1957 state->CTFiltMax[28] = 1061870000;
1958 state->CTFiltMax[29] = 1098330000;
1959 state->CTFiltMax[30] = 1138990000;
1960
1961 /*
1962 ** Fetch the FCU osc value and use it and the fRef value to
1963 ** scale all of the Band Max values
1964 */
1965
1966 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1967 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1968 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1969 if (status < 0)
1970 return status;
1971
1972 /* Read the ClearTune filter calibration value */
1973 status = mt2063_read(state, MT2063_REG_FIFFC,
1974 &state->reg[MT2063_REG_FIFFC], 1);
1975 if (status < 0)
1976 return status;
1977
1978 fcu_osc = state->reg[MT2063_REG_FIFFC];
1979
1980 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1981 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1982 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1983 if (status < 0)
1984 return status;
1985
1986 /* Adjust each of the values in the ClearTune filter cross-over table */
1987 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001988 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001989
1990 status = MT2063_SoftwareShutdown(state, 1);
1991 if (status < 0)
1992 return status;
1993 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
1994 if (status < 0)
1995 return status;
1996
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03001997 state->init = true;
1998
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001999 return 0;
2000}
2001
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002002static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002003{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03002004 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002005 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002006
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002007 dprintk(2, "\n");
2008
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002009 if (!state->init)
2010 return -ENODEV;
2011
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002012 *tuner_status = 0;
2013 status = mt2063_lockStatus(state);
2014 if (status < 0)
2015 return status;
2016 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002017 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002018
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002019 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002020}
2021
2022static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002023{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002024 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002025
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002026 dprintk(2, "\n");
2027
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002028 fe->tuner_priv = NULL;
2029 kfree(state);
2030
2031 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002032}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002033
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002034static int mt2063_set_analog_params(struct dvb_frontend *fe,
2035 struct analog_parameters *params)
2036{
2037 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002038 s32 pict_car;
2039 s32 pict2chanb_vsb;
2040 s32 ch_bw;
2041 s32 if_mid;
2042 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002043 int status;
2044
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002045 dprintk(2, "\n");
2046
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002047 if (!state->init) {
2048 status = mt2063_init(fe);
2049 if (status < 0)
2050 return status;
2051 }
2052
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002053 switch (params->mode) {
2054 case V4L2_TUNER_RADIO:
2055 pict_car = 38900000;
2056 ch_bw = 8000000;
2057 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002058 rcvr_mode = MT2063_OFFAIR_ANALOG;
2059 break;
2060 case V4L2_TUNER_ANALOG_TV:
2061 rcvr_mode = MT2063_CABLE_ANALOG;
2062 if (params->std & ~V4L2_STD_MN) {
2063 pict_car = 38900000;
2064 ch_bw = 6000000;
2065 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002066 } else if (params->std & V4L2_STD_PAL_G) {
2067 pict_car = 38900000;
2068 ch_bw = 7000000;
2069 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002070 } else { /* PAL/SECAM standards */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002071 pict_car = 38900000;
2072 ch_bw = 8000000;
2073 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002074 }
2075 break;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002076 default:
2077 return -EINVAL;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002078 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002079 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2080
2081 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2082 state->AS_Data.f_out = if_mid;
2083 state->AS_Data.f_out_bw = ch_bw + 750000;
2084 status = MT2063_SetReceiverMode(state, rcvr_mode);
2085 if (status < 0)
2086 return status;
2087
2088 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2089 if (status < 0)
2090 return status;
2091
2092 state->frequency = params->frequency;
2093 return 0;
2094}
2095
2096/*
2097 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
2098 * So, the amount of the needed bandwith is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002099 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002100 * As such, the maximum symbol rate supported by 6 MHz is given by:
2101 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2102 */
2103#define MAX_SYMBOL_RATE_6MHz 5217391
2104
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002105static int mt2063_set_params(struct dvb_frontend *fe)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002106{
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002107 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002108 struct mt2063_state *state = fe->tuner_priv;
2109 int status;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002110 s32 pict_car;
2111 s32 pict2chanb_vsb;
2112 s32 ch_bw;
2113 s32 if_mid;
2114 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002115
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002116 if (!state->init) {
2117 status = mt2063_init(fe);
2118 if (status < 0)
2119 return status;
2120 }
2121
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002122 dprintk(2, "\n");
2123
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002124 if (c->bandwidth_hz == 0)
2125 return -EINVAL;
2126 if (c->bandwidth_hz <= 6000000)
2127 ch_bw = 6000000;
2128 else if (c->bandwidth_hz <= 7000000)
2129 ch_bw = 7000000;
2130 else
2131 ch_bw = 8000000;
2132
2133 switch (c->delivery_system) {
2134 case SYS_DVBT:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002135 rcvr_mode = MT2063_OFFAIR_COFDM;
2136 pict_car = 36125000;
2137 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002138 break;
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002139 case SYS_DVBC_ANNEX_A:
2140 case SYS_DVBC_ANNEX_C:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002141 rcvr_mode = MT2063_CABLE_QAM;
2142 pict_car = 36125000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002143 pict2chanb_vsb = -(ch_bw / 2);
2144 break;
2145 default:
2146 return -EINVAL;
2147 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002148 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2149
2150 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2151 state->AS_Data.f_out = if_mid;
2152 state->AS_Data.f_out_bw = ch_bw + 750000;
2153 status = MT2063_SetReceiverMode(state, rcvr_mode);
2154 if (status < 0)
2155 return status;
2156
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002157 status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2))));
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002158
2159 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002160 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002161
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002162 state->frequency = c->frequency;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002163 return 0;
2164}
2165
2166static int mt2063_get_frequency(struct dvb_frontend *fe, u32 *freq)
2167{
2168 struct mt2063_state *state = fe->tuner_priv;
2169
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002170 dprintk(2, "\n");
2171
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002172 if (!state->init)
2173 return -ENODEV;
2174
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002175 *freq = state->frequency;
2176 return 0;
2177}
2178
2179static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2180{
2181 struct mt2063_state *state = fe->tuner_priv;
2182
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002183 dprintk(2, "\n");
2184
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002185 if (!state->init)
2186 return -ENODEV;
2187
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002188 *bw = state->AS_Data.f_out_bw - 750000;
2189 return 0;
2190}
2191
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002192static struct dvb_tuner_ops mt2063_ops = {
2193 .info = {
2194 .name = "MT2063 Silicon Tuner",
2195 .frequency_min = 45000000,
2196 .frequency_max = 850000000,
2197 .frequency_step = 0,
2198 },
2199
2200 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002201 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002202 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002203 .set_analog_params = mt2063_set_analog_params,
2204 .set_params = mt2063_set_params,
2205 .get_frequency = mt2063_get_frequency,
2206 .get_bandwidth = mt2063_get_bandwidth,
2207 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002208};
2209
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002210struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2211 struct mt2063_config *config,
2212 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002213{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002214 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002215
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002216 dprintk(2, "\n");
2217
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002218 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002219 if (state == NULL)
2220 goto error;
2221
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002222 state->config = config;
2223 state->i2c = i2c;
2224 state->frontend = fe;
2225 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002226 fe->tuner_priv = state;
2227 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002228
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002229 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002230 return fe;
2231
2232error:
2233 kfree(state);
2234 return NULL;
2235}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002236EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002237
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002238/*
2239 * Ancillary routines visible outside mt2063
2240 * FIXME: Remove them in favor of using standard tuner callbacks
2241 */
2242unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
2243{
2244 struct mt2063_state *state = fe->tuner_priv;
2245 int err = 0;
2246
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002247 dprintk(2, "\n");
2248
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002249 err = MT2063_SoftwareShutdown(state, 1);
2250 if (err < 0)
2251 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2252
2253 return err;
2254}
2255EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown);
2256
2257unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
2258{
2259 struct mt2063_state *state = fe->tuner_priv;
2260 int err = 0;
2261
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002262 dprintk(2, "\n");
2263
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002264 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2265 if (err < 0)
2266 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2267
2268 return err;
2269}
2270EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits);
2271
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002272MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002273MODULE_DESCRIPTION("MT2063 Silicon tuner");
2274MODULE_LICENSE("GPL");