xref: /openbmc/linux/drivers/media/tuners/tua9001.c (revision 96676239f59ca7d3db6afaf4911a4139dbeff858)
1ccae7af2SMauro Carvalho Chehab /*
2ccae7af2SMauro Carvalho Chehab  * Infineon TUA 9001 silicon tuner driver
3ccae7af2SMauro Carvalho Chehab  *
4ccae7af2SMauro Carvalho Chehab  * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
5ccae7af2SMauro Carvalho Chehab  *
6ccae7af2SMauro Carvalho Chehab  *    This program is free software; you can redistribute it and/or modify
7ccae7af2SMauro Carvalho Chehab  *    it under the terms of the GNU General Public License as published by
8ccae7af2SMauro Carvalho Chehab  *    the Free Software Foundation; either version 2 of the License, or
9ccae7af2SMauro Carvalho Chehab  *    (at your option) any later version.
10ccae7af2SMauro Carvalho Chehab  *
11ccae7af2SMauro Carvalho Chehab  *    This program is distributed in the hope that it will be useful,
12ccae7af2SMauro Carvalho Chehab  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13ccae7af2SMauro Carvalho Chehab  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14ccae7af2SMauro Carvalho Chehab  *    GNU General Public License for more details.
15ccae7af2SMauro Carvalho Chehab  *
16ccae7af2SMauro Carvalho Chehab  *    You should have received a copy of the GNU General Public License along
17ccae7af2SMauro Carvalho Chehab  *    with this program; if not, write to the Free Software Foundation, Inc.,
18ccae7af2SMauro Carvalho Chehab  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19ccae7af2SMauro Carvalho Chehab  */
20ccae7af2SMauro Carvalho Chehab 
21ccae7af2SMauro Carvalho Chehab #include "tua9001.h"
22ccae7af2SMauro Carvalho Chehab #include "tua9001_priv.h"
23ccae7af2SMauro Carvalho Chehab 
24ccae7af2SMauro Carvalho Chehab /* write register */
25ccae7af2SMauro Carvalho Chehab static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
26ccae7af2SMauro Carvalho Chehab {
27ccae7af2SMauro Carvalho Chehab 	int ret;
28ccae7af2SMauro Carvalho Chehab 	u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
29ccae7af2SMauro Carvalho Chehab 	struct i2c_msg msg[1] = {
30ccae7af2SMauro Carvalho Chehab 		{
31fc851c66SAntti Palosaari 			.addr = priv->i2c_addr,
32ccae7af2SMauro Carvalho Chehab 			.flags = 0,
33ccae7af2SMauro Carvalho Chehab 			.len = sizeof(buf),
34ccae7af2SMauro Carvalho Chehab 			.buf = buf,
35ccae7af2SMauro Carvalho Chehab 		}
36ccae7af2SMauro Carvalho Chehab 	};
37ccae7af2SMauro Carvalho Chehab 
38ccae7af2SMauro Carvalho Chehab 	ret = i2c_transfer(priv->i2c, msg, 1);
39ccae7af2SMauro Carvalho Chehab 	if (ret == 1) {
40ccae7af2SMauro Carvalho Chehab 		ret = 0;
41ccae7af2SMauro Carvalho Chehab 	} else {
42e6211c7cSAntti Palosaari 		dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
43e6211c7cSAntti Palosaari 				KBUILD_MODNAME, ret, reg);
44ccae7af2SMauro Carvalho Chehab 		ret = -EREMOTEIO;
45ccae7af2SMauro Carvalho Chehab 	}
46ccae7af2SMauro Carvalho Chehab 
47ccae7af2SMauro Carvalho Chehab 	return ret;
48ccae7af2SMauro Carvalho Chehab }
49ccae7af2SMauro Carvalho Chehab 
50ccae7af2SMauro Carvalho Chehab static int tua9001_init(struct dvb_frontend *fe)
51ccae7af2SMauro Carvalho Chehab {
52ccae7af2SMauro Carvalho Chehab 	struct tua9001_priv *priv = fe->tuner_priv;
53ccae7af2SMauro Carvalho Chehab 	int ret = 0;
54ccae7af2SMauro Carvalho Chehab 	u8 i;
55ccae7af2SMauro Carvalho Chehab 	struct reg_val data[] = {
56ccae7af2SMauro Carvalho Chehab 		{ 0x1e, 0x6512 },
57ccae7af2SMauro Carvalho Chehab 		{ 0x25, 0xb888 },
58ccae7af2SMauro Carvalho Chehab 		{ 0x39, 0x5460 },
59ccae7af2SMauro Carvalho Chehab 		{ 0x3b, 0x00c0 },
60ccae7af2SMauro Carvalho Chehab 		{ 0x3a, 0xf000 },
61ccae7af2SMauro Carvalho Chehab 		{ 0x08, 0x0000 },
62ccae7af2SMauro Carvalho Chehab 		{ 0x32, 0x0030 },
63ccae7af2SMauro Carvalho Chehab 		{ 0x41, 0x703a },
64ccae7af2SMauro Carvalho Chehab 		{ 0x40, 0x1c78 },
65ccae7af2SMauro Carvalho Chehab 		{ 0x2c, 0x1c00 },
66ccae7af2SMauro Carvalho Chehab 		{ 0x36, 0xc013 },
67ccae7af2SMauro Carvalho Chehab 		{ 0x37, 0x6f18 },
68ccae7af2SMauro Carvalho Chehab 		{ 0x27, 0x0008 },
69ccae7af2SMauro Carvalho Chehab 		{ 0x2a, 0x0001 },
70ccae7af2SMauro Carvalho Chehab 		{ 0x34, 0x0a40 },
71ccae7af2SMauro Carvalho Chehab 	};
72ccae7af2SMauro Carvalho Chehab 
73e6211c7cSAntti Palosaari 	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
74e6211c7cSAntti Palosaari 
7589054e37SAntti Palosaari 	if (fe->callback) {
7689054e37SAntti Palosaari 		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
7789054e37SAntti Palosaari 				TUA9001_CMD_RESETN, 0);
7889054e37SAntti Palosaari 		if (ret < 0)
7989054e37SAntti Palosaari 			goto err;
8089054e37SAntti Palosaari 	}
8189054e37SAntti Palosaari 
82ccae7af2SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(data); i++) {
83ccae7af2SMauro Carvalho Chehab 		ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
8489054e37SAntti Palosaari 		if (ret < 0)
85*96676239SAntti Palosaari 			goto err;
86ccae7af2SMauro Carvalho Chehab 	}
8789054e37SAntti Palosaari err:
8889054e37SAntti Palosaari 	if (ret < 0)
89e6211c7cSAntti Palosaari 		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
9089054e37SAntti Palosaari 
9189054e37SAntti Palosaari 	return ret;
9289054e37SAntti Palosaari }
9389054e37SAntti Palosaari 
9489054e37SAntti Palosaari static int tua9001_sleep(struct dvb_frontend *fe)
9589054e37SAntti Palosaari {
9689054e37SAntti Palosaari 	struct tua9001_priv *priv = fe->tuner_priv;
9789054e37SAntti Palosaari 	int ret = 0;
9889054e37SAntti Palosaari 
99e6211c7cSAntti Palosaari 	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
100e6211c7cSAntti Palosaari 
10189054e37SAntti Palosaari 	if (fe->callback)
10289054e37SAntti Palosaari 		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
10389054e37SAntti Palosaari 				TUA9001_CMD_RESETN, 1);
104ccae7af2SMauro Carvalho Chehab 
105ccae7af2SMauro Carvalho Chehab 	if (ret < 0)
106e6211c7cSAntti Palosaari 		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
107ccae7af2SMauro Carvalho Chehab 
108ccae7af2SMauro Carvalho Chehab 	return ret;
109ccae7af2SMauro Carvalho Chehab }
110ccae7af2SMauro Carvalho Chehab 
111ccae7af2SMauro Carvalho Chehab static int tua9001_set_params(struct dvb_frontend *fe)
112ccae7af2SMauro Carvalho Chehab {
113ccae7af2SMauro Carvalho Chehab 	struct tua9001_priv *priv = fe->tuner_priv;
114ccae7af2SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
11559e54059SMauro Carvalho Chehab 	int ret = 0, i;
116ccae7af2SMauro Carvalho Chehab 	u16 val;
117ccae7af2SMauro Carvalho Chehab 	u32 frequency;
118ccae7af2SMauro Carvalho Chehab 	struct reg_val data[2];
119ccae7af2SMauro Carvalho Chehab 
120e6211c7cSAntti Palosaari 	dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
121e6211c7cSAntti Palosaari 			"bandwidth_hz=%d\n", __func__,
122e6211c7cSAntti Palosaari 			c->delivery_system, c->frequency, c->bandwidth_hz);
123ccae7af2SMauro Carvalho Chehab 
124ccae7af2SMauro Carvalho Chehab 	switch (c->delivery_system) {
125ccae7af2SMauro Carvalho Chehab 	case SYS_DVBT:
126ccae7af2SMauro Carvalho Chehab 		switch (c->bandwidth_hz) {
127ccae7af2SMauro Carvalho Chehab 		case 8000000:
128ccae7af2SMauro Carvalho Chehab 			val  = 0x0000;
129ccae7af2SMauro Carvalho Chehab 			break;
130ccae7af2SMauro Carvalho Chehab 		case 7000000:
131ccae7af2SMauro Carvalho Chehab 			val  = 0x1000;
132ccae7af2SMauro Carvalho Chehab 			break;
133ccae7af2SMauro Carvalho Chehab 		case 6000000:
134ccae7af2SMauro Carvalho Chehab 			val  = 0x2000;
135ccae7af2SMauro Carvalho Chehab 			break;
136ccae7af2SMauro Carvalho Chehab 		case 5000000:
137ccae7af2SMauro Carvalho Chehab 			val  = 0x3000;
138ccae7af2SMauro Carvalho Chehab 			break;
139ccae7af2SMauro Carvalho Chehab 		default:
140ccae7af2SMauro Carvalho Chehab 			ret = -EINVAL;
141ccae7af2SMauro Carvalho Chehab 			goto err;
142ccae7af2SMauro Carvalho Chehab 		}
143ccae7af2SMauro Carvalho Chehab 		break;
144ccae7af2SMauro Carvalho Chehab 	default:
145ccae7af2SMauro Carvalho Chehab 		ret = -EINVAL;
146ccae7af2SMauro Carvalho Chehab 		goto err;
147ccae7af2SMauro Carvalho Chehab 	}
148ccae7af2SMauro Carvalho Chehab 
149ccae7af2SMauro Carvalho Chehab 	data[0].reg = 0x04;
150ccae7af2SMauro Carvalho Chehab 	data[0].val = val;
151ccae7af2SMauro Carvalho Chehab 
152ccae7af2SMauro Carvalho Chehab 	frequency = (c->frequency - 150000000);
153ccae7af2SMauro Carvalho Chehab 	frequency /= 100;
154ccae7af2SMauro Carvalho Chehab 	frequency *= 48;
155ccae7af2SMauro Carvalho Chehab 	frequency /= 10000;
156ccae7af2SMauro Carvalho Chehab 
157ccae7af2SMauro Carvalho Chehab 	data[1].reg = 0x1f;
158ccae7af2SMauro Carvalho Chehab 	data[1].val = frequency;
159ccae7af2SMauro Carvalho Chehab 
16089054e37SAntti Palosaari 	if (fe->callback) {
16189054e37SAntti Palosaari 		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
16289054e37SAntti Palosaari 				TUA9001_CMD_RXEN, 0);
16389054e37SAntti Palosaari 		if (ret < 0)
164*96676239SAntti Palosaari 			goto err;
16589054e37SAntti Palosaari 	}
16689054e37SAntti Palosaari 
167ccae7af2SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(data); i++) {
168ccae7af2SMauro Carvalho Chehab 		ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
169ccae7af2SMauro Carvalho Chehab 		if (ret < 0)
170*96676239SAntti Palosaari 			goto err;
171ccae7af2SMauro Carvalho Chehab 	}
172ccae7af2SMauro Carvalho Chehab 
17389054e37SAntti Palosaari 	if (fe->callback) {
17489054e37SAntti Palosaari 		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
17589054e37SAntti Palosaari 				TUA9001_CMD_RXEN, 1);
17689054e37SAntti Palosaari 		if (ret < 0)
177*96676239SAntti Palosaari 			goto err;
17889054e37SAntti Palosaari 	}
179ccae7af2SMauro Carvalho Chehab err:
180ccae7af2SMauro Carvalho Chehab 	if (ret < 0)
181e6211c7cSAntti Palosaari 		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
182ccae7af2SMauro Carvalho Chehab 
183ccae7af2SMauro Carvalho Chehab 	return ret;
184ccae7af2SMauro Carvalho Chehab }
185ccae7af2SMauro Carvalho Chehab 
186ccae7af2SMauro Carvalho Chehab static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
187ccae7af2SMauro Carvalho Chehab {
188e6211c7cSAntti Palosaari 	struct tua9001_priv *priv = fe->tuner_priv;
189e6211c7cSAntti Palosaari 
190e6211c7cSAntti Palosaari 	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
191e6211c7cSAntti Palosaari 
192ccae7af2SMauro Carvalho Chehab 	*frequency = 0; /* Zero-IF */
193ccae7af2SMauro Carvalho Chehab 
194ccae7af2SMauro Carvalho Chehab 	return 0;
195ccae7af2SMauro Carvalho Chehab }
196ccae7af2SMauro Carvalho Chehab 
197ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tua9001_tuner_ops = {
198ccae7af2SMauro Carvalho Chehab 	.info = {
199ccae7af2SMauro Carvalho Chehab 		.name           = "Infineon TUA 9001",
200ccae7af2SMauro Carvalho Chehab 
201ccae7af2SMauro Carvalho Chehab 		.frequency_min  = 170000000,
202ccae7af2SMauro Carvalho Chehab 		.frequency_max  = 862000000,
203ccae7af2SMauro Carvalho Chehab 		.frequency_step = 0,
204ccae7af2SMauro Carvalho Chehab 	},
205ccae7af2SMauro Carvalho Chehab 
206ccae7af2SMauro Carvalho Chehab 	.init = tua9001_init,
20789054e37SAntti Palosaari 	.sleep = tua9001_sleep,
208ccae7af2SMauro Carvalho Chehab 	.set_params = tua9001_set_params,
209ccae7af2SMauro Carvalho Chehab 
210ccae7af2SMauro Carvalho Chehab 	.get_if_frequency = tua9001_get_if_frequency,
211ccae7af2SMauro Carvalho Chehab };
212ccae7af2SMauro Carvalho Chehab 
213fc851c66SAntti Palosaari static int tua9001_probe(struct i2c_client *client,
214fc851c66SAntti Palosaari 			const struct i2c_device_id *id)
215fc851c66SAntti Palosaari {
216fc851c66SAntti Palosaari 	struct tua9001_priv *dev;
217fc851c66SAntti Palosaari 	struct tua9001_platform_data *pdata = client->dev.platform_data;
218fc851c66SAntti Palosaari 	struct dvb_frontend *fe = pdata->dvb_frontend;
219fc851c66SAntti Palosaari 	int ret;
220fc851c66SAntti Palosaari 
221fc851c66SAntti Palosaari 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
222fc851c66SAntti Palosaari 	if (!dev) {
223fc851c66SAntti Palosaari 		ret = -ENOMEM;
224fc851c66SAntti Palosaari 		goto err;
225fc851c66SAntti Palosaari 	}
226fc851c66SAntti Palosaari 
227fc851c66SAntti Palosaari 	dev->client = client;
228fc851c66SAntti Palosaari 	dev->i2c_addr = client->addr;
229fc851c66SAntti Palosaari 	dev->i2c = client->adapter;
230fc851c66SAntti Palosaari 	dev->fe = pdata->dvb_frontend;
231fc851c66SAntti Palosaari 
232fc851c66SAntti Palosaari 	if (fe->callback) {
233fc851c66SAntti Palosaari 		ret = fe->callback(client->adapter,
234fc851c66SAntti Palosaari 				   DVB_FRONTEND_COMPONENT_TUNER,
235fc851c66SAntti Palosaari 				   TUA9001_CMD_CEN, 1);
236fc851c66SAntti Palosaari 		if (ret)
237fc851c66SAntti Palosaari 			goto err_kfree;
238fc851c66SAntti Palosaari 
239fc851c66SAntti Palosaari 		ret = fe->callback(client->adapter,
240fc851c66SAntti Palosaari 				   DVB_FRONTEND_COMPONENT_TUNER,
241fc851c66SAntti Palosaari 				   TUA9001_CMD_RXEN, 0);
242fc851c66SAntti Palosaari 		if (ret)
243fc851c66SAntti Palosaari 			goto err_kfree;
244fc851c66SAntti Palosaari 
245fc851c66SAntti Palosaari 		ret = fe->callback(client->adapter,
246fc851c66SAntti Palosaari 				   DVB_FRONTEND_COMPONENT_TUNER,
247fc851c66SAntti Palosaari 				   TUA9001_CMD_RESETN, 1);
248fc851c66SAntti Palosaari 		if (ret)
249fc851c66SAntti Palosaari 			goto err_kfree;
250fc851c66SAntti Palosaari 	}
251fc851c66SAntti Palosaari 
252fc851c66SAntti Palosaari 	fe->tuner_priv = dev;
253fc851c66SAntti Palosaari 	memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
254fc851c66SAntti Palosaari 			sizeof(struct dvb_tuner_ops));
255fc851c66SAntti Palosaari 	i2c_set_clientdata(client, dev);
256fc851c66SAntti Palosaari 
257fc851c66SAntti Palosaari 	dev_info(&client->dev, "Infineon TUA 9001 successfully attached\n");
258fc851c66SAntti Palosaari 	return 0;
259fc851c66SAntti Palosaari err_kfree:
260fc851c66SAntti Palosaari 	kfree(dev);
261fc851c66SAntti Palosaari err:
262fc851c66SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
263fc851c66SAntti Palosaari 	return ret;
264fc851c66SAntti Palosaari }
265fc851c66SAntti Palosaari 
266fc851c66SAntti Palosaari static int tua9001_remove(struct i2c_client *client)
267fc851c66SAntti Palosaari {
268fc851c66SAntti Palosaari 	struct tua9001_priv *dev = i2c_get_clientdata(client);
269fc851c66SAntti Palosaari 	struct dvb_frontend *fe = dev->fe;
270fc851c66SAntti Palosaari 	int ret;
271fc851c66SAntti Palosaari 
272fc851c66SAntti Palosaari 	dev_dbg(&client->dev, "\n");
273fc851c66SAntti Palosaari 
274fc851c66SAntti Palosaari 	if (fe->callback) {
275fc851c66SAntti Palosaari 		ret = fe->callback(client->adapter,
276fc851c66SAntti Palosaari 				   DVB_FRONTEND_COMPONENT_TUNER,
277fc851c66SAntti Palosaari 				   TUA9001_CMD_CEN, 0);
278fc851c66SAntti Palosaari 		if (ret)
279fc851c66SAntti Palosaari 			goto err_kfree;
280fc851c66SAntti Palosaari 	}
281fc851c66SAntti Palosaari 	kfree(dev);
282fc851c66SAntti Palosaari 	return 0;
283fc851c66SAntti Palosaari err_kfree:
284fc851c66SAntti Palosaari 	kfree(dev);
285fc851c66SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
286fc851c66SAntti Palosaari 	return ret;
287fc851c66SAntti Palosaari }
288fc851c66SAntti Palosaari 
289fc851c66SAntti Palosaari static const struct i2c_device_id tua9001_id_table[] = {
290fc851c66SAntti Palosaari 	{"tua9001", 0},
291fc851c66SAntti Palosaari 	{}
292fc851c66SAntti Palosaari };
293fc851c66SAntti Palosaari MODULE_DEVICE_TABLE(i2c, tua9001_id_table);
294fc851c66SAntti Palosaari 
295fc851c66SAntti Palosaari static struct i2c_driver tua9001_driver = {
296fc851c66SAntti Palosaari 	.driver = {
297fc851c66SAntti Palosaari 		.owner	= THIS_MODULE,
298fc851c66SAntti Palosaari 		.name	= "tua9001",
299fc851c66SAntti Palosaari 		.suppress_bind_attrs = true,
300fc851c66SAntti Palosaari 	},
301fc851c66SAntti Palosaari 	.probe		= tua9001_probe,
302fc851c66SAntti Palosaari 	.remove		= tua9001_remove,
303fc851c66SAntti Palosaari 	.id_table	= tua9001_id_table,
304fc851c66SAntti Palosaari };
305fc851c66SAntti Palosaari 
306fc851c66SAntti Palosaari module_i2c_driver(tua9001_driver);
307fc851c66SAntti Palosaari 
308ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
309ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
310ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL");
311