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