[PATCH 12/30] [media] coda: Add runtime pm support

2014-06-13 Thread Philipp Zabel
This patch allows to use the runtime pm and generic pm domain frameworks
to completely gate power to the VPU if it is unused. This functionality
is available on i.MX6.

Signed-off-by: Philipp Zabel p.za...@pengutronix.de
---
 drivers/media/platform/coda.c | 65 +++
 1 file changed, 60 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 8321243..f39f693 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -22,6 +22,7 @@
 #include linux/module.h
 #include linux/of_device.h
 #include linux/platform_device.h
+#include linux/pm_runtime.h
 #include linux/slab.h
 #include linux/videodev2.h
 #include linux/of.h
@@ -2820,6 +2821,13 @@ static int coda_open(struct file *file)
ctx-reg_idx = idx;
}
 
+   /* Power up and upload firmware if necessary */
+   ret = pm_runtime_get_sync(dev-plat_dev-dev);
+   if (ret  0) {
+   v4l2_err(dev-v4l2_dev, failed to power up: %d\n, ret);
+   goto err_pm_get;
+   }
+
ret = clk_prepare_enable(dev-clk_per);
if (ret)
goto err_clk_per;
@@ -2889,6 +2897,8 @@ err_ctx_init:
 err_clk_ahb:
clk_disable_unprepare(dev-clk_per);
 err_clk_per:
+   pm_runtime_put_sync(dev-plat_dev-dev);
+err_pm_get:
v4l2_fh_del(ctx-fh);
v4l2_fh_exit(ctx-fh);
clear_bit(ctx-idx, dev-instance_mask);
@@ -2930,6 +2940,7 @@ static int coda_release(struct file *file)
v4l2_ctrl_handler_free(ctx-ctrls);
clk_disable_unprepare(dev-clk_ahb);
clk_disable_unprepare(dev-clk_per);
+   pm_runtime_put_sync(dev-plat_dev-dev);
v4l2_fh_del(ctx-fh);
v4l2_fh_exit(ctx-fh);
clear_bit(ctx-idx, dev-instance_mask);
@@ -3243,7 +3254,7 @@ static int coda_hw_init(struct coda_dev *dev)
 
ret = clk_prepare_enable(dev-clk_per);
if (ret)
-   return ret;
+   goto err_clk_per;
 
ret = clk_prepare_enable(dev-clk_ahb);
if (ret)
@@ -3369,6 +3380,7 @@ static int coda_hw_init(struct coda_dev *dev)
 
 err_clk_ahb:
clk_disable_unprepare(dev-clk_per);
+err_clk_per:
return ret;
 }
 
@@ -3394,10 +3406,29 @@ static void coda_fw_callback(const struct firmware *fw, 
void *context)
memcpy(dev-codebuf.vaddr, fw-data, fw-size);
release_firmware(fw);
 
-   ret = coda_hw_init(dev);
-   if (ret) {
-   v4l2_err(dev-v4l2_dev, HW initialization failed\n);
-   return;
+   if (IS_ENABLED(CONFIG_PM_RUNTIME)  pdev-dev.pm_domain) {
+   /*
+* Enabling power temporarily will cause coda_hw_init to be
+* called via coda_runtime_resume by the pm domain.
+*/
+   ret = pm_runtime_get_sync(dev-plat_dev-dev);
+   if (ret  0) {
+   v4l2_err(dev-v4l2_dev, failed to power on: %d\n,
+ret);
+   return;
+   }
+
+   pm_runtime_put_sync(dev-plat_dev-dev);
+   } else {
+   /*
+* If runtime pm is disabled or pm_domain is not set,
+* initialize once manually.
+*/
+   ret = coda_hw_init(dev);
+   if (ret  0) {
+   v4l2_err(dev-v4l2_dev, HW initialization failed\n);
+   return;
+   }
}
 
dev-vfd.fops   = coda_fops,
@@ -3635,6 +3666,8 @@ static int coda_probe(struct platform_device *pdev)
 
platform_set_drvdata(pdev, dev);
 
+   pm_runtime_enable(pdev-dev);
+
return coda_firmware_request(dev);
 }
 
