blob: c400440c5deaa958624b710b9bd42c3e8cdb6ff3 [file] [log] [blame]
Antti Palosaari51ff2e22010-08-13 03:41:02 -03001/*
2 * NXP TDA18218HN silicon tuner driver
3 *
4 * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
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
24static int debug;
25module_param(debug, int, 0644);
26MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
27
28/* write multiple registers */
29static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
30{
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030031 int ret = 0, len2, remaining;
32 u8 buf[1 + len];
Antti Palosaari51ff2e22010-08-13 03:41:02 -030033 struct i2c_msg msg[1] = {
34 {
35 .addr = priv->cfg->i2c_address,
36 .flags = 0,
37 .buf = buf,
38 }
39 };
40
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030041 for (remaining = len; remaining > 0;
42 remaining -= (priv->cfg->i2c_wr_max - 1)) {
43 len2 = remaining;
44 if (len2 > (priv->cfg->i2c_wr_max - 1))
45 len2 = (priv->cfg->i2c_wr_max - 1);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030046
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030047 msg[0].len = 1 + len2;
48 buf[0] = reg + len - remaining;
49 memcpy(&buf[1], &val[len - remaining], len2);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030050
51 ret = i2c_transfer(priv->i2c, msg, 1);
52 if (ret != 1)
53 break;
54 }
55
56 if (ret == 1) {
57 ret = 0;
58 } else {
59 warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
60 ret = -EREMOTEIO;
61 }
62
63 return ret;
64}
65
66/* read multiple registers */
67static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
68{
69 int ret;
70 u8 buf[reg+len]; /* we must start read always from reg 0x00 */
71 struct i2c_msg msg[2] = {
72 {
73 .addr = priv->cfg->i2c_address,
74 .flags = 0,
75 .len = 1,
76 .buf = "\x00",
77 }, {
78 .addr = priv->cfg->i2c_address,
79 .flags = I2C_M_RD,
80 .len = sizeof(buf),
81 .buf = buf,
82 }
83 };
84
85 ret = i2c_transfer(priv->i2c, msg, 2);
86 if (ret == 2) {
87 memcpy(val, &buf[reg], len);
88 ret = 0;
89 } else {
90 warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
91 ret = -EREMOTEIO;
92 }
93
94 return ret;
95}
96
97/* write single register */
98static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
99{
100 return tda18218_wr_regs(priv, reg, &val, 1);
101}
102
103/* read single register */
104
105static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
106{
107 return tda18218_rd_regs(priv, reg, val, 1);
108}
109
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300110static int tda18218_set_params(struct dvb_frontend *fe)
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300111{
112 struct tda18218_priv *priv = fe->tuner_priv;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300113 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
114 u32 bw = c->bandwidth_hz;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300115 int ret;
116 u8 buf[3], i, BP_Filter, LP_Fc;
117 u32 LO_Frac;
118 /* TODO: find out correct AGC algorithm */
119 u8 agc[][2] = {
120 { R20_AGC11, 0x60 },
121 { R23_AGC21, 0x02 },
122 { R20_AGC11, 0xa0 },
123 { R23_AGC21, 0x09 },
124 { R20_AGC11, 0xe0 },
125 { R23_AGC21, 0x0c },
126 { R20_AGC11, 0x40 },
127 { R23_AGC21, 0x01 },
128 { R20_AGC11, 0x80 },
129 { R23_AGC21, 0x08 },
130 { R20_AGC11, 0xc0 },
131 { R23_AGC21, 0x0b },
132 { R24_AGC22, 0x1c },
133 { R24_AGC22, 0x0c },
134 };
135
136 if (fe->ops.i2c_gate_ctrl)
137 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
138
139 /* low-pass filter cut-off frequency */
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300140 if (bw <= 6000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300141 LP_Fc = 0;
Mauro Carvalho Chehabb4d48c92011-12-30 13:59:37 -0200142 priv->if_frequency = 3000000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300143 } else if (bw <= 7000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300144 LP_Fc = 1;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300145 priv->if_frequency = 3500000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300146 } else {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300147 LP_Fc = 2;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300148 priv->if_frequency = 4000000;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300149 }
150
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300151 LO_Frac = c->frequency + priv->if_frequency;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300152
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300153 /* band-pass filter */
154 if (LO_Frac < 188000000)
155 BP_Filter = 3;
156 else if (LO_Frac < 253000000)
157 BP_Filter = 4;
158 else if (LO_Frac < 343000000)
159 BP_Filter = 5;
160 else
161 BP_Filter = 6;
162
163 buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
164 buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
165 buf[2] = priv->regs[R1C_AGC2B];
166 ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
167 if (ret)
168 goto error;
169
170 buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
171 buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
172 buf[2] = (LO_Frac / 1000) << 4 |
173 (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
174 ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
175 if (ret)
176 goto error;
177
178 buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
179 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
180 if (ret)
181 goto error;
182
183 buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
184 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
185 if (ret)
186 goto error;
187
188 /* trigger AGC */
189 for (i = 0; i < ARRAY_SIZE(agc); i++) {
190 ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
191 if (ret)
192 goto error;
193 }
194
195error:
196 if (fe->ops.i2c_gate_ctrl)
197 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
198
199 if (ret)
200 dbg("%s: failed ret:%d", __func__, ret);
201
202 return ret;
203}
204
Antti Palosaari522fdf72011-11-13 00:19:56 -0300205static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
206{
207 struct tda18218_priv *priv = fe->tuner_priv;
208 *frequency = priv->if_frequency;
209 dbg("%s: if=%d", __func__, *frequency);
210 return 0;
211}
212
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300213static int tda18218_sleep(struct dvb_frontend *fe)
214{
215 struct tda18218_priv *priv = fe->tuner_priv;
216 int ret;
217
218 if (fe->ops.i2c_gate_ctrl)
219 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
220
221 /* standby */
222 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
223
224 if (fe->ops.i2c_gate_ctrl)
225 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
226
227 if (ret)
228 dbg("%s: failed ret:%d", __func__, ret);
229
230 return ret;
231}
232
233static int tda18218_init(struct dvb_frontend *fe)
234{
235 struct tda18218_priv *priv = fe->tuner_priv;
236 int ret;
237
238 /* TODO: calibrations */
239
240 if (fe->ops.i2c_gate_ctrl)
241 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
242
243 ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
244
245 if (fe->ops.i2c_gate_ctrl)
246 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
247
248 if (ret)
249 dbg("%s: failed ret:%d", __func__, ret);
250
251 return ret;
252}
253
254static int tda18218_release(struct dvb_frontend *fe)
255{
256 kfree(fe->tuner_priv);
257 fe->tuner_priv = NULL;
258 return 0;
259}
260
261static const struct dvb_tuner_ops tda18218_tuner_ops = {
262 .info = {
263 .name = "NXP TDA18218",
264
265 .frequency_min = 174000000,
266 .frequency_max = 864000000,
267 .frequency_step = 1000,
268 },
269
270 .release = tda18218_release,
271 .init = tda18218_init,
272 .sleep = tda18218_sleep,
273
274 .set_params = tda18218_set_params,
Antti Palosaari522fdf72011-11-13 00:19:56 -0300275
276 .get_if_frequency = tda18218_get_if_frequency,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300277};
278
279struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
280 struct i2c_adapter *i2c, struct tda18218_config *cfg)
281{
282 struct tda18218_priv *priv = NULL;
Antti Palosaarie0e52d42012-08-09 20:50:37 -0300283 u8 uninitialized_var(val);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300284 int ret;
285 /* chip default registers values */
286 static u8 def_regs[] = {
287 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
288 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
289 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
290 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
291 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
292 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
293 };
294
295 priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
296 if (priv == NULL)
297 return NULL;
298
299 priv->cfg = cfg;
300 priv->i2c = i2c;
301 fe->tuner_priv = priv;
302
303 if (fe->ops.i2c_gate_ctrl)
304 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
305
306 /* check if the tuner is there */
307 ret = tda18218_rd_reg(priv, R00_ID, &val);
308 dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
309 if (ret || val != def_regs[R00_ID]) {
310 kfree(priv);
311 return NULL;
312 }
313
314 info("NXP TDA18218HN successfully identified.");
315
316 memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
317 sizeof(struct dvb_tuner_ops));
318 memcpy(priv->regs, def_regs, sizeof(def_regs));
319
320 /* loop-through enabled chip default register values */
321 if (priv->cfg->loop_through) {
322 priv->regs[R17_PD1] = 0xb0;
323 priv->regs[R18_PD2] = 0x59;
324 }
325
326 /* standby */
327 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
328 if (ret)
329 dbg("%s: failed ret:%d", __func__, ret);
330
331 if (fe->ops.i2c_gate_ctrl)
332 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
333
334 return fe;
335}
336EXPORT_SYMBOL(tda18218_attach);
337
338MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
339MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
340MODULE_LICENSE("GPL");