root/drivers/media/usb/tm6000/tm6000-cards.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tm6000_flash_led
  2. tm6000_xc5000_callback
  3. tm6000_tuner_callback
  4. tm6000_cards_setup
  5. tm6000_config_tuner
  6. fill_board_specific_data
  7. use_alternative_detection_method
  8. request_module_async
  9. request_modules
  10. flush_request_modules
  11. tm6000_init_dev
  12. get_max_endpoint
  13. tm6000_usb_probe
  14. tm6000_usb_disconnect

   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 "tuner-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
  47 static unsigned int card[]     = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
  48 
  49 module_param_array(card,  int, NULL, 0444);
  50 
  51 static unsigned long tm6000_devused;
  52 
  53 
  54 struct tm6000_board {
  55         char            *name;
  56         char            eename[16];             /* EEPROM name */
  57         unsigned        eename_size;            /* size of EEPROM name */
  58         unsigned        eename_pos;             /* Position where it appears at ROM */
  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 
  75 static struct tm6000_board tm6000_boards[] = {
  76         [TM6000_BOARD_UNKNOWN] = {
  77                 .name         = "Unknown tm6000 video grabber",
  78                 .caps = {
  79                         .has_tuner      = 1,
  80                         .has_eeprom     = 1,
  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 */
 605 static const struct usb_device_id tm6000_id_table[] = {
 606         { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
 607         { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
 608         { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
 609         { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
 610         { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
 611         { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
 612         { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
 613         { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
 614         { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
 615         { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
 616         { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
 617         { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
 618         { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
 619         { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
 620         { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
 621         { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
 622         { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
 623         { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
 624         { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
 625         { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
 626         { }
 627 };
 628 MODULE_DEVICE_TABLE(usb, tm6000_id_table);
 629 
 630 /* Control power led for show some activity */
 631 void 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 */
 676 int 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 }
 698 EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
 699 
 700 /* Tuner callback to provide the proper gpio changes needed for xc2028 */
 701 
 702 int 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 }
 783 EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
 784 
 785 int 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 
 887 static 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 
 969 static 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 
1010 static 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)
1043 static 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 
1054 static 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 
1060 static 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 
1069 static 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 
1136 err:
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 
1144 static void get_max_endpoint(struct usb_device *udev,
1145                              struct usb_host_interface *alt,
1146                              char *msgtype,
1147                              struct usb_host_endpoint *curr_e,
1148                              struct tm6000_endpoint *tm_ep)
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  */
1172 static 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 
1318 free_device:
1319         kfree(dev);
1320 report_failure:
1321         printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1322 
1323         clear_bit(nr, &tm6000_devused);
1324 put_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  */
1334 static 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 
1386 static struct usb_driver tm6000_usb_driver = {
1387                 .name = "tm6000",
1388                 .probe = tm6000_usb_probe,
1389                 .disconnect = tm6000_usb_disconnect,
1390                 .id_table = tm6000_id_table,
1391 };
1392 
1393 module_usb_driver(tm6000_usb_driver);
1394 
1395 MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1396 MODULE_AUTHOR("Mauro Carvalho Chehab");
1397 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */