diff options
author | Antti Palosaari <crope@iki.fi> | 2014-04-10 21:00:50 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-04-23 08:50:23 -0400 |
commit | 845f35052ea94661dd32d80fc95a93d0502345e2 (patch) | |
tree | d62879de0b6475480d0f31051b5f7587875265be | |
parent | 930a873081986393f6e7e0fb9275753c1485277b (diff) |
[media] si2168: Silicon Labs Si2168 DVB-T/T2/C demod driver
Silicon Labs Si2168 DVB-T/T2/C demod driver.
That driver version supports only DVB-T.
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/dvb-frontends/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/si2168.c | 708 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/si2168.h | 23 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/si2168_priv.h | 30 |
5 files changed, 769 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 025fc5496bfc..1469d44acb22 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig | |||
@@ -446,6 +446,13 @@ config DVB_RTL2832 | |||
446 | help | 446 | help |
447 | Say Y when you want to support this frontend. | 447 | Say Y when you want to support this frontend. |
448 | 448 | ||
449 | config DVB_SI2168 | ||
450 | tristate "Silicon Labs Si2168" | ||
451 | depends on DVB_CORE && I2C && I2C_MUX | ||
452 | default m if !MEDIA_SUBDRV_AUTOSELECT | ||
453 | help | ||
454 | Say Y when you want to support this frontend. | ||
455 | |||
449 | comment "DVB-C (cable) frontends" | 456 | comment "DVB-C (cable) frontends" |
450 | depends on DVB_CORE | 457 | depends on DVB_CORE |
451 | 458 | ||
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 282aba2fe8db..dda0bee36f29 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile | |||
@@ -78,6 +78,7 @@ obj-$(CONFIG_DVB_AF9013) += af9013.o | |||
78 | obj-$(CONFIG_DVB_CX24116) += cx24116.o | 78 | obj-$(CONFIG_DVB_CX24116) += cx24116.o |
79 | obj-$(CONFIG_DVB_CX24117) += cx24117.o | 79 | obj-$(CONFIG_DVB_CX24117) += cx24117.o |
80 | obj-$(CONFIG_DVB_SI21XX) += si21xx.o | 80 | obj-$(CONFIG_DVB_SI21XX) += si21xx.o |
81 | obj-$(CONFIG_DVB_SI2168) += si2168.o | ||
81 | obj-$(CONFIG_DVB_STV0288) += stv0288.o | 82 | obj-$(CONFIG_DVB_STV0288) += stv0288.o |
82 | obj-$(CONFIG_DVB_STB6000) += stb6000.o | 83 | obj-$(CONFIG_DVB_STB6000) += stb6000.o |
83 | obj-$(CONFIG_DVB_S921) += s921.o | 84 | obj-$(CONFIG_DVB_S921) += s921.o |
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c new file mode 100644 index 000000000000..eef4e456548d --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.c | |||
@@ -0,0 +1,708 @@ | |||
1 | #include "si2168_priv.h" | ||
2 | |||
3 | static const struct dvb_frontend_ops si2168_ops; | ||
4 | |||
5 | /* execute firmware command */ | ||
6 | static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) | ||
7 | { | ||
8 | int ret; | ||
9 | unsigned long timeout; | ||
10 | |||
11 | mutex_lock(&s->i2c_mutex); | ||
12 | |||
13 | if (cmd->wlen) { | ||
14 | /* write cmd and args for firmware */ | ||
15 | ret = i2c_master_send(s->client, cmd->args, cmd->wlen); | ||
16 | if (ret < 0) { | ||
17 | goto err_mutex_unlock; | ||
18 | } else if (ret != cmd->wlen) { | ||
19 | ret = -EREMOTEIO; | ||
20 | goto err_mutex_unlock; | ||
21 | } | ||
22 | } | ||
23 | |||
24 | if (cmd->rlen) { | ||
25 | /* wait cmd execution terminate */ | ||
26 | #define TIMEOUT 50 | ||
27 | timeout = jiffies + msecs_to_jiffies(TIMEOUT); | ||
28 | while (!time_after(jiffies, timeout)) { | ||
29 | ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); | ||
30 | if (ret < 0) { | ||
31 | goto err_mutex_unlock; | ||
32 | } else if (ret != cmd->rlen) { | ||
33 | ret = -EREMOTEIO; | ||
34 | goto err_mutex_unlock; | ||
35 | } | ||
36 | |||
37 | /* firmware ready? */ | ||
38 | if ((cmd->args[0] >> 7) & 0x01) | ||
39 | break; | ||
40 | } | ||
41 | |||
42 | dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", | ||
43 | __func__, | ||
44 | jiffies_to_msecs(jiffies) - | ||
45 | (jiffies_to_msecs(timeout) - TIMEOUT)); | ||
46 | |||
47 | if (!(cmd->args[0] >> 7) & 0x01) { | ||
48 | ret = -ETIMEDOUT; | ||
49 | goto err_mutex_unlock; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | ret = 0; | ||
54 | |||
55 | err_mutex_unlock: | ||
56 | mutex_unlock(&s->i2c_mutex); | ||
57 | if (ret) | ||
58 | goto err; | ||
59 | |||
60 | return 0; | ||
61 | err: | ||
62 | dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); | ||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) | ||
67 | { | ||
68 | struct si2168 *s = fe->demodulator_priv; | ||
69 | int ret; | ||
70 | struct si2168_cmd cmd; | ||
71 | |||
72 | *status = 0; | ||
73 | |||
74 | if (!s->active) { | ||
75 | ret = -EAGAIN; | ||
76 | goto err; | ||
77 | } | ||
78 | |||
79 | cmd.args[0] = 0xa0; | ||
80 | cmd.args[1] = 0x01; | ||
81 | cmd.wlen = 2; | ||
82 | cmd.rlen = 13; | ||
83 | ret = si2168_cmd_execute(s, &cmd); | ||
84 | if (ret) | ||
85 | goto err; | ||
86 | |||
87 | /* | ||
88 | * Possible values seen, in order from strong signal to weak: | ||
89 | * 16 0001 0110 full lock | ||
90 | * 1e 0001 1110 partial lock | ||
91 | * 1a 0001 1010 partial lock | ||
92 | * 18 0001 1000 no lock | ||
93 | * | ||
94 | * [b3:b1] lock bits | ||
95 | * [b4] statistics ready? Set in a few secs after lock is gained. | ||
96 | */ | ||
97 | |||
98 | switch ((cmd.args[2] >> 0) & 0x0f) { | ||
99 | case 0x0a: | ||
100 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | ||
101 | break; | ||
102 | case 0x0e: | ||
103 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI; | ||
104 | break; | ||
105 | case 0x06: | ||
106 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | | ||
107 | FE_HAS_SYNC | FE_HAS_LOCK; | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | s->fe_status = *status; | ||
112 | |||
113 | dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n", | ||
114 | __func__, *status, cmd.rlen, cmd.args); | ||
115 | |||
116 | return 0; | ||
117 | err: | ||
118 | dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static int si2168_set_frontend(struct dvb_frontend *fe) | ||
123 | { | ||
124 | struct si2168 *s = fe->demodulator_priv; | ||
125 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
126 | int ret; | ||
127 | struct si2168_cmd cmd; | ||
128 | u8 bandwidth; | ||
129 | |||
130 | dev_dbg(&s->client->dev, | ||
131 | "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n", | ||
132 | __func__, c->delivery_system, c->modulation, | ||
133 | c->frequency, c->bandwidth_hz, c->symbol_rate, | ||
134 | c->inversion); | ||
135 | |||
136 | if (!s->active) { | ||
137 | ret = -EAGAIN; | ||
138 | goto err; | ||
139 | } | ||
140 | |||
141 | switch (c->bandwidth_hz) { | ||
142 | case 5000000: | ||
143 | bandwidth = 0x25; | ||
144 | break; | ||
145 | case 6000000: | ||
146 | bandwidth = 0x26; | ||
147 | break; | ||
148 | case 7000000: | ||
149 | bandwidth = 0x27; | ||
150 | break; | ||
151 | case 8000000: | ||
152 | bandwidth = 0x28; | ||
153 | break; | ||
154 | default: | ||
155 | ret = -EINVAL; | ||
156 | goto err; | ||
157 | } | ||
158 | |||
159 | /* program tuner */ | ||
160 | if (fe->ops.tuner_ops.set_params) { | ||
161 | ret = fe->ops.tuner_ops.set_params(fe); | ||
162 | if (ret) | ||
163 | goto err; | ||
164 | } | ||
165 | |||
166 | memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5); | ||
167 | cmd.wlen = 5; | ||
168 | cmd.rlen = 5; | ||
169 | ret = si2168_cmd_execute(s, &cmd); | ||
170 | if (ret) | ||
171 | goto err; | ||
172 | |||
173 | memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6); | ||
174 | cmd.wlen = 6; | ||
175 | cmd.rlen = 3; | ||
176 | ret = si2168_cmd_execute(s, &cmd); | ||
177 | if (ret) | ||
178 | goto err; | ||
179 | |||
180 | memcpy(cmd.args, "\x51\x03", 2); | ||
181 | cmd.wlen = 2; | ||
182 | cmd.rlen = 12; | ||
183 | ret = si2168_cmd_execute(s, &cmd); | ||
184 | if (ret) | ||
185 | goto err; | ||
186 | |||
187 | memcpy(cmd.args, "\x12\x08\x04", 3); | ||
188 | cmd.wlen = 3; | ||
189 | cmd.rlen = 3; | ||
190 | ret = si2168_cmd_execute(s, &cmd); | ||
191 | if (ret) | ||
192 | goto err; | ||
193 | |||
194 | memcpy(cmd.args, "\x14\x00\x01\x04\x00\x00", 6); | ||
195 | cmd.wlen = 6; | ||
196 | cmd.rlen = 1; | ||
197 | ret = si2168_cmd_execute(s, &cmd); | ||
198 | if (ret) | ||
199 | goto err; | ||
200 | |||
201 | memcpy(cmd.args, "\x14\x00\x03\x10\x17\x00", 6); | ||
202 | cmd.wlen = 6; | ||
203 | cmd.rlen = 1; | ||
204 | ret = si2168_cmd_execute(s, &cmd); | ||
205 | if (ret) | ||
206 | goto err; | ||
207 | |||
208 | memcpy(cmd.args, "\x14\x00\x02\x10\x15\x00", 6); | ||
209 | cmd.wlen = 6; | ||
210 | cmd.rlen = 1; | ||
211 | ret = si2168_cmd_execute(s, &cmd); | ||
212 | if (ret) | ||
213 | goto err; | ||
214 | |||
215 | memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); | ||
216 | cmd.wlen = 6; | ||
217 | cmd.rlen = 1; | ||
218 | ret = si2168_cmd_execute(s, &cmd); | ||
219 | if (ret) | ||
220 | goto err; | ||
221 | |||
222 | memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); | ||
223 | cmd.wlen = 6; | ||
224 | cmd.rlen = 1; | ||
225 | ret = si2168_cmd_execute(s, &cmd); | ||
226 | if (ret) | ||
227 | goto err; | ||
228 | |||
229 | memcpy(cmd.args, "\x14\x00\x0b\x10\x88\x13", 6); | ||
230 | cmd.wlen = 6; | ||
231 | cmd.rlen = 1; | ||
232 | ret = si2168_cmd_execute(s, &cmd); | ||
233 | if (ret) | ||
234 | goto err; | ||
235 | |||
236 | memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); | ||
237 | cmd.wlen = 6; | ||
238 | cmd.rlen = 1; | ||
239 | ret = si2168_cmd_execute(s, &cmd); | ||
240 | if (ret) | ||
241 | goto err; | ||
242 | |||
243 | memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); | ||
244 | cmd.args[4] = bandwidth; | ||
245 | cmd.wlen = 6; | ||
246 | cmd.rlen = 1; | ||
247 | ret = si2168_cmd_execute(s, &cmd); | ||
248 | if (ret) | ||
249 | goto err; | ||
250 | |||
251 | memcpy(cmd.args, "\x14\x00\x04\x10\x15\x00", 6); | ||
252 | cmd.wlen = 6; | ||
253 | cmd.rlen = 1; | ||
254 | ret = si2168_cmd_execute(s, &cmd); | ||
255 | if (ret) | ||
256 | goto err; | ||
257 | |||
258 | memcpy(cmd.args, "\x14\x00\x05\x10\xa1\x00", 6); | ||
259 | cmd.wlen = 6; | ||
260 | cmd.rlen = 1; | ||
261 | ret = si2168_cmd_execute(s, &cmd); | ||
262 | if (ret) | ||
263 | goto err; | ||
264 | |||
265 | memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); | ||
266 | cmd.wlen = 6; | ||
267 | cmd.rlen = 1; | ||
268 | ret = si2168_cmd_execute(s, &cmd); | ||
269 | if (ret) | ||
270 | goto err; | ||
271 | |||
272 | memcpy(cmd.args, "\x14\x00\x0d\x10\xd0\x02", 6); | ||
273 | cmd.wlen = 6; | ||
274 | cmd.rlen = 1; | ||
275 | ret = si2168_cmd_execute(s, &cmd); | ||
276 | if (ret) | ||
277 | goto err; | ||
278 | |||
279 | memcpy(cmd.args, "\x14\x00\x01\x10\x00\x00", 6); | ||
280 | cmd.wlen = 6; | ||
281 | cmd.rlen = 1; | ||
282 | ret = si2168_cmd_execute(s, &cmd); | ||
283 | if (ret) | ||
284 | goto err; | ||
285 | |||
286 | memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6); | ||
287 | cmd.wlen = 6; | ||
288 | cmd.rlen = 1; | ||
289 | ret = si2168_cmd_execute(s, &cmd); | ||
290 | if (ret) | ||
291 | goto err; | ||
292 | |||
293 | memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6); | ||
294 | cmd.wlen = 6; | ||
295 | cmd.rlen = 1; | ||
296 | ret = si2168_cmd_execute(s, &cmd); | ||
297 | if (ret) | ||
298 | goto err; | ||
299 | |||
300 | memcpy(cmd.args, "\x14\x00\x04\x03\x00\x00", 6); | ||
301 | cmd.wlen = 6; | ||
302 | cmd.rlen = 1; | ||
303 | ret = si2168_cmd_execute(s, &cmd); | ||
304 | if (ret) | ||
305 | goto err; | ||
306 | |||
307 | memcpy(cmd.args, "\x14\x00\x03\x03\x00\x00", 6); | ||
308 | cmd.wlen = 6; | ||
309 | cmd.rlen = 1; | ||
310 | ret = si2168_cmd_execute(s, &cmd); | ||
311 | if (ret) | ||
312 | goto err; | ||
313 | |||
314 | memcpy(cmd.args, "\x14\x00\x08\x03\x00\x00", 6); | ||
315 | cmd.wlen = 6; | ||
316 | cmd.rlen = 1; | ||
317 | ret = si2168_cmd_execute(s, &cmd); | ||
318 | if (ret) | ||
319 | goto err; | ||
320 | |||
321 | memcpy(cmd.args, "\x14\x00\x07\x03\x01\x02", 6); | ||
322 | cmd.wlen = 6; | ||
323 | cmd.rlen = 1; | ||
324 | ret = si2168_cmd_execute(s, &cmd); | ||
325 | if (ret) | ||
326 | goto err; | ||
327 | |||
328 | memcpy(cmd.args, "\x14\x00\x06\x03\x00\x00", 6); | ||
329 | cmd.wlen = 6; | ||
330 | cmd.rlen = 1; | ||
331 | ret = si2168_cmd_execute(s, &cmd); | ||
332 | if (ret) | ||
333 | goto err; | ||
334 | |||
335 | memcpy(cmd.args, "\x14\x00\x05\x03\x00\x00", 6); | ||
336 | cmd.wlen = 6; | ||
337 | cmd.rlen = 1; | ||
338 | ret = si2168_cmd_execute(s, &cmd); | ||
339 | if (ret) | ||
340 | goto err; | ||
341 | |||
342 | memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x40", 6); | ||
343 | cmd.wlen = 6; | ||
344 | cmd.rlen = 1; | ||
345 | ret = si2168_cmd_execute(s, &cmd); | ||
346 | if (ret) | ||
347 | goto err; | ||
348 | |||
349 | memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); | ||
350 | cmd.wlen = 6; | ||
351 | cmd.rlen = 1; | ||
352 | ret = si2168_cmd_execute(s, &cmd); | ||
353 | if (ret) | ||
354 | goto err; | ||
355 | |||
356 | memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); | ||
357 | cmd.wlen = 6; | ||
358 | cmd.rlen = 1; | ||
359 | ret = si2168_cmd_execute(s, &cmd); | ||
360 | if (ret) | ||
361 | goto err; | ||
362 | |||
363 | cmd.args[0] = 0x85; | ||
364 | cmd.wlen = 1; | ||
365 | cmd.rlen = 1; | ||
366 | ret = si2168_cmd_execute(s, &cmd); | ||
367 | if (ret) | ||
368 | goto err; | ||
369 | |||
370 | s->delivery_system = c->delivery_system; | ||
371 | |||
372 | return 0; | ||
373 | err: | ||
374 | dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | static int si2168_init(struct dvb_frontend *fe) | ||
379 | { | ||
380 | struct si2168 *s = fe->demodulator_priv; | ||
381 | int ret, len, remaining; | ||
382 | const struct firmware *fw = NULL; | ||
383 | u8 *fw_file = SI2168_FIRMWARE; | ||
384 | const unsigned int i2c_wr_max = 8; | ||
385 | struct si2168_cmd cmd; | ||
386 | |||
387 | dev_dbg(&s->client->dev, "%s:\n", __func__); | ||
388 | |||
389 | cmd.args[0] = 0x13; | ||
390 | cmd.wlen = 1; | ||
391 | cmd.rlen = 0; | ||
392 | ret = si2168_cmd_execute(s, &cmd); | ||
393 | if (ret) | ||
394 | goto err; | ||
395 | |||
396 | cmd.args[0] = 0xc0; | ||
397 | cmd.args[1] = 0x12; | ||
398 | cmd.args[2] = 0x00; | ||
399 | cmd.args[3] = 0x0c; | ||
400 | cmd.args[4] = 0x00; | ||
401 | cmd.args[5] = 0x0d; | ||
402 | cmd.args[6] = 0x16; | ||
403 | cmd.args[7] = 0x00; | ||
404 | cmd.args[8] = 0x00; | ||
405 | cmd.args[9] = 0x00; | ||
406 | cmd.args[10] = 0x00; | ||
407 | cmd.args[11] = 0x00; | ||
408 | cmd.args[12] = 0x00; | ||
409 | cmd.wlen = 13; | ||
410 | cmd.rlen = 0; | ||
411 | ret = si2168_cmd_execute(s, &cmd); | ||
412 | if (ret) | ||
413 | goto err; | ||
414 | |||
415 | cmd.args[0] = 0xc0; | ||
416 | cmd.args[1] = 0x06; | ||
417 | cmd.args[2] = 0x01; | ||
418 | cmd.args[3] = 0x0f; | ||
419 | cmd.args[4] = 0x00; | ||
420 | cmd.args[5] = 0x20; | ||
421 | cmd.args[6] = 0x20; | ||
422 | cmd.args[7] = 0x01; | ||
423 | cmd.wlen = 8; | ||
424 | cmd.rlen = 1; | ||
425 | ret = si2168_cmd_execute(s, &cmd); | ||
426 | if (ret) | ||
427 | goto err; | ||
428 | |||
429 | cmd.args[0] = 0x02; | ||
430 | cmd.wlen = 1; | ||
431 | cmd.rlen = 13; | ||
432 | ret = si2168_cmd_execute(s, &cmd); | ||
433 | if (ret) | ||
434 | goto err; | ||
435 | |||
436 | cmd.args[0] = 0x05; | ||
437 | cmd.args[1] = 0x00; | ||
438 | cmd.args[2] = 0xaa; | ||
439 | cmd.args[3] = 0x4d; | ||
440 | cmd.args[4] = 0x56; | ||
441 | cmd.args[5] = 0x40; | ||
442 | cmd.args[6] = 0x00; | ||
443 | cmd.args[7] = 0x00; | ||
444 | cmd.wlen = 8; | ||
445 | cmd.rlen = 1; | ||
446 | ret = si2168_cmd_execute(s, &cmd); | ||
447 | if (ret) | ||
448 | goto err; | ||
449 | |||
450 | /* cold state - try to download firmware */ | ||
451 | dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", | ||
452 | KBUILD_MODNAME, si2168_ops.info.name); | ||
453 | |||
454 | /* request the firmware, this will block and timeout */ | ||
455 | ret = request_firmware(&fw, fw_file, &s->client->dev); | ||
456 | if (ret) { | ||
457 | dev_err(&s->client->dev, "%s: firmare file '%s' not found\n", | ||
458 | KBUILD_MODNAME, fw_file); | ||
459 | goto err; | ||
460 | } | ||
461 | |||
462 | dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", | ||
463 | KBUILD_MODNAME, fw_file); | ||
464 | |||
465 | for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) { | ||
466 | len = remaining; | ||
467 | if (len > i2c_wr_max) | ||
468 | len = i2c_wr_max; | ||
469 | |||
470 | memcpy(cmd.args, &fw->data[fw->size - remaining], len); | ||
471 | cmd.wlen = len; | ||
472 | cmd.rlen = 1; | ||
473 | ret = si2168_cmd_execute(s, &cmd); | ||
474 | if (ret) { | ||
475 | dev_err(&s->client->dev, | ||
476 | "%s: firmware download failed=%d\n", | ||
477 | KBUILD_MODNAME, ret); | ||
478 | goto err; | ||
479 | } | ||
480 | } | ||
481 | |||
482 | release_firmware(fw); | ||
483 | fw = NULL; | ||
484 | |||
485 | cmd.args[0] = 0x01; | ||
486 | cmd.args[1] = 0x01; | ||
487 | cmd.wlen = 2; | ||
488 | cmd.rlen = 1; | ||
489 | ret = si2168_cmd_execute(s, &cmd); | ||
490 | if (ret) | ||
491 | goto err; | ||
492 | |||
493 | dev_info(&s->client->dev, "%s: found a '%s' in warm state\n", | ||
494 | KBUILD_MODNAME, si2168_ops.info.name); | ||
495 | |||
496 | s->active = true; | ||
497 | |||
498 | return 0; | ||
499 | err: | ||
500 | if (fw) | ||
501 | release_firmware(fw); | ||
502 | |||
503 | dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); | ||
504 | return ret; | ||
505 | } | ||
506 | |||
507 | static int si2168_sleep(struct dvb_frontend *fe) | ||
508 | { | ||
509 | struct si2168 *s = fe->demodulator_priv; | ||
510 | |||
511 | dev_dbg(&s->client->dev, "%s:\n", __func__); | ||
512 | |||
513 | s->active = false; | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int si2168_get_tune_settings(struct dvb_frontend *fe, | ||
519 | struct dvb_frontend_tune_settings *s) | ||
520 | { | ||
521 | s->min_delay_ms = 900; | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | /* | ||
527 | * I2C gate logic | ||
528 | * We must use unlocked i2c_transfer() here because I2C lock is already taken | ||
529 | * by tuner driver. | ||
530 | */ | ||
531 | static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) | ||
532 | { | ||
533 | struct si2168 *s = mux_priv; | ||
534 | int ret; | ||
535 | struct i2c_msg gate_open_msg = { | ||
536 | .addr = s->client->addr, | ||
537 | .flags = 0, | ||
538 | .len = 3, | ||
539 | .buf = "\xc0\x0d\x01", | ||
540 | }; | ||
541 | |||
542 | mutex_lock(&s->i2c_mutex); | ||
543 | |||
544 | /* open tuner I2C gate */ | ||
545 | ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1); | ||
546 | if (ret != 1) { | ||
547 | dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", | ||
548 | KBUILD_MODNAME, ret); | ||
549 | if (ret >= 0) | ||
550 | ret = -EREMOTEIO; | ||
551 | } else { | ||
552 | ret = 0; | ||
553 | } | ||
554 | |||
555 | return ret; | ||
556 | } | ||
557 | |||
558 | static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) | ||
559 | { | ||
560 | struct si2168 *s = mux_priv; | ||
561 | int ret; | ||
562 | struct i2c_msg gate_close_msg = { | ||
563 | .addr = s->client->addr, | ||
564 | .flags = 0, | ||
565 | .len = 3, | ||
566 | .buf = "\xc0\x0d\x00", | ||
567 | }; | ||
568 | |||
569 | /* close tuner I2C gate */ | ||
570 | ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1); | ||
571 | if (ret != 1) { | ||
572 | dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", | ||
573 | KBUILD_MODNAME, ret); | ||
574 | if (ret >= 0) | ||
575 | ret = -EREMOTEIO; | ||
576 | } else { | ||
577 | ret = 0; | ||
578 | } | ||
579 | |||
580 | mutex_unlock(&s->i2c_mutex); | ||
581 | |||
582 | return ret; | ||
583 | } | ||
584 | |||
585 | static const struct dvb_frontend_ops si2168_ops = { | ||
586 | .delsys = {SYS_DVBT}, | ||
587 | .info = { | ||
588 | .name = "Silicon Labs Si2168", | ||
589 | .caps = FE_CAN_FEC_1_2 | | ||
590 | FE_CAN_FEC_2_3 | | ||
591 | FE_CAN_FEC_3_4 | | ||
592 | FE_CAN_FEC_5_6 | | ||
593 | FE_CAN_FEC_7_8 | | ||
594 | FE_CAN_FEC_AUTO | | ||
595 | FE_CAN_QPSK | | ||
596 | FE_CAN_QAM_16 | | ||
597 | FE_CAN_QAM_32 | | ||
598 | FE_CAN_QAM_64 | | ||
599 | FE_CAN_QAM_128 | | ||
600 | FE_CAN_QAM_256 | | ||
601 | FE_CAN_QAM_AUTO | | ||
602 | FE_CAN_TRANSMISSION_MODE_AUTO | | ||
603 | FE_CAN_GUARD_INTERVAL_AUTO | | ||
604 | FE_CAN_HIERARCHY_AUTO | | ||
605 | FE_CAN_MUTE_TS | | ||
606 | FE_CAN_2G_MODULATION | ||
607 | }, | ||
608 | |||
609 | .get_tune_settings = si2168_get_tune_settings, | ||
610 | |||
611 | .init = si2168_init, | ||
612 | .sleep = si2168_sleep, | ||
613 | |||
614 | .set_frontend = si2168_set_frontend, | ||
615 | |||
616 | .read_status = si2168_read_status, | ||
617 | }; | ||
618 | |||
619 | static int si2168_probe(struct i2c_client *client, | ||
620 | const struct i2c_device_id *id) | ||
621 | { | ||
622 | struct si2168_config *config = client->dev.platform_data; | ||
623 | struct si2168 *s; | ||
624 | int ret; | ||
625 | struct si2168_cmd cmd; | ||
626 | |||
627 | dev_dbg(&client->dev, "%s:\n", __func__); | ||
628 | |||
629 | s = kzalloc(sizeof(struct si2168), GFP_KERNEL); | ||
630 | if (!s) { | ||
631 | ret = -ENOMEM; | ||
632 | dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); | ||
633 | goto err; | ||
634 | } | ||
635 | |||
636 | s->client = client; | ||
637 | mutex_init(&s->i2c_mutex); | ||
638 | |||
639 | /* check if the demod is there */ | ||
640 | cmd.wlen = 0; | ||
641 | cmd.rlen = 1; | ||
642 | ret = si2168_cmd_execute(s, &cmd); | ||
643 | if (ret) | ||
644 | goto err; | ||
645 | |||
646 | /* create mux i2c adapter for tuner */ | ||
647 | s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, | ||
648 | 0, 0, 0, si2168_select, si2168_deselect); | ||
649 | if (s->adapter == NULL) | ||
650 | goto err; | ||
651 | |||
652 | /* create dvb_frontend */ | ||
653 | memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); | ||
654 | s->fe.demodulator_priv = s; | ||
655 | |||
656 | *config->i2c_adapter = s->adapter; | ||
657 | *config->fe = &s->fe; | ||
658 | |||
659 | i2c_set_clientdata(client, s); | ||
660 | |||
661 | dev_info(&s->client->dev, | ||
662 | "%s: Silicon Labs Si2168 successfully attached\n", | ||
663 | KBUILD_MODNAME); | ||
664 | return 0; | ||
665 | err: | ||
666 | kfree(s); | ||
667 | dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); | ||
668 | return ret; | ||
669 | } | ||
670 | |||
671 | static int si2168_remove(struct i2c_client *client) | ||
672 | { | ||
673 | struct si2168 *s = i2c_get_clientdata(client); | ||
674 | |||
675 | dev_dbg(&client->dev, "%s:\n", __func__); | ||
676 | |||
677 | i2c_del_mux_adapter(s->adapter); | ||
678 | |||
679 | s->fe.ops.release = NULL; | ||
680 | s->fe.demodulator_priv = NULL; | ||
681 | |||
682 | kfree(s); | ||
683 | |||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | static const struct i2c_device_id si2168_id[] = { | ||
688 | {"si2168", 0}, | ||
689 | {} | ||
690 | }; | ||
691 | MODULE_DEVICE_TABLE(i2c, si2168_id); | ||
692 | |||
693 | static struct i2c_driver si2168_driver = { | ||
694 | .driver = { | ||
695 | .owner = THIS_MODULE, | ||
696 | .name = "si2168", | ||
697 | }, | ||
698 | .probe = si2168_probe, | ||
699 | .remove = si2168_remove, | ||
700 | .id_table = si2168_id, | ||
701 | }; | ||
702 | |||
703 | module_i2c_driver(si2168_driver); | ||
704 | |||
705 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | ||
706 | MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); | ||
707 | MODULE_LICENSE("GPL"); | ||
708 | MODULE_FIRMWARE(SI2168_FIRMWARE); | ||
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h new file mode 100644 index 000000000000..5a801aa382f7 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.h | |||
@@ -0,0 +1,23 @@ | |||
1 | #ifndef SI2168_H | ||
2 | #define SI2168_H | ||
3 | |||
4 | #include <linux/dvb/frontend.h> | ||
5 | /* | ||
6 | * I2C address | ||
7 | * 0x64 | ||
8 | */ | ||
9 | struct si2168_config { | ||
10 | /* | ||
11 | * frontend | ||
12 | * returned by driver | ||
13 | */ | ||
14 | struct dvb_frontend **fe; | ||
15 | |||
16 | /* | ||
17 | * tuner I2C adapter | ||
18 | * returned by driver | ||
19 | */ | ||
20 | struct i2c_adapter **i2c_adapter; | ||
21 | }; | ||
22 | |||
23 | #endif | ||
diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h new file mode 100644 index 000000000000..36463246c6a8 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168_priv.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef SI2168_PRIV_H | ||
2 | #define SI2168_PRIV_H | ||
3 | |||
4 | #include "si2168.h" | ||
5 | #include "dvb_frontend.h" | ||
6 | #include <linux/firmware.h> | ||
7 | #include <linux/i2c-mux.h> | ||
8 | |||
9 | #define SI2168_FIRMWARE "dvb-demod-si2168-01.fw" | ||
10 | |||
11 | /* state struct */ | ||
12 | struct si2168 { | ||
13 | struct i2c_client *client; | ||
14 | struct i2c_adapter *adapter; | ||
15 | struct mutex i2c_mutex; | ||
16 | struct dvb_frontend fe; | ||
17 | fe_delivery_system_t delivery_system; | ||
18 | fe_status_t fe_status; | ||
19 | bool active; | ||
20 | }; | ||
21 | |||
22 | /* firmare command struct */ | ||
23 | #define SI2157_ARGLEN 30 | ||
24 | struct si2168_cmd { | ||
25 | u8 args[SI2157_ARGLEN]; | ||
26 | unsigned wlen; | ||
27 | unsigned rlen; | ||
28 | }; | ||
29 | |||
30 | #endif | ||