blob: 6457ac91ef09567b8b3986d3c6bf3aa55771cfaa [file] [log] [blame]
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001/*
2 * Driver for mt2063 Micronas tuner
3 *
Mauro Carvalho Chehab37e59f82014-02-07 08:03:07 -02004 * Copyright (c) 2011 Mauro Carvalho Chehab
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03005 *
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -03006 * This driver came from a driver originally written by:
7 * Henry Wang <Henry.wang@AzureWave.com>
8 * Made publicly available by Terratec, at:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03009 * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
Mauro Carvalho Chehabd76f28f2011-07-21 17:36:20 -030010 * The original driver's license is GPL, as declared with MODULE_LICENSE()
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -030011 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation under version 2 of the License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
21
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -030022#include <linux/init.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/string.h>
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -030026#include <linux/videodev2.h>
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030027
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030028#include "mt2063.h"
29
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -030030static unsigned int debug;
31module_param(debug, int, 0644);
32MODULE_PARM_DESC(debug, "Set Verbosity level");
33
34#define dprintk(level, fmt, arg...) do { \
35if (debug >= level) \
36 printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \
37} while (0)
38
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -030039
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -030040/* positive error codes used internally */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030041
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -030042/* Info: Unavoidable LO-related spur may be present in the output */
Mauro Carvalho Chehab29a0a4fe2011-07-20 23:44:10 -030043#define MT2063_SPUR_PRESENT_ERR (0x00800000)
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030044
45/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */
46#define MT2063_SPUR_CNT_MASK (0x001f0000)
47#define MT2063_SPUR_SHIFT (16)
48
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030049/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */
50#define MT2063_UPC_RANGE (0x04000000)
51
52/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */
53#define MT2063_DNC_RANGE (0x08000000)
54
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030055/*
56 * Constant defining the version of the following structure
57 * and therefore the API for this code.
58 *
59 * When compiling the tuner driver, the preprocessor will
60 * check against this version number to make sure that
61 * it matches the version that the tuner driver knows about.
62 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030063
64/* DECT Frequency Avoidance */
65#define MT2063_DECT_AVOID_US_FREQS 0x00000001
66
67#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002
68
69#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0)
70
71#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0)
72
73enum MT2063_DECT_Avoid_Type {
74 MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */
75 MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */
76 MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */
77 MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */
78};
79
80#define MT2063_MAX_ZONES 48
81
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030082struct MT2063_ExclZone_t {
83 u32 min_;
84 u32 max_;
85 struct MT2063_ExclZone_t *next_;
86};
87
88/*
89 * Structure of data needed for Spur Avoidance
90 */
91struct MT2063_AvoidSpursData_t {
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -030092 u32 f_ref;
93 u32 f_in;
94 u32 f_LO1;
95 u32 f_if1_Center;
96 u32 f_if1_Request;
97 u32 f_if1_bw;
98 u32 f_LO2;
99 u32 f_out;
100 u32 f_out_bw;
101 u32 f_LO1_Step;
102 u32 f_LO2_Step;
103 u32 f_LO1_FracN_Avoid;
104 u32 f_LO2_FracN_Avoid;
105 u32 f_zif_bw;
106 u32 f_min_LO_Separation;
107 u32 maxH1;
108 u32 maxH2;
109 enum MT2063_DECT_Avoid_Type avoidDECT;
110 u32 bSpurPresent;
111 u32 bSpurAvoided;
112 u32 nSpursFound;
113 u32 nZones;
114 struct MT2063_ExclZone_t *freeZones;
115 struct MT2063_ExclZone_t *usedZones;
116 struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES];
117};
118
119/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300120 * Parameter for function MT2063_SetPowerMask that specifies the power down
121 * of various sections of the MT2063.
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300122 */
123enum MT2063_Mask_Bits {
124 MT2063_REG_SD = 0x0040, /* Shutdown regulator */
125 MT2063_SRO_SD = 0x0020, /* Shutdown SRO */
126 MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */
127 MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */
128 MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */
129 MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */
130 MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */
131 MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */
132 MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */
133 MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */
134 MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */
135 MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */
136 MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */
137 MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */
138 MT2063_NONE_SD = 0x0000 /* No shutdown bits */
139};
140
141/*
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300142 * Possible values for MT2063_DNC_OUTPUT
143 */
144enum MT2063_DNC_Output_Enable {
145 MT2063_DNC_NONE = 0,
146 MT2063_DNC_1,
147 MT2063_DNC_2,
148 MT2063_DNC_BOTH
149};
150
151/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300152 * Two-wire serial bus subaddresses of the tuner registers.
153 * Also known as the tuner's register addresses.
154 */
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300155enum MT2063_Register_Offsets {
156 MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */
157 MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */
158 MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */
159 MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */
160 MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */
161 MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */
162 MT2063_REG_RSVD_06, /* 0x06: Reserved */
163 MT2063_REG_LO_STATUS, /* 0x07: LO Status */
164 MT2063_REG_FIFFC, /* 0x08: FIFF Center */
165 MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */
166 MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */
167 MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */
168 MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */
169 MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */
170 MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */
171 MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */
172 MT2063_REG_RSVD_10, /* 0x10: Reserved */
173 MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */
174 MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */
175 MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */
176 MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */
177 MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */
178 MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */
179 MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */
180 MT2063_REG_RF_OV, /* 0x18: RF Attn Override */
181 MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */
182 MT2063_REG_LNA_TGT, /* 0x1A: Reserved */
183 MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */
184 MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */
185 MT2063_REG_RSVD_1D, /* 0x1D: Reserved */
186 MT2063_REG_RSVD_1E, /* 0x1E: Reserved */
187 MT2063_REG_RSVD_1F, /* 0x1F: Reserved */
188 MT2063_REG_RSVD_20, /* 0x20: Reserved */
189 MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */
190 MT2063_REG_RSVD_22, /* 0x22: Reserved */
191 MT2063_REG_RSVD_23, /* 0x23: Reserved */
192 MT2063_REG_RSVD_24, /* 0x24: Reserved */
193 MT2063_REG_RSVD_25, /* 0x25: Reserved */
194 MT2063_REG_RSVD_26, /* 0x26: Reserved */
195 MT2063_REG_RSVD_27, /* 0x27: Reserved */
196 MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */
197 MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */
198 MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */
199 MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */
200 MT2063_REG_CTRL_2C, /* 0x2C: Reserved */
201 MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */
202 MT2063_REG_RSVD_2E, /* 0x2E: Reserved */
203 MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */
204 MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */
205 MT2063_REG_RSVD_31, /* 0x31: Reserved */
206 MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */
207 MT2063_REG_RSVD_33, /* 0x33: Reserved */
208 MT2063_REG_RSVD_34, /* 0x34: Reserved */
209 MT2063_REG_RSVD_35, /* 0x35: Reserved */
210 MT2063_REG_RSVD_36, /* 0x36: Reserved */
211 MT2063_REG_RSVD_37, /* 0x37: Reserved */
212 MT2063_REG_RSVD_38, /* 0x38: Reserved */
213 MT2063_REG_RSVD_39, /* 0x39: Reserved */
214 MT2063_REG_RSVD_3A, /* 0x3A: Reserved */
215 MT2063_REG_RSVD_3B, /* 0x3B: Reserved */
216 MT2063_REG_RSVD_3C, /* 0x3C: Reserved */
217 MT2063_REG_END_REGS
218};
219
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300220struct mt2063_state {
221 struct i2c_adapter *i2c;
222
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -0300223 bool init;
224
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300225 const struct mt2063_config *config;
226 struct dvb_tuner_ops ops;
227 struct dvb_frontend *frontend;
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300228
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300229 u32 frequency;
230 u32 srate;
231 u32 bandwidth;
232 u32 reference;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300233
234 u32 tuner_id;
235 struct MT2063_AvoidSpursData_t AS_Data;
236 u32 f_IF1_actual;
237 u32 rcvr_mode;
238 u32 ctfilt_sw;
239 u32 CTFiltMax[31];
240 u32 num_regs;
241 u8 reg[MT2063_REG_END_REGS];
Mauro Carvalho Chehab6d3d7482011-07-20 22:21:26 -0300242};
Mauro Carvalho Chehab0ff48432011-07-20 20:21:42 -0300243
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300244/*
245 * mt2063_write - Write data into the I2C bus
246 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300247static int mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300248{
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300249 struct dvb_frontend *fe = state->frontend;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300250 int ret;
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300251 u8 buf[60];
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300252 struct i2c_msg msg = {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300253 .addr = state->config->tuner_address,
254 .flags = 0,
255 .buf = buf,
256 .len = len + 1
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300257 };
258
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300259 dprintk(2, "\n");
260
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300261 msg.buf[0] = reg;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300262 memcpy(msg.buf + 1, data, len);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300263
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300264 if (fe->ops.i2c_gate_ctrl)
265 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300266 ret = i2c_transfer(state->i2c, &msg, 1);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300267 if (fe->ops.i2c_gate_ctrl)
268 fe->ops.i2c_gate_ctrl(fe, 0);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300269
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300270 if (ret < 0)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300271 printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300272
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300273 return ret;
274}
275
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300276/*
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300277 * mt2063_write - Write register data into the I2C bus, caching the value
278 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300279static int mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300280{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300281 int status;
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300282
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300283 dprintk(2, "\n");
284
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300285 if (reg >= MT2063_REG_END_REGS)
286 return -ERANGE;
287
288 status = mt2063_write(state, reg, &val, 1);
289 if (status < 0)
290 return status;
291
292 state->reg[reg] = val;
293
294 return 0;
295}
296
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300297/*
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300298 * mt2063_read - Read data from the I2C bus
299 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300300static int mt2063_read(struct mt2063_state *state,
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300301 u8 subAddress, u8 *pData, u32 cnt)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300302{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300303 int status = 0; /* Status to be returned */
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -0300304 struct dvb_frontend *fe = state->frontend;
305 u32 i = 0;
306
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300307 dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300308
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -0300309 if (fe->ops.i2c_gate_ctrl)
310 fe->ops.i2c_gate_ctrl(fe, 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300311
312 for (i = 0; i < cnt; i++) {
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300313 u8 b0[] = { subAddress + i };
314 struct i2c_msg msg[] = {
315 {
316 .addr = state->config->tuner_address,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300317 .flags = 0,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300318 .buf = b0,
319 .len = 1
320 }, {
321 .addr = state->config->tuner_address,
322 .flags = I2C_M_RD,
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300323 .buf = pData + i,
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -0300324 .len = 1
325 }
326 };
327
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -0300328 status = i2c_transfer(state->i2c, msg, 2);
329 dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n",
330 subAddress + i, status, *(pData + i));
331 if (status < 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 Chehab36ae6df2011-07-23 09:48:08 -0300337 if (status < 0)
338 printk(KERN_ERR "Can't read from address 0x%02x,\n",
339 subAddress + i);
340
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300341 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300342}
343
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300344/*
345 * FIXME: Is this really needed?
346 */
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300347static int MT2063_Sleep(struct dvb_frontend *fe)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300348{
349 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300350 * ToDo: Add code here to implement a OS blocking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300351 */
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300352 msleep(100);
Mauro Carvalho Chehabf8676952011-07-20 22:00:30 -0300353
354 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300355}
356
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300357/*
358 * Microtune spur avoidance
359 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300360
361/* Implement ceiling, floor functions. */
362#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300363#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300364
365struct MT2063_FIFZone_t {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300366 s32 min_;
367 s32 max_;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300368};
369
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300370static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t
371 *pAS_Info,
372 struct MT2063_ExclZone_t *pPrevNode)
373{
374 struct MT2063_ExclZone_t *pNode;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300375
376 dprintk(2, "\n");
377
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300378 /* Check for a node in the free list */
379 if (pAS_Info->freeZones != NULL) {
380 /* Use one from the free list */
381 pNode = pAS_Info->freeZones;
382 pAS_Info->freeZones = pNode->next_;
383 } else {
384 /* Grab a node from the array */
385 pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
386 }
387
388 if (pPrevNode != NULL) {
389 pNode->next_ = pPrevNode->next_;
390 pPrevNode->next_ = pNode;
391 } else { /* insert at the beginning of the list */
392
393 pNode->next_ = pAS_Info->usedZones;
394 pAS_Info->usedZones = pNode;
395 }
396
397 pAS_Info->nZones++;
398 return pNode;
399}
400
401static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t
402 *pAS_Info,
403 struct MT2063_ExclZone_t *pPrevNode,
404 struct MT2063_ExclZone_t
405 *pNodeToRemove)
406{
407 struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_;
408
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300409 dprintk(2, "\n");
410
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300411 /* Make previous node point to the subsequent node */
412 if (pPrevNode != NULL)
413 pPrevNode->next_ = pNext;
414
415 /* Add pNodeToRemove to the beginning of the freeZones */
416 pNodeToRemove->next_ = pAS_Info->freeZones;
417 pAS_Info->freeZones = pNodeToRemove;
418
419 /* Decrement node count */
420 pAS_Info->nZones--;
421
422 return pNext;
423}
424
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300425/*
426 * MT_AddExclZone()
427 *
428 * Add (and merge) an exclusion zone into the list.
429 * If the range (f_min, f_max) is totally outside the
430 * 1st IF BW, ignore the entry.
431 * If the range (f_min, f_max) is negative, ignore the entry.
432 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300433static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300434 u32 f_min, u32 f_max)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300435{
436 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
437 struct MT2063_ExclZone_t *pPrev = NULL;
438 struct MT2063_ExclZone_t *pNext = NULL;
439
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300440 dprintk(2, "\n");
441
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300442 /* Check to see if this overlaps the 1st IF filter */
443 if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
444 && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
445 && (f_min < f_max)) {
446 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300447 * 1 2 3 4 5 6
448 *
449 * New entry: |---| |--| |--| |-| |---| |--|
450 * or or or or or
451 * Existing: |--| |--| |--| |---| |-| |--|
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300452 */
453
454 /* Check for our place in the list */
455 while ((pNode != NULL) && (pNode->max_ < f_min)) {
456 pPrev = pNode;
457 pNode = pNode->next_;
458 }
459
460 if ((pNode != NULL) && (pNode->min_ < f_max)) {
461 /* Combine me with pNode */
462 if (f_min < pNode->min_)
463 pNode->min_ = f_min;
464 if (f_max > pNode->max_)
465 pNode->max_ = f_max;
466 } else {
467 pNode = InsertNode(pAS_Info, pPrev);
468 pNode->min_ = f_min;
469 pNode->max_ = f_max;
470 }
471
472 /* Look for merging possibilities */
473 pNext = pNode->next_;
474 while ((pNext != NULL) && (pNext->min_ < pNode->max_)) {
475 if (pNext->max_ > pNode->max_)
476 pNode->max_ = pNext->max_;
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300477 /* Remove pNext, return ptr to pNext->next */
478 pNext = RemoveNode(pAS_Info, pNode, pNext);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300479 }
480 }
481}
482
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300483/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300484 * Reset all exclusion zones.
485 * Add zones to protect the PLL FracN regions near zero
486 */
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300487static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
488{
489 u32 center;
490
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300491 dprintk(2, "\n");
492
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -0300493 pAS_Info->nZones = 0; /* this clears the used list */
494 pAS_Info->usedZones = NULL; /* reset ptr */
495 pAS_Info->freeZones = NULL; /* reset ptr */
496
497 center =
498 pAS_Info->f_ref *
499 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
500 pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in;
501 while (center <
502 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
503 pAS_Info->f_LO1_FracN_Avoid) {
504 /* Exclude LO1 FracN */
505 MT2063_AddExclZone(pAS_Info,
506 center - pAS_Info->f_LO1_FracN_Avoid,
507 center - 1);
508 MT2063_AddExclZone(pAS_Info, center + 1,
509 center + pAS_Info->f_LO1_FracN_Avoid);
510 center += pAS_Info->f_ref;
511 }
512
513 center =
514 pAS_Info->f_ref *
515 ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
516 pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out;
517 while (center <
518 pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
519 pAS_Info->f_LO2_FracN_Avoid) {
520 /* Exclude LO2 FracN */
521 MT2063_AddExclZone(pAS_Info,
522 center - pAS_Info->f_LO2_FracN_Avoid,
523 center - 1);
524 MT2063_AddExclZone(pAS_Info, center + 1,
525 center + pAS_Info->f_LO2_FracN_Avoid);
526 center += pAS_Info->f_ref;
527 }
528
529 if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
530 /* Exclude LO1 values that conflict with DECT channels */
531 MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */
532 MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */
533 MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */
534 MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */
535 MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */
536 }
537
538 if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) {
539 MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */
540 MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */
541 MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */
542 MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */
543 MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */
544 MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */
545 MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */
546 MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */
547 MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */
548 MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */
549 }
550}
551
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300552/*
553 * MT_ChooseFirstIF - Choose the best available 1st IF
554 * If f_Desired is not excluded, choose that first.
555 * Otherwise, return the value closest to f_Center that is
556 * not excluded
557 */
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300558static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300559{
560 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300561 * Update "f_Desired" to be the nearest "combinational-multiple" of
562 * "f_LO1_Step".
563 * The resulting number, F_LO1 must be a multiple of f_LO1_Step.
564 * And F_LO1 is the arithmetic sum of f_in + f_Center.
565 * Neither f_in, nor f_Center must be a multiple of f_LO1_Step.
566 * However, the sum must be.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300567 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300568 const u32 f_Desired =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300569 pAS_Info->f_LO1_Step *
570 ((pAS_Info->f_if1_Request + pAS_Info->f_in +
571 pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
572 pAS_Info->f_in;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300573 const u32 f_Step =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300574 (pAS_Info->f_LO1_Step >
575 pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
576 f_LO2_Step;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300577 u32 f_Center;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300578 s32 i;
579 s32 j = 0;
580 u32 bDesiredExcluded = 0;
581 u32 bZeroExcluded = 0;
582 s32 tmpMin, tmpMax;
583 s32 bestDiff;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300584 struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones;
585 struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
586
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300587 dprintk(2, "\n");
588
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300589 if (pAS_Info->nZones == 0)
590 return f_Desired;
591
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300592 /*
593 * f_Center needs to be an integer multiple of f_Step away
594 * from f_Desired
595 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300596 if (pAS_Info->f_if1_Center > f_Desired)
597 f_Center =
598 f_Desired +
599 f_Step *
600 ((pAS_Info->f_if1_Center - f_Desired +
601 f_Step / 2) / f_Step);
602 else
603 f_Center =
604 f_Desired -
605 f_Step *
606 ((f_Desired - pAS_Info->f_if1_Center +
607 f_Step / 2) / f_Step);
608
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300609 /*
610 * Take MT_ExclZones, center around f_Center and change the
611 * resolution to f_Step
612 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300613 while (pNode != NULL) {
614 /* floor function */
615 tmpMin =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300616 floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300617
618 /* ceil function */
619 tmpMax =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300620 ceil((s32) (pNode->max_ - f_Center), (s32) f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300621
622 if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired))
623 bDesiredExcluded = 1;
624
625 if ((tmpMin < 0) && (tmpMax > 0))
626 bZeroExcluded = 1;
627
628 /* See if this zone overlaps the previous */
629 if ((j > 0) && (tmpMin < zones[j - 1].max_))
630 zones[j - 1].max_ = tmpMax;
631 else {
632 /* Add new zone */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300633 zones[j].min_ = tmpMin;
634 zones[j].max_ = tmpMax;
635 j++;
636 }
637 pNode = pNode->next_;
638 }
639
640 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300641 * If the desired is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300642 */
643 if (bDesiredExcluded == 0)
644 return f_Desired;
645
646 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300647 * If the desired is excluded and the center is okay, return with it
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300648 */
649 if (bZeroExcluded == 0)
650 return f_Center;
651
652 /* Find the value closest to 0 (f_Center) */
653 bestDiff = zones[0].min_;
654 for (i = 0; i < j; i++) {
655 if (abs(zones[i].min_) < abs(bestDiff))
656 bestDiff = zones[i].min_;
657 if (abs(zones[i].max_) < abs(bestDiff))
658 bestDiff = zones[i].max_;
659 }
660
661 if (bestDiff < 0)
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300662 return f_Center - ((u32) (-bestDiff) * f_Step);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300663
664 return f_Center + (bestDiff * f_Step);
665}
666
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300667/**
668 * gcd() - Uses Euclid's algorithm
669 *
670 * @u, @v: Unsigned values whose GCD is desired.
671 *
672 * Returns THE greatest common divisor of u and v, if either value is 0,
673 * the other value is returned as the result.
674 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300675static u32 MT2063_gcd(u32 u, u32 v)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300676{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300677 u32 r;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300678
679 while (v != 0) {
680 r = u % v;
681 u = v;
682 v = r;
683 }
684
685 return u;
686}
687
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300688/**
689 * IsSpurInBand() - Checks to see if a spur will be present within the IF's
690 * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
691 *
692 * ma mb mc md
693 * <--+-+-+-------------------+-------------------+-+-+-->
694 * | ^ 0 ^ |
695 * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^
696 * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2
697 *
698 * Note that some equations are doubled to prevent round-off
699 * problems when calculating fIFBW/2
700 *
701 * @pAS_Info: Avoid Spurs information block
702 * @fm: If spur, amount f_IF1 has to move negative
703 * @fp: If spur, amount f_IF1 has to move positive
704 *
705 * Returns 1 if an LO spur would be present, otherwise 0.
706 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300707static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300708 u32 *fm, u32 * fp)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300709{
710 /*
711 ** Calculate LO frequency settings.
712 */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300713 u32 n, n0;
714 const u32 f_LO1 = pAS_Info->f_LO1;
715 const u32 f_LO2 = pAS_Info->f_LO2;
716 const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2;
717 const u32 c = d - pAS_Info->f_out_bw;
718 const u32 f = pAS_Info->f_zif_bw / 2;
Mauro Carvalho Chehabd0dcc2d2011-07-21 02:30:19 -0300719 const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300720 s32 f_nsLO1, f_nsLO2;
721 s32 f_Spur;
722 u32 ma, mb, mc, md, me, mf;
723 u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300724
725 dprintk(2, "\n");
726
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300727 *fm = 0;
728
729 /*
730 ** For each edge (d, c & f), calculate a scale, based on the gcd
731 ** of f_LO1, f_LO2 and the edge value. Use the larger of this
732 ** gcd-based scale factor or f_Scale.
733 */
734 lo_gcd = MT2063_gcd(f_LO1, f_LO2);
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300735 gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300736 hgds = gd_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300737 gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300738 hgcs = gc_Scale / 2;
Mauro Carvalho Chehabfd1126c2011-07-21 03:30:57 -0300739 gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300740 hgfs = gf_Scale / 2;
741
Mauro Carvalho Chehabe930b3a2011-07-21 03:02:16 -0300742 n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300743
744 /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */
745 for (n = n0; n <= pAS_Info->maxH1; ++n) {
746 md = (n * ((f_LO1 + hgds) / gd_Scale) -
747 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
748
749 /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */
750 if (md >= pAS_Info->maxH1)
751 break;
752
753 ma = (n * ((f_LO1 + hgds) / gd_Scale) +
754 ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
755
756 /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */
757 if (md == ma)
758 continue;
759
760 mc = (n * ((f_LO1 + hgcs) / gc_Scale) -
761 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
762 if (mc != md) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300763 f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale));
764 f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale));
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300765 f_Spur =
766 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
767 n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale);
768
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300769 *fp = ((f_Spur - (s32) c) / (mc - n)) + 1;
770 *fm = (((s32) d - f_Spur) / (mc - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300771 return 1;
772 }
773
774 /* Location of Zero-IF-spur to be checked */
775 me = (n * ((f_LO1 + hgfs) / gf_Scale) +
776 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
777 mf = (n * ((f_LO1 + hgfs) / gf_Scale) -
778 ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale);
779 if (me != mf) {
780 f_nsLO1 = n * (f_LO1 / gf_Scale);
781 f_nsLO2 = me * (f_LO2 / gf_Scale);
782 f_Spur =
783 (gf_Scale * (f_nsLO1 - f_nsLO2)) +
784 n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale);
785
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300786 *fp = ((f_Spur + (s32) f) / (me - n)) + 1;
787 *fm = (((s32) f - f_Spur) / (me - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300788 return 1;
789 }
790
791 mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
792 ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale);
793 if (ma != mb) {
794 f_nsLO1 = n * (f_LO1 / gc_Scale);
795 f_nsLO2 = ma * (f_LO2 / gc_Scale);
796 f_Spur =
797 (gc_Scale * (f_nsLO1 - f_nsLO2)) +
798 n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
799
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300800 *fp = (((s32) d + f_Spur) / (ma - n)) + 1;
801 *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300802 return 1;
803 }
804 }
805
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300806 /* No spurs found */
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300807 return 0;
808}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300809
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300810/*
811 * MT_AvoidSpurs() - Main entry point to avoid spurs.
812 * Checks for existing spurs in present LO1, LO2 freqs
813 * and if present, chooses spur-free LO1, LO2 combination
814 * that tunes the same input/output frequencies.
815 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -0300816static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300817{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300818 int status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300819 u32 fm, fp; /* restricted range on LO's */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300820 pAS_Info->bSpurAvoided = 0;
821 pAS_Info->nSpursFound = 0;
822
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300823 dprintk(2, "\n");
824
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300825 if (pAS_Info->maxH1 == 0)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300826 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300827
828 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300829 * Avoid LO Generated Spurs
830 *
831 * Make sure that have no LO-related spurs within the IF output
832 * bandwidth.
833 *
834 * If there is an LO spur in this band, start at the current IF1 frequency
835 * and work out until we find a spur-free frequency or run up against the
836 * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they
837 * will be unchanged if a spur-free setting is not found.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300838 */
839 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
840 if (pAS_Info->bSpurPresent) {
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300841 u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
842 u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
843 u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
844 u32 delta_IF1;
845 u32 new_IF1;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300846
847 /*
848 ** Spur was found, attempt to find a spur-free 1st IF
849 */
850 do {
851 pAS_Info->nSpursFound++;
852
853 /* Raise f_IF1_upper, if needed */
854 MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp);
855
856 /* Choose next IF1 that is closest to f_IF1_CENTER */
857 new_IF1 = MT2063_ChooseFirstIF(pAS_Info);
858
859 if (new_IF1 > zfIF1) {
860 pAS_Info->f_LO1 += (new_IF1 - zfIF1);
861 pAS_Info->f_LO2 += (new_IF1 - zfIF1);
862 } else {
863 pAS_Info->f_LO1 -= (zfIF1 - new_IF1);
864 pAS_Info->f_LO2 -= (zfIF1 - new_IF1);
865 }
866 zfIF1 = new_IF1;
867
868 if (zfIF1 > pAS_Info->f_if1_Center)
869 delta_IF1 = zfIF1 - pAS_Info->f_if1_Center;
870 else
871 delta_IF1 = pAS_Info->f_if1_Center - zfIF1;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300872
873 pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300874 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300875 * Continue while the new 1st IF is still within the 1st IF bandwidth
876 * and there is a spur in the band (again)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300877 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300878 } 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 -0300879
880 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300881 * Use the LO-spur free values found. If the search went all
882 * the way to the 1st IF band edge and always found spurs, just
883 * leave the original choice. It's as "good" as any other.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300884 */
885 if (pAS_Info->bSpurPresent == 1) {
886 status |= MT2063_SPUR_PRESENT_ERR;
887 pAS_Info->f_LO1 = zfLO1;
888 pAS_Info->f_LO2 = zfLO2;
889 } else
890 pAS_Info->bSpurAvoided = 1;
891 }
892
893 status |=
894 ((pAS_Info->
895 nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
896
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300897 return status;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -0300898}
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300899
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300900/*
Mauro Carvalho Chehab66aea302011-07-21 03:57:10 -0300901 * Constants used by the tuning algorithm
902 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300903#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */
904#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */
905#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */
906#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */
907#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */
908#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */
909#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */
910#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */
911#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */
912#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */
913#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */
914#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */
915#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */
916#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */
917#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */
918#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */
919#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */
920#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
921
922/*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -0300923 * Define the supported Part/Rev codes for the MT2063
924 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300925#define MT2063_B0 (0x9B)
926#define MT2063_B1 (0x9C)
927#define MT2063_B2 (0x9D)
928#define MT2063_B3 (0x9E)
929
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300930/**
931 * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked
932 *
933 * @state: struct mt2063_state pointer
934 *
935 * This function returns 0, if no lock, 1 if locked and a value < 1 if error
936 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300937static int mt2063_lockStatus(struct mt2063_state *state)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300938{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300939 const u32 nMaxWait = 100; /* wait a maximum of 100 msec */
940 const u32 nPollRate = 2; /* poll status bits every 2 ms */
941 const u32 nMaxLoops = nMaxWait / nPollRate;
942 const u8 LO1LK = 0x80;
943 u8 LO2LK = 0x08;
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -0300944 int status;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -0300945 u32 nDelays = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300946
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -0300947 dprintk(2, "\n");
948
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300949 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300950 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300951 LO2LK = 0x40;
952
953 do {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300954 status = mt2063_read(state, MT2063_REG_LO_STATUS,
955 &state->reg[MT2063_REG_LO_STATUS], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300956
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -0300957 if (status < 0)
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300958 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300959
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -0300960 if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300961 (LO1LK | LO2LK)) {
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300962 return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300963 }
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -0300964 msleep(nPollRate); /* Wait between retries */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -0300965 } while (++nDelays < nMaxLoops);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300966
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -0300967 /*
968 * Got no lock or partial lock
969 */
970 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -0300971}
972
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -0300973/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -0300974 * Constants for setting receiver modes.
975 * (6 modes defined at this time, enumerated by mt2063_delivery_sys)
976 * (DNC1GC & DNC2GC are the values, which are used, when the specific
977 * DNC Output is selected, the other is always off)
978 *
979 * enum mt2063_delivery_sys
980 * -------------+----------------------------------------------
981 * Mode 0 : | MT2063_CABLE_QAM
982 * Mode 1 : | MT2063_CABLE_ANALOG
983 * Mode 2 : | MT2063_OFFAIR_COFDM
984 * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS
985 * Mode 4 : | MT2063_OFFAIR_ANALOG
986 * Mode 5 : | MT2063_OFFAIR_8VSB
987 * --------------+----------------------------------------------
988 *
989 * |<---------- Mode -------------->|
990 * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 |
991 * ------------+-----+-----+-----+-----+-----+-----+
992 * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF
993 * LNARin | 0 | 0 | 3 | 3 | 3 | 3
994 * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1
995 * FIFFq | 0 | 0 | 0 | 0 | 0 | 0
996 * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0
997 * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0
998 * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1
999 * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31
1000 * LNA Target | 44 | 43 | 43 | 43 | 43 | 43
1001 * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1002 * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31
1003 * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38
1004 * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0
1005 * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5
1006 * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42
1007 */
1008
1009enum mt2063_delivery_sys {
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001010 MT2063_CABLE_QAM = 0,
1011 MT2063_CABLE_ANALOG,
1012 MT2063_OFFAIR_COFDM,
1013 MT2063_OFFAIR_COFDM_SAWLESS,
1014 MT2063_OFFAIR_ANALOG,
1015 MT2063_OFFAIR_8VSB,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001016 MT2063_NUM_RCVR_MODES
1017};
1018
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001019static const char *mt2063_mode_name[] = {
1020 [MT2063_CABLE_QAM] = "digital cable",
1021 [MT2063_CABLE_ANALOG] = "analog cable",
1022 [MT2063_OFFAIR_COFDM] = "digital offair",
1023 [MT2063_OFFAIR_COFDM_SAWLESS] = "digital offair without SAW",
1024 [MT2063_OFFAIR_ANALOG] = "analog offair",
1025 [MT2063_OFFAIR_8VSB] = "analog offair 8vsb",
1026};
1027
1028static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 };
1029static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 };
1030static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 };
1031static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 };
1032static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 };
1033static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 };
1034static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 };
1035static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 };
1036static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1037static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 };
1038static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 };
1039static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 };
1040static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 };
1041static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 };
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001042
1043/*
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001044 * mt2063_set_dnc_output_enable()
1045 */
1046static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001047 enum MT2063_DNC_Output_Enable *pValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001048{
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001049 dprintk(2, "\n");
1050
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001051 if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */
1052 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1053 *pValue = MT2063_DNC_NONE;
1054 else
1055 *pValue = MT2063_DNC_2;
1056 } else { /* DNC1 is on */
1057 if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
1058 *pValue = MT2063_DNC_1;
1059 else
1060 *pValue = MT2063_DNC_BOTH;
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03001061 }
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001062 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001063}
1064
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001065/*
1066 * mt2063_set_dnc_output_enable()
1067 */
1068static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001069 enum MT2063_DNC_Output_Enable nValue)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001070{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001071 int status = 0; /* Status to be returned */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001072 u8 val = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001073
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001074 dprintk(2, "\n");
1075
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001076 /* selects, which DNC output is used */
1077 switch (nValue) {
1078 case MT2063_DNC_NONE:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001079 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1080 if (state->reg[MT2063_REG_DNC_GAIN] !=
1081 val)
1082 status |=
1083 mt2063_setreg(state,
1084 MT2063_REG_DNC_GAIN,
1085 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001086
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001087 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1088 if (state->reg[MT2063_REG_VGA_GAIN] !=
1089 val)
1090 status |=
1091 mt2063_setreg(state,
1092 MT2063_REG_VGA_GAIN,
1093 val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001094
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001095 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1096 if (state->reg[MT2063_REG_RSVD_20] !=
1097 val)
1098 status |=
1099 mt2063_setreg(state,
1100 MT2063_REG_RSVD_20,
1101 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001102
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001103 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001104 case MT2063_DNC_1:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001105 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1106 if (state->reg[MT2063_REG_DNC_GAIN] !=
1107 val)
1108 status |=
1109 mt2063_setreg(state,
1110 MT2063_REG_DNC_GAIN,
1111 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001112
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001113 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */
1114 if (state->reg[MT2063_REG_VGA_GAIN] !=
1115 val)
1116 status |=
1117 mt2063_setreg(state,
1118 MT2063_REG_VGA_GAIN,
1119 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001120
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001121 val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */
1122 if (state->reg[MT2063_REG_RSVD_20] !=
1123 val)
1124 status |=
1125 mt2063_setreg(state,
1126 MT2063_REG_RSVD_20,
1127 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001128
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001129 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001130 case MT2063_DNC_2:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001131 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */
1132 if (state->reg[MT2063_REG_DNC_GAIN] !=
1133 val)
1134 status |=
1135 mt2063_setreg(state,
1136 MT2063_REG_DNC_GAIN,
1137 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001138
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001139 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1140 if (state->reg[MT2063_REG_VGA_GAIN] !=
1141 val)
1142 status |=
1143 mt2063_setreg(state,
1144 MT2063_REG_VGA_GAIN,
1145 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001146
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001147 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1148 if (state->reg[MT2063_REG_RSVD_20] !=
1149 val)
1150 status |=
1151 mt2063_setreg(state,
1152 MT2063_REG_RSVD_20,
1153 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001154
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001155 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001156 case MT2063_DNC_BOTH:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001157 val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */
1158 if (state->reg[MT2063_REG_DNC_GAIN] !=
1159 val)
1160 status |=
1161 mt2063_setreg(state,
1162 MT2063_REG_DNC_GAIN,
1163 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001164
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001165 val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */
1166 if (state->reg[MT2063_REG_VGA_GAIN] !=
1167 val)
1168 status |=
1169 mt2063_setreg(state,
1170 MT2063_REG_VGA_GAIN,
1171 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001172
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001173 val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */
1174 if (state->reg[MT2063_REG_RSVD_20] !=
1175 val)
1176 status |=
1177 mt2063_setreg(state,
1178 MT2063_REG_RSVD_20,
1179 val);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001180
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001181 break;
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001182 default:
1183 break;
1184 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001185
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001186 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001187}
1188
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001189/*
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001190 * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with
1191 * the selected enum mt2063_delivery_sys type.
1192 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001193 * (DNC1GC & DNC2GC are the values, which are used, when the specific
1194 * DNC Output is selected, the other is always off)
1195 *
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001196 * @state: ptr to mt2063_state structure
Jonathan McCrohan39c1cb22013-10-20 21:34:01 -03001197 * @Mode: desired receiver delivery system
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001198 *
1199 * Note: Register cache must be valid for it to work
1200 */
1201
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001202static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
Mauro Carvalho Chehab8fdb2262011-07-21 17:20:49 -03001203 enum mt2063_delivery_sys Mode)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001204{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001205 int status = 0; /* Status to be returned */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001206 u8 val;
1207 u32 longval;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001208
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001209 dprintk(2, "\n");
1210
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001211 if (Mode >= MT2063_NUM_RCVR_MODES)
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001212 status = -ERANGE;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001213
1214 /* RFAGCen */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001215 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001216 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001217 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001218 reg[MT2063_REG_PD1_TGT] & ~0x40) | (RFAGCEN[Mode]
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001219 ? 0x40 :
1220 0x00);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001221 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001222 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001223 }
1224
1225 /* LNARin */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001226 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001227 u8 val = (state->reg[MT2063_REG_CTRL_2C] & ~0x03) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001228 (LNARIN[Mode] & 0x03);
1229 if (state->reg[MT2063_REG_CTRL_2C] != val)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001230 status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001231 }
1232
1233 /* FIFFQEN and FIFFQ */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001234 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001235 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001236 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001237 reg[MT2063_REG_FIFF_CTRL2] & ~0xF0) |
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001238 (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001239 if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001240 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001241 mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001242 /* trigger FIFF calibration, needed after changing FIFFQ */
1243 val =
Hans Verkuilfe10b842014-08-21 11:01:23 -03001244 (state->reg[MT2063_REG_FIFF_CTRL] | 0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001245 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001246 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001247 val =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001248 (state->
Hans Verkuilfe10b842014-08-21 11:01:23 -03001249 reg[MT2063_REG_FIFF_CTRL] & ~0x01);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001250 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001251 mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001252 }
1253 }
1254
1255 /* DNC1GC & DNC2GC */
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001256 status |= mt2063_get_dnc_output_enable(state, &longval);
1257 status |= mt2063_set_dnc_output_enable(state, longval);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001258
1259 /* acLNAmax */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001260 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001261 u8 val = (state->reg[MT2063_REG_LNA_OV] & ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001262 (ACLNAMAX[Mode] & 0x1F);
1263 if (state->reg[MT2063_REG_LNA_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001264 status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001265 }
1266
1267 /* LNATGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001268 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001269 u8 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001270 (LNATGT[Mode] & 0x3F);
1271 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001272 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001273 }
1274
1275 /* ACRF */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001276 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001277 u8 val = (state->reg[MT2063_REG_RF_OV] & ~0x1F) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001278 (ACRFMAX[Mode] & 0x1F);
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001279 if (state->reg[MT2063_REG_RF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001280 status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001281 }
1282
1283 /* PD1TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001284 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001285 u8 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001286 (PD1TGT[Mode] & 0x3F);
1287 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001288 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001289 }
1290
1291 /* FIFATN */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001292 if (status >= 0) {
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001293 u8 val = ACFIFMAX[Mode];
1294 if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
1295 val = 5;
Hans Verkuilfe10b842014-08-21 11:01:23 -03001296 val = (state->reg[MT2063_REG_FIF_OV] & ~0x1F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001297 (val & 0x1F);
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001298 if (state->reg[MT2063_REG_FIF_OV] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001299 status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001300 }
1301
1302 /* PD2TGT */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001303 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001304 u8 val = (state->reg[MT2063_REG_PD2_TGT] & ~0x3F) |
Mauro Carvalho Chehab4713e2252011-07-21 11:23:59 -03001305 (PD2TGT[Mode] & 0x3F);
1306 if (state->reg[MT2063_REG_PD2_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001307 status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001308 }
1309
1310 /* Ignore ATN Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001311 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001312 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x80) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001313 (RFOVDIS[Mode] ? 0x80 : 0x00);
1314 if (state->reg[MT2063_REG_LNA_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001315 status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001316 }
1317
1318 /* Ignore FIF Overload */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001319 if (status >= 0) {
Hans Verkuilfe10b842014-08-21 11:01:23 -03001320 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x80) |
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001321 (FIFOVDIS[Mode] ? 0x80 : 0x00);
1322 if (state->reg[MT2063_REG_PD1_TGT] != val)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001323 status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001324 }
1325
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001326 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001327 state->rcvr_mode = Mode;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03001328 dprintk(1, "mt2063 mode changed to %s\n",
1329 mt2063_mode_name[state->rcvr_mode]);
1330 }
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001331
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001332 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001333}
1334
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001335/*
1336 * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various
1337 * sections of the MT2063
1338 *
1339 * @Bits: Mask bits to be cleared.
1340 *
1341 * See definition of MT2063_Mask_Bits type for description
1342 * of each of the power bits.
1343 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001344static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state,
1345 enum MT2063_Mask_Bits Bits)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001346{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001347 int status = 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001348
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001349 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001350 Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */
1351 if ((Bits & 0xFF00) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001352 state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001353 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001354 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001355 MT2063_REG_PWR_2,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001356 &state->reg[MT2063_REG_PWR_2], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001357 }
1358 if ((Bits & 0xFF) != 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001359 state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001360 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001361 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001362 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001363 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001364 }
1365
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001366 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001367}
1368
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001369/*
1370 * MT2063_SoftwareShutdown() - Enables or disables software shutdown function.
1371 * When Shutdown is 1, any section whose power
1372 * mask is set will be shutdown.
1373 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001374static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001375{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001376 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001377
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001378 dprintk(2, "\n");
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001379 if (Shutdown == 1)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001380 state->reg[MT2063_REG_PWR_1] |= 0x04;
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001381 else
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001382 state->reg[MT2063_REG_PWR_1] &= ~0x04;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001383
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001384 status = mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001385 MT2063_REG_PWR_1,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001386 &state->reg[MT2063_REG_PWR_1], 1);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001387
1388 if (Shutdown != 1) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001389 state->reg[MT2063_REG_BYP_CTRL] =
1390 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001391 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001392 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001393 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001394 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001395 1);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001396 state->reg[MT2063_REG_BYP_CTRL] =
1397 (state->reg[MT2063_REG_BYP_CTRL] & 0x9F);
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001398 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001399 mt2063_write(state,
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001400 MT2063_REG_BYP_CTRL,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001401 &state->reg[MT2063_REG_BYP_CTRL],
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001402 1);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001403 }
1404
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001405 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001406}
1407
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001408static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001409{
1410 return f_ref * (f_LO / f_ref)
1411 + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step);
1412}
1413
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001414/**
1415 * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom.
1416 * This function preserves maximum precision without
1417 * risk of overflow. It accurately calculates
1418 * f_ref * num / denom to within 1 HZ with fixed math.
1419 *
1420 * @num : Fractional portion of the multiplier
1421 * @denom: denominator portion of the ratio
1422 * @f_Ref: SRO frequency.
1423 *
1424 * This calculation handles f_ref as two separate 14-bit fields.
1425 * Therefore, a maximum value of 2^28-1 may safely be used for f_ref.
1426 * This is the genesis of the magic number "14" and the magic mask value of
1427 * 0x03FFF.
1428 *
1429 * This routine successfully handles denom values up to and including 2^18.
1430 * Returns: f_ref * num / denom
1431 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001432static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001433{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001434 u32 t1 = (f_ref >> 14) * num;
1435 u32 term1 = t1 / denom;
1436 u32 loss = t1 % denom;
1437 u32 term2 =
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001438 (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom;
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001439 return (term1 << 14) + term2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001440}
1441
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001442/*
1443 * CalcLO1Mult()- Calculates Integer divider value and the numerator
1444 * value for a FracN PLL.
1445 *
1446 * This function assumes that the f_LO and f_Ref are
1447 * evenly divisible by f_LO_Step.
1448 *
1449 * @Div: OUTPUT: Whole number portion of the multiplier
1450 * @FracN: OUTPUT: Fractional portion of the multiplier
1451 * @f_LO: desired LO frequency.
1452 * @f_LO_Step: Minimum step size for the LO (in Hz).
1453 * @f_Ref: SRO frequency.
1454 * @f_Avoid: Range of PLL frequencies to avoid near integer multiples
1455 * of f_Ref (in Hz).
1456 *
1457 * Returns: Recalculated LO frequency.
1458 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001459static u32 MT2063_CalcLO1Mult(u32 *Div,
1460 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001461 u32 f_LO,
1462 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001463{
1464 /* Calculate the whole number portion of the divider */
1465 *Div = f_LO / f_Ref;
1466
1467 /* Calculate the numerator value (round to nearest f_LO_Step) */
1468 *FracN =
1469 (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1470 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1471
1472 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64);
1473}
1474
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001475/**
1476 * CalcLO2Mult() - Calculates Integer divider value and the numerator
1477 * value for a FracN PLL.
1478 *
1479 * This function assumes that the f_LO and f_Ref are
1480 * evenly divisible by f_LO_Step.
1481 *
1482 * @Div: OUTPUT: Whole number portion of the multiplier
1483 * @FracN: OUTPUT: Fractional portion of the multiplier
1484 * @f_LO: desired LO frequency.
1485 * @f_LO_Step: Minimum step size for the LO (in Hz).
1486 * @f_Ref: SRO frequency.
1487 * @f_Avoid: Range of PLL frequencies to avoid near
1488 * integer multiples of f_Ref (in Hz).
1489 *
1490 * Returns: Recalculated LO frequency.
1491 */
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03001492static u32 MT2063_CalcLO2Mult(u32 *Div,
1493 u32 *FracN,
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001494 u32 f_LO,
1495 u32 f_LO_Step, u32 f_Ref)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001496{
1497 /* Calculate the whole number portion of the divider */
1498 *Div = f_LO / f_Ref;
1499
1500 /* Calculate the numerator value (round to nearest f_LO_Step) */
1501 *FracN =
1502 (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) +
1503 (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step);
1504
1505 return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN,
1506 8191);
1507}
1508
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001509/*
1510 * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be
1511 * used for a given input frequency.
1512 *
1513 * @state: ptr to tuner data structure
1514 * @f_in: RF input center frequency (in Hz).
1515 *
1516 * Returns: ClearTune filter number (0-31)
1517 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001518static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001519{
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001520 u32 RFBand;
1521 u32 idx; /* index loop */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001522
1523 /*
1524 ** Find RF Band setting
1525 */
1526 RFBand = 31; /* def when f_in > all */
1527 for (idx = 0; idx < 31; ++idx) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001528 if (state->CTFiltMax[idx] >= f_in) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001529 RFBand = idx;
1530 break;
1531 }
1532 }
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001533 return RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001534}
1535
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001536/*
1537 * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
1538 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001539static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001540{ /* RF input center frequency */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001541
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001542 int status = 0;
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001543 u32 LO1; /* 1st LO register value */
1544 u32 Num1; /* Numerator for LO1 reg. value */
1545 u32 f_IF1; /* 1st IF requested */
1546 u32 LO2; /* 2nd LO register value */
1547 u32 Num2; /* Numerator for LO2 reg. value */
1548 u32 ofLO1, ofLO2; /* last time's LO frequencies */
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001549 u8 fiffc = 0x80; /* FIFF center freq from tuner */
1550 u32 fiffof; /* Offset from FIFF center freq */
1551 const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
1552 u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
1553 u8 val;
1554 u32 RFBand;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001555
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001556 dprintk(2, "\n");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001557 /* Check the input and output frequency ranges */
1558 if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001559 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001560
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001561 if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
1562 || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ))
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001563 return -EINVAL;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001564
1565 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001566 * Save original LO1 and LO2 register values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001567 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001568 ofLO1 = state->AS_Data.f_LO1;
Mauro Carvalho Chehabb5a91062011-07-22 17:07:17 -03001569 ofLO2 = state->AS_Data.f_LO2;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001570
1571 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001572 * Find and set RF Band setting
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001573 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001574 if (state->ctfilt_sw == 1) {
1575 val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08);
1576 if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001577 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001578 mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001579 }
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001580 val = state->reg[MT2063_REG_CTUNE_OV];
1581 RFBand = FindClearTuneFilter(state, f_in);
1582 state->reg[MT2063_REG_CTUNE_OV] =
1583 (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001584 | RFBand);
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001585 if (state->reg[MT2063_REG_CTUNE_OV] != val) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001586 status |=
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03001587 mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001588 }
1589 }
1590
1591 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001592 * Read the FIFF Center Frequency from the tuner
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001593 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001594 if (status >= 0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001595 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001596 mt2063_read(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001597 MT2063_REG_FIFFC,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001598 &state->reg[MT2063_REG_FIFFC], 1);
1599 fiffc = state->reg[MT2063_REG_FIFFC];
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001600 }
1601 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001602 * Assign in the requested values
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001603 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001604 state->AS_Data.f_in = f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001605 /* Request a 1st IF such that LO1 is on a step size */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001606 state->AS_Data.f_if1_Request =
1607 MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
1608 state->AS_Data.f_LO1_Step,
1609 state->AS_Data.f_ref) - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001610
1611 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001612 * Calculate frequency settings. f_IF1_FREQ + f_in is the
1613 * desired LO1 frequency
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001614 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001615 MT2063_ResetExclZones(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001616
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001617 f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001618
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001619 state->AS_Data.f_LO1 =
1620 MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step,
1621 state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001622
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001623 state->AS_Data.f_LO2 =
1624 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1625 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001626
1627 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001628 * Check for any LO spurs in the output bandwidth and adjust
1629 * the LO settings to avoid them if needed
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001630 */
Mauro Carvalho Chehabe3f94fb2011-07-21 13:41:29 -03001631 status |= MT2063_AvoidSpurs(&state->AS_Data);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001632 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001633 * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values.
1634 * Recalculate the LO frequencies and the values to be placed
1635 * in the tuning registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001636 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001637 state->AS_Data.f_LO1 =
1638 MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
1639 state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
1640 state->AS_Data.f_LO2 =
1641 MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
1642 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
1643 state->AS_Data.f_LO2 =
1644 MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
1645 state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001646
1647 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001648 * Check the upconverter and downconverter frequency ranges
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001649 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001650 if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
1651 || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001652 status |= MT2063_UPC_RANGE;
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001653 if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
1654 || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001655 status |= MT2063_DNC_RANGE;
1656 /* LO2 Lock bit was in a different place for B0 version */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001657 if (state->tuner_id == MT2063_B0)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001658 LO2LK = 0x40;
1659
1660 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001661 * If we have the same LO frequencies and we're already locked,
1662 * then skip re-programming the LO registers.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001663 */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001664 if ((ofLO1 != state->AS_Data.f_LO1)
1665 || (ofLO2 != state->AS_Data.f_LO2)
1666 || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001667 (LO1LK | LO2LK))) {
1668 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001669 * Calculate the FIFFOF register value
1670 *
1671 * IF1_Actual
1672 * FIFFOF = ------------ - 8 * FIFFC - 4992
1673 * f_ref/64
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001674 */
1675 fiffof =
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001676 (state->AS_Data.f_LO1 -
1677 f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001678 4992;
1679 if (fiffof > 0xFF)
1680 fiffof = 0xFF;
1681
1682 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001683 * Place all of the calculated values into the local tuner
1684 * register fields.
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001685 */
Mauro Carvalho Chehabfdf77a42011-07-20 22:55:25 -03001686 if (status >= 0) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001687 state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */
1688 state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */
1689 state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001690 |(Num2 >> 12)); /* NUM2q (hi) */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001691 state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */
1692 state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001693
1694 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001695 * Now write out the computed register values
1696 * IMPORTANT: There is a required order for writing
1697 * (0x05 must follow all the others).
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001698 */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001699 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 -03001700 if (state->tuner_id == MT2063_B0) {
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001701 /* Re-write the one-shot bits to trigger the tune operation */
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001702 status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001703 }
1704 /* Write out the FIFF offset only if it's changing */
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001705 if (state->reg[MT2063_REG_FIFF_OFFSET] !=
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001706 (u8) fiffof) {
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001707 state->reg[MT2063_REG_FIFF_OFFSET] =
Mauro Carvalho Chehabcfde8922011-07-20 21:01:48 -03001708 (u8) fiffof;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001709 status |=
Mauro Carvalho Chehabe1de3d12011-07-21 02:46:49 -03001710 mt2063_write(state,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001711 MT2063_REG_FIFF_OFFSET,
Mauro Carvalho Chehabdcd52d22011-07-21 02:25:39 -03001712 &state->
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001713 reg[MT2063_REG_FIFF_OFFSET],
1714 1);
1715 }
1716 }
1717
1718 /*
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03001719 * Check for LO's locking
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001720 */
1721
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001722 if (status < 0)
1723 return status;
1724
1725 status = mt2063_lockStatus(state);
1726 if (status < 0)
1727 return status;
1728 if (!status)
1729 return -EINVAL; /* Couldn't lock */
1730
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001731 /*
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001732 * If we locked OK, assign calculated data to mt2063_state structure
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001733 */
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001734 state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001735 }
1736
Mauro Carvalho Chehab31e67fa2011-07-21 10:30:11 -03001737 return status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001738}
1739
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001740static const u8 MT2063B0_defaults[] = {
1741 /* Reg, Value */
1742 0x19, 0x05,
1743 0x1B, 0x1D,
1744 0x1C, 0x1F,
1745 0x1D, 0x0F,
1746 0x1E, 0x3F,
1747 0x1F, 0x0F,
1748 0x20, 0x3F,
1749 0x22, 0x21,
1750 0x23, 0x3F,
1751 0x24, 0x20,
1752 0x25, 0x3F,
1753 0x27, 0xEE,
1754 0x2C, 0x27, /* bit at 0x20 is cleared below */
1755 0x30, 0x03,
1756 0x2C, 0x07, /* bit at 0x20 is cleared here */
1757 0x2D, 0x87,
1758 0x2E, 0xAA,
1759 0x28, 0xE1, /* Set the FIFCrst bit here */
1760 0x28, 0xE0, /* Clear the FIFCrst bit here */
1761 0x00
1762};
1763
1764/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1765static const u8 MT2063B1_defaults[] = {
1766 /* Reg, Value */
1767 0x05, 0xF0,
1768 0x11, 0x10, /* New Enable AFCsd */
1769 0x19, 0x05,
1770 0x1A, 0x6C,
1771 0x1B, 0x24,
1772 0x1C, 0x28,
1773 0x1D, 0x8F,
1774 0x1E, 0x14,
1775 0x1F, 0x8F,
1776 0x20, 0x57,
1777 0x22, 0x21, /* New - ver 1.03 */
1778 0x23, 0x3C, /* New - ver 1.10 */
1779 0x24, 0x20, /* New - ver 1.03 */
1780 0x2C, 0x24, /* bit at 0x20 is cleared below */
1781 0x2D, 0x87, /* FIFFQ=0 */
1782 0x2F, 0xF3,
1783 0x30, 0x0C, /* New - ver 1.11 */
1784 0x31, 0x1B, /* New - ver 1.11 */
1785 0x2C, 0x04, /* bit at 0x20 is cleared here */
1786 0x28, 0xE1, /* Set the FIFCrst bit here */
1787 0x28, 0xE0, /* Clear the FIFCrst bit here */
1788 0x00
1789};
1790
1791/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */
1792static const u8 MT2063B3_defaults[] = {
1793 /* Reg, Value */
1794 0x05, 0xF0,
1795 0x19, 0x3D,
1796 0x2C, 0x24, /* bit at 0x20 is cleared below */
1797 0x2C, 0x04, /* bit at 0x20 is cleared here */
1798 0x28, 0xE1, /* Set the FIFCrst bit here */
1799 0x28, 0xE0, /* Clear the FIFCrst bit here */
1800 0x00
1801};
1802
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001803static int mt2063_init(struct dvb_frontend *fe)
1804{
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03001805 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001806 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001807 u8 all_resets = 0xF0; /* reset/load bits */
1808 const u8 *def = NULL;
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001809 char *step;
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001810 u32 FCRUN;
1811 s32 maxReads;
1812 u32 fcu_osc;
1813 u32 i;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001814
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001815 dprintk(2, "\n");
1816
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001817 state->rcvr_mode = MT2063_CABLE_QAM;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001818
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001819 /* Read the Part/Rev code from the tuner */
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001820 status = mt2063_read(state, MT2063_REG_PART_REV,
1821 &state->reg[MT2063_REG_PART_REV], 1);
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001822 if (status < 0) {
1823 printk(KERN_ERR "Can't read mt2063 part ID\n");
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001824 return status;
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001825 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001826
1827 /* Check the part/rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001828 switch (state->reg[MT2063_REG_PART_REV]) {
1829 case MT2063_B0:
1830 step = "B0";
1831 break;
1832 case MT2063_B1:
1833 step = "B1";
1834 break;
1835 case MT2063_B2:
1836 step = "B2";
1837 break;
1838 case MT2063_B3:
1839 step = "B3";
1840 break;
1841 default:
1842 printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n",
1843 state->reg[MT2063_REG_PART_REV]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001844 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001845 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001846
1847 /* Check the 2nd byte of the Part/Rev code from the tuner */
1848 status = mt2063_read(state, MT2063_REG_RSVD_3B,
1849 &state->reg[MT2063_REG_RSVD_3B], 1);
1850
1851 /* b7 != 0 ==> NOT MT2063 */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001852 if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
Mauro Carvalho Chehab36ae6df2011-07-23 09:48:08 -03001853 printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n",
1854 state->reg[MT2063_REG_PART_REV],
1855 state->reg[MT2063_REG_RSVD_3B]);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001856 return -ENODEV; /* Wrong tuner Part/Rev code */
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03001857 }
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001858
Mauro Carvalho Chehabd1244f72011-07-23 11:55:57 -03001859 printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step);
Mauro Carvalho Chehab19ad6a02011-07-22 21:24:33 -03001860
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001861 /* Reset the tuner */
1862 status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1);
1863 if (status < 0)
1864 return status;
1865
1866 /* change all of the default values that vary from the HW reset values */
1867 /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */
1868 switch (state->reg[MT2063_REG_PART_REV]) {
1869 case MT2063_B3:
1870 def = MT2063B3_defaults;
1871 break;
1872
1873 case MT2063_B1:
1874 def = MT2063B1_defaults;
1875 break;
1876
1877 case MT2063_B0:
1878 def = MT2063B0_defaults;
1879 break;
1880
1881 default:
1882 return -ENODEV;
1883 break;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03001884 }
1885
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03001886 while (status >= 0 && *def) {
1887 u8 reg = *def++;
1888 u8 val = *def++;
1889 status = mt2063_write(state, reg, &val, 1);
1890 }
1891 if (status < 0)
1892 return status;
1893
1894 /* Wait for FIFF location to complete. */
1895 FCRUN = 1;
1896 maxReads = 10;
1897 while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) {
1898 msleep(2);
1899 status = mt2063_read(state,
1900 MT2063_REG_XO_STATUS,
1901 &state->
1902 reg[MT2063_REG_XO_STATUS], 1);
1903 FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6;
1904 }
1905
1906 if (FCRUN != 0 || status < 0)
1907 return -ENODEV;
1908
1909 status = mt2063_read(state,
1910 MT2063_REG_FIFFC,
1911 &state->reg[MT2063_REG_FIFFC], 1);
1912 if (status < 0)
1913 return status;
1914
1915 /* Read back all the registers from the tuner */
1916 status = mt2063_read(state,
1917 MT2063_REG_PART_REV,
1918 state->reg, MT2063_REG_END_REGS);
1919 if (status < 0)
1920 return status;
1921
1922 /* Initialize the tuner state. */
1923 state->tuner_id = state->reg[MT2063_REG_PART_REV];
1924 state->AS_Data.f_ref = MT2063_REF_FREQ;
1925 state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) *
1926 ((u32) state->reg[MT2063_REG_FIFFC] + 640);
1927 state->AS_Data.f_if1_bw = MT2063_IF1_BW;
1928 state->AS_Data.f_out = 43750000UL;
1929 state->AS_Data.f_out_bw = 6750000UL;
1930 state->AS_Data.f_zif_bw = MT2063_ZIF_BW;
1931 state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64;
1932 state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE;
1933 state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1;
1934 state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2;
1935 state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP;
1936 state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center;
1937 state->AS_Data.f_LO1 = 2181000000UL;
1938 state->AS_Data.f_LO2 = 1486249786UL;
1939 state->f_IF1_actual = state->AS_Data.f_if1_Center;
1940 state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual;
1941 state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID;
1942 state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID;
1943 state->num_regs = MT2063_REG_END_REGS;
1944 state->AS_Data.avoidDECT = MT2063_AVOID_BOTH;
1945 state->ctfilt_sw = 0;
1946
1947 state->CTFiltMax[0] = 69230000;
1948 state->CTFiltMax[1] = 105770000;
1949 state->CTFiltMax[2] = 140350000;
1950 state->CTFiltMax[3] = 177110000;
1951 state->CTFiltMax[4] = 212860000;
1952 state->CTFiltMax[5] = 241130000;
1953 state->CTFiltMax[6] = 274370000;
1954 state->CTFiltMax[7] = 309820000;
1955 state->CTFiltMax[8] = 342450000;
1956 state->CTFiltMax[9] = 378870000;
1957 state->CTFiltMax[10] = 416210000;
1958 state->CTFiltMax[11] = 456500000;
1959 state->CTFiltMax[12] = 495790000;
1960 state->CTFiltMax[13] = 534530000;
1961 state->CTFiltMax[14] = 572610000;
1962 state->CTFiltMax[15] = 598970000;
1963 state->CTFiltMax[16] = 635910000;
1964 state->CTFiltMax[17] = 672130000;
1965 state->CTFiltMax[18] = 714840000;
1966 state->CTFiltMax[19] = 739660000;
1967 state->CTFiltMax[20] = 770410000;
1968 state->CTFiltMax[21] = 814660000;
1969 state->CTFiltMax[22] = 846950000;
1970 state->CTFiltMax[23] = 867820000;
1971 state->CTFiltMax[24] = 915980000;
1972 state->CTFiltMax[25] = 947450000;
1973 state->CTFiltMax[26] = 983110000;
1974 state->CTFiltMax[27] = 1021630000;
1975 state->CTFiltMax[28] = 1061870000;
1976 state->CTFiltMax[29] = 1098330000;
1977 state->CTFiltMax[30] = 1138990000;
1978
1979 /*
1980 ** Fetch the FCU osc value and use it and the fRef value to
1981 ** scale all of the Band Max values
1982 */
1983
1984 state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
1985 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
1986 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
1987 if (status < 0)
1988 return status;
1989
1990 /* Read the ClearTune filter calibration value */
1991 status = mt2063_read(state, MT2063_REG_FIFFC,
1992 &state->reg[MT2063_REG_FIFFC], 1);
1993 if (status < 0)
1994 return status;
1995
1996 fcu_osc = state->reg[MT2063_REG_FIFFC];
1997
1998 state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
1999 status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
2000 &state->reg[MT2063_REG_CTUNE_CTRL], 1);
2001 if (status < 0)
2002 return status;
2003
2004 /* Adjust each of the values in the ClearTune filter cross-over table */
2005 for (i = 0; i < 31; i++)
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002006 state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
Mauro Carvalho Chehab01e0daf2011-07-21 03:20:43 -03002007
2008 status = MT2063_SoftwareShutdown(state, 1);
2009 if (status < 0)
2010 return status;
2011 status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2012 if (status < 0)
2013 return status;
2014
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002015 state->init = true;
2016
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002017 return 0;
2018}
2019
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002020static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status)
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002021{
Mauro Carvalho Chehab51f0f7b2011-07-21 02:24:18 -03002022 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002023 int status;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002024
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002025 dprintk(2, "\n");
2026
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002027 if (!state->init)
2028 return -ENODEV;
2029
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002030 *tuner_status = 0;
2031 status = mt2063_lockStatus(state);
2032 if (status < 0)
2033 return status;
2034 if (status)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002035 *tuner_status = TUNER_STATUS_LOCKED;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002036
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002037 dprintk(1, "Tuner status: %d", *tuner_status);
2038
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002039 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002040}
2041
2042static int mt2063_release(struct dvb_frontend *fe)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002043{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002044 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002045
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002046 dprintk(2, "\n");
2047
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002048 fe->tuner_priv = NULL;
2049 kfree(state);
2050
2051 return 0;
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002052}
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002053
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002054static int mt2063_set_analog_params(struct dvb_frontend *fe,
2055 struct analog_parameters *params)
2056{
2057 struct mt2063_state *state = fe->tuner_priv;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002058 s32 pict_car;
2059 s32 pict2chanb_vsb;
2060 s32 ch_bw;
2061 s32 if_mid;
2062 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002063 int status;
2064
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002065 dprintk(2, "\n");
2066
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002067 if (!state->init) {
2068 status = mt2063_init(fe);
2069 if (status < 0)
2070 return status;
2071 }
2072
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002073 switch (params->mode) {
2074 case V4L2_TUNER_RADIO:
2075 pict_car = 38900000;
2076 ch_bw = 8000000;
2077 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002078 rcvr_mode = MT2063_OFFAIR_ANALOG;
2079 break;
2080 case V4L2_TUNER_ANALOG_TV:
2081 rcvr_mode = MT2063_CABLE_ANALOG;
2082 if (params->std & ~V4L2_STD_MN) {
2083 pict_car = 38900000;
2084 ch_bw = 6000000;
2085 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002086 } else if (params->std & V4L2_STD_PAL_G) {
2087 pict_car = 38900000;
2088 ch_bw = 7000000;
2089 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002090 } else { /* PAL/SECAM standards */
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002091 pict_car = 38900000;
2092 ch_bw = 8000000;
2093 pict2chanb_vsb = -1250000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002094 }
2095 break;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002096 default:
2097 return -EINVAL;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002098 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002099 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2100
2101 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2102 state->AS_Data.f_out = if_mid;
2103 state->AS_Data.f_out_bw = ch_bw + 750000;
2104 status = MT2063_SetReceiverMode(state, rcvr_mode);
2105 if (status < 0)
2106 return status;
2107
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002108 dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n",
2109 params->frequency, ch_bw, pict2chanb_vsb);
2110
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002111 status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2))));
2112 if (status < 0)
2113 return status;
2114
2115 state->frequency = params->frequency;
2116 return 0;
2117}
2118
2119/*
2120 * As defined on EN 300 429, the DVB-C roll-off factor is 0.15.
Jonathan McCrohan39c1cb22013-10-20 21:34:01 -03002121 * So, the amount of the needed bandwidth is given by:
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002122 * Bw = Symbol_rate * (1 + 0.15)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002123 * As such, the maximum symbol rate supported by 6 MHz is given by:
2124 * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
2125 */
2126#define MAX_SYMBOL_RATE_6MHz 5217391
2127
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002128static int mt2063_set_params(struct dvb_frontend *fe)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002129{
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002130 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002131 struct mt2063_state *state = fe->tuner_priv;
2132 int status;
Mauro Carvalho Chehab2e1d5882011-07-22 17:05:15 -03002133 s32 pict_car;
2134 s32 pict2chanb_vsb;
2135 s32 ch_bw;
2136 s32 if_mid;
2137 s32 rcvr_mode;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002138
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002139 if (!state->init) {
2140 status = mt2063_init(fe);
2141 if (status < 0)
2142 return status;
2143 }
2144
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002145 dprintk(2, "\n");
2146
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002147 if (c->bandwidth_hz == 0)
2148 return -EINVAL;
2149 if (c->bandwidth_hz <= 6000000)
2150 ch_bw = 6000000;
2151 else if (c->bandwidth_hz <= 7000000)
2152 ch_bw = 7000000;
2153 else
2154 ch_bw = 8000000;
2155
2156 switch (c->delivery_system) {
2157 case SYS_DVBT:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002158 rcvr_mode = MT2063_OFFAIR_COFDM;
2159 pict_car = 36125000;
2160 pict2chanb_vsb = -(ch_bw / 2);
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002161 break;
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002162 case SYS_DVBC_ANNEX_A:
2163 case SYS_DVBC_ANNEX_C:
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002164 rcvr_mode = MT2063_CABLE_QAM;
2165 pict_car = 36125000;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002166 pict2chanb_vsb = -(ch_bw / 2);
2167 break;
2168 default:
2169 return -EINVAL;
2170 }
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002171 if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2));
2172
2173 state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */
2174 state->AS_Data.f_out = if_mid;
2175 state->AS_Data.f_out_bw = ch_bw + 750000;
2176 status = MT2063_SetReceiverMode(state, rcvr_mode);
2177 if (status < 0)
2178 return status;
2179
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002180 dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n",
2181 c->frequency, ch_bw, pict2chanb_vsb);
2182
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002183 status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2))));
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002184
2185 if (status < 0)
Mauro Carvalho Chehab54a46132011-07-21 16:40:03 -03002186 return status;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002187
Mauro Carvalho Chehab669b67d2012-01-04 22:29:32 -02002188 state->frequency = c->frequency;
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002189 return 0;
2190}
2191
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002192static int mt2063_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002193{
2194 struct mt2063_state *state = fe->tuner_priv;
2195
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002196 dprintk(2, "\n");
2197
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002198 if (!state->init)
2199 return -ENODEV;
2200
Stefan Ringeleeecd0c2011-12-17 16:57:15 -03002201 *freq = state->AS_Data.f_out;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002202
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002203 dprintk(1, "IF frequency: %d\n", *freq);
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002204
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002205 return 0;
2206}
2207
2208static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2209{
2210 struct mt2063_state *state = fe->tuner_priv;
2211
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002212 dprintk(2, "\n");
2213
Mauro Carvalho Chehab1b0bfee2011-07-22 21:22:29 -03002214 if (!state->init)
2215 return -ENODEV;
2216
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002217 *bw = state->AS_Data.f_out_bw - 750000;
Mauro Carvalho Chehabb52e7c72011-07-23 12:16:26 -03002218
2219 dprintk(1, "bandwidth: %d\n", *bw);
2220
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002221 return 0;
2222}
2223
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002224static struct dvb_tuner_ops mt2063_ops = {
2225 .info = {
2226 .name = "MT2063 Silicon Tuner",
2227 .frequency_min = 45000000,
Jose Alberto Reguerodb5823b2012-01-27 18:43:28 -03002228 .frequency_max = 865000000,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002229 .frequency_step = 0,
2230 },
2231
2232 .init = mt2063_init,
Mauro Carvalho Chehabbf975552011-07-20 21:43:30 -03002233 .sleep = MT2063_Sleep,
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002234 .get_status = mt2063_get_status,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002235 .set_analog_params = mt2063_set_analog_params,
2236 .set_params = mt2063_set_params,
Mauro Carvalho Chehab5160e812011-07-23 14:28:14 -03002237 .get_if_frequency = mt2063_get_if_frequency,
Mauro Carvalho Chehab99ac5412011-07-21 15:46:49 -03002238 .get_bandwidth = mt2063_get_bandwidth,
2239 .release = mt2063_release,
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002240};
2241
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002242struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
2243 struct mt2063_config *config,
2244 struct i2c_adapter *i2c)
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002245{
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002246 struct mt2063_state *state = NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002247
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002248 dprintk(2, "\n");
2249
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002250 state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL);
Peter Senna Tschudin6f0fdc42012-09-12 08:55:57 -03002251 if (!state)
2252 return NULL;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002253
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002254 state->config = config;
2255 state->i2c = i2c;
2256 state->frontend = fe;
2257 state->reference = config->refclock / 1000; /* kHz */
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002258 fe->tuner_priv = state;
2259 fe->ops.tuner_ops = mt2063_ops;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002260
Mauro Carvalho Chehab6fb16702011-07-21 14:12:04 -03002261 printk(KERN_INFO "%s: Attaching MT2063\n", __func__);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002262 return fe;
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002263}
Mauro Carvalho Chehab3d497002011-07-21 11:00:59 -03002264EXPORT_SYMBOL_GPL(mt2063_attach);
Mauro Carvalho Chehab223c7b02011-07-20 19:48:59 -03002265
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002266#if 0
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002267/*
2268 * Ancillary routines visible outside mt2063
2269 * FIXME: Remove them in favor of using standard tuner callbacks
2270 */
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002271static int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002272{
2273 struct mt2063_state *state = fe->tuner_priv;
2274 int err = 0;
2275
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002276 dprintk(2, "\n");
2277
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002278 err = MT2063_SoftwareShutdown(state, 1);
2279 if (err < 0)
2280 printk(KERN_ERR "%s: Couldn't shutdown\n", __func__);
2281
2282 return err;
2283}
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002284
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002285static int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002286{
2287 struct mt2063_state *state = fe->tuner_priv;
2288 int err = 0;
2289
Mauro Carvalho Chehabdb6587b2011-07-22 16:54:05 -03002290 dprintk(2, "\n");
2291
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002292 err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD);
2293 if (err < 0)
2294 printk(KERN_ERR "%s: Invalid parameter\n", __func__);
2295
2296 return err;
2297}
Mauro Carvalho Chehab20eb13a2012-10-06 11:21:02 -03002298#endif
Mauro Carvalho Chehab8294e3e2011-07-21 13:33:32 -03002299
Mauro Carvalho Chehab37e59f82014-02-07 08:03:07 -02002300MODULE_AUTHOR("Mauro Carvalho Chehab");
Mauro Carvalho Chehab0e301442011-07-20 19:52:49 -03002301MODULE_DESCRIPTION("MT2063 Silicon tuner");
2302MODULE_LICENSE("GPL");