drm: mali-dp: Enable power management for the device.
authorLiviu Dudau <Liviu.Dudau@arm.com>
Wed, 22 Mar 2017 10:44:57 +0000 (10:44 +0000)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Mon, 24 Apr 2017 09:45:33 +0000 (10:45 +0100)
Enable runtime and system Power Management. Clocks are now managed
from malidp_crtc_{enable,disable} functions. Suspend-to-RAM tested
as working on Juno.

Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
drivers/gpu/drm/arm/malidp_crtc.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/arm/malidp_drv.h
drivers/gpu/drm/arm/malidp_hw.h

index f9d665550d3e7a51af064ab9797d207f778f820c..fab776c37602539af317f1d65f635332f2d6aaf6 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <linux/clk.h>
+#include <linux/pm_runtime.h>
 #include <video/videomode.h>
 
 #include "malidp_drv.h"
@@ -58,9 +59,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc)
        struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
        struct malidp_hw_device *hwdev = malidp->dev;
        struct videomode vm;
+       int err = pm_runtime_get_sync(crtc->dev->dev);
 
-       drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
+       if (err < 0) {
+               DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
+               return;
+       }
 
+       drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
        clk_prepare_enable(hwdev->pxlclk);
 
        /* We rely on firmware to set mclk to a sensible level. */
@@ -75,10 +81,16 @@ static void malidp_crtc_disable(struct drm_crtc *crtc)
 {
        struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
        struct malidp_hw_device *hwdev = malidp->dev;
+       int err;
 
        drm_crtc_vblank_off(crtc);
        hwdev->enter_config_mode(hwdev);
        clk_disable_unprepare(hwdev->pxlclk);
+
+       err = pm_runtime_put(crtc->dev->dev);
+       if (err < 0) {
+               DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err);
+       }
 }
 
 static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
index 5442baf310c86ed3b3cb25057fd232ff12ec4926..e954c22bb974596b48aa67ce8253bedae5c9fe1d 100644 (file)
 #include <linux/module.h>
 #include <linux/clk.h>
 #include <linux/component.h>
+#include <linux/console.h>
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
 #include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -91,6 +93,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
 {
        struct drm_device *drm = state->dev;
 
+       pm_runtime_get_sync(drm->dev);
+
        drm_atomic_helper_commit_modeset_disables(drm, state);
 
        drm_atomic_helper_commit_planes(drm, state, 0);
@@ -101,6 +105,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
 
        drm_atomic_helper_wait_for_vblanks(drm, state);
 
+       pm_runtime_put(drm->dev);
+
        drm_atomic_helper_cleanup_planes(drm, state);
 }
 
@@ -283,6 +289,37 @@ static bool malidp_has_sufficient_address_space(const struct resource *res,
 
 #define MAX_OUTPUT_CHANNELS    3
 
+static int malidp_runtime_pm_suspend(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct malidp_drm *malidp = drm->dev_private;
+       struct malidp_hw_device *hwdev = malidp->dev;
+
+       /* we can only suspend if the hardware is in config mode */
+       WARN_ON(!hwdev->in_config_mode(hwdev));
+
+       hwdev->pm_suspended = true;
+       clk_disable_unprepare(hwdev->mclk);
+       clk_disable_unprepare(hwdev->aclk);
+       clk_disable_unprepare(hwdev->pclk);
+
+       return 0;
+}
+
+static int malidp_runtime_pm_resume(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct malidp_drm *malidp = drm->dev_private;
+       struct malidp_hw_device *hwdev = malidp->dev;
+
+       clk_prepare_enable(hwdev->pclk);
+       clk_prepare_enable(hwdev->aclk);
+       clk_prepare_enable(hwdev->mclk);
+       hwdev->pm_suspended = false;
+
+       return 0;
+}
+
 static int malidp_bind(struct device *dev)
 {
        struct resource *res;
@@ -311,7 +348,6 @@ static int malidp_bind(struct device *dev)
        memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
        malidp->dev = hwdev;
 
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        hwdev->regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(hwdev->regs))
@@ -344,14 +380,17 @@ static int malidp_bind(struct device *dev)
                goto alloc_fail;
        }
 
-       /* Enable APB clock in order to get access to the registers */
-       clk_prepare_enable(hwdev->pclk);
-       /*
-        * Enable AXI clock and main clock so that prefetch can start once
-        * the registers are set
-        */
-       clk_prepare_enable(hwdev->aclk);
-       clk_prepare_enable(hwdev->mclk);
+       drm->dev_private = malidp;
+       dev_set_drvdata(dev, drm);
+
+       /* Enable power management */
+       pm_runtime_enable(dev);
+
+       /* Resume device to enable the clocks */
+       if (pm_runtime_enabled(dev))
+               pm_runtime_get_sync(dev);
+       else
+               malidp_runtime_pm_resume(dev);
 
        dev_id = of_match_device(malidp_drm_of_match, dev);
        if (!dev_id) {
@@ -391,14 +430,12 @@ static int malidp_bind(struct device *dev)
                out_depth = (out_depth << 8) | (output_width[i] & 0xf);
        malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);
 
-       drm->dev_private = malidp;
-       dev_set_drvdata(dev, drm);
        atomic_set(&malidp->config_valid, 0);
        init_waitqueue_head(&malidp->wq);
 
        ret = malidp_init(drm);
        if (ret < 0)
-               goto init_fail;
+               goto query_hw_fail;
 
        /* Set the CRTC's port so that the encoder component can find it */
        malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
