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