]>
Commit | Line | Data |
---|---|---|
711615df AP |
1 | /* |
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 | ||
845f3505 AP |
17 | #include "si2168_priv.h" |
18 | ||
19 | static const struct dvb_frontend_ops si2168_ops; | |
20 | ||
21 | /* execute firmware command */ | |
e6d7ffcd | 22 | static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) |
845f3505 | 23 | { |
e6d7ffcd | 24 | struct si2168_dev *dev = i2c_get_clientdata(client); |
845f3505 AP |
25 | int ret; |
26 | unsigned long timeout; | |
27 | ||
e6d7ffcd AP |
28 | mutex_lock(&dev->i2c_mutex); |
29 | ||
845f3505 AP |
30 | if (cmd->wlen) { |
31 | /* write cmd and args for firmware */ | |
e6d7ffcd | 32 | ret = i2c_master_send(client, cmd->args, cmd->wlen); |
845f3505 | 33 | if (ret < 0) { |
e6d7ffcd | 34 | goto err_mutex_unlock; |
845f3505 AP |
35 | } else if (ret != cmd->wlen) { |
36 | ret = -EREMOTEIO; | |
e6d7ffcd | 37 | goto err_mutex_unlock; |
845f3505 AP |
38 | } |
39 | } | |
40 | ||
41 | if (cmd->rlen) { | |
42 | /* wait cmd execution terminate */ | |
551c33e7 | 43 | #define TIMEOUT 70 |
845f3505 AP |
44 | timeout = jiffies + msecs_to_jiffies(TIMEOUT); |
45 | while (!time_after(jiffies, timeout)) { | |
e6d7ffcd | 46 | ret = i2c_master_recv(client, cmd->args, cmd->rlen); |
845f3505 | 47 | if (ret < 0) { |
e6d7ffcd | 48 | goto err_mutex_unlock; |
845f3505 AP |
49 | } else if (ret != cmd->rlen) { |
50 | ret = -EREMOTEIO; | |
e6d7ffcd | 51 | goto err_mutex_unlock; |
845f3505 AP |
52 | } |
53 | ||
54 | /* firmware ready? */ | |
55 | if ((cmd->args[0] >> 7) & 0x01) | |
56 | break; | |
57 | } | |
58 | ||
3de35835 | 59 | dev_dbg(&client->dev, "cmd execution took %d ms\n", |
845f3505 AP |
60 | jiffies_to_msecs(jiffies) - |
61 | (jiffies_to_msecs(timeout) - TIMEOUT)); | |
62 | ||
7adf99d2 OS |
63 | /* error bit set? */ |
64 | if ((cmd->args[0] >> 6) & 0x01) { | |
65 | ret = -EREMOTEIO; | |
e6d7ffcd | 66 | goto err_mutex_unlock; |
7adf99d2 OS |
67 | } |
68 | ||
eefae30a | 69 | if (!((cmd->args[0] >> 7) & 0x01)) { |
845f3505 | 70 | ret = -ETIMEDOUT; |
e6d7ffcd | 71 | goto err_mutex_unlock; |
845f3505 AP |
72 | } |
73 | } | |
74 | ||
e6d7ffcd | 75 | mutex_unlock(&dev->i2c_mutex); |
4affbe1d | 76 | return 0; |
e6d7ffcd AP |
77 | err_mutex_unlock: |
78 | mutex_unlock(&dev->i2c_mutex); | |
3de35835 | 79 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
80 | return ret; |
81 | } | |
82 | ||
0df289a2 | 83 | static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status) |
845f3505 | 84 | { |
6307b560 AP |
85 | struct i2c_client *client = fe->demodulator_priv; |
86 | struct si2168_dev *dev = i2c_get_clientdata(client); | |
bffab93c | 87 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
8393c003 AP |
88 | int ret, i; |
89 | unsigned int utmp, utmp1, utmp2; | |
845f3505 AP |
90 | struct si2168_cmd cmd; |
91 | ||
92 | *status = 0; | |
93 | ||
bd01c766 | 94 | if (!dev->active) { |
845f3505 AP |
95 | ret = -EAGAIN; |
96 | goto err; | |
97 | } | |
98 | ||
bffab93c AP |
99 | switch (c->delivery_system) { |
100 | case SYS_DVBT: | |
888680ff | 101 | memcpy(cmd.args, "\xa0\x01", 2); |
bffab93c AP |
102 | cmd.wlen = 2; |
103 | cmd.rlen = 13; | |
104 | break; | |
c790885b | 105 | case SYS_DVBC_ANNEX_A: |
888680ff | 106 | memcpy(cmd.args, "\x90\x01", 2); |
c790885b AP |
107 | cmd.wlen = 2; |
108 | cmd.rlen = 9; | |
109 | break; | |
bffab93c | 110 | case SYS_DVBT2: |
888680ff | 111 | memcpy(cmd.args, "\x50\x01", 2); |
bffab93c AP |
112 | cmd.wlen = 2; |
113 | cmd.rlen = 14; | |
114 | break; | |
115 | default: | |
116 | ret = -EINVAL; | |
117 | goto err; | |
118 | } | |
119 | ||
6307b560 | 120 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
121 | if (ret) |
122 | goto err; | |
123 | ||
722a042d AP |
124 | switch ((cmd.args[2] >> 1) & 0x03) { |
125 | case 0x01: | |
845f3505 AP |
126 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; |
127 | break; | |
722a042d | 128 | case 0x03: |
845f3505 AP |
129 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | |
130 | FE_HAS_SYNC | FE_HAS_LOCK; | |
131 | break; | |
132 | } | |
133 | ||
bd01c766 | 134 | dev->fe_status = *status; |
845f3505 | 135 | |
88ac8f86 AP |
136 | if (*status & FE_HAS_LOCK) { |
137 | c->cnr.len = 1; | |
138 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; | |
139 | c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4; | |
140 | } else { | |
141 | c->cnr.len = 1; | |
142 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
143 | } | |
144 | ||
3de35835 | 145 | dev_dbg(&client->dev, "status=%02x args=%*ph\n", |
37b4e43d | 146 | *status, cmd.rlen, cmd.args); |
845f3505 | 147 | |
8393c003 AP |
148 | /* BER */ |
149 | if (*status & FE_HAS_VITERBI) { | |
150 | memcpy(cmd.args, "\x82\x00", 2); | |
151 | cmd.wlen = 2; | |
152 | cmd.rlen = 3; | |
153 | ret = si2168_cmd_execute(client, &cmd); | |
154 | if (ret) | |
155 | goto err; | |
156 | ||
157 | /* | |
158 | * Firmware returns [0, 255] mantissa and [0, 8] exponent. | |
159 | * Convert to DVB API: mantissa * 10^(8 - exponent) / 10^8 | |
160 | */ | |
161 | utmp = clamp(8 - cmd.args[1], 0, 8); | |
162 | for (i = 0, utmp1 = 1; i < utmp; i++) | |
163 | utmp1 = utmp1 * 10; | |
164 | ||
165 | utmp1 = cmd.args[2] * utmp1; | |
166 | utmp2 = 100000000; /* 10^8 */ | |
167 | ||
168 | dev_dbg(&client->dev, | |
169 | "post_bit_error=%u post_bit_count=%u ber=%u*10^-%u\n", | |
170 | utmp1, utmp2, cmd.args[2], cmd.args[1]); | |
171 | ||
172 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; | |
173 | c->post_bit_error.stat[0].uvalue += utmp1; | |
174 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; | |
175 | c->post_bit_count.stat[0].uvalue += utmp2; | |
176 | } else { | |
177 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
178 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
179 | } | |
180 | ||
c62d29c8 AP |
181 | /* UCB */ |
182 | if (*status & FE_HAS_SYNC) { | |
183 | memcpy(cmd.args, "\x84\x01", 2); | |
184 | cmd.wlen = 2; | |
185 | cmd.rlen = 3; | |
186 | ret = si2168_cmd_execute(client, &cmd); | |
187 | if (ret) | |
188 | goto err; | |
189 | ||
190 | utmp1 = cmd.args[2] << 8 | cmd.args[1] << 0; | |
191 | dev_dbg(&client->dev, "block_error=%u\n", utmp1); | |
192 | ||
193 | /* Sometimes firmware returns bogus value */ | |
194 | if (utmp1 == 0xffff) | |
195 | utmp1 = 0; | |
196 | ||
197 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; | |
198 | c->block_error.stat[0].uvalue += utmp1; | |
199 | } else { | |
200 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
201 | } | |
202 | ||
845f3505 AP |
203 | return 0; |
204 | err: | |
3de35835 | 205 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
206 | return ret; |
207 | } | |
208 | ||
209 | static int si2168_set_frontend(struct dvb_frontend *fe) | |
210 | { | |
6307b560 AP |
211 | struct i2c_client *client = fe->demodulator_priv; |
212 | struct si2168_dev *dev = i2c_get_clientdata(client); | |
845f3505 AP |
213 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
214 | int ret; | |
215 | struct si2168_cmd cmd; | |
bffab93c | 216 | u8 bandwidth, delivery_system; |
845f3505 | 217 | |
3de35835 | 218 | dev_dbg(&client->dev, |
e5dd1100 AP |
219 | "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u\n", |
220 | c->delivery_system, c->modulation, c->frequency, | |
221 | c->bandwidth_hz, c->symbol_rate, c->inversion, | |
222 | c->stream_id); | |
845f3505 | 223 | |
bd01c766 | 224 | if (!dev->active) { |
845f3505 AP |
225 | ret = -EAGAIN; |
226 | goto err; | |
227 | } | |
228 | ||
bffab93c AP |
229 | switch (c->delivery_system) { |
230 | case SYS_DVBT: | |
231 | delivery_system = 0x20; | |
232 | break; | |
c790885b AP |
233 | case SYS_DVBC_ANNEX_A: |
234 | delivery_system = 0x30; | |
235 | break; | |
bffab93c AP |
236 | case SYS_DVBT2: |
237 | delivery_system = 0x70; | |
238 | break; | |
239 | default: | |
240 | ret = -EINVAL; | |
241 | goto err; | |
242 | } | |
243 | ||
683e98b6 OS |
244 | if (c->bandwidth_hz == 0) { |
245 | ret = -EINVAL; | |
246 | goto err; | |
17d4d6ae OS |
247 | } else if (c->bandwidth_hz <= 2000000) |
248 | bandwidth = 0x02; | |
249 | else if (c->bandwidth_hz <= 5000000) | |
bffab93c | 250 | bandwidth = 0x05; |
c790885b | 251 | else if (c->bandwidth_hz <= 6000000) |
bffab93c | 252 | bandwidth = 0x06; |
c790885b | 253 | else if (c->bandwidth_hz <= 7000000) |
bffab93c | 254 | bandwidth = 0x07; |
c790885b | 255 | else if (c->bandwidth_hz <= 8000000) |
bffab93c | 256 | bandwidth = 0x08; |
c790885b AP |
257 | else if (c->bandwidth_hz <= 9000000) |
258 | bandwidth = 0x09; | |
259 | else if (c->bandwidth_hz <= 10000000) | |
260 | bandwidth = 0x0a; | |
261 | else | |
262 | bandwidth = 0x0f; | |
845f3505 AP |
263 | |
264 | /* program tuner */ | |
265 | if (fe->ops.tuner_ops.set_params) { | |
266 | ret = fe->ops.tuner_ops.set_params(fe); | |
267 | if (ret) | |
268 | goto err; | |
269 | } | |
270 | ||
271 | memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5); | |
272 | cmd.wlen = 5; | |
273 | cmd.rlen = 5; | |
6307b560 | 274 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
275 | if (ret) |
276 | goto err; | |
277 | ||
bffab93c AP |
278 | /* that has no big effect */ |
279 | if (c->delivery_system == SYS_DVBT) | |
280 | memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6); | |
c790885b AP |
281 | else if (c->delivery_system == SYS_DVBC_ANNEX_A) |
282 | memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6); | |
bffab93c AP |
283 | else if (c->delivery_system == SYS_DVBT2) |
284 | memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6); | |
845f3505 AP |
285 | cmd.wlen = 6; |
286 | cmd.rlen = 3; | |
6307b560 | 287 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
288 | if (ret) |
289 | goto err; | |
290 | ||
e395e573 C |
291 | if (c->delivery_system == SYS_DVBT2) { |
292 | /* select PLP */ | |
293 | cmd.args[0] = 0x52; | |
294 | cmd.args[1] = c->stream_id & 0xff; | |
295 | cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1; | |
296 | cmd.wlen = 3; | |
297 | cmd.rlen = 1; | |
6307b560 | 298 | ret = si2168_cmd_execute(client, &cmd); |
e395e573 C |
299 | if (ret) |
300 | goto err; | |
301 | } | |
302 | ||
845f3505 AP |
303 | memcpy(cmd.args, "\x51\x03", 2); |
304 | cmd.wlen = 2; | |
305 | cmd.rlen = 12; | |
6307b560 | 306 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
307 | if (ret) |
308 | goto err; | |
309 | ||
310 | memcpy(cmd.args, "\x12\x08\x04", 3); | |
311 | cmd.wlen = 3; | |
312 | cmd.rlen = 3; | |
6307b560 | 313 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
314 | if (ret) |
315 | goto err; | |
316 | ||
845f3505 AP |
317 | memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); |
318 | cmd.wlen = 6; | |
1d518c27 | 319 | cmd.rlen = 4; |
6307b560 | 320 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
321 | if (ret) |
322 | goto err; | |
323 | ||
324 | memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); | |
325 | cmd.wlen = 6; | |
1d518c27 | 326 | cmd.rlen = 4; |
6307b560 | 327 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
328 | if (ret) |
329 | goto err; | |
330 | ||
845f3505 AP |
331 | memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); |
332 | cmd.wlen = 6; | |
1d518c27 | 333 | cmd.rlen = 4; |
6307b560 | 334 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
335 | if (ret) |
336 | goto err; | |
337 | ||
338 | memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); | |
bffab93c | 339 | cmd.args[4] = delivery_system | bandwidth; |
845f3505 | 340 | cmd.wlen = 6; |
1d518c27 | 341 | cmd.rlen = 4; |
6307b560 | 342 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
343 | if (ret) |
344 | goto err; | |
345 | ||
32bf8818 LA |
346 | /* set DVB-C symbol rate */ |
347 | if (c->delivery_system == SYS_DVBC_ANNEX_A) { | |
348 | memcpy(cmd.args, "\x14\x00\x02\x11", 4); | |
346d4900 | 349 | cmd.args[4] = ((c->symbol_rate / 1000) >> 0) & 0xff; |
32bf8818 LA |
350 | cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff; |
351 | cmd.wlen = 6; | |
352 | cmd.rlen = 4; | |
6307b560 | 353 | ret = si2168_cmd_execute(client, &cmd); |
32bf8818 LA |
354 | if (ret) |
355 | goto err; | |
356 | } | |
357 | ||
845f3505 AP |
358 | memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); |
359 | cmd.wlen = 6; | |
1d518c27 | 360 | cmd.rlen = 4; |
6307b560 | 361 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
362 | if (ret) |
363 | goto err; | |
364 | ||
52791979 | 365 | memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6); |
bd01c766 | 366 | cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; |
845f3505 | 367 | cmd.wlen = 6; |
1d518c27 | 368 | cmd.rlen = 4; |
6307b560 | 369 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
370 | if (ret) |
371 | goto err; | |
372 | ||
52791979 | 373 | memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6); |
bd01c766 | 374 | cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; |
845f3505 | 375 | cmd.wlen = 6; |
1d518c27 | 376 | cmd.rlen = 4; |
6307b560 | 377 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
378 | if (ret) |
379 | goto err; | |
380 | ||
381 | memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); | |
382 | cmd.wlen = 6; | |
1d518c27 | 383 | cmd.rlen = 4; |
6307b560 | 384 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
385 | if (ret) |
386 | goto err; | |
387 | ||
43911776 OS |
388 | memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6); |
389 | cmd.wlen = 6; | |
390 | cmd.rlen = 4; | |
6307b560 | 391 | ret = si2168_cmd_execute(client, &cmd); |
43911776 OS |
392 | if (ret) |
393 | goto err; | |
394 | ||
888680ff | 395 | memcpy(cmd.args, "\x85", 1); |
845f3505 AP |
396 | cmd.wlen = 1; |
397 | cmd.rlen = 1; | |
6307b560 | 398 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
399 | if (ret) |
400 | goto err; | |
401 | ||
bd01c766 | 402 | dev->delivery_system = c->delivery_system; |
845f3505 AP |
403 | |
404 | return 0; | |
405 | err: | |
3de35835 | 406 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
407 | return ret; |
408 | } | |
409 | ||
410 | static int si2168_init(struct dvb_frontend *fe) | |
411 | { | |
6307b560 AP |
412 | struct i2c_client *client = fe->demodulator_priv; |
413 | struct si2168_dev *dev = i2c_get_clientdata(client); | |
8393c003 | 414 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
845f3505 | 415 | int ret, len, remaining; |
58f6693f | 416 | const struct firmware *fw; |
845f3505 AP |
417 | struct si2168_cmd cmd; |
418 | ||
3de35835 | 419 | dev_dbg(&client->dev, "\n"); |
845f3505 | 420 | |
8e417224 | 421 | /* initialize */ |
888680ff | 422 | memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); |
845f3505 AP |
423 | cmd.wlen = 13; |
424 | cmd.rlen = 0; | |
6307b560 | 425 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
426 | if (ret) |
427 | goto err; | |
428 | ||
6ab1e943 | 429 | if (dev->warm) { |
8e417224 OS |
430 | /* resume */ |
431 | memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8); | |
432 | cmd.wlen = 8; | |
433 | cmd.rlen = 1; | |
6307b560 | 434 | ret = si2168_cmd_execute(client, &cmd); |
8e417224 OS |
435 | if (ret) |
436 | goto err; | |
437 | ||
438 | memcpy(cmd.args, "\x85", 1); | |
439 | cmd.wlen = 1; | |
440 | cmd.rlen = 1; | |
6307b560 | 441 | ret = si2168_cmd_execute(client, &cmd); |
8e417224 OS |
442 | if (ret) |
443 | goto err; | |
444 | ||
445 | goto warm; | |
446 | } | |
447 | ||
448 | /* power up */ | |
888680ff | 449 | memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); |
845f3505 AP |
450 | cmd.wlen = 8; |
451 | cmd.rlen = 1; | |
6307b560 | 452 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
453 | if (ret) |
454 | goto err; | |
455 | ||
845f3505 | 456 | /* request the firmware, this will block and timeout */ |
6ab1e943 | 457 | ret = request_firmware(&fw, dev->firmware_name, &client->dev); |
845f3505 | 458 | if (ret) { |
b6b6fd6f | 459 | /* fallback mechanism to handle old name for Si2168 B40 fw */ |
6ab1e943 AP |
460 | if (dev->chip_id == SI2168_CHIP_ID_B40) { |
461 | dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK; | |
462 | ret = request_firmware(&fw, dev->firmware_name, | |
463 | &client->dev); | |
c9cb0820 | 464 | } |
b6b6fd6f AP |
465 | |
466 | if (ret == 0) { | |
3de35835 | 467 | dev_notice(&client->dev, |
37b4e43d OS |
468 | "please install firmware file '%s'\n", |
469 | SI2168_B40_FIRMWARE); | |
b6b6fd6f | 470 | } else { |
3de35835 | 471 | dev_err(&client->dev, |
37b4e43d | 472 | "firmware file '%s' not found\n", |
6ab1e943 | 473 | dev->firmware_name); |
346d4900 | 474 | goto err_release_firmware; |
c9cb0820 | 475 | } |
845f3505 AP |
476 | } |
477 | ||
3de35835 | 478 | dev_info(&client->dev, "downloading firmware from file '%s'\n", |
6ab1e943 | 479 | dev->firmware_name); |
845f3505 | 480 | |
1b97dc98 OS |
481 | if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { |
482 | /* firmware is in the new format */ | |
483 | for (remaining = fw->size; remaining > 0; remaining -= 17) { | |
484 | len = fw->data[fw->size - remaining]; | |
47810b43 LA |
485 | if (len > SI2168_ARGLEN) { |
486 | ret = -EINVAL; | |
487 | break; | |
488 | } | |
1b97dc98 OS |
489 | memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); |
490 | cmd.wlen = len; | |
491 | cmd.rlen = 1; | |
6307b560 | 492 | ret = si2168_cmd_execute(client, &cmd); |
68c16a76 AP |
493 | if (ret) |
494 | break; | |
1b97dc98 | 495 | } |
68c16a76 | 496 | } else if (fw->size % 8 == 0) { |
1b97dc98 | 497 | /* firmware is in the old format */ |
68c16a76 AP |
498 | for (remaining = fw->size; remaining > 0; remaining -= 8) { |
499 | len = 8; | |
1b97dc98 OS |
500 | memcpy(cmd.args, &fw->data[fw->size - remaining], len); |
501 | cmd.wlen = len; | |
502 | cmd.rlen = 1; | |
6307b560 | 503 | ret = si2168_cmd_execute(client, &cmd); |
68c16a76 AP |
504 | if (ret) |
505 | break; | |
845f3505 | 506 | } |
68c16a76 AP |
507 | } else { |
508 | /* bad or unknown firmware format */ | |
509 | ret = -EINVAL; | |
510 | } | |
511 | ||
512 | if (ret) { | |
513 | dev_err(&client->dev, "firmware download failed %d\n", ret); | |
514 | goto err_release_firmware; | |
845f3505 AP |
515 | } |
516 | ||
517 | release_firmware(fw); | |
845f3505 | 518 | |
888680ff | 519 | memcpy(cmd.args, "\x01\x01", 2); |
845f3505 AP |
520 | cmd.wlen = 2; |
521 | cmd.rlen = 1; | |
6307b560 | 522 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
523 | if (ret) |
524 | goto err; | |
525 | ||
a594cf21 OS |
526 | /* query firmware version */ |
527 | memcpy(cmd.args, "\x11", 1); | |
528 | cmd.wlen = 1; | |
529 | cmd.rlen = 10; | |
6307b560 | 530 | ret = si2168_cmd_execute(client, &cmd); |
a594cf21 OS |
531 | if (ret) |
532 | goto err; | |
533 | ||
6ab1e943 AP |
534 | dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 | |
535 | (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0; | |
536 | dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", | |
537 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
538 | dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); | |
a594cf21 | 539 | |
389ce398 OS |
540 | /* set ts mode */ |
541 | memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); | |
bd01c766 | 542 | cmd.args[4] |= dev->ts_mode; |
8117a312 OS |
543 | if (dev->ts_clock_gapped) |
544 | cmd.args[4] |= 0x40; | |
389ce398 OS |
545 | cmd.wlen = 6; |
546 | cmd.rlen = 4; | |
6307b560 | 547 | ret = si2168_cmd_execute(client, &cmd); |
389ce398 OS |
548 | if (ret) |
549 | goto err; | |
550 | ||
6ab1e943 | 551 | dev->warm = true; |
45c3cbb1 | 552 | warm: |
8393c003 AP |
553 | /* Init stats here to indicate which stats are supported */ |
554 | c->cnr.len = 1; | |
555 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
556 | c->post_bit_error.len = 1; | |
557 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
558 | c->post_bit_count.len = 1; | |
559 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
c62d29c8 AP |
560 | c->block_error.len = 1; |
561 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
8393c003 | 562 | |
bd01c766 | 563 | dev->active = true; |
845f3505 AP |
564 | |
565 | return 0; | |
346d4900 | 566 | err_release_firmware: |
034e1ec0 ME |
567 | release_firmware(fw); |
568 | err: | |
3de35835 | 569 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
570 | return ret; |
571 | } | |
572 | ||
573 | static int si2168_sleep(struct dvb_frontend *fe) | |
574 | { | |
6307b560 AP |
575 | struct i2c_client *client = fe->demodulator_priv; |
576 | struct si2168_dev *dev = i2c_get_clientdata(client); | |
4de0ed7c AP |
577 | int ret; |
578 | struct si2168_cmd cmd; | |
845f3505 | 579 | |
3de35835 | 580 | dev_dbg(&client->dev, "\n"); |
845f3505 | 581 | |
bd01c766 | 582 | dev->active = false; |
845f3505 | 583 | |
6ab1e943 AP |
584 | /* Firmware B 4.0-11 or later loses warm state during sleep */ |
585 | if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) | |
586 | dev->warm = false; | |
587 | ||
4de0ed7c AP |
588 | memcpy(cmd.args, "\x13", 1); |
589 | cmd.wlen = 1; | |
590 | cmd.rlen = 0; | |
6307b560 | 591 | ret = si2168_cmd_execute(client, &cmd); |
4de0ed7c AP |
592 | if (ret) |
593 | goto err; | |
594 | ||
845f3505 | 595 | return 0; |
4de0ed7c | 596 | err: |
3de35835 | 597 | dev_dbg(&client->dev, "failed=%d\n", ret); |
4de0ed7c | 598 | return ret; |
845f3505 AP |
599 | } |
600 | ||
601 | static int si2168_get_tune_settings(struct dvb_frontend *fe, | |
602 | struct dvb_frontend_tune_settings *s) | |
603 | { | |
604 | s->min_delay_ms = 900; | |
605 | ||
606 | return 0; | |
607 | } | |
608 | ||
58d7b541 | 609 | static int si2168_select(struct i2c_mux_core *muxc, u32 chan) |
845f3505 | 610 | { |
58d7b541 | 611 | struct i2c_client *client = i2c_mux_priv(muxc); |
845f3505 | 612 | int ret; |
d2b72f64 | 613 | struct si2168_cmd cmd; |
845f3505 | 614 | |
d2b72f64 AP |
615 | /* open I2C gate */ |
616 | memcpy(cmd.args, "\xc0\x0d\x01", 3); | |
617 | cmd.wlen = 3; | |
618 | cmd.rlen = 0; | |
e6d7ffcd | 619 | ret = si2168_cmd_execute(client, &cmd); |
d2b72f64 AP |
620 | if (ret) |
621 | goto err; | |
845f3505 | 622 | |
d2b72f64 AP |
623 | return 0; |
624 | err: | |
625 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
845f3505 AP |
626 | return ret; |
627 | } | |
628 | ||
58d7b541 | 629 | static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) |
845f3505 | 630 | { |
58d7b541 | 631 | struct i2c_client *client = i2c_mux_priv(muxc); |
845f3505 | 632 | int ret; |
d2b72f64 | 633 | struct si2168_cmd cmd; |
845f3505 | 634 | |
d2b72f64 AP |
635 | /* close I2C gate */ |
636 | memcpy(cmd.args, "\xc0\x0d\x00", 3); | |
637 | cmd.wlen = 3; | |
638 | cmd.rlen = 0; | |
e6d7ffcd | 639 | ret = si2168_cmd_execute(client, &cmd); |
d2b72f64 AP |
640 | if (ret) |
641 | goto err; | |
845f3505 | 642 | |
d2b72f64 AP |
643 | return 0; |
644 | err: | |
645 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
845f3505 AP |
646 | return ret; |
647 | } | |
648 | ||
649 | static const struct dvb_frontend_ops si2168_ops = { | |
c790885b | 650 | .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, |
845f3505 AP |
651 | .info = { |
652 | .name = "Silicon Labs Si2168", | |
f1ecc5d1 AP |
653 | .symbol_rate_min = 1000000, |
654 | .symbol_rate_max = 7200000, | |
845f3505 AP |
655 | .caps = FE_CAN_FEC_1_2 | |
656 | FE_CAN_FEC_2_3 | | |
657 | FE_CAN_FEC_3_4 | | |
658 | FE_CAN_FEC_5_6 | | |
659 | FE_CAN_FEC_7_8 | | |
660 | FE_CAN_FEC_AUTO | | |
661 | FE_CAN_QPSK | | |
662 | FE_CAN_QAM_16 | | |
663 | FE_CAN_QAM_32 | | |
664 | FE_CAN_QAM_64 | | |
665 | FE_CAN_QAM_128 | | |
666 | FE_CAN_QAM_256 | | |
667 | FE_CAN_QAM_AUTO | | |
668 | FE_CAN_TRANSMISSION_MODE_AUTO | | |
669 | FE_CAN_GUARD_INTERVAL_AUTO | | |
670 | FE_CAN_HIERARCHY_AUTO | | |
671 | FE_CAN_MUTE_TS | | |
327eeb3a OS |
672 | FE_CAN_2G_MODULATION | |
673 | FE_CAN_MULTISTREAM | |
845f3505 AP |
674 | }, |
675 | ||
676 | .get_tune_settings = si2168_get_tune_settings, | |
677 | ||
678 | .init = si2168_init, | |
679 | .sleep = si2168_sleep, | |
680 | ||
681 | .set_frontend = si2168_set_frontend, | |
682 | ||
683 | .read_status = si2168_read_status, | |
684 | }; | |
685 | ||
686 | static int si2168_probe(struct i2c_client *client, | |
687 | const struct i2c_device_id *id) | |
688 | { | |
689 | struct si2168_config *config = client->dev.platform_data; | |
bd01c766 | 690 | struct si2168_dev *dev; |
845f3505 | 691 | int ret; |
6ab1e943 | 692 | struct si2168_cmd cmd; |
845f3505 | 693 | |
37b4e43d | 694 | dev_dbg(&client->dev, "\n"); |
845f3505 | 695 | |
bd01c766 AP |
696 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
697 | if (!dev) { | |
845f3505 | 698 | ret = -ENOMEM; |
37b4e43d | 699 | dev_err(&client->dev, "kzalloc() failed\n"); |
1ee5e7dd | 700 | goto err; |
845f3505 AP |
701 | } |
702 | ||
6ab1e943 | 703 | i2c_set_clientdata(client, dev); |
e6d7ffcd AP |
704 | mutex_init(&dev->i2c_mutex); |
705 | ||
6ab1e943 AP |
706 | /* Initialize */ |
707 | memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); | |
708 | cmd.wlen = 13; | |
709 | cmd.rlen = 0; | |
710 | ret = si2168_cmd_execute(client, &cmd); | |
711 | if (ret) | |
712 | goto err_kfree; | |
713 | ||
714 | /* Power up */ | |
715 | memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); | |
716 | cmd.wlen = 8; | |
717 | cmd.rlen = 1; | |
718 | ret = si2168_cmd_execute(client, &cmd); | |
719 | if (ret) | |
720 | goto err_kfree; | |
721 | ||
722 | /* Query chip revision */ | |
723 | memcpy(cmd.args, "\x02", 1); | |
724 | cmd.wlen = 1; | |
725 | cmd.rlen = 13; | |
726 | ret = si2168_cmd_execute(client, &cmd); | |
727 | if (ret) | |
728 | goto err_kfree; | |
729 | ||
730 | dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | | |
731 | cmd.args[3] << 8 | cmd.args[4] << 0; | |
732 | ||
733 | switch (dev->chip_id) { | |
734 | case SI2168_CHIP_ID_A20: | |
735 | dev->firmware_name = SI2168_A20_FIRMWARE; | |
736 | break; | |
737 | case SI2168_CHIP_ID_A30: | |
738 | dev->firmware_name = SI2168_A30_FIRMWARE; | |
739 | break; | |
740 | case SI2168_CHIP_ID_B40: | |
741 | dev->firmware_name = SI2168_B40_FIRMWARE; | |
742 | break; | |
50d64462 EP |
743 | case SI2168_CHIP_ID_D60: |
744 | dev->firmware_name = SI2168_D60_FIRMWARE; | |
745 | break; | |
6ab1e943 AP |
746 | default: |
747 | dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", | |
748 | cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); | |
749 | ret = -ENODEV; | |
750 | goto err_kfree; | |
751 | } | |
752 | ||
753 | dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 | | |
754 | (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0; | |
755 | ||
845f3505 | 756 | /* create mux i2c adapter for tuner */ |
58d7b541 | 757 | dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, |
e6d7ffcd | 758 | 1, 0, I2C_MUX_LOCKED, |
58d7b541 PR |
759 | si2168_select, si2168_deselect); |
760 | if (!dev->muxc) { | |
761 | ret = -ENOMEM; | |
346d4900 | 762 | goto err_kfree; |
4d6efc7a | 763 | } |
58d7b541 PR |
764 | dev->muxc->priv = client; |
765 | ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); | |
766 | if (ret) | |
767 | goto err_kfree; | |
845f3505 AP |
768 | |
769 | /* create dvb_frontend */ | |
bd01c766 | 770 | memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); |
6307b560 | 771 | dev->fe.demodulator_priv = client; |
58d7b541 | 772 | *config->i2c_adapter = dev->muxc->adapter[0]; |
bd01c766 AP |
773 | *config->fe = &dev->fe; |
774 | dev->ts_mode = config->ts_mode; | |
775 | dev->ts_clock_inv = config->ts_clock_inv; | |
8117a312 | 776 | dev->ts_clock_gapped = config->ts_clock_gapped; |
845f3505 | 777 | |
6ab1e943 AP |
778 | dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n", |
779 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
780 | dev->version >> 8 & 0xff); | |
781 | dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", | |
782 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
783 | dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); | |
845f3505 | 784 | |
845f3505 | 785 | return 0; |
346d4900 | 786 | err_kfree: |
bd01c766 | 787 | kfree(dev); |
1ee5e7dd | 788 | err: |
37b4e43d | 789 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
790 | return ret; |
791 | } | |
792 | ||
793 | static int si2168_remove(struct i2c_client *client) | |
794 | { | |
bd01c766 | 795 | struct si2168_dev *dev = i2c_get_clientdata(client); |
845f3505 | 796 | |
37b4e43d | 797 | dev_dbg(&client->dev, "\n"); |
845f3505 | 798 | |
58d7b541 | 799 | i2c_mux_del_adapters(dev->muxc); |
845f3505 | 800 | |
bd01c766 AP |
801 | dev->fe.ops.release = NULL; |
802 | dev->fe.demodulator_priv = NULL; | |
845f3505 | 803 | |
bd01c766 | 804 | kfree(dev); |
845f3505 AP |
805 | |
806 | return 0; | |
807 | } | |
808 | ||
346d4900 | 809 | static const struct i2c_device_id si2168_id_table[] = { |
845f3505 AP |
810 | {"si2168", 0}, |
811 | {} | |
812 | }; | |
346d4900 | 813 | MODULE_DEVICE_TABLE(i2c, si2168_id_table); |
845f3505 AP |
814 | |
815 | static struct i2c_driver si2168_driver = { | |
816 | .driver = { | |
e06be1da AP |
817 | .name = "si2168", |
818 | .suppress_bind_attrs = true, | |
845f3505 AP |
819 | }, |
820 | .probe = si2168_probe, | |
821 | .remove = si2168_remove, | |
346d4900 | 822 | .id_table = si2168_id_table, |
845f3505 AP |
823 | }; |
824 | ||
825 | module_i2c_driver(si2168_driver); | |
826 | ||
827 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | |
828 | MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); | |
829 | MODULE_LICENSE("GPL"); | |
635a90cf | 830 | MODULE_FIRMWARE(SI2168_A20_FIRMWARE); |
668aa63c | 831 | MODULE_FIRMWARE(SI2168_A30_FIRMWARE); |
c9cb0820 | 832 | MODULE_FIRMWARE(SI2168_B40_FIRMWARE); |
50d64462 | 833 | MODULE_FIRMWARE(SI2168_D60_FIRMWARE); |