blob: cbbd4d5e15da7c060eb5084977c60913310b8100 [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.
Antti Palosaari51ff2e22010-08-13 03:41:02 -030015 */
16
Antti Palosaari51ff2e22010-08-13 03:41:02 -030017#include "tda18218_priv.h"
18
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030019/* Max transfer size done by I2C transfer functions */
20#define MAX_XFER_SIZE 64
21
Antti Palosaari51ff2e22010-08-13 03:41:02 -030022/* write multiple registers */
23static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
24{
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030025 int ret = 0, len2, remaining;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030026 u8 buf[MAX_XFER_SIZE];
Antti Palosaari51ff2e22010-08-13 03:41:02 -030027 struct i2c_msg msg[1] = {
28 {
29 .addr = priv->cfg->i2c_address,
30 .flags = 0,
31 .buf = buf,
32 }
33 };
34
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030035 if (1 + len > sizeof(buf)) {
36 dev_warn(&priv->i2c->dev,
37 "%s: i2c wr reg=%04x: len=%d is too big!\n",
38 KBUILD_MODNAME, reg, len);
39 return -EINVAL;
40 }
41
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030042 for (remaining = len; remaining > 0;
43 remaining -= (priv->cfg->i2c_wr_max - 1)) {
44 len2 = remaining;
45 if (len2 > (priv->cfg->i2c_wr_max - 1))
46 len2 = (priv->cfg->i2c_wr_max - 1);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030047
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030048 msg[0].len = 1 + len2;
49 buf[0] = reg + len - remaining;
50 memcpy(&buf[1], &val[len - remaining], len2);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030051
52 ret = i2c_transfer(priv->i2c, msg, 1);
53 if (ret != 1)
54 break;
55 }
56
57 if (ret == 1) {
58 ret = 0;
59 } else {
Antti Palosaari9edd6982012-08-21 12:12:49 -030060 dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
61 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030062 ret = -EREMOTEIO;
63 }
64
65 return ret;
66}
67
68/* read multiple registers */
69static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
70{
71 int ret;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030072 u8 buf[MAX_XFER_SIZE]; /* we must start read always from reg 0x00 */
Antti Palosaari51ff2e22010-08-13 03:41:02 -030073 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,
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030082 .len = reg + len,
Antti Palosaari51ff2e22010-08-13 03:41:02 -030083 .buf = buf,
84 }
85 };
86
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030087 if (reg + len > sizeof(buf)) {
88 dev_warn(&priv->i2c->dev,
89 "%s: i2c wr reg=%04x: len=%d is too big!\n",
90 KBUILD_MODNAME, reg, len);
91 return -EINVAL;
92 }
93
Antti Palosaari51ff2e22010-08-13 03:41:02 -030094 ret = i2c_transfer(priv->i2c, msg, 2);
95 if (ret == 2) {
96 memcpy(val, &buf[reg], len);
97 ret = 0;
98 } else {
Antti Palosaari9edd6982012-08-21 12:12:49 -030099 dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
100 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300101 ret = -EREMOTEIO;
102 }
103
104 return ret;
105}
106
107/* write single register */
108static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
109{
110 return tda18218_wr_regs(priv, reg, &val, 1);
111}
112
113/* read single register */
114
115static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
116{
117 return tda18218_rd_regs(priv, reg, val, 1);
118}
119
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300120static int tda18218_set_params(struct dvb_frontend *fe)
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300121{
122 struct tda18218_priv *priv = fe->tuner_priv;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300123 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
124 u32 bw = c->bandwidth_hz;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300125 int ret;
126 u8 buf[3], i, BP_Filter, LP_Fc;
127 u32 LO_Frac;
128 /* TODO: find out correct AGC algorithm */
129 u8 agc[][2] = {
130 { R20_AGC11, 0x60 },
131 { R23_AGC21, 0x02 },
132 { R20_AGC11, 0xa0 },
133 { R23_AGC21, 0x09 },
134 { R20_AGC11, 0xe0 },
135 { R23_AGC21, 0x0c },
136 { R20_AGC11, 0x40 },
137 { R23_AGC21, 0x01 },
138 { R20_AGC11, 0x80 },
139 { R23_AGC21, 0x08 },
140 { R20_AGC11, 0xc0 },
141 { R23_AGC21, 0x0b },
142 { R24_AGC22, 0x1c },
143 { R24_AGC22, 0x0c },
144 };
145
146 if (fe->ops.i2c_gate_ctrl)
147 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
148
149 /* low-pass filter cut-off frequency */
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300150 if (bw <= 6000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300151 LP_Fc = 0;
Mauro Carvalho Chehabb4d48c92011-12-30 13:59:37 -0200152 priv->if_frequency = 3000000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300153 } else if (bw <= 7000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300154 LP_Fc = 1;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300155 priv->if_frequency = 3500000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300156 } else {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300157 LP_Fc = 2;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300158 priv->if_frequency = 4000000;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300159 }
160
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300161 LO_Frac = c->frequency + priv->if_frequency;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300162
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300163 /* band-pass filter */
164 if (LO_Frac < 188000000)
165 BP_Filter = 3;
166 else if (LO_Frac < 253000000)
167 BP_Filter = 4;
168 else if (LO_Frac < 343000000)
169 BP_Filter = 5;
170 else
171 BP_Filter = 6;
172
173 buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
174 buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
175 buf[2] = priv->regs[R1C_AGC2B];
176 ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
177 if (ret)
178 goto error;
179
180 buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
181 buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
182 buf[2] = (LO_Frac / 1000) << 4 |
183 (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
184 ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
185 if (ret)
186 goto error;
187
188 buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
189 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
190 if (ret)
191 goto error;
192
193 buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
194 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
195 if (ret)
196 goto error;
197
198 /* trigger AGC */
199 for (i = 0; i < ARRAY_SIZE(agc); i++) {
200 ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
201 if (ret)
202 goto error;
203 }
204
205error:
206 if (fe->ops.i2c_gate_ctrl)
207 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
208
209 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300210 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300211
212 return ret;
213}
214
Antti Palosaari522fdf72011-11-13 00:19:56 -0300215static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
216{
217 struct tda18218_priv *priv = fe->tuner_priv;
218 *frequency = priv->if_frequency;
Antti Palosaari9edd6982012-08-21 12:12:49 -0300219 dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d\n", __func__, *frequency);
Antti Palosaari522fdf72011-11-13 00:19:56 -0300220 return 0;
221}
222
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300223static int tda18218_sleep(struct dvb_frontend *fe)
224{
225 struct tda18218_priv *priv = fe->tuner_priv;
226 int ret;
227
228 if (fe->ops.i2c_gate_ctrl)
229 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
230
231 /* standby */
232 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
233
234 if (fe->ops.i2c_gate_ctrl)
235 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
236
237 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300238 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300239
240 return ret;
241}
242
243static int tda18218_init(struct dvb_frontend *fe)
244{
245 struct tda18218_priv *priv = fe->tuner_priv;
246 int ret;
247
248 /* TODO: calibrations */
249
250 if (fe->ops.i2c_gate_ctrl)
251 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
252
253 ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
254
255 if (fe->ops.i2c_gate_ctrl)
256 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
257
258 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300259 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300260
261 return ret;
262}
263
Mauro Carvalho Chehabf2709c22016-11-18 20:30:51 -0200264static void tda18218_release(struct dvb_frontend *fe)
265{
266 kfree(fe->tuner_priv);
267 fe->tuner_priv = NULL;
268}
269
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300270static const struct dvb_tuner_ops tda18218_tuner_ops = {
271 .info = {
Mauro Carvalho Chehaba3f90c72018-07-05 18:59:35 -0400272 .name = "NXP TDA18218",
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300273
Mauro Carvalho Chehaba3f90c72018-07-05 18:59:35 -0400274 .frequency_min_hz = 174 * MHz,
275 .frequency_max_hz = 864 * MHz,
276 .frequency_step_hz = 1 * kHz,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300277 },
278
Mauro Carvalho Chehabf2709c22016-11-18 20:30:51 -0200279 .release = tda18218_release,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300280 .init = tda18218_init,
281 .sleep = tda18218_sleep,
282
283 .set_params = tda18218_set_params,
Antti Palosaari522fdf72011-11-13 00:19:56 -0300284
285 .get_if_frequency = tda18218_get_if_frequency,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300286};
287
288struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
289 struct i2c_adapter *i2c, struct tda18218_config *cfg)
290{
291 struct tda18218_priv *priv = NULL;
Paul Bolleed2e3302012-11-01 17:00:09 -0300292 u8 val;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300293 int ret;
294 /* chip default registers values */
295 static u8 def_regs[] = {
296 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
297 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
298 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
299 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
300 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
301 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
302 };
303
304 priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
305 if (priv == NULL)
306 return NULL;
307
308 priv->cfg = cfg;
309 priv->i2c = i2c;
310 fe->tuner_priv = priv;
311
312 if (fe->ops.i2c_gate_ctrl)
313 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
314
315 /* check if the tuner is there */
316 ret = tda18218_rd_reg(priv, R00_ID, &val);
Paul Bolleed2e3302012-11-01 17:00:09 -0300317 if (!ret)
318 dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300319 if (ret || val != def_regs[R00_ID]) {
320 kfree(priv);
321 return NULL;
322 }
323
Antti Palosaari9edd6982012-08-21 12:12:49 -0300324 dev_info(&priv->i2c->dev,
325 "%s: NXP TDA18218HN successfully identified\n",
326 KBUILD_MODNAME);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300327
328 memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
329 sizeof(struct dvb_tuner_ops));
330 memcpy(priv->regs, def_regs, sizeof(def_regs));
331
332 /* loop-through enabled chip default register values */
333 if (priv->cfg->loop_through) {
334 priv->regs[R17_PD1] = 0xb0;
335 priv->regs[R18_PD2] = 0x59;
336 }
337
338 /* standby */
339 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
340 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300341 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300342
343 if (fe->ops.i2c_gate_ctrl)
344 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
345
346 return fe;
347}
348EXPORT_SYMBOL(tda18218_attach);
349
350MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
351MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
352MODULE_LICENSE("GPL");