]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/media/usb/em28xx/em28xx-cards.c
[media] em28xx: add a missing le16_to_cpu conversion
[karo-tx-linux.git] / drivers / media / usb / em28xx / em28xx-cards.c
index 54a03b20de6e9a8b680413d4a136fcf1804da7a2..d2ed67825993b9a958a36f14411cbe8f2e1a7713 100644 (file)
@@ -34,7 +34,6 @@
 #include <media/saa7115.h>
 #include <media/tvp5150.h>
 #include <media/tvaudio.h>
-#include <media/mt9v011.h>
 #include <media/i2c-addr.h>
 #include <media/tveeprom.h>
 #include <media/v4l2-common.h>
@@ -345,6 +344,18 @@ static struct em28xx_reg_seq pctv_460e[] = {
        {             -1,   -1,   -1,  -1},
 };
 
+static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
+       {EM2874_R80_GPIO,       0xff,   0xff,   10},
+       {EM2874_R80_GPIO,       0xfd,   0xff,   10}, /* xc5000 reset */
+       {EM2874_R80_GPIO,       0xf9,   0xff,   35},
+       {EM2874_R80_GPIO,       0xfd,   0xff,   10},
+       {EM2874_R80_GPIO,       0xff,   0xff,   10},
+       {EM2874_R80_GPIO,       0xfe,   0xff,   10},
+       {EM2874_R80_GPIO,       0xbe,   0xff,   10},
+       {EM2874_R80_GPIO,       0xfe,   0xff,   20},
+       { -1,                   -1,     -1,     -1},
+};
+
 #if 0
 static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
        {EM2874_R80_GPIO,       0x6f,   0xff,   10},
