1711615dfSAntti Palosaari /*
2711615dfSAntti Palosaari  * Silicon Labs Si2168 DVB-T/T2/C demodulator driver
3711615dfSAntti Palosaari  *
4711615dfSAntti Palosaari  * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
5711615dfSAntti Palosaari  *
6711615dfSAntti Palosaari  *    This program is free software; you can redistribute it and/or modify
7711615dfSAntti Palosaari  *    it under the terms of the GNU General Public License as published by
8711615dfSAntti Palosaari  *    the Free Software Foundation; either version 2 of the License, or
9711615dfSAntti Palosaari  *    (at your option) any later version.
10711615dfSAntti Palosaari  *
11711615dfSAntti Palosaari  *    This program is distributed in the hope that it will be useful,
12711615dfSAntti Palosaari  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13711615dfSAntti Palosaari  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14711615dfSAntti Palosaari  *    GNU General Public License for more details.
15711615dfSAntti Palosaari  */
16711615dfSAntti Palosaari 
17845f3505SAntti Palosaari #include "si2168_priv.h"
18845f3505SAntti Palosaari 
19845f3505SAntti Palosaari static const struct dvb_frontend_ops si2168_ops;
20845f3505SAntti Palosaari 
21845f3505SAntti Palosaari /* execute firmware command */
22845f3505SAntti Palosaari static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd)
23845f3505SAntti Palosaari {
24845f3505SAntti Palosaari 	int ret;
25845f3505SAntti Palosaari 	unsigned long timeout;
26845f3505SAntti Palosaari 
27845f3505SAntti Palosaari 	mutex_lock(&s->i2c_mutex);
28845f3505SAntti Palosaari 
29845f3505SAntti Palosaari 	if (cmd->wlen) {
30845f3505SAntti Palosaari 		/* write cmd and args for firmware */
31845f3505SAntti Palosaari 		ret = i2c_master_send(s->client, cmd->args, cmd->wlen);
32845f3505SAntti Palosaari 		if (ret < 0) {
33845f3505SAntti Palosaari 			goto err_mutex_unlock;
34845f3505SAntti Palosaari 		} else if (ret != cmd->wlen) {
35845f3505SAntti Palosaari 			ret = -EREMOTEIO;
36845f3505SAntti Palosaari 			goto err_mutex_unlock;
37845f3505SAntti Palosaari 		}
38845f3505SAntti Palosaari 	}
39845f3505SAntti Palosaari 
40845f3505SAntti Palosaari 	if (cmd->rlen) {
41845f3505SAntti Palosaari 		/* wait cmd execution terminate */
42845f3505SAntti Palosaari 		#define TIMEOUT 50
43845f3505SAntti Palosaari 		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
44845f3505SAntti Palosaari 		while (!time_after(jiffies, timeout)) {
45845f3505SAntti Palosaari 			ret = i2c_master_recv(s->client, cmd->args, cmd->rlen);
46845f3505SAntti Palosaari 			if (ret < 0) {
47845f3505SAntti Palosaari 				goto err_mutex_unlock;
48845f3505SAntti Palosaari 			} else if (ret != cmd->rlen) {
49845f3505SAntti Palosaari 				ret = -EREMOTEIO;
50845f3505SAntti Palosaari 				goto err_mutex_unlock;
51845f3505SAntti Palosaari 			}
52845f3505SAntti Palosaari 
53845f3505SAntti Palosaari 			/* firmware ready? */
54845f3505SAntti Palosaari 			if ((cmd->args[0] >> 7) & 0x01)
55845f3505SAntti Palosaari 				break;
56845f3505SAntti Palosaari 		}
57845f3505SAntti Palosaari 
58845f3505SAntti Palosaari 		dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n",
59845f3505SAntti Palosaari 				__func__,
60845f3505SAntti Palosaari 				jiffies_to_msecs(jiffies) -
61845f3505SAntti Palosaari 				(jiffies_to_msecs(timeout) - TIMEOUT));
62845f3505SAntti Palosaari 
63845f3505SAntti Palosaari 		if (!(cmd->args[0] >> 7) & 0x01) {
64845f3505SAntti Palosaari 			ret = -ETIMEDOUT;
65845f3505SAntti Palosaari 			goto err_mutex_unlock;
66845f3505SAntti Palosaari 		}
67845f3505SAntti Palosaari 	}
68845f3505SAntti Palosaari 
69845f3505SAntti Palosaari 	ret = 0;
70845f3505SAntti Palosaari 
71845f3505SAntti Palosaari err_mutex_unlock:
72845f3505SAntti Palosaari 	mutex_unlock(&s->i2c_mutex);
73845f3505SAntti Palosaari 	if (ret)
74845f3505SAntti Palosaari 		goto err;
75845f3505SAntti Palosaari 
76845f3505SAntti Palosaari 	return 0;
77845f3505SAntti Palosaari err:
78845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
79845f3505SAntti Palosaari 	return ret;
80845f3505SAntti Palosaari }
81845f3505SAntti Palosaari 
82845f3505SAntti Palosaari static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
83845f3505SAntti Palosaari {
84845f3505SAntti Palosaari 	struct si2168 *s = fe->demodulator_priv;
85bffab93cSAntti Palosaari 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
86845f3505SAntti Palosaari 	int ret;
87845f3505SAntti Palosaari 	struct si2168_cmd cmd;
88845f3505SAntti Palosaari 
89845f3505SAntti Palosaari 	*status = 0;
90845f3505SAntti Palosaari 
91845f3505SAntti Palosaari 	if (!s->active) {
92845f3505SAntti Palosaari 		ret = -EAGAIN;
93845f3505SAntti Palosaari 		goto err;
94845f3505SAntti Palosaari 	}
95845f3505SAntti Palosaari 
96bffab93cSAntti Palosaari 	switch (c->delivery_system) {
97bffab93cSAntti Palosaari 	case SYS_DVBT:
98888680ffSAntti Palosaari 		memcpy(cmd.args, "\xa0\x01", 2);
99845f3505SAntti Palosaari 		cmd.wlen = 2;
100845f3505SAntti Palosaari 		cmd.rlen = 13;
101bffab93cSAntti Palosaari 		break;
102c790885bSAntti Palosaari 	case SYS_DVBC_ANNEX_A:
103888680ffSAntti Palosaari 		memcpy(cmd.args, "\x90\x01", 2);
104c790885bSAntti Palosaari 		cmd.wlen = 2;
105c790885bSAntti Palosaari 		cmd.rlen = 9;
106c790885bSAntti Palosaari 		break;
107bffab93cSAntti Palosaari 	case SYS_DVBT2:
108888680ffSAntti Palosaari 		memcpy(cmd.args, "\x50\x01", 2);
109bffab93cSAntti Palosaari 		cmd.wlen = 2;
110bffab93cSAntti Palosaari 		cmd.rlen = 14;
111bffab93cSAntti Palosaari 		break;
112bffab93cSAntti Palosaari 	default:
113bffab93cSAntti Palosaari 		ret = -EINVAL;
114bffab93cSAntti Palosaari 		goto err;
115bffab93cSAntti Palosaari 	}
116bffab93cSAntti Palosaari 
117845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
118845f3505SAntti Palosaari 	if (ret)
119845f3505SAntti Palosaari 		goto err;
120845f3505SAntti Palosaari 
121845f3505SAntti Palosaari 	/*
122845f3505SAntti Palosaari 	 * Possible values seen, in order from strong signal to weak:
123845f3505SAntti Palosaari 	 * 16 0001 0110 full lock
124845f3505SAntti Palosaari 	 * 1e 0001 1110 partial lock
125845f3505SAntti Palosaari 	 * 1a 0001 1010 partial lock
126845f3505SAntti Palosaari 	 * 18 0001 1000 no lock
127845f3505SAntti Palosaari 	 *
128845f3505SAntti Palosaari 	 * [b3:b1] lock bits
129845f3505SAntti Palosaari 	 * [b4] statistics ready? Set in a few secs after lock is gained.
130845f3505SAntti Palosaari 	 */
131845f3505SAntti Palosaari 
132722a042dSAntti Palosaari 	switch ((cmd.args[2] >> 1) & 0x03) {
133722a042dSAntti Palosaari 	case 0x01:
134845f3505SAntti Palosaari 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
135845f3505SAntti Palosaari 		break;
136722a042dSAntti Palosaari 	case 0x03:
137845f3505SAntti Palosaari 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
138845f3505SAntti Palosaari 				FE_HAS_SYNC | FE_HAS_LOCK;
139845f3505SAntti Palosaari 		break;
140845f3505SAntti Palosaari 	}
141845f3505SAntti Palosaari 
142845f3505SAntti Palosaari 	s->fe_status = *status;
143845f3505SAntti Palosaari 
14488ac8f86SAntti Palosaari 	if (*status & FE_HAS_LOCK) {
14588ac8f86SAntti Palosaari 		c->cnr.len = 1;
14688ac8f86SAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
14788ac8f86SAntti Palosaari 		c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4;
14888ac8f86SAntti Palosaari 	} else {
14988ac8f86SAntti Palosaari 		c->cnr.len = 1;
15088ac8f86SAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
15188ac8f86SAntti Palosaari 	}
15288ac8f86SAntti Palosaari 
153845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n",
154845f3505SAntti Palosaari 			__func__, *status, cmd.rlen, cmd.args);
155845f3505SAntti Palosaari 
156845f3505SAntti Palosaari 	return 0;
157845f3505SAntti Palosaari err:
158845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
159845f3505SAntti Palosaari 	return ret;
160845f3505SAntti Palosaari }
161845f3505SAntti Palosaari 
162845f3505SAntti Palosaari static int si2168_set_frontend(struct dvb_frontend *fe)
163845f3505SAntti Palosaari {
164845f3505SAntti Palosaari 	struct si2168 *s = fe->demodulator_priv;
165845f3505SAntti Palosaari 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
166845f3505SAntti Palosaari 	int ret;
167845f3505SAntti Palosaari 	struct si2168_cmd cmd;
168bffab93cSAntti Palosaari 	u8 bandwidth, delivery_system;
169845f3505SAntti Palosaari 
170845f3505SAntti Palosaari 	dev_dbg(&s->client->dev,
171845f3505SAntti Palosaari 			"%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n",
172845f3505SAntti Palosaari 			__func__, c->delivery_system, c->modulation,
173845f3505SAntti Palosaari 			c->frequency, c->bandwidth_hz, c->symbol_rate,
174845f3505SAntti Palosaari 			c->inversion);
175845f3505SAntti Palosaari 
176845f3505SAntti Palosaari 	if (!s->active) {
177845f3505SAntti Palosaari 		ret = -EAGAIN;
178845f3505SAntti Palosaari 		goto err;
179845f3505SAntti Palosaari 	}
180845f3505SAntti Palosaari 
181bffab93cSAntti Palosaari 	switch (c->delivery_system) {
182bffab93cSAntti Palosaari 	case SYS_DVBT:
183bffab93cSAntti Palosaari 		delivery_system = 0x20;
184bffab93cSAntti Palosaari 		break;
185c790885bSAntti Palosaari 	case SYS_DVBC_ANNEX_A:
186c790885bSAntti Palosaari 		delivery_system = 0x30;
187c790885bSAntti Palosaari 		break;
188bffab93cSAntti Palosaari 	case SYS_DVBT2:
189bffab93cSAntti Palosaari 		delivery_system = 0x70;
190bffab93cSAntti Palosaari 		break;
191bffab93cSAntti Palosaari 	default:
192bffab93cSAntti Palosaari 		ret = -EINVAL;
193bffab93cSAntti Palosaari 		goto err;
194bffab93cSAntti Palosaari 	}
195bffab93cSAntti Palosaari 
196c790885bSAntti Palosaari 	if (c->bandwidth_hz <= 5000000)
197bffab93cSAntti Palosaari 		bandwidth = 0x05;
198c790885bSAntti Palosaari 	else if (c->bandwidth_hz <= 6000000)
199bffab93cSAntti Palosaari 		bandwidth = 0x06;
200c790885bSAntti Palosaari 	else if (c->bandwidth_hz <= 7000000)
201bffab93cSAntti Palosaari 		bandwidth = 0x07;
202c790885bSAntti Palosaari 	else if (c->bandwidth_hz <= 8000000)
203bffab93cSAntti Palosaari 		bandwidth = 0x08;
204c790885bSAntti Palosaari 	else if (c->bandwidth_hz <= 9000000)
205c790885bSAntti Palosaari 		bandwidth = 0x09;
206c790885bSAntti Palosaari 	else if (c->bandwidth_hz <= 10000000)
207c790885bSAntti Palosaari 		bandwidth = 0x0a;
208c790885bSAntti Palosaari 	else
209c790885bSAntti Palosaari 		bandwidth = 0x0f;
210845f3505SAntti Palosaari 
211845f3505SAntti Palosaari 	/* program tuner */
212845f3505SAntti Palosaari 	if (fe->ops.tuner_ops.set_params) {
213845f3505SAntti Palosaari 		ret = fe->ops.tuner_ops.set_params(fe);
214845f3505SAntti Palosaari 		if (ret)
215845f3505SAntti Palosaari 			goto err;
216845f3505SAntti Palosaari 	}
217845f3505SAntti Palosaari 
218845f3505SAntti Palosaari 	memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5);
219845f3505SAntti Palosaari 	cmd.wlen = 5;
220845f3505SAntti Palosaari 	cmd.rlen = 5;
221845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
222845f3505SAntti Palosaari 	if (ret)
223845f3505SAntti Palosaari 		goto err;
224845f3505SAntti Palosaari 
225bffab93cSAntti Palosaari 	/* that has no big effect */
226bffab93cSAntti Palosaari 	if (c->delivery_system == SYS_DVBT)
227845f3505SAntti Palosaari 		memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6);
228c790885bSAntti Palosaari 	else if (c->delivery_system == SYS_DVBC_ANNEX_A)
229c790885bSAntti Palosaari 		memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6);
230bffab93cSAntti Palosaari 	else if (c->delivery_system == SYS_DVBT2)
231bffab93cSAntti Palosaari 		memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6);
232845f3505SAntti Palosaari 	cmd.wlen = 6;
233845f3505SAntti Palosaari 	cmd.rlen = 3;
234845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
235845f3505SAntti Palosaari 	if (ret)
236845f3505SAntti Palosaari 		goto err;
237845f3505SAntti Palosaari 
238845f3505SAntti Palosaari 	memcpy(cmd.args, "\x51\x03", 2);
239845f3505SAntti Palosaari 	cmd.wlen = 2;
240845f3505SAntti Palosaari 	cmd.rlen = 12;
241845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
242845f3505SAntti Palosaari 	if (ret)
243845f3505SAntti Palosaari 		goto err;
244845f3505SAntti Palosaari 
245845f3505SAntti Palosaari 	memcpy(cmd.args, "\x12\x08\x04", 3);
246845f3505SAntti Palosaari 	cmd.wlen = 3;
247845f3505SAntti Palosaari 	cmd.rlen = 3;
248845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
249845f3505SAntti Palosaari 	if (ret)
250845f3505SAntti Palosaari 		goto err;
251845f3505SAntti Palosaari 
252845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6);
253845f3505SAntti Palosaari 	cmd.wlen = 6;
2541d518c27SAntti Palosaari 	cmd.rlen = 4;
255845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
256845f3505SAntti Palosaari 	if (ret)
257845f3505SAntti Palosaari 		goto err;
258845f3505SAntti Palosaari 
259845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6);
260845f3505SAntti Palosaari 	cmd.wlen = 6;
2611d518c27SAntti Palosaari 	cmd.rlen = 4;
262845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
263845f3505SAntti Palosaari 	if (ret)
264845f3505SAntti Palosaari 		goto err;
265845f3505SAntti Palosaari 
266845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6);
267845f3505SAntti Palosaari 	cmd.wlen = 6;
2681d518c27SAntti Palosaari 	cmd.rlen = 4;
269845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
270845f3505SAntti Palosaari 	if (ret)
271845f3505SAntti Palosaari 		goto err;
272845f3505SAntti Palosaari 
273845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
274bffab93cSAntti Palosaari 	cmd.args[4] = delivery_system | bandwidth;
275845f3505SAntti Palosaari 	cmd.wlen = 6;
2761d518c27SAntti Palosaari 	cmd.rlen = 4;
277845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
278845f3505SAntti Palosaari 	if (ret)
279845f3505SAntti Palosaari 		goto err;
280845f3505SAntti Palosaari 
281845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
282845f3505SAntti Palosaari 	cmd.wlen = 6;
2831d518c27SAntti Palosaari 	cmd.rlen = 4;
284845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
285845f3505SAntti Palosaari 	if (ret)
286845f3505SAntti Palosaari 		goto err;
287845f3505SAntti Palosaari 
28893f72639SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6);
289845f3505SAntti Palosaari 	cmd.wlen = 6;
2901d518c27SAntti Palosaari 	cmd.rlen = 4;
291845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
292845f3505SAntti Palosaari 	if (ret)
293845f3505SAntti Palosaari 		goto err;
294845f3505SAntti Palosaari 
295845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6);
296845f3505SAntti Palosaari 	cmd.wlen = 6;
2971d518c27SAntti Palosaari 	cmd.rlen = 4;
298845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
299845f3505SAntti Palosaari 	if (ret)
300845f3505SAntti Palosaari 		goto err;
301845f3505SAntti Palosaari 
302845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6);
303845f3505SAntti Palosaari 	cmd.wlen = 6;
3041d518c27SAntti Palosaari 	cmd.rlen = 4;
305845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
306845f3505SAntti Palosaari 	if (ret)
307845f3505SAntti Palosaari 		goto err;
308845f3505SAntti Palosaari 
309845f3505SAntti Palosaari 	memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6);
310845f3505SAntti Palosaari 	cmd.wlen = 6;
3111d518c27SAntti Palosaari 	cmd.rlen = 4;
312845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
313845f3505SAntti Palosaari 	if (ret)
314845f3505SAntti Palosaari 		goto err;
315845f3505SAntti Palosaari 
316888680ffSAntti Palosaari 	memcpy(cmd.args, "\x85", 1);
317845f3505SAntti Palosaari 	cmd.wlen = 1;
318845f3505SAntti Palosaari 	cmd.rlen = 1;
319845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
320845f3505SAntti Palosaari 	if (ret)
321845f3505SAntti Palosaari 		goto err;
322845f3505SAntti Palosaari 
323845f3505SAntti Palosaari 	s->delivery_system = c->delivery_system;
324845f3505SAntti Palosaari 
325845f3505SAntti Palosaari 	return 0;
326845f3505SAntti Palosaari err:
327845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
328845f3505SAntti Palosaari 	return ret;
329845f3505SAntti Palosaari }
330845f3505SAntti Palosaari 
331845f3505SAntti Palosaari static int si2168_init(struct dvb_frontend *fe)
332845f3505SAntti Palosaari {
333845f3505SAntti Palosaari 	struct si2168 *s = fe->demodulator_priv;
334845f3505SAntti Palosaari 	int ret, len, remaining;
335845f3505SAntti Palosaari 	const struct firmware *fw = NULL;
336845f3505SAntti Palosaari 	u8 *fw_file = SI2168_FIRMWARE;
337845f3505SAntti Palosaari 	const unsigned int i2c_wr_max = 8;
338845f3505SAntti Palosaari 	struct si2168_cmd cmd;
339845f3505SAntti Palosaari 
340845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s:\n", __func__);
341845f3505SAntti Palosaari 
342888680ffSAntti Palosaari 	memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
343845f3505SAntti Palosaari 	cmd.wlen = 13;
344845f3505SAntti Palosaari 	cmd.rlen = 0;
345845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
346845f3505SAntti Palosaari 	if (ret)
347845f3505SAntti Palosaari 		goto err;
348845f3505SAntti Palosaari 
349888680ffSAntti Palosaari 	memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
350845f3505SAntti Palosaari 	cmd.wlen = 8;
351845f3505SAntti Palosaari 	cmd.rlen = 1;
352845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
353845f3505SAntti Palosaari 	if (ret)
354845f3505SAntti Palosaari 		goto err;
355845f3505SAntti Palosaari 
356888680ffSAntti Palosaari 	memcpy(cmd.args, "\x02", 1);
357845f3505SAntti Palosaari 	cmd.wlen = 1;
358845f3505SAntti Palosaari 	cmd.rlen = 13;
359845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
360845f3505SAntti Palosaari 	if (ret)
361845f3505SAntti Palosaari 		goto err;
362845f3505SAntti Palosaari 
363845f3505SAntti Palosaari 	cmd.args[0] = 0x05;
364845f3505SAntti Palosaari 	cmd.args[1] = 0x00;
365845f3505SAntti Palosaari 	cmd.args[2] = 0xaa;
366845f3505SAntti Palosaari 	cmd.args[3] = 0x4d;
367845f3505SAntti Palosaari 	cmd.args[4] = 0x56;
368845f3505SAntti Palosaari 	cmd.args[5] = 0x40;
369845f3505SAntti Palosaari 	cmd.args[6] = 0x00;
370845f3505SAntti Palosaari 	cmd.args[7] = 0x00;
371845f3505SAntti Palosaari 	cmd.wlen = 8;
372845f3505SAntti Palosaari 	cmd.rlen = 1;
373845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
374845f3505SAntti Palosaari 	if (ret)
375845f3505SAntti Palosaari 		goto err;
376845f3505SAntti Palosaari 
377845f3505SAntti Palosaari 	/* cold state - try to download firmware */
378845f3505SAntti Palosaari 	dev_info(&s->client->dev, "%s: found a '%s' in cold state\n",
379845f3505SAntti Palosaari 			KBUILD_MODNAME, si2168_ops.info.name);
380845f3505SAntti Palosaari 
381845f3505SAntti Palosaari 	/* request the firmware, this will block and timeout */
382845f3505SAntti Palosaari 	ret = request_firmware(&fw, fw_file, &s->client->dev);
383845f3505SAntti Palosaari 	if (ret) {
384845f3505SAntti Palosaari 		dev_err(&s->client->dev, "%s: firmare file '%s' not found\n",
385845f3505SAntti Palosaari 				KBUILD_MODNAME, fw_file);
386845f3505SAntti Palosaari 		goto err;
387845f3505SAntti Palosaari 	}
388845f3505SAntti Palosaari 
389845f3505SAntti Palosaari 	dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n",
390845f3505SAntti Palosaari 			KBUILD_MODNAME, fw_file);
391845f3505SAntti Palosaari 
392845f3505SAntti Palosaari 	for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
393845f3505SAntti Palosaari 		len = remaining;
394845f3505SAntti Palosaari 		if (len > i2c_wr_max)
395845f3505SAntti Palosaari 			len = i2c_wr_max;
396845f3505SAntti Palosaari 
397845f3505SAntti Palosaari 		memcpy(cmd.args, &fw->data[fw->size - remaining], len);
398845f3505SAntti Palosaari 		cmd.wlen = len;
399845f3505SAntti Palosaari 		cmd.rlen = 1;
400845f3505SAntti Palosaari 		ret = si2168_cmd_execute(s, &cmd);
401845f3505SAntti Palosaari 		if (ret) {
402845f3505SAntti Palosaari 			dev_err(&s->client->dev,
403845f3505SAntti Palosaari 					"%s: firmware download failed=%d\n",
404845f3505SAntti Palosaari 					KBUILD_MODNAME, ret);
405845f3505SAntti Palosaari 			goto err;
406845f3505SAntti Palosaari 		}
407845f3505SAntti Palosaari 	}
408845f3505SAntti Palosaari 
409845f3505SAntti Palosaari 	release_firmware(fw);
410845f3505SAntti Palosaari 	fw = NULL;
411845f3505SAntti Palosaari 
412888680ffSAntti Palosaari 	memcpy(cmd.args, "\x01\x01", 2);
413845f3505SAntti Palosaari 	cmd.wlen = 2;
414845f3505SAntti Palosaari 	cmd.rlen = 1;
415845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
416845f3505SAntti Palosaari 	if (ret)
417845f3505SAntti Palosaari 		goto err;
418845f3505SAntti Palosaari 
419845f3505SAntti Palosaari 	dev_info(&s->client->dev, "%s: found a '%s' in warm state\n",
420845f3505SAntti Palosaari 			KBUILD_MODNAME, si2168_ops.info.name);
421845f3505SAntti Palosaari 
422845f3505SAntti Palosaari 	s->active = true;
423845f3505SAntti Palosaari 
424845f3505SAntti Palosaari 	return 0;
425845f3505SAntti Palosaari err:
426845f3505SAntti Palosaari 	if (fw)
427845f3505SAntti Palosaari 		release_firmware(fw);
428845f3505SAntti Palosaari 
429845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
430845f3505SAntti Palosaari 	return ret;
431845f3505SAntti Palosaari }
432845f3505SAntti Palosaari 
433845f3505SAntti Palosaari static int si2168_sleep(struct dvb_frontend *fe)
434845f3505SAntti Palosaari {
435845f3505SAntti Palosaari 	struct si2168 *s = fe->demodulator_priv;
4364de0ed7cSAntti Palosaari 	int ret;
4374de0ed7cSAntti Palosaari 	struct si2168_cmd cmd;
438845f3505SAntti Palosaari 
439845f3505SAntti Palosaari 	dev_dbg(&s->client->dev, "%s:\n", __func__);
440845f3505SAntti Palosaari 
441845f3505SAntti Palosaari 	s->active = false;
442845f3505SAntti Palosaari 
4434de0ed7cSAntti Palosaari 	memcpy(cmd.args, "\x13", 1);
4444de0ed7cSAntti Palosaari 	cmd.wlen = 1;
4454de0ed7cSAntti Palosaari 	cmd.rlen = 0;
4464de0ed7cSAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
4474de0ed7cSAntti Palosaari 	if (ret)
4484de0ed7cSAntti Palosaari 		goto err;
4494de0ed7cSAntti Palosaari 
450845f3505SAntti Palosaari 	return 0;
4514de0ed7cSAntti Palosaari err:
4524de0ed7cSAntti Palosaari 	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
4534de0ed7cSAntti Palosaari 	return ret;
454845f3505SAntti Palosaari }
455845f3505SAntti Palosaari 
456845f3505SAntti Palosaari static int si2168_get_tune_settings(struct dvb_frontend *fe,
457845f3505SAntti Palosaari 	struct dvb_frontend_tune_settings *s)
458845f3505SAntti Palosaari {
459845f3505SAntti Palosaari 	s->min_delay_ms = 900;
460845f3505SAntti Palosaari 
461845f3505SAntti Palosaari 	return 0;
462845f3505SAntti Palosaari }
463845f3505SAntti Palosaari 
464845f3505SAntti Palosaari /*
465845f3505SAntti Palosaari  * I2C gate logic
466845f3505SAntti Palosaari  * We must use unlocked i2c_transfer() here because I2C lock is already taken
467845f3505SAntti Palosaari  * by tuner driver.
468845f3505SAntti Palosaari  */
469845f3505SAntti Palosaari static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
470845f3505SAntti Palosaari {
471845f3505SAntti Palosaari 	struct si2168 *s = mux_priv;
472845f3505SAntti Palosaari 	int ret;
473845f3505SAntti Palosaari 	struct i2c_msg gate_open_msg = {
474845f3505SAntti Palosaari 		.addr = s->client->addr,
475845f3505SAntti Palosaari 		.flags = 0,
476845f3505SAntti Palosaari 		.len = 3,
477845f3505SAntti Palosaari 		.buf = "\xc0\x0d\x01",
478845f3505SAntti Palosaari 	};
479845f3505SAntti Palosaari 
480845f3505SAntti Palosaari 	mutex_lock(&s->i2c_mutex);
481845f3505SAntti Palosaari 
482845f3505SAntti Palosaari 	/* open tuner I2C gate */
483845f3505SAntti Palosaari 	ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1);
484845f3505SAntti Palosaari 	if (ret != 1) {
485845f3505SAntti Palosaari 		dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
486845f3505SAntti Palosaari 				KBUILD_MODNAME, ret);
487845f3505SAntti Palosaari 		if (ret >= 0)
488845f3505SAntti Palosaari 			ret = -EREMOTEIO;
489845f3505SAntti Palosaari 	} else {
490845f3505SAntti Palosaari 		ret = 0;
491845f3505SAntti Palosaari 	}
492845f3505SAntti Palosaari 
493845f3505SAntti Palosaari 	return ret;
494845f3505SAntti Palosaari }
495845f3505SAntti Palosaari 
496845f3505SAntti Palosaari static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
497845f3505SAntti Palosaari {
498845f3505SAntti Palosaari 	struct si2168 *s = mux_priv;
499845f3505SAntti Palosaari 	int ret;
500845f3505SAntti Palosaari 	struct i2c_msg gate_close_msg = {
501845f3505SAntti Palosaari 		.addr = s->client->addr,
502845f3505SAntti Palosaari 		.flags = 0,
503845f3505SAntti Palosaari 		.len = 3,
504845f3505SAntti Palosaari 		.buf = "\xc0\x0d\x00",
505845f3505SAntti Palosaari 	};
506845f3505SAntti Palosaari 
507845f3505SAntti Palosaari 	/* close tuner I2C gate */
508845f3505SAntti Palosaari 	ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1);
509845f3505SAntti Palosaari 	if (ret != 1) {
510845f3505SAntti Palosaari 		dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
511845f3505SAntti Palosaari 				KBUILD_MODNAME, ret);
512845f3505SAntti Palosaari 		if (ret >= 0)
513845f3505SAntti Palosaari 			ret = -EREMOTEIO;
514845f3505SAntti Palosaari 	} else {
515845f3505SAntti Palosaari 		ret = 0;
516845f3505SAntti Palosaari 	}
517845f3505SAntti Palosaari 
518845f3505SAntti Palosaari 	mutex_unlock(&s->i2c_mutex);
519845f3505SAntti Palosaari 
520845f3505SAntti Palosaari 	return ret;
521845f3505SAntti Palosaari }
522845f3505SAntti Palosaari 
523845f3505SAntti Palosaari static const struct dvb_frontend_ops si2168_ops = {
524c790885bSAntti Palosaari 	.delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
525845f3505SAntti Palosaari 	.info = {
526845f3505SAntti Palosaari 		.name = "Silicon Labs Si2168",
527845f3505SAntti Palosaari 		.caps =	FE_CAN_FEC_1_2 |
528845f3505SAntti Palosaari 			FE_CAN_FEC_2_3 |
529845f3505SAntti Palosaari 			FE_CAN_FEC_3_4 |
530845f3505SAntti Palosaari 			FE_CAN_FEC_5_6 |
531845f3505SAntti Palosaari 			FE_CAN_FEC_7_8 |
532845f3505SAntti Palosaari 			FE_CAN_FEC_AUTO |
533845f3505SAntti Palosaari 			FE_CAN_QPSK |
534845f3505SAntti Palosaari 			FE_CAN_QAM_16 |
535845f3505SAntti Palosaari 			FE_CAN_QAM_32 |
536845f3505SAntti Palosaari 			FE_CAN_QAM_64 |
537845f3505SAntti Palosaari 			FE_CAN_QAM_128 |
538845f3505SAntti Palosaari 			FE_CAN_QAM_256 |
539845f3505SAntti Palosaari 			FE_CAN_QAM_AUTO |
540845f3505SAntti Palosaari 			FE_CAN_TRANSMISSION_MODE_AUTO |
541845f3505SAntti Palosaari 			FE_CAN_GUARD_INTERVAL_AUTO |
542845f3505SAntti Palosaari 			FE_CAN_HIERARCHY_AUTO |
543845f3505SAntti Palosaari 			FE_CAN_MUTE_TS |
544845f3505SAntti Palosaari 			FE_CAN_2G_MODULATION
545845f3505SAntti Palosaari 	},
546845f3505SAntti Palosaari 
547845f3505SAntti Palosaari 	.get_tune_settings = si2168_get_tune_settings,
548845f3505SAntti Palosaari 
549845f3505SAntti Palosaari 	.init = si2168_init,
550845f3505SAntti Palosaari 	.sleep = si2168_sleep,
551845f3505SAntti Palosaari 
552845f3505SAntti Palosaari 	.set_frontend = si2168_set_frontend,
553845f3505SAntti Palosaari 
554845f3505SAntti Palosaari 	.read_status = si2168_read_status,
555845f3505SAntti Palosaari };
556845f3505SAntti Palosaari 
557845f3505SAntti Palosaari static int si2168_probe(struct i2c_client *client,
558845f3505SAntti Palosaari 		const struct i2c_device_id *id)
559845f3505SAntti Palosaari {
560845f3505SAntti Palosaari 	struct si2168_config *config = client->dev.platform_data;
561845f3505SAntti Palosaari 	struct si2168 *s;
562845f3505SAntti Palosaari 	int ret;
563845f3505SAntti Palosaari 	struct si2168_cmd cmd;
564845f3505SAntti Palosaari 
565845f3505SAntti Palosaari 	dev_dbg(&client->dev, "%s:\n", __func__);
566845f3505SAntti Palosaari 
567845f3505SAntti Palosaari 	s = kzalloc(sizeof(struct si2168), GFP_KERNEL);
568845f3505SAntti Palosaari 	if (!s) {
569845f3505SAntti Palosaari 		ret = -ENOMEM;
570845f3505SAntti Palosaari 		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
571845f3505SAntti Palosaari 		goto err;
572845f3505SAntti Palosaari 	}
573845f3505SAntti Palosaari 
574845f3505SAntti Palosaari 	s->client = client;
575845f3505SAntti Palosaari 	mutex_init(&s->i2c_mutex);
576845f3505SAntti Palosaari 
577845f3505SAntti Palosaari 	/* check if the demod is there */
578845f3505SAntti Palosaari 	cmd.wlen = 0;
579845f3505SAntti Palosaari 	cmd.rlen = 1;
580845f3505SAntti Palosaari 	ret = si2168_cmd_execute(s, &cmd);
581845f3505SAntti Palosaari 	if (ret)
582845f3505SAntti Palosaari 		goto err;
583845f3505SAntti Palosaari 
584845f3505SAntti Palosaari 	/* create mux i2c adapter for tuner */
585845f3505SAntti Palosaari 	s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s,
586845f3505SAntti Palosaari 			0, 0, 0, si2168_select, si2168_deselect);
587845f3505SAntti Palosaari 	if (s->adapter == NULL)
588845f3505SAntti Palosaari 		goto err;
589845f3505SAntti Palosaari 
590845f3505SAntti Palosaari 	/* create dvb_frontend */
591845f3505SAntti Palosaari 	memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
592845f3505SAntti Palosaari 	s->fe.demodulator_priv = s;
593845f3505SAntti Palosaari 
594845f3505SAntti Palosaari 	*config->i2c_adapter = s->adapter;
595845f3505SAntti Palosaari 	*config->fe = &s->fe;
596845f3505SAntti Palosaari 
597845f3505SAntti Palosaari 	i2c_set_clientdata(client, s);
598845f3505SAntti Palosaari 
599845f3505SAntti Palosaari 	dev_info(&s->client->dev,
600845f3505SAntti Palosaari 			"%s: Silicon Labs Si2168 successfully attached\n",
601845f3505SAntti Palosaari 			KBUILD_MODNAME);
602845f3505SAntti Palosaari 	return 0;
603845f3505SAntti Palosaari err:
604845f3505SAntti Palosaari 	kfree(s);
605845f3505SAntti Palosaari 	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
606845f3505SAntti Palosaari 	return ret;
607845f3505SAntti Palosaari }
608845f3505SAntti Palosaari 
609845f3505SAntti Palosaari static int si2168_remove(struct i2c_client *client)
610845f3505SAntti Palosaari {
611845f3505SAntti Palosaari 	struct si2168 *s = i2c_get_clientdata(client);
612845f3505SAntti Palosaari 
613845f3505SAntti Palosaari 	dev_dbg(&client->dev, "%s:\n", __func__);
614845f3505SAntti Palosaari 
615845f3505SAntti Palosaari 	i2c_del_mux_adapter(s->adapter);
616845f3505SAntti Palosaari 
617845f3505SAntti Palosaari 	s->fe.ops.release = NULL;
618845f3505SAntti Palosaari 	s->fe.demodulator_priv = NULL;
619845f3505SAntti Palosaari 
620845f3505SAntti Palosaari 	kfree(s);
621845f3505SAntti Palosaari 
622845f3505SAntti Palosaari 	return 0;
623845f3505SAntti Palosaari }
624845f3505SAntti Palosaari 
625845f3505SAntti Palosaari static const struct i2c_device_id si2168_id[] = {
626845f3505SAntti Palosaari 	{"si2168", 0},
627845f3505SAntti Palosaari 	{}
628845f3505SAntti Palosaari };
629845f3505SAntti Palosaari MODULE_DEVICE_TABLE(i2c, si2168_id);
630845f3505SAntti Palosaari 
631845f3505SAntti Palosaari static struct i2c_driver si2168_driver = {
632845f3505SAntti Palosaari 	.driver = {
633845f3505SAntti Palosaari 		.owner	= THIS_MODULE,
634845f3505SAntti Palosaari 		.name	= "si2168",
635845f3505SAntti Palosaari 	},
636845f3505SAntti Palosaari 	.probe		= si2168_probe,
637845f3505SAntti Palosaari 	.remove		= si2168_remove,
638845f3505SAntti Palosaari 	.id_table	= si2168_id,
639845f3505SAntti Palosaari };
640845f3505SAntti Palosaari 
641845f3505SAntti Palosaari module_i2c_driver(si2168_driver);
642845f3505SAntti Palosaari 
643845f3505SAntti Palosaari MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
644845f3505SAntti Palosaari MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver");
645845f3505SAntti Palosaari MODULE_LICENSE("GPL");
646845f3505SAntti Palosaari MODULE_FIRMWARE(SI2168_FIRMWARE);
647