]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
V4L/DVB (12741): em28xx: make video isoc stream work when VBI is enabled
authorDevin Heitmueller <dheitmueller@kernellabs.com>
Tue, 1 Sep 2009 04:19:46 +0000 (01:19 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 19 Sep 2009 02:47:40 +0000 (23:47 -0300)
Add code enabling the VBI registers for variants of the em28xx chip that
support VBI, and make sure the isoc streaming code continues to work for
the video component of the stream (note the video and vbi data arrive
intermixed on the same isoc pipe).

Note that this version just drops the actual VBI data onto the floor as
opposed to processing it.  The "#ifdef 0" tags are for the videobuf code that
appears in the next patch in this series.

We created a separate version of the isoc_copy version for parsing the version
of the stream that includes VBI data.  In theory, they might be able to be
merged at some point in the future, but the initial goal is to ensure that we
do not cause any regressions with devices that do not have VBI support.

This work was sponsored by EyeMagnet Limited.

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/em28xx/em28xx-core.c
drivers/media/video/em28xx/em28xx-reg.h
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index 98e140b5d95e66a48876514721ab203369cf2e89..d4107f6933f892a24dcf308977d505e6d7790258 100644 (file)
@@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT;
 module_param(alt, int, 0644);
 MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
 
+static unsigned int disable_vbi;
+module_param(disable_vbi, int, 0644);
+MODULE_PARM_DESC(disable_vbi, "disable vbi support");
+
 /* FIXME */
 #define em28xx_isocdbg(fmt, arg...) do {\
        if (core_debug) \
@@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start)
        return rc;
 }
 
+int em28xx_vbi_supported(struct em28xx *dev)
+{
+       /* Modprobe option to manually disable */
+       if (disable_vbi == 1)
+               return 0;
+
+       if (dev->chip_id == CHIP_ID_EM2860 ||
+           dev->chip_id == CHIP_ID_EM2883)
+               return 1;
+
+       /* Version of em28xx that does not support VBI */
+       return 0;
+}
+
 int em28xx_set_outfmt(struct em28xx *dev)
 {
        int ret;
+       u8 vinctrl;
 
        ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
                                dev->format->reg | 0x20, 0xff);
@@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev)
        if (ret < 0)
                return ret;
 
-       return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl);
+       vinctrl = dev->vinctl;
+       if (em28xx_vbi_supported(dev) == 1) {
+               vinctrl |= EM28XX_VINCTRL_VBI_RAW;
+               em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
+               em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+               em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4);
+               em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c);
+       }
+
+       return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
 }
 
 static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev)
 
 
        em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
-       em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
+
+       /* If we don't set the start position to 4 in VBI mode, we end up
+          with line 21 being YUYV encoded instead of being in 8-bit
+          greyscale */
+       if (em28xx_vbi_supported(dev) == 1)
+               em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2);
+       else
+               em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
 
        return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
 }