@@ -958,8 +969,8 @@ struct em28xx_board em28xx_boards[] = {
 #else
                .tuner_type   = TUNER_ABSENT,
 #endif
-               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus  = 1,
+               .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
        [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
@@ -974,17 +985,27 @@ struct em28xx_board em28xx_boards[] = {
                .tuner_type   = TUNER_ABSENT,
 #endif
                .ir_codes     = RC_MAP_HAUPPAUGE,
-               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus  = 1,
+               .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
+       [EM2884_BOARD_C3TECH_DIGITAL_DUO] = {
+               .name         = "C3 Tech Digital Duo HDTV/SDTV USB",
+               .has_dvb      = 1,
+               /* FIXME: Add analog support - need a saa7136 driver */
+               .tuner_type = TUNER_ABSENT,     /* Digital-only TDA18271HD */
+               .ir_codes     = RC_MAP_EMPTY,
+               .def_i2c_bus  = 1,
+               .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE,
+               .dvb_gpio     = c3tech_digital_duo_digital,
+       },
        [EM2884_BOARD_CINERGY_HTC_STICK] = {
                .name         = "Terratec Cinergy HTC Stick",
                .has_dvb      = 1,
                .ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
                .tuner_type   = TUNER_ABSENT,
-               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus  = 1,
+               .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
        [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
@@ -1404,8 +1425,8 @@ struct em28xx_board em28xx_boards[] = {
        },
 
        [EM2874_BOARD_LEADERSHIP_ISDBT] = {
-               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
-                                 EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus    = 1,
+               .i2c_speed      = EM28XX_I2C_CLK_WAIT_ENABLE |
                                  EM28XX_I2C_FREQ_100_KHZ,
                .xclk           = EM28XX_XCLK_FREQUENCY_10MHZ,
                .name           = "EM2874 Leadership ISDBT",
@@ -1917,8 +1938,8 @@ struct em28xx_board em28xx_boards[] = {
         * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
        [EM28174_BOARD_PCTV_290E] = {
                .name          = "PCTV nanoStick T2 290e",
-               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
-                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
                .tuner_type    = TUNER_ABSENT,
                .tuner_gpio    = pctv_290e,
                .has_dvb       = 1,
@@ -1927,8 +1948,8 @@ struct em28xx_board em28xx_boards[] = {
        /* 2013:024f PCTV DVB-S2 Stick 460e
         * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
        [EM28174_BOARD_PCTV_460E] = {
-               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
-                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
                .name          = "PCTV DVB-S2 Stick (460e)",
                .tuner_type    = TUNER_ABSENT,
                .tuner_gpio    = pctv_460e,
@@ -1958,8 +1979,9 @@ struct em28xx_board em28xx_boards[] = {
                .tuner_type    = TUNER_ABSENT,
                .tuner_gpio    = maxmedia_ub425_tc,
                .has_dvb       = 1,
-               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .ir_codes      = RC_MAP_REDDO,
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
        /* 2304:0242 PCTV QuatroStick (510e)
@@ -1970,8 +1992,8 @@ struct em28xx_board em28xx_boards[] = {
                .tuner_gpio    = pctv_510e,
                .has_dvb       = 1,
                .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
-               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
        /* 2013:0251 PCTV QuatroStick nano (520e)
@@ -1982,8 +2004,8 @@ struct em28xx_board em28xx_boards[] = {
                .tuner_gpio    = pctv_520e,
                .has_dvb       = 1,
                .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
-               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
        [EM2884_BOARD_TERRATEC_HTC_USB_XS] = {
@@ -1991,8 +2013,8 @@ struct em28xx_board em28xx_boards[] = {
                .has_dvb      = 1,
                .ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
                .tuner_type   = TUNER_ABSENT,
-               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
-                               EM28XX_I2C_CLK_WAIT_ENABLE |
+               .def_i2c_bus  = 1,
+               .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
 };
@@ -2144,6 +2166,8 @@ struct usb_device_id em28xx_id_table[] = {
                        .driver_info = EM28174_BOARD_PCTV_460E },
        { USB_DEVICE(0x2040, 0x1605),
                        .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
+       { USB_DEVICE(0x1b80, 0xe755),
+                       .driver_info = EM2884_BOARD_C3TECH_DIGITAL_DUO },
        { USB_DEVICE(0xeb1a, 0x5006),
                        .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
        { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
@@ -2183,6 +2207,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = {
        {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
        {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
 };
+/* NOTE: introduce a separate hash table for devices with 16 bit eeproms */
 
 /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
 static unsigned short saa711x_addrs[] = {
@@ -2204,8 +2229,9 @@ static unsigned short msp3400_addrs[] = {
 
 int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
 {
+       struct em28xx_i2c_bus *i2c_bus = ptr;
+       struct em28xx *dev = i2c_bus->dev;
        int rc = 0;
-       struct em28xx *dev = ptr;
 
        if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
                return 0;
@@ -2233,145 +2259,9 @@ static inline void em28xx_set_model(struct em28xx *dev)
        if (!dev->board.i2c_speed)
                dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
                                       EM28XX_I2C_FREQ_100_KHZ;
-}
-
-
-/* FIXME: Should be replaced by a proper mt9m111 driver */
-static int em28xx_initialize_mt9m111(struct em28xx *dev)
-{
-       int i;
-       unsigned char regs[][3] = {
-               { 0x0d, 0x00, 0x01, },  /* reset and use defaults */
-               { 0x0d, 0x00, 0x00, },
-               { 0x0a, 0x00, 0x21, },
-               { 0x21, 0x04, 0x00, },  /* full readout speed, no row/col skipping */
-       };
-
-       for (i = 0; i < ARRAY_SIZE(regs); i++)
-               i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
-
-       return 0;
-}
-
-
-/* FIXME: Should be replaced by a proper mt9m001 driver */
-static int em28xx_initialize_mt9m001(struct em28xx *dev)
-{
-       int i;
-       unsigned char regs[][3] = {
-               { 0x0d, 0x00, 0x01, },
-               { 0x0d, 0x00, 0x00, },
-               { 0x04, 0x05, 0x00, },  /* hres = 1280 */
-               { 0x03, 0x04, 0x00, },  /* vres = 1024 */
-               { 0x20, 0x11, 0x00, },
-               { 0x06, 0x00, 0x10, },
-               { 0x2b, 0x00, 0x24, },
-               { 0x2e, 0x00, 0x24, },
-               { 0x35, 0x00, 0x24, },
-               { 0x2d, 0x00, 0x20, },
-               { 0x2c, 0x00, 0x20, },
-               { 0x09, 0x0a, 0xd4, },
-               { 0x35, 0x00, 0x57, },
-       };
-
-       for (i = 0; i < ARRAY_SIZE(regs); i++)
-               i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
-
-       return 0;
-}
-
-/* HINT method: webcam I2C chips
- *
- * This method works for webcams with Micron sensors
- */
-static int em28xx_hint_sensor(struct em28xx *dev)
-{
-       int rc;
-       char *sensor_name;
-       unsigned char cmd;
-       __be16 version_be;
-       u16 version;
-
-       /* Micron sensor detection */
-       dev->i2c_client.addr = 0xba >> 1;
-       cmd = 0;
-       i2c_master_send(&dev->i2c_client, &cmd, 1);
-       rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
-       if (rc != 2)
-               return -EINVAL;
-
-       version = be16_to_cpu(version_be);
-       switch (version) {
-       case 0x8232:            /* mt9v011 640x480 1.3 Mpix sensor */
-       case 0x8243:            /* mt9v011 rev B 640x480 1.3 Mpix sensor */
-               dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
-               em28xx_set_model(dev);
-
-               sensor_name = "mt9v011";
-               dev->em28xx_sensor = EM28XX_MT9V011;
-               dev->sensor_xres = 640;
-               dev->sensor_yres = 480;
-               /*
-                * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
-                * the Silvercrest cam I have here for testing - for higher
-                * resolutions, a high clock cause horizontal artifacts, so we
-                * need to use a lower xclk frequency.
-                * Yet, it would be possible to adjust xclk depending on the
-                * desired resolution, since this affects directly the
-                * frame rate.
-                */
-               dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
-               dev->sensor_xtal = 4300000;
-
-               /* probably means GRGB 16 bit bayer */
-               dev->vinmode = 0x0d;
-               dev->vinctl = 0x00;
-
-               break;
-
-       case 0x143a:    /* MT9M111 as found in the ECS G200 */
-               dev->model = EM2750_BOARD_UNKNOWN;
-               em28xx_set_model(dev);
-
-               sensor_name = "mt9m111";
-               dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
-               dev->em28xx_sensor = EM28XX_MT9M111;
-               em28xx_initialize_mt9m111(dev);
-               dev->sensor_xres = 640;
-               dev->sensor_yres = 512;
-
-               dev->vinmode = 0x0a;
-               dev->vinctl = 0x00;
-
-               break;
-
-       case 0x8431:
-               dev->model = EM2750_BOARD_UNKNOWN;
-               em28xx_set_model(dev);
-
-               sensor_name = "mt9m001";
-               dev->em28xx_sensor = EM28XX_MT9M001;
-               em28xx_initialize_mt9m001(dev);
-               dev->sensor_xres = 1280;
-               dev->sensor_yres = 1024;
-
-               /* probably means BGGR 16 bit bayer */
-               dev->vinmode = 0x0c;
-               dev->vinctl = 0x00;
-
-               break;
-       default:
-               printk("Unknown Micron Sensor 0x%04x\n", version);
-               return -EINVAL;
-       }
-
-       /* Setup webcam defaults */
-       em28xx_pre_card_setup(dev);
 
-       em28xx_errdev("Sensor is %s, using model %s entry.\n",
-                     sensor_name, em28xx_boards[dev->model].name);
-
-       return 0;
+       /* Should be initialized early, for I2C to work */
+       dev->def_i2c_bus = dev->board.def_i2c_bus;
 }
 
 /* Since em28xx_pre_card_setup() requires a proper dev->model,
@@ -2599,6 +2489,18 @@ static int em28xx_hint_board(struct em28xx *dev)
 {
        int i;
 
+       if (dev->board.is_webcam) {
+               if (dev->em28xx_sensor == EM28XX_MT9V011) {
+                       dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+               } else if (dev->em28xx_sensor == EM28XX_MT9M001 ||
+                          dev->em28xx_sensor == EM28XX_MT9M111) {
+                       dev->model = EM2750_BOARD_UNKNOWN;
+               }
+               /* FIXME: IMPROVE ! */
+
+               return 0;
+       }
+
        /* HINT method: EEPROM
         *
         * This method works only for boards with eeprom.
@@ -2638,7 +2540,7 @@ static int em28xx_hint_board(struct em28xx *dev)
 
        /* user did not request i2c scanning => do it now */
        if (!dev->i2c_hash)
-               em28xx_do_i2c_scan(dev);
+               em28xx_do_i2c_scan(dev, dev->def_i2c_bus);
 
        for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
                if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
@@ -2684,16 +2586,16 @@ static void em28xx_card_setup(struct em28xx *dev)
         * If sensor is not found, then it isn't a webcam.
         */
        if (dev->board.is_webcam) {
-               if (em28xx_hint_sensor(dev) < 0)
+               if (em28xx_detect_sensor(dev) < 0)
                        dev->board.is_webcam = 0;
                else
                        dev->progressive = 1;
        }
 
-       if (!dev->board.is_webcam) {
-               switch (dev->model) {
-               case EM2820_BOARD_UNKNOWN:
-               case EM2800_BOARD_UNKNOWN:
+       switch (dev->model) {
+       case EM2750_BOARD_UNKNOWN:
+       case EM2820_BOARD_UNKNOWN:
+       case EM2800_BOARD_UNKNOWN:
                /*
                 * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
                 *
@@ -2714,9 +2616,8 @@ static void em28xx_card_setup(struct em28xx *dev)
                        em28xx_pre_card_setup(dev);
                }
                break;
-               default:
-                       em28xx_set_model(dev);
-               }
+       default:
+               em28xx_set_model(dev);
        }
 
        em28xx_info("Identified as %s (card=%d)\n",
@@ -2736,15 +2637,19 @@ static void em28xx_card_setup(struct em28xx *dev)
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
        case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
        case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
        {
                struct tveeprom tv;
+
+               if (dev->eedata == NULL)
+                       break;
 #if defined(CONFIG_MODULES) && defined(MODULE)
                request_module("tveeprom");
 #endif
                /* Call first TVeeprom */
 
-               dev->i2c_client.addr = 0xa0 >> 1;
-               tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+               dev->i2c_client[dev->def_i2c_bus].addr = 0xa0 >> 1;
+               tveeprom_hauppauge_analog(&dev->i2c_client[dev->def_i2c_bus], &tv, dev->eedata);
 
                dev->tuner_type = tv.tuner_type;
 
@@ -2791,7 +2696,7 @@ static void em28xx_card_setup(struct em28xx *dev)
                em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
                break;
 
-/*
+               /*
                 * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
                 *
                 * This occurs because they share identical USB vendor and
@@ -2826,51 +2731,41 @@ static void em28xx_card_setup(struct em28xx *dev)
                                "addresses)\n\n");
        }
 
+       /* Free eeprom data memory */
+       kfree(dev->eedata);
+       dev->eedata = NULL;
+
        /* Allow override tuner type by a module parameter */
        if (tuner >= 0)
                dev->tuner_type = tuner;
 
        /* request some modules */
        if (dev->board.has_msp34xx)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                        "msp3400", 0, msp3400_addrs);
 
        if (dev->board.decoder == EM28XX_SAA711X)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                        "saa7115_auto", 0, saa711x_addrs);
 
        if (dev->board.decoder == EM28XX_TVP5150)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                        "tvp5150", 0, tvp5150_addrs);
 
-       if (dev->em28xx_sensor == EM28XX_MT9V011) {
-               struct mt9v011_platform_data pdata;
-               struct i2c_board_info mt9v011_info = {
-                       .type = "mt9v011",
-                       .addr = 0xba >> 1,
-                       .platform_data = &pdata,
-               };
-
-               pdata.xtal = dev->sensor_xtal;
-               v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
-                               &mt9v011_info, NULL);
-       }
-
-
        if (dev->board.adecoder == EM28XX_TVAUDIO)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                        "tvaudio", dev->board.tvaudio_addr, NULL);
 
        if (dev->board.tuner_type != TUNER_ABSENT) {
                int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
 
                if (dev->board.radio.type)
-                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                                "tuner", dev->board.radio_addr, NULL);
 
                if (has_demod)
                        v4l2_i2c_new_subdev(&dev->v4l2_dev,
-                               &dev->i2c_adap, "tuner",
+                               &dev->i2c_adap[dev->def_i2c_bus], "tuner",
                                0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
                if (dev->tuner_addr == 0) {
                        enum v4l2_i2c_tuner_type type =
@@ -2878,18 +2773,20 @@ static void em28xx_card_setup(struct em28xx *dev)
                        struct v4l2_subdev *sd;
 
                        sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
-                               &dev->i2c_adap, "tuner",
+                               &dev->i2c_adap[dev->def_i2c_bus], "tuner",
                                0, v4l2_i2c_tuner_addrs(type));
 
                        if (sd)
                                dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
                } else {
-                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
                                "tuner", dev->tuner_addr, NULL);
                }
        }
 
        em28xx_tuner_setup(dev);
+
+       em28xx_init_camera(dev);
 }
 
 
@@ -2914,7 +2811,8 @@ static void request_module_async(struct work_struct *work)
 
        if (dev->board.has_dvb)
                request_module("em28xx-dvb");
-       if ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir)
+       if (dev->board.has_snapshot_button ||
+           ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir))
                request_module("em28xx-rc");
 #endif /* CONFIG_MODULES */
 }
@@ -2941,7 +2839,9 @@ void em28xx_release_resources(struct em28xx *dev)
 
        em28xx_release_analog_resources(dev);
 
-       em28xx_i2c_unregister(dev);
+       if (dev->def_i2c_bus)
+               em28xx_i2c_unregister(dev, 1);
+       em28xx_i2c_unregister(dev, 0);
 
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
 
@@ -3002,8 +2902,23 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                case CHIP_ID_EM2750:
                        chip_name = "em2750";
                        break;
+               case CHIP_ID_EM2765:
+                       chip_name = "em2765";
+                       dev->wait_after_write = 0;
+                       dev->is_em25xx = 1;
+                       dev->eeprom_addrwidth_16bit = 1;
+                       break;
                case CHIP_ID_EM2820:
                        chip_name = "em2710/2820";
+                       if (le16_to_cpu(dev->udev->descriptor.idVendor)
+                                                                   == 0xeb1a) {
+                               __le16 idProd = dev->udev->descriptor.idProduct;
+                               if (le16_to_cpu(idProd) == 0x2710)
+                                       chip_name = "em2710";
+                               else if (le16_to_cpu(idProd) == 0x2820)
+                                       chip_name = "em2820";
+                       }
+                       /* NOTE: the em2820 is used in webcams, too ! */
                        break;
                case CHIP_ID_EM2840:
                        chip_name = "em2840";
@@ -3019,11 +2934,13 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                        chip_name = "em2874";
                        dev->reg_gpio_num = EM2874_R80_GPIO;
                        dev->wait_after_write = 0;
+                       dev->eeprom_addrwidth_16bit = 1;
                        break;
                case CHIP_ID_EM28174:
                        chip_name = "em28174";
                        dev->reg_gpio_num = EM2874_R80_GPIO;
                        dev->wait_after_write = 0;
+                       dev->eeprom_addrwidth_16bit = 1;
                        break;
                case CHIP_ID_EM2883:
                        chip_name = "em2882/3";
@@ -3033,6 +2950,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                        chip_name = "em2884";
                        dev->reg_gpio_num = EM2874_R80_GPIO;
                        dev->wait_after_write = 0;
+                       dev->eeprom_addrwidth_16bit = 1;
                        break;
                default:
                        printk(KERN_INFO DRIVER_NAME
@@ -3066,14 +2984,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
 
        em28xx_pre_card_setup(dev);
 
-       if (dev->chip_id == CHIP_ID_EM2820) {
-               if (dev->board.is_webcam)
-                       chip_name = "em2710";
-               else
-                       chip_name = "em2820";
-               snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno);
-       }
-
        if (!dev->board.is_em2800) {
                /* Resets I2C speed */
                retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
@@ -3091,17 +3001,37 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                return retval;
        }
 
-       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_handler_init(hdl, 8);
        dev->v4l2_dev.ctrl_handler = hdl;
 
-       /* register i2c bus */
-       retval = em28xx_i2c_register(dev);
+       rt_mutex_init(&dev->i2c_bus_lock);
+
+       /* register i2c bus 0 */
+       if (dev->board.is_em2800)
+               retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM2800);
+       else
+               retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM28XX);
        if (retval < 0) {
-               em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+               em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n",
                        __func__, retval);
                goto unregister_dev;
        }
 
