V4L/DVB (7615): em28xx: Provide the proper support for switching between analog/digital
authorMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 18 Apr 2008 00:48:00 +0000 (21:48 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Thu, 24 Apr 2008 17:09:42 +0000 (14:09 -0300)
Before this patch,  HVR900/HVR950 were incorreclty going back to analog. The
result is that only digital were working.

This patch provides the proper setup for analog/digital and tuner callback.
It also properly resets analog into a sane state at open().

Thanks to Steven Toth <stoth@linuxtv.org> and Michael Krufky <mkrufky@linuxtv.org>
for helping to set the proper parameters to GPO/GPIO em2883 ports.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-core.c
drivers/media/video/em28xx/em28xx-dvb.c
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index ed50b4e..13ffde2 100644 (file)
@@ -436,19 +436,25 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
 
 /* Board Hauppauge WinTV HVR 900 analog */
 struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
-       {  -1,          -1,     6},
-       {EM28XX_R08_GPIO,       0x2d,  10},
-       {EM28XX_R08_GPIO,       0x3d,   5},
-       {  -1,          -1,    -1},
+       {EM28XX_R08_GPIO,       0x2d,   ~EM_GPIO_4,     10},
+       {0x05,                  0xff,   0x10,           10},
+       {  -1,                  -1,     -1,             -1},
 };
+
 /* Board Hauppauge WinTV HVR 900 digital */
 struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
-       {  -1,          -1,     6},
-       {EM28XX_R08_GPIO,       0x2e,   6},
-       {EM28XX_R08_GPIO,       0x3e,   6},
-       {EM2880_R04_GPO,        0x04,  10},
-       {EM2880_R04_GPO,        0x0c,  10},
-       { -1,    -1,  -1},
+       {EM28XX_R08_GPIO,       0x2e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x04,   0x0f,           10},
+       {EM2880_R04_GPO,        0x0c,   0x0f,           10},
+       { -1,                   -1,     -1,             -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 tuner_callback */
+struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = {
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       0,              EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {  -1,                  -1,             -1,             -1},
 };
 
 /*
@@ -469,7 +475,6 @@ int em28xx_tuner_callback(void *ptr, int command, int arg)
 {
        int rc = 0;
        struct em28xx *dev = ptr;
-       struct em28xx_reg_seq *gpio;
 
        if (dev->tuner_type != TUNER_XC2028)
                return 0;
@@ -478,32 +483,10 @@ int em28xx_tuner_callback(void *ptr, int command, int arg)
                return 0;
 
        if (dev->mode == EM28XX_ANALOG_MODE)
-               gpio = dev->analog_gpio;
+               rc = em28xx_gpio_set(dev, dev->tun_analog_gpio);
        else
-               gpio = dev->digital_gpio;
+               rc = em28xx_gpio_set(dev, dev->tun_digital_gpio);
 
-       /* djh - Not sure if these are still required */
-       dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
-       if (dev->mode == EM28XX_ANALOG_MODE)
-               dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
-       else
-               dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
-       msleep(6);
-
-       if (!gpio)
-               return rc;
-
-       /* Send GPIO reset sequences specified at board entry */
-       while (gpio->sleep >= 0) {
-               if (gpio->reg >= 0)
-                       rc = dev->em28xx_write_regs(dev,
-                                                   gpio->reg,
-                                                   &gpio->val, 1);
-               if (gpio->sleep > 0)
-                       msleep(gpio->sleep);
-
-               gpio++;
-       }
        return rc;
 }
 EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
@@ -527,6 +510,10 @@ void em28xx_pre_card_setup(struct em28xx *dev)
 {
        int rc;
 
+       rc = em28xx_read_reg(dev, EM2880_R04_GPO);
+       if (rc >= 0)
+               dev->reg_gpo = rc;
+
        dev->wait_after_write = 5;
        rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
        if (rc > 0) {
@@ -547,24 +534,24 @@ void em28xx_pre_card_setup(struct em28xx *dev)
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
        case EM2880_BOARD_TERRATEC_HYBRID_XS:
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
-               em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
+               em28xx_write_regs(dev, EM28XX_R0F_XCLK,    "\x27", 1);
                em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
-               em28xx_write_regs(dev, 0x08, "\xff", 1);
-               em28xx_write_regs(dev, 0x04, "\x00", 1);
-               msleep(100);
-               em28xx_write_regs(dev, 0x04, "\x08", 1);
-               msleep(100);
-               em28xx_write_regs(dev, 0x08, "\xff", 1);
-               msleep(50);
-               em28xx_write_regs(dev, 0x08, "\x2d", 1);
                msleep(50);
-               em28xx_write_regs(dev, 0x08, "\x3d", 1);
 
-               dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
-               dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
+               /* Sets GPO/GPIO sequences for this device */
+               dev->analog_gpio      = hauppauge_wintv_hvr_900_analog;
+               dev->digital_gpio     = hauppauge_wintv_hvr_900_digital;
+               dev->tun_analog_gpio  = hauppauge_wintv_hvr_900_tuner_callback;
+               dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback;
 
                break;
        }
+
+       em28xx_gpio_set(dev, dev->tun_analog_gpio);
+       em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+
+       /* Unlock device */
+       em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
 }
 
 void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