@@ -420,6 +457,7 @@ static int malidp_bind(struct device *dev)
                DRM_ERROR("failed to initialise vblank\n");
                goto vblank_fail;
        }
+       pm_runtime_put(dev);
 
        drm_mode_config_reset(drm);
 
@@ -445,7 +483,9 @@ register_fail:
                drm_fbdev_cma_fini(malidp->fbdev);
                malidp->fbdev = NULL;
        }
+       drm_kms_helper_poll_fini(drm);
 fbdev_fail:
+       pm_runtime_get_sync(dev);
        drm_vblank_cleanup(drm);
 vblank_fail:
        malidp_se_irq_fini(drm);
@@ -457,13 +497,14 @@ bind_fail:
        of_node_put(malidp->crtc.port);
        malidp->crtc.port = NULL;
        malidp_fini(drm);
-init_fail:
+query_hw_fail:
+       pm_runtime_put(dev);
+       if (pm_runtime_enabled(dev))
+               pm_runtime_disable(dev);
+       else
+               malidp_runtime_pm_suspend(dev);
        drm->dev_private = NULL;
        dev_set_drvdata(dev, NULL);
-query_hw_fail:
-       clk_disable_unprepare(hwdev->mclk);
-       clk_disable_unprepare(hwdev->aclk);
-       clk_disable_unprepare(hwdev->pclk);
        drm_dev_unref(drm);
 alloc_fail:
        of_reserved_mem_device_release(dev);
@@ -475,7 +516,6 @@ static void malidp_unbind(struct device *dev)
 {
        struct drm_device *drm = dev_get_drvdata(dev);
        struct malidp_drm *malidp = drm->dev_private;
-       struct malidp_hw_device *hwdev = malidp->dev;
 
        drm_dev_unregister(drm);
        if (malidp->fbdev) {
@@ -483,18 +523,21 @@ static void malidp_unbind(struct device *dev)
                malidp->fbdev = NULL;
        }
        drm_kms_helper_poll_fini(drm);
+       pm_runtime_get_sync(dev);
+       drm_vblank_cleanup(drm);
        malidp_se_irq_fini(drm);
        malidp_de_irq_fini(drm);
-       drm_vblank_cleanup(drm);
        component_unbind_all(dev, drm);
        of_node_put(malidp->crtc.port);
        malidp->crtc.port = NULL;
        malidp_fini(drm);
+       pm_runtime_put(dev);
+       if (pm_runtime_enabled(dev))
+               pm_runtime_disable(dev);
+       else
+               malidp_runtime_pm_suspend(dev);
        drm->dev_private = NULL;
        dev_set_drvdata(dev, NULL);
-       clk_disable_unprepare(hwdev->mclk);
-       clk_disable_unprepare(hwdev->aclk);
-       clk_disable_unprepare(hwdev->pclk);
        drm_dev_unref(drm);
        of_reserved_mem_device_release(dev);
 }
@@ -537,11 +580,52 @@ static int malidp_platform_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int __maybe_unused malidp_pm_suspend(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct malidp_drm *malidp = drm->dev_private;
+
+       drm_kms_helper_poll_disable(drm);
+       console_lock();
+       drm_fbdev_cma_set_suspend(malidp->fbdev, 1);
+       console_unlock();
+       malidp->pm_state = drm_atomic_helper_suspend(drm);
+       if (IS_ERR(malidp->pm_state)) {
+               console_lock();
+               drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
+               console_unlock();
+               drm_kms_helper_poll_enable(drm);
+               return PTR_ERR(malidp->pm_state);
+       }
+
+       return 0;
+}
+
+static int __maybe_unused malidp_pm_resume(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct malidp_drm *malidp = drm->dev_private;
+
+       drm_atomic_helper_resume(drm, malidp->pm_state);
+       console_lock();
+       drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
+       console_unlock();
+       drm_kms_helper_poll_enable(drm);
+
+       return 0;
+}
+
+static const struct dev_pm_ops malidp_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
+       SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
+};
+
 static struct platform_driver malidp_platform_driver = {
        .probe          = malidp_platform_probe,
        .remove         = malidp_platform_remove,
        .driver = {
                .name = "mali-dp",
+               .pm = &malidp_pm_ops,
                .of_match_table = malidp_drm_of_match,
        },
 };
index dbc617c6e4ef35006fb40148b83e6776fab1ef41..cd4c04c65eadf2da7062e9dafaaf4974ad219509 100644 (file)
@@ -24,6 +24,7 @@ struct malidp_drm {
        struct drm_crtc crtc;
        wait_queue_head_t wq;
        atomic_t config_valid;
+       struct drm_atomic_state *pm_state;
 };
 
 #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
index 00974b59407df59915c5751d5845228d040f91b7..ea2dbae08990108d54cc30af63af8e5022dd74d1 100644 (file)
@@ -156,6 +156,9 @@ struct malidp_hw_device {
        u8 min_line_size;
        u16 max_line_size;
 
+       /* track the device PM state */
+       bool pm_suspended;
+
        /* size of memory used for rotating layers, up to two banks available */
        u32 rotation_memory[2];
 };
@@ -173,12 +176,14 @@ extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES];
 
 static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
 {
+       WARN_ON(hwdev->pm_suspended);
        return readl(hwdev->regs + reg);
 }
 
 static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
                                   u32 value, u32 reg)
 {
+       WARN_ON(hwdev->pm_suspended);
        writel(value, hwdev->regs + reg);
 }