1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones |
4 | * |
5 | * Copyright (c) Henry Wang <Henry.wang@AzureWave.com> |
6 | * |
7 | * This driver was made publicly available by Terratec, at: |
8 | * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz |
9 | * The original driver's license is GPL, as declared with MODULE_LICENSE() |
10 | * |
11 | * Copyright (c) 2010-2012 Mauro Carvalho Chehab |
12 | * Driver modified by in order to work with upstream drxk driver, and |
13 | * tons of bugs got fixed, and converted to use dvb-usb-v2. |
14 | */ |
15 | |
16 | #include "drxk.h" |
17 | #include "mt2063.h" |
18 | #include <media/dvb_ca_en50221.h> |
19 | #include "dvb_usb.h" |
20 | #include "cypress_firmware.h" |
21 | |
22 | #define AZ6007_FIRMWARE "dvb-usb-terratec-h7-az6007.fw" |
23 | |
24 | static int az6007_xfer_debug; |
25 | module_param_named(xfer_debug, az6007_xfer_debug, int, 0644); |
26 | MODULE_PARM_DESC(xfer_debug, "Enable xfer debug" ); |
27 | |
28 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
29 | |
30 | /* Known requests (Cypress FX2 firmware + az6007 "private" ones*/ |
31 | |
32 | #define FX2_OED 0xb5 |
33 | #define AZ6007_READ_DATA 0xb7 |
34 | #define AZ6007_I2C_RD 0xb9 |
35 | #define AZ6007_POWER 0xbc |
36 | #define AZ6007_I2C_WR 0xbd |
37 | #define FX2_SCON1 0xc0 |
38 | #define AZ6007_TS_THROUGH 0xc7 |
39 | #define AZ6007_READ_IR 0xb4 |
40 | |
41 | struct az6007_device_state { |
42 | struct mutex mutex; |
43 | struct mutex ca_mutex; |
44 | struct dvb_ca_en50221 ca; |
45 | unsigned warm:1; |
46 | int (*gate_ctrl) (struct dvb_frontend *, int); |
47 | unsigned char data[4096]; |
48 | }; |
49 | |
50 | static struct drxk_config terratec_h7_drxk = { |
51 | .adr = 0x29, |
52 | .parallel_ts = true, |
53 | .dynamic_clk = true, |
54 | .single_master = true, |
55 | .enable_merr_cfg = true, |
56 | .no_i2c_bridge = false, |
57 | .chunk_size = 64, |
58 | .mpeg_out_clk_strength = 0x02, |
59 | .qam_demod_parameter_count = 2, |
60 | .microcode_name = "dvb-usb-terratec-h7-drxk.fw" , |
61 | }; |
62 | |
63 | static struct drxk_config cablestar_hdci_drxk = { |
64 | .adr = 0x29, |
65 | .parallel_ts = true, |
66 | .dynamic_clk = true, |
67 | .single_master = true, |
68 | .enable_merr_cfg = true, |
69 | .no_i2c_bridge = false, |
70 | .chunk_size = 64, |
71 | .mpeg_out_clk_strength = 0x02, |
72 | .qam_demod_parameter_count = 2, |
73 | .microcode_name = "dvb-usb-technisat-cablestar-hdci-drxk.fw" , |
74 | }; |
75 | |
76 | static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) |
77 | { |
78 | struct az6007_device_state *st = fe_to_priv(fe); |
79 | struct dvb_usb_adapter *adap = fe->sec_priv; |
80 | int status = 0; |
81 | |
82 | pr_debug("%s: %s\n" , __func__, enable ? "enable" : "disable" ); |
83 | |
84 | if (!adap || !st) |
85 | return -EINVAL; |
86 | |
87 | if (enable) |
88 | status = st->gate_ctrl(fe, 1); |
89 | else |
90 | status = st->gate_ctrl(fe, 0); |
91 | |
92 | return status; |
93 | } |
94 | |
95 | static struct mt2063_config az6007_mt2063_config = { |
96 | .tuner_address = 0x60, |
97 | .refclock = 36125000, |
98 | }; |
99 | |
100 | static int __az6007_read(struct usb_device *udev, u8 req, u16 value, |
101 | u16 index, u8 *b, int blen) |
102 | { |
103 | int ret; |
104 | |
105 | ret = usb_control_msg(dev: udev, |
106 | usb_rcvctrlpipe(udev, 0), |
107 | request: req, |
108 | USB_TYPE_VENDOR | USB_DIR_IN, |
109 | value, index, data: b, size: blen, timeout: 5000); |
110 | if (ret < 0) { |
111 | pr_warn("usb read operation failed. (%d)\n" , ret); |
112 | return -EIO; |
113 | } |
114 | |
115 | if (az6007_xfer_debug) { |
116 | printk(KERN_DEBUG "az6007: IN req: %02x, value: %04x, index: %04x\n" , |
117 | req, value, index); |
118 | print_hex_dump_bytes("az6007: payload: " , |
119 | DUMP_PREFIX_NONE, b, blen); |
120 | } |
121 | |
122 | return ret; |
123 | } |
124 | |
125 | static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value, |
126 | u16 index, u8 *b, int blen) |
127 | { |
128 | struct az6007_device_state *st = d->priv; |
129 | int ret; |
130 | |
131 | if (mutex_lock_interruptible(&st->mutex) < 0) |
132 | return -EAGAIN; |
133 | |
134 | ret = __az6007_read(udev: d->udev, req, value, index, b, blen); |
135 | |
136 | mutex_unlock(lock: &st->mutex); |
137 | |
138 | return ret; |
139 | } |
140 | |
141 | static int __az6007_write(struct usb_device *udev, u8 req, u16 value, |
142 | u16 index, u8 *b, int blen) |
143 | { |
144 | int ret; |
145 | |
146 | if (az6007_xfer_debug) { |
147 | printk(KERN_DEBUG "az6007: OUT req: %02x, value: %04x, index: %04x\n" , |
148 | req, value, index); |
149 | print_hex_dump_bytes("az6007: payload: " , |
150 | DUMP_PREFIX_NONE, b, blen); |
151 | } |
152 | |
153 | if (blen > 64) { |
154 | pr_err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n" , |
155 | blen); |
156 | return -EOPNOTSUPP; |
157 | } |
158 | |
159 | ret = usb_control_msg(dev: udev, |
160 | usb_sndctrlpipe(udev, 0), |
161 | request: req, |
162 | USB_TYPE_VENDOR | USB_DIR_OUT, |
163 | value, index, data: b, size: blen, timeout: 5000); |
164 | if (ret != blen) { |
165 | pr_err("usb write operation failed. (%d)\n" , ret); |
166 | return -EIO; |
167 | } |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value, |
173 | u16 index, u8 *b, int blen) |
174 | { |
175 | struct az6007_device_state *st = d->priv; |
176 | int ret; |
177 | |
178 | if (mutex_lock_interruptible(&st->mutex) < 0) |
179 | return -EAGAIN; |
180 | |
181 | ret = __az6007_write(udev: d->udev, req, value, index, b, blen); |
182 | |
183 | mutex_unlock(lock: &st->mutex); |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) |
189 | { |
190 | struct dvb_usb_device *d = fe_to_d(fe); |
191 | |
192 | pr_debug("%s: %s\n" , __func__, onoff ? "enable" : "disable" ); |
193 | |
194 | return az6007_write(d, req: 0xbc, value: onoff, index: 0, NULL, blen: 0); |
195 | } |
196 | |
197 | #if IS_ENABLED(CONFIG_RC_CORE) |
198 | /* remote control stuff (does not work with my box) */ |
199 | static int az6007_rc_query(struct dvb_usb_device *d) |
200 | { |
201 | struct az6007_device_state *st = d_to_priv(d); |
202 | unsigned code; |
203 | enum rc_proto proto; |
204 | |
205 | if (az6007_read(d, AZ6007_READ_IR, value: 0, index: 0, b: st->data, blen: 10) < 0) |
206 | return -EIO; |
207 | |
208 | if (st->data[1] == 0x44) |
209 | return 0; |
210 | |
211 | if ((st->data[3] ^ st->data[4]) == 0xff) { |
212 | if ((st->data[1] ^ st->data[2]) == 0xff) { |
213 | code = RC_SCANCODE_NEC(st->data[1], st->data[3]); |
214 | proto = RC_PROTO_NEC; |
215 | } else { |
216 | code = RC_SCANCODE_NECX(st->data[1] << 8 | st->data[2], |
217 | st->data[3]); |
218 | proto = RC_PROTO_NECX; |
219 | } |
220 | } else { |
221 | code = RC_SCANCODE_NEC32(st->data[1] << 24 | |
222 | st->data[2] << 16 | |
223 | st->data[3] << 8 | |
224 | st->data[4]); |
225 | proto = RC_PROTO_NEC32; |
226 | } |
227 | |
228 | rc_keydown(dev: d->rc_dev, protocol: proto, scancode: code, toggle: st->data[5]); |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) |
234 | { |
235 | pr_debug("Getting az6007 Remote Control properties\n" ); |
236 | |
237 | rc->allowed_protos = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | |
238 | RC_PROTO_BIT_NEC32; |
239 | rc->query = az6007_rc_query; |
240 | rc->interval = 400; |
241 | |
242 | return 0; |
243 | } |
244 | #else |
245 | #define az6007_get_rc_config NULL |
246 | #endif |
247 | |
248 | static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, |
249 | int slot, |
250 | int address) |
251 | { |
252 | struct dvb_usb_device *d = ca->data; |
253 | struct az6007_device_state *state = d_to_priv(d); |
254 | |
255 | int ret; |
256 | u8 req; |
257 | u16 value; |
258 | u16 index; |
259 | int blen; |
260 | u8 *b; |
261 | |
262 | if (slot != 0) |
263 | return -EINVAL; |
264 | |
265 | b = kmalloc(size: 12, GFP_KERNEL); |
266 | if (!b) |
267 | return -ENOMEM; |
268 | |
269 | mutex_lock(&state->ca_mutex); |
270 | |
271 | req = 0xC1; |
272 | value = address; |
273 | index = 0; |
274 | blen = 1; |
275 | |
276 | ret = az6007_read(d, req, value, index, b, blen); |
277 | if (ret < 0) { |
278 | pr_warn("usb in operation failed. (%d)\n" , ret); |
279 | ret = -EINVAL; |
280 | } else { |
281 | ret = b[0]; |
282 | } |
283 | |
284 | mutex_unlock(lock: &state->ca_mutex); |
285 | kfree(objp: b); |
286 | return ret; |
287 | } |
288 | |
289 | static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, |
290 | int slot, |
291 | int address, |
292 | u8 value) |
293 | { |
294 | struct dvb_usb_device *d = ca->data; |
295 | struct az6007_device_state *state = d_to_priv(d); |
296 | |
297 | int ret; |
298 | u8 req; |
299 | u16 value1; |
300 | u16 index; |
301 | int blen; |
302 | |
303 | pr_debug("%s(), slot %d\n" , __func__, slot); |
304 | if (slot != 0) |
305 | return -EINVAL; |
306 | |
307 | mutex_lock(&state->ca_mutex); |
308 | req = 0xC2; |
309 | value1 = address; |
310 | index = value; |
311 | blen = 0; |
312 | |
313 | ret = az6007_write(d, req, value: value1, index, NULL, blen); |
314 | if (ret != 0) |
315 | pr_warn("usb out operation failed. (%d)\n" , ret); |
316 | |
317 | mutex_unlock(lock: &state->ca_mutex); |
318 | return ret; |
319 | } |
320 | |
321 | static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca, |
322 | int slot, |
323 | u8 address) |
324 | { |
325 | struct dvb_usb_device *d = ca->data; |
326 | struct az6007_device_state *state = d_to_priv(d); |
327 | |
328 | int ret; |
329 | u8 req; |
330 | u16 value; |
331 | u16 index; |
332 | int blen; |
333 | u8 *b; |
334 | |
335 | if (slot != 0) |
336 | return -EINVAL; |
337 | |
338 | b = kmalloc(size: 12, GFP_KERNEL); |
339 | if (!b) |
340 | return -ENOMEM; |
341 | |
342 | mutex_lock(&state->ca_mutex); |
343 | |
344 | req = 0xC3; |
345 | value = address; |
346 | index = 0; |
347 | blen = 2; |
348 | |
349 | ret = az6007_read(d, req, value, index, b, blen); |
350 | if (ret < 0) { |
351 | pr_warn("usb in operation failed. (%d)\n" , ret); |
352 | ret = -EINVAL; |
353 | } else { |
354 | if (b[0] == 0) |
355 | pr_warn("Read CI IO error\n" ); |
356 | |
357 | ret = b[1]; |
358 | pr_debug("read cam data = %x from 0x%x\n" , b[1], value); |
359 | } |
360 | |
361 | mutex_unlock(lock: &state->ca_mutex); |
362 | kfree(objp: b); |
363 | return ret; |
364 | } |
365 | |
366 | static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca, |
367 | int slot, |
368 | u8 address, |
369 | u8 value) |
370 | { |
371 | struct dvb_usb_device *d = ca->data; |
372 | struct az6007_device_state *state = d_to_priv(d); |
373 | |
374 | int ret; |
375 | u8 req; |
376 | u16 value1; |
377 | u16 index; |
378 | int blen; |
379 | |
380 | if (slot != 0) |
381 | return -EINVAL; |
382 | |
383 | mutex_lock(&state->ca_mutex); |
384 | req = 0xC4; |
385 | value1 = address; |
386 | index = value; |
387 | blen = 0; |
388 | |
389 | ret = az6007_write(d, req, value: value1, index, NULL, blen); |
390 | if (ret != 0) { |
391 | pr_warn("usb out operation failed. (%d)\n" , ret); |
392 | goto failed; |
393 | } |
394 | |
395 | failed: |
396 | mutex_unlock(lock: &state->ca_mutex); |
397 | return ret; |
398 | } |
399 | |
400 | static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) |
401 | { |
402 | struct dvb_usb_device *d = ca->data; |
403 | |
404 | int ret; |
405 | u8 req; |
406 | u16 value; |
407 | u16 index; |
408 | int blen; |
409 | u8 *b; |
410 | |
411 | b = kmalloc(size: 12, GFP_KERNEL); |
412 | if (!b) |
413 | return -ENOMEM; |
414 | |
415 | req = 0xC8; |
416 | value = 0; |
417 | index = 0; |
418 | blen = 1; |
419 | |
420 | ret = az6007_read(d, req, value, index, b, blen); |
421 | if (ret < 0) { |
422 | pr_warn("usb in operation failed. (%d)\n" , ret); |
423 | ret = -EIO; |
424 | } else{ |
425 | ret = b[0]; |
426 | } |
427 | kfree(objp: b); |
428 | return ret; |
429 | } |
430 | |
431 | static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) |
432 | { |
433 | struct dvb_usb_device *d = ca->data; |
434 | struct az6007_device_state *state = d_to_priv(d); |
435 | |
436 | int ret, i; |
437 | u8 req; |
438 | u16 value; |
439 | u16 index; |
440 | int blen; |
441 | |
442 | mutex_lock(&state->ca_mutex); |
443 | |
444 | req = 0xC6; |
445 | value = 1; |
446 | index = 0; |
447 | blen = 0; |
448 | |
449 | ret = az6007_write(d, req, value, index, NULL, blen); |
450 | if (ret != 0) { |
451 | pr_warn("usb out operation failed. (%d)\n" , ret); |
452 | goto failed; |
453 | } |
454 | |
455 | msleep(msecs: 500); |
456 | req = 0xC6; |
457 | value = 0; |
458 | index = 0; |
459 | blen = 0; |
460 | |
461 | ret = az6007_write(d, req, value, index, NULL, blen); |
462 | if (ret != 0) { |
463 | pr_warn("usb out operation failed. (%d)\n" , ret); |
464 | goto failed; |
465 | } |
466 | |
467 | for (i = 0; i < 15; i++) { |
468 | msleep(msecs: 100); |
469 | |
470 | if (CI_CamReady(ca, slot)) { |
471 | pr_debug("CAM Ready\n" ); |
472 | break; |
473 | } |
474 | } |
475 | msleep(msecs: 5000); |
476 | |
477 | failed: |
478 | mutex_unlock(lock: &state->ca_mutex); |
479 | return ret; |
480 | } |
481 | |
482 | static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) |
483 | { |
484 | return 0; |
485 | } |
486 | |
487 | static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) |
488 | { |
489 | struct dvb_usb_device *d = ca->data; |
490 | struct az6007_device_state *state = d_to_priv(d); |
491 | |
492 | int ret; |
493 | u8 req; |
494 | u16 value; |
495 | u16 index; |
496 | int blen; |
497 | |
498 | pr_debug("%s()\n" , __func__); |
499 | mutex_lock(&state->ca_mutex); |
500 | req = 0xC7; |
501 | value = 1; |
502 | index = 0; |
503 | blen = 0; |
504 | |
505 | ret = az6007_write(d, req, value, index, NULL, blen); |
506 | if (ret != 0) { |
507 | pr_warn("usb out operation failed. (%d)\n" , ret); |
508 | goto failed; |
509 | } |
510 | |
511 | failed: |
512 | mutex_unlock(lock: &state->ca_mutex); |
513 | return ret; |
514 | } |
515 | |
516 | static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) |
517 | { |
518 | struct dvb_usb_device *d = ca->data; |
519 | struct az6007_device_state *state = d_to_priv(d); |
520 | int ret; |
521 | u8 req; |
522 | u16 value; |
523 | u16 index; |
524 | int blen; |
525 | u8 *b; |
526 | |
527 | b = kmalloc(size: 12, GFP_KERNEL); |
528 | if (!b) |
529 | return -ENOMEM; |
530 | mutex_lock(&state->ca_mutex); |
531 | |
532 | req = 0xC5; |
533 | value = 0; |
534 | index = 0; |
535 | blen = 1; |
536 | |
537 | ret = az6007_read(d, req, value, index, b, blen); |
538 | if (ret < 0) { |
539 | pr_warn("usb in operation failed. (%d)\n" , ret); |
540 | ret = -EIO; |
541 | } else |
542 | ret = 0; |
543 | |
544 | if (!ret && b[0] == 1) { |
545 | ret = DVB_CA_EN50221_POLL_CAM_PRESENT | |
546 | DVB_CA_EN50221_POLL_CAM_READY; |
547 | } |
548 | |
549 | mutex_unlock(lock: &state->ca_mutex); |
550 | kfree(objp: b); |
551 | return ret; |
552 | } |
553 | |
554 | |
555 | static void az6007_ci_uninit(struct dvb_usb_device *d) |
556 | { |
557 | struct az6007_device_state *state; |
558 | |
559 | pr_debug("%s()\n" , __func__); |
560 | |
561 | if (NULL == d) |
562 | return; |
563 | |
564 | state = d_to_priv(d); |
565 | if (NULL == state) |
566 | return; |
567 | |
568 | if (NULL == state->ca.data) |
569 | return; |
570 | |
571 | dvb_ca_en50221_release(ca: &state->ca); |
572 | |
573 | memset(&state->ca, 0, sizeof(state->ca)); |
574 | } |
575 | |
576 | |
577 | static int az6007_ci_init(struct dvb_usb_adapter *adap) |
578 | { |
579 | struct dvb_usb_device *d = adap_to_d(adap); |
580 | struct az6007_device_state *state = adap_to_priv(adap); |
581 | int ret; |
582 | |
583 | pr_debug("%s()\n" , __func__); |
584 | |
585 | mutex_init(&state->ca_mutex); |
586 | state->ca.owner = THIS_MODULE; |
587 | state->ca.read_attribute_mem = az6007_ci_read_attribute_mem; |
588 | state->ca.write_attribute_mem = az6007_ci_write_attribute_mem; |
589 | state->ca.read_cam_control = az6007_ci_read_cam_control; |
590 | state->ca.write_cam_control = az6007_ci_write_cam_control; |
591 | state->ca.slot_reset = az6007_ci_slot_reset; |
592 | state->ca.slot_shutdown = az6007_ci_slot_shutdown; |
593 | state->ca.slot_ts_enable = az6007_ci_slot_ts_enable; |
594 | state->ca.poll_slot_status = az6007_ci_poll_slot_status; |
595 | state->ca.data = d; |
596 | |
597 | ret = dvb_ca_en50221_init(dvb_adapter: &adap->dvb_adap, |
598 | ca: &state->ca, |
599 | flags: 0, /* flags */ |
600 | slot_count: 1);/* n_slots */ |
601 | if (ret != 0) { |
602 | pr_err("Cannot initialize CI: Error %d.\n" , ret); |
603 | memset(&state->ca, 0, sizeof(state->ca)); |
604 | return ret; |
605 | } |
606 | |
607 | pr_debug("CI initialized.\n" ); |
608 | |
609 | return 0; |
610 | } |
611 | |
612 | static int az6007_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6]) |
613 | { |
614 | struct dvb_usb_device *d = adap_to_d(adap); |
615 | struct az6007_device_state *st = adap_to_priv(adap); |
616 | int ret; |
617 | |
618 | ret = az6007_read(d, AZ6007_READ_DATA, value: 6, index: 0, b: st->data, blen: 6); |
619 | memcpy(mac, st->data, 6); |
620 | |
621 | if (ret > 0) |
622 | pr_debug("%s: mac is %pM\n" , __func__, mac); |
623 | |
624 | return ret; |
625 | } |
626 | |
627 | static int az6007_frontend_attach(struct dvb_usb_adapter *adap) |
628 | { |
629 | struct az6007_device_state *st = adap_to_priv(adap); |
630 | struct dvb_usb_device *d = adap_to_d(adap); |
631 | |
632 | pr_debug("attaching demod drxk\n" ); |
633 | |
634 | adap->fe[0] = dvb_attach(drxk_attach, &terratec_h7_drxk, |
635 | &d->i2c_adap); |
636 | if (!adap->fe[0]) |
637 | return -EINVAL; |
638 | |
639 | adap->fe[0]->sec_priv = adap; |
640 | st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl; |
641 | adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; |
642 | |
643 | az6007_ci_init(adap); |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | static int az6007_cablestar_hdci_frontend_attach(struct dvb_usb_adapter *adap) |
649 | { |
650 | struct az6007_device_state *st = adap_to_priv(adap); |
651 | struct dvb_usb_device *d = adap_to_d(adap); |
652 | |
653 | pr_debug("attaching demod drxk\n" ); |
654 | |
655 | adap->fe[0] = dvb_attach(drxk_attach, &cablestar_hdci_drxk, |
656 | &d->i2c_adap); |
657 | if (!adap->fe[0]) |
658 | return -EINVAL; |
659 | |
660 | adap->fe[0]->sec_priv = adap; |
661 | st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl; |
662 | adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; |
663 | |
664 | az6007_ci_init(adap); |
665 | |
666 | return 0; |
667 | } |
668 | |
669 | static int az6007_tuner_attach(struct dvb_usb_adapter *adap) |
670 | { |
671 | struct dvb_usb_device *d = adap_to_d(adap); |
672 | |
673 | pr_debug("attaching tuner mt2063\n" ); |
674 | |
675 | /* Attach mt2063 to DVB-C frontend */ |
676 | if (adap->fe[0]->ops.i2c_gate_ctrl) |
677 | adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 1); |
678 | if (!dvb_attach(mt2063_attach, adap->fe[0], |
679 | &az6007_mt2063_config, |
680 | &d->i2c_adap)) |
681 | return -EINVAL; |
682 | |
683 | if (adap->fe[0]->ops.i2c_gate_ctrl) |
684 | adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 0); |
685 | |
686 | return 0; |
687 | } |
688 | |
689 | static int az6007_power_ctrl(struct dvb_usb_device *d, int onoff) |
690 | { |
691 | struct az6007_device_state *state = d_to_priv(d); |
692 | int ret; |
693 | |
694 | pr_debug("%s()\n" , __func__); |
695 | |
696 | if (!state->warm) { |
697 | mutex_init(&state->mutex); |
698 | |
699 | ret = az6007_write(d, AZ6007_POWER, value: 0, index: 2, NULL, blen: 0); |
700 | if (ret < 0) |
701 | return ret; |
702 | msleep(msecs: 60); |
703 | ret = az6007_write(d, AZ6007_POWER, value: 1, index: 4, NULL, blen: 0); |
704 | if (ret < 0) |
705 | return ret; |
706 | msleep(msecs: 100); |
707 | ret = az6007_write(d, AZ6007_POWER, value: 1, index: 3, NULL, blen: 0); |
708 | if (ret < 0) |
709 | return ret; |
710 | msleep(msecs: 20); |
711 | ret = az6007_write(d, AZ6007_POWER, value: 1, index: 4, NULL, blen: 0); |
712 | if (ret < 0) |
713 | return ret; |
714 | |
715 | msleep(msecs: 400); |
716 | ret = az6007_write(d, FX2_SCON1, value: 0, index: 3, NULL, blen: 0); |
717 | if (ret < 0) |
718 | return ret; |
719 | msleep(msecs: 150); |
720 | ret = az6007_write(d, FX2_SCON1, value: 1, index: 3, NULL, blen: 0); |
721 | if (ret < 0) |
722 | return ret; |
723 | msleep(msecs: 430); |
724 | ret = az6007_write(d, AZ6007_POWER, value: 0, index: 0, NULL, blen: 0); |
725 | if (ret < 0) |
726 | return ret; |
727 | |
728 | state->warm = true; |
729 | |
730 | return 0; |
731 | } |
732 | |
733 | if (!onoff) |
734 | return 0; |
735 | |
736 | az6007_write(d, AZ6007_POWER, value: 0, index: 0, NULL, blen: 0); |
737 | az6007_write(d, AZ6007_TS_THROUGH, value: 0, index: 0, NULL, blen: 0); |
738 | |
739 | return 0; |
740 | } |
741 | |
742 | /* I2C */ |
743 | static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], |
744 | int num) |
745 | { |
746 | struct dvb_usb_device *d = i2c_get_adapdata(adap); |
747 | struct az6007_device_state *st = d_to_priv(d); |
748 | int i, j, len; |
749 | int ret = 0; |
750 | u16 index; |
751 | u16 value; |
752 | int length; |
753 | u8 req, addr; |
754 | |
755 | if (mutex_lock_interruptible(&st->mutex) < 0) |
756 | return -EAGAIN; |
757 | |
758 | for (i = 0; i < num; i++) { |
759 | addr = msgs[i].addr << 1; |
760 | if (((i + 1) < num) |
761 | && (msgs[i].len == 1) |
762 | && ((msgs[i].flags & I2C_M_RD) != I2C_M_RD) |
763 | && (msgs[i + 1].flags & I2C_M_RD) |
764 | && (msgs[i].addr == msgs[i + 1].addr)) { |
765 | /* |
766 | * A write + read xfer for the same address, where |
767 | * the first xfer has just 1 byte length. |
768 | * Need to join both into one operation |
769 | */ |
770 | if (az6007_xfer_debug) |
771 | printk(KERN_DEBUG "az6007: I2C W/R addr=0x%x len=%d/%d\n" , |
772 | addr, msgs[i].len, msgs[i + 1].len); |
773 | req = AZ6007_I2C_RD; |
774 | index = msgs[i].buf[0]; |
775 | value = addr | (1 << 8); |
776 | length = 6 + msgs[i + 1].len; |
777 | len = msgs[i + 1].len; |
778 | ret = __az6007_read(udev: d->udev, req, value, index, |
779 | b: st->data, blen: length); |
780 | if (ret >= len) { |
781 | for (j = 0; j < len; j++) |
782 | msgs[i + 1].buf[j] = st->data[j + 5]; |
783 | } else |
784 | ret = -EIO; |
785 | i++; |
786 | } else if (!(msgs[i].flags & I2C_M_RD)) { |
787 | /* write bytes */ |
788 | if (az6007_xfer_debug) |
789 | printk(KERN_DEBUG "az6007: I2C W addr=0x%x len=%d\n" , |
790 | addr, msgs[i].len); |
791 | if (msgs[i].len < 1) { |
792 | ret = -EIO; |
793 | goto err; |
794 | } |
795 | req = AZ6007_I2C_WR; |
796 | index = msgs[i].buf[0]; |
797 | value = addr | (1 << 8); |
798 | length = msgs[i].len - 1; |
799 | len = msgs[i].len - 1; |
800 | for (j = 0; j < len; j++) |
801 | st->data[j] = msgs[i].buf[j + 1]; |
802 | ret = __az6007_write(udev: d->udev, req, value, index, |
803 | b: st->data, blen: length); |
804 | } else { |
805 | /* read bytes */ |
806 | if (az6007_xfer_debug) |
807 | printk(KERN_DEBUG "az6007: I2C R addr=0x%x len=%d\n" , |
808 | addr, msgs[i].len); |
809 | if (msgs[i].len < 1) { |
810 | ret = -EIO; |
811 | goto err; |
812 | } |
813 | req = AZ6007_I2C_RD; |
814 | index = msgs[i].buf[0]; |
815 | value = addr; |
816 | length = msgs[i].len + 6; |
817 | len = msgs[i].len; |
818 | ret = __az6007_read(udev: d->udev, req, value, index, |
819 | b: st->data, blen: length); |
820 | for (j = 0; j < len; j++) |
821 | msgs[i].buf[j] = st->data[j + 5]; |
822 | } |
823 | if (ret < 0) |
824 | goto err; |
825 | } |
826 | err: |
827 | mutex_unlock(lock: &st->mutex); |
828 | |
829 | if (ret < 0) { |
830 | pr_info("%s ERROR: %i\n" , __func__, ret); |
831 | return ret; |
832 | } |
833 | return num; |
834 | } |
835 | |
836 | static u32 az6007_i2c_func(struct i2c_adapter *adapter) |
837 | { |
838 | return I2C_FUNC_I2C; |
839 | } |
840 | |
841 | static struct i2c_algorithm az6007_i2c_algo = { |
842 | .master_xfer = az6007_i2c_xfer, |
843 | .functionality = az6007_i2c_func, |
844 | }; |
845 | |
846 | static int az6007_identify_state(struct dvb_usb_device *d, const char **name) |
847 | { |
848 | int ret; |
849 | u8 *mac; |
850 | |
851 | pr_debug("Identifying az6007 state\n" ); |
852 | |
853 | mac = kmalloc(size: 6, GFP_ATOMIC); |
854 | if (!mac) |
855 | return -ENOMEM; |
856 | |
857 | /* Try to read the mac address */ |
858 | ret = __az6007_read(udev: d->udev, AZ6007_READ_DATA, value: 6, index: 0, b: mac, blen: 6); |
859 | if (ret == 6) |
860 | ret = WARM; |
861 | else |
862 | ret = COLD; |
863 | |
864 | kfree(objp: mac); |
865 | |
866 | if (ret == COLD) { |
867 | __az6007_write(udev: d->udev, req: 0x09, value: 1, index: 0, NULL, blen: 0); |
868 | __az6007_write(udev: d->udev, req: 0x00, value: 0, index: 0, NULL, blen: 0); |
869 | __az6007_write(udev: d->udev, req: 0x00, value: 0, index: 0, NULL, blen: 0); |
870 | } |
871 | |
872 | pr_debug("Device is on %s state\n" , |
873 | ret == WARM ? "warm" : "cold" ); |
874 | return ret; |
875 | } |
876 | |
877 | static void az6007_usb_disconnect(struct usb_interface *intf) |
878 | { |
879 | struct dvb_usb_device *d = usb_get_intfdata(intf); |
880 | az6007_ci_uninit(d); |
881 | dvb_usbv2_disconnect(intf); |
882 | } |
883 | |
884 | static int az6007_download_firmware(struct dvb_usb_device *d, |
885 | const struct firmware *fw) |
886 | { |
887 | pr_debug("Loading az6007 firmware\n" ); |
888 | |
889 | return cypress_load_firmware(d->udev, fw, CYPRESS_FX2); |
890 | } |
891 | |
892 | /* DVB USB Driver stuff */ |
893 | static struct dvb_usb_device_properties az6007_props = { |
894 | .driver_name = KBUILD_MODNAME, |
895 | .owner = THIS_MODULE, |
896 | .firmware = AZ6007_FIRMWARE, |
897 | |
898 | .adapter_nr = adapter_nr, |
899 | .size_of_priv = sizeof(struct az6007_device_state), |
900 | .i2c_algo = &az6007_i2c_algo, |
901 | .tuner_attach = az6007_tuner_attach, |
902 | .frontend_attach = az6007_frontend_attach, |
903 | .streaming_ctrl = az6007_streaming_ctrl, |
904 | .get_rc_config = az6007_get_rc_config, |
905 | .read_mac_address = az6007_read_mac_addr, |
906 | .download_firmware = az6007_download_firmware, |
907 | .identify_state = az6007_identify_state, |
908 | .power_ctrl = az6007_power_ctrl, |
909 | .num_adapters = 1, |
910 | .adapter = { |
911 | { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), } |
912 | } |
913 | }; |
914 | |
915 | static struct dvb_usb_device_properties az6007_cablestar_hdci_props = { |
916 | .driver_name = KBUILD_MODNAME, |
917 | .owner = THIS_MODULE, |
918 | .firmware = AZ6007_FIRMWARE, |
919 | |
920 | .adapter_nr = adapter_nr, |
921 | .size_of_priv = sizeof(struct az6007_device_state), |
922 | .i2c_algo = &az6007_i2c_algo, |
923 | .tuner_attach = az6007_tuner_attach, |
924 | .frontend_attach = az6007_cablestar_hdci_frontend_attach, |
925 | .streaming_ctrl = az6007_streaming_ctrl, |
926 | /* ditch get_rc_config as it can't work (TS35 remote, I believe it's rc5) */ |
927 | .get_rc_config = NULL, |
928 | .read_mac_address = az6007_read_mac_addr, |
929 | .download_firmware = az6007_download_firmware, |
930 | .identify_state = az6007_identify_state, |
931 | .power_ctrl = az6007_power_ctrl, |
932 | .num_adapters = 1, |
933 | .adapter = { |
934 | { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), } |
935 | } |
936 | }; |
937 | |
938 | static const struct usb_device_id az6007_usb_table[] = { |
939 | {DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007, |
940 | &az6007_props, "Azurewave 6007" , RC_MAP_EMPTY)}, |
941 | {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7, |
942 | &az6007_props, "Terratec H7" , RC_MAP_NEC_TERRATEC_CINERGY_XS)}, |
943 | {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2, |
944 | &az6007_props, "Terratec H7" , RC_MAP_NEC_TERRATEC_CINERGY_XS)}, |
945 | {DVB_USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI, |
946 | &az6007_cablestar_hdci_props, "Technisat CableStar Combo HD CI" , RC_MAP_EMPTY)}, |
947 | {0}, |
948 | }; |
949 | |
950 | MODULE_DEVICE_TABLE(usb, az6007_usb_table); |
951 | |
952 | static int az6007_suspend(struct usb_interface *intf, pm_message_t msg) |
953 | { |
954 | struct dvb_usb_device *d = usb_get_intfdata(intf); |
955 | |
956 | az6007_ci_uninit(d); |
957 | return dvb_usbv2_suspend(intf, msg); |
958 | } |
959 | |
960 | static int az6007_resume(struct usb_interface *intf) |
961 | { |
962 | struct dvb_usb_device *d = usb_get_intfdata(intf); |
963 | struct dvb_usb_adapter *adap = &d->adapter[0]; |
964 | |
965 | az6007_ci_init(adap); |
966 | return dvb_usbv2_resume(intf); |
967 | } |
968 | |
969 | /* usb specific object needed to register this driver with the usb subsystem */ |
970 | static struct usb_driver az6007_usb_driver = { |
971 | .name = KBUILD_MODNAME, |
972 | .id_table = az6007_usb_table, |
973 | .probe = dvb_usbv2_probe, |
974 | .disconnect = az6007_usb_disconnect, |
975 | .no_dynamic_id = 1, |
976 | .soft_unbind = 1, |
977 | /* |
978 | * FIXME: need to implement reset_resume, likely with |
979 | * dvb-usb-v2 core support |
980 | */ |
981 | .suspend = az6007_suspend, |
982 | .resume = az6007_resume, |
983 | }; |
984 | |
985 | module_usb_driver(az6007_usb_driver); |
986 | |
987 | MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>" ); |
988 | MODULE_AUTHOR("Mauro Carvalho Chehab" ); |
989 | MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones" ); |
990 | MODULE_VERSION("2.0" ); |
991 | MODULE_LICENSE("GPL" ); |
992 | MODULE_FIRMWARE(AZ6007_FIRMWARE); |
993 | |