@@ -1566,12 +1566,24 @@ static int isp_pm_prepare(struct device *dev)
{
struct isp_device *isp = dev_get_drvdata(dev);
int reset;
+ int err = 0;
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ struct isp_video *video = &ccdc->video_out;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ unsigned long flags;
WARN_ON(mutex_is_locked(&isp->isp_mutex));
-
if (isp->ref_count == 0)
return 0;
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state |= ISP_PIPELINE_PREPARE_SUSPEND;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ err = isp_video_handle_buffer_starvation(video);
+ if (err < 0)
+ return err;
+
reset = isp_suspend_modules(isp);
isp_disable_interrupts(isp);
isp_save_ctx(isp);
@@ -1596,16 +1608,32 @@ static int isp_pm_suspend(struct device *dev)
static int isp_pm_resume(struct device *dev)
{
struct isp_device *isp = dev_get_drvdata(dev);
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ struct isp_video *video = &ccdc->video_out;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ unsigned long flags;
if (isp->ref_count == 0)
return 0;
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_PREPARE_SUSPEND;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
return isp_enable_clocks(isp);
}
static void isp_pm_complete(struct device *dev)
{
struct isp_device *isp = dev_get_drvdata(dev);
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ struct isp_video *video = &ccdc->video_out;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_PREPARE_SUSPEND;
+ spin_unlock_irqrestore(&pipe->lock, flags);
if (isp->ref_count == 0)
return;
@@ -259,6 +259,10 @@ int omap3isp_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
void omap3isp_unregister_entities(struct platform_device *pdev);
+#ifdef CONFIG_PM
+int isp_video_handle_buffer_starvation(struct isp_video *video);
+#endif
+
/*
* isp_reg_readl - Read value of an OMAP3 ISP register
* @dev: Device pointer specific to the OMAP3 ISP.
@@ -526,6 +526,26 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
return 0;
}
+#ifdef CONFIG_PM
+static int isp_video_deq_enq(struct isp_video_queue *queue)
+{
+ int err = 0;
+ struct v4l2_buffer vbuf;
+
+ vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ /* blocking dequeue to ensure DMA is done */
+ err = omap3isp_video_queue_dqbuf(queue, &vbuf, 0);
+ if (err < 0)
+ return err;
+ else {
+ err = omap3isp_video_queue_qbuf(queue, &vbuf);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+#endif
+
/*
* isp_video_buffer_queue - Add buffer to streaming queue
* @buf: Video buffer
@@ -549,7 +569,7 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
empty = list_empty(&video->dmaqueue);
list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
- if (empty) {
+ if (empty && !(pipe->state & ISP_PIPELINE_PREPARE_SUSPEND)) {
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
state = ISP_PIPELINE_QUEUE_OUTPUT;
else
@@ -690,6 +710,42 @@ void omap3isp_video_resume(struct isp_video *video, int continuous)
}
}
+#ifdef CONFIG_PM
+
+/*
+ * isp_video_handle_buffer_starvation - Handles the case when there are only 1
+ * or less active buffers present on the dmaqueue while preparing for suspend.
+ *
+ * @video: ISP video object
+ *
+ * This function is intended to be used in suspend/resume scenario. While
+ * preparing for suspend, if the number of active buffers on dmaqueue is 0 or 1,
+ * the last buffer is dequeued after DMA completes and re-queued again. This
+ * prevents ISP_VIDEO_DMAQUEUE_UNDERRUN from occuring on issue of resume.
+ */
+int isp_video_handle_buffer_starvation(struct isp_video *video)
+{
+ int err = 0;
+ struct isp_video_queue *queue = video->queue;
+ struct isp_video_buffer *buf;
+ struct list_head *head = &video->dmaqueue;
+ struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
+
+ if (list_empty(&video->dmaqueue)
+ && ccdc->state != ISP_PIPELINE_STREAM_STOPPED) {
+ err = isp_video_deq_enq(queue);
+ } else if (head->next->next == head) {
+ /* only one buffer is left on dmaqueue */
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_video_buffer,
+ irqlist);
+ if (buf->state == ISP_BUF_STATE_ACTIVE)
+ err = isp_video_deq_enq(queue);
+ }
+ return err;
+}
+#endif
+
/* -----------------------------------------------------------------------------
* V4L2 ioctls
*/
@@ -83,6 +83,8 @@ enum isp_pipeline_state {
ISP_PIPELINE_IDLE_OUTPUT = 32,
/* The pipeline is currently streaming. */
ISP_PIPELINE_STREAM = 64,
+ /* The pipeline is currently preparing to suspend. */
+ ISP_PIPELINE_PREPARE_SUSPEND = 128,
};
struct isp_pipeline {