@@ -3645,6 +3678,7 @@ static int coda_remove(struct platform_device *pdev)
video_unregister_device(dev-vfd);
if (dev-m2m_dev)
v4l2_m2m_release(dev-m2m_dev);
+   pm_runtime_disable(pdev-dev);
if (dev-alloc_ctx)
vb2_dma_contig_cleanup_ctx(dev-alloc_ctx);
v4l2_device_unregister(dev-v4l2_dev);
@@ -3658,6 +3692,26 @@ static int coda_remove(struct platform_device *pdev)
return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int coda_runtime_resume(struct device *dev)
+{
+   struct coda_dev *cdev = dev_get_drvdata(dev);
+   int ret = 0;
+
+   if (dev-pm_domain) {
+   ret = coda_hw_init(cdev);
+   if (ret)
+   v4l2_err(cdev-v4l2_dev, HW initialization failed\n);
+   }
+
+   return ret;
+}
+#endif
+
+static const struct dev_pm_ops coda_pm_ops = {
+   SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
+};
+
 static struct platform_driver coda_driver = {
.probe  = coda_probe,
.remove = coda_remove,
@@ -3665,6 +3719,7 @@ static struct platform_driver coda_driver = {
.name   = CODA_NAME,
.owner  = THIS_MODULE,
.of_match_table = 

Re: [PATCH 12/30] [media] coda: Add runtime pm support

2014-06-13 Thread Sylwester Nawrocki
Hello Philipp,

On 13/06/14 18:08, Philipp Zabel wrote:
 This patch allows to use the runtime pm and generic pm domain frameworks
 to completely gate power to the VPU if it is unused. This functionality
 is available on i.MX6.
 
 Signed-off-by: Philipp Zabel p.za...@pengutronix.de
 ---
  drivers/media/platform/coda.c | 65 
 +++
  1 file changed, 60 insertions(+), 5 deletions(-)
 
 diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
 index 8321243..f39f693 100644
 --- a/drivers/media/platform/coda.c
 +++ b/drivers/media/platform/coda.c
 @@ -22,6 +22,7 @@
  #include linux/module.h
  #include linux/of_device.h
  #include linux/platform_device.h
 +#include linux/pm_runtime.h
  #include linux/slab.h
  #include linux/videodev2.h
  #include linux/of.h
 @@ -2820,6 +2821,13 @@ static int coda_open(struct file *file)
   ctx-reg_idx = idx;
   }
  
 + /* Power up and upload firmware if necessary */
 + ret = pm_runtime_get_sync(dev-plat_dev-dev);
 + if (ret  0) {
 + v4l2_err(dev-v4l2_dev, failed to power up: %d\n, ret);
 + goto err_pm_get;
 + }
 +
   ret = clk_prepare_enable(dev-clk_per);
   if (ret)
   goto err_clk_per;
 @@ -2889,6 +2897,8 @@ err_ctx_init:
  err_clk_ahb:
   clk_disable_unprepare(dev-clk_per);
  err_clk_per:
 + pm_runtime_put_sync(dev-plat_dev-dev);
 +err_pm_get:
   v4l2_fh_del(ctx-fh);
   v4l2_fh_exit(ctx-fh);
   clear_bit(ctx-idx, dev-instance_mask);
 @@ -2930,6 +2940,7 @@ static int coda_release(struct file *file)
   v4l2_ctrl_handler_free(ctx-ctrls);
   clk_disable_unprepare(dev-clk_ahb);
   clk_disable_unprepare(dev-clk_per);
 + pm_runtime_put_sync(dev-plat_dev-dev);
   v4l2_fh_del(ctx-fh);
   v4l2_fh_exit(ctx-fh);
   clear_bit(ctx-idx, dev-instance_mask);
 @@ -3243,7 +3254,7 @@ static int coda_hw_init(struct coda_dev *dev)
  
   ret = clk_prepare_enable(dev-clk_per);
   if (ret)
 - return ret;
 + goto err_clk_per;
  
   ret = clk_prepare_enable(dev-clk_ahb);
   if (ret)
 @@ -3369,6 +3380,7 @@ static int coda_hw_init(struct coda_dev *dev)
  
  err_clk_ahb:
   clk_disable_unprepare(dev-clk_per);
 +err_clk_per:
   return ret;
  }
  
 @@ -3394,10 +3406,29 @@ static void coda_fw_callback(const struct firmware 
 *fw, void *context)
   memcpy(dev-codebuf.vaddr, fw-data, fw-size);
   release_firmware(fw);
  
 - ret = coda_hw_init(dev);
 - if (ret) {
 - v4l2_err(dev-v4l2_dev, HW initialization failed\n);
 - return;
 + if (IS_ENABLED(CONFIG_PM_RUNTIME)  pdev-dev.pm_domain) {

How about using the pm_runtime_enabled() helper ? Also why do you need to
be checking dev.pm_domain here and in the resume() callback ? Couldn't it 
be done unconditionally ? Why the driver needs to care about the PM domain
existence ?

 + /*
 +  * Enabling power temporarily will cause coda_hw_init to be
 +  * called via coda_runtime_resume by the pm domain.
 +  */
 + ret = pm_runtime_get_sync(dev-plat_dev-dev);
 + if (ret  0) {
 + v4l2_err(dev-v4l2_dev, failed to power on: %d\n,
 +  ret);
 + return;
 + }
 +
 + pm_runtime_put_sync(dev-plat_dev-dev);
 + } else {
 + /*
 +  * If runtime pm is disabled or pm_domain is not set,
 +  * initialize once manually.
 +  */
 + ret = coda_hw_init(dev);
 + if (ret  0) {
 + v4l2_err(dev-v4l2_dev, HW initialization failed\n);
 + return;
 + }
   }
  
   dev-vfd.fops   = coda_fops,
 @@ -3635,6 +3666,8 @@ static int coda_probe(struct platform_device *pdev)
  
   platform_set_drvdata(pdev, dev);
  
 + pm_runtime_enable(pdev-dev);
 +
   return coda_firmware_request(dev);
  }
  
 @@ -3645,6 +3678,7 @@ static int coda_remove(struct platform_device *pdev)
   video_unregister_device(dev-vfd);
   if (dev-m2m_dev)
   v4l2_m2m_release(dev-m2m_dev);
 + pm_runtime_disable(pdev-dev);
   if (dev-alloc_ctx)
   vb2_dma_contig_cleanup_ctx(dev-alloc_ctx);
   v4l2_device_unregister(dev-v4l2_dev);
 @@ -3658,6 +3692,26 @@ static int coda_remove(struct platform_device *pdev)
   return 0;
  }
  
 +#ifdef CONFIG_PM_RUNTIME
 +static int coda_runtime_resume(struct device *dev)
 +{
 + struct coda_dev *cdev = dev_get_drvdata(dev);
 + int ret = 0;
 +
 + if (dev-pm_domain) {
 + ret = coda_hw_init(cdev);
 + if (ret)
 + v4l2_err(cdev-v4l2_dev, HW initialization failed\n);
 + }
 +
 + return ret;
 +}
 +#endif

