Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
0003 //
0004 // Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org>
0005 
0006 #include <linux/init.h>
0007 #include <linux/module.h>
0008 #include <linux/pci.h>
0009 #include <linux/delay.h>
0010 #include <linux/i2c.h>
0011 #include <linux/usb.h>
0012 #include <linux/slab.h>
0013 #include <media/v4l2-common.h>
0014 #include <media/tuner.h>
0015 #include <media/i2c/tvaudio.h>
0016 #include <media/rc-map.h>
0017 
0018 #include "tm6000.h"
0019 #include "tm6000-regs.h"
0020 #include "xc2028.h"
0021 #include "xc5000.h"
0022 
0023 #define TM6000_BOARD_UNKNOWN            0
0024 #define TM5600_BOARD_GENERIC            1
0025 #define TM6000_BOARD_GENERIC            2
0026 #define TM6010_BOARD_GENERIC            3
0027 #define TM5600_BOARD_10MOONS_UT821      4
0028 #define TM5600_BOARD_10MOONS_UT330      5
0029 #define TM6000_BOARD_ADSTECH_DUAL_TV        6
0030 #define TM6000_BOARD_FREECOM_AND_SIMILAR    7
0031 #define TM6000_BOARD_ADSTECH_MINI_DUAL_TV   8
0032 #define TM6010_BOARD_HAUPPAUGE_900H     9
0033 #define TM6010_BOARD_BEHOLD_WANDER      10
0034 #define TM6010_BOARD_BEHOLD_VOYAGER     11
0035 #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
0036 #define TM6010_BOARD_TWINHAN_TU501      13
0037 #define TM6010_BOARD_BEHOLD_WANDER_LITE     14
0038 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE    15
0039 #define TM5600_BOARD_TERRATEC_GRABSTER      16
0040 
0041 #define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
0042                (model == TM5600_BOARD_GENERIC) || \
0043                (model == TM6000_BOARD_GENERIC) || \
0044                (model == TM6010_BOARD_GENERIC))
0045 
0046 #define TM6000_MAXBOARDS        16
0047 static unsigned int card[]     = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
0048 
0049 module_param_array(card,  int, NULL, 0444);
0050 
0051 static unsigned long tm6000_devused;
0052 
0053 
0054 struct tm6000_board {
0055     char            *name;
0056     char        eename[16];     /* EEPROM name */
0057     unsigned    eename_size;        /* size of EEPROM name */
0058     unsigned    eename_pos;     /* Position where it appears at ROM */
0059 
0060     struct tm6000_capabilities caps;
0061 
0062     enum        tm6000_devtype type;    /* variant of the chipset */
0063     int             tuner_type;     /* type of the tuner */
0064     int             tuner_addr;     /* tuner address */
0065     int             demod_addr;     /* demodulator address */
0066 
0067     struct tm6000_gpio gpio;
0068 
0069     struct tm6000_input vinput[3];
0070     struct tm6000_input rinput;
0071 
0072     char        *ir_codes;
0073 };
0074 
0075 static struct tm6000_board tm6000_boards[] = {
0076     [TM6000_BOARD_UNKNOWN] = {
0077         .name         = "Unknown tm6000 video grabber",
0078         .caps = {
0079             .has_tuner  = 1,
0080             .has_eeprom = 1,
0081         },
0082         .gpio = {
0083             .tuner_reset    = TM6000_GPIO_1,
0084         },
0085         .vinput = { {
0086             .type   = TM6000_INPUT_TV,
0087             .vmux   = TM6000_VMUX_VIDEO_B,
0088             .amux   = TM6000_AMUX_ADC1,
0089             }, {
0090             .type   = TM6000_INPUT_COMPOSITE1,
0091             .vmux   = TM6000_VMUX_VIDEO_A,
0092             .amux   = TM6000_AMUX_ADC2,
0093             }, {
0094             .type   = TM6000_INPUT_SVIDEO,
0095             .vmux   = TM6000_VMUX_VIDEO_AB,
0096             .amux   = TM6000_AMUX_ADC2,
0097             },
0098         },
0099     },
0100     [TM5600_BOARD_GENERIC] = {
0101         .name         = "Generic tm5600 board",
0102         .type         = TM5600,
0103         .tuner_type   = TUNER_XC2028,
0104         .tuner_addr   = 0xc2 >> 1,
0105         .caps = {
0106             .has_tuner  = 1,
0107             .has_eeprom = 1,
0108         },
0109         .gpio = {
0110             .tuner_reset    = TM6000_GPIO_1,
0111         },
0112         .vinput = { {
0113             .type   = TM6000_INPUT_TV,
0114             .vmux   = TM6000_VMUX_VIDEO_B,
0115             .amux   = TM6000_AMUX_ADC1,
0116             }, {
0117             .type   = TM6000_INPUT_COMPOSITE1,
0118             .vmux   = TM6000_VMUX_VIDEO_A,
0119             .amux   = TM6000_AMUX_ADC2,
0120             }, {
0121             .type   = TM6000_INPUT_SVIDEO,
0122             .vmux   = TM6000_VMUX_VIDEO_AB,
0123             .amux   = TM6000_AMUX_ADC2,
0124             },
0125         },
0126     },
0127     [TM6000_BOARD_GENERIC] = {
0128         .name         = "Generic tm6000 board",
0129         .tuner_type   = TUNER_XC2028,
0130         .tuner_addr   = 0xc2 >> 1,
0131         .caps = {
0132             .has_tuner  = 1,
0133             .has_eeprom = 1,
0134         },
0135         .gpio = {
0136             .tuner_reset    = TM6000_GPIO_1,
0137         },
0138         .vinput = { {
0139             .type   = TM6000_INPUT_TV,
0140             .vmux   = TM6000_VMUX_VIDEO_B,
0141             .amux   = TM6000_AMUX_ADC1,
0142             }, {
0143             .type   = TM6000_INPUT_COMPOSITE1,
0144             .vmux   = TM6000_VMUX_VIDEO_A,
0145             .amux   = TM6000_AMUX_ADC2,
0146             }, {
0147             .type   = TM6000_INPUT_SVIDEO,
0148             .vmux   = TM6000_VMUX_VIDEO_AB,
0149             .amux   = TM6000_AMUX_ADC2,
0150             },
0151         },
0152     },
0153     [TM6010_BOARD_GENERIC] = {
0154         .name         = "Generic tm6010 board",
0155         .type         = TM6010,
0156         .tuner_type   = TUNER_XC2028,
0157         .tuner_addr   = 0xc2 >> 1,
0158         .demod_addr   = 0x1e >> 1,
0159         .caps = {
0160             .has_tuner  = 1,
0161             .has_dvb    = 1,
0162             .has_zl10353    = 1,
0163             .has_eeprom = 1,
0164             .has_remote = 1,
0165         },
0166         .gpio = {
0167             .tuner_reset    = TM6010_GPIO_2,
0168             .tuner_on   = TM6010_GPIO_3,
0169             .demod_reset    = TM6010_GPIO_1,
0170             .demod_on   = TM6010_GPIO_4,
0171             .power_led  = TM6010_GPIO_7,
0172             .dvb_led    = TM6010_GPIO_5,
0173             .ir     = TM6010_GPIO_0,
0174         },
0175         .vinput = { {
0176             .type   = TM6000_INPUT_TV,
0177             .vmux   = TM6000_VMUX_VIDEO_B,
0178             .amux   = TM6000_AMUX_SIF1,
0179             }, {
0180             .type   = TM6000_INPUT_COMPOSITE1,
0181             .vmux   = TM6000_VMUX_VIDEO_A,
0182             .amux   = TM6000_AMUX_ADC2,
0183             }, {
0184             .type   = TM6000_INPUT_SVIDEO,
0185             .vmux   = TM6000_VMUX_VIDEO_AB,
0186             .amux   = TM6000_AMUX_ADC2,
0187             },
0188         },
0189     },
0190     [TM5600_BOARD_10MOONS_UT821] = {
0191         .name         = "10Moons UT 821",
0192         .tuner_type   = TUNER_XC2028,
0193         .eename       = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
0194         .eename_size  = 14,
0195         .eename_pos   = 0x14,
0196         .type         = TM5600,
0197         .tuner_addr   = 0xc2 >> 1,
0198         .caps = {
0199             .has_tuner    = 1,
0200             .has_eeprom   = 1,
0201         },
0202         .gpio = {
0203             .tuner_reset    = TM6000_GPIO_1,
0204         },
0205         .vinput = { {
0206             .type   = TM6000_INPUT_TV,
0207             .vmux   = TM6000_VMUX_VIDEO_B,
0208             .amux   = TM6000_AMUX_ADC1,
0209             }, {
0210             .type   = TM6000_INPUT_COMPOSITE1,
0211             .vmux   = TM6000_VMUX_VIDEO_A,
0212             .amux   = TM6000_AMUX_ADC2,
0213             }, {
0214             .type   = TM6000_INPUT_SVIDEO,
0215             .vmux   = TM6000_VMUX_VIDEO_AB,
0216             .amux   = TM6000_AMUX_ADC2,
0217             },
0218         },
0219     },
0220     [TM5600_BOARD_10MOONS_UT330] = {
0221         .name         = "10Moons UT 330",
0222         .tuner_type   = TUNER_PHILIPS_FQ1216AME_MK4,
0223         .tuner_addr   = 0xc8 >> 1,
0224         .caps = {
0225             .has_tuner    = 1,
0226             .has_dvb      = 0,
0227             .has_zl10353  = 0,
0228             .has_eeprom   = 1,
0229         },
0230         .vinput = { {
0231             .type   = TM6000_INPUT_TV,
0232             .vmux   = TM6000_VMUX_VIDEO_B,
0233             .amux   = TM6000_AMUX_ADC1,
0234             }, {
0235             .type   = TM6000_INPUT_COMPOSITE1,
0236             .vmux   = TM6000_VMUX_VIDEO_A,
0237             .amux   = TM6000_AMUX_ADC2,
0238             }, {
0239             .type   = TM6000_INPUT_SVIDEO,
0240             .vmux   = TM6000_VMUX_VIDEO_AB,
0241             .amux   = TM6000_AMUX_ADC2,
0242             },
0243         },
0244     },
0245     [TM6000_BOARD_ADSTECH_DUAL_TV] = {
0246         .name         = "ADSTECH Dual TV USB",
0247         .tuner_type   = TUNER_XC2028,
0248         .tuner_addr   = 0xc8 >> 1,
0249         .caps = {
0250             .has_tuner    = 1,
0251             .has_tda9874  = 1,
0252             .has_dvb      = 1,
0253             .has_zl10353  = 1,
0254             .has_eeprom   = 1,
0255         },
0256         .vinput = { {
0257             .type   = TM6000_INPUT_TV,
0258             .vmux   = TM6000_VMUX_VIDEO_B,
0259             .amux   = TM6000_AMUX_ADC1,
0260             }, {
0261             .type   = TM6000_INPUT_COMPOSITE1,
0262             .vmux   = TM6000_VMUX_VIDEO_A,
0263             .amux   = TM6000_AMUX_ADC2,
0264             }, {
0265             .type   = TM6000_INPUT_SVIDEO,
0266             .vmux   = TM6000_VMUX_VIDEO_AB,
0267             .amux   = TM6000_AMUX_ADC2,
0268             },
0269         },
0270     },
0271     [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
0272         .name         = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
0273         .tuner_type   = TUNER_XC2028, /* has a XC3028 */
0274         .tuner_addr   = 0xc2 >> 1,
0275         .demod_addr   = 0x1e >> 1,
0276         .caps = {
0277             .has_tuner    = 1,
0278             .has_dvb      = 1,
0279             .has_zl10353  = 1,
0280             .has_eeprom   = 0,
0281             .has_remote   = 1,
0282         },
0283         .gpio = {
0284             .tuner_reset    = TM6000_GPIO_4,
0285         },
0286         .vinput = { {
0287             .type   = TM6000_INPUT_TV,
0288             .vmux   = TM6000_VMUX_VIDEO_B,
0289             .amux   = TM6000_AMUX_ADC1,
0290             }, {
0291             .type   = TM6000_INPUT_COMPOSITE1,
0292             .vmux   = TM6000_VMUX_VIDEO_A,
0293             .amux   = TM6000_AMUX_ADC2,
0294             }, {
0295             .type   = TM6000_INPUT_SVIDEO,
0296             .vmux   = TM6000_VMUX_VIDEO_AB,
0297             .amux   = TM6000_AMUX_ADC2,
0298             },
0299         },
0300     },
0301     [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
0302         .name         = "ADSTECH Mini Dual TV USB",
0303         .tuner_type   = TUNER_XC2028, /* has a XC3028 */
0304         .tuner_addr   = 0xc8 >> 1,
0305         .demod_addr   = 0x1e >> 1,
0306         .caps = {
0307             .has_tuner    = 1,
0308             .has_dvb      = 1,
0309             .has_zl10353  = 1,
0310             .has_eeprom   = 0,
0311         },
0312         .gpio = {
0313             .tuner_reset    = TM6000_GPIO_4,
0314         },
0315         .vinput = { {
0316             .type   = TM6000_INPUT_TV,
0317             .vmux   = TM6000_VMUX_VIDEO_B,
0318             .amux   = TM6000_AMUX_ADC1,
0319             }, {
0320             .type   = TM6000_INPUT_COMPOSITE1,
0321             .vmux   = TM6000_VMUX_VIDEO_A,
0322             .amux   = TM6000_AMUX_ADC2,
0323             }, {
0324             .type   = TM6000_INPUT_SVIDEO,
0325             .vmux   = TM6000_VMUX_VIDEO_AB,
0326             .amux   = TM6000_AMUX_ADC2,
0327             },
0328         },
0329     },
0330     [TM6010_BOARD_HAUPPAUGE_900H] = {
0331         .name         = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
0332         .eename       = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
0333         .eename_size  = 14,
0334         .eename_pos   = 0x42,
0335         .tuner_type   = TUNER_XC2028, /* has a XC3028 */
0336         .tuner_addr   = 0xc2 >> 1,
0337         .demod_addr   = 0x1e >> 1,
0338         .type         = TM6010,
0339         .ir_codes = RC_MAP_HAUPPAUGE,
0340         .caps = {
0341             .has_tuner    = 1,
0342             .has_dvb      = 1,
0343             .has_zl10353  = 1,
0344             .has_eeprom   = 1,
0345             .has_remote   = 1,
0346         },
0347         .gpio = {
0348             .tuner_reset    = TM6010_GPIO_2,
0349             .tuner_on   = TM6010_GPIO_3,
0350             .demod_reset    = TM6010_GPIO_1,
0351             .demod_on   = TM6010_GPIO_4,
0352             .power_led  = TM6010_GPIO_7,
0353             .dvb_led    = TM6010_GPIO_5,
0354             .ir     = TM6010_GPIO_0,
0355         },
0356         .vinput = { {
0357             .type   = TM6000_INPUT_TV,
0358             .vmux   = TM6000_VMUX_VIDEO_B,
0359             .amux   = TM6000_AMUX_SIF1,
0360             }, {
0361             .type   = TM6000_INPUT_COMPOSITE1,
0362             .vmux   = TM6000_VMUX_VIDEO_A,
0363             .amux   = TM6000_AMUX_ADC2,
0364             }, {
0365             .type   = TM6000_INPUT_SVIDEO,
0366             .vmux   = TM6000_VMUX_VIDEO_AB,
0367             .amux   = TM6000_AMUX_ADC2,
0368             },
0369         },
0370     },
0371     [TM6010_BOARD_BEHOLD_WANDER] = {
0372         .name         = "Beholder Wander DVB-T/TV/FM USB2.0",
0373         .tuner_type   = TUNER_XC5000,
0374         .tuner_addr   = 0xc2 >> 1,
0375         .demod_addr   = 0x1e >> 1,
0376         .type         = TM6010,
0377         .caps = {
0378             .has_tuner      = 1,
0379             .has_dvb        = 1,
0380             .has_zl10353    = 1,
0381             .has_eeprom     = 1,
0382             .has_remote     = 1,
0383             .has_radio  = 1,
0384         },
0385         .gpio = {
0386             .tuner_reset    = TM6010_GPIO_0,
0387             .demod_reset    = TM6010_GPIO_1,
0388             .power_led  = TM6010_GPIO_6,
0389         },
0390         .vinput = { {
0391             .type   = TM6000_INPUT_TV,
0392             .vmux   = TM6000_VMUX_VIDEO_B,
0393             .amux   = TM6000_AMUX_SIF1,
0394             }, {
0395             .type   = TM6000_INPUT_COMPOSITE1,
0396             .vmux   = TM6000_VMUX_VIDEO_A,
0397             .amux   = TM6000_AMUX_ADC2,
0398             }, {
0399             .type   = TM6000_INPUT_SVIDEO,
0400             .vmux   = TM6000_VMUX_VIDEO_AB,
0401             .amux   = TM6000_AMUX_ADC2,
0402             },
0403         },
0404         .rinput = {
0405             .type   = TM6000_INPUT_RADIO,
0406             .amux   = TM6000_AMUX_ADC1,
0407         },
0408     },
0409     [TM6010_BOARD_BEHOLD_VOYAGER] = {
0410         .name         = "Beholder Voyager TV/FM USB2.0",
0411         .tuner_type   = TUNER_XC5000,
0412         .tuner_addr   = 0xc2 >> 1,
0413         .type         = TM6010,
0414         .caps = {
0415             .has_tuner      = 1,
0416             .has_dvb        = 0,
0417             .has_zl10353    = 0,
0418             .has_eeprom     = 1,
0419             .has_remote     = 1,
0420             .has_radio  = 1,
0421         },
0422         .gpio = {
0423             .tuner_reset    = TM6010_GPIO_0,
0424             .power_led  = TM6010_GPIO_6,
0425         },
0426         .vinput = { {
0427             .type   = TM6000_INPUT_TV,
0428             .vmux   = TM6000_VMUX_VIDEO_B,
0429             .amux   = TM6000_AMUX_SIF1,
0430             }, {
0431             .type   = TM6000_INPUT_COMPOSITE1,
0432             .vmux   = TM6000_VMUX_VIDEO_A,
0433             .amux   = TM6000_AMUX_ADC2,
0434             }, {
0435             .type   = TM6000_INPUT_SVIDEO,
0436             .vmux   = TM6000_VMUX_VIDEO_AB,
0437             .amux   = TM6000_AMUX_ADC2,
0438             },
0439         },
0440         .rinput = {
0441             .type   = TM6000_INPUT_RADIO,
0442             .amux   = TM6000_AMUX_ADC1,
0443         },
0444     },
0445     [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
0446         .name         = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
0447         .tuner_type   = TUNER_XC2028, /* has a XC3028 */
0448         .tuner_addr   = 0xc2 >> 1,
0449         .demod_addr   = 0x1e >> 1,
0450         .type         = TM6010,
0451         .caps = {
0452             .has_tuner    = 1,
0453             .has_dvb      = 1,
0454             .has_zl10353  = 1,
0455             .has_eeprom   = 1,
0456             .has_remote   = 1,
0457             .has_radio    = 1,
0458         },
0459         .gpio = {
0460             .tuner_reset    = TM6010_GPIO_2,
0461             .tuner_on   = TM6010_GPIO_3,
0462             .demod_reset    = TM6010_GPIO_1,
0463             .demod_on   = TM6010_GPIO_4,
0464             .power_led  = TM6010_GPIO_7,
0465             .dvb_led    = TM6010_GPIO_5,
0466             .ir     = TM6010_GPIO_0,
0467         },
0468         .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
0469         .vinput = { {
0470             .type   = TM6000_INPUT_TV,
0471             .vmux   = TM6000_VMUX_VIDEO_B,
0472             .amux   = TM6000_AMUX_SIF1,
0473             }, {
0474             .type   = TM6000_INPUT_COMPOSITE1,
0475             .vmux   = TM6000_VMUX_VIDEO_A,
0476             .amux   = TM6000_AMUX_ADC2,
0477             }, {
0478             .type   = TM6000_INPUT_SVIDEO,
0479             .vmux   = TM6000_VMUX_VIDEO_AB,
0480             .amux   = TM6000_AMUX_ADC2,
0481             },
0482         },
0483         .rinput = {
0484             .type = TM6000_INPUT_RADIO,
0485             .amux = TM6000_AMUX_SIF1,
0486         },
0487     },
0488     [TM5600_BOARD_TERRATEC_GRABSTER] = {
0489         .name         = "Terratec Grabster AV 150/250 MX",
0490         .type         = TM5600,
0491         .tuner_type   = TUNER_ABSENT,
0492         .vinput = { {
0493             .type   = TM6000_INPUT_TV,
0494             .vmux   = TM6000_VMUX_VIDEO_B,
0495             .amux   = TM6000_AMUX_ADC1,
0496             }, {
0497             .type   = TM6000_INPUT_COMPOSITE1,
0498             .vmux   = TM6000_VMUX_VIDEO_A,
0499             .amux   = TM6000_AMUX_ADC2,
0500             }, {
0501             .type   = TM6000_INPUT_SVIDEO,
0502             .vmux   = TM6000_VMUX_VIDEO_AB,
0503             .amux   = TM6000_AMUX_ADC2,
0504             },
0505         },
0506     },
0507     [TM6010_BOARD_TWINHAN_TU501] = {
0508         .name         = "Twinhan TU501(704D1)",
0509         .tuner_type   = TUNER_XC2028, /* has a XC3028 */
0510         .tuner_addr   = 0xc2 >> 1,
0511         .demod_addr   = 0x1e >> 1,
0512         .type         = TM6010,
0513         .caps = {
0514             .has_tuner    = 1,
0515             .has_dvb      = 1,
0516             .has_zl10353  = 1,
0517             .has_eeprom   = 1,
0518             .has_remote   = 1,
0519         },
0520         .gpio = {
0521             .tuner_reset    = TM6010_GPIO_2,
0522             .tuner_on   = TM6010_GPIO_3,
0523             .demod_reset    = TM6010_GPIO_1,
0524             .demod_on   = TM6010_GPIO_4,
0525             .power_led  = TM6010_GPIO_7,
0526             .dvb_led    = TM6010_GPIO_5,
0527             .ir     = TM6010_GPIO_0,
0528         },
0529         .vinput = { {
0530             .type   = TM6000_INPUT_TV,
0531             .vmux   = TM6000_VMUX_VIDEO_B,
0532             .amux   = TM6000_AMUX_SIF1,
0533             }, {
0534             .type   = TM6000_INPUT_COMPOSITE1,
0535             .vmux   = TM6000_VMUX_VIDEO_A,
0536             .amux   = TM6000_AMUX_ADC2,
0537             }, {
0538             .type   = TM6000_INPUT_SVIDEO,
0539             .vmux   = TM6000_VMUX_VIDEO_AB,
0540             .amux   = TM6000_AMUX_ADC2,
0541             },
0542         },
0543     },
0544     [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
0545         .name         = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
0546         .tuner_type   = TUNER_XC5000,
0547         .tuner_addr   = 0xc2 >> 1,
0548         .demod_addr   = 0x1e >> 1,
0549         .type         = TM6010,
0550         .caps = {
0551             .has_tuner      = 1,
0552             .has_dvb        = 1,
0553             .has_zl10353    = 1,
0554             .has_eeprom     = 1,
0555             .has_remote     = 0,
0556             .has_radio  = 1,
0557         },
0558         .gpio = {
0559             .tuner_reset    = TM6010_GPIO_0,
0560             .demod_reset    = TM6010_GPIO_1,
0561             .power_led  = TM6010_GPIO_6,
0562         },
0563         .vinput = { {
0564             .type   = TM6000_INPUT_TV,
0565             .vmux   = TM6000_VMUX_VIDEO_B,
0566             .amux   = TM6000_AMUX_SIF1,
0567             },
0568         },
0569         .rinput = {
0570             .type   = TM6000_INPUT_RADIO,
0571             .amux   = TM6000_AMUX_ADC1,
0572         },
0573     },
0574     [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
0575         .name         = "Beholder Voyager Lite TV/FM USB2.0",
0576         .tuner_type   = TUNER_XC5000,
0577         .tuner_addr   = 0xc2 >> 1,
0578         .type         = TM6010,
0579         .caps = {
0580             .has_tuner      = 1,
0581             .has_dvb        = 0,
0582             .has_zl10353    = 0,
0583             .has_eeprom     = 1,
0584             .has_remote     = 0,
0585             .has_radio  = 1,
0586         },
0587         .gpio = {
0588             .tuner_reset    = TM6010_GPIO_0,
0589             .power_led  = TM6010_GPIO_6,
0590         },
0591         .vinput = { {
0592             .type   = TM6000_INPUT_TV,
0593             .vmux   = TM6000_VMUX_VIDEO_B,
0594             .amux   = TM6000_AMUX_SIF1,
0595             },
0596         },
0597         .rinput = {
0598             .type   = TM6000_INPUT_RADIO,
0599             .amux   = TM6000_AMUX_ADC1,
0600         },
0601     },
0602 };
0603 
0604 /* table of devices that work with this driver */
0605 static const struct usb_device_id tm6000_id_table[] = {
0606     { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
0607     { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
0608     { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
0609     { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
0610     { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
0611     { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
0612     { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
0613     { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
0614     { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
0615     { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
0616     { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
0617     { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
0618     { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
0619     { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
0620     { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
0621     { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
0622     { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
0623     { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
0624     { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
0625     { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
0626     { }
0627 };
0628 MODULE_DEVICE_TABLE(usb, tm6000_id_table);
0629 
0630 /* Control power led for show some activity */
0631 void tm6000_flash_led(struct tm6000_core *dev, u8 state)
0632 {
0633     /* Power LED unconfigured */
0634     if (!dev->gpio.power_led)
0635         return;
0636 
0637     /* ON Power LED */
0638     if (state) {
0639         switch (dev->model) {
0640         case TM6010_BOARD_HAUPPAUGE_900H:
0641         case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0642         case TM6010_BOARD_TWINHAN_TU501:
0643             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0644                 dev->gpio.power_led, 0x00);
0645             break;
0646         case TM6010_BOARD_BEHOLD_WANDER:
0647         case TM6010_BOARD_BEHOLD_VOYAGER:
0648         case TM6010_BOARD_BEHOLD_WANDER_LITE:
0649         case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
0650             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0651                 dev->gpio.power_led, 0x01);
0652             break;
0653         }
0654     }
0655     /* OFF Power LED */
0656     else {
0657         switch (dev->model) {
0658         case TM6010_BOARD_HAUPPAUGE_900H:
0659         case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0660         case TM6010_BOARD_TWINHAN_TU501:
0661             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0662                 dev->gpio.power_led, 0x01);
0663             break;
0664         case TM6010_BOARD_BEHOLD_WANDER:
0665         case TM6010_BOARD_BEHOLD_VOYAGER:
0666         case TM6010_BOARD_BEHOLD_WANDER_LITE:
0667         case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
0668             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0669                 dev->gpio.power_led, 0x00);
0670             break;
0671         }
0672     }
0673 }
0674 
0675 /* Tuner callback to provide the proper gpio changes needed for xc5000 */
0676 int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
0677 {
0678     int rc = 0;
0679     struct tm6000_core *dev = ptr;
0680 
0681     if (dev->tuner_type != TUNER_XC5000)
0682         return 0;
0683 
0684     switch (command) {
0685     case XC5000_TUNER_RESET:
0686         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0687                    dev->gpio.tuner_reset, 0x01);
0688         msleep(15);
0689         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0690                    dev->gpio.tuner_reset, 0x00);
0691         msleep(15);
0692         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0693                    dev->gpio.tuner_reset, 0x01);
0694         break;
0695     }
0696     return rc;
0697 }
0698 EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
0699 
0700 /* Tuner callback to provide the proper gpio changes needed for xc2028 */
0701 
0702 int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
0703 {
0704     int rc = 0;
0705     struct tm6000_core *dev = ptr;
0706 
0707     if (dev->tuner_type != TUNER_XC2028)
0708         return 0;
0709 
0710     switch (command) {
0711     case XC2028_RESET_CLK:
0712         tm6000_ir_wait(dev, 0);
0713 
0714         tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
0715                     0x02, arg);
0716         msleep(10);
0717         rc = tm6000_i2c_reset(dev, 10);
0718         break;
0719     case XC2028_TUNER_RESET:
0720         /* Reset codes during load firmware */
0721         switch (arg) {
0722         case 0:
0723             /* newer tuner can faster reset */
0724             switch (dev->model) {
0725             case TM5600_BOARD_10MOONS_UT821:
0726                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0727                            dev->gpio.tuner_reset, 0x01);
0728                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0729                            0x300, 0x01);
0730                 msleep(10);
0731                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0732                            dev->gpio.tuner_reset, 0x00);
0733                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0734                            0x300, 0x00);
0735                 msleep(10);
0736                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0737                            dev->gpio.tuner_reset, 0x01);
0738                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0739                            0x300, 0x01);
0740                 break;
0741             case TM6010_BOARD_HAUPPAUGE_900H:
0742             case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0743             case TM6010_BOARD_TWINHAN_TU501:
0744                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0745                            dev->gpio.tuner_reset, 0x01);
0746                 msleep(60);
0747                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0748                            dev->gpio.tuner_reset, 0x00);
0749                 msleep(75);
0750                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0751                            dev->gpio.tuner_reset, 0x01);
0752                 msleep(60);
0753                 break;
0754             default:
0755                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0756                            dev->gpio.tuner_reset, 0x00);
0757                 msleep(130);
0758                 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0759                            dev->gpio.tuner_reset, 0x01);
0760                 msleep(130);
0761                 break;
0762             }
0763 
0764             tm6000_ir_wait(dev, 1);
0765             break;
0766         case 1:
0767             tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
0768                         0x02, 0x01);
0769             msleep(10);
0770             break;
0771         case 2:
0772             rc = tm6000_i2c_reset(dev, 100);
0773             break;
0774         }
0775         break;
0776     case XC2028_I2C_FLUSH:
0777         tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
0778         tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
0779         break;
0780     }
0781     return rc;
0782 }
0783 EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
0784 
0785 int tm6000_cards_setup(struct tm6000_core *dev)
0786 {
0787     /*
0788      * Board-specific initialization sequence. Handles all GPIO
0789      * initialization sequences that are board-specific.
0790      * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
0791      * Probably, they're all based on some reference device. Due to that,
0792      * there's a common routine at the end to handle those GPIO's. Devices
0793      * that use different pinups or init sequences can just return at
0794      * the board-specific session.
0795      */
0796     switch (dev->model) {
0797     case TM6010_BOARD_HAUPPAUGE_900H:
0798     case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0799     case TM6010_BOARD_TWINHAN_TU501:
0800     case TM6010_BOARD_GENERIC:
0801         /* Turn xceive 3028 on */
0802         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
0803         msleep(15);
0804         /* Turn zarlink zl10353 on */
0805         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
0806         msleep(15);
0807         /* Reset zarlink zl10353 */
0808         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
0809         msleep(50);
0810         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
0811         msleep(15);
0812         /* Turn zarlink zl10353 off */
0813         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
0814         msleep(15);
0815         /* ir ? */
0816         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
0817         msleep(15);
0818         /* Power led on (blue) */
0819         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
0820         msleep(15);
0821         /* DVB led off (orange) */
0822         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
0823         msleep(15);
0824         /* Turn zarlink zl10353 on */
0825         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
0826         msleep(15);
0827         break;
0828     case TM6010_BOARD_BEHOLD_WANDER:
0829     case TM6010_BOARD_BEHOLD_WANDER_LITE:
0830         /* Power led on (blue) */
0831         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
0832         msleep(15);
0833         /* Reset zarlink zl10353 */
0834         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
0835         msleep(50);
0836         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
0837         msleep(15);
0838         break;
0839     case TM6010_BOARD_BEHOLD_VOYAGER:
0840     case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
0841         /* Power led on (blue) */
0842         tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
0843         msleep(15);
0844         break;
0845     default:
0846         break;
0847     }
0848 
0849     /*
0850      * Default initialization. Most of the devices seem to use GPIO1
0851      * and GPIO4.on the same way, so, this handles the common sequence
0852      * used by most devices.
0853      * If a device uses a different sequence or different GPIO pins for
0854      * reset, just add the code at the board-specific part
0855      */
0856 
0857     if (dev->gpio.tuner_reset) {
0858         int rc;
0859         int i;
0860 
0861         for (i = 0; i < 2; i++) {
0862             rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0863                         dev->gpio.tuner_reset, 0x00);
0864             if (rc < 0) {
0865                 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
0866                 return rc;
0867             }
0868 
0869             msleep(10); /* Just to be conservative */
0870             rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
0871                         dev->gpio.tuner_reset, 0x01);
0872             if (rc < 0) {
0873                 printk(KERN_ERR "Error %i doing tuner reset\n", rc);
0874                 return rc;
0875             }
0876         }
0877     } else {
0878         printk(KERN_ERR "Tuner reset is not configured\n");
0879         return -1;
0880     }
0881 
0882     msleep(50);
0883 
0884     return 0;
0885 };
0886 
0887 static void tm6000_config_tuner(struct tm6000_core *dev)
0888 {
0889     struct tuner_setup tun_setup;
0890 
0891     /* Load tuner module */
0892     v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
0893         "tuner", dev->tuner_addr, NULL);
0894 
0895     memset(&tun_setup, 0, sizeof(tun_setup));
0896     tun_setup.type = dev->tuner_type;
0897     tun_setup.addr = dev->tuner_addr;
0898 
0899     tun_setup.mode_mask = 0;
0900     if (dev->caps.has_tuner)
0901         tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
0902 
0903     switch (dev->tuner_type) {
0904     case TUNER_XC2028:
0905         tun_setup.tuner_callback = tm6000_tuner_callback;
0906         break;
0907     case TUNER_XC5000:
0908         tun_setup.tuner_callback = tm6000_xc5000_callback;
0909         break;
0910     }
0911 
0912     v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
0913 
0914     switch (dev->tuner_type) {
0915     case TUNER_XC2028: {
0916         struct v4l2_priv_tun_config xc2028_cfg;
0917         struct xc2028_ctrl ctl;
0918 
0919         memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
0920         memset(&ctl, 0, sizeof(ctl));
0921 
0922         ctl.demod = XC3028_FE_ZARLINK456;
0923 
0924         xc2028_cfg.tuner = TUNER_XC2028;
0925         xc2028_cfg.priv  = &ctl;
0926 
0927         switch (dev->model) {
0928         case TM6010_BOARD_HAUPPAUGE_900H:
0929         case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0930         case TM6010_BOARD_TWINHAN_TU501:
0931             ctl.max_len = 80;
0932             ctl.fname = "xc3028L-v36.fw";
0933             break;
0934         default:
0935             if (dev->dev_type == TM6010)
0936                 ctl.fname = "xc3028-v27.fw";
0937             else
0938                 ctl.fname = "xc3028-v24.fw";
0939         }
0940 
0941         printk(KERN_INFO "Setting firmware parameters for xc2028\n");
0942         v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
0943                      &xc2028_cfg);
0944 
0945         }
0946         break;
0947     case TUNER_XC5000:
0948         {
0949         struct v4l2_priv_tun_config  xc5000_cfg;
0950         struct xc5000_config ctl = {
0951             .i2c_address = dev->tuner_addr,
0952             .if_khz      = 4570,
0953             .radio_input = XC5000_RADIO_FM1_MONO,
0954             };
0955 
0956         xc5000_cfg.tuner = TUNER_XC5000;
0957         xc5000_cfg.priv  = &ctl;
0958 
0959         v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
0960                      &xc5000_cfg);
0961         }
0962         break;
0963     default:
0964         printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
0965         break;
0966     }
0967 }
0968 
0969 static int fill_board_specific_data(struct tm6000_core *dev)
0970 {
0971     int rc;
0972 
0973     dev->dev_type   = tm6000_boards[dev->model].type;
0974     dev->tuner_type = tm6000_boards[dev->model].tuner_type;
0975     dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
0976 
0977     dev->gpio = tm6000_boards[dev->model].gpio;
0978 
0979     dev->ir_codes = tm6000_boards[dev->model].ir_codes;
0980 
0981     dev->demod_addr = tm6000_boards[dev->model].demod_addr;
0982 
0983     dev->caps = tm6000_boards[dev->model].caps;
0984 
0985     dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
0986     dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
0987     dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
0988     dev->rinput = tm6000_boards[dev->model].rinput;
0989 
0990     /* setup per-model quirks */
0991     switch (dev->model) {
0992     case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
0993     case TM6010_BOARD_HAUPPAUGE_900H:
0994         dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
0995         break;
0996 
0997     default:
0998         break;
0999     }
1000 
1001     /* initialize hardware */
1002     rc = tm6000_init(dev);
1003     if (rc < 0)
1004         return rc;
1005 
1006     return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1007 }
1008 
1009 
1010 static void use_alternative_detection_method(struct tm6000_core *dev)
1011 {
1012     int i, model = -1;
1013 
1014     if (!dev->eedata_size)
1015         return;
1016 
1017     for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1018         if (!tm6000_boards[i].eename_size)
1019             continue;
1020         if (dev->eedata_size < tm6000_boards[i].eename_pos +
1021                        tm6000_boards[i].eename_size)
1022             continue;
1023 
1024         if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1025                 tm6000_boards[i].eename,
1026                 tm6000_boards[i].eename_size)) {
1027             model = i;
1028             break;
1029         }
1030     }
1031     if (model < 0) {
1032         printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1033         return;
1034     }
1035 
1036     dev->model = model;
1037 
1038     printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1039            tm6000_boards[model].name, model);
1040 }
1041 
1042 #if defined(CONFIG_MODULES) && defined(MODULE)
1043 static void request_module_async(struct work_struct *work)
1044 {
1045     struct tm6000_core *dev = container_of(work, struct tm6000_core,
1046                            request_module_wk);
1047 
1048     request_module("tm6000-alsa");
1049 
1050     if (dev->caps.has_dvb)
1051         request_module("tm6000-dvb");
1052 }
1053 
1054 static void request_modules(struct tm6000_core *dev)
1055 {
1056     INIT_WORK(&dev->request_module_wk, request_module_async);
1057     schedule_work(&dev->request_module_wk);
1058 }
1059 
1060 static void flush_request_modules(struct tm6000_core *dev)
1061 {
1062     flush_work(&dev->request_module_wk);
1063 }
1064 #else
1065 #define request_modules(dev)
1066 #define flush_request_modules(dev)
1067 #endif /* CONFIG_MODULES */
1068 
1069 static int tm6000_init_dev(struct tm6000_core *dev)
1070 {
1071     struct v4l2_frequency f;
1072     int rc = 0;
1073 
1074     mutex_init(&dev->lock);
1075     mutex_lock(&dev->lock);
1076 
1077     if (!is_generic(dev->model)) {
1078         rc = fill_board_specific_data(dev);
1079         if (rc < 0)
1080             goto err;
1081 
1082         /* register i2c bus */
1083         rc = tm6000_i2c_register(dev);
1084         if (rc < 0)
1085             goto err;
1086     } else {
1087         /* register i2c bus */
1088         rc = tm6000_i2c_register(dev);
1089         if (rc < 0)
1090             goto err;
1091 
1092         use_alternative_detection_method(dev);
1093 
1094         rc = fill_board_specific_data(dev);
1095         if (rc < 0)
1096             goto err;
1097     }
1098 
1099     /* Default values for STD and resolutions */
1100     dev->width = 720;
1101     dev->height = 480;
1102     dev->norm = V4L2_STD_NTSC_M;
1103 
1104     /* Configure tuner */
1105     tm6000_config_tuner(dev);
1106 
1107     /* Set video standard */
1108     v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
1109 
1110     /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
1111     f.tuner = 0;
1112     f.type = V4L2_TUNER_ANALOG_TV;
1113     f.frequency = 3092; /* 193.25 MHz */
1114     dev->freq = f.frequency;
1115     v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1116 
1117     if (dev->caps.has_tda9874)
1118         v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1119             "tvaudio", I2C_ADDR_TDA9874, NULL);
1120 
1121     /* register and initialize V4L2 */
1122     rc = tm6000_v4l2_register(dev);
1123     if (rc < 0)
1124         goto err;
1125 
1126     tm6000_add_into_devlist(dev);
1127     tm6000_init_extension(dev);
1128 
1129     tm6000_ir_init(dev);
1130 
1131     request_modules(dev);
1132 
1133     mutex_unlock(&dev->lock);
1134     return 0;
1135 
1136 err:
1137     mutex_unlock(&dev->lock);
1138     return rc;
1139 }
1140 
1141 /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
1142 #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1143 
1144 static void get_max_endpoint(struct usb_device *udev,
1145                  struct usb_host_interface *alt,
1146                  char *msgtype,
1147                  struct usb_host_endpoint *curr_e,
1148                  struct tm6000_endpoint *tm_ep)
1149 {
1150     u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1151     unsigned int size = tmp & 0x7ff;
1152 
1153     if (udev->speed == USB_SPEED_HIGH)
1154         size = size * hb_mult(tmp);
1155 
1156     if (size > tm_ep->maxsize) {
1157         tm_ep->endp = curr_e;
1158         tm_ep->maxsize = size;
1159         tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
1160         tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
1161 
1162         printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1163                     msgtype, curr_e->desc.bEndpointAddress,
1164                     size);
1165     }
1166 }
1167 
1168 /*
1169  * tm6000_usb_probe()
1170  * checks for supported devices
1171  */
1172 static int tm6000_usb_probe(struct usb_interface *interface,
1173                 const struct usb_device_id *id)
1174 {
1175     struct usb_device *usbdev;
1176     struct tm6000_core *dev;
1177     int i, rc;
1178     int nr = 0;
1179     char *speed;
1180 
1181     usbdev = usb_get_dev(interface_to_usbdev(interface));
1182 
1183     /* Selects the proper interface */
1184     rc = usb_set_interface(usbdev, 0, 1);
1185     if (rc < 0)
1186         goto report_failure;
1187 
1188     /* Check to see next free device and mark as used */
1189     nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
1190     if (nr >= TM6000_MAXBOARDS) {
1191         printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
1192         rc = -ENOMEM;
1193         goto put_device;
1194     }
1195 
1196     /* Create and initialize dev struct */
1197     dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1198     if (!dev) {
1199         rc = -ENOMEM;
1200         goto put_device;
1201     }
1202     spin_lock_init(&dev->slock);
1203     mutex_init(&dev->usb_lock);
1204 
1205     /* Increment usage count */
1206     set_bit(nr, &tm6000_devused);
1207     snprintf(dev->name, 29, "tm6000 #%d", nr);
1208 
1209     dev->model = id->driver_info;
1210     if (card[nr] < ARRAY_SIZE(tm6000_boards))
1211         dev->model = card[nr];
1212 
1213     dev->udev = usbdev;
1214     dev->devno = nr;
1215 
1216     switch (usbdev->speed) {
1217     case USB_SPEED_LOW:
1218         speed = "1.5";
1219         break;
1220     case USB_SPEED_UNKNOWN:
1221     case USB_SPEED_FULL:
1222         speed = "12";
1223         break;
1224     case USB_SPEED_HIGH:
1225         speed = "480";
1226         break;
1227     default:
1228         speed = "unknown";
1229     }
1230 
1231     /* Get endpoints */
1232     for (i = 0; i < interface->num_altsetting; i++) {
1233         int ep;
1234 
1235         for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1236             struct usb_host_endpoint    *e;
1237             int dir_out;
1238 
1239             e = &interface->altsetting[i].endpoint[ep];
1240 
1241             dir_out = ((e->desc.bEndpointAddress &
1242                     USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1243 
1244             printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1245                    i,
1246                    interface->altsetting[i].desc.bInterfaceNumber,
1247                    interface->altsetting[i].desc.bInterfaceClass);
1248 
1249             switch (e->desc.bmAttributes) {
1250             case USB_ENDPOINT_XFER_BULK:
1251                 if (!dir_out) {
1252                     get_max_endpoint(usbdev,
1253                              &interface->altsetting[i],
1254                              "Bulk IN", e,
1255                              &dev->bulk_in);
1256                 } else {
1257                     get_max_endpoint(usbdev,
1258                              &interface->altsetting[i],
1259                              "Bulk OUT", e,
1260                              &dev->bulk_out);
1261                 }
1262                 break;
1263             case USB_ENDPOINT_XFER_ISOC:
1264                 if (!dir_out) {
1265                     get_max_endpoint(usbdev,
1266                              &interface->altsetting[i],
1267                              "ISOC IN", e,
1268                              &dev->isoc_in);
1269                 } else {
1270                     get_max_endpoint(usbdev,
1271                              &interface->altsetting[i],
1272                              "ISOC OUT", e,
1273                              &dev->isoc_out);
1274                 }
1275                 break;
1276             case USB_ENDPOINT_XFER_INT:
1277                 if (!dir_out) {
1278                     get_max_endpoint(usbdev,
1279                             &interface->altsetting[i],
1280                             "INT IN", e,
1281                             &dev->int_in);
1282                 } else {
1283                     get_max_endpoint(usbdev,
1284                             &interface->altsetting[i],
1285                             "INT OUT", e,
1286                             &dev->int_out);
1287                 }
1288                 break;
1289             }
1290         }
1291     }
1292 
1293 
1294     printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1295         speed,
1296         le16_to_cpu(dev->udev->descriptor.idVendor),
1297         le16_to_cpu(dev->udev->descriptor.idProduct),
1298         interface->altsetting->desc.bInterfaceNumber);
1299 
1300 /* check if the the device has the iso in endpoint at the correct place */
1301     if (!dev->isoc_in.endp) {
1302         printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
1303         rc = -ENODEV;
1304         goto free_device;
1305     }
1306 
1307     /* save our data pointer in this interface device */
1308     usb_set_intfdata(interface, dev);
1309 
1310     printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1311 
1312     rc = tm6000_init_dev(dev);
1313     if (rc < 0)
1314         goto free_device;
1315 
1316     return 0;
1317 
1318 free_device:
1319     kfree(dev);
1320 report_failure:
1321     printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1322 
1323     clear_bit(nr, &tm6000_devused);
1324 put_device:
1325     usb_put_dev(usbdev);
1326     return rc;
1327 }
1328 
1329 /*
1330  * tm6000_usb_disconnect()
1331  * called when the device gets disconnected
1332  * video device will be unregistered on v4l2_close in case it is still open
1333  */
1334 static void tm6000_usb_disconnect(struct usb_interface *interface)
1335 {
1336     struct tm6000_core *dev = usb_get_intfdata(interface);
1337     usb_set_intfdata(interface, NULL);
1338 
1339     if (!dev)
1340         return;
1341 
1342     printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1343 
1344     flush_request_modules(dev);
1345 
1346     tm6000_ir_fini(dev);
1347 
1348     if (dev->gpio.power_led) {
1349         switch (dev->model) {
1350         case TM6010_BOARD_HAUPPAUGE_900H:
1351         case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1352         case TM6010_BOARD_TWINHAN_TU501:
1353             /* Power led off */
1354             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1355                 dev->gpio.power_led, 0x01);
1356             msleep(15);
1357             break;
1358         case TM6010_BOARD_BEHOLD_WANDER:
1359         case TM6010_BOARD_BEHOLD_VOYAGER:
1360         case TM6010_BOARD_BEHOLD_WANDER_LITE:
1361         case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
1362             /* Power led off */
1363             tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1364                 dev->gpio.power_led, 0x00);
1365             msleep(15);
1366             break;
1367         }
1368     }
1369     tm6000_v4l2_unregister(dev);
1370 
1371     tm6000_i2c_unregister(dev);
1372 
1373     v4l2_device_unregister(&dev->v4l2_dev);
1374 
1375     dev->state |= DEV_DISCONNECTED;
1376 
1377     usb_put_dev(dev->udev);
1378 
1379     tm6000_close_extension(dev);
1380     tm6000_remove_from_devlist(dev);
1381 
1382     clear_bit(dev->devno, &tm6000_devused);
1383     kfree(dev);
1384 }
1385 
1386 static struct usb_driver tm6000_usb_driver = {
1387         .name = "tm6000",
1388         .probe = tm6000_usb_probe,
1389         .disconnect = tm6000_usb_disconnect,
1390         .id_table = tm6000_id_table,
1391 };
1392 
1393 module_usb_driver(tm6000_usb_driver);
1394 
1395 MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1396 MODULE_AUTHOR("Mauro Carvalho Chehab");
1397 MODULE_LICENSE("GPL v2");