]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/media/video/em28xx/em28xx-video.c
V4L/DVB (12741): em28xx: make video isoc stream work when VBI is enabled
[mv-sheeva.git] / drivers / media / video / em28xx / em28xx-video.c
index ab079d9256c463a0c9889841ed3e0946969f1929..04c9ecc3c22a4f34892d33aa3be150648e84aab8 100644 (file)
@@ -124,7 +124,7 @@ static struct em28xx_fmt format[] = {
 
 /* supported controls */
 /* Common to all boards */
-static struct v4l2_queryctrl em28xx_qctrl[] = {
+static struct v4l2_queryctrl ac97_qctrl[] = {
        {
                .id = V4L2_CID_AUDIO_VOLUME,
                .type = V4L2_CTRL_TYPE_INTEGER,
@@ -133,7 +133,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
                .maximum = 0x1f,
                .step = 0x1,
                .default_value = 0x1f,
-               .flags = 0,
+               .flags = V4L2_CTRL_FLAG_SLIDER,
        }, {
                .id = V4L2_CID_AUDIO_MUTE,
                .type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -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;
        }
@@ -609,10 +728,29 @@ static void res_free(struct em28xx_fh *fh)
 }
 
 /*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
+ * ac97_queryctrl()
+ * return the ac97 supported controls
+ */
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+               if (qc->id && qc->id == ac97_qctrl[i].id) {
+                       memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+                       return 0;
+               }
+       }
+
+       /* Control is not ac97 related */
+       return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
  */
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
@@ -622,29 +760,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
                ctrl->value = dev->volume;
                return 0;
        default:
-               return -EINVAL;
+               /* Control is not ac97 related */
+               return 1;
        }
 }
 
 /*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
  */
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
 {
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+               if (ctrl->id == ac97_qctrl[i].id)
+                       goto handle;
+
+       /* Announce that hasn't handle it */
+       return 1;
+
+handle:
+       if (ctrl->value < ac97_qctrl[i].minimum ||
+           ctrl->value > ac97_qctrl[i].maximum)
+               return -ERANGE;
+
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value != dev->mute) {
-                       dev->mute = ctrl->value;
-                       return em28xx_audio_analog_set(dev);
-               }
-               return 0;
+               dev->mute = ctrl->value;
+               break;
        case V4L2_CID_AUDIO_VOLUME:
                dev->volume = ctrl->value;
-               return em28xx_audio_analog_set(dev);
-       default:
-               return -EINVAL;
+               break;
        }
+
+       return em28xx_audio_analog_set(dev);
 }
 
 static int check_dev(struct em28xx *dev)
@@ -974,6 +1124,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
        struct em28xx_fh   *fh    = priv;
        struct em28xx      *dev   = fh->dev;
 
