1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/pci.h>
19#include <linux/delay.h>
20#include <linux/i2c.h>
21#include <linux/usb.h>
22#include <linux/slab.h>
23#include <media/v4l2-common.h>
24#include <media/tuner.h>
25#include <media/i2c/tvaudio.h>
26#include <media/i2c-addr.h>
27#include <media/rc-map.h>
28
29#include "tm6000.h"
30#include "tm6000-regs.h"
31#include "tuner-xc2028.h"
32#include "xc5000.h"
33
34#define TM6000_BOARD_UNKNOWN 0
35#define TM5600_BOARD_GENERIC 1
36#define TM6000_BOARD_GENERIC 2
37#define TM6010_BOARD_GENERIC 3
38#define TM5600_BOARD_10MOONS_UT821 4
39#define TM5600_BOARD_10MOONS_UT330 5
40#define TM6000_BOARD_ADSTECH_DUAL_TV 6
41#define TM6000_BOARD_FREECOM_AND_SIMILAR 7
42#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8
43#define TM6010_BOARD_HAUPPAUGE_900H 9
44#define TM6010_BOARD_BEHOLD_WANDER 10
45#define TM6010_BOARD_BEHOLD_VOYAGER 11
46#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
47#define TM6010_BOARD_TWINHAN_TU501 13
48#define TM6010_BOARD_BEHOLD_WANDER_LITE 14
49#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
50#define TM5600_BOARD_TERRATEC_GRABSTER 16
51
52#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
53 (model == TM5600_BOARD_GENERIC) || \
54 (model == TM6000_BOARD_GENERIC) || \
55 (model == TM6010_BOARD_GENERIC))
56
57#define TM6000_MAXBOARDS 16
58static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
59
60module_param_array(card, int, NULL, 0444);
61
62static unsigned long tm6000_devused;
63
64
65struct tm6000_board {
66 char *name;
67 char eename[16];
68 unsigned eename_size;
69 unsigned eename_pos;
70
71 struct tm6000_capabilities caps;
72
73 enum tm6000_devtype type;
74 int tuner_type;
75 int tuner_addr;
76 int demod_addr;
77
78 struct tm6000_gpio gpio;
79
80 struct tm6000_input vinput[3];
81 struct tm6000_input rinput;
82
83 char *ir_codes;
84};
85
86static struct tm6000_board tm6000_boards[] = {
87 [TM6000_BOARD_UNKNOWN] = {
88 .name = "Unknown tm6000 video grabber",
89 .caps = {
90 .has_tuner = 1,
91 .has_eeprom = 1,
92 },
93 .gpio = {
94 .tuner_reset = TM6000_GPIO_1,
95 },
96 .vinput = { {
97 .type = TM6000_INPUT_TV,
98 .vmux = TM6000_VMUX_VIDEO_B,
99 .amux = TM6000_AMUX_ADC1,
100 }, {
101 .type = TM6000_INPUT_COMPOSITE1,
102 .vmux = TM6000_VMUX_VIDEO_A,
103 .amux = TM6000_AMUX_ADC2,
104 }, {
105 .type = TM6000_INPUT_SVIDEO,
106 .vmux = TM6000_VMUX_VIDEO_AB,
107 .amux = TM6000_AMUX_ADC2,
108 },
109 },
110 },
111 [TM5600_BOARD_GENERIC] = {
112 .name = "Generic tm5600 board",
113 .type = TM5600,
114 .tuner_type = TUNER_XC2028,
115 .tuner_addr = 0xc2 >> 1,
116 .caps = {
117 .has_tuner = 1,
118 .has_eeprom = 1,
119 },
120 .gpio = {
121 .tuner_reset = TM6000_GPIO_1,
122 },
123 .vinput = { {
124 .type = TM6000_INPUT_TV,
125 .vmux = TM6000_VMUX_VIDEO_B,
126 .amux = TM6000_AMUX_ADC1,
127 }, {
128 .type = TM6000_INPUT_COMPOSITE1,
129 .vmux = TM6000_VMUX_VIDEO_A,
130 .amux = TM6000_AMUX_ADC2,
131 }, {
132 .type = TM6000_INPUT_SVIDEO,
133 .vmux = TM6000_VMUX_VIDEO_AB,
134 .amux = TM6000_AMUX_ADC2,
135 },
136 },
137 },
138 [TM6000_BOARD_GENERIC] = {
139 .name = "Generic tm6000 board",
140 .tuner_type = TUNER_XC2028,
141 .tuner_addr = 0xc2 >> 1,
142 .caps = {
143 .has_tuner = 1,
144 .has_eeprom = 1,
145 },
146 .gpio = {
147 .tuner_reset = TM6000_GPIO_1,
148 },
149 .vinput = { {
150 .type = TM6000_INPUT_TV,
151 .vmux = TM6000_VMUX_VIDEO_B,
152 .amux = TM6000_AMUX_ADC1,
153 }, {
154 .type = TM6000_INPUT_COMPOSITE1,
155 .vmux = TM6000_VMUX_VIDEO_A,
156 .amux = TM6000_AMUX_ADC2,
157 }, {
158 .type = TM6000_INPUT_SVIDEO,
159 .vmux = TM6000_VMUX_VIDEO_AB,
160 .amux = TM6000_AMUX_ADC2,
161 },
162 },
163 },
164 [TM6010_BOARD_GENERIC] = {
165 .name = "Generic tm6010 board",
166 .type = TM6010,
167 .tuner_type = TUNER_XC2028,
168 .tuner_addr = 0xc2 >> 1,
169 .demod_addr = 0x1e >> 1,
170 .caps = {
171 .has_tuner = 1,
172 .has_dvb = 1,
173 .has_zl10353 = 1,
174 .has_eeprom = 1,
175 .has_remote = 1,
176 },
177 .gpio = {
178 .tuner_reset = TM6010_GPIO_2,
179 .tuner_on = TM6010_GPIO_3,
180 .demod_reset = TM6010_GPIO_1,
181 .demod_on = TM6010_GPIO_4,
182 .power_led = TM6010_GPIO_7,
183 .dvb_led = TM6010_GPIO_5,
184 .ir = TM6010_GPIO_0,
185 },
186 .vinput = { {
187 .type = TM6000_INPUT_TV,
188 .vmux = TM6000_VMUX_VIDEO_B,
189 .amux = TM6000_AMUX_SIF1,
190 }, {
191 .type = TM6000_INPUT_COMPOSITE1,
192 .vmux = TM6000_VMUX_VIDEO_A,
193 .amux = TM6000_AMUX_ADC2,
194 }, {
195 .type = TM6000_INPUT_SVIDEO,
196 .vmux = TM6000_VMUX_VIDEO_AB,
197 .amux = TM6000_AMUX_ADC2,
198 },
199 },
200 },
201 [TM5600_BOARD_10MOONS_UT821] = {
202 .name = "10Moons UT 821",
203 .tuner_type = TUNER_XC2028,
204 .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
205 .eename_size = 14,
206 .eename_pos = 0x14,
207 .type = TM5600,
208 .tuner_addr = 0xc2 >> 1,
209 .caps = {
210 .has_tuner = 1,
211 .has_eeprom = 1,
212 },
213 .gpio = {
214 .tuner_reset = TM6000_GPIO_1,
215 },
216 .vinput = { {
217 .type = TM6000_INPUT_TV,
218 .vmux = TM6000_VMUX_VIDEO_B,
219 .amux = TM6000_AMUX_ADC1,
220 }, {
221 .type = TM6000_INPUT_COMPOSITE1,
222 .vmux = TM6000_VMUX_VIDEO_A,
223 .amux = TM6000_AMUX_ADC2,
224 }, {
225 .type = TM6000_INPUT_SVIDEO,
226 .vmux = TM6000_VMUX_VIDEO_AB,
227 .amux = TM6000_AMUX_ADC2,
228 },
229 },
230 },
231 [TM5600_BOARD_10MOONS_UT330] = {
232 .name = "10Moons UT 330",
233 .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4,
234 .tuner_addr = 0xc8 >> 1,
235 .caps = {
236 .has_tuner = 1,
237 .has_dvb = 0,
238 .has_zl10353 = 0,
239 .has_eeprom = 1,
240 },
241 .vinput = { {
242 .type = TM6000_INPUT_TV,
243 .vmux = TM6000_VMUX_VIDEO_B,
244 .amux = TM6000_AMUX_ADC1,
245 }, {
246 .type = TM6000_INPUT_COMPOSITE1,
247 .vmux = TM6000_VMUX_VIDEO_A,
248 .amux = TM6000_AMUX_ADC2,
249 }, {
250 .type = TM6000_INPUT_SVIDEO,
251 .vmux = TM6000_VMUX_VIDEO_AB,
252 .amux = TM6000_AMUX_ADC2,
253 },
254 },
255 },
256 [TM6000_BOARD_ADSTECH_DUAL_TV] = {
257 .name = "ADSTECH Dual TV USB",
258 .tuner_type = TUNER_XC2028,
259 .tuner_addr = 0xc8 >> 1,
260 .caps = {
261 .has_tuner = 1,
262 .has_tda9874 = 1,
263 .has_dvb = 1,
264 .has_zl10353 = 1,
265 .has_eeprom = 1,
266 },
267 .vinput = { {
268 .type = TM6000_INPUT_TV,
269 .vmux = TM6000_VMUX_VIDEO_B,
270 .amux = TM6000_AMUX_ADC1,
271 }, {
272 .type = TM6000_INPUT_COMPOSITE1,
273 .vmux = TM6000_VMUX_VIDEO_A,
274 .amux = TM6000_AMUX_ADC2,
275 }, {
276 .type = TM6000_INPUT_SVIDEO,
277 .vmux = TM6000_VMUX_VIDEO_AB,
278 .amux = TM6000_AMUX_ADC2,
279 },
280 },
281 },
282 [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
283 .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
284 .tuner_type = TUNER_XC2028,
285 .tuner_addr = 0xc2 >> 1,
286 .demod_addr = 0x1e >> 1,
287 .caps = {
288 .has_tuner = 1,
289 .has_dvb = 1,
290 .has_zl10353 = 1,
291 .has_eeprom = 0,
292 .has_remote = 1,
293 },
294 .gpio = {
295 .tuner_reset = TM6000_GPIO_4,
296 },
297 .vinput = { {
298 .type = TM6000_INPUT_TV,
299 .vmux = TM6000_VMUX_VIDEO_B,
300 .amux = TM6000_AMUX_ADC1,
301 }, {
302 .type = TM6000_INPUT_COMPOSITE1,
303 .vmux = TM6000_VMUX_VIDEO_A,
304 .amux = TM6000_AMUX_ADC2,
305 }, {
306 .type = TM6000_INPUT_SVIDEO,
307 .vmux = TM6000_VMUX_VIDEO_AB,
308 .amux = TM6000_AMUX_ADC2,
309 },
310 },
311 },
312 [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
313 .name = "ADSTECH Mini Dual TV USB",
314 .tuner_type = TUNER_XC2028,
315 .tuner_addr = 0xc8 >> 1,
316 .demod_addr = 0x1e >> 1,
317 .caps = {
318 .has_tuner = 1,
319 .has_dvb = 1,
320 .has_zl10353 = 1,
321 .has_eeprom = 0,
322 },
323 .gpio = {
324 .tuner_reset = TM6000_GPIO_4,
325 },
326 .vinput = { {
327 .type = TM6000_INPUT_TV,
328 .vmux = TM6000_VMUX_VIDEO_B,
329 .amux = TM6000_AMUX_ADC1,
330 }, {
331 .type = TM6000_INPUT_COMPOSITE1,
332 .vmux = TM6000_VMUX_VIDEO_A,
333 .amux = TM6000_AMUX_ADC2,
334 }, {
335 .type = TM6000_INPUT_SVIDEO,
336 .vmux = TM6000_VMUX_VIDEO_AB,
337 .amux = TM6000_AMUX_ADC2,
338 },
339 },
340 },
341 [TM6010_BOARD_HAUPPAUGE_900H] = {
342 .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
343 .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
344 .eename_size = 14,
345 .eename_pos = 0x42,
346 .tuner_type = TUNER_XC2028,
347 .tuner_addr = 0xc2 >> 1,
348 .demod_addr = 0x1e >> 1,
349 .type = TM6010,
350 .ir_codes = RC_MAP_HAUPPAUGE,
351 .caps = {
352 .has_tuner = 1,
353 .has_dvb = 1,
354 .has_zl10353 = 1,
355 .has_eeprom = 1,
356 .has_remote = 1,
357 },
358 .gpio = {
359 .tuner_reset = TM6010_GPIO_2,
360 .tuner_on = TM6010_GPIO_3,
361 .demod_reset = TM6010_GPIO_1,
362 .demod_on = TM6010_GPIO_4,
363 .power_led = TM6010_GPIO_7,
364 .dvb_led = TM6010_GPIO_5,
365 .ir = TM6010_GPIO_0,
366 },
367 .vinput = { {
368 .type = TM6000_INPUT_TV,
369 .vmux = TM6000_VMUX_VIDEO_B,
370 .amux = TM6000_AMUX_SIF1,
371 }, {
372 .type = TM6000_INPUT_COMPOSITE1,
373 .vmux = TM6000_VMUX_VIDEO_A,
374 .amux = TM6000_AMUX_ADC2,
375 }, {
376 .type = TM6000_INPUT_SVIDEO,
377 .vmux = TM6000_VMUX_VIDEO_AB,
378 .amux = TM6000_AMUX_ADC2,
379 },
380 },
381 },
382 [TM6010_BOARD_BEHOLD_WANDER] = {
383 .name = "Beholder Wander DVB-T/TV/FM USB2.0",
384 .tuner_type = TUNER_XC5000,
385 .tuner_addr = 0xc2 >> 1,
386 .demod_addr = 0x1e >> 1,
387 .type = TM6010,
388 .caps = {
389 .has_tuner = 1,
390 .has_dvb = 1,
391 .has_zl10353 = 1,
392 .has_eeprom = 1,
393 .has_remote = 1,
394 .has_radio = 1,
395 },
396 .gpio = {
397 .tuner_reset = TM6010_GPIO_0,
398 .demod_reset = TM6010_GPIO_1,
399 .power_led = TM6010_GPIO_6,
400 },
401 .vinput = { {
402 .type = TM6000_INPUT_TV,
403 .vmux = TM6000_VMUX_VIDEO_B,
404 .amux = TM6000_AMUX_SIF1,
405 }, {
406 .type = TM6000_INPUT_COMPOSITE1,
407 .vmux = TM6000_VMUX_VIDEO_A,
408 .amux = TM6000_AMUX_ADC2,
409 }, {
410 .type = TM6000_INPUT_SVIDEO,
411 .vmux = TM6000_VMUX_VIDEO_AB,
412 .amux = TM6000_AMUX_ADC2,
413 },
414 },
415 .rinput = {
416 .type = TM6000_INPUT_RADIO,
417 .amux = TM6000_AMUX_ADC1,
418 },
419 },
420 [TM6010_BOARD_BEHOLD_VOYAGER] = {
421 .name = "Beholder Voyager TV/FM USB2.0",
422 .tuner_type = TUNER_XC5000,
423 .tuner_addr = 0xc2 >> 1,
424 .type = TM6010,
425 .caps = {
426 .has_tuner = 1,
427 .has_dvb = 0,
428 .has_zl10353 = 0,
429 .has_eeprom = 1,
430 .has_remote = 1,
431 .has_radio = 1,
432 },
433 .gpio = {
434 .tuner_reset = TM6010_GPIO_0,
435 .power_led = TM6010_GPIO_6,
436 },
437 .vinput = { {
438 .type = TM6000_INPUT_TV,
439 .vmux = TM6000_VMUX_VIDEO_B,
440 .amux = TM6000_AMUX_SIF1,
441 }, {
442 .type = TM6000_INPUT_COMPOSITE1,
443 .vmux = TM6000_VMUX_VIDEO_A,
444 .amux = TM6000_AMUX_ADC2,
445 }, {
446 .type = TM6000_INPUT_SVIDEO,
447 .vmux = TM6000_VMUX_VIDEO_AB,
448 .amux = TM6000_AMUX_ADC2,
449 },
450 },
451 .rinput = {
452 .type = TM6000_INPUT_RADIO,
453 .amux = TM6000_AMUX_ADC1,
454 },
455 },
456 [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
457 .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
458 .tuner_type = TUNER_XC2028,
459 .tuner_addr = 0xc2 >> 1,
460 .demod_addr = 0x1e >> 1,
461 .type = TM6010,
462 .caps = {
463 .has_tuner = 1,
464 .has_dvb = 1,
465 .has_zl10353 = 1,
466 .has_eeprom = 1,
467 .has_remote = 1,
468 .has_radio = 1,
469 },
470 .gpio = {
471 .tuner_reset = TM6010_GPIO_2,
472 .tuner_on = TM6010_GPIO_3,
473 .demod_reset = TM6010_GPIO_1,
474 .demod_on = TM6010_GPIO_4,
475 .power_led = TM6010_GPIO_7,
476 .dvb_led = TM6010_GPIO_5,
477 .ir = TM6010_GPIO_0,
478 },
479 .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
480 .vinput = { {
481 .type = TM6000_INPUT_TV,
482 .vmux = TM6000_VMUX_VIDEO_B,
483 .amux = TM6000_AMUX_SIF1,
484 }, {
485 .type = TM6000_INPUT_COMPOSITE1,
486 .vmux = TM6000_VMUX_VIDEO_A,
487 .amux = TM6000_AMUX_ADC2,
488 }, {
489 .type = TM6000_INPUT_SVIDEO,
490 .vmux = TM6000_VMUX_VIDEO_AB,
491 .amux = TM6000_AMUX_ADC2,
492 },
493 },
494 .rinput = {
495 .type = TM6000_INPUT_RADIO,
496 .amux = TM6000_AMUX_SIF1,
497 },
498 },
499 [TM5600_BOARD_TERRATEC_GRABSTER] = {
500 .name = "Terratec Grabster AV 150/250 MX",
501 .type = TM5600,
502 .tuner_type = TUNER_ABSENT,
503 .vinput = { {
504 .type = TM6000_INPUT_TV,
505 .vmux = TM6000_VMUX_VIDEO_B,
506 .amux = TM6000_AMUX_ADC1,
507 }, {
508 .type = TM6000_INPUT_COMPOSITE1,
509 .vmux = TM6000_VMUX_VIDEO_A,
510 .amux = TM6000_AMUX_ADC2,
511 }, {
512 .type = TM6000_INPUT_SVIDEO,
513 .vmux = TM6000_VMUX_VIDEO_AB,
514 .amux = TM6000_AMUX_ADC2,
515 },
516 },
517 },
518 [TM6010_BOARD_TWINHAN_TU501] = {
519 .name = "Twinhan TU501(704D1)",
520 .tuner_type = TUNER_XC2028,
521 .tuner_addr = 0xc2 >> 1,
522 .demod_addr = 0x1e >> 1,
523 .type = TM6010,
524 .caps = {
525 .has_tuner = 1,
526 .has_dvb = 1,
527 .has_zl10353 = 1,
528 .has_eeprom = 1,
529 .has_remote = 1,
530 },
531 .gpio = {
532 .tuner_reset = TM6010_GPIO_2,
533 .tuner_on = TM6010_GPIO_3,
534 .demod_reset = TM6010_GPIO_1,
535 .demod_on = TM6010_GPIO_4,
536 .power_led = TM6010_GPIO_7,
537 .dvb_led = TM6010_GPIO_5,
538 .ir = TM6010_GPIO_0,
539 },
540 .vinput = { {
541 .type = TM6000_INPUT_TV,
542 .vmux = TM6000_VMUX_VIDEO_B,
543 .amux = TM6000_AMUX_SIF1,
544 }, {
545 .type = TM6000_INPUT_COMPOSITE1,
546 .vmux = TM6000_VMUX_VIDEO_A,
547 .amux = TM6000_AMUX_ADC2,
548 }, {
549 .type = TM6000_INPUT_SVIDEO,
550 .vmux = TM6000_VMUX_VIDEO_AB,
551 .amux = TM6000_AMUX_ADC2,
552 },
553 },
554 },
555 [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
556 .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
557 .tuner_type = TUNER_XC5000,
558 .tuner_addr = 0xc2 >> 1,
559 .demod_addr = 0x1e >> 1,
560 .type = TM6010,
561 .caps = {
562 .has_tuner = 1,
563 .has_dvb = 1,
564 .has_zl10353 = 1,
565 .has_eeprom = 1,
566 .has_remote = 0,
567 .has_radio = 1,
568 },
569 .gpio = {
570 .tuner_reset = TM6010_GPIO_0,
571 .demod_reset = TM6010_GPIO_1,
572 .power_led = TM6010_GPIO_6,
573 },
574 .vinput = { {
575 .type = TM6000_INPUT_TV,
576 .vmux = TM6000_VMUX_VIDEO_B,
577 .amux = TM6000_AMUX_SIF1,
578 },
579 },
580 .rinput = {
581 .type = TM6000_INPUT_RADIO,
582 .amux = TM6000_AMUX_ADC1,
583 },
584 },
585 [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
586 .name = "Beholder Voyager Lite TV/FM USB2.0",
587 .tuner_type = TUNER_XC5000,
588 .tuner_addr = 0xc2 >> 1,
589 .type = TM6010,
590 .caps = {
591 .has_tuner = 1,
592 .has_dvb = 0,
593 .has_zl10353 = 0,
594 .has_eeprom = 1,
595 .has_remote = 0,
596 .has_radio = 1,
597 },
598 .gpio = {
599 .tuner_reset = TM6010_GPIO_0,
600 .power_led = TM6010_GPIO_6,
601 },
602 .vinput = { {
603 .type = TM6000_INPUT_TV,
604 .vmux = TM6000_VMUX_VIDEO_B,
605 .amux = TM6000_AMUX_SIF1,
606 },
607 },
608 .rinput = {
609 .type = TM6000_INPUT_RADIO,
610 .amux = TM6000_AMUX_ADC1,
611 },
612 },
613};
614
615
616static const struct usb_device_id tm6000_id_table[] = {
617 { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
618 { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
619 { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
620 { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
621 { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
622 { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
623 { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
624 { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
625 { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
626 { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
627 { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
628 { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
629 { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
630 { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
631 { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
632 { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
633 { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
634 { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
635 { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
636 { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
637 { }
638};
639MODULE_DEVICE_TABLE(usb, tm6000_id_table);
640
641
642void tm6000_flash_led(struct tm6000_core *dev, u8 state)
643{
644
645 if (!dev->gpio.power_led)
646 return;
647
648
649 if (state) {
650 switch (dev->model) {
651 case TM6010_BOARD_HAUPPAUGE_900H:
652 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
653 case TM6010_BOARD_TWINHAN_TU501:
654 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
655 dev->gpio.power_led, 0x00);
656 break;
657 case TM6010_BOARD_BEHOLD_WANDER:
658 case TM6010_BOARD_BEHOLD_VOYAGER:
659 case TM6010_BOARD_BEHOLD_WANDER_LITE:
660 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
661 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
662 dev->gpio.power_led, 0x01);
663 break;
664 }
665 }
666
667 else {
668 switch (dev->model) {
669 case TM6010_BOARD_HAUPPAUGE_900H:
670 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
671 case TM6010_BOARD_TWINHAN_TU501:
672 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
673 dev->gpio.power_led, 0x01);
674 break;
675 case TM6010_BOARD_BEHOLD_WANDER:
676 case TM6010_BOARD_BEHOLD_VOYAGER:
677 case TM6010_BOARD_BEHOLD_WANDER_LITE:
678 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
679 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
680 dev->gpio.power_led, 0x00);
681 break;
682 }
683 }
684}
685
686
687int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
688{
689 int rc = 0;
690 struct tm6000_core *dev = ptr;
691
692 if (dev->tuner_type != TUNER_XC5000)
693 return 0;
694
695 switch (command) {
696 case XC5000_TUNER_RESET:
697 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
698 dev->gpio.tuner_reset, 0x01);
699 msleep(15);
700 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
701 dev->gpio.tuner_reset, 0x00);
702 msleep(15);
703 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
704 dev->gpio.tuner_reset, 0x01);
705 break;
706 }
707 return rc;
708}
709EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
710
711
712
713int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
714{
715 int rc = 0;
716 struct tm6000_core *dev = ptr;
717
718 if (dev->tuner_type != TUNER_XC2028)
719 return 0;
720
721 switch (command) {
722 case XC2028_RESET_CLK:
723 tm6000_ir_wait(dev, 0);
724
725 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
726 0x02, arg);
727 msleep(10);
728 rc = tm6000_i2c_reset(dev, 10);
729 break;
730 case XC2028_TUNER_RESET:
731
732 switch (arg) {
733 case 0:
734
735 switch (dev->model) {
736 case TM5600_BOARD_10MOONS_UT821:
737 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
738 dev->gpio.tuner_reset, 0x01);
739 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
740 0x300, 0x01);
741 msleep(10);
742 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
743 dev->gpio.tuner_reset, 0x00);
744 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
745 0x300, 0x00);
746 msleep(10);
747 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
748 dev->gpio.tuner_reset, 0x01);
749 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
750 0x300, 0x01);
751 break;
752 case TM6010_BOARD_HAUPPAUGE_900H:
753 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
754 case TM6010_BOARD_TWINHAN_TU501:
755 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
756 dev->gpio.tuner_reset, 0x01);
757 msleep(60);
758 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
759 dev->gpio.tuner_reset, 0x00);
760 msleep(75);
761 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
762 dev->gpio.tuner_reset, 0x01);
763 msleep(60);
764 break;
765 default:
766 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
767 dev->gpio.tuner_reset, 0x00);
768 msleep(130);
769 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
770 dev->gpio.tuner_reset, 0x01);
771 msleep(130);
772 break;
773 }
774
775 tm6000_ir_wait(dev, 1);
776 break;
777 case 1:
778 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
779 0x02, 0x01);
780 msleep(10);
781 break;
782 case 2:
783 rc = tm6000_i2c_reset(dev, 100);
784 break;
785 }
786 break;
787 case XC2028_I2C_FLUSH:
788 tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
789 tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
790 break;
791 }
792 return rc;
793}
794EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
795
796int tm6000_cards_setup(struct tm6000_core *dev)
797{
798
799
800
801
802
803
804
805
806
807 switch (dev->model) {
808 case TM6010_BOARD_HAUPPAUGE_900H:
809 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
810 case TM6010_BOARD_TWINHAN_TU501:
811 case TM6010_BOARD_GENERIC:
812
813 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
814 msleep(15);
815
816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
817 msleep(15);
818
819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
820 msleep(50);
821 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
822 msleep(15);
823
824 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
825 msleep(15);
826
827 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
828 msleep(15);
829
830 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
831 msleep(15);
832
833 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
834 msleep(15);
835
836 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
837 msleep(15);
838 break;
839 case TM6010_BOARD_BEHOLD_WANDER:
840 case TM6010_BOARD_BEHOLD_WANDER_LITE:
841
842 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
843 msleep(15);
844
845 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
846 msleep(50);
847 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
848 msleep(15);
849 break;
850 case TM6010_BOARD_BEHOLD_VOYAGER:
851 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
852
853 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
854 msleep(15);
855 break;
856 default:
857 break;
858 }
859
860
861
862
863
864
865
866
867
868 if (dev->gpio.tuner_reset) {
869 int rc;
870 int i;
871
872 for (i = 0; i < 2; i++) {
873 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
874 dev->gpio.tuner_reset, 0x00);
875 if (rc < 0) {
876 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
877 return rc;
878 }
879
880 msleep(10);
881 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
882 dev->gpio.tuner_reset, 0x01);
883 if (rc < 0) {
884 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
885 return rc;
886 }
887 }
888 } else {
889 printk(KERN_ERR "Tuner reset is not configured\n");
890 return -1;
891 }
892
893 msleep(50);
894
895 return 0;
896};
897
898static void tm6000_config_tuner(struct tm6000_core *dev)
899{
900 struct tuner_setup tun_setup;
901
902
903 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
904 "tuner", dev->tuner_addr, NULL);
905
906 memset(&tun_setup, 0, sizeof(tun_setup));
907 tun_setup.type = dev->tuner_type;
908 tun_setup.addr = dev->tuner_addr;
909
910 tun_setup.mode_mask = 0;
911 if (dev->caps.has_tuner)
912 tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
913
914 switch (dev->tuner_type) {
915 case TUNER_XC2028:
916 tun_setup.tuner_callback = tm6000_tuner_callback;
917 break;
918 case TUNER_XC5000:
919 tun_setup.tuner_callback = tm6000_xc5000_callback;
920 break;
921 }
922
923 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
924
925 switch (dev->tuner_type) {
926 case TUNER_XC2028: {
927 struct v4l2_priv_tun_config xc2028_cfg;
928 struct xc2028_ctrl ctl;
929
930 memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
931 memset(&ctl, 0, sizeof(ctl));
932
933 ctl.demod = XC3028_FE_ZARLINK456;
934
935 xc2028_cfg.tuner = TUNER_XC2028;
936 xc2028_cfg.priv = &ctl;
937
938 switch (dev->model) {
939 case TM6010_BOARD_HAUPPAUGE_900H:
940 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
941 case TM6010_BOARD_TWINHAN_TU501:
942 ctl.max_len = 80;
943 ctl.fname = "xc3028L-v36.fw";
944 break;
945 default:
946 if (dev->dev_type == TM6010)
947 ctl.fname = "xc3028-v27.fw";
948 else
949 ctl.fname = "xc3028-v24.fw";
950 }
951
952 printk(KERN_INFO "Setting firmware parameters for xc2028\n");
953 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
954 &xc2028_cfg);
955
956 }
957 break;
958 case TUNER_XC5000:
959 {
960 struct v4l2_priv_tun_config xc5000_cfg;
961 struct xc5000_config ctl = {
962 .i2c_address = dev->tuner_addr,
963 .if_khz = 4570,
964 .radio_input = XC5000_RADIO_FM1_MONO,
965 };
966
967 xc5000_cfg.tuner = TUNER_XC5000;
968 xc5000_cfg.priv = &ctl;
969
970 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
971 &xc5000_cfg);
972 }
973 break;
974 default:
975 printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
976 break;
977 }
978}
979
980static int fill_board_specific_data(struct tm6000_core *dev)
981{
982 int rc;
983
984 dev->dev_type = tm6000_boards[dev->model].type;
985 dev->tuner_type = tm6000_boards[dev->model].tuner_type;
986 dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
987
988 dev->gpio = tm6000_boards[dev->model].gpio;
989
990 dev->ir_codes = tm6000_boards[dev->model].ir_codes;
991
992 dev->demod_addr = tm6000_boards[dev->model].demod_addr;
993
994 dev->caps = tm6000_boards[dev->model].caps;
995
996 dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
997 dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
998 dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
999 dev->rinput = tm6000_boards[dev->model].rinput;
1000
1001
1002 switch (dev->model) {
1003 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1004 case TM6010_BOARD_HAUPPAUGE_900H:
1005 dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
1006 break;
1007
1008 default:
1009 break;
1010 }
1011
1012
1013 rc = tm6000_init(dev);
1014 if (rc < 0)
1015 return rc;
1016
1017 return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1018}
1019
1020
1021static void use_alternative_detection_method(struct tm6000_core *dev)
1022{
1023 int i, model = -1;
1024
1025 if (!dev->eedata_size)
1026 return;
1027
1028 for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1029 if (!tm6000_boards[i].eename_size)
1030 continue;
1031 if (dev->eedata_size < tm6000_boards[i].eename_pos +
1032 tm6000_boards[i].eename_size)
1033 continue;
1034
1035 if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1036 tm6000_boards[i].eename,
1037 tm6000_boards[i].eename_size)) {
1038 model = i;
1039 break;
1040 }
1041 }
1042 if (model < 0) {
1043 printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1044 return;
1045 }
1046
1047 dev->model = model;
1048
1049 printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1050 tm6000_boards[model].name, model);
1051}
1052
1053#if defined(CONFIG_MODULES) && defined(MODULE)
1054static void request_module_async(struct work_struct *work)
1055{
1056 struct tm6000_core *dev = container_of(work, struct tm6000_core,
1057 request_module_wk);
1058
1059 request_module("tm6000-alsa");
1060
1061 if (dev->caps.has_dvb)
1062 request_module("tm6000-dvb");
1063}
1064
1065static void request_modules(struct tm6000_core *dev)
1066{
1067 INIT_WORK(&dev->request_module_wk, request_module_async);
1068 schedule_work(&dev->request_module_wk);
1069}
1070
1071static void flush_request_modules(struct tm6000_core *dev)
1072{
1073 flush_work(&dev->request_module_wk);
1074}
1075#else
1076#define request_modules(dev)
1077#define flush_request_modules(dev)
1078#endif
1079
1080static int tm6000_init_dev(struct tm6000_core *dev)
1081{
1082 struct v4l2_frequency f;
1083 int rc = 0;
1084
1085 mutex_init(&dev->lock);
1086 mutex_lock(&dev->lock);
1087
1088 if (!is_generic(dev->model)) {
1089 rc = fill_board_specific_data(dev);
1090 if (rc < 0)
1091 goto err;
1092
1093
1094 rc = tm6000_i2c_register(dev);
1095 if (rc < 0)
1096 goto err;
1097 } else {
1098
1099 rc = tm6000_i2c_register(dev);
1100 if (rc < 0)
1101 goto err;
1102
1103 use_alternative_detection_method(dev);
1104
1105 rc = fill_board_specific_data(dev);
1106 if (rc < 0)
1107 goto err;
1108 }
1109
1110
1111 dev->width = 720;
1112 dev->height = 480;
1113 dev->norm = V4L2_STD_NTSC_M;
1114
1115
1116 tm6000_config_tuner(dev);
1117
1118
1119 v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
1120
1121
1122 f.tuner = 0;
1123 f.type = V4L2_TUNER_ANALOG_TV;
1124 f.frequency = 3092;
1125 dev->freq = f.frequency;
1126 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1127
1128 if (dev->caps.has_tda9874)
1129 v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1130 "tvaudio", I2C_ADDR_TDA9874, NULL);
1131
1132
1133 rc = tm6000_v4l2_register(dev);
1134 if (rc < 0)
1135 goto err;
1136
1137 tm6000_add_into_devlist(dev);
1138 tm6000_init_extension(dev);
1139
1140 tm6000_ir_init(dev);
1141
1142 request_modules(dev);
1143
1144 mutex_unlock(&dev->lock);
1145 return 0;
1146
1147err:
1148 mutex_unlock(&dev->lock);
1149 return rc;
1150}
1151
1152
1153#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1154
1155static void get_max_endpoint(struct usb_device *udev,
1156 struct usb_host_interface *alt,
1157 char *msgtype,
1158 struct usb_host_endpoint *curr_e,
1159 struct tm6000_endpoint *tm_ep)
1160{
1161 u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1162 unsigned int size = tmp & 0x7ff;
1163
1164 if (udev->speed == USB_SPEED_HIGH)
1165 size = size * hb_mult(tmp);
1166
1167 if (size > tm_ep->maxsize) {
1168 tm_ep->endp = curr_e;
1169 tm_ep->maxsize = size;
1170 tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
1171 tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
1172
1173 printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1174 msgtype, curr_e->desc.bEndpointAddress,
1175 size);
1176 }
1177}
1178
1179
1180
1181
1182
1183static int tm6000_usb_probe(struct usb_interface *interface,
1184 const struct usb_device_id *id)
1185{
1186 struct usb_device *usbdev;
1187 struct tm6000_core *dev = NULL;
1188 int i, rc = 0;
1189 int nr = 0;
1190 char *speed;
1191
1192 usbdev = usb_get_dev(interface_to_usbdev(interface));
1193
1194
1195 rc = usb_set_interface(usbdev, 0, 1);
1196 if (rc < 0)
1197 goto err;
1198
1199
1200 nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
1201 if (nr >= TM6000_MAXBOARDS) {
1202 printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
1203 usb_put_dev(usbdev);
1204 return -ENOMEM;
1205 }
1206
1207
1208 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1209 if (dev == NULL) {
1210 printk(KERN_ERR "tm6000" ": out of memory!\n");
1211 usb_put_dev(usbdev);
1212 return -ENOMEM;
1213 }
1214 spin_lock_init(&dev->slock);
1215 mutex_init(&dev->usb_lock);
1216
1217
1218 set_bit(nr, &tm6000_devused);
1219 snprintf(dev->name, 29, "tm6000 #%d", nr);
1220
1221 dev->model = id->driver_info;
1222 if (card[nr] < ARRAY_SIZE(tm6000_boards))
1223 dev->model = card[nr];
1224
1225 dev->udev = usbdev;
1226 dev->devno = nr;
1227
1228 switch (usbdev->speed) {
1229 case USB_SPEED_LOW:
1230 speed = "1.5";
1231 break;
1232 case USB_SPEED_UNKNOWN:
1233 case USB_SPEED_FULL:
1234 speed = "12";
1235 break;
1236 case USB_SPEED_HIGH:
1237 speed = "480";
1238 break;
1239 default:
1240 speed = "unknown";
1241 }
1242
1243
1244 for (i = 0; i < interface->num_altsetting; i++) {
1245 int ep;
1246
1247 for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1248 struct usb_host_endpoint *e;
1249 int dir_out;
1250
1251 e = &interface->altsetting[i].endpoint[ep];
1252
1253 dir_out = ((e->desc.bEndpointAddress &
1254 USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1255
1256 printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1257 i,
1258 interface->altsetting[i].desc.bInterfaceNumber,
1259 interface->altsetting[i].desc.bInterfaceClass);
1260
1261 switch (e->desc.bmAttributes) {
1262 case USB_ENDPOINT_XFER_BULK:
1263 if (!dir_out) {
1264 get_max_endpoint(usbdev,
1265 &interface->altsetting[i],
1266 "Bulk IN", e,
1267 &dev->bulk_in);
1268 } else {
1269 get_max_endpoint(usbdev,
1270 &interface->altsetting[i],
1271 "Bulk OUT", e,
1272 &dev->bulk_out);
1273 }
1274 break;
1275 case USB_ENDPOINT_XFER_ISOC:
1276 if (!dir_out) {
1277 get_max_endpoint(usbdev,
1278 &interface->altsetting[i],
1279 "ISOC IN", e,
1280 &dev->isoc_in);
1281 } else {
1282 get_max_endpoint(usbdev,
1283 &interface->altsetting[i],
1284 "ISOC OUT", e,
1285 &dev->isoc_out);
1286 }
1287 break;
1288 case USB_ENDPOINT_XFER_INT:
1289 if (!dir_out) {
1290 get_max_endpoint(usbdev,
1291 &interface->altsetting[i],
1292 "INT IN", e,
1293 &dev->int_in);
1294 } else {
1295 get_max_endpoint(usbdev,
1296 &interface->altsetting[i],
1297 "INT OUT", e,
1298 &dev->int_out);
1299 }
1300 break;
1301 }
1302 }
1303 }
1304
1305
1306 printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1307 speed,
1308 le16_to_cpu(dev->udev->descriptor.idVendor),
1309 le16_to_cpu(dev->udev->descriptor.idProduct),
1310 interface->altsetting->desc.bInterfaceNumber);
1311
1312
1313 if (!dev->isoc_in.endp) {
1314 printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
1315 rc = -ENODEV;
1316
1317 goto err;
1318 }
1319
1320
1321 usb_set_intfdata(interface, dev);
1322
1323 printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1324
1325 rc = tm6000_init_dev(dev);
1326 if (rc < 0)
1327 goto err;
1328
1329 return 0;
1330
1331err:
1332 printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1333
1334 clear_bit(nr, &tm6000_devused);
1335 usb_put_dev(usbdev);
1336
1337 kfree(dev);
1338 return rc;
1339}
1340
1341
1342
1343
1344
1345
1346static void tm6000_usb_disconnect(struct usb_interface *interface)
1347{
1348 struct tm6000_core *dev = usb_get_intfdata(interface);
1349 usb_set_intfdata(interface, NULL);
1350
1351 if (!dev)
1352 return;
1353
1354 printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1355
1356 flush_request_modules(dev);
1357
1358 tm6000_ir_fini(dev);
1359
1360 if (dev->gpio.power_led) {
1361 switch (dev->model) {
1362 case TM6010_BOARD_HAUPPAUGE_900H:
1363 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1364 case TM6010_BOARD_TWINHAN_TU501:
1365
1366 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1367 dev->gpio.power_led, 0x01);
1368 msleep(15);
1369 break;
1370 case TM6010_BOARD_BEHOLD_WANDER:
1371 case TM6010_BOARD_BEHOLD_VOYAGER:
1372 case TM6010_BOARD_BEHOLD_WANDER_LITE:
1373 case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
1374
1375 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1376 dev->gpio.power_led, 0x00);
1377 msleep(15);
1378 break;
1379 }
1380 }
1381 tm6000_v4l2_unregister(dev);
1382
1383 tm6000_i2c_unregister(dev);
1384
1385 v4l2_device_unregister(&dev->v4l2_dev);
1386
1387 dev->state |= DEV_DISCONNECTED;
1388
1389 usb_put_dev(dev->udev);
1390
1391 tm6000_close_extension(dev);
1392 tm6000_remove_from_devlist(dev);
1393
1394 clear_bit(dev->devno, &tm6000_devused);
1395 kfree(dev);
1396}
1397
1398static struct usb_driver tm6000_usb_driver = {
1399 .name = "tm6000",
1400 .probe = tm6000_usb_probe,
1401 .disconnect = tm6000_usb_disconnect,
1402 .id_table = tm6000_id_table,
1403};
1404
1405module_usb_driver(tm6000_usb_driver);
1406
1407MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1408MODULE_AUTHOR("Mauro Carvalho Chehab");
1409MODULE_LICENSE("GPL");
1410