[media] omap3isp: Cancel streaming when a fatal error occurs
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 9 Dec 2013 14:36:51 +0000 (11:36 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Tue, 7 Jan 2014 10:08:48 +0000 (08:08 -0200)
When a fatal error that prevents any further video streaming occurs in a
pipeline, all queued buffers must be marked as erroneous and new buffers
must be prevented from being queued. Implement this behaviour with a new
omap3isp_pipeline_cancel_stream() function that can be used by
submodules to cancel streaming.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/omap3isp/isp.h
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/omap3isp/ispvideo.h

index bb4e0a7c7b5eb0751aa2bb7528cc4b6af4ed7c8c..7e09c1dd137a0c16e915ba3c1b37dd0b73d3a76f 100644 (file)
@@ -1056,6 +1056,23 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
        return ret;
 }
 
+/*
+ * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Cancelling a stream mark all buffers on all video nodes in the pipeline as
+ * erroneous and makes sure no new buffer can be queued. This function is called
+ * when a fatal error that prevents any further operation on the pipeline
+ * occurs.
+ */
+void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe)
+{
+       if (pipe->input)
+               omap3isp_video_cancel_stream(pipe->input);
+       if (pipe->output)
+               omap3isp_video_cancel_stream(pipe->output);
+}
+
 /*
  * isp_pipeline_resume - Resume streaming on a pipeline
  * @pipe: ISP pipeline
index 72685ad0fc1ed6eb23f484ffd6d42a4758d9c302..5b91f86198865f43d19466425451ad2dce143842 100644 (file)
@@ -236,6 +236,7 @@ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
 
 int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
                                 enum isp_pipeline_stream_state state);
+void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe);
 void omap3isp_configure_bridge(struct isp_device *isp,
                               enum ccdc_input_entity input,
                               const struct isp_parallel_platform_data *pdata,
index 3953aec7c5c5954efbf6a3aedddc110bd6105a13..856fdf55403580c996493b03b811b3c4ef6ac5b0 100644 (file)
@@ -411,6 +411,15 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
        struct isp_video *video = vfh->video;
        unsigned long addr;
 
+       /* Refuse to prepare the buffer is the video node has registered an
+        * error. We don't need to take any lock here as the operation is
+        * inherently racy. The authoritative check will be performed in the
+        * queue handler, which can't return an error, this check is just a best
+        * effort to notify userspace as early as possible.
+        */
+       if (unlikely(video->error))
+               return -EIO;
+
        addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
        if (IS_ERR_VALUE(addr))
                return -EIO;
@@ -447,6 +456,12 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
        unsigned int empty;
        unsigned int start;
 
+       if (unlikely(video->error)) {
+               buf->state = ISP_BUF_STATE_ERROR;
+               wake_up(&buf->wait);
+               return;
+       }
+
        empty = list_empty(&video->dmaqueue);
        list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
 
@@ -568,6 +583,36 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
        return to_isp_buffer(buf);
 }
 
+/*
+ * omap3isp_video_cancel_stream - Cancel stream on a video node
+ * @video: ISP video object
+ *
+ * Cancelling a stream mark all buffers on the video node as erroneous and makes
+ * sure no new buffer can be queued.
+ */
+void omap3isp_video_cancel_stream(struct isp_video *video)
+{
+       struct isp_video_queue *queue = video->queue;
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+
+       while (!list_empty(&video->dmaqueue)) {
+               struct isp_video_buffer *buf;
+
+               buf = list_first_entry(&video->dmaqueue,
+                                      struct isp_video_buffer, irqlist);
+               list_del(&buf->irqlist);
+
+               buf->state = ISP_BUF_STATE_ERROR;
+               wake_up(&buf->wait);
+       }
+
+       video->error = true;
+
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
 /*
  * omap3isp_video_resume - Perform resume operation on the buffers
  * @video: ISP video object
@@ -1105,6 +1150,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
        omap3isp_video_queue_streamoff(&vfh->queue);
        video->queue = NULL;
        video->streaming = 0;
+       video->error = false;
 
        if (video->isp->pdata->set_constraints)
                video->isp->pdata->set_constraints(video->isp, false);
index 1ad470ec2b9d5ab5f4cd212f11d0b3d412d93b20..4e194076cc60d611727a6441231f79006dbfadfc 100644 (file)
@@ -178,6 +178,7 @@ struct isp_video {
        /* Pipeline state */
        struct isp_pipeline pipe;
        struct mutex stream_lock;       /* pipeline and stream states */
+       bool error;
 
        /* Video buffers queue */
        struct isp_video_queue *queue;
@@ -207,6 +208,7 @@ int omap3isp_video_register(struct isp_video *video,
                            struct v4l2_device *vdev);
 void omap3isp_video_unregister(struct isp_video *video);
 struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video);
+void omap3isp_video_cancel_stream(struct isp_video *video);
 void omap3isp_video_resume(struct isp_video *video, int continuous);
 struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);