This allows the VE clocks and PLL_VE to be disabled most of the time.

Since the device is stateless, each frame gets a separate runtime PM
reference. Enable autosuspend so the PM callbacks are not run before and
after every frame.

Signed-off-by: Samuel Holland <sam...@sholland.org>
---

I tested this with v4l2-request-test. I don't have the setup to do
anything more complicated at the moment.

---
 drivers/staging/media/sunxi/cedrus/cedrus.c   |   7 ++
 .../staging/media/sunxi/cedrus/cedrus_hw.c    | 115 ++++++++++++------
 .../staging/media/sunxi/cedrus/cedrus_hw.h    |   3 +
 3 files changed, 88 insertions(+), 37 deletions(-)

diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c 
b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 3fad5edccd17..9aa1fc8a6c26 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -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>
@@ -474,6 +475,11 @@ static int cedrus_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct dev_pm_ops cedrus_dev_pm_ops = {
+       SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
+                          cedrus_hw_resume, NULL)
+};
+
 static const struct cedrus_variant sun4i_a10_cedrus_variant = {
        .mod_rate       = 320000000,
 };
@@ -559,6 +565,7 @@ static struct platform_driver cedrus_driver = {
        .driver         = {
                .name           = CEDRUS_NAME,
                .of_match_table = of_match_ptr(cedrus_dt_match),
+               .pm             = &cedrus_dev_pm_ops,
        },
 };
 module_platform_driver(cedrus_driver);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c 
b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index daf5f244f93b..b84814d5afe4 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -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>
@@ -63,6 +64,8 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx, enum 
cedrus_codec codec)
        if (ctx->src_fmt.width > 2048)
                reg |= VE_MODE_PIC_WIDTH_MORE_2048;
 
+       pm_runtime_get_sync(ctx->dev->dev);
+
        cedrus_write(ctx->dev, VE_MODE, reg);
 
        return 0;
@@ -71,6 +74,9 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx, enum 
cedrus_codec codec)
 void cedrus_engine_disable(struct cedrus_dev *dev)
 {
        cedrus_write(dev, VE_MODE, VE_MODE_DISABLED);
+
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
 }
 
 void cedrus_dst_format_set(struct cedrus_dev *dev,
@@ -134,12 +140,72 @@ static irqreturn_t cedrus_irq(int irq, void *data)
        else
                state = VB2_BUF_STATE_DONE;
 
+       cedrus_engine_disable(dev);
+
        v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx,
                                         state);
 
        return IRQ_HANDLED;
 }
 
+int cedrus_hw_resume(struct device *d)
+{
+       struct cedrus_dev *dev = dev_get_drvdata(d);
+       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_suspend(struct device *d)
+{
+       struct cedrus_dev *dev = dev_get_drvdata(d);
+
+       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_probe(struct cedrus_dev *dev)
 {
        const struct cedrus_variant *variant;
@@ -236,42 +302,19 @@ 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_set_autosuspend_delay(dev->dev, 1000);
+       pm_runtime_use_autosuspend(dev->dev);
+       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 +325,9 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
 
 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);
 
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h 
b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
index 604ff932fbf5..17822b470a1e 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
@@ -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_resume(struct device *dev);
+int cedrus_hw_suspend(struct device *dev);
+
 int cedrus_hw_probe(struct cedrus_dev *dev);
 void cedrus_hw_remove(struct cedrus_dev *dev);
 
-- 
2.24.1

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
To view this discussion on the web, visit 
https://groups.google.com/d/msgid/linux-sunxi/20200408010232.48432-1-samuel%40sholland.org.

Reply via email to