@@ -844,8 +879,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode);
  */
 static void em28xx_irq_callback(struct urb *urb)
 {
-       struct em28xx_dmaqueue  *dma_q = urb->context;
-       struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+       struct em28xx *dev = urb->context;
        int rc, i;
 
        switch (urb->status) {
@@ -994,7 +1028,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
 
                usb_fill_int_urb(urb, dev->udev, pipe,
                                 dev->isoc_ctl.transfer_buffer[i], sb_size,
-                                em28xx_irq_callback, dma_q, 1);
+                                em28xx_irq_callback, dev, 1);
 
                urb->number_of_packets = max_packets;
                urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
index 3bf69f9ec375e8dd95df0b438187327e705228b6..ed12e7ffcbd0fd73e70af05b94d14ea2518618a5 100644 (file)
 #define EM28XX_R31_HSCALEHIGH  0x31
 #define EM28XX_R32_VSCALELOW   0x32
 #define EM28XX_R33_VSCALEHIGH  0x33
+#define EM28XX_R34_VBI_START_H 0x34
+#define EM28XX_R35_VBI_START_V 0x35
+#define EM28XX_R36_VBI_WIDTH   0x36
+#define EM28XX_R37_VBI_HEIGHT  0x37
 
 #define EM28XX_R40_AC97LSB     0x40
 #define EM28XX_R41_AC97MSB     0x41
index a6bdbc21410e7764cc67eee1cd0543079f8c5b36..04c9ecc3c22a4f34892d33aa3be150648e84aab8 100644 (file)
@@ -329,7 +329,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
 static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
 {
        struct em28xx_buffer    *buf;
-       struct em28xx_dmaqueue  *dma_q = urb->context;
+       struct em28xx_dmaqueue  *dma_q = &dev->vidq;
        unsigned char *outp = NULL;
        int i, len = 0, rc = 1;
        unsigned char *p;
@@ -410,6 +410,118 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
        return rc;
 }
 
+/* Version of isoc handler that takes into account a mixture of video and
+   VBI data */
+static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+{
+       struct em28xx_buffer    *buf, *vbi_buf;
+       struct em28xx_dmaqueue  *dma_q = &dev->vidq;
+       unsigned char *outp = NULL;
+       unsigned char *vbioutp = NULL;
+       int i, len = 0, rc = 1;
+       unsigned char *p;
+       int vbi_size;
+
+       if (!dev)
+               return 0;
+
+       if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+               return 0;
+
+       if (urb->status < 0) {
+               print_err_status(dev, -1, urb->status);
+               if (urb->status == -ENOENT)
+                       return 0;
+       }
+
+       buf = dev->isoc_ctl.buf;
+       if (buf != NULL)
+               outp = videobuf_to_vmalloc(&buf->vb);
+       for (i = 0; i < urb->number_of_packets; i++) {
+               int status = urb->iso_frame_desc[i].status;
+
+               if (status < 0) {
+                       print_err_status(dev, i, status);
+                       if (urb->iso_frame_desc[i].status != -EPROTO)
+                               continue;
+               }
+
+               len = urb->iso_frame_desc[i].actual_length - 4;
+
+               if (urb->iso_frame_desc[i].actual_length <= 0) {
+                       /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+                       continue;
+               }
+               if (urb->iso_frame_desc[i].actual_length >
+                                               dev->max_pkt_size) {
+                       em28xx_isocdbg("packet bigger than packet size");
+                       continue;
+               }
+
+               p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+               /* capture type 0 = vbi start
+                  capture type 1 = video start
+                  capture type 2 = video in progress */
+               if (p[0] == 0x33 && p[1] == 0x95) {
+                       dev->capture_type = 0;
+                       dev->vbi_read = 0;
+                       em28xx_isocdbg("VBI START HEADER!!!\n");
+                       dev->cur_field = p[2];
+               }
+
+               /* FIXME: get rid of hard-coded value */
+               vbi_size = 720 * 0x0c;
+
+               if (dev->capture_type == 0) {
+                       if (dev->vbi_read >= vbi_size) {
+                               /* We've already read all the VBI data, so
+                                  treat the rest as video */
+                               printk("djh c should never happen\n");
+                       } else if ((dev->vbi_read + len) < vbi_size) {
+                               /* This entire frame is VBI data */
+                               dev->vbi_read += len;
+                       } else {
+                               /* Some of this frame is VBI data and some is
+                                  video data */
+                               int vbi_data_len = vbi_size - dev->vbi_read;
+                               dev->vbi_read += vbi_data_len;
+                               dev->capture_type = 1;
+                               p += vbi_data_len;
+                               len -= vbi_data_len;
+                       }
+               }
+
+               if (dev->capture_type == 1) {
+                       dev->capture_type = 2;
+                       em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+                                      len, (p[2] & 1) ? "odd" : "even");
+
+                       if (dev->progressive || !(dev->cur_field & 1)) {
+                               if (buf != NULL)
+                                       buffer_filled(dev, dma_q, buf);
+                               get_next_buf(dma_q, &buf);
+                               if (buf == NULL)
+                                       outp = NULL;
+                               else
+                                       outp = videobuf_to_vmalloc(&buf->vb);
+                       }
+                       if (buf != NULL) {
+                               if (dev->cur_field & 1)
+                                       buf->top_field = 0;
+                               else
+                                       buf->top_field = 1;
+                       }
+
+                       dma_q->pos = 0;
+               }
+               if (buf != NULL && dev->capture_type == 2)
+                       em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+       }
+       return rc;
+}
+
+
 /* ------------------------------------------------------------------
        Videobuf operations
    ------------------------------------------------------------------*/
@@ -494,9 +606,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                urb_init = 1;
 
        if (urb_init) {
-               rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
-                                     EM28XX_NUM_BUFS, dev->max_pkt_size,
-                                     em28xx_isoc_copy);
+               if (em28xx_vbi_supported(dev) == 1)
+                       rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+                                             EM28XX_NUM_BUFS,
+                                             dev->max_pkt_size,
+                                             em28xx_isoc_copy_vbi);
+               else
+                       rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+                                             EM28XX_NUM_BUFS,
+                                             dev->max_pkt_size,
+                                             em28xx_isoc_copy);
                if (rc < 0)
                        goto fail;
        }
index 0f2ba9a40d1726664498c6616c47bcba7ed1d3ab..1656d2cf34a9d28557cffcce5032ccd21d357e75 100644 (file)
@@ -544,6 +544,12 @@ struct em28xx {
        enum em28xx_dev_state state;
        enum em28xx_io_method io;
 
+       /* vbi related state tracking */
+       int capture_type;
+       int vbi_read;
+       unsigned char cur_field;
+
+
        struct work_struct         request_module_wk;
 
        /* locks */
@@ -639,6 +645,7 @@ int em28xx_audio_setup(struct em28xx *dev);
 
 int em28xx_colorlevels_set_default(struct em28xx *dev);
 int em28xx_capture_start(struct em28xx *dev, int start);
+int em28xx_vbi_supported(struct em28xx *dev);
 int em28xx_set_outfmt(struct em28xx *dev);
 int em28xx_resolution_set(struct em28xx *dev);
 int em28xx_set_alternate(struct em28xx *dev);