Add OPP and PM support to the GR2D driver. This is required for enabling
system-wide DVFS and supporting dynamic power management using a generic
power domain.

Tested-by: Peter Geis <pgwipe...@gmail.com>
Tested-by: Nicolas Chauvet <kwiz...@gmail.com>
Signed-off-by: Dmitry Osipenko <dig...@gmail.com>
---
 drivers/gpu/drm/tegra/gr2d.c | 73 +++++++++++++++++++++++++++++++++---
 1 file changed, 67 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index f30aa86e4c9f..689554ef81a8 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -7,6 +7,9 @@
 #include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+
+#include <soc/tegra/common.h>
 
 #include "drm.h"
 #include "gem.h"
@@ -185,8 +188,15 @@ static const u32 gr2d_addr_regs[] = {
        GR2D_VA_BASE_ADDR_SB,
 };
 
+static void gr2d_pm_runtime_release(void *dev)
+{
+       pm_runtime_put(dev);
+       pm_runtime_disable(dev);
+}
+
 static int gr2d_probe(struct platform_device *pdev)
 {
+       struct tegra_core_opp_params opp_params = {};
        struct device *dev = &pdev->dev;
        struct host1x_syncpt **syncpts;
        struct gr2d *gr2d;
@@ -197,6 +207,8 @@ static int gr2d_probe(struct platform_device *pdev)
        if (!gr2d)
                return -ENOMEM;
 
+       platform_set_drvdata(pdev, gr2d);
+
        gr2d->soc = of_device_get_match_data(dev);
 
        syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
@@ -209,12 +221,23 @@ static int gr2d_probe(struct platform_device *pdev)
                return PTR_ERR(gr2d->clk);
        }
 
-       err = clk_prepare_enable(gr2d->clk);
-       if (err) {
-               dev_err(dev, "cannot turn on clock\n");
+       opp_params.init_state = true;
+
+       err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
+       if (err && err != -ENODEV)
+               return err;
+
+       pm_runtime_enable(dev);
+       err = pm_runtime_get_sync(dev);
+       if (err < 0) {
+               gr2d_pm_runtime_release(dev);
                return err;
        }
 
+       err = devm_add_action_or_reset(dev, gr2d_pm_runtime_release, dev);
+       if (err)
+               return err;
+
        INIT_LIST_HEAD(&gr2d->client.base.list);
        gr2d->client.base.ops = &gr2d_client_ops;
        gr2d->client.base.dev = dev;
@@ -229,7 +252,6 @@ static int gr2d_probe(struct platform_device *pdev)
        err = host1x_client_register(&gr2d->client.base);
        if (err < 0) {
                dev_err(dev, "failed to register host1x client: %d\n", err);
-               clk_disable_unprepare(gr2d->clk);
                return err;
        }
 
@@ -237,8 +259,6 @@ static int gr2d_probe(struct platform_device *pdev)
        for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
                set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
 
-       platform_set_drvdata(pdev, gr2d);
-
        return 0;
 }
 
@@ -254,15 +274,56 @@ static int gr2d_remove(struct platform_device *pdev)
                return err;
        }
 
+       return 0;
+}
+
+static int __maybe_unused gr2d_runtime_suspend(struct device *dev)
+{
+       struct gr2d *gr2d = dev_get_drvdata(dev);
+
        clk_disable_unprepare(gr2d->clk);
 
        return 0;
 }
 
+static int __maybe_unused gr2d_runtime_resume(struct device *dev)
+{
+       struct gr2d *gr2d = dev_get_drvdata(dev);
+       int err;
+
+       err = clk_prepare_enable(gr2d->clk);
+       if (err) {
+               dev_err(dev, "failed to enable clock: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static __maybe_unused int gr2d_suspend(struct device *dev)
+{
+       struct gr2d *gr2d = dev_get_drvdata(dev);
+       int err;
+
+       host1x_channel_stop(gr2d->channel);
+
+       err = pm_runtime_force_suspend(dev);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static const struct dev_pm_ops tegra_gr2d_pm = {
+       SET_RUNTIME_PM_OPS(gr2d_runtime_suspend, gr2d_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(gr2d_suspend, pm_runtime_force_resume)
+};
+
 struct platform_driver tegra_gr2d_driver = {
        .driver = {
                .name = "tegra-gr2d",
                .of_match_table = gr2d_match,
+               .pm = &tegra_gr2d_pm,
        },
        .probe = gr2d_probe,
        .remove = gr2d_remove,
-- 
2.29.2

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to