index bd7794f..db40358 100644 (file)
@@ -134,7 +134,10 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
        unsigned char *bufs;
 
        if (dev->state & DEV_DISCONNECTED)
-               return(-ENODEV);
+               return -ENODEV;
+
+       if (len < 1)
+               return -EINVAL;
 
        bufs = kmalloc(len, GFP_KERNEL);
 
@@ -162,7 +165,23 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
 
 int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
 {
-       return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
+       int rc;
+
+       rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
+
+       /* Stores GPO/GPIO values at the cache, if changed
+          Only write values should be stored, since input on a GPIO
+          register will return the input bits.
+          Not sure what happens on reading GPO register.
+        */
+       if (rc >= 0) {
+               if (reg == EM2880_R04_GPO)
+                       dev->reg_gpo = buf[0];
+               else if (reg == EM28XX_R08_GPIO)
+                       dev->reg_gpio = buf[0];
+       }
+
+       return rc;
 }
 
 /*
@@ -176,12 +195,19 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
        int oldval;
        u8 newval;
 
-       oldval = em28xx_read_reg(dev, reg);
+       /* Uses cache for gpo/gpio registers */
+       if (reg == EM2880_R04_GPO)
+               oldval = dev->reg_gpo;
+       else if (reg == EM28XX_R08_GPIO)
+               oldval = dev->reg_gpio;
+       else
+               oldval = em28xx_read_reg(dev, reg);
 
        if (oldval < 0)
                return oldval;
 
        newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
+
        return em28xx_write_regs(dev, reg, &newval, 1);
 }
 
@@ -472,6 +498,57 @@ int em28xx_set_alternate(struct em28xx *dev)
        return 0;
 }
 
+int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
+{
+       int rc = 0;
+
+       if (!gpio)
+               return rc;
+
+       dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+       if (dev->mode == EM28XX_ANALOG_MODE)
+               dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
+       else
+               dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
+       msleep(6);
+
+       /* Send GPIO reset sequences specified at board entry */
+       while (gpio->sleep >= 0) {
+               if (gpio->reg >= 0) {
+                       rc = em28xx_write_reg_bits(dev,
+                                                  gpio->reg,
+                                                  gpio->val,
+                                                  gpio->mask);
+                       if (rc < 0)
+                               return rc;
+               }
+               if (gpio->sleep > 0)
+                       msleep(gpio->sleep);
+
+               gpio++;
+       }
+       return rc;
+}
+
+int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
+{
+       if (dev->mode == set_mode)
+               return 0;
+
+       if (set_mode == EM28XX_MODE_UNDEFINED) {
+               dev->mode = set_mode;
+               return 0;
+       }
+
+       dev->mode = set_mode;
+
+       if (dev->mode == EM28XX_DIGITAL_MODE)
+               return em28xx_gpio_set(dev, dev->digital_gpio);
+       else
+               return em28xx_gpio_set(dev, dev->analog_gpio);
+}
+EXPORT_SYMBOL_GPL(em28xx_set_mode);
+
 /* ------------------------------------------------------------------
        URB control
    ------------------------------------------------------------------*/
@@ -548,8 +625,7 @@ EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
  */
 int em28xx_init_isoc(struct em28xx *dev, int max_packets,
                     int num_bufs, int max_pkt_size,
-                    int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
-                    int cap_type)
+                    int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
 {
        struct em28xx_dmaqueue *dma_q = &dev->vidq;
        int i;
@@ -612,7 +688,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
                        should also be using 'desc.bInterval'
                 */
                pipe = usb_rcvisocpipe(dev->udev,
-                       cap_type == EM28XX_ANALOG_CAPTURE ? 0x82 : 0x84);
+                       dev->mode == EM28XX_ANALOG_MODE ? 0x82 : 0x84);
 
                usb_fill_int_urb(urb, dev->udev, pipe,
                                 dev->isoc_ctl.transfer_buffer[i], sb_size,
@@ -632,7 +708,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
 
        init_waitqueue_head(&dma_q->wq);
 
-       em28xx_capture_start(dev, cap_type);
+       em28xx_capture_start(dev, 1);
 
        /* submit urbs and enables IRQ */
        for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
index 39581d9..2e9ec62 100644 (file)
@@ -137,14 +137,17 @@ static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
 
 static int start_streaming(struct em28xx_dvb *dvb)
 {
+       int rc;
        struct em28xx *dev = dvb->adapter.priv;
 
        usb_set_interface(dev->udev, 0, 1);
-       dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+       rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+       if (rc < 0)
+               return rc;
 
        return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
                                EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE,
-                               dvb_isoc_copy, EM28XX_DIGITAL_CAPTURE);
+                               dvb_isoc_copy);
 }
 
 static int stop_streaming(struct em28xx_dvb *dvb)
@@ -152,6 +155,9 @@ static int stop_streaming(struct em28xx_dvb *dvb)
        struct em28xx *dev = dvb->adapter.priv;
 
        em28xx_uninit_isoc(dev);
+
+       em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+
        return 0;
 }
 
