media: usb: em28xx: add Hauppauge WinTV-DualHD DVB and ATSC devices
Henning Garbers [Sat, 2 Jul 2016 20:50:34 +0000 (22:50 +0200)]
Bug: 30095176
Bug 1736911

Change-Id: Ic9c7e6641417986d16de6dae9900bee074cfeb42
Signed-off-by: Sungtak Lee <taklee@google.com>
Signed-off-by: Jean Huang <jeanh@nvidia.com>
Signed-off-by: Patrick Horng <phorng@nvidia.com>
Reviewed-on: http://git-master/r/1284470
(cherry picked from commit 767de1720a89840c3962d1a476e1f654dfa720dd)
Reviewed-on: http://git-master/r/1311297
GVS: Gerrit_Virtual_Submit
Reviewed-by: Vinayak Pane <vpane@nvidia.com>
Reviewed-on: http://git-master/r/1456511
Reviewed-by: Manish Tuteja <mtuteja@nvidia.com>

drivers/media/usb/em28xx/Kconfig
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/em28xx/em28xx-core.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-reg.h
drivers/media/usb/em28xx/em28xx.h

index ca5ee6a..45ff31e 100644 (file)
@@ -35,6 +35,7 @@ config VIDEO_EM28XX_DVB
        depends on VIDEO_EM28XX && DVB_CORE
        select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
        select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT
        select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT
@@ -47,8 +48,10 @@ config VIDEO_EM28XX_DVB
        select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
        ---help---
          This adds support for DVB cards based on the
          Empiatech em28xx chips.
index 83bfbe4..fb3be4f 100644 (file)
@@ -411,6 +411,29 @@ static struct em28xx_reg_seq pctv_520e[] = {
        {             -1,   -1,   -1,  -1},
 };
 
+/* 2040:0265 Hauppauge WinTV-dualHD DVB
+ * reg 0x80/0x84:
+ * GPIO_0: Yellow LED tuner 1, 0=on, 1=off
+ * GPIO_1: Green LED tuner 1, 0=on, 1=off
+ * GPIO_2: Yellow LED tuner 2, 0=on, 1=off
+ * GPIO_3: Green LED tuner 2, 0=on, 1=off
+ * GPIO_5: Reset #2, 0=active
+ * GPIO_6: Reset #1, 0=active
+ */
+static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {
+       {EM2874_R80_GPIO,         0xff, 0xff,      0},
+       {0x0d,                    0xff, 0xff,    200},
+       {0x50,                    0x04, 0xff,    300},
+       {EM2874_R80_GPIO,         0xbf, 0xff,    100}, /* demod 1 reset */
+       {EM2874_R80_GPIO,         0xff, 0xff,    100},
+       {EM2874_R80_GPIO,         0xdf, 0xff,    100}, /* demod 2 reset */
+       {EM2874_R80_GPIO,         0xff, 0xff,    100},
+       {EM2874_R5F_TS_ENABLE,    0x44, 0xff,     50},
+       {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff,     50},
+       {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff,     50},
+       {                     -1,   -1,   -1,     -1},
+};
+
 /*
  *  Board definitions
  */
