Commit 3e4748d8 authored by Sylwester Nawrocki's avatar Sylwester Nawrocki Committed by Mauro Carvalho Chehab

[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: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 0295202c
...@@ -69,41 +69,45 @@ static int fimc_init_capture(struct fimc_dev *fimc) ...@@ -69,41 +69,45 @@ static int fimc_init_capture(struct fimc_dev *fimc)
return ret; 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_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *buf; struct fimc_vid_buffer *buf;
unsigned long flags; unsigned long flags;
bool streaming;
spin_lock_irqsave(&fimc->slock, flags); spin_lock_irqsave(&fimc->slock, flags);
fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND | streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM |
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. */ /* Release unused buffers */
while (!list_empty(&cap->pending_buf_q)) { while (!suspend && !list_empty(&cap->pending_buf_q)) {
buf = fimc_pending_queue_pop(cap); buf = fimc_pending_queue_pop(cap);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
} }
/* If suspending put unused buffers onto pending queue */
while (!list_empty(&cap->active_buf_q)) { while (!list_empty(&cap->active_buf_q)) {
buf = fimc_active_queue_pop(cap); 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); spin_unlock_irqrestore(&fimc->slock, flags);
if (test_bit(ST_CAPT_ISP_STREAM, &fimc->state)) if (streaming)
return fimc_pipeline_s_stream(fimc, 0); return fimc_pipeline_s_stream(fimc, 0);
else else
return 0; 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; unsigned long flags;
if (!fimc_capture_active(fimc)) if (!fimc_capture_active(fimc))
...@@ -116,9 +120,9 @@ static int fimc_stop_capture(struct fimc_dev *fimc) ...@@ -116,9 +120,9 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
wait_event_timeout(fimc->irq_queue, wait_event_timeout(fimc->irq_queue,
!test_bit(ST_CAPT_SHUT, &fimc->state), !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) ...@@ -181,7 +185,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
return 0; return 0;
error: error:
fimc_capture_state_cleanup(fimc); fimc_capture_state_cleanup(fimc, false);
return ret; return ret;
} }
...@@ -193,17 +197,46 @@ static int stop_streaming(struct vb2_queue *q) ...@@ -193,17 +197,46 @@ static int stop_streaming(struct vb2_queue *q)
if (!fimc_capture_active(fimc)) if (!fimc_capture_active(fimc))
return -EINVAL; return -EINVAL;
return fimc_stop_capture(fimc); return fimc_stop_capture(fimc, false);
} }
int fimc_capture_suspend(struct fimc_dev *fimc) 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) 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; return 0;
} }
static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) 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) ...@@ -271,8 +304,9 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&fimc->slock, flags); spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr); fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
if (!test_bit(ST_CAPT_STREAM, &fimc->state) if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
&& vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) { !test_bit(ST_CAPT_STREAM, &fimc->state) &&
vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
/* Setup the buffer directly for processing. */ /* Setup the buffer directly for processing. */
int buf_id = (vid_cap->reqbufs_count == 1) ? -1 : int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
vid_cap->buf_index; vid_cap->buf_index;
...@@ -366,6 +400,7 @@ static int fimc_capture_open(struct file *file) ...@@ -366,6 +400,7 @@ static int fimc_capture_open(struct file *file)
if (fimc_m2m_active(fimc)) if (fimc_m2m_active(fimc))
return -EBUSY; return -EBUSY;
set_bit(ST_CAPT_BUSY, &fimc->state);
pm_runtime_get_sync(&fimc->pdev->dev); pm_runtime_get_sync(&fimc->pdev->dev);
if (++fimc->vid_cap.refcnt == 1) { if (++fimc->vid_cap.refcnt == 1) {
...@@ -377,6 +412,7 @@ static int fimc_capture_open(struct file *file) ...@@ -377,6 +412,7 @@ static int fimc_capture_open(struct file *file)
pm_runtime_put_sync(&fimc->pdev->dev); pm_runtime_put_sync(&fimc->pdev->dev);
fimc->vid_cap.refcnt--; fimc->vid_cap.refcnt--;
v4l2_fh_release(file); v4l2_fh_release(file);
clear_bit(ST_CAPT_BUSY, &fimc->state);
return ret; return ret;
} }
ret = fimc_capture_ctrls_create(fimc); ret = fimc_capture_ctrls_create(fimc);
...@@ -394,14 +430,18 @@ static int fimc_capture_close(struct file *file) ...@@ -394,14 +430,18 @@ static int fimc_capture_close(struct file *file)
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) { 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_pipeline_shutdown(fimc);
fimc_ctrls_delete(fimc->vid_cap.ctx); clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
vb2_queue_release(&fimc->vid_cap.vbq);
} }
pm_runtime_put(&fimc->pdev->dev); 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); return v4l2_fh_release(file);
} }
......
...@@ -334,6 +334,11 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) ...@@ -334,6 +334,11 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
struct timeval *tv; struct timeval *tv;
struct timespec ts; 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) && if (!list_empty(&cap->active_buf_q) &&
test_bit(ST_CAPT_RUN, &fimc->state) && final) { test_bit(ST_CAPT_RUN, &fimc->state) && final) {
ktime_get_real_ts(&ts); ktime_get_real_ts(&ts);
...@@ -348,11 +353,6 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) ...@@ -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); 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)) { if (!list_empty(&cap->pending_buf_q)) {
v_buf = fimc_pending_queue_pop(cap); v_buf = fimc_pending_queue_pop(cap);
......
...@@ -63,6 +63,7 @@ enum fimc_dev_flags { ...@@ -63,6 +63,7 @@ enum fimc_dev_flags {
ST_CAPT_RUN, ST_CAPT_RUN,
ST_CAPT_STREAM, ST_CAPT_STREAM,
ST_CAPT_ISP_STREAM, ST_CAPT_ISP_STREAM,
ST_CAPT_SUSPENDED,
ST_CAPT_SHUT, ST_CAPT_SHUT,
ST_CAPT_BUSY, ST_CAPT_BUSY,
ST_CAPT_APPLY_CFG, ST_CAPT_APPLY_CFG,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment