V4L/DVB (12520): sh-mobile-ceu-camera: do not wait for interrupt when releasing buffers
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Tue, 25 Aug 2009 14:46:51 +0000 (11:46 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 19 Sep 2009 03:18:51 +0000 (00:18 -0300)
Patch

[PATCH] video: use videobuf_waiton() in sh_mobile_ceu free_buffer()

was not quite correct. It closed a race, but introduced a potential
lock-up, if for some reason an interrupt does not come. This has been
observed in tests with tw9910. This patch safely dequeues buffers without
waiting for their completion. It also moves a buffer state assignment
under a spinlock to make it atomic with queuing of the buffer.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/sh_mobile_ceu_camera.c

index 4c4b60c..c0dc4a1 100644 (file)
@@ -307,6 +307,27 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
 static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
                                           struct videobuf_buffer *vb)
 {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pcdev->lock, flags);
+
+       if (pcdev->active == vb) {
+               /* disable capture (release DMA buffer), reset */
+               ceu_write(pcdev, CAPSR, 1 << 16);
+               pcdev->active = NULL;
+       }
+
+       if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
+           !list_empty(&vb->queue)) {
+               vb->state = VIDEOBUF_ERROR;
+               list_del_init(&vb->queue);
+       }
+
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+
        free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb));
 }
 
@@ -326,6 +347,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
        spin_lock_irqsave(&pcdev->lock, flags);
 
        vb = pcdev->active;
+       if (!vb)
+               /* Stale interrupt from a released buffer */
+               goto out;
+
        list_del_init(&vb->queue);
 
        if (!list_empty(&pcdev->capture))
@@ -340,6 +365,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
        do_gettimeofday(&vb->ts);
        vb->field_count++;
        wake_up(&vb->done);
+
+out:
        spin_unlock_irqrestore(&pcdev->lock, flags);
 
        return IRQ_HANDLED;