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
36struct 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
50static int debug;
51module_param(debug, int, 0644);
52MODULE_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
57static 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
71static 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
85static 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
98static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
99				 0x00, 0x52, 0x99, 0x3f };
100
101static 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
108static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
109						0x22, 0x22, 0x22, 0x22 };
110
111static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
112						0x32, 0x32, 0x32, 0x32 };
113
114static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
115						0xa7, 0xa7, 0xa7, 0xa7 };
116
117static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
118			   0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
119
120static 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
125static 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
246static 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
264static 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
271static 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
278static 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
291static 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
299static int mt2266_release(struct dvb_frontend *fe)
300{
301	kfree(fe->tuner_priv);
302	fe->tuner_priv = NULL;
303	return 0;
304}
305
306static const struct dvb_tuner_ops mt2266_tuner_ops = {
307	.info = {
308		.name           = "Microtune MT2266",
309		.frequency_min  = 174000000,
310		.frequency_max  = 862000000,
311		.frequency_step =     50000,
312	},
313	.release       = mt2266_release,
314	.init          = mt2266_init,
315	.sleep         = mt2266_sleep,
316	.set_params    = mt2266_set_params,
317	.get_frequency = mt2266_get_frequency,
318	.get_bandwidth = mt2266_get_bandwidth
319};
320
321struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
322{
323	struct mt2266_priv *priv = NULL;
324	u8 id = 0;
325
326	priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL);
327	if (priv == NULL)
328		return NULL;
329
330	priv->cfg      = cfg;
331	priv->i2c      = i2c;
332	priv->band     = MT2266_UHF;
333
334	if (mt2266_readreg(priv, 0, &id)) {
335		kfree(priv);
336		return NULL;
337	}
338	if (id != PART_REV) {
339		kfree(priv);
340		return NULL;
341	}
342	printk(KERN_INFO "MT2266: successfully identified\n");
343	memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops));
344
345	fe->tuner_priv = priv;
346	mt2266_calibrate(priv);
347	return fe;
348}
349EXPORT_SYMBOL(mt2266_attach);
350
351MODULE_AUTHOR("Olivier DANET");
352MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
353MODULE_LICENSE("GPL");
354