Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/common/tuners/tda18218.c
15112 views
1
/*
2
* NXP TDA18218HN silicon tuner driver
3
*
4
* Copyright (C) 2010 Antti Palosaari <[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
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
*/
20
21
#include "tda18218.h"
22
#include "tda18218_priv.h"
23
24
static int debug;
25
module_param(debug, int, 0644);
26
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
27
28
/* write multiple registers */
29
static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
30
{
31
int ret = 0;
32
u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;
33
struct i2c_msg msg[1] = {
34
{
35
.addr = priv->cfg->i2c_address,
36
.flags = 0,
37
.buf = buf,
38
}
39
};
40
41
msg_len_max = priv->cfg->i2c_wr_max - 1;
42
quotient = len / msg_len_max;
43
remainder = len % msg_len_max;
44
msg_len = msg_len_max;
45
for (i = 0; (i <= quotient && remainder); i++) {
46
if (i == quotient) /* set len of the last msg */
47
msg_len = remainder;
48
49
msg[0].len = msg_len + 1;
50
buf[0] = reg + i * msg_len_max;
51
memcpy(&buf[1], &val[i * msg_len_max], msg_len);
52
53
ret = i2c_transfer(priv->i2c, msg, 1);
54
if (ret != 1)
55
break;
56
}
57
58
if (ret == 1) {
59
ret = 0;
60
} else {
61
warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
62
ret = -EREMOTEIO;
63
}
64
65
return ret;
66
}
67
68
/* read multiple registers */
69
static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
70
{
71
int ret;
72
u8 buf[reg+len]; /* we must start read always from reg 0x00 */
73
struct i2c_msg msg[2] = {
74
{
75
.addr = priv->cfg->i2c_address,
76
.flags = 0,
77
.len = 1,
78
.buf = "\x00",
79
}, {
80
.addr = priv->cfg->i2c_address,
81
.flags = I2C_M_RD,
82
.len = sizeof(buf),
83
.buf = buf,
84
}
85
};
86
87
ret = i2c_transfer(priv->i2c, msg, 2);
88
if (ret == 2) {
89
memcpy(val, &buf[reg], len);
90
ret = 0;
91
} else {
92
warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
93
ret = -EREMOTEIO;
94
}
95
96
return ret;
97
}
98
99
/* write single register */
100
static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
101
{
102
return tda18218_wr_regs(priv, reg, &val, 1);
103
}
104
105
/* read single register */
106
107
static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
108
{
109
return tda18218_rd_regs(priv, reg, val, 1);
110
}
111
112
static int tda18218_set_params(struct dvb_frontend *fe,
113
struct dvb_frontend_parameters *params)
114
{
115
struct tda18218_priv *priv = fe->tuner_priv;
116
int ret;
117
u8 buf[3], i, BP_Filter, LP_Fc;
118
u32 LO_Frac;
119
/* TODO: find out correct AGC algorithm */
120
u8 agc[][2] = {
121
{ R20_AGC11, 0x60 },
122
{ R23_AGC21, 0x02 },
123
{ R20_AGC11, 0xa0 },
124
{ R23_AGC21, 0x09 },
125
{ R20_AGC11, 0xe0 },
126
{ R23_AGC21, 0x0c },
127
{ R20_AGC11, 0x40 },
128
{ R23_AGC21, 0x01 },
129
{ R20_AGC11, 0x80 },
130
{ R23_AGC21, 0x08 },
131
{ R20_AGC11, 0xc0 },
132
{ R23_AGC21, 0x0b },
133
{ R24_AGC22, 0x1c },
134
{ R24_AGC22, 0x0c },
135
};
136
137
if (fe->ops.i2c_gate_ctrl)
138
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
139
140
/* low-pass filter cut-off frequency */
141
switch (params->u.ofdm.bandwidth) {
142
case BANDWIDTH_6_MHZ:
143
LP_Fc = 0;
144
LO_Frac = params->frequency + 4000000;
145
break;
146
case BANDWIDTH_7_MHZ:
147
LP_Fc = 1;
148
LO_Frac = params->frequency + 3500000;
149
break;
150
case BANDWIDTH_8_MHZ:
151
default:
152
LP_Fc = 2;
153
LO_Frac = params->frequency + 4000000;
154
break;
155
}
156
157
/* band-pass filter */
158
if (LO_Frac < 188000000)
159
BP_Filter = 3;
160
else if (LO_Frac < 253000000)
161
BP_Filter = 4;
162
else if (LO_Frac < 343000000)
163
BP_Filter = 5;
164
else
165
BP_Filter = 6;
166
167
buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
168
buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
169
buf[2] = priv->regs[R1C_AGC2B];
170
ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
171
if (ret)
172
goto error;
173
174
buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
175
buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
176
buf[2] = (LO_Frac / 1000) << 4 |
177
(priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
178
ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
179
if (ret)
180
goto error;
181
182
buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
183
ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
184
if (ret)
185
goto error;
186
187
buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
188
ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
189
if (ret)
190
goto error;
191
192
/* trigger AGC */
193
for (i = 0; i < ARRAY_SIZE(agc); i++) {
194
ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
195
if (ret)
196
goto error;
197
}
198
199
error:
200
if (fe->ops.i2c_gate_ctrl)
201
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
202
203
if (ret)
204
dbg("%s: failed ret:%d", __func__, ret);
205
206
return ret;
207
}
208
209
static int tda18218_sleep(struct dvb_frontend *fe)
210
{
211
struct tda18218_priv *priv = fe->tuner_priv;
212
int ret;
213
214
if (fe->ops.i2c_gate_ctrl)
215
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
216
217
/* standby */
218
ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
219
220
if (fe->ops.i2c_gate_ctrl)
221
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
222
223
if (ret)
224
dbg("%s: failed ret:%d", __func__, ret);
225
226
return ret;
227
}
228
229
static int tda18218_init(struct dvb_frontend *fe)
230
{
231
struct tda18218_priv *priv = fe->tuner_priv;
232
int ret;
233
234
/* TODO: calibrations */
235
236
if (fe->ops.i2c_gate_ctrl)
237
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
238
239
ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
240
241
if (fe->ops.i2c_gate_ctrl)
242
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
243
244
if (ret)
245
dbg("%s: failed ret:%d", __func__, ret);
246
247
return ret;
248
}
249
250
static int tda18218_release(struct dvb_frontend *fe)
251
{
252
kfree(fe->tuner_priv);
253
fe->tuner_priv = NULL;
254
return 0;
255
}
256
257
static const struct dvb_tuner_ops tda18218_tuner_ops = {
258
.info = {
259
.name = "NXP TDA18218",
260
261
.frequency_min = 174000000,
262
.frequency_max = 864000000,
263
.frequency_step = 1000,
264
},
265
266
.release = tda18218_release,
267
.init = tda18218_init,
268
.sleep = tda18218_sleep,
269
270
.set_params = tda18218_set_params,
271
};
272
273
struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
274
struct i2c_adapter *i2c, struct tda18218_config *cfg)
275
{
276
struct tda18218_priv *priv = NULL;
277
u8 val;
278
int ret;
279
/* chip default registers values */
280
static u8 def_regs[] = {
281
0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
282
0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
283
0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
284
0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
285
0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
286
0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
287
};
288
289
priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
290
if (priv == NULL)
291
return NULL;
292
293
priv->cfg = cfg;
294
priv->i2c = i2c;
295
fe->tuner_priv = priv;
296
297
if (fe->ops.i2c_gate_ctrl)
298
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
299
300
/* check if the tuner is there */
301
ret = tda18218_rd_reg(priv, R00_ID, &val);
302
dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
303
if (ret || val != def_regs[R00_ID]) {
304
kfree(priv);
305
return NULL;
306
}
307
308
info("NXP TDA18218HN successfully identified.");
309
310
memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
311
sizeof(struct dvb_tuner_ops));
312
memcpy(priv->regs, def_regs, sizeof(def_regs));
313
314
/* loop-through enabled chip default register values */
315
if (priv->cfg->loop_through) {
316
priv->regs[R17_PD1] = 0xb0;
317
priv->regs[R18_PD2] = 0x59;
318
}
319
320
/* standby */
321
ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
322
if (ret)
323
dbg("%s: failed ret:%d", __func__, ret);
324
325
if (fe->ops.i2c_gate_ctrl)
326
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
327
328
return fe;
329
}
330
EXPORT_SYMBOL(tda18218_attach);
331
332
MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
333
MODULE_AUTHOR("Antti Palosaari <[email protected]>");
334
MODULE_LICENSE("GPL");
335
336