Re: [PATCH v4] s5p-fimc: Add runtime PM support in the mem-to-mem driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <s.nawrocki@xxxxxxxxxxx>
> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> ---
> 
> 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 majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Sakari Ailus
e-mail: sakari.ailus@xxxxxx	jabber/XMPP/Gmail: sailus@xxxxxxxxxxxxxx
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux