si2157.c revision 67d0113a224f0fb1be784f7553fdeafd82cadc6c
1/*
2 * Silicon Labs Si2157/2158 silicon tuner driver
3 *
4 * Copyright (C) 2014 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.
15 */
16
17#include "si2157_priv.h"
18
19static const struct dvb_tuner_ops si2157_ops;
20
21/* execute firmware command */
22static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd)
23{
24	int ret;
25	unsigned long timeout;
26
27	mutex_lock(&s->i2c_mutex);
28
29	if (cmd->wlen) {
30		/* write cmd and args for firmware */
31		ret = i2c_master_send(s->client, cmd->args, cmd->wlen);
32		if (ret < 0) {
33			goto err_mutex_unlock;
34		} else if (ret != cmd->wlen) {
35			ret = -EREMOTEIO;
36			goto err_mutex_unlock;
37		}
38	}
39
40	if (cmd->rlen) {
41		/* wait cmd execution terminate */
42		#define TIMEOUT 80
43		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
44		while (!time_after(jiffies, timeout)) {
45			ret = i2c_master_recv(s->client, cmd->args, cmd->rlen);
46			if (ret < 0) {
47				goto err_mutex_unlock;
48			} else if (ret != cmd->rlen) {
49				ret = -EREMOTEIO;
50				goto err_mutex_unlock;
51			}
52
53			/* firmware ready? */
54			if ((cmd->args[0] >> 7) & 0x01)
55				break;
56		}
57
58		dev_dbg(&s->client->dev, "cmd execution took %d ms\n",
59				jiffies_to_msecs(jiffies) -
60				(jiffies_to_msecs(timeout) - TIMEOUT));
61
62		if (!((cmd->args[0] >> 7) & 0x01)) {
63			ret = -ETIMEDOUT;
64			goto err_mutex_unlock;
65		}
66	}
67
68	ret = 0;
69
70err_mutex_unlock:
71	mutex_unlock(&s->i2c_mutex);
72	if (ret)
73		goto err;
74
75	return 0;
76err:
77	dev_dbg(&s->client->dev, "failed=%d\n", ret);
78	return ret;
79}
80
81static int si2157_init(struct dvb_frontend *fe)
82{
83	struct si2157 *s = fe->tuner_priv;
84	int ret, len, remaining;
85	struct si2157_cmd cmd;
86	const struct firmware *fw = NULL;
87	u8 *fw_file;
88	unsigned int chip_id;
89
90	dev_dbg(&s->client->dev, "\n");
91
92	/* configure? */
93	memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
94	cmd.wlen = 15;
95	cmd.rlen = 1;
96	ret = si2157_cmd_execute(s, &cmd);
97	if (ret)
98		goto err;
99
100	/* query chip revision */
101	memcpy(cmd.args, "\x02", 1);
102	cmd.wlen = 1;
103	cmd.rlen = 13;
104	ret = si2157_cmd_execute(s, &cmd);
105	if (ret)
106		goto err;
107
108	chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
109			cmd.args[4] << 0;
110
111	#define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
112	#define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
113
114	switch (chip_id) {
115	case SI2158_A20:
116		fw_file = SI2158_A20_FIRMWARE;
117		break;
118	case SI2157_A30:
119		goto skip_fw_download;
120		break;
121	default:
122		dev_err(&s->client->dev,
123				"unknown chip version Si21%d-%c%c%c\n",
124				cmd.args[2], cmd.args[1],
125				cmd.args[3], cmd.args[4]);
126		ret = -EINVAL;
127		goto err;
128	}
129
130	/* cold state - try to download firmware */
131	dev_info(&s->client->dev, "found a '%s' in cold state\n",
132			si2157_ops.info.name);
133
134	/* request the firmware, this will block and timeout */
135	ret = request_firmware(&fw, fw_file, &s->client->dev);
136	if (ret) {
137		dev_err(&s->client->dev, "firmware file '%s' not found\n",
138				fw_file);
139		goto err;
140	}
141
142	/* firmware should be n chunks of 17 bytes */
143	if (fw->size % 17 != 0) {
144		dev_err(&s->client->dev, "firmware file '%s' is invalid\n",
145				fw_file);
146		ret = -EINVAL;
147		goto err;
148	}
149
150	dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
151			fw_file);
152
153	for (remaining = fw->size; remaining > 0; remaining -= 17) {
154		len = fw->data[fw->size - remaining];
155		memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
156		cmd.wlen = len;
157		cmd.rlen = 1;
158		ret = si2157_cmd_execute(s, &cmd);
159		if (ret) {
160			dev_err(&s->client->dev,
161					"firmware download failed=%d\n",
162					ret);
163			goto err;
164		}
165	}
166
167	release_firmware(fw);
168	fw = NULL;
169
170skip_fw_download:
171	/* reboot the tuner with new firmware? */
172	memcpy(cmd.args, "\x01\x01", 2);
173	cmd.wlen = 2;
174	cmd.rlen = 1;
175	ret = si2157_cmd_execute(s, &cmd);
176	if (ret)
177		goto err;
178
179	s->active = true;
180
181	return 0;
182err:
183	if (fw)
184		release_firmware(fw);
185
186	dev_dbg(&s->client->dev, "failed=%d\n", ret);
187	return ret;
188}
189
190static int si2157_sleep(struct dvb_frontend *fe)
191{
192	struct si2157 *s = fe->tuner_priv;
193	int ret;
194	struct si2157_cmd cmd;
195
196	dev_dbg(&s->client->dev, "\n");
197
198	s->active = false;
199
200	memcpy(cmd.args, "\x13", 1);
201	cmd.wlen = 1;
202	cmd.rlen = 0;
203	ret = si2157_cmd_execute(s, &cmd);
204	if (ret)
205		goto err;
206
207	return 0;
208err:
209	dev_dbg(&s->client->dev, "failed=%d\n", ret);
210	return ret;
211}
212
213static int si2157_set_params(struct dvb_frontend *fe)
214{
215	struct si2157 *s = fe->tuner_priv;
216	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
217	int ret;
218	struct si2157_cmd cmd;
219	u8 bandwidth, delivery_system;
220
221	dev_dbg(&s->client->dev,
222			"delivery_system=%d frequency=%u bandwidth_hz=%u\n",
223			c->delivery_system, c->frequency,
224			c->bandwidth_hz);
225
226	if (!s->active) {
227		ret = -EAGAIN;
228		goto err;
229	}
230
231	if (c->bandwidth_hz <= 6000000)
232		bandwidth = 0x06;
233	else if (c->bandwidth_hz <= 7000000)
234		bandwidth = 0x07;
235	else if (c->bandwidth_hz <= 8000000)
236		bandwidth = 0x08;
237	else
238		bandwidth = 0x0f;
239
240	switch (c->delivery_system) {
241	case SYS_DVBT:
242	case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
243			delivery_system = 0x20;
244			break;
245	case SYS_DVBC_ANNEX_A:
246			delivery_system = 0x30;
247			break;
248	default:
249			ret = -EINVAL;
250			goto err;
251	}
252
253	memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6);
254	cmd.args[4] = delivery_system | bandwidth;
255	if (s->inversion)
256		cmd.args[5] = 0x01;
257	cmd.wlen = 6;
258	cmd.rlen = 1;
259	ret = si2157_cmd_execute(s, &cmd);
260	if (ret)
261		goto err;
262
263	/* set frequency */
264	memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8);
265	cmd.args[4] = (c->frequency >>  0) & 0xff;
266	cmd.args[5] = (c->frequency >>  8) & 0xff;
267	cmd.args[6] = (c->frequency >> 16) & 0xff;
268	cmd.args[7] = (c->frequency >> 24) & 0xff;
269	cmd.wlen = 8;
270	cmd.rlen = 1;
271	ret = si2157_cmd_execute(s, &cmd);
272	if (ret)
273		goto err;
274
275	return 0;
276err:
277	dev_dbg(&s->client->dev, "failed=%d\n", ret);
278	return ret;
279}
280
281static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
282{
283	*frequency = 5000000; /* default value of property 0x0706 */
284	return 0;
285}
286
287static const struct dvb_tuner_ops si2157_ops = {
288	.info = {
289		.name           = "Silicon Labs Si2157/Si2158",
290		.frequency_min  = 110000000,
291		.frequency_max  = 862000000,
292	},
293
294	.init = si2157_init,
295	.sleep = si2157_sleep,
296	.set_params = si2157_set_params,
297	.get_if_frequency = si2157_get_if_frequency,
298};
299
300static int si2157_probe(struct i2c_client *client,
301		const struct i2c_device_id *id)
302{
303	struct si2157_config *cfg = client->dev.platform_data;
304	struct dvb_frontend *fe = cfg->fe;
305	struct si2157 *s;
306	struct si2157_cmd cmd;
307	int ret;
308
309	s = kzalloc(sizeof(struct si2157), GFP_KERNEL);
310	if (!s) {
311		ret = -ENOMEM;
312		dev_err(&client->dev, "kzalloc() failed\n");
313		goto err;
314	}
315
316	s->client = client;
317	s->fe = cfg->fe;
318	s->inversion = cfg->inversion;
319	mutex_init(&s->i2c_mutex);
320
321	/* check if the tuner is there */
322	cmd.wlen = 0;
323	cmd.rlen = 1;
324	ret = si2157_cmd_execute(s, &cmd);
325	if (ret)
326		goto err;
327
328	fe->tuner_priv = s;
329	memcpy(&fe->ops.tuner_ops, &si2157_ops,
330			sizeof(struct dvb_tuner_ops));
331
332	i2c_set_clientdata(client, s);
333
334	dev_info(&s->client->dev,
335			"Silicon Labs Si2157/Si2158 successfully attached\n");
336	return 0;
337err:
338	dev_dbg(&client->dev, "failed=%d\n", ret);
339	kfree(s);
340
341	return ret;
342}
343
344static int si2157_remove(struct i2c_client *client)
345{
346	struct si2157 *s = i2c_get_clientdata(client);
347	struct dvb_frontend *fe = s->fe;
348
349	dev_dbg(&client->dev, "\n");
350
351	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
352	fe->tuner_priv = NULL;
353	kfree(s);
354
355	return 0;
356}
357
358static const struct i2c_device_id si2157_id[] = {
359	{"si2157", 0},
360	{}
361};
362MODULE_DEVICE_TABLE(i2c, si2157_id);
363
364static struct i2c_driver si2157_driver = {
365	.driver = {
366		.owner	= THIS_MODULE,
367		.name	= "si2157",
368	},
369	.probe		= si2157_probe,
370	.remove		= si2157_remove,
371	.id_table	= si2157_id,
372};
373
374module_i2c_driver(si2157_driver);
375
376MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver");
377MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
378MODULE_LICENSE("GPL");
379MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
380