diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 782b09e5aa52d98bc18d4b65ae3ed7aed2a62f80..131cb5f53b221dd964d34abcfc71951a0376d944 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 @@ static int coda_open(struct file *file) 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, };