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