@@ -368,13 +374,10 @@ static int dvb_init(struct em28xx *dev)
        }
        dev->dvb = dvb;
 
+       em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
        /* init frontend */
        switch (dev->model) {
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
-               /* Enable lgdt330x */
-               dev->mode = EM28XX_DIGITAL_MODE;
-               em28xx_tuner_callback(dev, XC2028_TUNER_RESET, 0);
-
                dvb->frontend = dvb_attach(lgdt330x_attach,
                                           &em2880_lgdt3303_dev,
                                           &dev->i2c_adap);
@@ -384,9 +387,6 @@ static int dvb_init(struct em28xx *dev)
                }
                break;
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
-               /* Enable zl10353 */
-               dev->mode = EM28XX_DIGITAL_MODE;
-               em28xx_tuner_callback(dev, XC2028_TUNER_RESET, 0);
                dvb->frontend = dvb_attach(zl10353_attach,
                                           &em28xx_zl10353_with_xc3028,
                                           &dev->i2c_adap);
@@ -415,10 +415,12 @@ static int dvb_init(struct em28xx *dev)
        if (result < 0)
                goto out_free;
 
+       em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
        printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
        return 0;
 
 out_free:
+       em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
        kfree(dvb);
        dev->dvb = NULL;
        return result;
index fb533fd..4ffd064 100644 (file)
@@ -461,7 +461,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
        if (urb_init) {
                rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
                                      EM28XX_NUM_BUFS, dev->max_pkt_size,
-                                     em28xx_isoc_copy, EM28XX_ANALOG_CAPTURE);
+                                     em28xx_isoc_copy);
                if (rc < 0)
                        goto fail;
        }
@@ -1534,8 +1534,8 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
        em28xx_videodbg("open minor=%d type=%s users=%d\n",
                                minor, v4l2_type_names[fh_type], dev->users);
 
-       fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
 
+       fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
        if (!fh) {
                em28xx_errdev("em28xx-video.c: Out of memory?!\n");
                return -ENOMEM;
@@ -1552,9 +1552,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
                dev->hscale = 0;
                dev->vscale = 0;
 
+               em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
                em28xx_set_alternate(dev);
                em28xx_resolution_set(dev);
 
+               /* Needed, since GPIO might have disabled power of
+                  some i2c device
+                */
+               em28xx_config_i2c(dev);
+
        }
        if (fh->radio) {
                em28xx_videodbg("video_open: setting radio device\n");
@@ -1568,6 +1574,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
                        sizeof(struct em28xx_buffer), fh);
 
        mutex_unlock(&dev->lock);
+
        return errCode;
 }
 
@@ -1647,6 +1654,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
 
                /* do this before setting alternate! */
                em28xx_uninit_isoc(dev);
+               em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
 
                /* set alternate 0 */
                dev->alt = 0;
index e4a56d8..e0d119c 100644 (file)
 #define EM2800_I2C_WRITE_TIMEOUT 20
 
 enum em28xx_mode {
+       EM28XX_MODE_UNDEFINED,
        EM28XX_ANALOG_MODE,
        EM28XX_DIGITAL_MODE,
 };
@@ -228,7 +229,7 @@ enum em28xx_decoder {
 
 struct em28xx_reg_seq {
        int reg;
-       unsigned char val;
+       unsigned char val, mask;
        int sleep;
 };
 
@@ -274,12 +275,6 @@ enum em28xx_dev_state {
        DEV_MISCONFIGURED = 0x04,
 };
 
-enum em28xx_capture_mode {
-       EM28XX_CAPTURE_OFF = 0,
-       EM28XX_ANALOG_CAPTURE,
-       EM28XX_DIGITAL_CAPTURE,
-};
-
 #define EM28XX_AUDIO_BUFS 5
 #define EM28XX_NUM_AUDIO_PACKETS 64
 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
@@ -335,9 +330,12 @@ struct em28xx {
        /* Some older em28xx chips needs a waiting time after writing */
        unsigned int wait_after_write;
 
-       /* GPIO sequences for tuner callback */
+       /* GPIO sequences for analog and digital mode */
        struct em28xx_reg_seq *analog_gpio, *digital_gpio;
 
+       /* GPIO sequences for tuner callbacks */
+       struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio;
+
        int video_inputs;       /* number of video inputs */
        struct list_head        devlist;
 
@@ -415,6 +413,9 @@ struct em28xx {
 
        enum em28xx_mode mode;
 
+       /* Caches GPO and GPIO registers */
+       unsigned char   reg_gpo, reg_gpio;
+
        struct em28xx_dvb *dvb;
 };
 
@@ -455,9 +456,10 @@ int em28xx_resolution_set(struct em28xx *dev);
 int em28xx_set_alternate(struct em28xx *dev);
 int em28xx_init_isoc(struct em28xx *dev, int max_packets,
                     int num_bufs, int max_pkt_size,
-                    int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
-                    int cap_type);
+                    int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
 void em28xx_uninit_isoc(struct em28xx *dev);
+int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
+int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
 
 /* Provided by em28xx-video.c */
 int em28xx_register_extension(struct em28xx_ops *dev);