--
Regards,
Sylwester
--
To unsubscribe from this list: send the line unsubscribe linux-media in
the body of a message to 

Re: [PATCH 12/30] [media] coda: Add runtime pm support

2014-06-13 Thread Philipp Zabel
Hi Sylwester,

On Fri, Jun 13, 2014 at 06:56:16PM +0200, Sylwester Nawrocki wrote:
[...]
  @@ -3394,10 +3406,29 @@ static void coda_fw_callback(const struct firmware 
  *fw, void *context)
  memcpy(dev-codebuf.vaddr, fw-data, fw-size);
  release_firmware(fw);
   
  -   ret = coda_hw_init(dev);
  -   if (ret) {
  -   v4l2_err(dev-v4l2_dev, HW initialization failed\n);
  -   return;
  +   if (IS_ENABLED(CONFIG_PM_RUNTIME)  pdev-dev.pm_domain) {
 
 How about using the pm_runtime_enabled() helper ? Also why do you need to
 be checking dev.pm_domain here and in the resume() callback ? Couldn't it 
 be done unconditionally ? Why the driver needs to care about the PM domain
 existence ?

Thank you for the hint, pm_runtime_enabled() is what I want here.

The idea with the pm_domain check was that without an associated pm_domain
there is no need to do the hardware initialization over and over again.
So if PM_RUNTIME is enabled, but no pm_domain is associated with the device,
we call hw_init only once, and not on every runtime_resume.
The hardware initialization on coda mostly consists of a 4KiB firmware upload
into the code SRAM via an upload register, and a reset of the DSP processor.

regards
Philipp
--
To unsubscribe from this list: send the line unsubscribe linux-media in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html