@@ -2017,6 +2040,30 @@ struct em28xx_board em28xx_boards[] = {
                .i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
+       /* 2040:0265 Hauppauge WinTV-dualHD (DVB version).
+        * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157 */
+       [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = {
+               .name          = "Hauppauge WinTV-dualHD DVB",
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = hauppauge_dualhd_dvb,
+               .has_dvb       = 1,
+               .has_dual_ts   = 1,
+               .ir_codes      = RC_MAP_HAUPPAUGE,
+       },
+       /* 2040:026D Hauppauge WinTV-dualHD (DVB version).
+        * Empia EM28274, 2x LGDT3306A, 2x Silicon Labs Si2157 */
+       [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_ATSC] = {
+               .name          = "Hauppauge WinTV-dualHD ATSC",
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = hauppauge_dualhd_dvb,
+               .has_dvb       = 1,
+               .has_dual_ts   = 1,
+               .ir_codes      = RC_MAP_HAUPPAUGE,
+       },
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -2178,6 +2225,10 @@ struct usb_device_id em28xx_id_table[] = {
                        .driver_info = EM2884_BOARD_PCTV_510E },
        { USB_DEVICE(0x2013, 0x0251),
                        .driver_info = EM2884_BOARD_PCTV_520E },
+       { USB_DEVICE(0x2040, 0x0265),
+                       .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+       { USB_DEVICE(0x2040, 0x026D),
+                       .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_ATSC },
        { },
 };
 MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2638,6 +2689,8 @@ static void em28xx_card_setup(struct em28xx *dev)
        case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
        case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
        case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
+       case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
+       case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_ATSC:
        {
                struct tveeprom tv;
 
@@ -2847,7 +2900,8 @@ void em28xx_release_resources(struct em28xx *dev)
 
        v4l2_device_unregister(&dev->v4l2_dev);
 
-       usb_put_dev(dev->udev);
+       if (dev->ts == PRIMARY_TS)
+               usb_put_dev(dev->udev);
 
        /* Mark device as unused */
        clear_bit(dev->devno, &em28xx_devused);
@@ -3115,6 +3169,21 @@ unregister_dev:
 /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
 #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
 
+int em28xx_duplicate_dev(struct em28xx *dev)
+{
+       struct em28xx *sec_dev = NULL;
+       int nr;
+       sec_dev = kzalloc(sizeof(struct em28xx), GFP_KERNEL);
+       memcpy(sec_dev, dev, sizeof(struct em28xx));
+       nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+       test_and_set_bit(nr, &em28xx_devused);
+       sec_dev->devno = nr;
+       snprintf(sec_dev->name, 28, "em28xx #%d", nr);
+       dev->dev_next = sec_dev;
+       sec_dev->dev_next = NULL;
+       return 0;
+}
+
 /*
  * em28xx_usb_probe()
  * checks for supported devices
@@ -3125,7 +3194,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
        struct usb_device *udev;
        struct em28xx *dev = NULL;
        int retval;
-       bool has_audio = false, has_video = false, has_dvb = false;
+       bool has_audio = false, has_video = false, has_dvb = false, has_dvb_ts2 = false;
        int i, nr, try_bulk;
        const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
        char *speed;
@@ -3232,6 +3301,19 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                                                }
                                        }
                                        break;
+                               case 0x85:
+                                       if (usb_endpoint_xfer_isoc(e)) {
+                                               if (size > dev->dvb_max_pkt_size_isoc_ts2) {
+                                                       has_dvb_ts2 = true; /* see NOTE (~) */
+                                                       dev->dvb_ep_isoc_ts2 = e->bEndpointAddress;
+                                                       dev->dvb_max_pkt_size_isoc_ts2 = size;
+                                                       dev->dvb_alt_isoc = i;
+                                               }
+                                       } else {
+                                               has_dvb_ts2 = true;
+                                               dev->dvb_ep_bulk_ts2 = e->bEndpointAddress;
+                                       }
+                                       break;
                                }
                        }
                        /* NOTE:
@@ -3313,6 +3395,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
        dev->is_audio_only = has_audio && !(has_video || has_dvb);
        dev->has_alsa_audio = has_audio;
        dev->audio_ifnum = ifnum;
+       dev->ts = PRIMARY_TS;
+       dev->dev_next = NULL;
 
        /* Checks if audio is provided by some interface */
        for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
@@ -3382,26 +3466,49 @@ static int em28xx_usb_probe(struct usb_interface *interface,
 
                em28xx_info("dvb set to %s mode.\n",
                            dev->dvb_xfer_bulk ? "bulk" : "isoc");
+       }
 
