blob: 4ed94646116fa8119f262be39f0e6b891017e874 [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Antti Palosaari51ff2e22010-08-13 03:41:02 -03002/*
3 * NXP TDA18218HN silicon tuner driver
4 *
5 * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
Antti Palosaari51ff2e22010-08-13 03:41:02 -03006 */
7
Antti Palosaari51ff2e22010-08-13 03:41:02 -03008#include "tda18218_priv.h"
9
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030010/* Max transfer size done by I2C transfer functions */
11#define MAX_XFER_SIZE 64
12
Antti Palosaari51ff2e22010-08-13 03:41:02 -030013/* write multiple registers */
14static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
15{
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030016 int ret = 0, len2, remaining;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030017 u8 buf[MAX_XFER_SIZE];
Antti Palosaari51ff2e22010-08-13 03:41:02 -030018 struct i2c_msg msg[1] = {
19 {
20 .addr = priv->cfg->i2c_address,
21 .flags = 0,
22 .buf = buf,
23 }
24 };
25
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030026 if (1 + len > sizeof(buf)) {
27 dev_warn(&priv->i2c->dev,
28 "%s: i2c wr reg=%04x: len=%d is too big!\n",
29 KBUILD_MODNAME, reg, len);
30 return -EINVAL;
31 }
32
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030033 for (remaining = len; remaining > 0;
34 remaining -= (priv->cfg->i2c_wr_max - 1)) {
35 len2 = remaining;
36 if (len2 > (priv->cfg->i2c_wr_max - 1))
37 len2 = (priv->cfg->i2c_wr_max - 1);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030038
Antti Palosaariaf3a07a2012-08-21 12:12:50 -030039 msg[0].len = 1 + len2;
40 buf[0] = reg + len - remaining;
41 memcpy(&buf[1], &val[len - remaining], len2);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030042
43 ret = i2c_transfer(priv->i2c, msg, 1);
44 if (ret != 1)
45 break;
46 }
47
48 if (ret == 1) {
49 ret = 0;
50 } else {
Antti Palosaari9edd6982012-08-21 12:12:49 -030051 dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
52 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030053 ret = -EREMOTEIO;
54 }
55
56 return ret;
57}
58
59/* read multiple registers */
60static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
61{
62 int ret;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030063 u8 buf[MAX_XFER_SIZE]; /* we must start read always from reg 0x00 */
Antti Palosaari51ff2e22010-08-13 03:41:02 -030064 struct i2c_msg msg[2] = {
65 {
66 .addr = priv->cfg->i2c_address,
67 .flags = 0,
68 .len = 1,
69 .buf = "\x00",
70 }, {
71 .addr = priv->cfg->i2c_address,
72 .flags = I2C_M_RD,
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030073 .len = reg + len,
Antti Palosaari51ff2e22010-08-13 03:41:02 -030074 .buf = buf,
75 }
76 };
77
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030078 if (reg + len > sizeof(buf)) {
79 dev_warn(&priv->i2c->dev,
80 "%s: i2c wr reg=%04x: len=%d is too big!\n",
81 KBUILD_MODNAME, reg, len);
82 return -EINVAL;
83 }
84
Antti Palosaari51ff2e22010-08-13 03:41:02 -030085 ret = i2c_transfer(priv->i2c, msg, 2);
86 if (ret == 2) {
87 memcpy(val, &buf[reg], len);
88 ret = 0;
89 } else {
Antti Palosaari9edd6982012-08-21 12:12:49 -030090 dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
91 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
Antti Palosaari51ff2e22010-08-13 03:41:02 -030092 ret = -EREMOTEIO;
93 }
94
95 return ret;
96}
97
98/* write single register */
99static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
100{
101 return tda18218_wr_regs(priv, reg, &val, 1);
102}
103
104/* read single register */
105
106static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
107{
108 return tda18218_rd_regs(priv, reg, val, 1);
109}
110
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300111static int tda18218_set_params(struct dvb_frontend *fe)
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300112{
113 struct tda18218_priv *priv = fe->tuner_priv;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300114 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
115 u32 bw = c->bandwidth_hz;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300116 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 */
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300141 if (bw <= 6000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300142 LP_Fc = 0;
Mauro Carvalho Chehabb4d48c92011-12-30 13:59:37 -0200143 priv->if_frequency = 3000000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300144 } else if (bw <= 7000000) {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300145 LP_Fc = 1;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300146 priv->if_frequency = 3500000;
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300147 } else {
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300148 LP_Fc = 2;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300149 priv->if_frequency = 4000000;
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300150 }
151
Mauro Carvalho Chehab67ccfe32011-12-21 07:47:27 -0300152 LO_Frac = c->frequency + priv->if_frequency;
Antti Palosaari522fdf72011-11-13 00:19:56 -0300153
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300154 /* band-pass filter */
155 if (LO_Frac < 188000000)
156 BP_Filter = 3;
157 else if (LO_Frac < 253000000)
158 BP_Filter = 4;
159 else if (LO_Frac < 343000000)
160 BP_Filter = 5;
161 else
162 BP_Filter = 6;
163
164 buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
165 buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
166 buf[2] = priv->regs[R1C_AGC2B];
167 ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
168 if (ret)
169 goto error;
170
171 buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
172 buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
173 buf[2] = (LO_Frac / 1000) << 4 |
174 (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
175 ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
176 if (ret)
177 goto error;
178
179 buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
180 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
181 if (ret)
182 goto error;
183
184 buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
185 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
186 if (ret)
187 goto error;
188
189 /* trigger AGC */
190 for (i = 0; i < ARRAY_SIZE(agc); i++) {
191 ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
192 if (ret)
193 goto error;
194 }
195
196error:
197 if (fe->ops.i2c_gate_ctrl)
198 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
199
200 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300201 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300202
203 return ret;
204}
205
Antti Palosaari522fdf72011-11-13 00:19:56 -0300206static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
207{
208 struct tda18218_priv *priv = fe->tuner_priv;
209 *frequency = priv->if_frequency;
Antti Palosaari9edd6982012-08-21 12:12:49 -0300210 dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d\n", __func__, *frequency);
Antti Palosaari522fdf72011-11-13 00:19:56 -0300211 return 0;
212}
213
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300214static int tda18218_sleep(struct dvb_frontend *fe)
215{
216 struct tda18218_priv *priv = fe->tuner_priv;
217 int ret;
218
219 if (fe->ops.i2c_gate_ctrl)
220 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
221
222 /* standby */
223 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
224
225 if (fe->ops.i2c_gate_ctrl)
226 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
227
228 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300229 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300230
231 return ret;
232}
233
234static int tda18218_init(struct dvb_frontend *fe)
235{
236 struct tda18218_priv *priv = fe->tuner_priv;
237 int ret;
238
239 /* TODO: calibrations */
240
241 if (fe->ops.i2c_gate_ctrl)
242 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
243
244 ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
245
246 if (fe->ops.i2c_gate_ctrl)
247 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
248
249 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300250 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300251
252 return ret;
253}
254
Mauro Carvalho Chehabf2709c22016-11-18 20:30:51 -0200255static void tda18218_release(struct dvb_frontend *fe)
256{
257 kfree(fe->tuner_priv);
258 fe->tuner_priv = NULL;
259}
260
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300261static const struct dvb_tuner_ops tda18218_tuner_ops = {
262 .info = {
Mauro Carvalho Chehaba3f90c72018-07-05 18:59:35 -0400263 .name = "NXP TDA18218",
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300264
Mauro Carvalho Chehaba3f90c72018-07-05 18:59:35 -0400265 .frequency_min_hz = 174 * MHz,
266 .frequency_max_hz = 864 * MHz,
267 .frequency_step_hz = 1 * kHz,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300268 },
269
Mauro Carvalho Chehabf2709c22016-11-18 20:30:51 -0200270 .release = tda18218_release,
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300271 .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;
Paul Bolleed2e3302012-11-01 17:00:09 -0300283 u8 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);
Paul Bolleed2e3302012-11-01 17:00:09 -0300308 if (!ret)
309 dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300310 if (ret || val != def_regs[R00_ID]) {
311 kfree(priv);
312 return NULL;
313 }
314
Antti Palosaari9edd6982012-08-21 12:12:49 -0300315 dev_info(&priv->i2c->dev,
316 "%s: NXP TDA18218HN successfully identified\n",
317 KBUILD_MODNAME);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300318
319 memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
320 sizeof(struct dvb_tuner_ops));
321 memcpy(priv->regs, def_regs, sizeof(def_regs));
322
323 /* loop-through enabled chip default register values */
324 if (priv->cfg->loop_through) {
325 priv->regs[R17_PD1] = 0xb0;
326 priv->regs[R18_PD2] = 0x59;
327 }
328
329 /* standby */
330 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
331 if (ret)
Antti Palosaari9edd6982012-08-21 12:12:49 -0300332 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
Antti Palosaari51ff2e22010-08-13 03:41:02 -0300333
334 if (fe->ops.i2c_gate_ctrl)
335 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
336
337 return fe;
338}
339EXPORT_SYMBOL(tda18218_attach);
340
341MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
342MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
343MODULE_LICENSE("GPL");