Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/common/tuners/mt2266.c
15112 views
1
/*
2
* Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
3
*
4
* Copyright (c) 2007 Olivier DANET <[email protected]>
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, 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
245
static 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
263
static 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
270
static 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
277
static 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
290
static 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
298
static int mt2266_release(struct dvb_frontend *fe)
299
{
300
kfree(fe->tuner_priv);
301
fe->tuner_priv = NULL;
302
return 0;
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
354