summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorPhilipp Zabel <p.zabel@pengutronix.de>2014-07-11 06:36:23 -0300
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-07-22 12:01:26 -0300
commit1e17273112d60ca93e1248715039f9bf1aa09733 (patch)
treeca59f8fd697ab59942aa2de2b3925b07052dc188 /drivers/media/platform
parent14604e3a4d2c435ce4f1c95d952f464315c635f4 (diff)
downloadlinux-0-day-1e17273112d60ca93e1248715039f9bf1aa09733.tar.gz
linux-0-day-1e17273112d60ca93e1248715039f9bf1aa09733.tar.xz
[media] coda: Add runtime pm support
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.zabel@pengutronix.de> Signed-off-by: Kamil Debski <k.debski@samsung.com> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/coda.c65
1 files changed, 60 insertions, 5 deletions
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 782b09e5aa52d..131cb5f53b221 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>
@@ -2767,6 +2768,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;
@@ -2836,6 +2844,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);
@@ -2877,6 +2887,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);
@@ -3191,7 +3202,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)
@@ -3317,6 +3328,7 @@ static int coda_hw_init(struct coda_dev *dev)
err_clk_ahb:
clk_disable_unprepare(dev->clk_per);
+err_clk_per:
return ret;
}
@@ -3342,10 +3354,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 (pm_runtime_enabled(&pdev->dev) && 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,
@@ -3583,6 +3614,8 @@ static int coda_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
+ pm_runtime_enable(&pdev->dev);
+
return coda_firmware_request(dev);
}
@@ -3593,6 +3626,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);
@@ -3606,6 +3640,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,
@@ -3613,6 +3667,7 @@ static struct platform_driver coda_driver = {
.name = CODA_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(coda_dt_ids),
+ .pm = &coda_pm_ops,
},
.id_table = coda_platform_ids,
};