-               /* pre-allocate DVB usb transfer buffers */
-               if (dev->dvb_xfer_bulk) {
-                       retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
-                                           dev->dvb_xfer_bulk,
-                                           EM28XX_DVB_NUM_BUFS,
-                                           512,
-                                           EM28XX_DVB_BULK_PACKET_MULTIPLIER);
+       if (dev->board.has_dual_ts) {
+               em28xx_duplicate_dev(dev);
+               dev->dev_next->ts = SECONDARY_TS;
+               dev->dev_next->alt = -1;
+               dev->dev_next->is_audio_only = has_audio && !(has_video || has_dvb);
+               dev->dev_next->has_video = false;
+               dev->dev_next->ifnum = ifnum;
+               dev->dev_next->model = id->driver_info;
+
+               mutex_init(&dev->dev_next->lock);
+               mutex_lock(&dev->dev_next->lock);
+               retval = em28xx_init_dev(dev->dev_next, udev, interface, dev->dev_next->devno);
+               if (retval)
+                       goto err_free;
+
+               if (usb_xfer_mode < 0) {
+                       if (dev->dev_next->board.is_webcam)
+                               try_bulk = 1;
+                       else
+                               try_bulk = 0;
                } else {
-                       retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
-                                           dev->dvb_xfer_bulk,
-                                           EM28XX_DVB_NUM_BUFS,
-                                           dev->dvb_max_pkt_size_isoc,
-                                           EM28XX_DVB_NUM_ISOC_PACKETS);
+                       try_bulk = usb_xfer_mode > 0;
                }
-               if (retval) {
-                       printk(DRIVER_NAME
-                              ": Failed to pre-allocate USB transfer buffers for DVB.\n");
-                       goto unlock_and_free;
+
+               /* Select USB transfer types to use */
+               if (has_dvb) {
+                       if (!dev->dvb_ep_isoc_ts2 || (try_bulk && dev->dvb_ep_bulk_ts2))
+                               dev->dev_next->dvb_xfer_bulk = 1;
+                               em28xx_info("dvb ts2 set to %s mode.\n",
+                                           dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc");
                }
+
+               dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;
+               dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;
+               dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;
+
+               /* Configure hardware to support TS2*/
+               em28xx_write_reg(dev, 0x0b, 0x96);
+               mdelay(100);
+               em28xx_write_reg(dev, 0x0b, 0x82);
+               mdelay(100);
        }
 
        request_modules(dev);
@@ -3410,6 +3517,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
           open the device before fully initializing it
         */
        mutex_unlock(&dev->lock);
+       if (dev->dev_next != NULL)
+               mutex_unlock(&dev->dev_next->lock);
 
        return 0;
 
@@ -3422,6 +3531,8 @@ err_free:
 
 err:
        clear_bit(nr, &em28xx_devused);
+       if (dev->dev_next != NULL)
+               clear_bit(dev->dev_next->devno, &em28xx_devused);
 
 err_no_slot:
        usb_put_dev(udev);
@@ -3443,6 +3554,14 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
        if (!dev)
                return;
 
+       if (dev->dev_next != NULL) {
+               dev->dev_next->disconnected = 1;
+
+               em28xx_info("Disconnecting %s\n", dev->dev_next->name);
+
+               flush_request_modules(dev->dev_next);
+       }
+
        dev->disconnected = 1;
 
        if (dev->is_audio_only) {
@@ -3471,11 +3590,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
        em28xx_close_extension(dev);
        /* NOTE: must be called BEFORE the resources are released */
 
+       if ((!dev->users) && (dev->dev_next != NULL))
+               em28xx_release_resources(dev->dev_next);
+
        if (!dev->users)
                em28xx_release_resources(dev);
 
        mutex_unlock(&dev->lock);
 
+       if ((!dev->users) && (dev->dev_next != NULL)) {
+               kfree(dev->dev_next->alt_max_pkt_size_isoc);
+               kfree(dev->dev_next);
+               dev->dev_next = NULL;
+       }
+
        if (!dev->users) {
                kfree(dev->alt_max_pkt_size_isoc);
                kfree(dev);
index a802128..e9efc92 100644 (file)
@@ -249,6 +249,24 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
 EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
 
 /*
+ * em28xx_toggle_reg_bits()
+ * toggles/inverts the bits (specified by bitmask) of a register
+ */
+static int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask)
+{
+       int oldval;
+       u8 newval;
+
+       oldval = em28xx_read_reg(dev, reg);
+       if (oldval < 0)
+               return oldval;
+
+       newval = (~oldval & bitmask) | (oldval & ~bitmask);
+
+       return em28xx_write_reg(dev, reg, newval);
+}
+
+/*
  * em28xx_is_ac97_ready()
  * Checks if ac97 is ready
  */
@@ -631,18 +649,32 @@ int em28xx_capture_start(struct em28xx *dev, int start)
            dev->chip_id == CHIP_ID_EM2884 ||
            dev->chip_id == CHIP_ID_EM28174) {
                /* The Transport Stream Enable Register moved in em2874 */
-               if (!start) {
+               if (dev->board.has_dual_ts) {
+                       if (start) {
+                               rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+                                                          (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE),
+                                                          (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE));
+                       } else {
+                               if (dev->ts == PRIMARY_TS)
+                                       rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS1_CAPTURE_ENABLE);
+                               else
+                                       rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS2_CAPTURE_ENABLE);
+                       }
+                       return rc;
+               } else {
+                       if (!start) {
+                               rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+                                                          0x00,
+                                                          EM2874_TS1_CAPTURE_ENABLE);
+                               return rc;
+                       }
+
+                       /* Enable Transport Stream */
                        rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
