1 // SPDX-License-Identifier: GPL-2.0
2 // tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
4 // Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
6 #include <linux/init.h>
7 #include <linux/module.h>
9 #include <linux/delay.h>
10 #include <linux/i2c.h>
11 #include <linux/usb.h>
12 #include <linux/slab.h>
13 #include <media/v4l2-common.h>
14 #include <media/tuner.h>
15 #include <media/i2c/tvaudio.h>
16 #include <media/rc-map.h>
19 #include "tm6000-regs.h"
20 #include "tuner-xc2028.h"
23 #define TM6000_BOARD_UNKNOWN 0
24 #define TM5600_BOARD_GENERIC 1
25 #define TM6000_BOARD_GENERIC 2
26 #define TM6010_BOARD_GENERIC 3
27 #define TM5600_BOARD_10MOONS_UT821 4
28 #define TM5600_BOARD_10MOONS_UT330 5
29 #define TM6000_BOARD_ADSTECH_DUAL_TV 6
30 #define TM6000_BOARD_FREECOM_AND_SIMILAR 7
31 #define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8
32 #define TM6010_BOARD_HAUPPAUGE_900H 9
33 #define TM6010_BOARD_BEHOLD_WANDER 10
34 #define TM6010_BOARD_BEHOLD_VOYAGER 11
35 #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
36 #define TM6010_BOARD_TWINHAN_TU501 13
37 #define TM6010_BOARD_BEHOLD_WANDER_LITE 14
38 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
39 #define TM5600_BOARD_TERRATEC_GRABSTER 16
41 #define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
42 (model == TM5600_BOARD_GENERIC) || \
43 (model == TM6000_BOARD_GENERIC) || \
44 (model == TM6010_BOARD_GENERIC))
46 #define TM6000_MAXBOARDS 16
47 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
49 module_param_array(card, int, NULL, 0444);
51 static unsigned long tm6000_devused;
56 char eename[16]; /* EEPROM name */
57 unsigned eename_size; /* size of EEPROM name */
58 unsigned eename_pos; /* Position where it appears at ROM */
60 struct tm6000_capabilities caps;
62 enum tm6000_devtype type; /* variant of the chipset */
63 int tuner_type; /* type of the tuner */
64 int tuner_addr; /* tuner address */
65 int demod_addr; /* demodulator address */
67 struct tm6000_gpio gpio;
69 struct tm6000_input vinput[3];
70 struct tm6000_input rinput;
75 static struct tm6000_board tm6000_boards[] = {
76 [TM6000_BOARD_UNKNOWN] = {
77 .name = "Unknown tm6000 video grabber",
83 .tuner_reset = TM6000_GPIO_1,
86 .type = TM6000_INPUT_TV,
87 .vmux = TM6000_VMUX_VIDEO_B,
88 .amux = TM6000_AMUX_ADC1,
90 .type = TM6000_INPUT_COMPOSITE1,
91 .vmux = TM6000_VMUX_VIDEO_A,
92 .amux = TM6000_AMUX_ADC2,
94 .type = TM6000_INPUT_SVIDEO,
95 .vmux = TM6000_VMUX_VIDEO_AB,
96 .amux = TM6000_AMUX_ADC2,
100 [TM5600_BOARD_GENERIC] = {
101 .name = "Generic tm5600 board",
103 .tuner_type = TUNER_XC2028,
104 .tuner_addr = 0xc2 >> 1,
110 .tuner_reset = TM6000_GPIO_1,
113 .type = TM6000_INPUT_TV,
114 .vmux = TM6000_VMUX_VIDEO_B,
115 .amux = TM6000_AMUX_ADC1,
117 .type = TM6000_INPUT_COMPOSITE1,
118 .vmux = TM6000_VMUX_VIDEO_A,
119 .amux = TM6000_AMUX_ADC2,
121 .type = TM6000_INPUT_SVIDEO,
122 .vmux = TM6000_VMUX_VIDEO_AB,
123 .amux = TM6000_AMUX_ADC2,
127 [TM6000_BOARD_GENERIC] = {
128 .name = "Generic tm6000 board",
129 .tuner_type = TUNER_XC2028,
130 .tuner_addr = 0xc2 >> 1,
136 .tuner_reset = TM6000_GPIO_1,
139 .type = TM6000_INPUT_TV,
140 .vmux = TM6000_VMUX_VIDEO_B,
141 .amux = TM6000_AMUX_ADC1,
143 .type = TM6000_INPUT_COMPOSITE1,
144 .vmux = TM6000_VMUX_VIDEO_A,
145 .amux = TM6000_AMUX_ADC2,
147 .type = TM6000_INPUT_SVIDEO,
148 .vmux = TM6000_VMUX_VIDEO_AB,
149 .amux = TM6000_AMUX_ADC2,
153 [TM6010_BOARD_GENERIC] = {
154 .name = "Generic tm6010 board",
156 .tuner_type = TUNER_XC2028,
157 .tuner_addr = 0xc2 >> 1,
158 .demod_addr = 0x1e >> 1,
167 .tuner_reset = TM6010_GPIO_2,
168 .tuner_on = TM6010_GPIO_3,
169 .demod_reset = TM6010_GPIO_1,
170 .demod_on = TM6010_GPIO_4,
171 .power_led = TM6010_GPIO_7,
172 .dvb_led = TM6010_GPIO_5,
176 .type = TM6000_INPUT_TV,
177 .vmux = TM6000_VMUX_VIDEO_B,
178 .amux = TM6000_AMUX_SIF1,
180 .type = TM6000_INPUT_COMPOSITE1,
181 .vmux = TM6000_VMUX_VIDEO_A,
182 .amux = TM6000_AMUX_ADC2,
184 .type = TM6000_INPUT_SVIDEO,
185 .vmux = TM6000_VMUX_VIDEO_AB,
186 .amux = TM6000_AMUX_ADC2,
190 [TM5600_BOARD_10MOONS_UT821] = {
191 .name = "10Moons UT 821",
192 .tuner_type = TUNER_XC2028,
193 .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
197 .tuner_addr = 0xc2 >> 1,
203 .tuner_reset = TM6000_GPIO_1,
206 .type = TM6000_INPUT_TV,
207 .vmux = TM6000_VMUX_VIDEO_B,
208 .amux = TM6000_AMUX_ADC1,
210 .type = TM6000_INPUT_COMPOSITE1,
211 .vmux = TM6000_VMUX_VIDEO_A,
212 .amux = TM6000_AMUX_ADC2,
214 .type = TM6000_INPUT_SVIDEO,
215 .vmux = TM6000_VMUX_VIDEO_AB,
216 .amux = TM6000_AMUX_ADC2,
220 [TM5600_BOARD_10MOONS_UT330] = {
221 .name = "10Moons UT 330",
222 .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4,
223 .tuner_addr = 0xc8 >> 1,
231 .type = TM6000_INPUT_TV,
232 .vmux = TM6000_VMUX_VIDEO_B,
233 .amux = TM6000_AMUX_ADC1,
235 .type = TM6000_INPUT_COMPOSITE1,
236 .vmux = TM6000_VMUX_VIDEO_A,
237 .amux = TM6000_AMUX_ADC2,
239 .type = TM6000_INPUT_SVIDEO,
240 .vmux = TM6000_VMUX_VIDEO_AB,
241 .amux = TM6000_AMUX_ADC2,
245 [TM6000_BOARD_ADSTECH_DUAL_TV] = {
246 .name = "ADSTECH Dual TV USB",
247 .tuner_type = TUNER_XC2028,
248 .tuner_addr = 0xc8 >> 1,
257 .type = TM6000_INPUT_TV,
258 .vmux = TM6000_VMUX_VIDEO_B,
259 .amux = TM6000_AMUX_ADC1,
261 .type = TM6000_INPUT_COMPOSITE1,
262 .vmux = TM6000_VMUX_VIDEO_A,
263 .amux = TM6000_AMUX_ADC2,
265 .type = TM6000_INPUT_SVIDEO,
266 .vmux = TM6000_VMUX_VIDEO_AB,
267 .amux = TM6000_AMUX_ADC2,
271 [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
272 .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
273 .tuner_type = TUNER_XC2028, /* has a XC3028 */
274 .tuner_addr = 0xc2 >> 1,
275 .demod_addr = 0x1e >> 1,
284 .tuner_reset = TM6000_GPIO_4,
287 .type = TM6000_INPUT_TV,
288 .vmux = TM6000_VMUX_VIDEO_B,
289 .amux = TM6000_AMUX_ADC1,
291 .type = TM6000_INPUT_COMPOSITE1,
292 .vmux = TM6000_VMUX_VIDEO_A,
293 .amux = TM6000_AMUX_ADC2,
295 .type = TM6000_INPUT_SVIDEO,
296 .vmux = TM6000_VMUX_VIDEO_AB,
297 .amux = TM6000_AMUX_ADC2,
301 [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
302 .name = "ADSTECH Mini Dual TV USB",
303 .tuner_type = TUNER_XC2028, /* has a XC3028 */
304 .tuner_addr = 0xc8 >> 1,
305 .demod_addr = 0x1e >> 1,
313 .tuner_reset = TM6000_GPIO_4,
316 .type = TM6000_INPUT_TV,
317 .vmux = TM6000_VMUX_VIDEO_B,
318 .amux = TM6000_AMUX_ADC1,
320 .type = TM6000_INPUT_COMPOSITE1,
321 .vmux = TM6000_VMUX_VIDEO_A,
322 .amux = TM6000_AMUX_ADC2,
324 .type = TM6000_INPUT_SVIDEO,
325 .vmux = TM6000_VMUX_VIDEO_AB,
326 .amux = TM6000_AMUX_ADC2,
330 [TM6010_BOARD_HAUPPAUGE_900H] = {
331 .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
332 .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
335 .tuner_type = TUNER_XC2028, /* has a XC3028 */
336 .tuner_addr = 0xc2 >> 1,
337 .demod_addr = 0x1e >> 1,
339 .ir_codes = RC_MAP_HAUPPAUGE,
348 .tuner_reset = TM6010_GPIO_2,
349 .tuner_on = TM6010_GPIO_3,
350 .demod_reset = TM6010_GPIO_1,
351 .demod_on = TM6010_GPIO_4,
352 .power_led = TM6010_GPIO_7,
353 .dvb_led = TM6010_GPIO_5,
357 .type = TM6000_INPUT_TV,
358 .vmux = TM6000_VMUX_VIDEO_B,
359 .amux = TM6000_AMUX_SIF1,
361 .type = TM6000_INPUT_COMPOSITE1,
362 .vmux = TM6000_VMUX_VIDEO_A,
363 .amux = TM6000_AMUX_ADC2,
365 .type = TM6000_INPUT_SVIDEO,
366 .vmux = TM6000_VMUX_VIDEO_AB,
367 .amux = TM6000_AMUX_ADC2,
371 [TM6010_BOARD_BEHOLD_WANDER] = {
372 .name = "Beholder Wander DVB-T/TV/FM USB2.0",
373 .tuner_type = TUNER_XC5000,
374 .tuner_addr = 0xc2 >> 1,
375 .demod_addr = 0x1e >> 1,
386 .tuner_reset = TM6010_GPIO_0,
387 .demod_reset = TM6010_GPIO_1,
388 .power_led = TM6010_GPIO_6,
391 .type = TM6000_INPUT_TV,
392 .vmux = TM6000_VMUX_VIDEO_B,
393 .amux = TM6000_AMUX_SIF1,
395 .type = TM6000_INPUT_COMPOSITE1,
396 .vmux = TM6000_VMUX_VIDEO_A,
397 .amux = TM6000_AMUX_ADC2,
399 .type = TM6000_INPUT_SVIDEO,
400 .vmux = TM6000_VMUX_VIDEO_AB,
401 .amux = TM6000_AMUX_ADC2,
405 .type = TM6000_INPUT_RADIO,
406 .amux = TM6000_AMUX_ADC1,
409 [TM6010_BOARD_BEHOLD_VOYAGER] = {
410 .name = "Beholder Voyager TV/FM USB2.0",
411 .tuner_type = TUNER_XC5000,
412 .tuner_addr = 0xc2 >> 1,
423 .tuner_reset = TM6010_GPIO_0,
424 .power_led = TM6010_GPIO_6,
427 .type = TM6000_INPUT_TV,
428 .vmux = TM6000_VMUX_VIDEO_B,
429 .amux = TM6000_AMUX_SIF1,
431 .type = TM6000_INPUT_COMPOSITE1,
432 .vmux = TM6000_VMUX_VIDEO_A,
433 .amux = TM6000_AMUX_ADC2,
435 .type = TM6000_INPUT_SVIDEO,
436 .vmux = TM6000_VMUX_VIDEO_AB,
437 .amux = TM6000_AMUX_ADC2,
441 .type = TM6000_INPUT_RADIO,
442 .amux = TM6000_AMUX_ADC1,
445 [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
446 .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
447 .tuner_type = TUNER_XC2028, /* has a XC3028 */
448 .tuner_addr = 0xc2 >> 1,
449 .demod_addr = 0x1e >> 1,
460 .tuner_reset = TM6010_GPIO_2,
461 .tuner_on = TM6010_GPIO_3,
462 .demod_reset = TM6010_GPIO_1,
463 .demod_on = TM6010_GPIO_4,
464 .power_led = TM6010_GPIO_7,
465 .dvb_led = TM6010_GPIO_5,
468 .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
470 .type = TM6000_INPUT_TV,
471 .vmux = TM6000_VMUX_VIDEO_B,
472 .amux = TM6000_AMUX_SIF1,
474 .type = TM6000_INPUT_COMPOSITE1,
475 .vmux = TM6000_VMUX_VIDEO_A,
476 .amux = TM6000_AMUX_ADC2,
478 .type = TM6000_INPUT_SVIDEO,
479 .vmux = TM6000_VMUX_VIDEO_AB,
480 .amux = TM6000_AMUX_ADC2,
484 .type = TM6000_INPUT_RADIO,
485 .amux = TM6000_AMUX_SIF1,
488 [TM5600_BOARD_TERRATEC_GRABSTER] = {
489 .name = "Terratec Grabster AV 150/250 MX",
491 .tuner_type = TUNER_ABSENT,
493 .type = TM6000_INPUT_TV,
494 .vmux = TM6000_VMUX_VIDEO_B,
495 .amux = TM6000_AMUX_ADC1,
497 .type = TM6000_INPUT_COMPOSITE1,
498 .vmux = TM6000_VMUX_VIDEO_A,
499 .amux = TM6000_AMUX_ADC2,
501 .type = TM6000_INPUT_SVIDEO,
502 .vmux = TM6000_VMUX_VIDEO_AB,
503 .amux = TM6000_AMUX_ADC2,
507 [TM6010_BOARD_TWINHAN_TU501] = {
508 .name = "Twinhan TU501(704D1)",
509 .tuner_type = TUNER_XC2028, /* has a XC3028 */
510 .tuner_addr = 0xc2 >> 1,
511 .demod_addr = 0x1e >> 1,
521 .tuner_reset = TM6010_GPIO_2,
522 .tuner_on = TM6010_GPIO_3,
523 .demod_reset = TM6010_GPIO_1,
524 .demod_on = TM6010_GPIO_4,
525 .power_led = TM6010_GPIO_7,
526 .dvb_led = TM6010_GPIO_5,
530 .type = TM6000_INPUT_TV,
531 .vmux = TM6000_VMUX_VIDEO_B,
532 .amux = TM6000_AMUX_SIF1,
534 .type = TM6000_INPUT_COMPOSITE1,
535 .vmux = TM6000_VMUX_VIDEO_A,
536 .amux = TM6000_AMUX_ADC2,
538 .type = TM6000_INPUT_SVIDEO,
539 .vmux = TM6000_VMUX_VIDEO_AB,
540 .amux = TM6000_AMUX_ADC2,
544 [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
545 .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
546 .tuner_type = TUNER_XC5000,
547 .tuner_addr = 0xc2 >> 1,
548 .demod_addr = 0x1e >> 1,
559 .tuner_reset = TM6010_GPIO_0,
560 .demod_reset = TM6010_GPIO_1,
561 .power_led = TM6010_GPIO_6,
564 .type = TM6000_INPUT_TV,
565 .vmux = TM6000_VMUX_VIDEO_B,
566 .amux = TM6000_AMUX_SIF1,
570 .type = TM6000_INPUT_RADIO,
571 .amux = TM6000_AMUX_ADC1,
574 [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
575 .name = "Beholder Voyager Lite TV/FM USB2.0",
576 .tuner_type = TUNER_XC5000,
577 .tuner_addr = 0xc2 >> 1,
588 .tuner_reset = TM6010_GPIO_0,
589 .power_led = TM6010_GPIO_6,
592 .type = TM6000_INPUT_TV,
593 .vmux = TM6000_VMUX_VIDEO_B,
594 .amux = TM6000_AMUX_SIF1,
598 .type = TM6000_INPUT_RADIO,
599 .amux = TM6000_AMUX_ADC1,
604 /* table of devices that work with this driver */
605 static const struct usb_device_id tm6000_id_table[] = {
606 { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
607 { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
608 { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
609 { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
610 { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
611 { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
612 { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
613 { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
614 { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
615 { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
616 { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
617 { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
618 { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
619 { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
620 { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
621 { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
622 { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
623 { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
624 { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
625 { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
628 MODULE_DEVICE_TABLE(usb, tm6000_id_table);
630 /* Control power led for show some activity */
631 void tm6000_flash_led(struct tm6000_core *dev, u8 state)
633 /* Power LED unconfigured */
634 if (!dev->gpio.power_led)
639 switch (dev->model) {
640 case TM6010_BOARD_HAUPPAUGE_900H:
641 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
642 case TM6010_BOARD_TWINHAN_TU501:
643 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
644 dev->gpio.power_led, 0x00);
646 case TM6010_BOARD_BEHOLD_WANDER:
647 case TM6010_BOARD_BEHOLD_VOYAGER:
648 case TM6010_BOARD_BEHOLD_WANDER_LITE:
649 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
650 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
651 dev->gpio.power_led, 0x01);
657 switch (dev->model) {
658 case TM6010_BOARD_HAUPPAUGE_900H:
659 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
660 case TM6010_BOARD_TWINHAN_TU501:
661 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
662 dev->gpio.power_led, 0x01);
664 case TM6010_BOARD_BEHOLD_WANDER:
665 case TM6010_BOARD_BEHOLD_VOYAGER:
666 case TM6010_BOARD_BEHOLD_WANDER_LITE:
667 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
668 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
669 dev->gpio.power_led, 0x00);
675 /* Tuner callback to provide the proper gpio changes needed for xc5000 */
676 int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
679 struct tm6000_core *dev = ptr;
681 if (dev->tuner_type != TUNER_XC5000)
685 case XC5000_TUNER_RESET:
686 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
687 dev->gpio.tuner_reset, 0x01);
689 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
690 dev->gpio.tuner_reset, 0x00);
692 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
693 dev->gpio.tuner_reset, 0x01);
698 EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
700 /* Tuner callback to provide the proper gpio changes needed for xc2028 */
702 int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
705 struct tm6000_core *dev = ptr;
707 if (dev->tuner_type != TUNER_XC2028)
711 case XC2028_RESET_CLK:
712 tm6000_ir_wait(dev, 0);
714 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
717 rc = tm6000_i2c_reset(dev, 10);
719 case XC2028_TUNER_RESET:
720 /* Reset codes during load firmware */
723 /* newer tuner can faster reset */
724 switch (dev->model) {
725 case TM5600_BOARD_10MOONS_UT821:
726 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
727 dev->gpio.tuner_reset, 0x01);
728 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
731 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
732 dev->gpio.tuner_reset, 0x00);
733 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
736 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
737 dev->gpio.tuner_reset, 0x01);
738 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
741 case TM6010_BOARD_HAUPPAUGE_900H:
742 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
743 case TM6010_BOARD_TWINHAN_TU501:
744 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
745 dev->gpio.tuner_reset, 0x01);
747 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
748 dev->gpio.tuner_reset, 0x00);
750 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
751 dev->gpio.tuner_reset, 0x01);
755 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
756 dev->gpio.tuner_reset, 0x00);
758 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
759 dev->gpio.tuner_reset, 0x01);
764 tm6000_ir_wait(dev, 1);
767 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
772 rc = tm6000_i2c_reset(dev, 100);
776 case XC2028_I2C_FLUSH:
777 tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
778 tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
783 EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
785 int tm6000_cards_setup(struct tm6000_core *dev)
788 * Board-specific initialization sequence. Handles all GPIO
789 * initialization sequences that are board-specific.
790 * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
791 * Probably, they're all based on some reference device. Due to that,
792 * there's a common routine at the end to handle those GPIO's. Devices
793 * that use different pinups or init sequences can just return at
794 * the board-specific session.
796 switch (dev->model) {
797 case TM6010_BOARD_HAUPPAUGE_900H:
798 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
799 case TM6010_BOARD_TWINHAN_TU501:
800 case TM6010_BOARD_GENERIC:
801 /* Turn xceive 3028 on */
802 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
804 /* Turn zarlink zl10353 on */
805 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
807 /* Reset zarlink zl10353 */
808 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
810 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
812 /* Turn zarlink zl10353 off */
813 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
818 /* Power led on (blue) */
819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
821 /* DVB led off (orange) */
822 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
824 /* Turn zarlink zl10353 on */
825 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
828 case TM6010_BOARD_BEHOLD_WANDER:
829 case TM6010_BOARD_BEHOLD_WANDER_LITE:
830 /* Power led on (blue) */
831 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
833 /* Reset zarlink zl10353 */
834 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
836 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
839 case TM6010_BOARD_BEHOLD_VOYAGER:
840 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
841 /* Power led on (blue) */
842 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
850 * Default initialization. Most of the devices seem to use GPIO1
851 * and GPIO4.on the same way, so, this handles the common sequence
852 * used by most devices.
853 * If a device uses a different sequence or different GPIO pins for
854 * reset, just add the code at the board-specific part
857 if (dev->gpio.tuner_reset) {
861 for (i = 0; i < 2; i++) {
862 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
863 dev->gpio.tuner_reset, 0x00);
865 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
869 msleep(10); /* Just to be conservative */
870 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
871 dev->gpio.tuner_reset, 0x01);
873 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
878 printk(KERN_ERR "Tuner reset is not configured\n");
887 static void tm6000_config_tuner(struct tm6000_core *dev)
889 struct tuner_setup tun_setup;
891 /* Load tuner module */
892 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
893 "tuner", dev->tuner_addr, NULL);
895 memset(&tun_setup, 0, sizeof(tun_setup));
896 tun_setup.type = dev->tuner_type;
897 tun_setup.addr = dev->tuner_addr;
899 tun_setup.mode_mask = 0;
900 if (dev->caps.has_tuner)
901 tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
903 switch (dev->tuner_type) {
905 tun_setup.tuner_callback = tm6000_tuner_callback;
908 tun_setup.tuner_callback = tm6000_xc5000_callback;
912 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
914 switch (dev->tuner_type) {
916 struct v4l2_priv_tun_config xc2028_cfg;
917 struct xc2028_ctrl ctl;
919 memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
920 memset(&ctl, 0, sizeof(ctl));
922 ctl.demod = XC3028_FE_ZARLINK456;
924 xc2028_cfg.tuner = TUNER_XC2028;
925 xc2028_cfg.priv = &ctl;
927 switch (dev->model) {
928 case TM6010_BOARD_HAUPPAUGE_900H:
929 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
930 case TM6010_BOARD_TWINHAN_TU501:
932 ctl.fname = "xc3028L-v36.fw";
935 if (dev->dev_type == TM6010)
936 ctl.fname = "xc3028-v27.fw";
938 ctl.fname = "xc3028-v24.fw";
941 printk(KERN_INFO "Setting firmware parameters for xc2028\n");
942 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
949 struct v4l2_priv_tun_config xc5000_cfg;
950 struct xc5000_config ctl = {
951 .i2c_address = dev->tuner_addr,
953 .radio_input = XC5000_RADIO_FM1_MONO,
956 xc5000_cfg.tuner = TUNER_XC5000;
957 xc5000_cfg.priv = &ctl;
959 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
964 printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
969 static int fill_board_specific_data(struct tm6000_core *dev)
973 dev->dev_type = tm6000_boards[dev->model].type;
974 dev->tuner_type = tm6000_boards[dev->model].tuner_type;
975 dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
977 dev->gpio = tm6000_boards[dev->model].gpio;
979 dev->ir_codes = tm6000_boards[dev->model].ir_codes;
981 dev->demod_addr = tm6000_boards[dev->model].demod_addr;
983 dev->caps = tm6000_boards[dev->model].caps;
985 dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
986 dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
987 dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
988 dev->rinput = tm6000_boards[dev->model].rinput;
990 /* setup per-model quirks */
991 switch (dev->model) {
992 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
993 case TM6010_BOARD_HAUPPAUGE_900H:
994 dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
1001 /* initialize hardware */
1002 rc = tm6000_init(dev);
1006 return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1010 static void use_alternative_detection_method(struct tm6000_core *dev)
1014 if (!dev->eedata_size)
1017 for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1018 if (!tm6000_boards[i].eename_size)
1020 if (dev->eedata_size < tm6000_boards[i].eename_pos +
1021 tm6000_boards[i].eename_size)
1024 if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1025 tm6000_boards[i].eename,
1026 tm6000_boards[i].eename_size)) {
1032 printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1038 printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1039 tm6000_boards[model].name, model);
1042 #if defined(CONFIG_MODULES) && defined(MODULE)
1043 static void request_module_async(struct work_struct *work)
1045 struct tm6000_core *dev = container_of(work, struct tm6000_core,
1048 request_module("tm6000-alsa");
1050 if (dev->caps.has_dvb)
1051 request_module("tm6000-dvb");
1054 static void request_modules(struct tm6000_core *dev)
1056 INIT_WORK(&dev->request_module_wk, request_module_async);
1057 schedule_work(&dev->request_module_wk);
1060 static void flush_request_modules(struct tm6000_core *dev)
1062 flush_work(&dev->request_module_wk);
1065 #define request_modules(dev)
1066 #define flush_request_modules(dev)
1067 #endif /* CONFIG_MODULES */
1069 static int tm6000_init_dev(struct tm6000_core *dev)
1071 struct v4l2_frequency f;
1074 mutex_init(&dev->lock);
1075 mutex_lock(&dev->lock);
1077 if (!is_generic(dev->model)) {
1078 rc = fill_board_specific_data(dev);
1082 /* register i2c bus */
1083 rc = tm6000_i2c_register(dev);
1087 /* register i2c bus */
1088 rc = tm6000_i2c_register(dev);
1092 use_alternative_detection_method(dev);
1094 rc = fill_board_specific_data(dev);
1099 /* Default values for STD and resolutions */
1102 dev->norm = V4L2_STD_NTSC_M;
1104 /* Configure tuner */
1105 tm6000_config_tuner(dev);
1107 /* Set video standard */
1108 v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
1110 /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
1112 f.type = V4L2_TUNER_ANALOG_TV;
1113 f.frequency = 3092; /* 193.25 MHz */
1114 dev->freq = f.frequency;
1115 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1117 if (dev->caps.has_tda9874)
1118 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1119 "tvaudio", I2C_ADDR_TDA9874, NULL);
1121 /* register and initialize V4L2 */
1122 rc = tm6000_v4l2_register(dev);
1126 tm6000_add_into_devlist(dev);
1127 tm6000_init_extension(dev);
1129 tm6000_ir_init(dev);
1131 request_modules(dev);
1133 mutex_unlock(&dev->lock);
1137 mutex_unlock(&dev->lock);
1141 /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
1142 #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1144 static void get_max_endpoint(struct usb_device *udev,
1145 struct usb_host_interface *alt,
1147 struct usb_host_endpoint *curr_e,
1148 struct tm6000_endpoint *tm_ep)
1150 u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1151 unsigned int size = tmp & 0x7ff;
1153 if (udev->speed == USB_SPEED_HIGH)
1154 size = size * hb_mult(tmp);
1156 if (size > tm_ep->maxsize) {
1157 tm_ep->endp = curr_e;
1158 tm_ep->maxsize = size;
1159 tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
1160 tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
1162 printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1163 msgtype, curr_e->desc.bEndpointAddress,
1169 * tm6000_usb_probe()
1170 * checks for supported devices
1172 static int tm6000_usb_probe(struct usb_interface *interface,
1173 const struct usb_device_id *id)
1175 struct usb_device *usbdev;
1176 struct tm6000_core *dev;
1181 usbdev = usb_get_dev(interface_to_usbdev(interface));
1183 /* Selects the proper interface */
1184 rc = usb_set_interface(usbdev, 0, 1);
1186 goto report_failure;
1188 /* Check to see next free device and mark as used */
1189 nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
1190 if (nr >= TM6000_MAXBOARDS) {
1191 printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
1196 /* Create and initialize dev struct */
1197 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1202 spin_lock_init(&dev->slock);
1203 mutex_init(&dev->usb_lock);
1205 /* Increment usage count */
1206 set_bit(nr, &tm6000_devused);
1207 snprintf(dev->name, 29, "tm6000 #%d", nr);
1209 dev->model = id->driver_info;
1210 if (card[nr] < ARRAY_SIZE(tm6000_boards))
1211 dev->model = card[nr];
1216 switch (usbdev->speed) {
1220 case USB_SPEED_UNKNOWN:
1221 case USB_SPEED_FULL:
1224 case USB_SPEED_HIGH:
1232 for (i = 0; i < interface->num_altsetting; i++) {
1235 for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1236 struct usb_host_endpoint *e;
1239 e = &interface->altsetting[i].endpoint[ep];
1241 dir_out = ((e->desc.bEndpointAddress &
1242 USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1244 printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1246 interface->altsetting[i].desc.bInterfaceNumber,
1247 interface->altsetting[i].desc.bInterfaceClass);
1249 switch (e->desc.bmAttributes) {
1250 case USB_ENDPOINT_XFER_BULK:
1252 get_max_endpoint(usbdev,
1253 &interface->altsetting[i],
1257 get_max_endpoint(usbdev,
1258 &interface->altsetting[i],
1263 case USB_ENDPOINT_XFER_ISOC:
1265 get_max_endpoint(usbdev,
1266 &interface->altsetting[i],
1270 get_max_endpoint(usbdev,
1271 &interface->altsetting[i],
1276 case USB_ENDPOINT_XFER_INT:
1278 get_max_endpoint(usbdev,
1279 &interface->altsetting[i],
1283 get_max_endpoint(usbdev,
1284 &interface->altsetting[i],
1294 printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1296 le16_to_cpu(dev->udev->descriptor.idVendor),
1297 le16_to_cpu(dev->udev->descriptor.idProduct),
1298 interface->altsetting->desc.bInterfaceNumber);
1300 /* check if the the device has the iso in endpoint at the correct place */
1301 if (!dev->isoc_in.endp) {
1302 printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
1307 /* save our data pointer in this interface device */
1308 usb_set_intfdata(interface, dev);
1310 printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1312 rc = tm6000_init_dev(dev);
1321 printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1323 clear_bit(nr, &tm6000_devused);
1325 usb_put_dev(usbdev);
1330 * tm6000_usb_disconnect()
1331 * called when the device gets diconencted
1332 * video device will be unregistered on v4l2_close in case it is still open
1334 static void tm6000_usb_disconnect(struct usb_interface *interface)
1336 struct tm6000_core *dev = usb_get_intfdata(interface);
1337 usb_set_intfdata(interface, NULL);
1342 printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1344 flush_request_modules(dev);
1346 tm6000_ir_fini(dev);
1348 if (dev->gpio.power_led) {
1349 switch (dev->model) {
1350 case TM6010_BOARD_HAUPPAUGE_900H:
1351 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1352 case TM6010_BOARD_TWINHAN_TU501:
1354 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1355 dev->gpio.power_led, 0x01);
1358 case TM6010_BOARD_BEHOLD_WANDER:
1359 case TM6010_BOARD_BEHOLD_VOYAGER:
1360 case TM6010_BOARD_BEHOLD_WANDER_LITE:
1361 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
1363 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1364 dev->gpio.power_led, 0x00);
1369 tm6000_v4l2_unregister(dev);
1371 tm6000_i2c_unregister(dev);
1373 v4l2_device_unregister(&dev->v4l2_dev);
1375 dev->state |= DEV_DISCONNECTED;
1377 usb_put_dev(dev->udev);
1379 tm6000_close_extension(dev);
1380 tm6000_remove_from_devlist(dev);
1382 clear_bit(dev->devno, &tm6000_devused);
1386 static struct usb_driver tm6000_usb_driver = {
1388 .probe = tm6000_usb_probe,
1389 .disconnect = tm6000_usb_disconnect,
1390 .id_table = tm6000_id_table,
1393 module_usb_driver(tm6000_usb_driver);
1395 MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1396 MODULE_AUTHOR("Mauro Carvalho Chehab");
1397 MODULE_LICENSE("GPL v2");