+       if (!dev->audio_mode.has_audio)
+               return -EINVAL;
+
        switch (a->index) {
        case EM28XX_AMUX_VIDEO:
                strcpy(a->name, "Television");
@@ -1015,6 +1168,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
        struct em28xx      *dev = fh->dev;
 
 
+       if (!dev->audio_mode.has_audio)
+               return -EINVAL;
+
        if (a->index >= MAX_EM28XX_INPUT)
                return -EINVAL;
        if (0 == INPUT(a->index)->type)
@@ -1038,7 +1194,6 @@ static int vidioc_queryctrl(struct file *file, void *priv,
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
        int                   id  = qc->id;
-       int                   i;
        int                   rc;
 
        rc = check_dev(dev);
@@ -1049,15 +1204,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
 
        qc->id = id;
 
-       if (!dev->board.has_msp34xx) {
-               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                       if (qc->id && qc->id == em28xx_qctrl[i].id) {
-                               memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
-                               return 0;
-                       }
-               }
+       /* enumberate AC97 controls */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+               rc = ac97_queryctrl(qc);
+               if (!rc)
+                       return 0;
        }
 
+       /* enumberate V4L2 device controls */
        mutex_lock(&dev->lock);
        v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
        mutex_unlock(&dev->lock);
@@ -1082,14 +1236,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
 
        mutex_lock(&dev->lock);
 
-       if (dev->board.has_msp34xx)
+       /* Set an AC97 control */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+               rc = ac97_get_ctrl(dev, ctrl);
+       else
+               rc = 1;
+
+       /* It were not an AC97 control. Sends it to the v4l2 dev interface */
+       if (rc == 1) {
                v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
-       else {
-               rc = em28xx_get_ctrl(dev, ctrl);
-               if (rc < 0) {
-                       v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
-                       rc = 0;
-               }
+               rc = 0;
        }
 
        mutex_unlock(&dev->lock);
@@ -1101,7 +1257,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
-       u8                    i;
        int                   rc;
 
        rc = check_dev(dev);
@@ -1110,28 +1265,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 
        mutex_lock(&dev->lock);
 
-       if (dev->board.has_msp34xx)
-               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
-       else {
+       /* Set an AC97 control */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+               rc = ac97_set_ctrl(dev, ctrl);
+       else
                rc = 1;
-               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                       if (ctrl->id == em28xx_qctrl[i].id) {
-                               if (ctrl->value < em28xx_qctrl[i].minimum ||
-                                   ctrl->value > em28xx_qctrl[i].maximum) {
-                                       rc = -ERANGE;
-                                       break;
-                               }
-
-                               rc = em28xx_set_ctrl(dev, ctrl);
-                               break;
-                       }
-               }
-       }
 
-       /* Control not found - try to send it to the attached devices */
+       /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
        if (rc == 1) {
                v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
-               rc = 0;
+
+               /*
+                * In the case of non-AC97 volume controls, we still need
+                * to do some setups at em28xx, in order to mute/unmute
+                * and to adjust audio volume. However, the value ranges
+                * should be checked by the corresponding V4L subdriver.
+                */
+               switch (ctrl->id) {
+               case V4L2_CID_AUDIO_MUTE:
+                       dev->mute = ctrl->value;
+                       rc = em28xx_audio_analog_set(dev);
+                       break;
+               case V4L2_CID_AUDIO_VOLUME:
+                       dev->volume = ctrl->value;
+                       rc = em28xx_audio_analog_set(dev);
+               }
        }
 
        mutex_unlock(&dev->lock);
@@ -1275,8 +1433,9 @@ static int vidioc_g_register(struct file *file, void *priv,
                v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
                return 0;
        case V4L2_CHIP_MATCH_I2C_ADDR:
-               /* Not supported yet */
-               return -EINVAL;
+               /* TODO: is this correct? */
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+               return 0;
        default:
                if (!v4l2_chip_match_host(&reg->match))
                        return -EINVAL;
@@ -1327,8 +1486,9 @@ static int vidioc_s_register(struct file *file, void *priv,
                v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
                return 0;
        case V4L2_CHIP_MATCH_I2C_ADDR:
-               /* Not supported yet */
-               return -EINVAL;
+               /* TODO: is this correct? */
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+               return 0;
        default:
                if (!v4l2_chip_match_host(&reg->match))
                        return -EINVAL;
@@ -1431,9 +1591,11 @@ static int vidioc_querycap(struct file *file, void  *priv,
        cap->capabilities =
                        V4L2_CAP_SLICED_VBI_CAPTURE |
                        V4L2_CAP_VIDEO_CAPTURE |
-                       V4L2_CAP_AUDIO |
                        V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
+       if (dev->audio_mode.has_audio)
+               cap->capabilities |= V4L2_CAP_AUDIO;
+
        if (dev->tuner_type != TUNER_ABSENT)
                cap->capabilities |= V4L2_CAP_TUNER;
 
@@ -1654,9 +1816,9 @@ static int radio_queryctrl(struct file *file, void *priv,
                qc->id >= V4L2_CID_LASTP1)
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-               if (qc->id && qc->id == em28xx_qctrl[i].id) {
-                       memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+               if (qc->id && qc->id == ac97_qctrl[i].id) {
+                       memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
                        return 0;
                }
        }