perf tools: Don't clone maps from parent when synthesizing forks
[linux/fpc-iii.git] / drivers / media / usb / tm6000 / tm6000-cards.c
blob23df50aa0a4af6da850d08cc591e6ace135ab223
1 // SPDX-License-Identifier: GPL-2.0
2 // tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
3 //
4 // Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org>
6 #include <linux/init.h>
7 #include <linux/module.h>
8 #include <linux/pci.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>
18 #include "tm6000.h"
19 #include "tm6000-regs.h"
20 #include "tuner-xc2028.h"
21 #include "xc5000.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;
54 struct tm6000_board {
55 char *name;
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;
72 char *ir_codes;
75 static struct tm6000_board tm6000_boards[] = {
76 [TM6000_BOARD_UNKNOWN] = {
77 .name = "Unknown tm6000 video grabber",
78 .caps = {
79 .has_tuner = 1,
80 .has_eeprom = 1,
82 .gpio = {
83 .tuner_reset = TM6000_GPIO_1,
85 .vinput = { {
86 .type = TM6000_INPUT_TV,
87 .vmux = TM6000_VMUX_VIDEO_B,
88 .amux = TM6000_AMUX_ADC1,
89 }, {
90 .type = TM6000_INPUT_COMPOSITE1,
91 .vmux = TM6000_VMUX_VIDEO_A,
92 .amux = TM6000_AMUX_ADC2,
93 }, {
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",
102 .type = TM5600,
103 .tuner_type = TUNER_XC2028,
104 .tuner_addr = 0xc2 >> 1,
105 .caps = {
106 .has_tuner = 1,
107 .has_eeprom = 1,
109 .gpio = {
110 .tuner_reset = TM6000_GPIO_1,
112 .vinput = { {
113 .type = TM6000_INPUT_TV,
114 .vmux = TM6000_VMUX_VIDEO_B,
115 .amux = TM6000_AMUX_ADC1,
116 }, {
117 .type = TM6000_INPUT_COMPOSITE1,
118 .vmux = TM6000_VMUX_VIDEO_A,
119 .amux = TM6000_AMUX_ADC2,
120 }, {
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,
131 .caps = {
132 .has_tuner = 1,
133 .has_eeprom = 1,
135 .gpio = {
136 .tuner_reset = TM6000_GPIO_1,
138 .vinput = { {
139 .type = TM6000_INPUT_TV,
140 .vmux = TM6000_VMUX_VIDEO_B,
141 .amux = TM6000_AMUX_ADC1,
142 }, {
143 .type = TM6000_INPUT_COMPOSITE1,
144 .vmux = TM6000_VMUX_VIDEO_A,
145 .amux = TM6000_AMUX_ADC2,
146 }, {
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",
155 .type = TM6010,
156 .tuner_type = TUNER_XC2028,
157 .tuner_addr = 0xc2 >> 1,
158 .demod_addr = 0x1e >> 1,
159 .caps = {
160 .has_tuner = 1,
161 .has_dvb = 1,
162 .has_zl10353 = 1,
163 .has_eeprom = 1,
164 .has_remote = 1,
166 .gpio = {
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,
173 .ir = TM6010_GPIO_0,
175 .vinput = { {
176 .type = TM6000_INPUT_TV,
177 .vmux = TM6000_VMUX_VIDEO_B,
178 .amux = TM6000_AMUX_SIF1,
179 }, {
180 .type = TM6000_INPUT_COMPOSITE1,
181 .vmux = TM6000_VMUX_VIDEO_A,
182 .amux = TM6000_AMUX_ADC2,
183 }, {
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},
194 .eename_size = 14,
195 .eename_pos = 0x14,
196 .type = TM5600,
197 .tuner_addr = 0xc2 >> 1,
198 .caps = {
199 .has_tuner = 1,
200 .has_eeprom = 1,
202 .gpio = {
203 .tuner_reset = TM6000_GPIO_1,
205 .vinput = { {
206 .type = TM6000_INPUT_TV,
207 .vmux = TM6000_VMUX_VIDEO_B,
208 .amux = TM6000_AMUX_ADC1,
209 }, {
210 .type = TM6000_INPUT_COMPOSITE1,
211 .vmux = TM6000_VMUX_VIDEO_A,
212 .amux = TM6000_AMUX_ADC2,
213 }, {
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,
224 .caps = {
225 .has_tuner = 1,
226 .has_dvb = 0,
227 .has_zl10353 = 0,
228 .has_eeprom = 1,
230 .vinput = { {
231 .type = TM6000_INPUT_TV,
232 .vmux = TM6000_VMUX_VIDEO_B,
233 .amux = TM6000_AMUX_ADC1,
234 }, {
235 .type = TM6000_INPUT_COMPOSITE1,
236 .vmux = TM6000_VMUX_VIDEO_A,
237 .amux = TM6000_AMUX_ADC2,
238 }, {
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,
249 .caps = {
250 .has_tuner = 1,
251 .has_tda9874 = 1,
252 .has_dvb = 1,
253 .has_zl10353 = 1,
254 .has_eeprom = 1,
256 .vinput = { {
257 .type = TM6000_INPUT_TV,
258 .vmux = TM6000_VMUX_VIDEO_B,
259 .amux = TM6000_AMUX_ADC1,
260 }, {
261 .type = TM6000_INPUT_COMPOSITE1,
262 .vmux = TM6000_VMUX_VIDEO_A,
263 .amux = TM6000_AMUX_ADC2,
264 }, {
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,
276 .caps = {
277 .has_tuner = 1,
278 .has_dvb = 1,
279 .has_zl10353 = 1,
280 .has_eeprom = 0,
281 .has_remote = 1,
283 .gpio = {
284 .tuner_reset = TM6000_GPIO_4,
286 .vinput = { {
287 .type = TM6000_INPUT_TV,
288 .vmux = TM6000_VMUX_VIDEO_B,
289 .amux = TM6000_AMUX_ADC1,
290 }, {
291 .type = TM6000_INPUT_COMPOSITE1,
292 .vmux = TM6000_VMUX_VIDEO_A,
293 .amux = TM6000_AMUX_ADC2,
294 }, {
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,
306 .caps = {
307 .has_tuner = 1,
308 .has_dvb = 1,
309 .has_zl10353 = 1,
310 .has_eeprom = 0,
312 .gpio = {
313 .tuner_reset = TM6000_GPIO_4,
315 .vinput = { {
316 .type = TM6000_INPUT_TV,
317 .vmux = TM6000_VMUX_VIDEO_B,
318 .amux = TM6000_AMUX_ADC1,
319 }, {
320 .type = TM6000_INPUT_COMPOSITE1,
321 .vmux = TM6000_VMUX_VIDEO_A,
322 .amux = TM6000_AMUX_ADC2,
323 }, {
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 },
333 .eename_size = 14,
334 .eename_pos = 0x42,
335 .tuner_type = TUNER_XC2028, /* has a XC3028 */
336 .tuner_addr = 0xc2 >> 1,
337 .demod_addr = 0x1e >> 1,
338 .type = TM6010,
339 .ir_codes = RC_MAP_HAUPPAUGE,
340 .caps = {
341 .has_tuner = 1,
342 .has_dvb = 1,
343 .has_zl10353 = 1,
344 .has_eeprom = 1,
345 .has_remote = 1,
347 .gpio = {
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,
354 .ir = TM6010_GPIO_0,
356 .vinput = { {
357 .type = TM6000_INPUT_TV,
358 .vmux = TM6000_VMUX_VIDEO_B,
359 .amux = TM6000_AMUX_SIF1,
360 }, {
361 .type = TM6000_INPUT_COMPOSITE1,
362 .vmux = TM6000_VMUX_VIDEO_A,
363 .amux = TM6000_AMUX_ADC2,
364 }, {
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,
376 .type = TM6010,
377 .caps = {
378 .has_tuner = 1,
379 .has_dvb = 1,
380 .has_zl10353 = 1,
381 .has_eeprom = 1,
382 .has_remote = 1,
383 .has_radio = 1,
385 .gpio = {
386 .tuner_reset = TM6010_GPIO_0,
387 .demod_reset = TM6010_GPIO_1,
388 .power_led = TM6010_GPIO_6,
390 .vinput = { {
391 .type = TM6000_INPUT_TV,
392 .vmux = TM6000_VMUX_VIDEO_B,
393 .amux = TM6000_AMUX_SIF1,
394 }, {
395 .type = TM6000_INPUT_COMPOSITE1,
396 .vmux = TM6000_VMUX_VIDEO_A,
397 .amux = TM6000_AMUX_ADC2,
398 }, {
399 .type = TM6000_INPUT_SVIDEO,
400 .vmux = TM6000_VMUX_VIDEO_AB,
401 .amux = TM6000_AMUX_ADC2,
404 .rinput = {
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,
413 .type = TM6010,
414 .caps = {
415 .has_tuner = 1,
416 .has_dvb = 0,
417 .has_zl10353 = 0,
418 .has_eeprom = 1,
419 .has_remote = 1,
420 .has_radio = 1,
422 .gpio = {
423 .tuner_reset = TM6010_GPIO_0,
424 .power_led = TM6010_GPIO_6,
426 .vinput = { {
427 .type = TM6000_INPUT_TV,
428 .vmux = TM6000_VMUX_VIDEO_B,
429 .amux = TM6000_AMUX_SIF1,
430 }, {
431 .type = TM6000_INPUT_COMPOSITE1,
432 .vmux = TM6000_VMUX_VIDEO_A,
433 .amux = TM6000_AMUX_ADC2,
434 }, {
435 .type = TM6000_INPUT_SVIDEO,
436 .vmux = TM6000_VMUX_VIDEO_AB,
437 .amux = TM6000_AMUX_ADC2,
440 .rinput = {
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,
450 .type = TM6010,
451 .caps = {
452 .has_tuner = 1,
453 .has_dvb = 1,
454 .has_zl10353 = 1,
455 .has_eeprom = 1,
456 .has_remote = 1,
457 .has_radio = 1,
459 .gpio = {
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,
466 .ir = TM6010_GPIO_0,
468 .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
469 .vinput = { {
470 .type = TM6000_INPUT_TV,
471 .vmux = TM6000_VMUX_VIDEO_B,
472 .amux = TM6000_AMUX_SIF1,
473 }, {
474 .type = TM6000_INPUT_COMPOSITE1,
475 .vmux = TM6000_VMUX_VIDEO_A,
476 .amux = TM6000_AMUX_ADC2,
477 }, {
478 .type = TM6000_INPUT_SVIDEO,
479 .vmux = TM6000_VMUX_VIDEO_AB,
480 .amux = TM6000_AMUX_ADC2,
483 .rinput = {
484 .type = TM6000_INPUT_RADIO,
485 .amux = TM6000_AMUX_SIF1,
488 [TM5600_BOARD_TERRATEC_GRABSTER] = {
489 .name = "Terratec Grabster AV 150/250 MX",
490 .type = TM5600,
491 .tuner_type = TUNER_ABSENT,
492 .vinput = { {
493 .type = TM6000_INPUT_TV,
494 .vmux = TM6000_VMUX_VIDEO_B,
495 .amux = TM6000_AMUX_ADC1,
496 }, {
497 .type = TM6000_INPUT_COMPOSITE1,
498 .vmux = TM6000_VMUX_VIDEO_A,
499 .amux = TM6000_AMUX_ADC2,
500 }, {
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,
512 .type = TM6010,
513 .caps = {
514 .has_tuner = 1,
515 .has_dvb = 1,
516 .has_zl10353 = 1,
517 .has_eeprom = 1,
518 .has_remote = 1,
520 .gpio = {
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,
527 .ir = TM6010_GPIO_0,
529 .vinput = { {
530 .type = TM6000_INPUT_TV,
531 .vmux = TM6000_VMUX_VIDEO_B,
532 .amux = TM6000_AMUX_SIF1,
533 }, {
534 .type = TM6000_INPUT_COMPOSITE1,
535 .vmux = TM6000_VMUX_VIDEO_A,
536 .amux = TM6000_AMUX_ADC2,
537 }, {
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,
549 .type = TM6010,
550 .caps = {
551 .has_tuner = 1,
552 .has_dvb = 1,
553 .has_zl10353 = 1,
554 .has_eeprom = 1,
555 .has_remote = 0,
556 .has_radio = 1,
558 .gpio = {
559 .tuner_reset = TM6010_GPIO_0,
560 .demod_reset = TM6010_GPIO_1,
561 .power_led = TM6010_GPIO_6,
563 .vinput = { {
564 .type = TM6000_INPUT_TV,
565 .vmux = TM6000_VMUX_VIDEO_B,
566 .amux = TM6000_AMUX_SIF1,
569 .rinput = {
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,
578 .type = TM6010,
579 .caps = {
580 .has_tuner = 1,
581 .has_dvb = 0,
582 .has_zl10353 = 0,
583 .has_eeprom = 1,
584 .has_remote = 0,
585 .has_radio = 1,
587 .gpio = {
588 .tuner_reset = TM6010_GPIO_0,
589 .power_led = TM6010_GPIO_6,
591 .vinput = { {
592 .type = TM6000_INPUT_TV,
593 .vmux = TM6000_VMUX_VIDEO_B,
594 .amux = TM6000_AMUX_SIF1,
597 .rinput = {
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)
635 return;
637 /* ON Power LED */
638 if (state) {
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);
645 break;
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);
652 break;
655 /* OFF Power LED */
656 else {
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);
663 break;
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);
670 break;
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)
678 int rc = 0;
679 struct tm6000_core *dev = ptr;
681 if (dev->tuner_type != TUNER_XC5000)
682 return 0;
684 switch (command) {
685 case XC5000_TUNER_RESET:
686 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
687 dev->gpio.tuner_reset, 0x01);
688 msleep(15);
689 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
690 dev->gpio.tuner_reset, 0x00);
691 msleep(15);
692 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
693 dev->gpio.tuner_reset, 0x01);
694 break;
696 return rc;
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)
704 int rc = 0;
705 struct tm6000_core *dev = ptr;
707 if (dev->tuner_type != TUNER_XC2028)
708 return 0;
710 switch (command) {
711 case XC2028_RESET_CLK:
712 tm6000_ir_wait(dev, 0);
714 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
715 0x02, arg);
716 msleep(10);
717 rc = tm6000_i2c_reset(dev, 10);
718 break;
719 case XC2028_TUNER_RESET:
720 /* Reset codes during load firmware */
721 switch (arg) {
722 case 0:
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,
729 0x300, 0x01);
730 msleep(10);
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,
734 0x300, 0x00);
735 msleep(10);
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,
739 0x300, 0x01);
740 break;
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);
746 msleep(60);
747 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
748 dev->gpio.tuner_reset, 0x00);
749 msleep(75);
750 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
751 dev->gpio.tuner_reset, 0x01);
752 msleep(60);
753 break;
754 default:
755 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
756 dev->gpio.tuner_reset, 0x00);
757 msleep(130);
758 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
759 dev->gpio.tuner_reset, 0x01);
760 msleep(130);
761 break;
764 tm6000_ir_wait(dev, 1);
765 break;
766 case 1:
767 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
768 0x02, 0x01);
769 msleep(10);
770 break;
771 case 2:
772 rc = tm6000_i2c_reset(dev, 100);
773 break;
775 break;
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);
779 break;
781 return rc;
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);
803 msleep(15);
804 /* Turn zarlink zl10353 on */
805 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
806 msleep(15);
807 /* Reset zarlink zl10353 */
808 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
809 msleep(50);
810 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
811 msleep(15);
812 /* Turn zarlink zl10353 off */
813 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
814 msleep(15);
815 /* ir ? */
816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
817 msleep(15);
818 /* Power led on (blue) */
819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
820 msleep(15);
821 /* DVB led off (orange) */
822 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
823 msleep(15);
824 /* Turn zarlink zl10353 on */
825 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
826 msleep(15);
827 break;
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);
832 msleep(15);
833 /* Reset zarlink zl10353 */
834 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
835 msleep(50);
836 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
837 msleep(15);
838 break;
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);
843 msleep(15);
844 break;
845 default:
846 break;
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) {
858 int rc;
859 int i;
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);
864 if (rc < 0) {
865 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
866 return 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);
872 if (rc < 0) {
873 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
874 return rc;
877 } else {
878 printk(KERN_ERR "Tuner reset is not configured\n");
879 return -1;
882 msleep(50);
884 return 0;
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) {
904 case TUNER_XC2028:
905 tun_setup.tuner_callback = tm6000_tuner_callback;
906 break;
907 case TUNER_XC5000:
908 tun_setup.tuner_callback = tm6000_xc5000_callback;
909 break;
912 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
914 switch (dev->tuner_type) {
915 case TUNER_XC2028: {
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:
931 ctl.max_len = 80;
932 ctl.fname = "xc3028L-v36.fw";
933 break;
934 default:
935 if (dev->dev_type == TM6010)
936 ctl.fname = "xc3028-v27.fw";
937 else
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,
943 &xc2028_cfg);
946 break;
947 case TUNER_XC5000:
949 struct v4l2_priv_tun_config xc5000_cfg;
950 struct xc5000_config ctl = {
951 .i2c_address = dev->tuner_addr,
952 .if_khz = 4570,
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,
960 &xc5000_cfg);
962 break;
963 default:
964 printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
965 break;
969 static int fill_board_specific_data(struct tm6000_core *dev)
971 int rc;
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;
995 break;
997 default:
998 break;
1001 /* initialize hardware */
1002 rc = tm6000_init(dev);
1003 if (rc < 0)
1004 return rc;
1006 return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1010 static void use_alternative_detection_method(struct tm6000_core *dev)
1012 int i, model = -1;
1014 if (!dev->eedata_size)
1015 return;
1017 for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1018 if (!tm6000_boards[i].eename_size)
1019 continue;
1020 if (dev->eedata_size < tm6000_boards[i].eename_pos +
1021 tm6000_boards[i].eename_size)
1022 continue;
1024 if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1025 tm6000_boards[i].eename,
1026 tm6000_boards[i].eename_size)) {
1027 model = i;
1028 break;
1031 if (model < 0) {
1032 printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1033 return;
1036 dev->model = model;
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,
1046 request_module_wk);
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);
1064 #else
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;
1072 int rc = 0;
1074 mutex_init(&dev->lock);
1075 mutex_lock(&dev->lock);
1077 if (!is_generic(dev->model)) {
1078 rc = fill_board_specific_data(dev);
1079 if (rc < 0)
1080 goto err;
1082 /* register i2c bus */
1083 rc = tm6000_i2c_register(dev);
1084 if (rc < 0)
1085 goto err;
1086 } else {
1087 /* register i2c bus */
1088 rc = tm6000_i2c_register(dev);
1089 if (rc < 0)
1090 goto err;
1092 use_alternative_detection_method(dev);
1094 rc = fill_board_specific_data(dev);
1095 if (rc < 0)
1096 goto err;
1099 /* Default values for STD and resolutions */
1100 dev->width = 720;
1101 dev->height = 480;
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 */
1111 f.tuner = 0;
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);
1123 if (rc < 0)
1124 goto err;
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);
1134 return 0;
1136 err:
1137 mutex_unlock(&dev->lock);
1138 return rc;
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,
1146 char *msgtype,
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,
1164 size);
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;
1177 int i, rc;
1178 int nr = 0;
1179 char *speed;
1181 usbdev = usb_get_dev(interface_to_usbdev(interface));
1183 /* Selects the proper interface */
1184 rc = usb_set_interface(usbdev, 0, 1);
1185 if (rc < 0)
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);
1192 rc = -ENOMEM;
1193 goto put_device;
1196 /* Create and initialize dev struct */
1197 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1198 if (!dev) {
1199 rc = -ENOMEM;
1200 goto put_device;
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];
1213 dev->udev = usbdev;
1214 dev->devno = nr;
1216 switch (usbdev->speed) {
1217 case USB_SPEED_LOW:
1218 speed = "1.5";
1219 break;
1220 case USB_SPEED_UNKNOWN:
1221 case USB_SPEED_FULL:
1222 speed = "12";
1223 break;
1224 case USB_SPEED_HIGH:
1225 speed = "480";
1226 break;
1227 default:
1228 speed = "unknown";
1231 /* Get endpoints */
1232 for (i = 0; i < interface->num_altsetting; i++) {
1233 int ep;
1235 for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1236 struct usb_host_endpoint *e;
1237 int dir_out;
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:
1251 if (!dir_out) {
1252 get_max_endpoint(usbdev,
1253 &interface->altsetting[i],
1254 "Bulk IN", e,
1255 &dev->bulk_in);
1256 } else {
1257 get_max_endpoint(usbdev,
1258 &interface->altsetting[i],
1259 "Bulk OUT", e,
1260 &dev->bulk_out);
1262 break;
1263 case USB_ENDPOINT_XFER_ISOC:
1264 if (!dir_out) {
1265 get_max_endpoint(usbdev,
1266 &interface->altsetting[i],
1267 "ISOC IN", e,
1268 &dev->isoc_in);
1269 } else {
1270 get_max_endpoint(usbdev,
1271 &interface->altsetting[i],
1272 "ISOC OUT", e,
1273 &dev->isoc_out);
1275 break;
1276 case USB_ENDPOINT_XFER_INT:
1277 if (!dir_out) {
1278 get_max_endpoint(usbdev,
1279 &interface->altsetting[i],
1280 "INT IN", e,
1281 &dev->int_in);
1282 } else {
1283 get_max_endpoint(usbdev,
1284 &interface->altsetting[i],
1285 "INT OUT", e,
1286 &dev->int_out);
1288 break;
1294 printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1295 speed,
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");
1303 rc = -ENODEV;
1304 goto free_device;
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);
1313 if (rc < 0)
1314 goto free_device;
1316 return 0;
1318 free_device:
1319 kfree(dev);
1320 report_failure:
1321 printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1323 clear_bit(nr, &tm6000_devused);
1324 put_device:
1325 usb_put_dev(usbdev);
1326 return rc;
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);
1339 if (!dev)
1340 return;
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:
1353 /* Power led off */
1354 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1355 dev->gpio.power_led, 0x01);
1356 msleep(15);
1357 break;
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:
1362 /* Power led off */
1363 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1364 dev->gpio.power_led, 0x00);
1365 msleep(15);
1366 break;
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);
1383 kfree(dev);
1386 static struct usb_driver tm6000_usb_driver = {
1387 .name = "tm6000",
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");