media: cedrus: Implement runtime PM
authorJernej Skrabec <jernej.skrabec@siol.net>
Sat, 9 May 2020 20:06:43 +0000 (22:06 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Thu, 11 Jun 2020 17:19:27 +0000 (19:19 +0200)
This allows the VE clocks and PLL_VE to be disabled most of the time.
A runtime PM reference is held while streaming.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/staging/media/sunxi/cedrus/cedrus.c
drivers/staging/media/sunxi/cedrus/cedrus_hw.c
drivers/staging/media/sunxi/cedrus/cedrus_hw.h
drivers/staging/media/sunxi/cedrus/cedrus_video.c

index 05a8551..bc27f94 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/pm.h>
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -551,12 +552,18 @@ static const struct of_device_id cedrus_dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
 
+static const struct dev_pm_ops cedrus_dev_pm_ops = {
+       SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
+                          cedrus_hw_resume, NULL)
+};
+
 static struct platform_driver cedrus_driver = {
        .probe          = cedrus_probe,
        .remove         = cedrus_remove,
        .driver         = {
                .name           = CEDRUS_NAME,
                .of_match_table = of_match_ptr(cedrus_dt_match),
+               .pm             = &cedrus_dev_pm_ops,
        },
 };
 module_platform_driver(cedrus_driver);
index daf5f24..1744e6f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/interrupt.h>
 #include <linux/clk.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/soc/sunxi/sunxi_sram.h>
@@ -140,6 +141,64 @@ static irqreturn_t cedrus_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+int cedrus_hw_suspend(struct device *device)
+{
+       struct cedrus_dev *dev = dev_get_drvdata(device);
+
+       reset_control_assert(dev->rstc);
+
+       clk_disable_unprepare(dev->ram_clk);
+       clk_disable_unprepare(dev->mod_clk);
+       clk_disable_unprepare(dev->ahb_clk);
+
+       return 0;
+}
+
+int cedrus_hw_resume(struct device *device)
+{
+       struct cedrus_dev *dev = dev_get_drvdata(device);
+       int ret;
+
+       ret = clk_prepare_enable(dev->ahb_clk);
+       if (ret) {
+               dev_err(dev->dev, "Failed to enable AHB clock\n");
+
+               return ret;
+       }
+
+       ret = clk_prepare_enable(dev->mod_clk);
+       if (ret) {
+               dev_err(dev->dev, "Failed to enable MOD clock\n");
+
+               goto err_ahb_clk;
+       }
+
+       ret = clk_prepare_enable(dev->ram_clk);
+       if (ret) {
+               dev_err(dev->dev, "Failed to enable RAM clock\n");
+
+               goto err_mod_clk;
+       }
+
+       ret = reset_control_reset(dev->rstc);
+       if (ret) {
+               dev_err(dev->dev, "Failed to apply reset\n");
+
+               goto err_ram_clk;
+       }
+
+       return 0;
+
+err_ram_clk:
+       clk_disable_unprepare(dev->ram_clk);
+err_mod_clk:
+       clk_disable_unprepare(dev->mod_clk);
+err_ahb_clk:
+       clk_disable_unprepare(dev->ahb_clk);
+
+       return ret;
+}
+
 int cedrus_hw_probe(struct cedrus_dev *dev)
 {
        const struct cedrus_variant *variant;
@@ -236,42 +295,17 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
                goto err_sram;
        }
 
-       ret = clk_prepare_enable(dev->ahb_clk);
-       if (ret) {
-               dev_err(dev->dev, "Failed to enable AHB clock\n");
-
-               goto err_sram;
-       }
-
-       ret = clk_prepare_enable(dev->mod_clk);
-       if (ret) {
-               dev_err(dev->dev, "Failed to enable MOD clock\n");
-
-               goto err_ahb_clk;
-       }
-
-       ret = clk_prepare_enable(dev->ram_clk);
-       if (ret) {
-               dev_err(dev->dev, "Failed to enable RAM clock\n");
-
-               goto err_mod_clk;
-       }
-
-       ret = reset_control_reset(dev->rstc);
-       if (ret) {
-               dev_err(dev->dev, "Failed to apply reset\n");
-
-               goto err_ram_clk;
+       pm_runtime_enable(dev->dev);
+       if (!pm_runtime_enabled(dev->dev)) {
+               ret = cedrus_hw_resume(dev->dev);
+               if (ret)
+                       goto err_pm;
        }
 
        return 0;
 
-err_ram_clk:
-       clk_disable_unprepare(dev->ram_clk);
-err_mod_clk:
-       clk_disable_unprepare(dev->mod_clk);
-err_ahb_clk:
-       clk_disable_unprepare(dev->ahb_clk);
+err_pm:
+       pm_runtime_disable(dev->dev);
 err_sram:
        sunxi_sram_release(dev->dev);
 err_mem:
@@ -282,11 +316,9 @@ err_mem:
 
 void cedrus_hw_remove(struct cedrus_dev *dev)
 {
-       reset_control_assert(dev->rstc);
-
-       clk_disable_unprepare(dev->ram_clk);
-       clk_disable_unprepare(dev->mod_clk);
-       clk_disable_unprepare(dev->ahb_clk);
+       pm_runtime_disable(dev->dev);
+       if (!pm_runtime_status_suspended(dev->dev))
+               cedrus_hw_suspend(dev->dev);
 
        sunxi_sram_release(dev->dev);
 
index 604ff93..45f641f 100644 (file)
@@ -22,6 +22,9 @@ void cedrus_engine_disable(struct cedrus_dev *dev);
 void cedrus_dst_format_set(struct cedrus_dev *dev,
                           struct v4l2_pix_format *fmt);
 
+int cedrus_hw_suspend(struct device *device);
+int cedrus_hw_resume(struct device *device);
+
 int cedrus_hw_probe(struct cedrus_dev *dev);
 void cedrus_hw_remove(struct cedrus_dev *dev);
 
index ed3f511..16d8230 100644 (file)
@@ -13,6 +13,8 @@
  * Marek Szyprowski, <m.szyprowski@samsung.com>
  */
 
+#include <linux/pm_runtime.h>
+
 #include <media/videobuf2-dma-contig.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -450,12 +452,24 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
                return -EINVAL;
        }
 
-       if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
-           dev->dec_ops[ctx->current_codec]->start)
-               ret = dev->dec_ops[ctx->current_codec]->start(ctx);
+       if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+               ret = pm_runtime_get_sync(dev->dev);
+               if (ret < 0)
+                       goto err_cleanup;
 
-       if (ret)
-               cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
+               if (dev->dec_ops[ctx->current_codec]->start) {
+                       ret = dev->dec_ops[ctx->current_codec]->start(ctx);
+                       if (ret)
+                               goto err_pm;
+               }
+       }
+
+       return 0;
+
+err_pm:
+       pm_runtime_put(dev->dev);
+err_cleanup:
+       cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
 
        return ret;
 }
@@ -465,9 +479,12 @@ static void cedrus_stop_streaming(struct vb2_queue *vq)
        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
        struct cedrus_dev *dev = ctx->dev;
 
-       if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
-           dev->dec_ops[ctx->current_codec]->stop)
-               dev->dec_ops[ctx->current_codec]->stop(ctx);
+       if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+               if (dev->dec_ops[ctx->current_codec]->stop)
+                       dev->dec_ops[ctx->current_codec]->stop(ctx);
+
+               pm_runtime_put(dev->dev);
+       }
 
        cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
 }