[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>
This commit is contained in:
parent
0295202ca7
commit
3e4748d867
|
@ -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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 {
|
||||||
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,
|
||||||
|
|
Loading…
Reference in New Issue