blob: 8f81d979de30541b31fb6f56559ef2c56aeb62ea [file] [log] [blame]
Antti Palosaari711615d2014-04-14 21:55:12 -03001/*
2 * Silicon Labs Si2168 DVB-T/T2/C demodulator 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
Antti Palosaari845f3502014-04-10 22:00:50 -030017#include "si2168_priv.h"
18
19static const struct dvb_frontend_ops si2168_ops;
20
21/* execute firmware command */
22static int si2168_cmd_execute(struct si2168 *s, struct si2168_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 50
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, "%s: cmd execution took %d ms\n",
59 __func__,
60 jiffies_to_msecs(jiffies) -
61 (jiffies_to_msecs(timeout) - TIMEOUT));
62
Antti Palosaarieefae302014-06-13 11:08:25 -030063 if (!((cmd->args[0] >> 7) & 0x01)) {
Antti Palosaari845f3502014-04-10 22:00:50 -030064 ret = -ETIMEDOUT;
65 goto err_mutex_unlock;
66 }
67 }
68
69 ret = 0;
70
71err_mutex_unlock:
72 mutex_unlock(&s->i2c_mutex);
73 if (ret)
74 goto err;
75
76 return 0;
77err:
78 dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
79 return ret;
80}
81
82static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
83{
84 struct si2168 *s = fe->demodulator_priv;
Antti Palosaaribffab932014-04-11 20:35:27 -030085 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Antti Palosaari845f3502014-04-10 22:00:50 -030086 int ret;
87 struct si2168_cmd cmd;
88
89 *status = 0;
90
91 if (!s->active) {
92 ret = -EAGAIN;
93 goto err;
94 }
95
Antti Palosaaribffab932014-04-11 20:35:27 -030096 switch (c->delivery_system) {
97 case SYS_DVBT:
Antti Palosaari888680f2014-07-09 19:36:58 -030098 memcpy(cmd.args, "\xa0\x01", 2);
Antti Palosaaribffab932014-04-11 20:35:27 -030099 cmd.wlen = 2;
100 cmd.rlen = 13;
101 break;
Antti Palosaaric7908852014-04-12 01:58:32 -0300102 case SYS_DVBC_ANNEX_A:
Antti Palosaari888680f2014-07-09 19:36:58 -0300103 memcpy(cmd.args, "\x90\x01", 2);
Antti Palosaaric7908852014-04-12 01:58:32 -0300104 cmd.wlen = 2;
105 cmd.rlen = 9;
106 break;
Antti Palosaaribffab932014-04-11 20:35:27 -0300107 case SYS_DVBT2:
Antti Palosaari888680f2014-07-09 19:36:58 -0300108 memcpy(cmd.args, "\x50\x01", 2);
Antti Palosaaribffab932014-04-11 20:35:27 -0300109 cmd.wlen = 2;
110 cmd.rlen = 14;
111 break;
112 default:
113 ret = -EINVAL;
114 goto err;
115 }
116
Antti Palosaari845f3502014-04-10 22:00:50 -0300117 ret = si2168_cmd_execute(s, &cmd);
118 if (ret)
119 goto err;
120
121 /*
122 * Possible values seen, in order from strong signal to weak:
123 * 16 0001 0110 full lock
124 * 1e 0001 1110 partial lock
125 * 1a 0001 1010 partial lock
126 * 18 0001 1000 no lock
127 *
128 * [b3:b1] lock bits
129 * [b4] statistics ready? Set in a few secs after lock is gained.
130 */
131
Antti Palosaari722a0422014-04-22 21:36:32 -0300132 switch ((cmd.args[2] >> 1) & 0x03) {
133 case 0x01:
Antti Palosaari845f3502014-04-10 22:00:50 -0300134 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
135 break;
Antti Palosaari722a0422014-04-22 21:36:32 -0300136 case 0x03:
Antti Palosaari845f3502014-04-10 22:00:50 -0300137 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
138 FE_HAS_SYNC | FE_HAS_LOCK;
139 break;
140 }
141
142 s->fe_status = *status;
143
Antti Palosaari88ac8f82014-07-09 20:22:27 -0300144 if (*status & FE_HAS_LOCK) {
145 c->cnr.len = 1;
146 c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
147 c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4;
148 } else {
149 c->cnr.len = 1;
150 c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
151 }
152
Antti Palosaari845f3502014-04-10 22:00:50 -0300153 dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n",
154 __func__, *status, cmd.rlen, cmd.args);
155
156 return 0;
157err:
158 dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
159 return ret;
160}
161
162static int si2168_set_frontend(struct dvb_frontend *fe)
163{
164 struct si2168 *s = fe->demodulator_priv;
165 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
166 int ret;
167 struct si2168_cmd cmd;
Antti Palosaaribffab932014-04-11 20:35:27 -0300168 u8 bandwidth, delivery_system;
Antti Palosaari845f3502014-04-10 22:00:50 -0300169
170 dev_dbg(&s->client->dev,
171 "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n",
172 __func__, c->delivery_system, c->modulation,
173 c->frequency, c->bandwidth_hz, c->symbol_rate,
174 c->inversion);
175
176 if (!s->active) {
177 ret = -EAGAIN;
178 goto err;
179 }
180
Antti Palosaaribffab932014-04-11 20:35:27 -0300181 switch (c->delivery_system) {
182 case SYS_DVBT:
183 delivery_system = 0x20;
184 break;
Antti Palosaaric7908852014-04-12 01:58:32 -0300185 case SYS_DVBC_ANNEX_A:
186 delivery_system = 0x30;
187 break;
Antti Palosaaribffab932014-04-11 20:35:27 -0300188 case SYS_DVBT2:
189 delivery_system = 0x70;
190 break;
191 default:
192 ret = -EINVAL;
193 goto err;
194 }
195
Antti Palosaaric7908852014-04-12 01:58:32 -0300196 if (c->bandwidth_hz <= 5000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300197 bandwidth = 0x05;
Antti Palosaaric7908852014-04-12 01:58:32 -0300198 else if (c->bandwidth_hz <= 6000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300199 bandwidth = 0x06;
Antti Palosaaric7908852014-04-12 01:58:32 -0300200 else if (c->bandwidth_hz <= 7000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300201 bandwidth = 0x07;
Antti Palosaaric7908852014-04-12 01:58:32 -0300202 else if (c->bandwidth_hz <= 8000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300203 bandwidth = 0x08;
Antti Palosaaric7908852014-04-12 01:58:32 -0300204 else if (c->bandwidth_hz <= 9000000)
205 bandwidth = 0x09;
206 else if (c->bandwidth_hz <= 10000000)
207 bandwidth = 0x0a;
208 else
209 bandwidth = 0x0f;
Antti Palosaari845f3502014-04-10 22:00:50 -0300210
211 /* program tuner */
212 if (fe->ops.tuner_ops.set_params) {
213 ret = fe->ops.tuner_ops.set_params(fe);
214 if (ret)
215 goto err;
216 }
217
218 memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5);
219 cmd.wlen = 5;
220 cmd.rlen = 5;
221 ret = si2168_cmd_execute(s, &cmd);
222 if (ret)
223 goto err;
224
Antti Palosaaribffab932014-04-11 20:35:27 -0300225 /* that has no big effect */
226 if (c->delivery_system == SYS_DVBT)
227 memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6);
Antti Palosaaric7908852014-04-12 01:58:32 -0300228 else if (c->delivery_system == SYS_DVBC_ANNEX_A)
229 memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6);
Antti Palosaaribffab932014-04-11 20:35:27 -0300230 else if (c->delivery_system == SYS_DVBT2)
231 memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6);
Antti Palosaari845f3502014-04-10 22:00:50 -0300232 cmd.wlen = 6;
233 cmd.rlen = 3;
234 ret = si2168_cmd_execute(s, &cmd);
235 if (ret)
236 goto err;
237
238 memcpy(cmd.args, "\x51\x03", 2);
239 cmd.wlen = 2;
240 cmd.rlen = 12;
241 ret = si2168_cmd_execute(s, &cmd);
242 if (ret)
243 goto err;
244
245 memcpy(cmd.args, "\x12\x08\x04", 3);
246 cmd.wlen = 3;
247 cmd.rlen = 3;
248 ret = si2168_cmd_execute(s, &cmd);
249 if (ret)
250 goto err;
251
Antti Palosaari845f3502014-04-10 22:00:50 -0300252 memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6);
253 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300254 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300255 ret = si2168_cmd_execute(s, &cmd);
256 if (ret)
257 goto err;
258
259 memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6);
260 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300261 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300262 ret = si2168_cmd_execute(s, &cmd);
263 if (ret)
264 goto err;
265
Antti Palosaari845f3502014-04-10 22:00:50 -0300266 memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6);
267 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300268 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300269 ret = si2168_cmd_execute(s, &cmd);
270 if (ret)
271 goto err;
272
273 memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
Antti Palosaaribffab932014-04-11 20:35:27 -0300274 cmd.args[4] = delivery_system | bandwidth;
Antti Palosaari845f3502014-04-10 22:00:50 -0300275 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300276 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300277 ret = si2168_cmd_execute(s, &cmd);
278 if (ret)
279 goto err;
280
Luis Alves32bf8812014-07-17 14:31:28 -0300281 /* set DVB-C symbol rate */
282 if (c->delivery_system == SYS_DVBC_ANNEX_A) {
283 memcpy(cmd.args, "\x14\x00\x02\x11", 4);
284 cmd.args[4] = (c->symbol_rate / 1000) & 0xff;
285 cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff;
286 cmd.wlen = 6;
287 cmd.rlen = 4;
288 ret = si2168_cmd_execute(s, &cmd);
289 if (ret)
290 goto err;
291 }
292
Antti Palosaari845f3502014-04-10 22:00:50 -0300293 memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
294 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300295 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300296 ret = si2168_cmd_execute(s, &cmd);
297 if (ret)
298 goto err;
299
Antti Palosaari93f72632014-07-10 20:21:59 -0300300 memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6);
Antti Palosaari845f3502014-04-10 22:00:50 -0300301 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300302 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300303 ret = si2168_cmd_execute(s, &cmd);
304 if (ret)
305 goto err;
306
307 memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6);
308 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300309 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300310 ret = si2168_cmd_execute(s, &cmd);
311 if (ret)
312 goto err;
313
314 memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6);
315 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300316 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300317 ret = si2168_cmd_execute(s, &cmd);
318 if (ret)
319 goto err;
320
Antti Palosaari845f3502014-04-10 22:00:50 -0300321 memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6);
322 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300323 cmd.rlen = 4;
Antti Palosaari845f3502014-04-10 22:00:50 -0300324 ret = si2168_cmd_execute(s, &cmd);
325 if (ret)
326 goto err;
327
Olli Salonen43911772014-07-17 15:43:27 -0300328 memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6);
329 cmd.wlen = 6;
330 cmd.rlen = 4;
331 ret = si2168_cmd_execute(s, &cmd);
332 if (ret)
333 goto err;
334
Antti Palosaari888680f2014-07-09 19:36:58 -0300335 memcpy(cmd.args, "\x85", 1);
Antti Palosaari845f3502014-04-10 22:00:50 -0300336 cmd.wlen = 1;
337 cmd.rlen = 1;
338 ret = si2168_cmd_execute(s, &cmd);
339 if (ret)
340 goto err;
341
342 s->delivery_system = c->delivery_system;
343
344 return 0;
345err:
346 dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
347 return ret;
348}
349
350static int si2168_init(struct dvb_frontend *fe)
351{
352 struct si2168 *s = fe->demodulator_priv;
353 int ret, len, remaining;
354 const struct firmware *fw = NULL;
Olli Salonenc9cb0822014-07-13 10:52:18 -0300355 u8 *fw_file;
Antti Palosaari845f3502014-04-10 22:00:50 -0300356 const unsigned int i2c_wr_max = 8;
357 struct si2168_cmd cmd;
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300358 unsigned int chip_id;
Antti Palosaari845f3502014-04-10 22:00:50 -0300359
360 dev_dbg(&s->client->dev, "%s:\n", __func__);
361
Antti Palosaari888680f2014-07-09 19:36:58 -0300362 memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
Antti Palosaari845f3502014-04-10 22:00:50 -0300363 cmd.wlen = 13;
364 cmd.rlen = 0;
365 ret = si2168_cmd_execute(s, &cmd);
366 if (ret)
367 goto err;
368
Antti Palosaari888680f2014-07-09 19:36:58 -0300369 memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
Antti Palosaari845f3502014-04-10 22:00:50 -0300370 cmd.wlen = 8;
371 cmd.rlen = 1;
372 ret = si2168_cmd_execute(s, &cmd);
373 if (ret)
374 goto err;
375
Olli Salonenc9cb0822014-07-13 10:52:18 -0300376 /* query chip revision */
Antti Palosaari888680f2014-07-09 19:36:58 -0300377 memcpy(cmd.args, "\x02", 1);
Antti Palosaari845f3502014-04-10 22:00:50 -0300378 cmd.wlen = 1;
379 cmd.rlen = 13;
380 ret = si2168_cmd_execute(s, &cmd);
381 if (ret)
382 goto err;
383
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300384 chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
385 cmd.args[4] << 0;
386
Luis Alves635a90c2014-07-17 19:43:37 -0300387 #define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300388 #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
389 #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
390
391 switch (chip_id) {
Luis Alves635a90c2014-07-17 19:43:37 -0300392 case SI2168_A20:
393 fw_file = SI2168_A20_FIRMWARE;
394 break;
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300395 case SI2168_A30:
Olli Salonenc9cb0822014-07-13 10:52:18 -0300396 fw_file = SI2168_A30_FIRMWARE;
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300397 break;
398 case SI2168_B40:
399 fw_file = SI2168_B40_FIRMWARE;
400 break;
401 default:
Olli Salonenc9cb0822014-07-13 10:52:18 -0300402 dev_err(&s->client->dev,
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300403 "%s: unkown chip version Si21%d-%c%c%c\n",
404 KBUILD_MODNAME, cmd.args[2], cmd.args[1],
405 cmd.args[3], cmd.args[4]);
Olli Salonenc9cb0822014-07-13 10:52:18 -0300406 ret = -EINVAL;
407 goto err;
408 }
409
Antti Palosaari845f3502014-04-10 22:00:50 -0300410 /* cold state - try to download firmware */
411 dev_info(&s->client->dev, "%s: found a '%s' in cold state\n",
412 KBUILD_MODNAME, si2168_ops.info.name);
413
414 /* request the firmware, this will block and timeout */
415 ret = request_firmware(&fw, fw_file, &s->client->dev);
416 if (ret) {
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300417 /* fallback mechanism to handle old name for Si2168 B40 fw */
418 if (chip_id == SI2168_B40) {
Olli Salonenc9cb0822014-07-13 10:52:18 -0300419 fw_file = SI2168_B40_FIRMWARE_FALLBACK;
420 ret = request_firmware(&fw, fw_file, &s->client->dev);
421 }
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300422
423 if (ret == 0) {
424 dev_notice(&s->client->dev,
425 "%s: please install firmware file '%s'\n",
426 KBUILD_MODNAME, SI2168_B40_FIRMWARE);
427 } else {
428 dev_err(&s->client->dev,
429 "%s: firmware file '%s' not found\n",
Olli Salonenc9cb0822014-07-13 10:52:18 -0300430 KBUILD_MODNAME, fw_file);
431 goto err;
432 }
Antti Palosaari845f3502014-04-10 22:00:50 -0300433 }
434
435 dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n",
436 KBUILD_MODNAME, fw_file);
437
438 for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
439 len = remaining;
440 if (len > i2c_wr_max)
441 len = i2c_wr_max;
442
443 memcpy(cmd.args, &fw->data[fw->size - remaining], len);
444 cmd.wlen = len;
445 cmd.rlen = 1;
446 ret = si2168_cmd_execute(s, &cmd);
447 if (ret) {
448 dev_err(&s->client->dev,
449 "%s: firmware download failed=%d\n",
450 KBUILD_MODNAME, ret);
451 goto err;
452 }
453 }
454
455 release_firmware(fw);
456 fw = NULL;
457
Antti Palosaari888680f2014-07-09 19:36:58 -0300458 memcpy(cmd.args, "\x01\x01", 2);
Antti Palosaari845f3502014-04-10 22:00:50 -0300459 cmd.wlen = 2;
460 cmd.rlen = 1;
461 ret = si2168_cmd_execute(s, &cmd);
462 if (ret)
463 goto err;
464
465 dev_info(&s->client->dev, "%s: found a '%s' in warm state\n",
466 KBUILD_MODNAME, si2168_ops.info.name);
467
468 s->active = true;
469
470 return 0;
471err:
472 if (fw)
473 release_firmware(fw);
474
475 dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
476 return ret;
477}
478
479static int si2168_sleep(struct dvb_frontend *fe)
480{
481 struct si2168 *s = fe->demodulator_priv;
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300482 int ret;
483 struct si2168_cmd cmd;
Antti Palosaari845f3502014-04-10 22:00:50 -0300484
485 dev_dbg(&s->client->dev, "%s:\n", __func__);
486
487 s->active = false;
488
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300489 memcpy(cmd.args, "\x13", 1);
490 cmd.wlen = 1;
491 cmd.rlen = 0;
492 ret = si2168_cmd_execute(s, &cmd);
493 if (ret)
494 goto err;
495
Antti Palosaari845f3502014-04-10 22:00:50 -0300496 return 0;
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300497err:
498 dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
499 return ret;
Antti Palosaari845f3502014-04-10 22:00:50 -0300500}
501
502static int si2168_get_tune_settings(struct dvb_frontend *fe,
503 struct dvb_frontend_tune_settings *s)
504{
505 s->min_delay_ms = 900;
506
507 return 0;
508}
509
510/*
511 * I2C gate logic
512 * We must use unlocked i2c_transfer() here because I2C lock is already taken
513 * by tuner driver.
514 */
515static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
516{
517 struct si2168 *s = mux_priv;
518 int ret;
519 struct i2c_msg gate_open_msg = {
520 .addr = s->client->addr,
521 .flags = 0,
522 .len = 3,
523 .buf = "\xc0\x0d\x01",
524 };
525
526 mutex_lock(&s->i2c_mutex);
527
528 /* open tuner I2C gate */
529 ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1);
530 if (ret != 1) {
531 dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
532 KBUILD_MODNAME, ret);
533 if (ret >= 0)
534 ret = -EREMOTEIO;
535 } else {
536 ret = 0;
537 }
538
539 return ret;
540}
541
542static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
543{
544 struct si2168 *s = mux_priv;
545 int ret;
546 struct i2c_msg gate_close_msg = {
547 .addr = s->client->addr,
548 .flags = 0,
549 .len = 3,
550 .buf = "\xc0\x0d\x00",
551 };
552
553 /* close tuner I2C gate */
554 ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1);
555 if (ret != 1) {
556 dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
557 KBUILD_MODNAME, ret);
558 if (ret >= 0)
559 ret = -EREMOTEIO;
560 } else {
561 ret = 0;
562 }
563
564 mutex_unlock(&s->i2c_mutex);
565
566 return ret;
567}
568
569static const struct dvb_frontend_ops si2168_ops = {
Antti Palosaaric7908852014-04-12 01:58:32 -0300570 .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
Antti Palosaari845f3502014-04-10 22:00:50 -0300571 .info = {
572 .name = "Silicon Labs Si2168",
573 .caps = FE_CAN_FEC_1_2 |
574 FE_CAN_FEC_2_3 |
575 FE_CAN_FEC_3_4 |
576 FE_CAN_FEC_5_6 |
577 FE_CAN_FEC_7_8 |
578 FE_CAN_FEC_AUTO |
579 FE_CAN_QPSK |
580 FE_CAN_QAM_16 |
581 FE_CAN_QAM_32 |
582 FE_CAN_QAM_64 |
583 FE_CAN_QAM_128 |
584 FE_CAN_QAM_256 |
585 FE_CAN_QAM_AUTO |
586 FE_CAN_TRANSMISSION_MODE_AUTO |
587 FE_CAN_GUARD_INTERVAL_AUTO |
588 FE_CAN_HIERARCHY_AUTO |
589 FE_CAN_MUTE_TS |
590 FE_CAN_2G_MODULATION
591 },
592
593 .get_tune_settings = si2168_get_tune_settings,
594
595 .init = si2168_init,
596 .sleep = si2168_sleep,
597
598 .set_frontend = si2168_set_frontend,
599
600 .read_status = si2168_read_status,
601};
602
603static int si2168_probe(struct i2c_client *client,
604 const struct i2c_device_id *id)
605{
606 struct si2168_config *config = client->dev.platform_data;
607 struct si2168 *s;
608 int ret;
Antti Palosaari845f3502014-04-10 22:00:50 -0300609
610 dev_dbg(&client->dev, "%s:\n", __func__);
611
612 s = kzalloc(sizeof(struct si2168), GFP_KERNEL);
613 if (!s) {
614 ret = -ENOMEM;
615 dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
616 goto err;
617 }
618
619 s->client = client;
620 mutex_init(&s->i2c_mutex);
621
Antti Palosaari845f3502014-04-10 22:00:50 -0300622 /* create mux i2c adapter for tuner */
623 s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s,
624 0, 0, 0, si2168_select, si2168_deselect);
Luis Alves4d6efc72014-07-17 16:38:08 -0300625 if (s->adapter == NULL) {
626 ret = -ENODEV;
Antti Palosaari845f3502014-04-10 22:00:50 -0300627 goto err;
Luis Alves4d6efc72014-07-17 16:38:08 -0300628 }
Antti Palosaari845f3502014-04-10 22:00:50 -0300629
630 /* create dvb_frontend */
631 memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
632 s->fe.demodulator_priv = s;
633
634 *config->i2c_adapter = s->adapter;
635 *config->fe = &s->fe;
636
637 i2c_set_clientdata(client, s);
638
639 dev_info(&s->client->dev,
640 "%s: Silicon Labs Si2168 successfully attached\n",
641 KBUILD_MODNAME);
642 return 0;
643err:
644 kfree(s);
645 dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
646 return ret;
647}
648
649static int si2168_remove(struct i2c_client *client)
650{
651 struct si2168 *s = i2c_get_clientdata(client);
652
653 dev_dbg(&client->dev, "%s:\n", __func__);
654
655 i2c_del_mux_adapter(s->adapter);
656
657 s->fe.ops.release = NULL;
658 s->fe.demodulator_priv = NULL;
659
660 kfree(s);
661
662 return 0;
663}
664
665static const struct i2c_device_id si2168_id[] = {
666 {"si2168", 0},
667 {}
668};
669MODULE_DEVICE_TABLE(i2c, si2168_id);
670
671static struct i2c_driver si2168_driver = {
672 .driver = {
673 .owner = THIS_MODULE,
674 .name = "si2168",
675 },
676 .probe = si2168_probe,
677 .remove = si2168_remove,
678 .id_table = si2168_id,
679};
680
681module_i2c_driver(si2168_driver);
682
683MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
684MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver");
685MODULE_LICENSE("GPL");
Luis Alves635a90c2014-07-17 19:43:37 -0300686MODULE_FIRMWARE(SI2168_A20_FIRMWARE);
Antti Palosaari668aa632014-07-13 16:09:03 -0300687MODULE_FIRMWARE(SI2168_A30_FIRMWARE);
Olli Salonenc9cb0822014-07-13 10:52:18 -0300688MODULE_FIRMWARE(SI2168_B40_FIRMWARE);