Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/stm: support runtime power management

This patch enables runtime power management (runtime PM) support for
the display controller. pm_runtime_enable() and pm_runtime_disable()
are added during ltdc load and unload respectively.
pm_runtime_get_sync() and pm_runtime_put_sync() are added for ltdc
register access.

Signed-off-by: Yannick Fertré <yannick.fertre@st.com>
Acked-by: Philippe Cornu <philippe.cornu@st.com>
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/1559550722-14091-1-git-send-email-yannick.fertre@st.com

authored by

Yannick Fertré and committed by
Benjamin Gaignard
35ab6cfb 630bec0c

+85 -23
+35 -8
drivers/gpu/drm/stm/drv.c
··· 12 12 #include <linux/dma-mapping.h> 13 13 #include <linux/module.h> 14 14 #include <linux/of_platform.h> 15 + #include <linux/pm_runtime.h> 15 16 16 17 #include <drm/drm_atomic.h> 17 18 #include <drm/drm_atomic_helper.h> ··· 136 135 struct ltdc_device *ldev = ddev->dev_private; 137 136 struct drm_atomic_state *state; 138 137 139 - drm_kms_helper_poll_disable(ddev); 138 + if (WARN_ON(!ldev->suspend_state)) 139 + return -ENOENT; 140 + 140 141 state = drm_atomic_helper_suspend(ddev); 141 - if (IS_ERR(state)) { 142 - drm_kms_helper_poll_enable(ddev); 142 + if (IS_ERR(state)) 143 143 return PTR_ERR(state); 144 - } 144 + 145 145 ldev->suspend_state = state; 146 - ltdc_suspend(ddev); 146 + pm_runtime_force_suspend(dev); 147 147 148 148 return 0; 149 149 } ··· 153 151 { 154 152 struct drm_device *ddev = dev_get_drvdata(dev); 155 153 struct ltdc_device *ldev = ddev->dev_private; 154 + int ret; 156 155 157 - ltdc_resume(ddev); 158 - drm_atomic_helper_resume(ddev, ldev->suspend_state); 159 - drm_kms_helper_poll_enable(ddev); 156 + pm_runtime_force_resume(dev); 157 + ret = drm_atomic_helper_resume(ddev, ldev->suspend_state); 158 + if (ret) { 159 + pm_runtime_force_suspend(dev); 160 + ldev->suspend_state = NULL; 161 + return ret; 162 + } 160 163 161 164 return 0; 162 165 } 163 166 167 + static __maybe_unused int drv_runtime_suspend(struct device *dev) 168 + { 169 + struct drm_device *ddev = dev_get_drvdata(dev); 170 + 171 + DRM_DEBUG_DRIVER("\n"); 172 + ltdc_suspend(ddev); 173 + 174 + return 0; 175 + } 176 + 177 + static __maybe_unused int drv_runtime_resume(struct device *dev) 178 + { 179 + struct drm_device *ddev = dev_get_drvdata(dev); 180 + 181 + DRM_DEBUG_DRIVER("\n"); 182 + return ltdc_resume(ddev); 183 + } 184 + 164 185 static const struct dev_pm_ops drv_pm_ops = { 165 186 SET_SYSTEM_SLEEP_PM_OPS(drv_suspend, drv_resume) 187 + SET_RUNTIME_PM_OPS(drv_runtime_suspend, 188 + drv_runtime_resume, NULL) 166 189 }; 167 190 168 191 static int stm_drm_platform_probe(struct platform_device *pdev)
+50 -15
drivers/gpu/drm/stm/ltdc.c
··· 16 16 #include <linux/of_address.h> 17 17 #include <linux/of_graph.h> 18 18 #include <linux/platform_device.h> 19 + #include <linux/pm_runtime.h> 19 20 #include <linux/reset.h> 20 21 21 22 #include <drm/drm_atomic.h> ··· 445 444 struct drm_crtc_state *old_state) 446 445 { 447 446 struct ltdc_device *ldev = crtc_to_ltdc(crtc); 447 + struct drm_device *ddev = crtc->dev; 448 448 449 449 DRM_DEBUG_DRIVER("\n"); 450 450 ··· 459 457 460 458 /* immediately commit disable of layers before switching off LTDC */ 461 459 reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); 460 + 461 + pm_runtime_put_sync(ddev->dev); 462 462 } 463 463 464 464 #define CLK_TOLERANCE_HZ 50 ··· 509 505 struct drm_display_mode *adjusted_mode) 510 506 { 511 507 struct ltdc_device *ldev = crtc_to_ltdc(crtc); 508 + struct drm_device *ddev = crtc->dev; 512 509 int rate = mode->clock * 1000; 510 + bool runtime_active; 511 + int ret; 513 512 514 - clk_disable(ldev->pixel_clk); 513 + runtime_active = pm_runtime_active(ddev->dev); 514 + 515 + if (runtime_active) 516 + pm_runtime_put_sync(ddev->dev); 517 + 515 518 if (clk_set_rate(ldev->pixel_clk, rate) < 0) { 516 519 DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); 517 520 return false; 518 521 } 519 - clk_enable(ldev->pixel_clk); 520 522 521 523 adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000; 524 + 525 + if (runtime_active) { 526 + ret = pm_runtime_get_sync(ddev->dev); 527 + if (ret) { 528 + DRM_ERROR("Failed to fixup mode, cannot get sync\n"); 529 + return false; 530 + } 531 + } 522 532 523 533 DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n", 524 534 mode->clock, adjusted_mode->clock); ··· 543 525 static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) 544 526 { 545 527 struct ltdc_device *ldev = crtc_to_ltdc(crtc); 528 + struct drm_device *ddev = crtc->dev; 546 529 struct drm_display_mode *mode = &crtc->state->adjusted_mode; 547 530 struct videomode vm; 548 531 u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h; 549 532 u32 total_width, total_height; 550 533 u32 val; 534 + int ret; 535 + 536 + if (!pm_runtime_active(ddev->dev)) { 537 + ret = pm_runtime_get_sync(ddev->dev); 538 + if (ret) { 539 + DRM_ERROR("Failed to set mode, cannot get sync\n"); 540 + return; 541 + } 542 + } 551 543 552 544 drm_display_mode_to_videomode(mode, &vm); 553 545 ··· 618 590 struct drm_crtc_state *old_crtc_state) 619 591 { 620 592 struct ltdc_device *ldev = crtc_to_ltdc(crtc); 593 + struct drm_device *ddev = crtc->dev; 621 594 struct drm_pending_vblank_event *event = crtc->state->event; 622 595 623 596 DRM_DEBUG_ATOMIC("\n"); ··· 631 602 if (event) { 632 603 crtc->state->event = NULL; 633 604 634 - spin_lock_irq(&crtc->dev->event_lock); 605 + spin_lock_irq(&ddev->event_lock); 635 606 if (drm_crtc_vblank_get(crtc) == 0) 636 607 drm_crtc_arm_vblank_event(crtc, event); 637 608 else 638 609 drm_crtc_send_vblank_event(crtc, event); 639 - spin_unlock_irq(&crtc->dev->event_lock); 610 + spin_unlock_irq(&ddev->event_lock); 640 611 } 641 612 } 642 613 ··· 692 663 * Computation for the two first cases are identical so we can 693 664 * simplify the code and only test if line > vactive_end 694 665 */ 695 - line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; 696 - vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; 697 - vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; 698 - vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; 666 + if (pm_runtime_active(ddev->dev)) { 667 + line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; 668 + vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; 669 + vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; 670 + vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; 699 671 700 - if (line > vactive_end) 701 - *vpos = line - vtotal - vactive_start; 702 - else 703 - *vpos = line - vactive_start; 672 + if (line > vactive_end) 673 + *vpos = line - vtotal - vactive_start; 674 + else 675 + *vpos = line - vactive_start; 676 + } else { 677 + *vpos = 0; 678 + } 704 679 705 680 *hpos = 0; 706 681 ··· 1276 1243 /* Allow usage of vblank without having to call drm_irq_install */ 1277 1244 ddev->irq_enabled = 1; 1278 1245 1279 - return 0; 1246 + clk_disable_unprepare(ldev->pixel_clk); 1280 1247 1248 + pm_runtime_enable(ddev->dev); 1249 + 1250 + return 0; 1281 1251 err: 1282 1252 for (i = 0; i < MAX_ENDPOINTS; i++) 1283 1253 drm_panel_bridge_remove(bridge[i]); ··· 1292 1256 1293 1257 void ltdc_unload(struct drm_device *ddev) 1294 1258 { 1295 - struct ltdc_device *ldev = ddev->dev_private; 1296 1259 int i; 1297 1260 1298 1261 DRM_DEBUG_DRIVER("\n"); ··· 1299 1264 for (i = 0; i < MAX_ENDPOINTS; i++) 1300 1265 drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); 1301 1266 1302 - clk_disable_unprepare(ldev->pixel_clk); 1267 + pm_runtime_disable(ddev->dev); 1303 1268 } 1304 1269 1305 1270 MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");