gma500: begin the config based split
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / staging / tm6000 / tm6000-cards.c
blob9c516cde80479f3f726cdf76231415ebc0bf93f8
1 /*
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 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation version 2
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <linux/init.h>
21 #include <linux/module.h>
22 #include <linux/pci.h>
23 #include <linux/delay.h>
24 #include <linux/i2c.h>
25 #include <linux/usb.h>
26 #include <linux/version.h>
27 #include <linux/slab.h>
28 #include <media/v4l2-common.h>
29 #include <media/tuner.h>
30 #include <media/tvaudio.h>
31 #include <media/i2c-addr.h>
32 #include <media/rc-map.h>
34 #include "tm6000.h"
35 #include "tm6000-regs.h"
36 #include "tuner-xc2028.h"
37 #include "xc5000.h"
39 #define TM6000_BOARD_UNKNOWN 0
40 #define TM5600_BOARD_GENERIC 1
41 #define TM6000_BOARD_GENERIC 2
42 #define TM6010_BOARD_GENERIC 3
43 #define TM5600_BOARD_10MOONS_UT821 4
44 #define TM5600_BOARD_10MOONS_UT330 5
45 #define TM6000_BOARD_ADSTECH_DUAL_TV 6
46 #define TM6000_BOARD_FREECOM_AND_SIMILAR 7
47 #define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8
48 #define TM6010_BOARD_HAUPPAUGE_900H 9
49 #define TM6010_BOARD_BEHOLD_WANDER 10
50 #define TM6010_BOARD_BEHOLD_VOYAGER 11
51 #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
52 #define TM6010_BOARD_TWINHAN_TU501 13
53 #define TM6010_BOARD_BEHOLD_WANDER_LITE 14
54 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
55 #define TM5600_BOARD_TERRATEC_GRABSTER 16
57 #define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
58 (model == TM5600_BOARD_GENERIC) || \
59 (model == TM6000_BOARD_GENERIC) || \
60 (model == TM6010_BOARD_GENERIC))
62 #define TM6000_MAXBOARDS 16
63 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
65 module_param_array(card, int, NULL, 0444);
67 static unsigned long tm6000_devused;
70 struct tm6000_board {
71 char *name;
72 char eename[16]; /* EEPROM name */
73 unsigned eename_size; /* size of EEPROM name */
74 unsigned eename_pos; /* Position where it appears at ROM */
76 struct tm6000_capabilities caps;
78 enum tm6000_devtype type; /* variant of the chipset */
79 int tuner_type; /* type of the tuner */
80 int tuner_addr; /* tuner address */
81 int demod_addr; /* demodulator address */
83 struct tm6000_gpio gpio;
85 struct tm6000_input vinput[3];
86 struct tm6000_input rinput;
88 char *ir_codes;
91 struct tm6000_board tm6000_boards[] = {
92 [TM6000_BOARD_UNKNOWN] = {
93 .name = "Unknown tm6000 video grabber",
94 .caps = {
95 .has_tuner = 1,
96 .has_eeprom = 1,
98 .gpio = {
99 .tuner_reset = TM6000_GPIO_1,
101 .vinput = { {
102 .type = TM6000_INPUT_TV,
103 .vmux = TM6000_VMUX_VIDEO_B,
104 .amux = TM6000_AMUX_ADC1,
105 }, {
106 .type = TM6000_INPUT_COMPOSITE1,
107 .vmux = TM6000_VMUX_VIDEO_A,
108 .amux = TM6000_AMUX_ADC2,
109 }, {
110 .type = TM6000_INPUT_SVIDEO,
111 .vmux = TM6000_VMUX_VIDEO_AB,
112 .amux = TM6000_AMUX_ADC2,
116 [TM5600_BOARD_GENERIC] = {
117 .name = "Generic tm5600 board",
118 .type = TM5600,
119 .tuner_type = TUNER_XC2028,
120 .tuner_addr = 0xc2 >> 1,
121 .caps = {
122 .has_tuner = 1,
123 .has_eeprom = 1,
125 .gpio = {
126 .tuner_reset = TM6000_GPIO_1,
128 .vinput = { {
129 .type = TM6000_INPUT_TV,
130 .vmux = TM6000_VMUX_VIDEO_B,
131 .amux = TM6000_AMUX_ADC1,
132 }, {
133 .type = TM6000_INPUT_COMPOSITE1,
134 .vmux = TM6000_VMUX_VIDEO_A,
135 .amux = TM6000_AMUX_ADC2,
136 }, {
137 .type = TM6000_INPUT_SVIDEO,
138 .vmux = TM6000_VMUX_VIDEO_AB,
139 .amux = TM6000_AMUX_ADC2,
143 [TM6000_BOARD_GENERIC] = {
144 .name = "Generic tm6000 board",
145 .tuner_type = TUNER_XC2028,
146 .tuner_addr = 0xc2 >> 1,
147 .caps = {
148 .has_tuner = 1,
149 .has_eeprom = 1,
151 .gpio = {
152 .tuner_reset = TM6000_GPIO_1,
154 .vinput = { {
155 .type = TM6000_INPUT_TV,
156 .vmux = TM6000_VMUX_VIDEO_B,
157 .amux = TM6000_AMUX_ADC1,
158 }, {
159 .type = TM6000_INPUT_COMPOSITE1,
160 .vmux = TM6000_VMUX_VIDEO_A,
161 .amux = TM6000_AMUX_ADC2,
162 }, {
163 .type = TM6000_INPUT_SVIDEO,
164 .vmux = TM6000_VMUX_VIDEO_AB,
165 .amux = TM6000_AMUX_ADC2,
169 [TM6010_BOARD_GENERIC] = {
170 .name = "Generic tm6010 board",
171 .type = TM6010,
172 .tuner_type = TUNER_XC2028,
173 .tuner_addr = 0xc2 >> 1,
174 .demod_addr = 0x1e >> 1,
175 .caps = {
176 .has_tuner = 1,
177 .has_dvb = 1,
178 .has_zl10353 = 1,
179 .has_eeprom = 1,
180 .has_remote = 1,
182 .gpio = {
183 .tuner_reset = TM6010_GPIO_2,
184 .tuner_on = TM6010_GPIO_3,
185 .demod_reset = TM6010_GPIO_1,
186 .demod_on = TM6010_GPIO_4,
187 .power_led = TM6010_GPIO_7,
188 .dvb_led = TM6010_GPIO_5,
189 .ir = TM6010_GPIO_0,
191 .vinput = { {
192 .type = TM6000_INPUT_TV,
193 .vmux = TM6000_VMUX_VIDEO_B,
194 .amux = TM6000_AMUX_SIF1,
195 }, {
196 .type = TM6000_INPUT_COMPOSITE1,
197 .vmux = TM6000_VMUX_VIDEO_A,
198 .amux = TM6000_AMUX_ADC2,
199 }, {
200 .type = TM6000_INPUT_SVIDEO,
201 .vmux = TM6000_VMUX_VIDEO_AB,
202 .amux = TM6000_AMUX_ADC2,
206 [TM5600_BOARD_10MOONS_UT821] = {
207 .name = "10Moons UT 821",
208 .tuner_type = TUNER_XC2028,
209 .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
210 .eename_size = 14,
211 .eename_pos = 0x14,
212 .type = TM5600,
213 .tuner_addr = 0xc2 >> 1,
214 .caps = {
215 .has_tuner = 1,
216 .has_eeprom = 1,
218 .gpio = {
219 .tuner_reset = TM6000_GPIO_1,
221 .vinput = { {
222 .type = TM6000_INPUT_TV,
223 .vmux = TM6000_VMUX_VIDEO_B,
224 .amux = TM6000_AMUX_ADC1,
225 }, {
226 .type = TM6000_INPUT_COMPOSITE1,
227 .vmux = TM6000_VMUX_VIDEO_A,
228 .amux = TM6000_AMUX_ADC2,
229 }, {
230 .type = TM6000_INPUT_SVIDEO,
231 .vmux = TM6000_VMUX_VIDEO_AB,
232 .amux = TM6000_AMUX_ADC2,
236 [TM5600_BOARD_10MOONS_UT330] = {
237 .name = "10Moons UT 330",
238 .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4,
239 .tuner_addr = 0xc8 >> 1,
240 .caps = {
241 .has_tuner = 1,
242 .has_dvb = 0,
243 .has_zl10353 = 0,
244 .has_eeprom = 1,
246 .vinput = { {
247 .type = TM6000_INPUT_TV,
248 .vmux = TM6000_VMUX_VIDEO_B,
249 .amux = TM6000_AMUX_ADC1,
250 }, {
251 .type = TM6000_INPUT_COMPOSITE1,
252 .vmux = TM6000_VMUX_VIDEO_A,
253 .amux = TM6000_AMUX_ADC2,
254 }, {
255 .type = TM6000_INPUT_SVIDEO,
256 .vmux = TM6000_VMUX_VIDEO_AB,
257 .amux = TM6000_AMUX_ADC2,
261 [TM6000_BOARD_ADSTECH_DUAL_TV] = {
262 .name = "ADSTECH Dual TV USB",
263 .tuner_type = TUNER_XC2028,
264 .tuner_addr = 0xc8 >> 1,
265 .caps = {
266 .has_tuner = 1,
267 .has_tda9874 = 1,
268 .has_dvb = 1,
269 .has_zl10353 = 1,
270 .has_eeprom = 1,
272 .vinput = { {
273 .type = TM6000_INPUT_TV,
274 .vmux = TM6000_VMUX_VIDEO_B,
275 .amux = TM6000_AMUX_ADC1,
276 }, {
277 .type = TM6000_INPUT_COMPOSITE1,
278 .vmux = TM6000_VMUX_VIDEO_A,
279 .amux = TM6000_AMUX_ADC2,
280 }, {
281 .type = TM6000_INPUT_SVIDEO,
282 .vmux = TM6000_VMUX_VIDEO_AB,
283 .amux = TM6000_AMUX_ADC2,
287 [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
288 .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
289 .tuner_type = TUNER_XC2028, /* has a XC3028 */
290 .tuner_addr = 0xc2 >> 1,
291 .demod_addr = 0x1e >> 1,
292 .caps = {
293 .has_tuner = 1,
294 .has_dvb = 1,
295 .has_zl10353 = 1,
296 .has_eeprom = 0,
297 .has_remote = 1,
299 .gpio = {
300 .tuner_reset = TM6000_GPIO_4,
302 .vinput = { {
303 .type = TM6000_INPUT_TV,
304 .vmux = TM6000_VMUX_VIDEO_B,
305 .amux = TM6000_AMUX_ADC1,
306 }, {
307 .type = TM6000_INPUT_COMPOSITE1,
308 .vmux = TM6000_VMUX_VIDEO_A,
309 .amux = TM6000_AMUX_ADC2,
310 }, {
311 .type = TM6000_INPUT_SVIDEO,
312 .vmux = TM6000_VMUX_VIDEO_AB,
313 .amux = TM6000_AMUX_ADC2,
317 [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
318 .name = "ADSTECH Mini Dual TV USB",
319 .tuner_type = TUNER_XC2028, /* has a XC3028 */
320 .tuner_addr = 0xc8 >> 1,
321 .demod_addr = 0x1e >> 1,
322 .caps = {
323 .has_tuner = 1,
324 .has_dvb = 1,
325 .has_zl10353 = 1,
326 .has_eeprom = 0,
328 .gpio = {
329 .tuner_reset = TM6000_GPIO_4,
331 .vinput = { {
332 .type = TM6000_INPUT_TV,
333 .vmux = TM6000_VMUX_VIDEO_B,
334 .amux = TM6000_AMUX_ADC1,
335 }, {
336 .type = TM6000_INPUT_COMPOSITE1,
337 .vmux = TM6000_VMUX_VIDEO_A,
338 .amux = TM6000_AMUX_ADC2,
339 }, {
340 .type = TM6000_INPUT_SVIDEO,
341 .vmux = TM6000_VMUX_VIDEO_AB,
342 .amux = TM6000_AMUX_ADC2,
346 [TM6010_BOARD_HAUPPAUGE_900H] = {
347 .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
348 .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
349 .eename_size = 14,
350 .eename_pos = 0x42,
351 .tuner_type = TUNER_XC2028, /* has a XC3028 */
352 .tuner_addr = 0xc2 >> 1,
353 .demod_addr = 0x1e >> 1,
354 .type = TM6010,
355 .caps = {
356 .has_tuner = 1,
357 .has_dvb = 1,
358 .has_zl10353 = 1,
359 .has_eeprom = 1,
360 .has_remote = 1,
362 .gpio = {
363 .tuner_reset = TM6010_GPIO_2,
364 .tuner_on = TM6010_GPIO_3,
365 .demod_reset = TM6010_GPIO_1,
366 .demod_on = TM6010_GPIO_4,
367 .power_led = TM6010_GPIO_7,
368 .dvb_led = TM6010_GPIO_5,
369 .ir = TM6010_GPIO_0,
371 .vinput = { {
372 .type = TM6000_INPUT_TV,
373 .vmux = TM6000_VMUX_VIDEO_B,
374 .amux = TM6000_AMUX_SIF1,
375 }, {
376 .type = TM6000_INPUT_COMPOSITE1,
377 .vmux = TM6000_VMUX_VIDEO_A,
378 .amux = TM6000_AMUX_ADC2,
379 }, {
380 .type = TM6000_INPUT_SVIDEO,
381 .vmux = TM6000_VMUX_VIDEO_AB,
382 .amux = TM6000_AMUX_ADC2,
386 [TM6010_BOARD_BEHOLD_WANDER] = {
387 .name = "Beholder Wander DVB-T/TV/FM USB2.0",
388 .tuner_type = TUNER_XC5000,
389 .tuner_addr = 0xc2 >> 1,
390 .demod_addr = 0x1e >> 1,
391 .type = TM6010,
392 .caps = {
393 .has_tuner = 1,
394 .has_dvb = 1,
395 .has_zl10353 = 1,
396 .has_eeprom = 1,
397 .has_remote = 1,
398 .has_radio = 1.
400 .gpio = {
401 .tuner_reset = TM6010_GPIO_0,
402 .demod_reset = TM6010_GPIO_1,
403 .power_led = TM6010_GPIO_6,
405 .vinput = { {
406 .type = TM6000_INPUT_TV,
407 .vmux = TM6000_VMUX_VIDEO_B,
408 .amux = TM6000_AMUX_SIF1,
409 }, {
410 .type = TM6000_INPUT_COMPOSITE1,
411 .vmux = TM6000_VMUX_VIDEO_A,
412 .amux = TM6000_AMUX_ADC2,
413 }, {
414 .type = TM6000_INPUT_SVIDEO,
415 .vmux = TM6000_VMUX_VIDEO_AB,
416 .amux = TM6000_AMUX_ADC2,
419 .rinput = {
420 .type = TM6000_INPUT_RADIO,
421 .amux = TM6000_AMUX_ADC1,
424 [TM6010_BOARD_BEHOLD_VOYAGER] = {
425 .name = "Beholder Voyager TV/FM USB2.0",
426 .tuner_type = TUNER_XC5000,
427 .tuner_addr = 0xc2 >> 1,
428 .type = TM6010,
429 .caps = {
430 .has_tuner = 1,
431 .has_dvb = 0,
432 .has_zl10353 = 0,
433 .has_eeprom = 1,
434 .has_remote = 1,
435 .has_radio = 1,
437 .gpio = {
438 .tuner_reset = TM6010_GPIO_0,
439 .power_led = TM6010_GPIO_6,
441 .vinput = { {
442 .type = TM6000_INPUT_TV,
443 .vmux = TM6000_VMUX_VIDEO_B,
444 .amux = TM6000_AMUX_SIF1,
445 }, {
446 .type = TM6000_INPUT_COMPOSITE1,
447 .vmux = TM6000_VMUX_VIDEO_A,
448 .amux = TM6000_AMUX_ADC2,
449 }, {
450 .type = TM6000_INPUT_SVIDEO,
451 .vmux = TM6000_VMUX_VIDEO_AB,
452 .amux = TM6000_AMUX_ADC2,
455 .rinput = {
456 .type = TM6000_INPUT_RADIO,
457 .amux = TM6000_AMUX_ADC1,
460 [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
461 .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
462 .tuner_type = TUNER_XC2028, /* has a XC3028 */
463 .tuner_addr = 0xc2 >> 1,
464 .demod_addr = 0x1e >> 1,
465 .type = TM6010,
466 .caps = {
467 .has_tuner = 1,
468 .has_dvb = 1,
469 .has_zl10353 = 1,
470 .has_eeprom = 1,
471 .has_remote = 1,
473 .gpio = {
474 .tuner_reset = TM6010_GPIO_2,
475 .tuner_on = TM6010_GPIO_3,
476 .demod_reset = TM6010_GPIO_1,
477 .demod_on = TM6010_GPIO_4,
478 .power_led = TM6010_GPIO_7,
479 .dvb_led = TM6010_GPIO_5,
480 .ir = TM6010_GPIO_0,
482 .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
483 .vinput = { {
484 .type = TM6000_INPUT_TV,
485 .vmux = TM6000_VMUX_VIDEO_B,
486 .amux = TM6000_AMUX_SIF1,
487 }, {
488 .type = TM6000_INPUT_COMPOSITE1,
489 .vmux = TM6000_VMUX_VIDEO_A,
490 .amux = TM6000_AMUX_ADC2,
491 }, {
492 .type = TM6000_INPUT_SVIDEO,
493 .vmux = TM6000_VMUX_VIDEO_AB,
494 .amux = TM6000_AMUX_ADC2,
498 [TM5600_BOARD_TERRATEC_GRABSTER] = {
499 .name = "Terratec Grabster AV 150/250 MX",
500 .type = TM5600,
501 .tuner_type = TUNER_ABSENT,
502 .vinput = { {
503 .type = TM6000_INPUT_TV,
504 .vmux = TM6000_VMUX_VIDEO_B,
505 .amux = TM6000_AMUX_ADC1,
506 }, {
507 .type = TM6000_INPUT_COMPOSITE1,
508 .vmux = TM6000_VMUX_VIDEO_A,
509 .amux = TM6000_AMUX_ADC2,
510 }, {
511 .type = TM6000_INPUT_SVIDEO,
512 .vmux = TM6000_VMUX_VIDEO_AB,
513 .amux = TM6000_AMUX_ADC2,
517 [TM6010_BOARD_TWINHAN_TU501] = {
518 .name = "Twinhan TU501(704D1)",
519 .tuner_type = TUNER_XC2028, /* has a XC3028 */
520 .tuner_addr = 0xc2 >> 1,
521 .demod_addr = 0x1e >> 1,
522 .type = TM6010,
523 .caps = {
524 .has_tuner = 1,
525 .has_dvb = 1,
526 .has_zl10353 = 1,
527 .has_eeprom = 1,
528 .has_remote = 1,
530 .gpio = {
531 .tuner_reset = TM6010_GPIO_2,
532 .tuner_on = TM6010_GPIO_3,
533 .demod_reset = TM6010_GPIO_1,
534 .demod_on = TM6010_GPIO_4,
535 .power_led = TM6010_GPIO_7,
536 .dvb_led = TM6010_GPIO_5,
537 .ir = TM6010_GPIO_0,
539 .vinput = { {
540 .type = TM6000_INPUT_TV,
541 .vmux = TM6000_VMUX_VIDEO_B,
542 .amux = TM6000_AMUX_SIF1,
543 }, {
544 .type = TM6000_INPUT_COMPOSITE1,
545 .vmux = TM6000_VMUX_VIDEO_A,
546 .amux = TM6000_AMUX_ADC2,
547 }, {
548 .type = TM6000_INPUT_SVIDEO,
549 .vmux = TM6000_VMUX_VIDEO_AB,
550 .amux = TM6000_AMUX_ADC2,
554 [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
555 .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
556 .tuner_type = TUNER_XC5000,
557 .tuner_addr = 0xc2 >> 1,
558 .demod_addr = 0x1e >> 1,
559 .type = TM6010,
560 .caps = {
561 .has_tuner = 1,
562 .has_dvb = 1,
563 .has_zl10353 = 1,
564 .has_eeprom = 1,
565 .has_remote = 0,
566 .has_radio = 1,
568 .gpio = {
569 .tuner_reset = TM6010_GPIO_0,
570 .demod_reset = TM6010_GPIO_1,
571 .power_led = TM6010_GPIO_6,
573 .vinput = { {
574 .type = TM6000_INPUT_TV,
575 .vmux = TM6000_VMUX_VIDEO_B,
576 .amux = TM6000_AMUX_SIF1,
579 .rinput = {
580 .type = TM6000_INPUT_RADIO,
581 .amux = TM6000_AMUX_ADC1,
584 [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
585 .name = "Beholder Voyager Lite TV/FM USB2.0",
586 .tuner_type = TUNER_XC5000,
587 .tuner_addr = 0xc2 >> 1,
588 .type = TM6010,
589 .caps = {
590 .has_tuner = 1,
591 .has_dvb = 0,
592 .has_zl10353 = 0,
593 .has_eeprom = 1,
594 .has_remote = 0,
595 .has_radio = 1,
597 .gpio = {
598 .tuner_reset = TM6010_GPIO_0,
599 .power_led = TM6010_GPIO_6,
601 .vinput = { {
602 .type = TM6000_INPUT_TV,
603 .vmux = TM6000_VMUX_VIDEO_B,
604 .amux = TM6000_AMUX_SIF1,
607 .rinput = {
608 .type = TM6000_INPUT_RADIO,
609 .amux = TM6000_AMUX_ADC1,
614 /* table of devices that work with this driver */
615 struct usb_device_id tm6000_id_table[] = {
616 { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
617 { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
618 { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
619 { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
620 { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
621 { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
622 { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
623 { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
624 { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
625 { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
626 { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
627 { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
628 { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
629 { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
630 { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
631 { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
632 { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
633 { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
634 { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
635 { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
636 { },
639 /* Control power led for show some activity */
640 void tm6000_flash_led(struct tm6000_core *dev, u8 state)
642 /* Power LED unconfigured */
643 if (!dev->gpio.power_led)
644 return;
646 /* ON Power LED */
647 if (state) {
648 switch (dev->model) {
649 case TM6010_BOARD_HAUPPAUGE_900H:
650 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
651 case TM6010_BOARD_TWINHAN_TU501:
652 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
653 dev->gpio.power_led, 0x00);
654 break;
655 case TM6010_BOARD_BEHOLD_WANDER:
656 case TM6010_BOARD_BEHOLD_VOYAGER:
657 case TM6010_BOARD_BEHOLD_WANDER_LITE:
658 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
659 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
660 dev->gpio.power_led, 0x01);
661 break;
664 /* OFF Power LED */
665 else {
666 switch (dev->model) {
667 case TM6010_BOARD_HAUPPAUGE_900H:
668 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
669 case TM6010_BOARD_TWINHAN_TU501:
670 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
671 dev->gpio.power_led, 0x01);
672 break;
673 case TM6010_BOARD_BEHOLD_WANDER:
674 case TM6010_BOARD_BEHOLD_VOYAGER:
675 case TM6010_BOARD_BEHOLD_WANDER_LITE:
676 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
677 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
678 dev->gpio.power_led, 0x00);
679 break;
684 /* Tuner callback to provide the proper gpio changes needed for xc5000 */
685 int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
687 int rc = 0;
688 struct tm6000_core *dev = ptr;
690 if (dev->tuner_type != TUNER_XC5000)
691 return 0;
693 switch (command) {
694 case XC5000_TUNER_RESET:
695 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
696 dev->gpio.tuner_reset, 0x01);
697 msleep(15);
698 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
699 dev->gpio.tuner_reset, 0x00);
700 msleep(15);
701 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
702 dev->gpio.tuner_reset, 0x01);
703 break;
705 return rc;
707 EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
709 /* Tuner callback to provide the proper gpio changes needed for xc2028 */
711 int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
713 int rc = 0;
714 struct tm6000_core *dev = ptr;
716 if (dev->tuner_type != TUNER_XC2028)
717 return 0;
719 switch (command) {
720 case XC2028_RESET_CLK:
721 tm6000_ir_wait(dev, 0);
723 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
724 0x02, arg);
725 msleep(10);
726 rc = tm6000_i2c_reset(dev, 10);
727 break;
728 case XC2028_TUNER_RESET:
729 /* Reset codes during load firmware */
730 switch (arg) {
731 case 0:
732 /* newer tuner can faster reset */
733 switch (dev->model) {
734 case TM5600_BOARD_10MOONS_UT821:
735 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
736 dev->gpio.tuner_reset, 0x01);
737 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
738 0x300, 0x01);
739 msleep(10);
740 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
741 dev->gpio.tuner_reset, 0x00);
742 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
743 0x300, 0x00);
744 msleep(10);
745 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
746 dev->gpio.tuner_reset, 0x01);
747 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
748 0x300, 0x01);
749 break;
750 case TM6010_BOARD_HAUPPAUGE_900H:
751 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
752 case TM6010_BOARD_TWINHAN_TU501:
753 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
754 dev->gpio.tuner_reset, 0x01);
755 msleep(60);
756 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
757 dev->gpio.tuner_reset, 0x00);
758 msleep(75);
759 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
760 dev->gpio.tuner_reset, 0x01);
761 msleep(60);
762 break;
763 default:
764 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
765 dev->gpio.tuner_reset, 0x00);
766 msleep(130);
767 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
768 dev->gpio.tuner_reset, 0x01);
769 msleep(130);
770 break;
773 tm6000_ir_wait(dev, 1);
774 break;
775 case 1:
776 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
777 0x02, 0x01);
778 msleep(10);
779 break;
780 case 2:
781 rc = tm6000_i2c_reset(dev, 100);
782 break;
785 return rc;
787 EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
789 int tm6000_cards_setup(struct tm6000_core *dev)
791 int i, rc;
794 * Board-specific initialization sequence. Handles all GPIO
795 * initialization sequences that are board-specific.
796 * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
797 * Probably, they're all based on some reference device. Due to that,
798 * there's a common routine at the end to handle those GPIO's. Devices
799 * that use different pinups or init sequences can just return at
800 * the board-specific session.
802 switch (dev->model) {
803 case TM6010_BOARD_HAUPPAUGE_900H:
804 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
805 case TM6010_BOARD_TWINHAN_TU501:
806 case TM6010_BOARD_GENERIC:
807 /* Turn xceive 3028 on */
808 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
809 msleep(15);
810 /* Turn zarlink zl10353 on */
811 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
812 msleep(15);
813 /* Reset zarlink zl10353 */
814 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
815 msleep(50);
816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
817 msleep(15);
818 /* Turn zarlink zl10353 off */
819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
820 msleep(15);
821 /* ir ? */
822 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
823 msleep(15);
824 /* Power led on (blue) */
825 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
826 msleep(15);
827 /* DVB led off (orange) */
828 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
829 msleep(15);
830 /* Turn zarlink zl10353 on */
831 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
832 msleep(15);
833 break;
834 case TM6010_BOARD_BEHOLD_WANDER:
835 case TM6010_BOARD_BEHOLD_WANDER_LITE:
836 /* Power led on (blue) */
837 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
838 msleep(15);
839 /* Reset zarlink zl10353 */
840 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
841 msleep(50);
842 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
843 msleep(15);
844 break;
845 case TM6010_BOARD_BEHOLD_VOYAGER:
846 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
847 /* Power led on (blue) */
848 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
849 msleep(15);
850 break;
851 default:
852 break;
856 * Default initialization. Most of the devices seem to use GPIO1
857 * and GPIO4.on the same way, so, this handles the common sequence
858 * used by most devices.
859 * If a device uses a different sequence or different GPIO pins for
860 * reset, just add the code at the board-specific part
863 if (dev->gpio.tuner_reset) {
864 for (i = 0; i < 2; i++) {
865 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
866 dev->gpio.tuner_reset, 0x00);
867 if (rc < 0) {
868 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
869 return rc;
872 msleep(10); /* Just to be conservative */
873 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
874 dev->gpio.tuner_reset, 0x01);
875 if (rc < 0) {
876 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
877 return rc;
880 } else {
881 printk(KERN_ERR "Tuner reset is not configured\n");
882 return -1;
885 msleep(50);
887 return 0;
890 static void tm6000_config_tuner(struct tm6000_core *dev)
892 struct tuner_setup tun_setup;
894 /* Load tuner module */
895 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
896 "tuner", dev->tuner_addr, NULL);
898 memset(&tun_setup, 0, sizeof(tun_setup));
899 tun_setup.type = dev->tuner_type;
900 tun_setup.addr = dev->tuner_addr;
902 tun_setup.mode_mask = 0;
903 if (dev->caps.has_tuner)
904 tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
906 switch (dev->tuner_type) {
907 case TUNER_XC2028:
908 tun_setup.tuner_callback = tm6000_tuner_callback;
909 break;
910 case TUNER_XC5000:
911 tun_setup.tuner_callback = tm6000_xc5000_callback;
912 break;
915 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
917 switch (dev->tuner_type) {
918 case TUNER_XC2028: {
919 struct v4l2_priv_tun_config xc2028_cfg;
920 struct xc2028_ctrl ctl;
922 memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
923 memset(&ctl, 0, sizeof(ctl));
925 ctl.demod = XC3028_FE_ZARLINK456;
927 xc2028_cfg.tuner = TUNER_XC2028;
928 xc2028_cfg.priv = &ctl;
930 switch (dev->model) {
931 case TM6010_BOARD_HAUPPAUGE_900H:
932 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
933 case TM6010_BOARD_TWINHAN_TU501:
934 ctl.fname = "xc3028L-v36.fw";
935 break;
936 default:
937 if (dev->dev_type == TM6010)
938 ctl.fname = "xc3028-v27.fw";
939 else
940 ctl.fname = "xc3028-v24.fw";
943 printk(KERN_INFO "Setting firmware parameters for xc2028\n");
944 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
945 &xc2028_cfg);
948 break;
949 case TUNER_XC5000:
951 struct v4l2_priv_tun_config xc5000_cfg;
952 struct xc5000_config ctl = {
953 .i2c_address = dev->tuner_addr,
954 .if_khz = 4570,
955 .radio_input = XC5000_RADIO_FM1_MONO,
958 xc5000_cfg.tuner = TUNER_XC5000;
959 xc5000_cfg.priv = &ctl;
961 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
962 &xc5000_cfg);
964 break;
965 default:
966 printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
967 break;
971 static int fill_board_specific_data(struct tm6000_core *dev)
973 int rc;
975 dev->dev_type = tm6000_boards[dev->model].type;
976 dev->tuner_type = tm6000_boards[dev->model].tuner_type;
977 dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
979 dev->gpio = tm6000_boards[dev->model].gpio;
981 dev->ir_codes = tm6000_boards[dev->model].ir_codes;
983 dev->demod_addr = tm6000_boards[dev->model].demod_addr;
985 dev->caps = tm6000_boards[dev->model].caps;
987 dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
988 dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
989 dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
990 dev->rinput = tm6000_boards[dev->model].rinput;
992 /* initialize hardware */
993 rc = tm6000_init(dev);
994 if (rc < 0)
995 return rc;
997 return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1001 static void use_alternative_detection_method(struct tm6000_core *dev)
1003 int i, model = -1;
1005 if (!dev->eedata_size)
1006 return;
1008 for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1009 if (!tm6000_boards[i].eename_size)
1010 continue;
1011 if (dev->eedata_size < tm6000_boards[i].eename_pos +
1012 tm6000_boards[i].eename_size)
1013 continue;
1015 if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1016 tm6000_boards[i].eename,
1017 tm6000_boards[i].eename_size)) {
1018 model = i;
1019 break;
1022 if (model < 0) {
1023 printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1024 return;
1027 dev->model = model;
1029 printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1030 tm6000_boards[model].name, model);
1033 static int tm6000_init_dev(struct tm6000_core *dev)
1035 struct v4l2_frequency f;
1036 int rc = 0;
1038 mutex_init(&dev->lock);
1039 mutex_lock(&dev->lock);
1041 if (!is_generic(dev->model)) {
1042 rc = fill_board_specific_data(dev);
1043 if (rc < 0)
1044 goto err;
1046 /* register i2c bus */
1047 rc = tm6000_i2c_register(dev);
1048 if (rc < 0)
1049 goto err;
1050 } else {
1051 /* register i2c bus */
1052 rc = tm6000_i2c_register(dev);
1053 if (rc < 0)
1054 goto err;
1056 use_alternative_detection_method(dev);
1058 rc = fill_board_specific_data(dev);
1059 if (rc < 0)
1060 goto err;
1063 /* Default values for STD and resolutions */
1064 dev->width = 720;
1065 dev->height = 480;
1066 dev->norm = V4L2_STD_PAL_M;
1068 /* Configure tuner */
1069 tm6000_config_tuner(dev);
1071 /* Set video standard */
1072 v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
1074 /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
1075 f.tuner = 0;
1076 f.type = V4L2_TUNER_ANALOG_TV;
1077 f.frequency = 3092; /* 193.25 MHz */
1078 dev->freq = f.frequency;
1079 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1081 if (dev->caps.has_tda9874)
1082 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1083 "tvaudio", I2C_ADDR_TDA9874, NULL);
1085 /* register and initialize V4L2 */
1086 rc = tm6000_v4l2_register(dev);
1087 if (rc < 0)
1088 goto err;
1090 tm6000_add_into_devlist(dev);
1091 tm6000_init_extension(dev);
1093 tm6000_ir_init(dev);
1095 mutex_unlock(&dev->lock);
1096 return 0;
1098 err:
1099 mutex_unlock(&dev->lock);
1100 return rc;
1103 /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
1104 #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1106 static void get_max_endpoint(struct usb_device *udev,
1107 struct usb_host_interface *alt,
1108 char *msgtype,
1109 struct usb_host_endpoint *curr_e,
1110 struct tm6000_endpoint *tm_ep)
1112 u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1113 unsigned int size = tmp & 0x7ff;
1115 if (udev->speed == USB_SPEED_HIGH)
1116 size = size * hb_mult(tmp);
1118 if (size > tm_ep->maxsize) {
1119 tm_ep->endp = curr_e;
1120 tm_ep->maxsize = size;
1121 tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
1122 tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
1124 printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1125 msgtype, curr_e->desc.bEndpointAddress,
1126 size);
1131 * tm6000_usb_probe()
1132 * checks for supported devices
1134 static int tm6000_usb_probe(struct usb_interface *interface,
1135 const struct usb_device_id *id)
1137 struct usb_device *usbdev;
1138 struct tm6000_core *dev = NULL;
1139 int i, rc = 0;
1140 int nr = 0;
1141 char *speed;
1143 usbdev = usb_get_dev(interface_to_usbdev(interface));
1145 /* Selects the proper interface */
1146 rc = usb_set_interface(usbdev, 0, 1);
1147 if (rc < 0)
1148 goto err;
1150 /* Check to see next free device and mark as used */
1151 nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
1152 if (nr >= TM6000_MAXBOARDS) {
1153 printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
1154 usb_put_dev(usbdev);
1155 return -ENOMEM;
1158 /* Create and initialize dev struct */
1159 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1160 if (dev == NULL) {
1161 printk(KERN_ERR "tm6000" ": out of memory!\n");
1162 usb_put_dev(usbdev);
1163 return -ENOMEM;
1165 spin_lock_init(&dev->slock);
1167 /* Increment usage count */
1168 tm6000_devused |= 1<<nr;
1169 snprintf(dev->name, 29, "tm6000 #%d", nr);
1171 dev->model = id->driver_info;
1172 if ((card[nr] >= 0) && (card[nr] < ARRAY_SIZE(tm6000_boards)))
1173 dev->model = card[nr];
1175 dev->udev = usbdev;
1176 dev->devno = nr;
1178 switch (usbdev->speed) {
1179 case USB_SPEED_LOW:
1180 speed = "1.5";
1181 break;
1182 case USB_SPEED_UNKNOWN:
1183 case USB_SPEED_FULL:
1184 speed = "12";
1185 break;
1186 case USB_SPEED_HIGH:
1187 speed = "480";
1188 break;
1189 default:
1190 speed = "unknown";
1195 /* Get endpoints */
1196 for (i = 0; i < interface->num_altsetting; i++) {
1197 int ep;
1199 for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1200 struct usb_host_endpoint *e;
1201 int dir_out;
1203 e = &interface->altsetting[i].endpoint[ep];
1205 dir_out = ((e->desc.bEndpointAddress &
1206 USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1208 printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1210 interface->altsetting[i].desc.bInterfaceNumber,
1211 interface->altsetting[i].desc.bInterfaceClass);
1213 switch (e->desc.bmAttributes) {
1214 case USB_ENDPOINT_XFER_BULK:
1215 if (!dir_out) {
1216 get_max_endpoint(usbdev,
1217 &interface->altsetting[i],
1218 "Bulk IN", e,
1219 &dev->bulk_in);
1220 } else {
1221 get_max_endpoint(usbdev,
1222 &interface->altsetting[i],
1223 "Bulk OUT", e,
1224 &dev->bulk_out);
1226 break;
1227 case USB_ENDPOINT_XFER_ISOC:
1228 if (!dir_out) {
1229 get_max_endpoint(usbdev,
1230 &interface->altsetting[i],
1231 "ISOC IN", e,
1232 &dev->isoc_in);
1233 } else {
1234 get_max_endpoint(usbdev,
1235 &interface->altsetting[i],
1236 "ISOC OUT", e,
1237 &dev->isoc_out);
1239 break;
1240 case USB_ENDPOINT_XFER_INT:
1241 if (!dir_out) {
1242 get_max_endpoint(usbdev,
1243 &interface->altsetting[i],
1244 "INT IN", e,
1245 &dev->int_in);
1246 } else {
1247 get_max_endpoint(usbdev,
1248 &interface->altsetting[i],
1249 "INT OUT", e,
1250 &dev->int_out);
1252 break;
1258 printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1259 speed,
1260 le16_to_cpu(dev->udev->descriptor.idVendor),
1261 le16_to_cpu(dev->udev->descriptor.idProduct),
1262 interface->altsetting->desc.bInterfaceNumber);
1264 /* check if the the device has the iso in endpoint at the correct place */
1265 if (!dev->isoc_in.endp) {
1266 printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
1267 rc = -ENODEV;
1269 goto err;
1272 /* save our data pointer in this interface device */
1273 usb_set_intfdata(interface, dev);
1275 printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1277 rc = tm6000_init_dev(dev);
1279 if (rc < 0)
1280 goto err;
1282 return 0;
1284 err:
1285 printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1287 tm6000_devused &= ~(1<<nr);
1288 usb_put_dev(usbdev);
1290 kfree(dev);
1291 return rc;
1295 * tm6000_usb_disconnect()
1296 * called when the device gets diconencted
1297 * video device will be unregistered on v4l2_close in case it is still open
1299 static void tm6000_usb_disconnect(struct usb_interface *interface)
1301 struct tm6000_core *dev = usb_get_intfdata(interface);
1302 usb_set_intfdata(interface, NULL);
1304 if (!dev)
1305 return;
1307 printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1309 tm6000_ir_fini(dev);
1311 if (dev->gpio.power_led) {
1312 switch (dev->model) {
1313 case TM6010_BOARD_HAUPPAUGE_900H:
1314 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1315 case TM6010_BOARD_TWINHAN_TU501:
1316 /* Power led off */
1317 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1318 dev->gpio.power_led, 0x01);
1319 msleep(15);
1320 break;
1321 case TM6010_BOARD_BEHOLD_WANDER:
1322 case TM6010_BOARD_BEHOLD_VOYAGER:
1323 case TM6010_BOARD_BEHOLD_WANDER_LITE:
1324 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
1325 /* Power led off */
1326 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1327 dev->gpio.power_led, 0x00);
1328 msleep(15);
1329 break;
1332 tm6000_v4l2_unregister(dev);
1334 tm6000_i2c_unregister(dev);
1336 v4l2_device_unregister(&dev->v4l2_dev);
1338 dev->state |= DEV_DISCONNECTED;
1340 usb_put_dev(dev->udev);
1342 tm6000_close_extension(dev);
1343 tm6000_remove_from_devlist(dev);
1345 kfree(dev);
1348 static struct usb_driver tm6000_usb_driver = {
1349 .name = "tm6000",
1350 .probe = tm6000_usb_probe,
1351 .disconnect = tm6000_usb_disconnect,
1352 .id_table = tm6000_id_table,
1355 static int __init tm6000_module_init(void)
1357 int result;
1359 printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n",
1360 (TM6000_VERSION >> 16) & 0xff,
1361 (TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff);
1363 /* register this driver with the USB subsystem */
1364 result = usb_register(&tm6000_usb_driver);
1365 if (result)
1366 printk(KERN_ERR "tm6000"
1367 " usb_register failed. Error number %d.\n", result);
1369 return result;
1372 static void __exit tm6000_module_exit(void)
1374 /* deregister at USB subsystem */
1375 usb_deregister(&tm6000_usb_driver);
1378 module_init(tm6000_module_init);
1379 module_exit(tm6000_module_exit);
1381 MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1382 MODULE_AUTHOR("Mauro Carvalho Chehab");
1383 MODULE_LICENSE("GPL");