-                                                  0x00,
+                                                  EM2874_TS1_CAPTURE_ENABLE,
                                                   EM2874_TS1_CAPTURE_ENABLE);
                        return rc;
                }
-
-               /* Enable Transport Stream */
-               rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
-                                          EM2874_TS1_CAPTURE_ENABLE,
-                                          EM2874_TS1_CAPTURE_ENABLE);
-               return rc;
        }
 
 
@@ -1293,6 +1325,8 @@ int em28xx_register_extension(struct em28xx_ops *ops)
        list_add_tail(&ops->next, &em28xx_extension_devlist);
        list_for_each_entry(dev, &em28xx_devlist, devlist) {
                ops->init(dev);
+               if (dev->dev_next != NULL)
+                       ops->init(dev->dev_next);
        }
        mutex_unlock(&em28xx_devlist_mutex);
        printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
@@ -1307,6 +1341,8 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
        mutex_lock(&em28xx_devlist_mutex);
        list_for_each_entry(dev, &em28xx_devlist, devlist) {
                ops->fini(dev);
+               if (dev->dev_next != NULL)
+                       ops->fini(dev->dev_next);
        }
        list_del(&ops->next);
        mutex_unlock(&em28xx_devlist_mutex);
@@ -1323,6 +1359,8 @@ void em28xx_init_extension(struct em28xx *dev)
        list_for_each_entry(ops, &em28xx_extension_devlist, next) {
                if (ops->init)
                        ops->init(dev);
+               if (dev->dev_next != NULL)
+                       ops->init(dev->dev_next);
        }
        mutex_unlock(&em28xx_devlist_mutex);
 }
@@ -1335,6 +1373,8 @@ void em28xx_close_extension(struct em28xx *dev)
        list_for_each_entry(ops, &em28xx_extension_devlist, next) {
                if (ops->fini)
                        ops->fini(dev);
+               if (dev->dev_next != NULL)
+                       ops->fini(dev->dev_next);
        }
        list_del(&dev->devlist);
        mutex_unlock(&em28xx_devlist_mutex);
index c4d6696..78ee86b 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "lgdt330x.h"
 #include "lgdt3305.h"
+#include "lgdt3306a.h"
 #include "zl10353.h"
 #include "s5h1409.h"
 #include "mt352.h"
@@ -51,6 +52,8 @@
 #include "a8293.h"
 #include "qt1010.h"
 #include "mb86a20s.h"
