• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
3  *
4  *  Copyright (c) 2007 Olivier DANET <odanet@caramail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/delay.h>
19 #include <linux/dvb/frontend.h>
20 #include <linux/i2c.h>
21 
22 #include "dvb_frontend.h"
23 #include "mt2266.h"
24 
25 #define I2C_ADDRESS 0x60
26 
27 #define REG_PART_REV   0
28 #define REG_TUNE       1
29 #define REG_BAND       6
30 #define REG_BANDWIDTH  8
31 #define REG_LOCK       0x12
32 
33 #define PART_REV 0x85
34 
35 struct mt2266_priv {
36 	struct mt2266_config *cfg;
37 	struct i2c_adapter   *i2c;
38 
39 	u32 frequency;
40 	u32 bandwidth;
41 	u8 band;
42 };
43 
44 #define MT2266_VHF 1
45 #define MT2266_UHF 0
46 
47 /* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
48 
49 static int debug;
50 module_param(debug, int, 0644);
51 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
52 
53 #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0)
54 
55 // Reads a single register
mt2266_readreg(struct mt2266_priv * priv,u8 reg,u8 * val)56 static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val)
57 {
58 	struct i2c_msg msg[2] = {
59 		{ .addr = priv->cfg->i2c_address, .flags = 0,        .buf = &reg, .len = 1 },
60 		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val,  .len = 1 },
61 	};
62 	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
63 		printk(KERN_WARNING "MT2266 I2C read failed\n");
64 		return -EREMOTEIO;
65 	}
66 	return 0;
67 }
68 
69 // Writes a single register
mt2266_writereg(struct mt2266_priv * priv,u8 reg,u8 val)70 static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val)
71 {
72 	u8 buf[2] = { reg, val };
73 	struct i2c_msg msg = {
74 		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
75 	};
76 	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
77 		printk(KERN_WARNING "MT2266 I2C write failed\n");
78 		return -EREMOTEIO;
79 	}
80 	return 0;
81 }
82 
83 // Writes a set of consecutive registers
mt2266_writeregs(struct mt2266_priv * priv,u8 * buf,u8 len)84 static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
85 {
86 	struct i2c_msg msg = {
87 		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
88 	};
89 	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
90 		printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len);
91 		return -EREMOTEIO;
92 	}
93 	return 0;
94 }
95 
96 // Initialisation sequences
97 static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
98 				 0x00, 0x52, 0x99, 0x3f };
99 
100 static u8 mt2266_init2[] = {
101     0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4,
102     0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14,
103     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff,
104     0xff, 0x00, 0x77, 0x0f, 0x2d
105 };
106 
107 static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
108 						0x22, 0x22, 0x22, 0x22 };
109 
110 static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
111 						0x32, 0x32, 0x32, 0x32 };
112 
113 static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
114 						0xa7, 0xa7, 0xa7, 0xa7 };
115 
116 static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
117 			   0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
118 
119 static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
120 			   0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
121 
122 #define FREF 30000       // Quartz oscillator 30 MHz
123 
mt2266_set_params(struct dvb_frontend * fe,struct dvb_frontend_parameters * params)124 static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
125 {
126 	struct mt2266_priv *priv;
127 	int ret=0;
128 	u32 freq;
129 	u32 tune;
130 	u8  lnaband;
131 	u8  b[10];
132 	int i;
133 	u8 band;
134 
135 	priv = fe->tuner_priv;
136 
137 	freq = params->frequency / 1000; // Hz -> kHz
138 	if (freq < 470000 && freq > 230000)
139 		return -EINVAL; /* Gap between VHF and UHF bands */
140 	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
141 	priv->frequency = freq * 1000;
142 
143 	tune = 2 * freq * (8192/16) / (FREF/16);
144 	band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
145 	if (band == MT2266_VHF)
146 		tune *= 2;
147 
148 	switch (params->u.ofdm.bandwidth) {
149 	case BANDWIDTH_6_MHZ:
150 		mt2266_writeregs(priv, mt2266_init_6mhz,
151 				 sizeof(mt2266_init_6mhz));
152 		break;
153 	case BANDWIDTH_7_MHZ:
154 		mt2266_writeregs(priv, mt2266_init_7mhz,
155 				 sizeof(mt2266_init_7mhz));
156 		break;
157 	case BANDWIDTH_8_MHZ:
158 	default:
159 		mt2266_writeregs(priv, mt2266_init_8mhz,
160 				 sizeof(mt2266_init_8mhz));
161 		break;
162 	}
163 
164 	if (band == MT2266_VHF && priv->band == MT2266_UHF) {
165 		dprintk("Switch from UHF to VHF");
166 		mt2266_writereg(priv, 0x05, 0x04);
167 		mt2266_writereg(priv, 0x19, 0x61);
168 		mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
169 	} else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
170 		dprintk("Switch from VHF to UHF");
171 		mt2266_writereg(priv, 0x05, 0x52);
172 		mt2266_writereg(priv, 0x19, 0x61);
173 		mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
174 	}
175 	msleep(10);
176 
177 	if (freq <= 495000)
178 		lnaband = 0xEE;
179 	else if (freq <= 525000)
180 		lnaband = 0xDD;
181 	else if (freq <= 550000)
182 		lnaband = 0xCC;
183 	else if (freq <= 580000)
184 		lnaband = 0xBB;
185 	else if (freq <= 605000)
186 		lnaband = 0xAA;
187 	else if (freq <= 630000)
188 		lnaband = 0x99;
189 	else if (freq <= 655000)
190 		lnaband = 0x88;
191 	else if (freq <= 685000)
192 		lnaband = 0x77;
193 	else if (freq <= 710000)
194 		lnaband = 0x66;
195 	else if (freq <= 735000)
196 		lnaband = 0x55;
197 	else if (freq <= 765000)
198 		lnaband = 0x44;
199 	else if (freq <= 802000)
200 		lnaband = 0x33;
201 	else if (freq <= 840000)
202 		lnaband = 0x22;
203 	else
204 		lnaband = 0x11;
205 
206 	b[0] = REG_TUNE;
207 	b[1] = (tune >> 8) & 0x1F;
208 	b[2] = tune & 0xFF;
209 	b[3] = tune >> 13;
210 	mt2266_writeregs(priv,b,4);
211 
212 	dprintk("set_parms: tune=%d band=%d %s",
213 		(int) tune, (int) lnaband,
214 		(band == MT2266_UHF) ? "UHF" : "VHF");
215 	dprintk("set_parms: [1..3]: %2x %2x %2x",
216 		(int) b[1], (int) b[2], (int)b[3]);
217 
218 	if (band == MT2266_UHF) {
219 		b[0] = 0x05;
220 		b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
221 		b[2] = lnaband;
222 		mt2266_writeregs(priv, b, 3);
223 	}
224 
225 	/* Wait for pll lock or timeout */
226 	i = 0;
227 	do {
228 		mt2266_readreg(priv,REG_LOCK,b);
229 		if (b[0] & 0x40)
230 			break;
231 		msleep(10);
232 		i++;
233 	} while (i<10);
234 	dprintk("Lock when i=%i",(int)i);
235 
236 	if (band == MT2266_UHF && priv->band == MT2266_VHF)
237 		mt2266_writereg(priv, 0x05, 0x62);
238 
239 	priv->band = band;
240 
241 	return ret;
242 }
243 
mt2266_calibrate(struct mt2266_priv * priv)244 static void mt2266_calibrate(struct mt2266_priv *priv)
245 {
246 	mt2266_writereg(priv, 0x11, 0x03);
247 	mt2266_writereg(priv, 0x11, 0x01);
248 	mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
249 	mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
250 	mt2266_writereg(priv, 0x33, 0x5e);
251 	mt2266_writereg(priv, 0x10, 0x10);
252 	mt2266_writereg(priv, 0x10, 0x00);
253 	mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
254 	msleep(25);
255 	mt2266_writereg(priv, 0x17, 0x6d);
256 	mt2266_writereg(priv, 0x1c, 0x00);
257 	msleep(75);
258 	mt2266_writereg(priv, 0x17, 0x6d);
259 	mt2266_writereg(priv, 0x1c, 0xff);
260 }
261 
mt2266_get_frequency(struct dvb_frontend * fe,u32 * frequency)262 static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
263 {
264 	struct mt2266_priv *priv = fe->tuner_priv;
265 	*frequency = priv->frequency;
266 	return 0;
267 }
268 
mt2266_get_bandwidth(struct dvb_frontend * fe,u32 * bandwidth)269 static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
270 {
271 	struct mt2266_priv *priv = fe->tuner_priv;
272 	*bandwidth = priv->bandwidth;
273 	return 0;
274 }
275 
mt2266_init(struct dvb_frontend * fe)276 static int mt2266_init(struct dvb_frontend *fe)
277 {
278 	int ret;
279 	struct mt2266_priv *priv = fe->tuner_priv;
280 	ret = mt2266_writereg(priv, 0x17, 0x6d);
281 	if (ret < 0)
282 		return ret;
283 	ret = mt2266_writereg(priv, 0x1c, 0xff);
284 	if (ret < 0)
285 		return ret;
286 	return 0;
287 }
288 
mt2266_sleep(struct dvb_frontend * fe)289 static int mt2266_sleep(struct dvb_frontend *fe)
290 {
291 	struct mt2266_priv *priv = fe->tuner_priv;
292 	mt2266_writereg(priv, 0x17, 0x6d);
293 	mt2266_writereg(priv, 0x1c, 0x00);
294 	return 0;
295 }
296 
mt2266_release(struct dvb_frontend * fe)297 static int mt2266_release(struct dvb_frontend *fe)
298 {
299 	kfree(fe->tuner_priv);
300 	fe->tuner_priv = NULL;
301 	return 0;
302 }
303 
304 static const struct dvb_tuner_ops mt2266_tuner_ops = {
305 	.info = {
306 		.name           = "Microtune MT2266",
307 		.frequency_min  = 174000000,
308 		.frequency_max  = 862000000,
309 		.frequency_step =     50000,
310 	},
311 	.release       = mt2266_release,
312 	.init          = mt2266_init,
313 	.sleep         = mt2266_sleep,
314 	.set_params    = mt2266_set_params,
315 	.get_frequency = mt2266_get_frequency,
316 	.get_bandwidth = mt2266_get_bandwidth
317 };
318 
mt2266_attach(struct dvb_frontend * fe,struct i2c_adapter * i2c,struct mt2266_config * cfg)319 struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
320 {
321 	struct mt2266_priv *priv = NULL;
322 	u8 id = 0;
323 
324 	priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL);
325 	if (priv == NULL)
326 		return NULL;
327 
328 	priv->cfg      = cfg;
329 	priv->i2c      = i2c;
330 	priv->band     = MT2266_UHF;
331 
332 	if (mt2266_readreg(priv, 0, &id)) {
333 		kfree(priv);
334 		return NULL;
335 	}
336 	if (id != PART_REV) {
337 		kfree(priv);
338 		return NULL;
339 	}
340 	printk(KERN_INFO "MT2266: successfully identified\n");
341 	memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops));
342 
343 	fe->tuner_priv = priv;
344 	mt2266_calibrate(priv);
345 	return fe;
346 }
347 EXPORT_SYMBOL(mt2266_attach);
348 
349 MODULE_AUTHOR("Olivier DANET");
350 MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
351 MODULE_LICENSE("GPL");
352