• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/media/common/tuners/
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, struct dvb_frontend_parameters *params)
126{
127	struct mt2266_priv *priv;
128	int ret=0;
129	u32 freq;
130	u32 tune;
131	u8  lnaband;
132	u8  b[10];
133	int i;
134	u8 band;
135
136	priv = fe->tuner_priv;
137
138	freq = params->frequency / 1000; // Hz -> kHz
139	if (freq < 470000 && freq > 230000)
140		return -EINVAL; /* Gap between VHF and UHF bands */
141	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
142	priv->frequency = freq * 1000;
143
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 (params->u.ofdm.bandwidth) {
150	case BANDWIDTH_6_MHZ:
151		mt2266_writeregs(priv, mt2266_init_6mhz,
152				 sizeof(mt2266_init_6mhz));
153		break;
154	case BANDWIDTH_7_MHZ:
155		mt2266_writeregs(priv, mt2266_init_7mhz,
156				 sizeof(mt2266_init_7mhz));
157		break;
158	case BANDWIDTH_8_MHZ:
159	default:
160		mt2266_writeregs(priv, mt2266_init_8mhz,
161				 sizeof(mt2266_init_8mhz));
162		break;
163	}
164
165	if (band == MT2266_VHF && priv->band == MT2266_UHF) {
166		dprintk("Switch from UHF to VHF");
167		mt2266_writereg(priv, 0x05, 0x04);
168		mt2266_writereg(priv, 0x19, 0x61);
169		mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
170	} else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
171		dprintk("Switch from VHF to UHF");
172		mt2266_writereg(priv, 0x05, 0x52);
173		mt2266_writereg(priv, 0x19, 0x61);
174		mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
175	}
176	msleep(10);
177
178	if (freq <= 495000)
179		lnaband = 0xEE;
180	else if (freq <= 525000)
181		lnaband = 0xDD;
182	else if (freq <= 550000)
183		lnaband = 0xCC;
184	else if (freq <= 580000)
185		lnaband = 0xBB;
186	else if (freq <= 605000)
187		lnaband = 0xAA;
188	else if (freq <= 630000)
189		lnaband = 0x99;
190	else if (freq <= 655000)
191		lnaband = 0x88;
192	else if (freq <= 685000)
193		lnaband = 0x77;
194	else if (freq <= 710000)
195		lnaband = 0x66;
196	else if (freq <= 735000)
197		lnaband = 0x55;
198	else if (freq <= 765000)
199		lnaband = 0x44;
200	else if (freq <= 802000)
201		lnaband = 0x33;
202	else if (freq <= 840000)
203		lnaband = 0x22;
204	else
205		lnaband = 0x11;
206
207	b[0] = REG_TUNE;
208	b[1] = (tune >> 8) & 0x1F;
209	b[2] = tune & 0xFF;
210	b[3] = tune >> 13;
211	mt2266_writeregs(priv,b,4);
212
213	dprintk("set_parms: tune=%d band=%d %s",
214		(int) tune, (int) lnaband,
215		(band == MT2266_UHF) ? "UHF" : "VHF");
216	dprintk("set_parms: [1..3]: %2x %2x %2x",
217		(int) b[1], (int) b[2], (int)b[3]);
218
219	if (band == MT2266_UHF) {
220		b[0] = 0x05;
221		b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
222		b[2] = lnaband;
223		mt2266_writeregs(priv, b, 3);
224	}
225
226	/* Wait for pll lock or timeout */
227	i = 0;
228	do {
229		mt2266_readreg(priv,REG_LOCK,b);
230		if (b[0] & 0x40)
231			break;
232		msleep(10);
233		i++;
234	} while (i<10);
235	dprintk("Lock when i=%i",(int)i);
236
237	if (band == MT2266_UHF && priv->band == MT2266_VHF)
238		mt2266_writereg(priv, 0x05, 0x62);
239
240	priv->band = band;
241
242	return ret;
243}
244
245static void mt2266_calibrate(struct mt2266_priv *priv)
246{
247	mt2266_writereg(priv, 0x11, 0x03);
248	mt2266_writereg(priv, 0x11, 0x01);
249	mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
250	mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
251	mt2266_writereg(priv, 0x33, 0x5e);
252	mt2266_writereg(priv, 0x10, 0x10);
253	mt2266_writereg(priv, 0x10, 0x00);
254	mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
255	msleep(25);
256	mt2266_writereg(priv, 0x17, 0x6d);
257	mt2266_writereg(priv, 0x1c, 0x00);
258	msleep(75);
259	mt2266_writereg(priv, 0x17, 0x6d);
260	mt2266_writereg(priv, 0x1c, 0xff);
261}
262
263static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
264{
265	struct mt2266_priv *priv = fe->tuner_priv;
266	*frequency = priv->frequency;
267	return 0;
268}
269
270static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
271{
272	struct mt2266_priv *priv = fe->tuner_priv;
273	*bandwidth = priv->bandwidth;
274	return 0;
275}
276
277static int mt2266_init(struct dvb_frontend *fe)
278{
279	int ret;
280	struct mt2266_priv *priv = fe->tuner_priv;
281	ret = mt2266_writereg(priv, 0x17, 0x6d);
282	if (ret < 0)
283		return ret;
284	ret = mt2266_writereg(priv, 0x1c, 0xff);
285	if (ret < 0)
286		return ret;
287	return 0;
288}
289
290static int mt2266_sleep(struct dvb_frontend *fe)
291{
292	struct mt2266_priv *priv = fe->tuner_priv;
293	mt2266_writereg(priv, 0x17, 0x6d);
294	mt2266_writereg(priv, 0x1c, 0x00);
295	return 0;
296}
297
298static int mt2266_release(struct dvb_frontend *fe)
299{
300	kfree(fe->tuner_priv);
301	fe->tuner_priv = NULL;
302	return 0;
303}
304
305static 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
320struct 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}
348EXPORT_SYMBOL(mt2266_attach);
349
350MODULE_AUTHOR("Olivier DANET");
351MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
352MODULE_LICENSE("GPL");
353