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) { |
b6b6fd6f | 451 | /* fallback mechanism to handle old name for Si2168 B40 fw */ |
6ab1e943 AP |
452 | if (dev->chip_id == SI2168_CHIP_ID_B40) { |
453 | dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK; | |
454 | ret = request_firmware(&fw, dev->firmware_name, | |
455 | &client->dev); | |
c9cb0820 | 456 | } |
b6b6fd6f AP |
457 | |
458 | if (ret == 0) { | |
3de35835 | 459 | dev_notice(&client->dev, |
37b4e43d OS |
460 | "please install firmware file '%s'\n", |
461 | SI2168_B40_FIRMWARE); | |
b6b6fd6f | 462 | } else { |
3de35835 | 463 | dev_err(&client->dev, |
37b4e43d | 464 | "firmware file '%s' not found\n", |
6ab1e943 | 465 | dev->firmware_name); |
346d4900 | 466 | goto err_release_firmware; |
c9cb0820 | 467 | } |
845f3505 AP |
468 | } |
469 | ||
3de35835 | 470 | dev_info(&client->dev, "downloading firmware from file '%s'\n", |
6ab1e943 | 471 | dev->firmware_name); |
845f3505 | 472 | |
1b97dc98 OS |
473 | if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { |
474 | /* firmware is in the new format */ | |
475 | for (remaining = fw->size; remaining > 0; remaining -= 17) { | |
476 | len = fw->data[fw->size - remaining]; | |
47810b43 LA |
477 | if (len > SI2168_ARGLEN) { |
478 | ret = -EINVAL; | |
479 | break; | |
480 | } | |
619f6fc3 MG |
481 | cmd_init(&cmd, &fw->data[(fw->size - remaining) + 1], |
482 | len, 1); | |
6307b560 | 483 | ret = si2168_cmd_execute(client, &cmd); |
68c16a76 AP |
484 | if (ret) |
485 | break; | |
1b97dc98 | 486 | } |
68c16a76 | 487 | } else if (fw->size % 8 == 0) { |
1b97dc98 | 488 | /* firmware is in the old format */ |
68c16a76 | 489 | for (remaining = fw->size; remaining > 0; remaining -= 8) { |
619f6fc3 | 490 | cmd_init(&cmd, &fw->data[fw->size - remaining], 8, 1); |
6307b560 | 491 | ret = si2168_cmd_execute(client, &cmd); |
68c16a76 AP |
492 | if (ret) |
493 | break; | |
845f3505 | 494 | } |
68c16a76 AP |
495 | } else { |
496 | /* bad or unknown firmware format */ | |
497 | ret = -EINVAL; | |
498 | } | |
499 | ||
500 | if (ret) { | |
501 | dev_err(&client->dev, "firmware download failed %d\n", ret); | |
502 | goto err_release_firmware; | |
845f3505 AP |
503 | } |
504 | ||
505 | release_firmware(fw); | |
845f3505 | 506 | |
619f6fc3 | 507 | cmd_init(&cmd, "\x01\x01", 2, 1); |
6307b560 | 508 | ret = si2168_cmd_execute(client, &cmd); |
845f3505 AP |
509 | if (ret) |
510 | goto err; | |
511 | ||
a594cf21 | 512 | /* query firmware version */ |
619f6fc3 | 513 | cmd_init(&cmd, "\x11", 1, 10); |
6307b560 | 514 | ret = si2168_cmd_execute(client, &cmd); |
a594cf21 OS |
515 | if (ret) |
516 | goto err; | |
517 | ||
6ab1e943 AP |
518 | dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 | |
519 | (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0; | |
520 | dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", | |
521 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
522 | dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); | |
a594cf21 | 523 | |
1844f498 | 524 | /* set ts mode */ |
44587774 | 525 | ret = si2168_ts_bus_ctrl(fe, 1); |
389ce398 OS |
526 | if (ret) |
527 | goto err; | |
528 | ||
6ab1e943 | 529 | dev->warm = true; |
45c3cbb1 | 530 | warm: |
8393c003 AP |
531 | /* Init stats here to indicate which stats are supported */ |
532 | c->cnr.len = 1; | |
533 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
534 | c->post_bit_error.len = 1; | |
535 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
536 | c->post_bit_count.len = 1; | |
537 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
c62d29c8 AP |
538 | c->block_error.len = 1; |
539 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
8393c003 | 540 | |
bd01c766 | 541 | dev->active = true; |
845f3505 AP |
542 | |
543 | return 0; | |
346d4900 | 544 | err_release_firmware: |
034e1ec0 ME |
545 | release_firmware(fw); |
546 | err: | |
3de35835 | 547 | dev_dbg(&client->dev, "failed=%d\n", ret); |
845f3505 AP |
548 | return ret; |
549 | } | |
550 | ||
551 | static int si2168_sleep(struct dvb_frontend *fe) | |
552 | { | |
6307b560 AP |
553 | struct i2c_client *client = fe->demodulator_priv; |
554 | struct si2168_dev *dev = i2c_get_clientdata(client); | |
4de0ed7c AP |
555 | int ret; |
556 | struct si2168_cmd cmd; | |
845f3505 | 557 | |
3de35835 | 558 | dev_dbg(&client->dev, "\n"); |
845f3505 | 559 | |
bd01c766 | 560 | dev->active = false; |
845f3505 | 561 | |
44587774 | 562 | /* tri-state data bus */ |
1844f498 BL |
563 | ret = si2168_ts_bus_ctrl(fe, 0); |
564 | if (ret) | |
565 | goto err; | |
44587774 | 566 | |
fce61d1d | 567 | /* Firmware later than B 4.0-11 loses warm state during sleep */ |
6ab1e943 AP |
568 | if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) |
569 | dev->warm = false; | |
570 | ||
619f6fc3 | 571 | cmd_init(&cmd, "\x13", 1, 0); |
6307b560 | 572 | ret = si2168_cmd_execute(client, &cmd); |
4de0ed7c AP |
573 | if (ret) |
574 | goto err; | |
575 | ||
845f3505 | 576 | return 0; |
4de0ed7c | 577 | err: |
3de35835 | 578 | dev_dbg(&client->dev, "failed=%d\n", ret); |
4de0ed7c | 579 | return ret; |
845f3505 AP |
580 | } |
581 | ||
582 | static int si2168_get_tune_settings(struct dvb_frontend *fe, | |
583 | struct dvb_frontend_tune_settings *s) | |
584 | { | |
585 | s->min_delay_ms = 900; | |
586 | ||
587 | return 0; | |
588 | } | |
589 | ||
58d7b541 | 590 | static int si2168_select(struct i2c_mux_core *muxc, u32 chan) |
845f3505 | 591 | { |
58d7b541 | 592 | struct i2c_client *client = i2c_mux_priv(muxc); |
845f3505 | 593 | int ret; |
d2b72f64 | 594 | struct si2168_cmd cmd; |
845f3505 | 595 | |
d2b72f64 | 596 | /* open I2C gate */ |
619f6fc3 | 597 | cmd_init(&cmd, "\xc0\x0d\x01", 3, 0); |
e6d7ffcd | 598 | ret = si2168_cmd_execute(client, &cmd); |
d2b72f64 AP |
599 | if (ret) |
600 | goto err; | |
845f3505 | 601 | |
d2b72f64 AP |
602 | return 0; |
603 | err: | |
604 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
845f3505 AP |
605 | return ret; |
606 | } | |
607 | ||
58d7b541 | 608 | static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) |
845f3505 | 609 | { |
58d7b541 | 610 | struct i2c_client *client = i2c_mux_priv(muxc); |
845f3505 | 611 | int ret; |
d2b72f64 | 612 | struct si2168_cmd cmd; |
845f3505 | 613 | |
d2b72f64 | 614 | /* close I2C gate */ |
619f6fc3 | 615 | cmd_init(&cmd, "\xc0\x0d\x00", 3, 0); |
e6d7ffcd | 616 | ret = si2168_cmd_execute(client, &cmd); |
d2b72f64 AP |
617 | if (ret) |
618 | goto err; | |
845f3505 | 619 | |
d2b72f64 AP |
620 | return 0; |
621 | err: | |
622 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
845f3505 AP |
623 | return ret; |
624 | } | |
625 | ||
626 | static const struct dvb_frontend_ops si2168_ops = { | |
c790885b | 627 | .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, |
845f3505 AP |
628 | .info = { |
629 | .name = "Silicon Labs Si2168", | |
4a0bbf48 BL |
630 | .frequency_min_hz = 48 * MHz, |
631 | .frequency_max_hz = 870 * MHz, | |
632 | .frequency_stepsize_hz = 62500, | |
633 | .symbol_rate_min = 1000000, | |
634 | .symbol_rate_max = 7200000, | |
845f3505 AP |
635 | .caps = FE_CAN_FEC_1_2 | |
636 | FE_CAN_FEC_2_3 | | |
637 | FE_CAN_FEC_3_4 | | |
638 | FE_CAN_FEC_5_6 | | |
639 | FE_CAN_FEC_7_8 | | |
640 | FE_CAN_FEC_AUTO | | |
641 | FE_CAN_QPSK | | |
642 | FE_CAN_QAM_16 | | |
643 | FE_CAN_QAM_32 | | |
644 | FE_CAN_QAM_64 | | |
645 | FE_CAN_QAM_128 | | |
646 | FE_CAN_QAM_256 | | |
647 | FE_CAN_QAM_AUTO | | |
648 | FE_CAN_TRANSMISSION_MODE_AUTO | | |
649 | FE_CAN_GUARD_INTERVAL_AUTO | | |
650 | FE_CAN_HIERARCHY_AUTO | | |
651 | FE_CAN_MUTE_TS | | |
327eeb3a OS |
652 | FE_CAN_2G_MODULATION | |
653 | FE_CAN_MULTISTREAM | |
845f3505 AP |
654 | }, |
655 | ||
656 | .get_tune_settings = si2168_get_tune_settings, | |
657 | ||
658 | .init = si2168_init, | |
659 | .sleep = si2168_sleep, | |
660 | ||
661 | .set_frontend = si2168_set_frontend, | |
662 | ||
663 | .read_status = si2168_read_status, | |
664 | }; | |
665 | ||
666 | static int si2168_probe(struct i2c_client *client, | |
667 | const struct i2c_device_id *id) | |
668 | { | |
669 | struct si2168_config *config = client->dev.platform_data; | |
bd01c766 | 670 | struct si2168_dev *dev; |
845f3505 | 671 | int ret; |
6ab1e943 | 672 | struct si2168_cmd cmd; |
845f3505 | 673 | |
37b4e43d | 674 | dev_dbg(&client->dev, "\n"); |
845f3505 | 675 | |
bd01c766 AP |
676 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
677 | if (!dev) { | |
845f3505 | 678 | ret = -ENOMEM; |
1ee5e7dd | 679 | goto err; |
845f3505 AP |
680 | } |
681 | ||
6ab1e943 | 682 | i2c_set_clientdata(client, dev); |
e6d7ffcd AP |
683 | mutex_init(&dev->i2c_mutex); |
684 | ||
6ab1e943 | 685 | /* Initialize */ |
619f6fc3 MG |
686 | cmd_init(&cmd, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", |
687 | 13, 0); | |
6ab1e943 AP |
688 | ret = si2168_cmd_execute(client, &cmd); |
689 | if (ret) | |
690 | goto err_kfree; | |
691 | ||
692 | /* Power up */ | |
619f6fc3 | 693 | cmd_init(&cmd, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8, 1); |
6ab1e943 AP |
694 | ret = si2168_cmd_execute(client, &cmd); |
695 | if (ret) | |
696 | goto err_kfree; | |
697 | ||
698 | /* Query chip revision */ | |
619f6fc3 | 699 | cmd_init(&cmd, "\x02", 1, 13); |
6ab1e943 AP |
700 | ret = si2168_cmd_execute(client, &cmd); |
701 | if (ret) | |
702 | goto err_kfree; | |
703 | ||
704 | dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | | |
705 | cmd.args[3] << 8 | cmd.args[4] << 0; | |
706 | ||
707 | switch (dev->chip_id) { | |
708 | case SI2168_CHIP_ID_A20: | |
709 | dev->firmware_name = SI2168_A20_FIRMWARE; | |
710 | break; | |
711 | case SI2168_CHIP_ID_A30: | |
712 | dev->firmware_name = SI2168_A30_FIRMWARE; | |
713 | break; | |
714 | case SI2168_CHIP_ID_B40: | |
715 | dev->firmware_name = SI2168_B40_FIRMWARE; | |
716 | break; | |
50d64462 EP |
717 | case SI2168_CHIP_ID_D60: |
718 | dev->firmware_name = SI2168_D60_FIRMWARE; | |
719 | break; | |
6ab1e943 AP |
720 | default: |
721 | dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", | |
722 | cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); | |
723 | ret = -ENODEV; | |
724 | goto err_kfree; | |
725 | } | |
726 | ||
727 | dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 | | |
728 | (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0; | |
729 | ||
845f3505 | 730 | /* create mux i2c adapter for tuner */ |
58d7b541 | 731 | dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, |
e6d7ffcd | 732 | 1, 0, I2C_MUX_LOCKED, |
58d7b541 PR |
733 | si2168_select, si2168_deselect); |
734 | if (!dev->muxc) { | |
735 | ret = -ENOMEM; | |
346d4900 | 736 | goto err_kfree; |
4d6efc7a | 737 | } |
58d7b541 PR |
738 | dev->muxc->priv = client; |
739 | ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); | |
740 | if (ret) | |
741 | goto err_kfree; | |
845f3505 AP |
742 | |
743 | /* create dvb_frontend */ | |
bd01c766 | 744 | memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); |
6307b560 | 745 | dev->fe.demodulator_priv = client; |
58d7b541 | 746 | *config->i2c_adapter = dev->muxc->adapter[0]; |
bd01c766 AP |
747 | *config->fe = &dev->fe; |
748 | dev->ts_mode = config->ts_mode; | |
749 | dev->ts_clock_inv = config->ts_clock_inv; | |
8117a312 | 750 | dev->ts_clock_gapped = config->ts_clock_gapped; |
d4c779bc | 751 | dev->spectral_inversion = config->spectral_inversion; |
845f3505 | 752 | |
6ab1e943 AP |
753 | dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n", |
754 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
755 | dev->version >> 8 & 0xff); | |
756 | dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", | |
757 | dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, | |
758 | dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); | |
845f3505 | 759 | |
845f3505 | 760 | return 0; |
346d4900 | 761 | err_kfree: |
bd01c766 | 762 | kfree(dev); |
1ee5e7dd | 763 | err: |
3061df06 | 764 | dev_warn(&client->dev, "probe failed = %d\n", ret); |
845f3505 AP |
765 | return ret; |
766 | } | |
767 | ||
768 | static int si2168_remove(struct i2c_client *client) | |
769 | { | |
bd01c766 | 770 | struct si2168_dev *dev = i2c_get_clientdata(client); |
845f3505 | 771 | |
37b4e43d | 772 | dev_dbg(&client->dev, "\n"); |
845f3505 | 773 | |
58d7b541 | 774 | i2c_mux_del_adapters(dev->muxc); |
845f3505 | 775 | |
bd01c766 AP |
776 | dev->fe.ops.release = NULL; |
777 | dev->fe.demodulator_priv = NULL; | |
845f3505 | 778 | |
bd01c766 | 779 | kfree(dev); |
845f3505 AP |
780 | |
781 | return 0; | |
782 | } | |
783 | ||
346d4900 | 784 | static const struct i2c_device_id si2168_id_table[] = { |
845f3505 AP |
785 | {"si2168", 0}, |
786 | {} | |
787 | }; | |
346d4900 | 788 | MODULE_DEVICE_TABLE(i2c, si2168_id_table); |
845f3505 AP |
789 | |
790 | static struct i2c_driver si2168_driver = { | |
791 | .driver = { | |
e06be1da AP |
792 | .name = "si2168", |
793 | .suppress_bind_attrs = true, | |
845f3505 AP |
794 | }, |
795 | .probe = si2168_probe, | |
796 | .remove = si2168_remove, | |
346d4900 | 797 | .id_table = si2168_id_table, |
845f3505 AP |
798 | }; |
799 | ||
800 | module_i2c_driver(si2168_driver); | |
801 | ||
802 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | |
803 | MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); | |
804 | MODULE_LICENSE("GPL"); | |
635a90cf | 805 | MODULE_FIRMWARE(SI2168_A20_FIRMWARE); |
668aa63c | 806 | MODULE_FIRMWARE(SI2168_A30_FIRMWARE); |
c9cb0820 | 807 | MODULE_FIRMWARE(SI2168_B40_FIRMWARE); |
50d64462 | 808 | MODULE_FIRMWARE(SI2168_D60_FIRMWARE); |