+#include "si2168.h"
+#include "si2157.h"
 
 MODULE_DESCRIPTION("driver for em28xx based DVB cards");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -87,6 +90,9 @@ struct em28xx_dvb {
        struct semaphore      pll_mutex;
        bool                    dont_attach_fe1;
        int                     lna_gpio;
+       struct i2c_client       *i2c_client_demod;
+       struct i2c_client       *i2c_client_tuner;
+       struct i2c_client       *i2c_client_sec;
 };
 
 
@@ -198,7 +204,8 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
                dvb_alt = dev->dvb_alt_isoc;
        }
 
-       usb_set_interface(dev->udev, 0, dvb_alt);
+       /* moved to em28xx_dvb_init*/
+       /* usb_set_interface(dev->udev, 0, dvb_alt);*/
        rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
        if (rc < 0)
                return rc;
@@ -957,7 +964,7 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
 
 static int em28xx_dvb_init(struct em28xx *dev)
 {
-       int result = 0, mfe_shared = 0;
+       int result = 0, mfe_shared = 0, dvb_alt = 0;
        struct em28xx_dvb *dvb;
 
        if (!dev->board.has_dvb) {
@@ -975,6 +982,26 @@ static int em28xx_dvb_init(struct em28xx *dev)
        dev->dvb = dvb;
        dvb->fe[0] = dvb->fe[1] = NULL;
 
+       /* pre-allocate DVB usb transfer buffers */
+       if (dev->dvb_xfer_bulk) {
+               result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+                                   dev->dvb_xfer_bulk,
+                                   EM28XX_DVB_NUM_BUFS,
+                                   512,
+                                   EM28XX_DVB_BULK_PACKET_MULTIPLIER);
+       } else {
+               result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+                                   dev->dvb_xfer_bulk,
+                                   EM28XX_DVB_NUM_BUFS,
+                                   dev->dvb_max_pkt_size_isoc,
+                                   EM28XX_DVB_NUM_ISOC_PACKETS);
+       }
+       if (result) {
+               printk(KERN_INFO "em28xx_dvb: Failed to pre-allocate USB transfer buffers for DVB.\n");
+               kfree(dvb);
+               dev->dvb = NULL;
+               return result;
+       }
        mutex_lock(&dev->lock);
        em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
        /* init frontend */
@@ -1297,6 +1324,153 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        goto out_free;
                }
                break;
