linux/drivers/media/usb/tm6000/tm6000-cards.c
<<
>>
Prefs
   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
  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");
1398