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