Hi Sylwester,
On Tue, Aug 30, 2011 at 05:00:39PM +0200, Sylwester Nawrocki wrote:
> Add runtime PM and system sleep support in the memory-to-memory driver.
> This is required to enable the device operation on Exynos4 SoCs. This patch
> prevents system boot failure when the driver is compiled in, as it now
> tries to access its I/O memory without first enabling the corresponding
> power domain.
>
> The camera capture device suspend/resume is not fully covered,
> the capture device is just powered on/off during the video node
> open/close. However this enables it's normal operation on Exynos4 SoCs.
>
> Signed-off-by: Sylwester Nawrocki <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
>
> I'm resending this patch after few minor changes since v3:
> - added the driver dependency on PM_RUNTIME
> - corrected the error path in probe()
> - s/*_in_use/_busy
> - edited the commit message
>
> ---
> drivers/media/video/Kconfig | 2 +-
> drivers/media/video/s5p-fimc/fimc-capture.c | 18 ++
> drivers/media/video/s5p-fimc/fimc-core.c | 279
> ++++++++++++++++++++-------
> drivers/media/video/s5p-fimc/fimc-core.h | 16 +-
> drivers/media/video/s5p-fimc/fimc-reg.c | 2 +-
> 5 files changed, 237 insertions(+), 80 deletions(-)
>
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f574dc0..14326d7 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -950,7 +950,7 @@ config VIDEO_MX2
>
> config VIDEO_SAMSUNG_S5P_FIMC
> tristate "Samsung S5P and EXYNOS4 camera host interface driver"
> - depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
> + depends on VIDEO_V4L2 && PLAT_S5P && PM_RUNTIME
> select VIDEOBUF2_DMA_CONTIG
> select V4L2_MEM2MEM_DEV
> ---help---
> diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c
> b/drivers/media/video/s5p-fimc/fimc-capture.c
> index 0d730e5..ea74e4b 100644
> --- a/drivers/media/video/s5p-fimc/fimc-capture.c
> +++ b/drivers/media/video/s5p-fimc/fimc-capture.c
> @@ -17,6 +17,7 @@
> #include <linux/interrupt.h>
> #include <linux/device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> #include <linux/list.h>
> #include <linux/slab.h>
> #include <linux/clk.h>
> @@ -196,6 +197,16 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
> return 0;
> }
>
> +int fimc_capture_suspend(struct fimc_dev *fimc)
> +{
> + return -EBUSY;
> +}
> +
> +int fimc_capture_resume(struct fimc_dev *fimc)
> +{
> + return 0;
> +}
> +
> static int start_streaming(struct vb2_queue *q)
> {
> struct fimc_ctx *ctx = q->drv_priv;
> @@ -381,9 +392,14 @@ static int fimc_capture_open(struct file *file)
> if (fimc_m2m_active(fimc))
> return -EBUSY;
>
> + ret = pm_runtime_get_sync(&fimc->pdev->dev);
> + if (ret)
> + return ret;
> +
> if (++fimc->vid_cap.refcnt == 1) {
> ret = fimc_isp_subdev_init(fimc, 0);
> if (ret) {
> + pm_runtime_put_sync(&fimc->pdev->dev);
> fimc->vid_cap.refcnt--;
> return -EIO;
> }
> @@ -411,6 +427,8 @@ static int fimc_capture_close(struct file *file)
> fimc_subdev_unregister(fimc);
> }
>
> + pm_runtime_put_sync(&fimc->pdev->dev);
> +
> return 0;
> }
>
> diff --git a/drivers/media/video/s5p-fimc/fimc-core.c
> b/drivers/media/video/s5p-fimc/fimc-core.c
> index aa55066..7ca8091 100644
> --- a/drivers/media/video/s5p-fimc/fimc-core.c
> +++ b/drivers/media/video/s5p-fimc/fimc-core.c
> @@ -18,6 +18,7 @@
> #include <linux/interrupt.h>
> #include <linux/device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> #include <linux/list.h>
> #include <linux/io.h>
> #include <linux/slab.h>
> @@ -301,7 +302,6 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
> static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
> {
> struct vb2_buffer *src_vb, *dst_vb;
> - struct fimc_dev *fimc = ctx->fimc_dev;
>
> if (!ctx || !ctx->m2m_ctx)
> return;
> @@ -312,39 +312,48 @@ static void fimc_m2m_job_finish(struct fimc_ctx *ctx,
> int vb_state)
> if (src_vb && dst_vb) {
> v4l2_m2m_buf_done(src_vb, vb_state);
> v4l2_m2m_buf_done(dst_vb, vb_state);
> - v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
> + v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
> + ctx->m2m_ctx);
> }
> }
>
> /* Complete the transaction which has been scheduled for execution. */
> -static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
> +static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
> {
> struct fimc_dev *fimc = ctx->fimc_dev;
> int ret;
>
> if (!fimc_m2m_pending(fimc))
> - return;
> + return 0;
>
> fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
>
> ret = wait_event_timeout(fimc->irq_queue,
> !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
> FIMC_SHUTDOWN_TIMEOUT);
> - /*
> - * In case of a timeout the buffers are not released in the interrupt
> - * handler so return them here with the error flag set, if there are
> - * any on the queue.
> - */
> - if (ret == 0)
> - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
> +
> + return ret == 0 ? -ETIMEDOUT : ret;
> +}
> +
> +static int start_streaming(struct vb2_queue *q)
> +{
> + struct fimc_ctx *ctx = q->drv_priv;
> + int ret;
> +
> + ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
> + return ret > 0 ? 0 : ret;
> }
>
> static int stop_streaming(struct vb2_queue *q)
> {
> struct fimc_ctx *ctx = q->drv_priv;
> + int ret;
>
> - fimc_m2m_shutdown(ctx);
> + ret = fimc_m2m_shutdown(ctx);
> + if (ret == -ETIMEDOUT)
> + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
>
> + pm_runtime_put(&ctx->fimc_dev->pdev->dev);
> return 0;
> }
>
> @@ -403,7 +412,7 @@ static void fimc_capture_irq_handler(struct fimc_dev
> *fimc)
> fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
> }
>
> -static irqreturn_t fimc_isr(int irq, void *priv)
> +static irqreturn_t fimc_irq_handler(int irq, void *priv)
> {
> struct fimc_dev *fimc = priv;
> struct fimc_vid_cap *cap = &fimc->vid_cap;
> @@ -411,9 +420,17 @@ static irqreturn_t fimc_isr(int irq, void *priv)
>
> fimc_hw_clear_irq(fimc);
>
> + spin_lock(&fimc->slock);
> +
> if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
> + if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) {
> + set_bit(ST_M2M_SUSPENDED, &fimc->state);
> + wake_up(&fimc->irq_queue);
> + goto out;
> + }
> ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
> if (ctx != NULL) {
> + spin_unlock(&fimc->slock);
> fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
>
> spin_lock(&ctx->slock);
> @@ -423,21 +440,18 @@ static irqreturn_t fimc_isr(int irq, void *priv)
> }
> spin_unlock(&ctx->slock);
> }
> -
> return IRQ_HANDLED;
> - }
> -
> - spin_lock(&fimc->slock);
> -
> - if (test_bit(ST_CAPT_PEND, &fimc->state)) {
> - fimc_capture_irq_handler(fimc);
> + } else {
> + if (test_bit(ST_CAPT_PEND, &fimc->state)) {
> + fimc_capture_irq_handler(fimc);
>
> - if (cap->active_buf_cnt == 1) {
> - fimc_deactivate_capture(fimc);
> - clear_bit(ST_CAPT_STREAM, &fimc->state);
> + if (cap->active_buf_cnt == 1) {
> + fimc_deactivate_capture(fimc);
> + clear_bit(ST_CAPT_STREAM, &fimc->state);
> + }
> }
> }
> -
> +out:
> spin_unlock(&fimc->slock);
> return IRQ_HANDLED;
> }
> @@ -635,10 +649,10 @@ static void fimc_dma_run(void *priv)
> return;
>
> fimc = ctx->fimc_dev;
> -
> - spin_lock_irqsave(&ctx->slock, flags);
> + spin_lock_irqsave(&fimc->slock, flags);
> set_bit(ST_M2M_PEND, &fimc->state);
>
> + spin_lock(&ctx->slock);
> ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
> ret = fimc_prepare_config(ctx, ctx->state);
> if (ret)
> @@ -649,8 +663,6 @@ static void fimc_dma_run(void *priv)
> ctx->state |= FIMC_PARAMS;
> fimc->m2m.ctx = ctx;
> }
> -
> - spin_lock(&fimc->slock);
> fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
>
> if (ctx->state & FIMC_PARAMS) {
> @@ -680,10 +692,9 @@ static void fimc_dma_run(void *priv)
> ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
> FIMC_SRC_FMT | FIMC_DST_FMT);
> fimc_hw_activate_input_dma(fimc, true);
> - spin_unlock(&fimc->slock);
> -
> dma_unlock:
> - spin_unlock_irqrestore(&ctx->slock, flags);
> + spin_unlock(&ctx->slock);
> + spin_unlock_irqrestore(&fimc->slock, flags);
> }
>
> static void fimc_job_abort(void *priv)
> @@ -762,6 +773,7 @@ static struct vb2_ops fimc_qops = {
> .wait_prepare = fimc_unlock,
> .wait_finish = fimc_lock,
> .stop_streaming = stop_streaming,
> + .start_streaming = start_streaming,
> };
>
> static int fimc_m2m_querycap(struct file *file, void *priv,
> @@ -873,7 +885,6 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void
> *priv,
> u32 max_width, mod_x, mod_y, mask;
> int i, is_output = 0;
>
> -
> if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx))
> return -EINVAL;
> @@ -1408,9 +1419,6 @@ static int fimc_m2m_open(struct file *file)
> if (fimc->vid_cap.refcnt > 0)
> return -EBUSY;
>
> - fimc->m2m.refcnt++;
> - set_bit(ST_OUTDMA_RUN, &fimc->state);
> -
> ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> if (!ctx)
> return -ENOMEM;
> @@ -1434,6 +1442,9 @@ static int fimc_m2m_open(struct file *file)
> return err;
> }
>
> + if (fimc->m2m.refcnt++ == 0)
> + set_bit(ST_M2M_RUN, &fimc->state);
> +
> return 0;
> }
>
> @@ -1446,10 +1457,10 @@ static int fimc_m2m_release(struct file *file)
> task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
>
> v4l2_m2m_ctx_release(ctx->m2m_ctx);
> - kfree(ctx);
> - if (--fimc->m2m.refcnt <= 0)
> - clear_bit(ST_OUTDMA_RUN, &fimc->state);
>
> + if (--fimc->m2m.refcnt <= 0)
> + clear_bit(ST_M2M_RUN, &fimc->state);
> + kfree(ctx);
> return 0;
> }
>
> @@ -1561,14 +1572,12 @@ static void fimc_unregister_m2m_device(struct
> fimc_dev *fimc)
> }
> }
>
> -static void fimc_clk_release(struct fimc_dev *fimc)
> +static void fimc_clk_put(struct fimc_dev *fimc)
> {
> int i;
> for (i = 0; i < fimc->num_clocks; i++) {
> - if (fimc->clock[i]) {
> - clk_disable(fimc->clock[i]);
> + if (fimc->clock[i])
> clk_put(fimc->clock[i]);
> - }
> }
> }
>
> @@ -1577,15 +1586,50 @@ static int fimc_clk_get(struct fimc_dev *fimc)
> int i;
> for (i = 0; i < fimc->num_clocks; i++) {
> fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
> -
> - if (!IS_ERR_OR_NULL(fimc->clock[i])) {
> - clk_enable(fimc->clock[i]);
> + if (!IS_ERR_OR_NULL(fimc->clock[i]))
> continue;
> - }
> dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
> fimc_clocks[i]);
> return -ENXIO;
> }
> +
> + return 0;
> +}
> +
> +static int fimc_m2m_suspend(struct fimc_dev *fimc)
> +{
> + unsigned long flags;
> + int timeout;
> +
> + spin_lock_irqsave(&fimc->slock, flags);
> + if (!fimc_m2m_pending(fimc)) {
> + spin_unlock_irqrestore(&fimc->slock, flags);
> + return 0;
> + }
> + clear_bit(ST_M2M_SUSPENDED, &fimc->state);
> + set_bit(ST_M2M_SUSPENDING, &fimc->state);
> + spin_unlock_irqrestore(&fimc->slock, flags);
> +
> + timeout = wait_event_timeout(fimc->irq_queue,
> + test_bit(ST_M2M_SUSPENDED, &fimc->state),
> + FIMC_SHUTDOWN_TIMEOUT);
> +
> + clear_bit(ST_M2M_SUSPENDING, &fimc->state);
> + return timeout == 0 ? -EAGAIN : 0;
> +}
> +
> +static int fimc_m2m_resume(struct fimc_dev *fimc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fimc->slock, flags);
> + /* Clear for full H/W setup in first run after resume */
> + fimc->m2m.ctx = NULL;
> + spin_unlock_irqrestore(&fimc->slock, flags);
> +
> + if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))
> + fimc_m2m_job_finish(fimc->m2m.ctx,
> + VB2_BUF_STATE_ERROR);
> return 0;
> }
>
> @@ -1614,11 +1658,13 @@ static int fimc_probe(struct platform_device *pdev)
> return -ENOMEM;
>
> fimc->id = pdev->id;
> +
> fimc->variant = drv_data->variant[fimc->id];
> fimc->pdev = pdev;
> pdata = pdev->dev.platform_data;
> fimc->pdata = pdata;
> - fimc->state = ST_IDLE;
> +
> + set_bit(ST_LPM, &fimc->state);
>
> init_waitqueue_head(&fimc->irq_queue);
> spin_lock_init(&fimc->slock);
> @@ -1655,63 +1701,66 @@ static int fimc_probe(struct platform_device *pdev)
> fimc->num_clocks++;
> }
>
> - ret = fimc_clk_get(fimc);
> - if (ret)
> - goto err_regs_unmap;
> - clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
> -
> res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> if (!res) {
> dev_err(&pdev->dev, "failed to get IRQ resource\n");
> ret = -ENXIO;
> - goto err_clk;
> + goto err_regs_unmap;
> }
> fimc->irq = res->start;
>
> - fimc_hw_reset(fimc);
> + ret = fimc_clk_get(fimc);
> + if (ret)
> + goto err_regs_unmap;
> + clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
> + clk_enable(fimc->clock[CLK_BUS]);
> +
> + platform_set_drvdata(pdev, fimc);
>
> - ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
> + ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc);
> if (ret) {
> dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> goto err_clk;
> }
>
> + pm_runtime_enable(&pdev->dev);
> + ret = pm_runtime_get_sync(&pdev->dev);
> + if (ret < 0)
> + goto err_irq;
> /* Initialize contiguous memory allocator */
> - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev);
> + fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> if (IS_ERR(fimc->alloc_ctx)) {
> ret = PTR_ERR(fimc->alloc_ctx);
> - goto err_irq;
> + goto err_pm;
> }
>
> ret = fimc_register_m2m_device(fimc);
> if (ret)
> - goto err_irq;
> + goto err_alloc;
>
> /* At least one camera sensor is required to register capture node */
> if (cap_input_index >= 0) {
> ret = fimc_register_capture_device(fimc);
> if (ret)
> goto err_m2m;
> - clk_disable(fimc->clock[CLK_CAM]);
> }
> - /*
> - * Exclude the additional output DMA address registers by masking
> - * them out on HW revisions that provide extended capabilites.
> - */
> - if (fimc->variant->out_buf_count > 4)
> - fimc_hw_set_dma_seq(fimc, 0xF);
>
> dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
> __func__, fimc->id);
>
> + pm_runtime_put(&pdev->dev);
> return 0;
>
> err_m2m:
> fimc_unregister_m2m_device(fimc);
> +err_alloc:
> + vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
> +err_pm:
> + pm_runtime_put(&pdev->dev);
> err_irq:
> free_irq(fimc->irq, fimc);
> err_clk:
> - fimc_clk_release(fimc);
> + fimc_clk_put(fimc);
> err_regs_unmap:
> iounmap(fimc->regs);
> err_req_region:
> @@ -1723,27 +1772,105 @@ err_info:
> return ret;
> }
>
> -static int __devexit fimc_remove(struct platform_device *pdev)
> +static int fimc_runtime_resume(struct device *dev)
> {
> - struct fimc_dev *fimc =
> - (struct fimc_dev *)platform_get_drvdata(pdev);
> + struct fimc_dev *fimc = dev_get_drvdata(dev);
>
> - free_irq(fimc->irq, fimc);
> + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
> +
> + /* Enable clocks and perform basic initalization */
> + clk_enable(fimc->clock[CLK_GATE]);
> fimc_hw_reset(fimc);
> + if (fimc->variant->out_buf_count > 4)
> + fimc_hw_set_dma_seq(fimc, 0xF);
> +
> + /* Resume the capture or mem-to-mem device */
> + if (fimc_capture_busy(fimc))
> + return fimc_capture_resume(fimc);
> + else if (fimc_m2m_pending(fimc))
> + return fimc_m2m_resume(fimc);
> + return 0;
> +}
> +
> +static int fimc_runtime_suspend(struct device *dev)
> +{
> + struct fimc_dev *fimc = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + if (fimc_capture_busy(fimc))
> + ret = fimc_capture_suspend(fimc);
> + else
> + ret = fimc_m2m_suspend(fimc);
> + if (!ret)
> + clk_disable(fimc->clock[CLK_GATE]);
> +
> + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
> + return ret;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int fimc_resume(struct device *dev)
> +{
> + struct fimc_dev *fimc = dev_get_drvdata(dev);
> + unsigned long flags;
> +
> + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
> +
> + /* Do not resume if the device was idle before system suspend */
> + spin_lock_irqsave(&fimc->slock, flags);
> + if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
> + (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) {
> + spin_unlock_irqrestore(&fimc->slock, flags);
> + return 0;
> + }
> + fimc_hw_reset(fimc);
> + if (fimc->variant->out_buf_count > 4)
> + fimc_hw_set_dma_seq(fimc, 0xF);
> + spin_unlock_irqrestore(&fimc->slock, flags);
> +
> + if (fimc_capture_busy(fimc))
> + return fimc_capture_resume(fimc);
> +
> + return fimc_m2m_resume(fimc);
> +}
> +
> +static int fimc_suspend(struct device *dev)
> +{
> + struct fimc_dev *fimc = dev_get_drvdata(dev);
> +
> + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
> +
> + if (test_and_set_bit(ST_LPM, &fimc->state))
> + return 0;
> + if (fimc_capture_busy(fimc))
> + return fimc_capture_suspend(fimc);
Now that fimc_capture_suspend() returns -EBUSY always, is this intended
behavious or do you plan to change this later on?
Not that it'd be really easy to do this properly; the sensors, for example,
probably need a clock from the ISP and I2C before they can continue. The
OMAP 3 ISP driver does attempt to do this but doesn't handle these
dependencies.
I'm not suggesting this should be part of the patch, just thought of asking
it. :)
> + return fimc_m2m_suspend(fimc);
Does pending mean there are further images to process in a queue, or just
that driver is busy one?
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static int __devexit fimc_remove(struct platform_device *pdev)
> +{
> + struct fimc_dev *fimc = platform_get_drvdata(pdev);
> +
> + pm_runtime_disable(&pdev->dev);
> + fimc_runtime_suspend(&pdev->dev);
> + pm_runtime_set_suspended(&pdev->dev);
>
> fimc_unregister_m2m_device(fimc);
> fimc_unregister_capture_device(fimc);
>
> - fimc_clk_release(fimc);
> -
> vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
>
> + clk_disable(fimc->clock[CLK_BUS]);
> + fimc_clk_put(fimc);
> + free_irq(fimc->irq, fimc);
> iounmap(fimc->regs);
> release_resource(fimc->regs_res);
> kfree(fimc->regs_res);
> kfree(fimc);
>
> - dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
> + dev_info(&pdev->dev, "driver unloaded\n");
> return 0;
> }
>
> @@ -1906,6 +2033,11 @@ static struct platform_device_id fimc_driver_ids[] = {
> };
> MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
>
> +static const struct dev_pm_ops fimc_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
> + SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
> +};
> +
> static struct platform_driver fimc_driver = {
> .probe = fimc_probe,
> .remove = __devexit_p(fimc_remove),
> @@ -1913,6 +2045,7 @@ static struct platform_driver fimc_driver = {
> .driver = {
> .name = MODULE_NAME,
> .owner = THIS_MODULE,
> + .pm = &fimc_pm_ops,
> }
> };
>
> diff --git a/drivers/media/video/s5p-fimc/fimc-core.h
> b/drivers/media/video/s5p-fimc/fimc-core.h
> index 1f70772..e34cf3b 100644
> --- a/drivers/media/video/s5p-fimc/fimc-core.h
> +++ b/drivers/media/video/s5p-fimc/fimc-core.h
> @@ -48,22 +48,26 @@ enum {
> };
>
> enum fimc_dev_flags {
> - /* for m2m node */
> - ST_IDLE,
> - ST_OUTDMA_RUN,
> + ST_LPM,
> + /* m2m node */
> + ST_M2M_RUN,
> ST_M2M_PEND,
> - /* for capture node */
> + ST_M2M_SUSPENDING,
> + ST_M2M_SUSPENDED,
> + /* capture node */
> ST_CAPT_PEND,
> ST_CAPT_RUN,
> ST_CAPT_STREAM,
> ST_CAPT_SHUT,
> + ST_CAPT_BUSY,
> };
>
> -#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
> +#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
> #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
>
> #define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
> #define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
> +#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
>
> enum fimc_datapath {
> FIMC_CAMERA,
> @@ -644,6 +648,8 @@ void fimc_unregister_capture_device(struct fimc_dev
> *fimc);
> int fimc_sensor_sd_init(struct fimc_dev *fimc, int index);
> int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
> struct fimc_vid_buffer *fimc_vb);
> +int fimc_capture_suspend(struct fimc_dev *fimc);
> +int fimc_capture_resume(struct fimc_dev *fimc);
>
> /* Locking: the caller holds fimc->slock */
> static inline void fimc_activate_capture(struct fimc_ctx *ctx)
> diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c
> b/drivers/media/video/s5p-fimc/fimc-reg.c
> index 4893b2d..938dadf 100644
> --- a/drivers/media/video/s5p-fimc/fimc-reg.c
> +++ b/drivers/media/video/s5p-fimc/fimc-reg.c
> @@ -30,7 +30,7 @@ void fimc_hw_reset(struct fimc_dev *dev)
> cfg = readl(dev->regs + S5P_CIGCTRL);
> cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
> writel(cfg, dev->regs + S5P_CIGCTRL);
> - udelay(1000);
> + udelay(10);
Good catch. Large delays such as this one should have either used msleep()
or usleep_range(). If a smaller one does, all the better.
> cfg = readl(dev->regs + S5P_CIGCTRL);
> cfg &= ~S5P_CIGCTRL_SWRST;
> --
> 1.7.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Sakari Ailus
e-mail: [email protected] jabber/XMPP/Gmail: [email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html