tm6000-cards.c (35644B)
1// SPDX-License-Identifier: GPL-2.0 2// tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices 3// 4// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> 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]; /* EEPROM name */ 57 unsigned eename_size; /* size of EEPROM name */ 58 unsigned eename_pos; /* Position where it appears at ROM */ 59 60 struct tm6000_capabilities caps; 61 62 enum tm6000_devtype type; /* variant of the chipset */ 63 int tuner_type; /* type of the tuner */ 64 int tuner_addr; /* tuner address */ 65 int demod_addr; /* demodulator address */ 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, /* has a XC3028 */ 274 .tuner_addr = 0xc2 >> 1, 275 .demod_addr = 0x1e >> 1, 276 .caps = { 277 .has_tuner = 1, 278 .has_dvb = 1, 279 .has_zl10353 = 1, 280 .has_eeprom = 0, 281 .has_remote = 1, 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, /* has a XC3028 */ 304 .tuner_addr = 0xc8 >> 1, 305 .demod_addr = 0x1e >> 1, 306 .caps = { 307 .has_tuner = 1, 308 .has_dvb = 1, 309 .has_zl10353 = 1, 310 .has_eeprom = 0, 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, /* has a XC3028 */ 336 .tuner_addr = 0xc2 >> 1, 337 .demod_addr = 0x1e >> 1, 338 .type = TM6010, 339 .ir_codes = RC_MAP_HAUPPAUGE, 340 .caps = { 341 .has_tuner = 1, 342 .has_dvb = 1, 343 .has_zl10353 = 1, 344 .has_eeprom = 1, 345 .has_remote = 1, 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, /* has a XC3028 */ 448 .tuner_addr = 0xc2 >> 1, 449 .demod_addr = 0x1e >> 1, 450 .type = TM6010, 451 .caps = { 452 .has_tuner = 1, 453 .has_dvb = 1, 454 .has_zl10353 = 1, 455 .has_eeprom = 1, 456 .has_remote = 1, 457 .has_radio = 1, 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, /* has a XC3028 */ 510 .tuner_addr = 0xc2 >> 1, 511 .demod_addr = 0x1e >> 1, 512 .type = TM6010, 513 .caps = { 514 .has_tuner = 1, 515 .has_dvb = 1, 516 .has_zl10353 = 1, 517 .has_eeprom = 1, 518 .has_remote = 1, 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/* table of devices that work with this driver */ 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/* Control power led for show some activity */ 631void tm6000_flash_led(struct tm6000_core *dev, u8 state) 632{ 633 /* Power LED unconfigured */ 634 if (!dev->gpio.power_led) 635 return; 636 637 /* ON Power LED */ 638 if (state) { 639 switch (dev->model) { 640 case TM6010_BOARD_HAUPPAUGE_900H: 641 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: 642 case TM6010_BOARD_TWINHAN_TU501: 643 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 644 dev->gpio.power_led, 0x00); 645 break; 646 case TM6010_BOARD_BEHOLD_WANDER: 647 case TM6010_BOARD_BEHOLD_VOYAGER: 648 case TM6010_BOARD_BEHOLD_WANDER_LITE: 649 case TM6010_BOARD_BEHOLD_VOYAGER_LITE: 650 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 651 dev->gpio.power_led, 0x01); 652 break; 653 } 654 } 655 /* OFF Power LED */ 656 else { 657 switch (dev->model) { 658 case TM6010_BOARD_HAUPPAUGE_900H: 659 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: 660 case TM6010_BOARD_TWINHAN_TU501: 661 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 662 dev->gpio.power_led, 0x01); 663 break; 664 case TM6010_BOARD_BEHOLD_WANDER: 665 case TM6010_BOARD_BEHOLD_VOYAGER: 666 case TM6010_BOARD_BEHOLD_WANDER_LITE: 667 case TM6010_BOARD_BEHOLD_VOYAGER_LITE: 668 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 669 dev->gpio.power_led, 0x00); 670 break; 671 } 672 } 673} 674 675/* Tuner callback to provide the proper gpio changes needed for xc5000 */ 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/* Tuner callback to provide the proper gpio changes needed for xc2028 */ 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 /* Reset codes during load firmware */ 721 switch (arg) { 722 case 0: 723 /* newer tuner can faster reset */ 724 switch (dev->model) { 725 case TM5600_BOARD_10MOONS_UT821: 726 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 727 dev->gpio.tuner_reset, 0x01); 728 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 729 0x300, 0x01); 730 msleep(10); 731 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 732 dev->gpio.tuner_reset, 0x00); 733 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 734 0x300, 0x00); 735 msleep(10); 736 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 737 dev->gpio.tuner_reset, 0x01); 738 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 739 0x300, 0x01); 740 break; 741 case TM6010_BOARD_HAUPPAUGE_900H: 742 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: 743 case TM6010_BOARD_TWINHAN_TU501: 744 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 745 dev->gpio.tuner_reset, 0x01); 746 msleep(60); 747 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 748 dev->gpio.tuner_reset, 0x00); 749 msleep(75); 750 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 751 dev->gpio.tuner_reset, 0x01); 752 msleep(60); 753 break; 754 default: 755 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 756 dev->gpio.tuner_reset, 0x00); 757 msleep(130); 758 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 759 dev->gpio.tuner_reset, 0x01); 760 msleep(130); 761 break; 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 * Board-specific initialization sequence. Handles all GPIO 789 * initialization sequences that are board-specific. 790 * Up to now, all found devices use GPIO1 and GPIO4 at the same way. 791 * Probably, they're all based on some reference device. Due to that, 792 * there's a common routine at the end to handle those GPIO's. Devices 793 * that use different pinups or init sequences can just return at 794 * the board-specific session. 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 /* Turn xceive 3028 on */ 802 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); 803 msleep(15); 804 /* Turn zarlink zl10353 on */ 805 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); 806 msleep(15); 807 /* Reset zarlink zl10353 */ 808 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); 809 msleep(50); 810 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); 811 msleep(15); 812 /* Turn zarlink zl10353 off */ 813 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); 814 msleep(15); 815 /* ir ? */ 816 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); 817 msleep(15); 818 /* Power led on (blue) */ 819 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); 820 msleep(15); 821 /* DVB led off (orange) */ 822 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); 823 msleep(15); 824 /* Turn zarlink zl10353 on */ 825 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); 826 msleep(15); 827 break; 828 case TM6010_BOARD_BEHOLD_WANDER: 829 case TM6010_BOARD_BEHOLD_WANDER_LITE: 830 /* Power led on (blue) */ 831 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); 832 msleep(15); 833 /* Reset zarlink zl10353 */ 834 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); 835 msleep(50); 836 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); 837 msleep(15); 838 break; 839 case TM6010_BOARD_BEHOLD_VOYAGER: 840 case TM6010_BOARD_BEHOLD_VOYAGER_LITE: 841 /* Power led on (blue) */ 842 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); 843 msleep(15); 844 break; 845 default: 846 break; 847 } 848 849 /* 850 * Default initialization. Most of the devices seem to use GPIO1 851 * and GPIO4.on the same way, so, this handles the common sequence 852 * used by most devices. 853 * If a device uses a different sequence or different GPIO pins for 854 * reset, just add the code at the board-specific part 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); /* Just to be conservative */ 870 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 871 dev->gpio.tuner_reset, 0x01); 872 if (rc < 0) { 873 printk(KERN_ERR "Error %i doing tuner reset\n", rc); 874 return rc; 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 /* Load tuner module */ 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 /* setup per-model quirks */ 991 switch (dev->model) { 992 case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: 993 case TM6010_BOARD_HAUPPAUGE_900H: 994 dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; 995 break; 996 997 default: 998 break; 999 } 1000 1001 /* initialize hardware */ 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 /* CONFIG_MODULES */ 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 /* register i2c bus */ 1083 rc = tm6000_i2c_register(dev); 1084 if (rc < 0) 1085 goto err; 1086 } else { 1087 /* register i2c bus */ 1088 rc = tm6000_i2c_register(dev); 1089 if (rc < 0) 1090 goto err; 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 /* Default values for STD and resolutions */ 1100 dev->width = 720; 1101 dev->height = 480; 1102 dev->norm = V4L2_STD_NTSC_M; 1103 1104 /* Configure tuner */ 1105 tm6000_config_tuner(dev); 1106 1107 /* Set video standard */ 1108 v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); 1109 1110 /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ 1111 f.tuner = 0; 1112 f.type = V4L2_TUNER_ANALOG_TV; 1113 f.frequency = 3092; /* 193.25 MHz */ 1114 dev->freq = f.frequency; 1115 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); 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 /* register and initialize V4L2 */ 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/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ 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 * tm6000_usb_probe() 1170 * checks for supported devices 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 /* Selects the proper interface */ 1184 rc = usb_set_interface(usbdev, 0, 1); 1185 if (rc < 0) 1186 goto report_failure; 1187 1188 /* Check to see next free device and mark as used */ 1189 nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); 1190 if (nr >= TM6000_MAXBOARDS) { 1191 printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); 1192 rc = -ENOMEM; 1193 goto put_device; 1194 } 1195 1196 /* Create and initialize dev struct */ 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 /* Increment usage count */ 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 /* Get endpoints */ 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/* check if the the device has the iso in endpoint at the correct place */ 1301 if (!dev->isoc_in.endp) { 1302 printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); 1303 rc = -ENODEV; 1304 goto free_device; 1305 } 1306 1307 /* save our data pointer in this interface device */ 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 * tm6000_usb_disconnect() 1331 * called when the device gets disconnected 1332 * video device will be unregistered on v4l2_close in case it is still open 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 /* Power led off */ 1354 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 1355 dev->gpio.power_led, 0x01); 1356 msleep(15); 1357 break; 1358 case TM6010_BOARD_BEHOLD_WANDER: 1359 case TM6010_BOARD_BEHOLD_VOYAGER: 1360 case TM6010_BOARD_BEHOLD_WANDER_LITE: 1361 case TM6010_BOARD_BEHOLD_VOYAGER_LITE: 1362 /* Power led off */ 1363 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 1364 dev->gpio.power_led, 0x00); 1365 msleep(15); 1366 break; 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");