+       case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
+               {
+                       struct i2c_adapter *adapter;
+                       struct i2c_client *client;
+                       struct i2c_board_info info;
+                       struct si2168_config si2168_config;
+                       struct si2157_config si2157_config;
+
+                       /* attach demod */
+                       memset(&si2168_config, 0, sizeof(si2168_config));
+                       si2168_config.i2c_adapter = &adapter;
+                       si2168_config.fe = &dvb->fe[0];
+                       si2168_config.ts_mode = SI2168_TS_SERIAL;
+                       memset(&info, 0, sizeof(info));
+                       strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+                       if (dev->ts == PRIMARY_TS)
+                               info.addr = 0x64;
+                       else
+                               info.addr = 0x67;
+                       info.platform_data = &si2168_config;
+                       request_module(info.type);
+                       client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->i2c_client_demod = client;
+                       em28xx_info("demod si2168 successfully loaded\n");
+
+                       /* attach tuner */
+                       memset(&si2157_config, 0, sizeof(si2157_config));
+                       si2157_config.fe = dvb->fe[0];
+                       si2157_config.if_port = 1;
+                       memset(&info, 0, sizeof(info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       if (dev->ts == PRIMARY_TS)
+                               info.addr = 0x60;
+                       else
+                               info.addr = 0x63;
+                       info.platform_data = &si2157_config;
+                       request_module(info.type);
+                       client = i2c_new_device(adapter, &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->i2c_client_tuner = client;
+               }
+               break;
+       case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_ATSC:
+               {
+                       struct i2c_adapter *adapter;
+                       struct i2c_client *client;
+                       struct i2c_board_info info;
+                       struct lgdt3306a_config lgdt3306a_config;
+                       struct si2157_config si2157_config;
+
+                       /* attach demod */
+                       memset(&lgdt3306a_config, 0, sizeof(lgdt3306a_config));
+                       lgdt3306a_config.i2c_adapter = &adapter;
+                       lgdt3306a_config.fe = &dvb->fe[0];
+                       if (dev->ts == PRIMARY_TS)
+                               lgdt3306a_config.i2c_addr = 0x59;
+                       else
+                               lgdt3306a_config.i2c_addr = 0x0e;
+                       lgdt3306a_config.qam_if_khz         = 4000;
+                       lgdt3306a_config.vsb_if_khz         = 3250;
+                       lgdt3306a_config.deny_i2c_rptr      = 1;
+                       lgdt3306a_config.spectral_inversion = 1;
+                       lgdt3306a_config.mpeg_mode          = LGDT3306A_MPEG_SERIAL;
+                       lgdt3306a_config.tpclk_edge         = LGDT3306A_TPCLK_RISING_EDGE;
+                       lgdt3306a_config.tpvalid_polarity   = LGDT3306A_TP_VALID_HIGH;
+                       lgdt3306a_config.xtalMHz            = 25;
+                       lgdt3306a_config.has_tuner_i2c_adapter = 1;
+                       memset(&info, 0, sizeof(info));
+                       strlcpy(info.type, "lgdt3306a", I2C_NAME_SIZE);
+                       if (dev->ts == PRIMARY_TS)
+                               info.addr = 0x59;
+                       else
+                               info.addr = 0x0e;
+                       info.platform_data = &lgdt3306a_config;
+                       request_module(info.type);
+                       client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
+                       dvb->i2c_client_demod = client;
+
+                       /* attach tuner */
+                       memset(&si2157_config, 0, sizeof(si2157_config));
+                       si2157_config.fe = dvb->fe[0];
+                       si2157_config.if_port = 1;
+                       si2157_config.inversion = true;
+                       memset(&info, 0, sizeof(info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       if (dev->ts == PRIMARY_TS)
+                               info.addr = 0x60;
+                       else
+                               info.addr = 0x62;
+                       info.platform_data = &si2157_config;
+                       request_module(info.type);
+                       client = i2c_new_device(adapter, &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->i2c_client_tuner = client;
+               }
+               break;
        default:
                em28xx_errdev("/2: The frontend of your DVB/ATSC card"
                                " isn't supported yet\n");
@@ -1321,6 +1495,12 @@ static int em28xx_dvb_init(struct em28xx *dev)
        /* MFE lock */
        dvb->adapter.mfe_shared = mfe_shared;
 
+       if (dev->dvb_xfer_bulk) {
+               dvb_alt = 0;
+       } else { /* isoc */
+               dvb_alt = dev->dvb_alt_isoc;
+       }
+       usb_set_interface(dev->udev, dev->ifnum, dvb_alt);
        em28xx_info("Successfully loaded em28xx-dvb\n");
 ret:
        em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -1342,6 +1522,8 @@ static inline void prevent_sleep(struct dvb_frontend_ops *ops)
 
 static int em28xx_dvb_fini(struct em28xx *dev)
 {
+       struct i2c_client *client;
+
        if (!dev->board.has_dvb) {
                /* This device does not support the extension */
                return 0;
@@ -1359,6 +1541,27 @@ static int em28xx_dvb_fini(struct em28xx *dev)
                                prevent_sleep(&dvb->fe[1]->ops);
                }
 
+               /* remove I2C SEC */
+               client = dvb->i2c_client_sec;
+               if (client) {
+                       module_put(client->dev.driver->owner);
+                       i2c_unregister_device(client);
+               }
+
+               /* remove I2C tuner */
+               client = dvb->i2c_client_tuner;
+               if (client) {
+                       module_put(client->dev.driver->owner);
+                       i2c_unregister_device(client);
+               }
+
+               /* remove I2C demod */
+               client = dvb->i2c_client_demod;
+               if (client) {
+                       module_put(client->dev.driver->owner);
+                       i2c_unregister_device(client);
+               }
+
                em28xx_unregister_dvb(dvb);
                kfree(dvb);
                dev->dvb = NULL;
index 622871d..18e715d 100644 (file)
 /* em2874 registers */
 #define EM2874_R50_IR_CONFIG    0x50
 #define EM2874_R51_IR           0x51
+#define EM2874_R5D_TS1_PKT_SIZE 0x5d
+#define EM2874_R5E_TS2_PKT_SIZE 0x5e
+       /*
+        * For both TS1 and TS2, In isochronous mode:
+        *  0x01  188 bytes
+        *  0x02  376 bytes
+        *  0x03  564 bytes
+        *  0x04  752 bytes
+        *  0x05  940 bytes
+        * In bulk mode:
+        *  0x01..0xff  total packet count in 188-byte
+        */
 #define EM2874_R5F_TS_ENABLE    0x5f
 #define EM2874_R80_GPIO         0x80
 
index a9323b6..e488113 100644 (file)
 #define EM2884_BOARD_PCTV_520E                   86
 #define EM2884_BOARD_TERRATEC_HTC_USB_XS         87
 #define EM2884_BOARD_C3TECH_DIGITAL_DUO                  88
+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB  89
+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_ATSC 90
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
 /* time in msecs to wait for i2c writes to finish */
 #define EM2800_I2C_XFER_TIMEOUT                20
 
+#define PRIMARY_TS             0
+
+#define SECONDARY_TS   1
+
 enum em28xx_mode {
        EM28XX_SUSPEND,
        EM28XX_ANALOG_MODE,
@@ -393,6 +399,7 @@ struct em28xx_board {
        unsigned int mts_firmware:1;
        unsigned int max_range_640_480:1;
        unsigned int has_dvb:1;
+       unsigned int has_dual_ts:1;
        unsigned int has_snapshot_button:1;
        unsigned int is_webcam:1;
        unsigned int valid:1;
@@ -506,6 +513,7 @@ struct em28xx {
 
        unsigned int has_audio_class:1;
        unsigned int has_alsa_audio:1;
+       unsigned int has_video:1;
        unsigned int is_audio_only:1;
 
        /* Controls audio streaming */
@@ -605,10 +613,13 @@ struct em28xx {
 
        /* usb transfer */
        struct usb_device *udev;        /* the usb device */
+       u8 ifnum;                       /* number of the assigned usb interface */
        u8 analog_ep_isoc;      /* address of isoc endpoint for analog */
        u8 analog_ep_bulk;      /* address of bulk endpoint for analog */
        u8 dvb_ep_isoc;         /* address of isoc endpoint for DVB */
        u8 dvb_ep_bulk;         /* address of bulk endpoint for DVB */
+       u8 dvb_ep_isoc_ts2;     /* address of isoc endpoint for DVB TS2*/
+       u8 dvb_ep_bulk_ts2;     /* address of bulk endpoint for DVB TS2*/
        int alt;                /* alternate setting */
        int max_pkt_size;       /* max packet size of the selected ep at alt */
        int packet_multiplier;  /* multiplier for wMaxPacketSize, used for
@@ -620,6 +631,8 @@ struct em28xx {
        int dvb_alt_isoc;       /* alternate setting for DVB isoc transfers */
        unsigned int dvb_max_pkt_size_isoc;     /* isoc max packet size of the
                                                   selected DVB ep at dvb_alt */
+       unsigned int dvb_max_pkt_size_isoc_ts2; /* isoc max packet size of the
+                                                  selected DVB ep at dvb_alt */
        unsigned int dvb_xfer_bulk:1;           /* use bulk instead of isoc
                                                   transfers for DVB          */
        char urb_buf[URB_MAX_CTRL_SIZE];        /* urb control msg buffer */
@@ -648,6 +661,8 @@ struct em28xx {
        struct delayed_work sbutton_query_work;
 
        struct em28xx_dvb *dvb;
+       struct em28xx *dev_next;
+       int ts;
 };
 
 struct em28xx_ops {