+       /* register i2c bus 1 */
+       if (dev->def_i2c_bus) {
+               if (dev->is_em25xx)
+                       retval = em28xx_i2c_register(dev, 1,
+                                                 EM28XX_I2C_ALGO_EM25XX_BUS_B);
+               else
+                       retval = em28xx_i2c_register(dev, 1,
+                                                       EM28XX_I2C_ALGO_EM28XX);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_i2c_register bus 1 - error [%d]!\n",
+                               __func__, retval);
+                       goto unregister_dev;
+               }
+       }
+
        /*
         * Default format, used for tvp5150 or saa711x output formats
         */
@@ -3160,11 +3090,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                msleep(3);
        }
 
-       v4l2_ctrl_handler_setup(&dev->ctrl_handler);
-       retval = dev->ctrl_handler.error;
-       if (retval)
-               goto fail;
-
        retval = em28xx_register_analog_devices(dev);
        if (retval < 0) {
                goto fail;
@@ -3176,7 +3101,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
        return 0;
 
 fail:
-       em28xx_i2c_unregister(dev);
+       if (dev->def_i2c_bus)
+               em28xx_i2c_unregister(dev, 1);
+       em28xx_i2c_unregister(dev, 0);
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
 
 unregister_dev:
@@ -3292,14 +3219,15 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                                                dev->analog_ep_bulk =
                                                            e->bEndpointAddress;
                                        } else {
-                                               has_dvb = true;
                                                if (usb_endpoint_xfer_isoc(e)) {
                                                        dev->dvb_ep_isoc = e->bEndpointAddress;
                                                        if (size > dev->dvb_max_pkt_size_isoc) {
+                                                               has_dvb = true; /* see NOTE (~) */
                                                                dev->dvb_max_pkt_size_isoc = size;
                                                                dev->dvb_alt_isoc = i;
                                                        }
                                                } else {
+                                                       has_dvb = true;
                                                        dev->dvb_ep_bulk = e->bEndpointAddress;
                                                }
                                        }
@@ -3326,6 +3254,12 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                         * so far. But there might be devices for which this
                         * logic is not sufficient...
                         */
+                       /*
+                        * NOTE (~): some manufacturers (e.g. Terratec) disable
+                        * endpoints by setting wMaxPacketSize to 0 bytes for
+                        * all alt settings. So far, we've seen this for
+                        * DVB isoc endpoints only.
+                        */
                }
        }