summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/s5p-fimc
diff options
context:
space:
mode:
authorSylwester Nawrocki2011-08-25 01:45:34 +0200
committerMauro Carvalho Chehab2011-09-06 22:50:31 +0200
commit3e4748d867781732c29c00fe8fe4b179d914a977 (patch)
tree404854429c4784a31c3f63bcb056e3c2342fb64d /drivers/media/video/s5p-fimc
parent[media] s5p-fimc: Use consistent names for the buffer list functions (diff)
downloadkernel-qcow2-linux-3e4748d867781732c29c00fe8fe4b179d914a977.tar.gz
kernel-qcow2-linux-3e4748d867781732c29c00fe8fe4b179d914a977.tar.xz
kernel-qcow2-linux-3e4748d867781732c29c00fe8fe4b179d914a977.zip
[media] s5p-fimc: Add runtime PM support in the camera capture driver
Add support for whole pipeline suspend/resume. Sensors must support suspend/resume through s_power subdev operation. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/s5p-fimc')
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c86
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c10
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h1
3 files changed, 69 insertions, 28 deletions
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 469384623952..d6219c5aeffc 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -69,41 +69,45 @@ static int fimc_init_capture(struct fimc_dev *fimc)
return ret;
}
-static int fimc_capture_state_cleanup(struct fimc_dev *fimc)
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *buf;
unsigned long flags;
+ bool streaming;
spin_lock_irqsave(&fimc->slock, flags);
- fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
- 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM |
- 1 << ST_CAPT_ISP_STREAM);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
- fimc->vid_cap.active_buf_cnt = 0;
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (!suspend)
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
- /* Release buffers that were enqueued in the driver by videobuf2. */
- while (!list_empty(&cap->pending_buf_q)) {
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
buf = fimc_pending_queue_pop(cap);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
-
+ /* If suspending put unused buffers onto pending queue */
while (!list_empty(&cap->active_buf_q)) {
buf = fimc_active_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
-
+ set_bit(ST_CAPT_SUSPENDED, &fimc->state);
spin_unlock_irqrestore(&fimc->slock, flags);
- if (test_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ if (streaming)
return fimc_pipeline_s_stream(fimc, 0);
else
return 0;
}
-static int fimc_stop_capture(struct fimc_dev *fimc)
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
{
- struct fimc_vid_cap *cap = &fimc->vid_cap;
unsigned long flags;
if (!fimc_capture_active(fimc))
@@ -116,9 +120,9 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
wait_event_timeout(fimc->irq_queue,
!test_bit(ST_CAPT_SHUT, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
+ (2*HZ/10)); /* 200 ms */
- return fimc_capture_state_cleanup(fimc);
+ return fimc_capture_state_cleanup(fimc, suspend);
}
/**
@@ -181,7 +185,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
error:
- fimc_capture_state_cleanup(fimc);
+ fimc_capture_state_cleanup(fimc, false);
return ret;
}
@@ -193,17 +197,46 @@ static int stop_streaming(struct vb2_queue *q)
if (!fimc_capture_active(fimc))
return -EINVAL;
- return fimc_stop_capture(fimc);
+ return fimc_stop_capture(fimc, false);
}
int fimc_capture_suspend(struct fimc_dev *fimc)
{
- return -EBUSY;
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_shutdown(fimc);
}
+static void buffer_queue(struct vb2_buffer *vb);
+
int fimc_capture_resume(struct fimc_dev *fimc)
{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
+
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ false);
+ fimc_init_capture(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
return 0;
+
}
static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
@@ -271,8 +304,9 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
- if (!test_bit(ST_CAPT_STREAM, &fimc->state)
- && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
/* Setup the buffer directly for processing. */
int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
vid_cap->buf_index;
@@ -366,6 +400,7 @@ static int fimc_capture_open(struct file *file)
if (fimc_m2m_active(fimc))
return -EBUSY;
+ set_bit(ST_CAPT_BUSY, &fimc->state);
pm_runtime_get_sync(&fimc->pdev->dev);
if (++fimc->vid_cap.refcnt == 1) {
@@ -377,6 +412,7 @@ static int fimc_capture_open(struct file *file)
pm_runtime_put_sync(&fimc->pdev->dev);
fimc->vid_cap.refcnt--;
v4l2_fh_release(file);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
return ret;
}
ret = fimc_capture_ctrls_create(fimc);
@@ -394,14 +430,18 @@ static int fimc_capture_close(struct file *file)
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) {
- fimc_stop_capture(fimc);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
fimc_pipeline_shutdown(fimc);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- vb2_queue_release(&fimc->vid_cap.vbq);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
}
pm_runtime_put(&fimc->pdev->dev);
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ }
return v4l2_fh_release(file);
}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 33c706985763..a51bf309f4dc 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -334,6 +334,11 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
struct timeval *tv;
struct timespec ts;
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ return;
+ }
+
if (!list_empty(&cap->active_buf_q) &&
test_bit(ST_CAPT_RUN, &fimc->state) && final) {
ktime_get_real_ts(&ts);
@@ -348,11 +353,6 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
- if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- return;
- }
-
if (!list_empty(&cap->pending_buf_q)) {
v_buf = fimc_pending_queue_pop(cap);
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 82ac59776df7..a6936dad5b10 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -63,6 +63,7 @@ enum fimc_dev_flags {
ST_CAPT_RUN,
ST_CAPT_STREAM,
ST_CAPT_ISP_STREAM,
+ ST_CAPT_SUSPENDED,
ST_CAPT_SHUT,
ST_CAPT_BUSY,
ST_CAPT_APPLY_CFG,