116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ccae7af2SMauro Carvalho Chehab /*
3ccae7af2SMauro Carvalho Chehab * NXP TDA18212HN silicon tuner driver
4ccae7af2SMauro Carvalho Chehab *
5ccae7af2SMauro Carvalho Chehab * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
6ccae7af2SMauro Carvalho Chehab */
7ccae7af2SMauro Carvalho Chehab
8ccae7af2SMauro Carvalho Chehab #include "tda18212.h"
93b60b761SAntti Palosaari #include <linux/regmap.h>
10f1baab87SMauro Carvalho Chehab
11e4a42e18SAntti Palosaari struct tda18212_dev {
120e584cc2SAntti Palosaari struct tda18212_config cfg;
130e584cc2SAntti Palosaari struct i2c_client *client;
143b60b761SAntti Palosaari struct regmap *regmap;
15ccae7af2SMauro Carvalho Chehab
16ccae7af2SMauro Carvalho Chehab u32 if_frequency;
17ccae7af2SMauro Carvalho Chehab };
18ccae7af2SMauro Carvalho Chehab
tda18212_set_params(struct dvb_frontend * fe)19ccae7af2SMauro Carvalho Chehab static int tda18212_set_params(struct dvb_frontend *fe)
20ccae7af2SMauro Carvalho Chehab {
21e4a42e18SAntti Palosaari struct tda18212_dev *dev = fe->tuner_priv;
22ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache;
23ccae7af2SMauro Carvalho Chehab int ret, i;
24ccae7af2SMauro Carvalho Chehab u32 if_khz;
25ccae7af2SMauro Carvalho Chehab u8 buf[9];
26ccae7af2SMauro Carvalho Chehab #define DVBT_6 0
27ccae7af2SMauro Carvalho Chehab #define DVBT_7 1
28ccae7af2SMauro Carvalho Chehab #define DVBT_8 2
29ccae7af2SMauro Carvalho Chehab #define DVBT2_6 3
30ccae7af2SMauro Carvalho Chehab #define DVBT2_7 4
31ccae7af2SMauro Carvalho Chehab #define DVBT2_8 5
32ccae7af2SMauro Carvalho Chehab #define DVBC_6 6
33ccae7af2SMauro Carvalho Chehab #define DVBC_8 7
346cff36b2SMauro Carvalho Chehab #define ATSC_VSB 8
356cff36b2SMauro Carvalho Chehab #define ATSC_QAM 9
36ccae7af2SMauro Carvalho Chehab static const u8 bw_params[][3] = {
37ccae7af2SMauro Carvalho Chehab /* reg: 0f 13 23 */
38ccae7af2SMauro Carvalho Chehab [DVBT_6] = { 0xb3, 0x20, 0x03 },
39ccae7af2SMauro Carvalho Chehab [DVBT_7] = { 0xb3, 0x31, 0x01 },
40ccae7af2SMauro Carvalho Chehab [DVBT_8] = { 0xb3, 0x22, 0x01 },
41ccae7af2SMauro Carvalho Chehab [DVBT2_6] = { 0xbc, 0x20, 0x03 },
42ccae7af2SMauro Carvalho Chehab [DVBT2_7] = { 0xbc, 0x72, 0x03 },
43ccae7af2SMauro Carvalho Chehab [DVBT2_8] = { 0xbc, 0x22, 0x01 },
44ccae7af2SMauro Carvalho Chehab [DVBC_6] = { 0x92, 0x50, 0x03 },
45ccae7af2SMauro Carvalho Chehab [DVBC_8] = { 0x92, 0x53, 0x03 },
466cff36b2SMauro Carvalho Chehab [ATSC_VSB] = { 0x7d, 0x20, 0x63 },
476cff36b2SMauro Carvalho Chehab [ATSC_QAM] = { 0x7d, 0x20, 0x63 },
48ccae7af2SMauro Carvalho Chehab };
49ccae7af2SMauro Carvalho Chehab
50e4a42e18SAntti Palosaari dev_dbg(&dev->client->dev,
51bdb32655SAntti Palosaari "delivery_system=%d frequency=%d bandwidth_hz=%d\n",
52bdb32655SAntti Palosaari c->delivery_system, c->frequency,
53ccae7af2SMauro Carvalho Chehab c->bandwidth_hz);
54ccae7af2SMauro Carvalho Chehab
55ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl)
56ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
57ccae7af2SMauro Carvalho Chehab
58ccae7af2SMauro Carvalho Chehab switch (c->delivery_system) {
596cff36b2SMauro Carvalho Chehab case SYS_ATSC:
60e4a42e18SAntti Palosaari if_khz = dev->cfg.if_atsc_vsb;
616cff36b2SMauro Carvalho Chehab i = ATSC_VSB;
626cff36b2SMauro Carvalho Chehab break;
636cff36b2SMauro Carvalho Chehab case SYS_DVBC_ANNEX_B:
64e4a42e18SAntti Palosaari if_khz = dev->cfg.if_atsc_qam;
656cff36b2SMauro Carvalho Chehab i = ATSC_QAM;
666cff36b2SMauro Carvalho Chehab break;
67ccae7af2SMauro Carvalho Chehab case SYS_DVBT:
68ccae7af2SMauro Carvalho Chehab switch (c->bandwidth_hz) {
69ccae7af2SMauro Carvalho Chehab case 6000000:
70e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt_6;
71ccae7af2SMauro Carvalho Chehab i = DVBT_6;
72ccae7af2SMauro Carvalho Chehab break;
73ccae7af2SMauro Carvalho Chehab case 7000000:
74e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt_7;
75ccae7af2SMauro Carvalho Chehab i = DVBT_7;
76ccae7af2SMauro Carvalho Chehab break;
77ccae7af2SMauro Carvalho Chehab case 8000000:
78e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt_8;
79ccae7af2SMauro Carvalho Chehab i = DVBT_8;
80ccae7af2SMauro Carvalho Chehab break;
81ccae7af2SMauro Carvalho Chehab default:
82ccae7af2SMauro Carvalho Chehab ret = -EINVAL;
83ccae7af2SMauro Carvalho Chehab goto error;
84ccae7af2SMauro Carvalho Chehab }
85ccae7af2SMauro Carvalho Chehab break;
86ccae7af2SMauro Carvalho Chehab case SYS_DVBT2:
87ccae7af2SMauro Carvalho Chehab switch (c->bandwidth_hz) {
88ccae7af2SMauro Carvalho Chehab case 6000000:
89e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt2_6;
90ccae7af2SMauro Carvalho Chehab i = DVBT2_6;
91ccae7af2SMauro Carvalho Chehab break;
92ccae7af2SMauro Carvalho Chehab case 7000000:
93e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt2_7;
94ccae7af2SMauro Carvalho Chehab i = DVBT2_7;
95ccae7af2SMauro Carvalho Chehab break;
96ccae7af2SMauro Carvalho Chehab case 8000000:
97e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbt2_8;
98ccae7af2SMauro Carvalho Chehab i = DVBT2_8;
99ccae7af2SMauro Carvalho Chehab break;
100ccae7af2SMauro Carvalho Chehab default:
101ccae7af2SMauro Carvalho Chehab ret = -EINVAL;
102ccae7af2SMauro Carvalho Chehab goto error;
103ccae7af2SMauro Carvalho Chehab }
104ccae7af2SMauro Carvalho Chehab break;
105ccae7af2SMauro Carvalho Chehab case SYS_DVBC_ANNEX_A:
106ccae7af2SMauro Carvalho Chehab case SYS_DVBC_ANNEX_C:
107e4a42e18SAntti Palosaari if_khz = dev->cfg.if_dvbc;
108ccae7af2SMauro Carvalho Chehab i = DVBC_8;
109ccae7af2SMauro Carvalho Chehab break;
110ccae7af2SMauro Carvalho Chehab default:
111ccae7af2SMauro Carvalho Chehab ret = -EINVAL;
112ccae7af2SMauro Carvalho Chehab goto error;
113ccae7af2SMauro Carvalho Chehab }
114ccae7af2SMauro Carvalho Chehab
1153b60b761SAntti Palosaari ret = regmap_write(dev->regmap, 0x23, bw_params[i][2]);
116ccae7af2SMauro Carvalho Chehab if (ret)
117ccae7af2SMauro Carvalho Chehab goto error;
118ccae7af2SMauro Carvalho Chehab
1193b60b761SAntti Palosaari ret = regmap_write(dev->regmap, 0x06, 0x00);
120ccae7af2SMauro Carvalho Chehab if (ret)
121ccae7af2SMauro Carvalho Chehab goto error;
122ccae7af2SMauro Carvalho Chehab
1233b60b761SAntti Palosaari ret = regmap_write(dev->regmap, 0x0f, bw_params[i][0]);
124ccae7af2SMauro Carvalho Chehab if (ret)
125ccae7af2SMauro Carvalho Chehab goto error;
126ccae7af2SMauro Carvalho Chehab
127ccae7af2SMauro Carvalho Chehab buf[0] = 0x02;
128ccae7af2SMauro Carvalho Chehab buf[1] = bw_params[i][1];
129ccae7af2SMauro Carvalho Chehab buf[2] = 0x03; /* default value */
130ccae7af2SMauro Carvalho Chehab buf[3] = DIV_ROUND_CLOSEST(if_khz, 50);
131ccae7af2SMauro Carvalho Chehab buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
132ccae7af2SMauro Carvalho Chehab buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
133ccae7af2SMauro Carvalho Chehab buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
134ccae7af2SMauro Carvalho Chehab buf[7] = 0xc1;
135ccae7af2SMauro Carvalho Chehab buf[8] = 0x01;
1363b60b761SAntti Palosaari ret = regmap_bulk_write(dev->regmap, 0x12, buf, sizeof(buf));
137ccae7af2SMauro Carvalho Chehab if (ret)
138ccae7af2SMauro Carvalho Chehab goto error;
139ccae7af2SMauro Carvalho Chehab
140ccae7af2SMauro Carvalho Chehab /* actual IF rounded as it is on register */
141e4a42e18SAntti Palosaari dev->if_frequency = buf[3] * 50 * 1000;
142ccae7af2SMauro Carvalho Chehab
143ccae7af2SMauro Carvalho Chehab exit:
144ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl)
145ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
146ccae7af2SMauro Carvalho Chehab
147ccae7af2SMauro Carvalho Chehab return ret;
148ccae7af2SMauro Carvalho Chehab
149ccae7af2SMauro Carvalho Chehab error:
150e4a42e18SAntti Palosaari dev_dbg(&dev->client->dev, "failed=%d\n", ret);
151ccae7af2SMauro Carvalho Chehab goto exit;
152ccae7af2SMauro Carvalho Chehab }
153ccae7af2SMauro Carvalho Chehab
tda18212_get_if_frequency(struct dvb_frontend * fe,u32 * frequency)154ccae7af2SMauro Carvalho Chehab static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
155ccae7af2SMauro Carvalho Chehab {
156e4a42e18SAntti Palosaari struct tda18212_dev *dev = fe->tuner_priv;
157ccae7af2SMauro Carvalho Chehab
158e4a42e18SAntti Palosaari *frequency = dev->if_frequency;
159ccae7af2SMauro Carvalho Chehab
160ccae7af2SMauro Carvalho Chehab return 0;
161ccae7af2SMauro Carvalho Chehab }
162ccae7af2SMauro Carvalho Chehab
163ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tda18212_tuner_ops = {
164ccae7af2SMauro Carvalho Chehab .info = {
165ccae7af2SMauro Carvalho Chehab .name = "NXP TDA18212",
166ccae7af2SMauro Carvalho Chehab
167a3f90c75SMauro Carvalho Chehab .frequency_min_hz = 48 * MHz,
168a3f90c75SMauro Carvalho Chehab .frequency_max_hz = 864 * MHz,
169a3f90c75SMauro Carvalho Chehab .frequency_step_hz = 1 * kHz,
170ccae7af2SMauro Carvalho Chehab },
171ccae7af2SMauro Carvalho Chehab
172ccae7af2SMauro Carvalho Chehab .set_params = tda18212_set_params,
173ccae7af2SMauro Carvalho Chehab .get_if_frequency = tda18212_get_if_frequency,
174ccae7af2SMauro Carvalho Chehab };
175ccae7af2SMauro Carvalho Chehab
tda18212_probe(struct i2c_client * client)1761d3264c1SUwe Kleine-König static int tda18212_probe(struct i2c_client *client)
177ccae7af2SMauro Carvalho Chehab {
1780e584cc2SAntti Palosaari struct tda18212_config *cfg = client->dev.platform_data;
1790e584cc2SAntti Palosaari struct dvb_frontend *fe = cfg->fe;
180e4a42e18SAntti Palosaari struct tda18212_dev *dev;
181ccae7af2SMauro Carvalho Chehab int ret;
1823b60b761SAntti Palosaari unsigned int chip_id;
183976bcb2fSAntti Palosaari char *version;
1843b60b761SAntti Palosaari static const struct regmap_config regmap_config = {
1853b60b761SAntti Palosaari .reg_bits = 8,
1863b60b761SAntti Palosaari .val_bits = 8,
1873b60b761SAntti Palosaari };
188ccae7af2SMauro Carvalho Chehab
189e4a42e18SAntti Palosaari dev = kzalloc(sizeof(*dev), GFP_KERNEL);
190e4a42e18SAntti Palosaari if (dev == NULL) {
1910e584cc2SAntti Palosaari ret = -ENOMEM;
192bdb32655SAntti Palosaari dev_err(&client->dev, "kzalloc() failed\n");
1930e584cc2SAntti Palosaari goto err;
1940e584cc2SAntti Palosaari }
195ccae7af2SMauro Carvalho Chehab
196e4a42e18SAntti Palosaari memcpy(&dev->cfg, cfg, sizeof(struct tda18212_config));
197e4a42e18SAntti Palosaari dev->client = client;
1983b60b761SAntti Palosaari dev->regmap = devm_regmap_init_i2c(client, ®map_config);
1993b60b761SAntti Palosaari if (IS_ERR(dev->regmap)) {
2003b60b761SAntti Palosaari ret = PTR_ERR(dev->regmap);
2013b60b761SAntti Palosaari goto err;
2023b60b761SAntti Palosaari }
203ccae7af2SMauro Carvalho Chehab
2040e584cc2SAntti Palosaari /* check if the tuner is there */
205ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl)
206ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
207ccae7af2SMauro Carvalho Chehab
2083b60b761SAntti Palosaari ret = regmap_read(dev->regmap, 0x00, &chip_id);
209e4a42e18SAntti Palosaari dev_dbg(&dev->client->dev, "chip_id=%02x\n", chip_id);
210ccae7af2SMauro Carvalho Chehab
211ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl)
212ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
213ccae7af2SMauro Carvalho Chehab
214976bcb2fSAntti Palosaari if (ret)
215976bcb2fSAntti Palosaari goto err;
216976bcb2fSAntti Palosaari
217976bcb2fSAntti Palosaari switch (chip_id) {
218976bcb2fSAntti Palosaari case 0xc7:
219976bcb2fSAntti Palosaari version = "M"; /* master */
220976bcb2fSAntti Palosaari break;
221976bcb2fSAntti Palosaari case 0x47:
222976bcb2fSAntti Palosaari version = "S"; /* slave */
223976bcb2fSAntti Palosaari break;
224976bcb2fSAntti Palosaari default:
2250e584cc2SAntti Palosaari ret = -ENODEV;
226976bcb2fSAntti Palosaari goto err;
227ccae7af2SMauro Carvalho Chehab }
228ccae7af2SMauro Carvalho Chehab
229e4a42e18SAntti Palosaari dev_info(&dev->client->dev,
230bdb32655SAntti Palosaari "NXP TDA18212HN/%s successfully identified\n", version);
231ccae7af2SMauro Carvalho Chehab
232e4a42e18SAntti Palosaari fe->tuner_priv = dev;
233ccae7af2SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
234ccae7af2SMauro Carvalho Chehab sizeof(struct dvb_tuner_ops));
235e4a42e18SAntti Palosaari i2c_set_clientdata(client, dev);
236ccae7af2SMauro Carvalho Chehab
2370e584cc2SAntti Palosaari return 0;
238976bcb2fSAntti Palosaari err:
239bdb32655SAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret);
240e4a42e18SAntti Palosaari kfree(dev);
2410e584cc2SAntti Palosaari return ret;
242ccae7af2SMauro Carvalho Chehab }
2430e584cc2SAntti Palosaari
tda18212_remove(struct i2c_client * client)244ed5c2f5fSUwe Kleine-König static void tda18212_remove(struct i2c_client *client)
2450e584cc2SAntti Palosaari {
246e4a42e18SAntti Palosaari struct tda18212_dev *dev = i2c_get_clientdata(client);
247e4a42e18SAntti Palosaari struct dvb_frontend *fe = dev->cfg.fe;
2480e584cc2SAntti Palosaari
249bdb32655SAntti Palosaari dev_dbg(&client->dev, "\n");
2500e584cc2SAntti Palosaari
2510e584cc2SAntti Palosaari memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
2520e584cc2SAntti Palosaari fe->tuner_priv = NULL;
253e4a42e18SAntti Palosaari kfree(dev);
2540e584cc2SAntti Palosaari }
2550e584cc2SAntti Palosaari
2560e584cc2SAntti Palosaari static const struct i2c_device_id tda18212_id[] = {
2570e584cc2SAntti Palosaari {"tda18212", 0},
2580e584cc2SAntti Palosaari {}
2590e584cc2SAntti Palosaari };
2600e584cc2SAntti Palosaari MODULE_DEVICE_TABLE(i2c, tda18212_id);
2610e584cc2SAntti Palosaari
2620e584cc2SAntti Palosaari static struct i2c_driver tda18212_driver = {
2630e584cc2SAntti Palosaari .driver = {
2640e584cc2SAntti Palosaari .name = "tda18212",
2650e584cc2SAntti Palosaari },
266*aaeb31c0SUwe Kleine-König .probe = tda18212_probe,
2670e584cc2SAntti Palosaari .remove = tda18212_remove,
2680e584cc2SAntti Palosaari .id_table = tda18212_id,
2690e584cc2SAntti Palosaari };
2700e584cc2SAntti Palosaari
2710e584cc2SAntti Palosaari module_i2c_driver(tda18212_driver);
272ccae7af2SMauro Carvalho Chehab
273ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
274ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
275ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL");
276