2
* Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
4
* Copyright (c) 2007 Olivier DANET <odanet@caramail.com>
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.
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.
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>
23
#include "dvb_frontend.h"
26
#define I2C_ADDRESS 0x60
28
#define REG_PART_REV 0
31
#define REG_BANDWIDTH 8
37
struct mt2266_config *cfg;
38
struct i2c_adapter *i2c;
48
/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
51
module_param(debug, int, 0644);
52
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
54
#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0)
56
// Reads a single register
57
static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val)
59
struct i2c_msg msg[2] = {
60
{ .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 },
61
{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 },
63
if (i2c_transfer(priv->i2c, msg, 2) != 2) {
64
printk(KERN_WARNING "MT2266 I2C read failed\n");
70
// Writes a single register
71
static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val)
73
u8 buf[2] = { reg, val };
74
struct i2c_msg msg = {
75
.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
77
if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
78
printk(KERN_WARNING "MT2266 I2C write failed\n");
84
// Writes a set of consecutive registers
85
static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
87
struct i2c_msg msg = {
88
.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
90
if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
91
printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len);
97
// Initialisation sequences
98
static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
99
0x00, 0x52, 0x99, 0x3f };
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
108
static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
109
0x22, 0x22, 0x22, 0x22 };
111
static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
112
0x32, 0x32, 0x32, 0x32 };
114
static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
115
0xa7, 0xa7, 0xa7, 0xa7 };
117
static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
118
0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
120
static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
121
0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
123
#define FREF 30000 // Quartz oscillator 30 MHz
125
static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
127
struct mt2266_priv *priv;
136
priv = fe->tuner_priv;
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;
144
tune = 2 * freq * (8192/16) / (FREF/16);
145
band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
146
if (band == MT2266_VHF)
149
switch (params->u.ofdm.bandwidth) {
150
case BANDWIDTH_6_MHZ:
151
mt2266_writeregs(priv, mt2266_init_6mhz,
152
sizeof(mt2266_init_6mhz));
154
case BANDWIDTH_7_MHZ:
155
mt2266_writeregs(priv, mt2266_init_7mhz,
156
sizeof(mt2266_init_7mhz));
158
case BANDWIDTH_8_MHZ:
160
mt2266_writeregs(priv, mt2266_init_8mhz,
161
sizeof(mt2266_init_8mhz));
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));
180
else if (freq <= 525000)
182
else if (freq <= 550000)
184
else if (freq <= 580000)
186
else if (freq <= 605000)
188
else if (freq <= 630000)
190
else if (freq <= 655000)
192
else if (freq <= 685000)
194
else if (freq <= 710000)
196
else if (freq <= 735000)
198
else if (freq <= 765000)
200
else if (freq <= 802000)
202
else if (freq <= 840000)
208
b[1] = (tune >> 8) & 0x1F;
211
mt2266_writeregs(priv,b,4);
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]);
219
if (band == MT2266_UHF) {
221
b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
223
mt2266_writeregs(priv, b, 3);
226
/* Wait for pll lock or timeout */
229
mt2266_readreg(priv,REG_LOCK,b);
235
dprintk("Lock when i=%i",(int)i);
237
if (band == MT2266_UHF && priv->band == MT2266_VHF)
238
mt2266_writereg(priv, 0x05, 0x62);
245
static void mt2266_calibrate(struct mt2266_priv *priv)
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));
256
mt2266_writereg(priv, 0x17, 0x6d);
257
mt2266_writereg(priv, 0x1c, 0x00);
259
mt2266_writereg(priv, 0x17, 0x6d);
260
mt2266_writereg(priv, 0x1c, 0xff);
263
static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
265
struct mt2266_priv *priv = fe->tuner_priv;
266
*frequency = priv->frequency;
270
static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
272
struct mt2266_priv *priv = fe->tuner_priv;
273
*bandwidth = priv->bandwidth;
277
static int mt2266_init(struct dvb_frontend *fe)
280
struct mt2266_priv *priv = fe->tuner_priv;
281
ret = mt2266_writereg(priv, 0x17, 0x6d);
284
ret = mt2266_writereg(priv, 0x1c, 0xff);
290
static int mt2266_sleep(struct dvb_frontend *fe)
292
struct mt2266_priv *priv = fe->tuner_priv;
293
mt2266_writereg(priv, 0x17, 0x6d);
294
mt2266_writereg(priv, 0x1c, 0x00);
298
static int mt2266_release(struct dvb_frontend *fe)
300
kfree(fe->tuner_priv);
301
fe->tuner_priv = NULL;
305
static const struct dvb_tuner_ops mt2266_tuner_ops = {
307
.name = "Microtune MT2266",
308
.frequency_min = 174000000,
309
.frequency_max = 862000000,
310
.frequency_step = 50000,
312
.release = mt2266_release,
314
.sleep = mt2266_sleep,
315
.set_params = mt2266_set_params,
316
.get_frequency = mt2266_get_frequency,
317
.get_bandwidth = mt2266_get_bandwidth
320
struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
322
struct mt2266_priv *priv = NULL;
325
priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL);
331
priv->band = MT2266_UHF;
333
if (mt2266_readreg(priv, 0, &id)) {
337
if (id != PART_REV) {
341
printk(KERN_INFO "MT2266: successfully identified\n");
342
memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops));
344
fe->tuner_priv = priv;
345
mt2266_calibrate(priv);
348
EXPORT_SYMBOL(mt2266_attach);
350
MODULE_AUTHOR("Olivier DANET");
351
MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
352
MODULE_LICENSE("GPL");