diff options
author | Liviu Dudau <Liviu.Dudau@arm.com> | 2017-03-22 06:44:57 -0400 |
---|---|---|
committer | Liviu Dudau <Liviu.Dudau@arm.com> | 2017-04-24 05:45:33 -0400 |
commit | 85f6421889eca68ceb0a0403c4c00b2eaf3c16e0 (patch) | |
tree | d010c422ee0fbe187dc7c42e1326d66ce910e412 | |
parent | 46f1d42f273836a3b8840637b9ee3d98a528ffd2 (diff) |
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: Liviu Dudau <Liviu.Dudau@arm.com>
-rw-r--r-- | drivers/gpu/drm/arm/malidp_crtc.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/malidp_drv.c | 128 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/malidp_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/malidp_hw.h | 5 |
4 files changed, 125 insertions, 23 deletions
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c index f9d665550d3e..fab776c37602 100644 --- a/drivers/gpu/drm/arm/malidp_crtc.c +++ b/drivers/gpu/drm/arm/malidp_crtc.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <drm/drm_crtc.h> | 16 | #include <drm/drm_crtc.h> |
17 | #include <drm/drm_crtc_helper.h> | 17 | #include <drm/drm_crtc_helper.h> |
18 | #include <linux/clk.h> | 18 | #include <linux/clk.h> |
19 | #include <linux/pm_runtime.h> | ||
19 | #include <video/videomode.h> | 20 | #include <video/videomode.h> |
20 | 21 | ||
21 | #include "malidp_drv.h" | 22 | #include "malidp_drv.h" |
@@ -58,9 +59,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc) | |||
58 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | 59 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); |
59 | struct malidp_hw_device *hwdev = malidp->dev; | 60 | struct malidp_hw_device *hwdev = malidp->dev; |
60 | struct videomode vm; | 61 | struct videomode vm; |
62 | int err = pm_runtime_get_sync(crtc->dev->dev); | ||
61 | 63 | ||
62 | drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm); | 64 | if (err < 0) { |
65 | DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err); | ||
66 | return; | ||
67 | } | ||
63 | 68 | ||
69 | drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm); | ||
64 | clk_prepare_enable(hwdev->pxlclk); | 70 | clk_prepare_enable(hwdev->pxlclk); |
65 | 71 | ||
66 | /* We rely on firmware to set mclk to a sensible level. */ | 72 | /* 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 | { | 81 | { |
76 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | 82 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); |
77 | struct malidp_hw_device *hwdev = malidp->dev; | 83 | struct malidp_hw_device *hwdev = malidp->dev; |
84 | int err; | ||
78 | 85 | ||
79 | drm_crtc_vblank_off(crtc); | 86 | drm_crtc_vblank_off(crtc); |
80 | hwdev->enter_config_mode(hwdev); | 87 | hwdev->enter_config_mode(hwdev); |
81 | clk_disable_unprepare(hwdev->pxlclk); | 88 | clk_disable_unprepare(hwdev->pxlclk); |
89 | |||
90 | err = pm_runtime_put(crtc->dev->dev); | ||
91 | if (err < 0) { | ||
92 | DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err); | ||
93 | } | ||
82 | } | 94 | } |
83 | 95 | ||
84 | static int malidp_crtc_atomic_check(struct drm_crtc *crtc, | 96 | static int malidp_crtc_atomic_check(struct drm_crtc *crtc, |
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 5442baf310c8..e954c22bb974 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c | |||
@@ -13,9 +13,11 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/clk.h> | 14 | #include <linux/clk.h> |
15 | #include <linux/component.h> | 15 | #include <linux/component.h> |
16 | #include <linux/console.h> | ||
16 | #include <linux/of_device.h> | 17 | #include <linux/of_device.h> |
17 | #include <linux/of_graph.h> | 18 | #include <linux/of_graph.h> |
18 | #include <linux/of_reserved_mem.h> | 19 | #include <linux/of_reserved_mem.h> |
20 | #include <linux/pm_runtime.h> | ||
19 | 21 | ||
20 | #include <drm/drmP.h> | 22 | #include <drm/drmP.h> |
21 | #include <drm/drm_atomic.h> | 23 | #include <drm/drm_atomic.h> |
@@ -91,6 +93,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) | |||
91 | { | 93 | { |
92 | struct drm_device *drm = state->dev; | 94 | struct drm_device *drm = state->dev; |
93 | 95 | ||
96 | pm_runtime_get_sync(drm->dev); | ||
97 | |||
94 | drm_atomic_helper_commit_modeset_disables(drm, state); | 98 | drm_atomic_helper_commit_modeset_disables(drm, state); |
95 | 99 | ||
96 | drm_atomic_helper_commit_planes(drm, state, 0); | 100 | drm_atomic_helper_commit_planes(drm, state, 0); |
@@ -101,6 +105,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) | |||
101 | 105 | ||
102 | drm_atomic_helper_wait_for_vblanks(drm, state); | 106 | drm_atomic_helper_wait_for_vblanks(drm, state); |
103 | 107 | ||
108 | pm_runtime_put(drm->dev); | ||
109 | |||
104 | drm_atomic_helper_cleanup_planes(drm, state); | 110 | drm_atomic_helper_cleanup_planes(drm, state); |
105 | } | 111 | } |
106 | 112 | ||
@@ -283,6 +289,37 @@ static bool malidp_has_sufficient_address_space(const struct resource *res, | |||
283 | 289 | ||
284 | #define MAX_OUTPUT_CHANNELS 3 | 290 | #define MAX_OUTPUT_CHANNELS 3 |
285 | 291 | ||
292 | static int malidp_runtime_pm_suspend(struct device *dev) | ||
293 | { | ||
294 | struct drm_device *drm = dev_get_drvdata(dev); | ||
295 | struct malidp_drm *malidp = drm->dev_private; | ||
296 | struct malidp_hw_device *hwdev = malidp->dev; | ||
297 | |||
298 | /* we can only suspend if the hardware is in config mode */ | ||
299 | WARN_ON(!hwdev->in_config_mode(hwdev)); | ||
300 | |||
301 | hwdev->pm_suspended = true; | ||
302 | clk_disable_unprepare(hwdev->mclk); | ||
303 | clk_disable_unprepare(hwdev->aclk); | ||
304 | clk_disable_unprepare(hwdev->pclk); | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int malidp_runtime_pm_resume(struct device *dev) | ||
310 | { | ||
311 | struct drm_device *drm = dev_get_drvdata(dev); | ||
312 | struct malidp_drm *malidp = drm->dev_private; | ||
313 | struct malidp_hw_device *hwdev = malidp->dev; | ||
314 | |||
315 | clk_prepare_enable(hwdev->pclk); | ||
316 | clk_prepare_enable(hwdev->aclk); | ||
317 | clk_prepare_enable(hwdev->mclk); | ||
318 | hwdev->pm_suspended = false; | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
286 | static int malidp_bind(struct device *dev) | 323 | static int malidp_bind(struct device *dev) |
287 | { | 324 | { |
288 | struct resource *res; | 325 | struct resource *res; |
@@ -311,7 +348,6 @@ static int malidp_bind(struct device *dev) | |||
311 | memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev)); | 348 | memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev)); |
312 | malidp->dev = hwdev; | 349 | malidp->dev = hwdev; |
313 | 350 | ||
314 | |||
315 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 351 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
316 | hwdev->regs = devm_ioremap_resource(dev, res); | 352 | hwdev->regs = devm_ioremap_resource(dev, res); |
317 | if (IS_ERR(hwdev->regs)) | 353 | if (IS_ERR(hwdev->regs)) |
@@ -344,14 +380,17 @@ static int malidp_bind(struct device *dev) | |||
344 | goto alloc_fail; | 380 | goto alloc_fail; |
345 | } | 381 | } |
346 | 382 | ||
347 | /* Enable APB clock in order to get access to the registers */ | 383 | drm->dev_private = malidp; |
348 | clk_prepare_enable(hwdev->pclk); | 384 | dev_set_drvdata(dev, drm); |
349 | /* | 385 | |
350 | * Enable AXI clock and main clock so that prefetch can start once | 386 | /* Enable power management */ |
351 | * the registers are set | 387 | pm_runtime_enable(dev); |
352 | */ | 388 | |
353 | clk_prepare_enable(hwdev->aclk); | 389 | /* Resume device to enable the clocks */ |
354 | clk_prepare_enable(hwdev->mclk); | 390 | if (pm_runtime_enabled(dev)) |
391 | pm_runtime_get_sync(dev); | ||
392 | else | ||
393 | malidp_runtime_pm_resume(dev); | ||
355 | 394 | ||
356 | dev_id = of_match_device(malidp_drm_of_match, dev); | 395 | dev_id = of_match_device(malidp_drm_of_match, dev); |
357 | if (!dev_id) { | 396 | if (!dev_id) { |
@@ -391,14 +430,12 @@ static int malidp_bind(struct device *dev) | |||
391 | out_depth = (out_depth << 8) | (output_width[i] & 0xf); | 430 | out_depth = (out_depth << 8) | (output_width[i] & 0xf); |
392 | malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base); | 431 | malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base); |
393 | 432 | ||
394 | drm->dev_private = malidp; | ||
395 | dev_set_drvdata(dev, drm); | ||
396 | atomic_set(&malidp->config_valid, 0); | 433 | atomic_set(&malidp->config_valid, 0); |
397 | init_waitqueue_head(&malidp->wq); | 434 | init_waitqueue_head(&malidp->wq); |
398 | 435 | ||
399 | ret = malidp_init(drm); | 436 | ret = malidp_init(drm); |
400 | if (ret < 0) | 437 | if (ret < 0) |
401 | goto init_fail; | 438 | goto query_hw_fail; |
402 | 439 | ||
403 | /* Set the CRTC's port so that the encoder component can find it */ | 440 | /* Set the CRTC's port so that the encoder component can find it */ |
404 | malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); | 441 | 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 | DRM_ERROR("failed to initialise vblank\n"); | 457 | DRM_ERROR("failed to initialise vblank\n"); |
421 | goto vblank_fail; | 458 | goto vblank_fail; |
422 | } | 459 | } |
460 | pm_runtime_put(dev); | ||
423 | 461 | ||
424 | drm_mode_config_reset(drm); | 462 | drm_mode_config_reset(drm); |
425 | 463 | ||
@@ -445,7 +483,9 @@ register_fail: | |||
445 | drm_fbdev_cma_fini(malidp->fbdev); | 483 | drm_fbdev_cma_fini(malidp->fbdev); |
446 | malidp->fbdev = NULL; | 484 | malidp->fbdev = NULL; |
447 | } | 485 | } |
486 | drm_kms_helper_poll_fini(drm); | ||
448 | fbdev_fail: | 487 | fbdev_fail: |
488 | pm_runtime_get_sync(dev); | ||
449 | drm_vblank_cleanup(drm); | 489 | drm_vblank_cleanup(drm); |
450 | vblank_fail: | 490 | vblank_fail: |
451 | malidp_se_irq_fini(drm); | 491 | malidp_se_irq_fini(drm); |
@@ -457,13 +497,14 @@ bind_fail: | |||
457 | of_node_put(malidp->crtc.port); | 497 | of_node_put(malidp->crtc.port); |
458 | malidp->crtc.port = NULL; | 498 | malidp->crtc.port = NULL; |
459 | malidp_fini(drm); | 499 | malidp_fini(drm); |
460 | init_fail: | 500 | query_hw_fail: |
501 | pm_runtime_put(dev); | ||
502 | if (pm_runtime_enabled(dev)) | ||
503 | pm_runtime_disable(dev); | ||
504 | else | ||
505 | malidp_runtime_pm_suspend(dev); | ||
461 | drm->dev_private = NULL; | 506 | drm->dev_private = NULL; |
462 | dev_set_drvdata(dev, NULL); | 507 | dev_set_drvdata(dev, NULL); |
463 | query_hw_fail: | ||
464 | clk_disable_unprepare(hwdev->mclk); | ||
465 | clk_disable_unprepare(hwdev->aclk); | ||
466 | clk_disable_unprepare(hwdev->pclk); | ||
467 | drm_dev_unref(drm); | 508 | drm_dev_unref(drm); |
468 | alloc_fail: | 509 | alloc_fail: |
469 | of_reserved_mem_device_release(dev); | 510 | of_reserved_mem_device_release(dev); |
@@ -475,7 +516,6 @@ static void malidp_unbind(struct device *dev) | |||
475 | { | 516 | { |
476 | struct drm_device *drm = dev_get_drvdata(dev); | 517 | struct drm_device *drm = dev_get_drvdata(dev); |
477 | struct malidp_drm *malidp = drm->dev_private; | 518 | struct malidp_drm *malidp = drm->dev_private; |
478 | struct malidp_hw_device *hwdev = malidp->dev; | ||
479 | 519 | ||
480 | drm_dev_unregister(drm); | 520 | drm_dev_unregister(drm); |
481 | if (malidp->fbdev) { | 521 | if (malidp->fbdev) { |
@@ -483,18 +523,21 @@ static void malidp_unbind(struct device *dev) | |||
483 | malidp->fbdev = NULL; | 523 | malidp->fbdev = NULL; |
484 | } | 524 | } |
485 | drm_kms_helper_poll_fini(drm); | 525 | drm_kms_helper_poll_fini(drm); |
526 | pm_runtime_get_sync(dev); | ||
527 | drm_vblank_cleanup(drm); | ||
486 | malidp_se_irq_fini(drm); | 528 | malidp_se_irq_fini(drm); |
487 | malidp_de_irq_fini(drm); | 529 | malidp_de_irq_fini(drm); |
488 | drm_vblank_cleanup(drm); | ||
489 | component_unbind_all(dev, drm); | 530 | component_unbind_all(dev, drm); |
490 | of_node_put(malidp->crtc.port); | 531 | of_node_put(malidp->crtc.port); |
491 | malidp->crtc.port = NULL; | 532 | malidp->crtc.port = NULL; |
492 | malidp_fini(drm); | 533 | malidp_fini(drm); |
534 | pm_runtime_put(dev); | ||
535 | if (pm_runtime_enabled(dev)) | ||
536 | pm_runtime_disable(dev); | ||
537 | else | ||
538 | malidp_runtime_pm_suspend(dev); | ||
493 | drm->dev_private = NULL; | 539 | drm->dev_private = NULL; |
494 | dev_set_drvdata(dev, NULL); | 540 | dev_set_drvdata(dev, NULL); |
495 | clk_disable_unprepare(hwdev->mclk); | ||
496 | clk_disable_unprepare(hwdev->aclk); | ||
497 | clk_disable_unprepare(hwdev->pclk); | ||
498 | drm_dev_unref(drm); | 541 | drm_dev_unref(drm); |
499 | of_reserved_mem_device_release(dev); | 542 | of_reserved_mem_device_release(dev); |
500 | } | 543 | } |
@@ -537,11 +580,52 @@ static int malidp_platform_remove(struct platform_device *pdev) | |||
537 | return 0; | 580 | return 0; |
538 | } | 581 | } |
539 | 582 | ||
583 | static int __maybe_unused malidp_pm_suspend(struct device *dev) | ||
584 | { | ||
585 | struct drm_device *drm = dev_get_drvdata(dev); | ||
586 | struct malidp_drm *malidp = drm->dev_private; | ||
587 | |||
588 | drm_kms_helper_poll_disable(drm); | ||
589 | console_lock(); | ||
590 | drm_fbdev_cma_set_suspend(malidp->fbdev, 1); | ||
591 | console_unlock(); | ||
592 | malidp->pm_state = drm_atomic_helper_suspend(drm); | ||
593 | if (IS_ERR(malidp->pm_state)) { | ||
594 | console_lock(); | ||
595 | drm_fbdev_cma_set_suspend(malidp->fbdev, 0); | ||
596 | console_unlock(); | ||
597 | drm_kms_helper_poll_enable(drm); | ||
598 | return PTR_ERR(malidp->pm_state); | ||
599 | } | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | static int __maybe_unused malidp_pm_resume(struct device *dev) | ||
605 | { | ||
606 | struct drm_device *drm = dev_get_drvdata(dev); | ||
607 | struct malidp_drm *malidp = drm->dev_private; | ||
608 | |||
609 | drm_atomic_helper_resume(drm, malidp->pm_state); | ||
610 | console_lock(); | ||
611 | drm_fbdev_cma_set_suspend(malidp->fbdev, 0); | ||
612 | console_unlock(); | ||
613 | drm_kms_helper_poll_enable(drm); | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static const struct dev_pm_ops malidp_pm_ops = { | ||
619 | SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ | ||
620 | SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) | ||
621 | }; | ||
622 | |||
540 | static struct platform_driver malidp_platform_driver = { | 623 | static struct platform_driver malidp_platform_driver = { |
541 | .probe = malidp_platform_probe, | 624 | .probe = malidp_platform_probe, |
542 | .remove = malidp_platform_remove, | 625 | .remove = malidp_platform_remove, |
543 | .driver = { | 626 | .driver = { |
544 | .name = "mali-dp", | 627 | .name = "mali-dp", |
628 | .pm = &malidp_pm_ops, | ||
545 | .of_match_table = malidp_drm_of_match, | 629 | .of_match_table = malidp_drm_of_match, |
546 | }, | 630 | }, |
547 | }; | 631 | }; |
diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index dbc617c6e4ef..cd4c04c65ead 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h | |||
@@ -24,6 +24,7 @@ struct malidp_drm { | |||
24 | struct drm_crtc crtc; | 24 | struct drm_crtc crtc; |
25 | wait_queue_head_t wq; | 25 | wait_queue_head_t wq; |
26 | atomic_t config_valid; | 26 | atomic_t config_valid; |
27 | struct drm_atomic_state *pm_state; | ||
27 | }; | 28 | }; |
28 | 29 | ||
29 | #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) | 30 | #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) |
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h index 00974b59407d..ea2dbae08990 100644 --- a/drivers/gpu/drm/arm/malidp_hw.h +++ b/drivers/gpu/drm/arm/malidp_hw.h | |||
@@ -156,6 +156,9 @@ struct malidp_hw_device { | |||
156 | u8 min_line_size; | 156 | u8 min_line_size; |
157 | u16 max_line_size; | 157 | u16 max_line_size; |
158 | 158 | ||
159 | /* track the device PM state */ | ||
160 | bool pm_suspended; | ||
161 | |||
159 | /* size of memory used for rotating layers, up to two banks available */ | 162 | /* size of memory used for rotating layers, up to two banks available */ |
160 | u32 rotation_memory[2]; | 163 | u32 rotation_memory[2]; |
161 | }; | 164 | }; |
@@ -173,12 +176,14 @@ extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES]; | |||
173 | 176 | ||
174 | static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg) | 177 | static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg) |
175 | { | 178 | { |
179 | WARN_ON(hwdev->pm_suspended); | ||
176 | return readl(hwdev->regs + reg); | 180 | return readl(hwdev->regs + reg); |
177 | } | 181 | } |
178 | 182 | ||
179 | static inline void malidp_hw_write(struct malidp_hw_device *hwdev, | 183 | static inline void malidp_hw_write(struct malidp_hw_device *hwdev, |
180 | u32 value, u32 reg) | 184 | u32 value, u32 reg) |
181 | { | 185 | { |
186 | WARN_ON(hwdev->pm_suspended); | ||
182 | writel(value, hwdev->regs + reg); | 187 | writel(value, hwdev->regs + reg); |
183 | } | 188 | } |
184 | 189 | ||