1
2
3
4
5
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>
17
18#include "tm6000.h"
19#include "tm6000-regs.h"
20#include "xc2028.h"
21#include "xc5000.h"
22
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
40
41#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
42 (model == TM5600_BOARD_GENERIC) || \
43 (model == TM6000_BOARD_GENERIC) || \
44 (model == TM6010_BOARD_GENERIC))
45
46#define TM6000_MAXBOARDS 16
47static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
48
49module_param_array(card, int, NULL, 0444);
50
51static unsigned long tm6000_devused;
52
53
54struct tm6000_board {
55 char *name;
56 char eename[16];
57 unsigned eename_size;
58 unsigned eename_pos;
59
60 struct tm6000_capabilities caps;
61
62 enum tm6000_devtype type;
63 int tuner_type;
64 int tuner_addr;
65 int demod_addr;
66
67 struct tm6000_gpio gpio;
68
69 struct tm6000_input vinput[3];
70 struct tm6000_input rinput;
71
72 char *ir_codes;
73};
74
75static 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,
81 },
82 .gpio = {
83 .tuner_reset = TM6000_GPIO_1,
84 },
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,
97 },
98 },
99 },
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,
108 },
109 .gpio = {
110 .tuner_reset = TM6000_GPIO_1,
111 },
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,
124 },
125 },
126 },
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,
134 },
135 .gpio = {
136 .tuner_reset = TM6000_GPIO_1,
137 },
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,
150 },
151 },
152 },
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,
165 },
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,
174 },
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,
187 },
188 },
189 },
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,
201 },
202 .gpio = {
203 .tuner_reset = TM6000_GPIO_1,
204 },
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,
217 },
218 },
219 },
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,
229 },
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,
242 },
243 },
244 },
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,
255 },
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,
268 },
269 },
270 },
271 [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
272 .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
273 .tuner_type = TUNER_XC2028,
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,
282 },
283 .gpio = {
284 .tuner_reset = TM6000_GPIO_4,
285 },
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,
298 },
299 },
300 },
301 [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
302 .name = "ADSTECH Mini Dual TV USB",
303 .tuner_type = TUNER_XC2028,
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,
311 },
312 .gpio = {
313 .tuner_reset = TM6000_GPIO_4,
314 },
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,
327 },
328 },
329 },
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,
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,
346 },
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,
355 },
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,
368 },
369 },
370 },
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,
384 },
385 .gpio = {
386 .tuner_reset = TM6010_GPIO_0,
387 .demod_reset = TM6010_GPIO_1,
388 .power_led = TM6010_GPIO_6,
389 },
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,
402 },
403 },
404 .rinput = {
405 .type = TM6000_INPUT_RADIO,
406 .amux = TM6000_AMUX_ADC1,
407 },
408 },
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,
421 },
422 .gpio = {
423 .tuner_reset = TM6010_GPIO_0,
424 .power_led = TM6010_GPIO_6,
425 },
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,
438 },
439 },
440 .rinput = {
441 .type = TM6000_INPUT_RADIO,
442 .amux = TM6000_AMUX_ADC1,
443 },
444 },
445 [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
446 .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
447 .tuner_type = TUNER_XC2028,
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,
458 },
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,
467 },
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,
481 },
482 },
483 .rinput = {
484 .type = TM6000_INPUT_RADIO,
485 .amux = TM6000_AMUX_SIF1,
486 },
487 },
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,
504 },
505 },
506 },
507 [TM6010_BOARD_TWINHAN_TU501] = {
508 .name = "Twinhan TU501(704D1)",
509 .tuner_type = TUNER_XC2028,
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,
519 },
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,
528 },
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,
541 },
542 },
543 },
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,
557 },
558 .gpio = {
559 .tuner_reset = TM6010_GPIO_0,
560 .demod_reset = TM6010_GPIO_1,
561 .power_led = TM6010_GPIO_6,
562 },
563 .vinput = { {
564 .type = TM6000_INPUT_TV,
565 .vmux = TM6000_VMUX_VIDEO_B,
566 .amux = TM6000_AMUX_SIF1,
567 },
568 },
569 .rinput = {
570 .type = TM6000_INPUT_RADIO,
571 .amux = TM6000_AMUX_ADC1,
572 },
573 },
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,
586 },
587 .gpio = {
588 .tuner_reset = TM6010_GPIO_0,
589 .power_led = TM6010_GPIO_6,
590 },
591 .vinput = { {
592 .type = TM6000_INPUT_TV,
593 .vmux = TM6000_VMUX_VIDEO_B,
594 .amux = TM6000_AMUX_SIF1,
595 },
596 },
597 .rinput = {
598 .type = TM6000_INPUT_RADIO,
599 .amux = TM6000_AMUX_ADC1,
600 },
601 },
602};
603
604
605static 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 },
626 { }
627};
628MODULE_DEVICE_TABLE(usb, tm6000_id_table);
629
630
631void tm6000_flash_led(struct tm6000_core *dev, u8 state)
632{
633
634 if (!dev->gpio.power_led)
635 return;
636
637
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;
653 }
654 }
655
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;
671 }
672 }
673}
674
675
676int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
677{
678 int rc = 0;
679 struct tm6000_core *dev = ptr;
680
681 if (dev->tuner_type != TUNER_XC5000)
682 return 0;
683
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;
695 }
696 return rc;
697}
698EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
699
700
701
702int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
703{
704 int rc = 0;
705 struct tm6000_core *dev = ptr;
706
707 if (dev->tuner_type != TUNER_XC2028)
708 return 0;
709
710 switch (command) {
711 case XC2028_RESET_CLK:
712 tm6000_ir_wait(dev, 0);
713
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
721 switch (arg) {
722 case 0:
723
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;
762 }
763
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;
774 }
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;
780 }
781 return rc;
782}
783EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
784
785int tm6000_cards_setup(struct tm6000_core *dev)
786{
787
788
789
790
791
792
793
794
795
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
802 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
803 msleep(15);
804
805 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
806 msleep(15);
807
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
813 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
814 msleep(15);
815
816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
817 msleep(15);
818
819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
820 msleep(15);
821
822 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
823 msleep(15);
824
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
831 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
832 msleep(15);
833
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
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;
847 }
848
849
850
851
852
853
854
855
856
857 if (dev->gpio.tuner_reset) {
858 int rc;
859 int i;
860
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;
867 }
868
869 msleep(10);
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;
875 }
876 }
877 } else {
878 printk(KERN_ERR "Tuner reset is not configured\n");
879 return -1;
880 }
881
882 msleep(50);
883
884 return 0;
885};
886
887static void tm6000_config_tuner(struct tm6000_core *dev)
888{
889 struct tuner_setup tun_setup;
890
891
892 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
893 "tuner", dev->tuner_addr, NULL);
894
895 memset(&tun_setup, 0, sizeof(tun_setup));
896 tun_setup.type = dev->tuner_type;
897 tun_setup.addr = dev->tuner_addr;
898
899 tun_setup.mode_mask = 0;
900 if (dev->caps.has_tuner)
901 tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
902
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;
910 }
911
912 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
913
914 switch (dev->tuner_type) {
915 case TUNER_XC2028: {
916 struct v4l2_priv_tun_config xc2028_cfg;
917 struct xc2028_ctrl ctl;
918
919 memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
920 memset(&ctl, 0, sizeof(ctl));
921
922 ctl.demod = XC3028_FE_ZARLINK456;
923
924 xc2028_cfg.tuner = TUNER_XC2028;
925 xc2028_cfg.priv = &ctl;
926
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";
939 }
940
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);
944
945 }
946 break;
947 case TUNER_XC5000:
948 {
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,
954 };
955
956 xc5000_cfg.tuner = TUNER_XC5000;
957 xc5000_cfg.priv = &ctl;
958
959 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
960 &xc5000_cfg);
961 }
962 break;
963 default:
964 printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
965 break;
966 }
967}
968
969static int fill_board_specific_data(struct tm6000_core *dev)
970{
971 int rc;
972
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;
976
977 dev->gpio = tm6000_boards[dev->model].gpio;
978
979 dev->ir_codes = tm6000_boards[dev->model].ir_codes;
980
981 dev->demod_addr = tm6000_boards[dev->model].demod_addr;
982
983 dev->caps = tm6000_boards[dev->model].caps;
984
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;
989
990
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;
996
997 default:
998 break;
999 }
1000
1001
1002 rc = tm6000_init(dev);
1003 if (rc < 0)
1004 return rc;
1005
1006 return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1007}
1008
1009
1010static void use_alternative_detection_method(struct tm6000_core *dev)
1011{
1012 int i, model = -1;
1013
1014 if (!dev->eedata_size)
1015 return;
1016
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;
1023
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;
1029 }
1030 }
1031 if (model < 0) {
1032 printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1033 return;
1034 }
1035
1036 dev->model = model;
1037
1038 printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1039 tm6000_boards[model].name, model);
1040}
1041
1042#if defined(CONFIG_MODULES) && defined(MODULE)
1043static void request_module_async(struct work_struct *work)
1044{
1045 struct tm6000_core *dev = container_of(work, struct tm6000_core,
1046 request_module_wk);
1047
1048 request_module("tm6000-alsa");
1049
1050 if (dev->caps.has_dvb)
1051 request_module("tm6000-dvb");
1052}
1053
1054static void request_modules(struct tm6000_core *dev)
1055{
1056 INIT_WORK(&dev->request_module_wk, request_module_async);
1057 schedule_work(&dev->request_module_wk);
1058}
1059
1060static void flush_request_modules(struct tm6000_core *dev)
1061{
1062 flush_work(&dev->request_module_wk);
1063}
1064#else
1065#define request_modules(dev)
1066#define flush_request_modules(dev)
1067#endif
1068
1069static int tm6000_init_dev(struct tm6000_core *dev)
1070{
1071 struct v4l2_frequency f;
1072 int rc = 0;
1073
1074 mutex_init(&dev->lock);
1075 mutex_lock(&dev->lock);
1076
1077 if (!is_generic(dev->model)) {
1078 rc = fill_board_specific_data(dev);
1079 if (rc < 0)
1080 goto err;
1081
1082
1083 rc = tm6000_i2c_register(dev);
1084 if (rc < 0)
1085 goto err;
1086 } else {
1087
1088 rc = tm6000_i2c_register(dev);
1089 if (rc < 0)
1090 goto err;
1091
1092 use_alternative_detection_method(dev);
1093
1094 rc = fill_board_specific_data(dev);
1095 if (rc < 0)
1096 goto err;
1097 }
1098
1099
1100 dev->width = 720;
1101 dev->height = 480;
1102 dev->norm = V4L2_STD_NTSC_M;
1103
1104
1105 tm6000_config_tuner(dev);
1106
1107
1108 v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
1109
1110
1111 f.tuner = 0;
1112 f.type = V4L2_TUNER_ANALOG_TV;
1113 f.frequency = 3092;
1114 dev->freq = f.frequency;
1115 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1116
1117 if (dev->caps.has_tda9874)
1118 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1119 "tvaudio", I2C_ADDR_TDA9874, NULL);
1120
1121
1122 rc = tm6000_v4l2_register(dev);
1123 if (rc < 0)
1124 goto err;
1125
1126 tm6000_add_into_devlist(dev);
1127 tm6000_init_extension(dev);
1128
1129 tm6000_ir_init(dev);
1130
1131 request_modules(dev);
1132
1133 mutex_unlock(&dev->lock);
1134 return 0;
1135
1136err:
1137 mutex_unlock(&dev->lock);
1138 return rc;
1139}
1140
1141
1142#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1143
1144static 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)
1149{
1150 u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1151 unsigned int size = tmp & 0x7ff;
1152
1153 if (udev->speed == USB_SPEED_HIGH)
1154 size = size * hb_mult(tmp);
1155
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;
1161
1162 printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1163 msgtype, curr_e->desc.bEndpointAddress,
1164 size);
1165 }
1166}
1167
1168
1169
1170
1171
1172static int tm6000_usb_probe(struct usb_interface *interface,
1173 const struct usb_device_id *id)
1174{
1175 struct usb_device *usbdev;
1176 struct tm6000_core *dev;
1177 int i, rc;
1178 int nr = 0;
1179 char *speed;
1180
1181 usbdev = usb_get_dev(interface_to_usbdev(interface));
1182
1183
1184 rc = usb_set_interface(usbdev, 0, 1);
1185 if (rc < 0)
1186 goto report_failure;
1187
1188
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;
1194 }
1195
1196
1197 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1198 if (!dev) {
1199 rc = -ENOMEM;
1200 goto put_device;
1201 }
1202 spin_lock_init(&dev->slock);
1203 mutex_init(&dev->usb_lock);
1204
1205
1206 set_bit(nr, &tm6000_devused);
1207 snprintf(dev->name, 29, "tm6000 #%d", nr);
1208
1209 dev->model = id->driver_info;
1210 if (card[nr] < ARRAY_SIZE(tm6000_boards))
1211 dev->model = card[nr];
1212
1213 dev->udev = usbdev;
1214 dev->devno = nr;
1215
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";
1229 }
1230
1231
1232 for (i = 0; i < interface->num_altsetting; i++) {
1233 int ep;
1234
1235 for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1236 struct usb_host_endpoint *e;
1237 int dir_out;
1238
1239 e = &interface->altsetting[i].endpoint[ep];
1240
1241 dir_out = ((e->desc.bEndpointAddress &
1242 USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1243
1244 printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1245 i,
1246 interface->altsetting[i].desc.bInterfaceNumber,
1247 interface->altsetting[i].desc.bInterfaceClass);
1248
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);
1261 }
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);
1274 }
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);
1287 }
1288 break;
1289 }
1290 }
1291 }
1292
1293
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);
1299
1300
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;
1305 }
1306
1307
1308 usb_set_intfdata(interface, dev);
1309
1310 printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1311
1312 rc = tm6000_init_dev(dev);
1313 if (rc < 0)
1314 goto free_device;
1315
1316 return 0;
1317
1318free_device:
1319 kfree(dev);
1320report_failure:
1321 printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1322
1323 clear_bit(nr, &tm6000_devused);
1324put_device:
1325 usb_put_dev(usbdev);
1326 return rc;
1327}
1328
1329
1330
1331
1332
1333
1334static void tm6000_usb_disconnect(struct usb_interface *interface)
1335{
1336 struct tm6000_core *dev = usb_get_intfdata(interface);
1337 usb_set_intfdata(interface, NULL);
1338
1339 if (!dev)
1340 return;
1341
1342 printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1343
1344 flush_request_modules(dev);
1345
1346 tm6000_ir_fini(dev);
1347
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
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
1363 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1364 dev->gpio.power_led, 0x00);
1365 msleep(15);
1366 break;
1367 }
1368 }
1369 tm6000_v4l2_unregister(dev);
1370
1371 tm6000_i2c_unregister(dev);
1372
1373 v4l2_device_unregister(&dev->v4l2_dev);
1374
1375 dev->state |= DEV_DISCONNECTED;
1376
1377 usb_put_dev(dev->udev);
1378
1379 tm6000_close_extension(dev);
1380 tm6000_remove_from_devlist(dev);
1381
1382 clear_bit(dev->devno, &tm6000_devused);
1383 kfree(dev);
1384}
1385
1386static 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,
1391};
1392
1393module_usb_driver(tm6000_usb_driver);
1394
1395MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1396MODULE_AUTHOR("Mauro Carvalho Chehab");
1397MODULE_LICENSE("GPL v2");
1398