Commit 85f64218 authored by Liviu Dudau's avatar Liviu Dudau

drm: mali-dp: Enable power management for the device.

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: default avatarLiviu Dudau <Liviu.Dudau@arm.com>
parent 46f1d42f
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <video/videomode.h> #include <video/videomode.h>
#include "malidp_drv.h" #include "malidp_drv.h"
...@@ -58,9 +59,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc) ...@@ -58,9 +59,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc)
struct malidp_drm *malidp = crtc_to_malidp_device(crtc); struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
struct malidp_hw_device *hwdev = malidp->dev; struct malidp_hw_device *hwdev = malidp->dev;
struct videomode vm; 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); clk_prepare_enable(hwdev->pxlclk);
/* We rely on firmware to set mclk to a sensible level. */ /* We rely on firmware to set mclk to a sensible level. */
...@@ -75,10 +81,16 @@ static void malidp_crtc_disable(struct drm_crtc *crtc) ...@@ -75,10 +81,16 @@ static void malidp_crtc_disable(struct drm_crtc *crtc)
{ {
struct malidp_drm *malidp = crtc_to_malidp_device(crtc); struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
struct malidp_hw_device *hwdev = malidp->dev; struct malidp_hw_device *hwdev = malidp->dev;
int err;
drm_crtc_vblank_off(crtc); drm_crtc_vblank_off(crtc);
hwdev->enter_config_mode(hwdev); hwdev->enter_config_mode(hwdev);
clk_disable_unprepare(hwdev->pxlclk); 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, static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
......
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/console.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_graph.h> #include <linux/of_graph.h>
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
...@@ -91,6 +93,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) ...@@ -91,6 +93,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
{ {
struct drm_device *drm = state->dev; struct drm_device *drm = state->dev;
pm_runtime_get_sync(drm->dev);
drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_disables(drm, state);
drm_atomic_helper_commit_planes(drm, state, 0); drm_atomic_helper_commit_planes(drm, state, 0);
...@@ -101,6 +105,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) ...@@ -101,6 +105,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
drm_atomic_helper_wait_for_vblanks(drm, state); drm_atomic_helper_wait_for_vblanks(drm, state);
pm_runtime_put(drm->dev);
drm_atomic_helper_cleanup_planes(drm, state); drm_atomic_helper_cleanup_planes(drm, state);
} }
...@@ -283,6 +289,37 @@ static bool malidp_has_sufficient_address_space(const struct resource *res, ...@@ -283,6 +289,37 @@ static bool malidp_has_sufficient_address_space(const struct resource *res,
#define MAX_OUTPUT_CHANNELS 3 #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) static int malidp_bind(struct device *dev)
{ {
struct resource *res; struct resource *res;
...@@ -311,7 +348,6 @@ static int malidp_bind(struct device *dev) ...@@ -311,7 +348,6 @@ static int malidp_bind(struct device *dev)
memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev)); memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
malidp->dev = hwdev; malidp->dev = hwdev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hwdev->regs = devm_ioremap_resource(dev, res); hwdev->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hwdev->regs)) if (IS_ERR(hwdev->regs))
...@@ -344,14 +380,17 @@ static int malidp_bind(struct device *dev) ...@@ -344,14 +380,17 @@ static int malidp_bind(struct device *dev)
goto alloc_fail; goto alloc_fail;
} }
/* Enable APB clock in order to get access to the registers */ drm->dev_private = malidp;
clk_prepare_enable(hwdev->pclk); dev_set_drvdata(dev, drm);
/*
* Enable AXI clock and main clock so that prefetch can start once /* Enable power management */
* the registers are set pm_runtime_enable(dev);
*/
clk_prepare_enable(hwdev->aclk); /* Resume device to enable the clocks */
clk_prepare_enable(hwdev->mclk); 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); dev_id = of_match_device(malidp_drm_of_match, dev);
if (!dev_id) { if (!dev_id) {
...@@ -391,14 +430,12 @@ static int malidp_bind(struct device *dev) ...@@ -391,14 +430,12 @@ static int malidp_bind(struct device *dev)
out_depth = (out_depth << 8) | (output_width[i] & 0xf); out_depth = (out_depth << 8) | (output_width[i] & 0xf);
malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base); 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); atomic_set(&malidp->config_valid, 0);
init_waitqueue_head(&malidp->wq); init_waitqueue_head(&malidp->wq);
ret = malidp_init(drm); ret = malidp_init(drm);
if (ret < 0) if (ret < 0)
goto init_fail; goto query_hw_fail;
/* Set the CRTC's port so that the encoder component can find it */ /* 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); malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
...@@ -420,6 +457,7 @@ static int malidp_bind(struct device *dev) ...@@ -420,6 +457,7 @@ static int malidp_bind(struct device *dev)
DRM_ERROR("failed to initialise vblank\n"); DRM_ERROR("failed to initialise vblank\n");
goto vblank_fail; goto vblank_fail;
} }
pm_runtime_put(dev);
drm_mode_config_reset(drm); drm_mode_config_reset(drm);
...@@ -445,7 +483,9 @@ static int malidp_bind(struct device *dev) ...@@ -445,7 +483,9 @@ static int malidp_bind(struct device *dev)
drm_fbdev_cma_fini(malidp->fbdev); drm_fbdev_cma_fini(malidp->fbdev);
malidp->fbdev = NULL; malidp->fbdev = NULL;
} }
drm_kms_helper_poll_fini(drm);
fbdev_fail: fbdev_fail:
pm_runtime_get_sync(dev);
drm_vblank_cleanup(drm); drm_vblank_cleanup(drm);
vblank_fail: vblank_fail:
malidp_se_irq_fini(drm); malidp_se_irq_fini(drm);
...@@ -457,13 +497,14 @@ static int malidp_bind(struct device *dev) ...@@ -457,13 +497,14 @@ static int malidp_bind(struct device *dev)
of_node_put(malidp->crtc.port); of_node_put(malidp->crtc.port);
malidp->crtc.port = NULL; malidp->crtc.port = NULL;
malidp_fini(drm); 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; drm->dev_private = NULL;
dev_set_drvdata(dev, 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); drm_dev_unref(drm);
alloc_fail: alloc_fail:
of_reserved_mem_device_release(dev); of_reserved_mem_device_release(dev);
...@@ -475,7 +516,6 @@ static void malidp_unbind(struct device *dev) ...@@ -475,7 +516,6 @@ static void malidp_unbind(struct device *dev)
{ {
struct drm_device *drm = dev_get_drvdata(dev); struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm->dev_private; struct malidp_drm *malidp = drm->dev_private;
struct malidp_hw_device *hwdev = malidp->dev;
drm_dev_unregister(drm); drm_dev_unregister(drm);
if (malidp->fbdev) { if (malidp->fbdev) {
...@@ -483,18 +523,21 @@ static void malidp_unbind(struct device *dev) ...@@ -483,18 +523,21 @@ static void malidp_unbind(struct device *dev)
malidp->fbdev = NULL; malidp->fbdev = NULL;
} }
drm_kms_helper_poll_fini(drm); drm_kms_helper_poll_fini(drm);
pm_runtime_get_sync(dev);
drm_vblank_cleanup(drm);
malidp_se_irq_fini(drm); malidp_se_irq_fini(drm);
malidp_de_irq_fini(drm); malidp_de_irq_fini(drm);
drm_vblank_cleanup(drm);
component_unbind_all(dev, drm); component_unbind_all(dev, drm);
of_node_put(malidp->crtc.port); of_node_put(malidp->crtc.port);
malidp->crtc.port = NULL; malidp->crtc.port = NULL;
malidp_fini(drm); 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; drm->dev_private = NULL;
dev_set_drvdata(dev, 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); drm_dev_unref(drm);
of_reserved_mem_device_release(dev); of_reserved_mem_device_release(dev);
} }
...@@ -537,11 +580,52 @@ static int malidp_platform_remove(struct platform_device *pdev) ...@@ -537,11 +580,52 @@ static int malidp_platform_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver malidp_platform_driver = {
.probe = malidp_platform_probe, .probe = malidp_platform_probe,
.remove = malidp_platform_remove, .remove = malidp_platform_remove,
.driver = { .driver = {
.name = "mali-dp", .name = "mali-dp",
.pm = &malidp_pm_ops,
.of_match_table = malidp_drm_of_match, .of_match_table = malidp_drm_of_match,
}, },
}; };
......
...@@ -24,6 +24,7 @@ struct malidp_drm { ...@@ -24,6 +24,7 @@ struct malidp_drm {
struct drm_crtc crtc; struct drm_crtc crtc;
wait_queue_head_t wq; wait_queue_head_t wq;
atomic_t config_valid; atomic_t config_valid;
struct drm_atomic_state *pm_state;
}; };
#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
......
...@@ -156,6 +156,9 @@ struct malidp_hw_device { ...@@ -156,6 +156,9 @@ struct malidp_hw_device {
u8 min_line_size; u8 min_line_size;
u16 max_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 */ /* size of memory used for rotating layers, up to two banks available */
u32 rotation_memory[2]; u32 rotation_memory[2];
}; };
...@@ -173,12 +176,14 @@ extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES]; ...@@ -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) static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
{ {
WARN_ON(hwdev->pm_suspended);
return readl(hwdev->regs + reg); return readl(hwdev->regs + reg);
} }
static inline void malidp_hw_write(struct malidp_hw_device *hwdev, static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
u32 value, u32 reg) u32 value, u32 reg)
{ {
WARN_ON(hwdev->pm_suspended);
writel(value, hwdev->regs + reg); writel(value, hwdev->regs + reg);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment