xref: /openbmc/linux/drivers/media/tuners/tda18212.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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, &regmap_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