diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2009-08-25 16:46:51 +0200 |
---|---|---|
committer | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2009-08-25 16:46:51 +0200 |
commit | 476b4e44d0836e75896fd12b0d5f33fdc706bee9 (patch) | |
tree | 3af1496753e0942443c7fbad50d19b785f55cef8 /linux/drivers | |
parent | 1e42790cf4308ec89ea72d095fe8aa119ea43dc5 (diff) | |
download | mediapointer-dvb-s2-476b4e44d0836e75896fd12b0d5f33fdc706bee9.tar.gz mediapointer-dvb-s2-476b4e44d0836e75896fd12b0d5f33fdc706bee9.tar.bz2 |
sh-mobile-ceu-camera: do not wait for interrupt when releasing buffers
From: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
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>
Priority: low
---
drivers/media/video/sh_mobile_ceu_camera.c | 27 +++++++++++++++++++++++++++
1 files changed, 27 insertions(+), 0 deletions(-)
Diffstat (limited to 'linux/drivers')
-rw-r--r-- | linux/drivers/media/video/sh_mobile_ceu_camera.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 4c33c3efd..f54a9f23f 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -309,6 +309,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)); } @@ -328,6 +349,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)) @@ -342,6 +367,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; |