diff options
author | Dave Airlie <airlied@redhat.com> | 2012-09-17 00:40:31 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2013-11-01 12:43:14 -0400 |
commit | 10ebc0bc09344ab6310309169efc73dfe6c23d72 (patch) | |
tree | 9d93730a567190aa7b57d91999565273bc002dc0 /drivers/gpu/drm/radeon/radeon_drv.c | |
parent | 7473e830c400b2ebf54937a4f075279f7713a2b7 (diff) |
drm/radeon: add runtime PM support (v2)
This hooks radeon up to the runtime PM system to enable
dynamic power management for secondary GPUs in switchable
and powerxpress laptops.
v2: agd5f: clean up, add module parameter
Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_drv.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 122 |
1 files changed, 113 insertions, 9 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 59f067e857db..9902ac76f683 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -36,8 +36,9 @@ | |||
36 | #include <drm/drm_pciids.h> | 36 | #include <drm/drm_pciids.h> |
37 | #include <linux/console.h> | 37 | #include <linux/console.h> |
38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
39 | 39 | #include <linux/pm_runtime.h> | |
40 | 40 | #include <linux/vga_switcheroo.h> | |
41 | #include "drm_crtc_helper.h" | ||
41 | /* | 42 | /* |
42 | * KMS wrapper. | 43 | * KMS wrapper. |
43 | * - 2.0.0 - initial interface | 44 | * - 2.0.0 - initial interface |
@@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev, | |||
87 | struct drm_file *file_priv); | 88 | struct drm_file *file_priv); |
88 | void radeon_driver_preclose_kms(struct drm_device *dev, | 89 | void radeon_driver_preclose_kms(struct drm_device *dev, |
89 | struct drm_file *file_priv); | 90 | struct drm_file *file_priv); |
90 | int radeon_suspend_kms(struct drm_device *dev, bool suspend); | 91 | int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon); |
91 | int radeon_resume_kms(struct drm_device *dev, bool resume); | 92 | int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon); |
92 | u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc); | 93 | u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc); |
93 | int radeon_enable_vblank_kms(struct drm_device *dev, int crtc); | 94 | int radeon_enable_vblank_kms(struct drm_device *dev, int crtc); |
94 | void radeon_disable_vblank_kms(struct drm_device *dev, int crtc); | 95 | void radeon_disable_vblank_kms(struct drm_device *dev, int crtc); |
@@ -136,9 +137,11 @@ void radeon_debugfs_cleanup(struct drm_minor *minor); | |||
136 | #if defined(CONFIG_VGA_SWITCHEROO) | 137 | #if defined(CONFIG_VGA_SWITCHEROO) |
137 | void radeon_register_atpx_handler(void); | 138 | void radeon_register_atpx_handler(void); |
138 | void radeon_unregister_atpx_handler(void); | 139 | void radeon_unregister_atpx_handler(void); |
140 | bool radeon_is_px(void); | ||
139 | #else | 141 | #else |
140 | static inline void radeon_register_atpx_handler(void) {} | 142 | static inline void radeon_register_atpx_handler(void) {} |
141 | static inline void radeon_unregister_atpx_handler(void) {} | 143 | static inline void radeon_unregister_atpx_handler(void) {} |
144 | static inline bool radeon_is_px(void) { return false; } | ||
142 | #endif | 145 | #endif |
143 | 146 | ||
144 | int radeon_no_wb; | 147 | int radeon_no_wb; |
@@ -161,6 +164,7 @@ int radeon_lockup_timeout = 10000; | |||
161 | int radeon_fastfb = 0; | 164 | int radeon_fastfb = 0; |
162 | int radeon_dpm = -1; | 165 | int radeon_dpm = -1; |
163 | int radeon_aspm = -1; | 166 | int radeon_aspm = -1; |
167 | int radeon_runtime_pm = -1; | ||
164 | 168 | ||
165 | MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); | 169 | MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); |
166 | module_param_named(no_wb, radeon_no_wb, int, 0444); | 170 | module_param_named(no_wb, radeon_no_wb, int, 0444); |
@@ -222,6 +226,9 @@ module_param_named(dpm, radeon_dpm, int, 0444); | |||
222 | MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)"); | 226 | MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)"); |
223 | module_param_named(aspm, radeon_aspm, int, 0444); | 227 | module_param_named(aspm, radeon_aspm, int, 0444); |
224 | 228 | ||
229 | MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1 = PX only default)"); | ||
230 | module_param_named(runpm, radeon_runtime_pm, int, 0444); | ||
231 | |||
225 | static struct pci_device_id pciidlist[] = { | 232 | static struct pci_device_id pciidlist[] = { |
226 | radeon_PCI_IDS | 233 | radeon_PCI_IDS |
227 | }; | 234 | }; |
@@ -258,6 +265,7 @@ static int radeon_resume(struct drm_device *dev) | |||
258 | return 0; | 265 | return 0; |
259 | } | 266 | } |
260 | 267 | ||
268 | |||
261 | static const struct file_operations radeon_driver_old_fops = { | 269 | static const struct file_operations radeon_driver_old_fops = { |
262 | .owner = THIS_MODULE, | 270 | .owner = THIS_MODULE, |
263 | .open = drm_open, | 271 | .open = drm_open, |
@@ -356,28 +364,121 @@ static int radeon_pmops_suspend(struct device *dev) | |||
356 | { | 364 | { |
357 | struct pci_dev *pdev = to_pci_dev(dev); | 365 | struct pci_dev *pdev = to_pci_dev(dev); |
358 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | 366 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
359 | return radeon_suspend_kms(drm_dev, 1); | 367 | return radeon_suspend_kms(drm_dev, true, true); |
360 | } | 368 | } |
361 | 369 | ||
362 | static int radeon_pmops_resume(struct device *dev) | 370 | static int radeon_pmops_resume(struct device *dev) |
363 | { | 371 | { |
364 | struct pci_dev *pdev = to_pci_dev(dev); | 372 | struct pci_dev *pdev = to_pci_dev(dev); |
365 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | 373 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
366 | return radeon_resume_kms(drm_dev, 1); | 374 | return radeon_resume_kms(drm_dev, true, true); |
367 | } | 375 | } |
368 | 376 | ||
369 | static int radeon_pmops_freeze(struct device *dev) | 377 | static int radeon_pmops_freeze(struct device *dev) |
370 | { | 378 | { |
371 | struct pci_dev *pdev = to_pci_dev(dev); | 379 | struct pci_dev *pdev = to_pci_dev(dev); |
372 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | 380 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
373 | return radeon_suspend_kms(drm_dev, 0); | 381 | return radeon_suspend_kms(drm_dev, false, true); |
374 | } | 382 | } |
375 | 383 | ||
376 | static int radeon_pmops_thaw(struct device *dev) | 384 | static int radeon_pmops_thaw(struct device *dev) |
377 | { | 385 | { |
378 | struct pci_dev *pdev = to_pci_dev(dev); | 386 | struct pci_dev *pdev = to_pci_dev(dev); |
379 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | 387 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
380 | return radeon_resume_kms(drm_dev, 0); | 388 | return radeon_resume_kms(drm_dev, false, true); |
389 | } | ||
390 | |||
391 | static int radeon_pmops_runtime_suspend(struct device *dev) | ||
392 | { | ||
393 | struct pci_dev *pdev = to_pci_dev(dev); | ||
394 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
395 | int ret; | ||
396 | |||
397 | if (radeon_runtime_pm == 0) | ||
398 | return -EINVAL; | ||
399 | |||
400 | drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | ||
401 | drm_kms_helper_poll_disable(drm_dev); | ||
402 | vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); | ||
403 | |||
404 | ret = radeon_suspend_kms(drm_dev, false, false); | ||
405 | pci_save_state(pdev); | ||
406 | pci_disable_device(pdev); | ||
407 | pci_set_power_state(pdev, PCI_D3cold); | ||
408 | drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int radeon_pmops_runtime_resume(struct device *dev) | ||
414 | { | ||
415 | struct pci_dev *pdev = to_pci_dev(dev); | ||
416 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
417 | int ret; | ||
418 | |||
419 | if (radeon_runtime_pm == 0) | ||
420 | return -EINVAL; | ||
421 | |||
422 | drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | ||
423 | |||
424 | pci_set_power_state(pdev, PCI_D0); | ||
425 | pci_restore_state(pdev); | ||
426 | ret = pci_enable_device(pdev); | ||
427 | if (ret) | ||
428 | return ret; | ||
429 | pci_set_master(pdev); | ||
430 | |||
431 | ret = radeon_resume_kms(drm_dev, false, false); | ||
432 | drm_kms_helper_poll_enable(drm_dev); | ||
433 | vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); | ||
434 | drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static int radeon_pmops_runtime_idle(struct device *dev) | ||
439 | { | ||
440 | struct pci_dev *pdev = to_pci_dev(dev); | ||
441 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
442 | struct drm_crtc *crtc; | ||
443 | |||
444 | if (radeon_runtime_pm == 0) | ||
445 | return -EBUSY; | ||
446 | |||
447 | /* are we PX enabled? */ | ||
448 | if (radeon_runtime_pm == -1 && !radeon_is_px()) { | ||
449 | DRM_DEBUG_DRIVER("failing to power off - not px\n"); | ||
450 | return -EBUSY; | ||
451 | } | ||
452 | |||
453 | list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { | ||
454 | if (crtc->enabled) { | ||
455 | DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); | ||
456 | return -EBUSY; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | pm_runtime_mark_last_busy(dev); | ||
461 | pm_runtime_autosuspend(dev); | ||
462 | /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ | ||
463 | return 1; | ||
464 | } | ||
465 | |||
466 | long radeon_drm_ioctl(struct file *filp, | ||
467 | unsigned int cmd, unsigned long arg) | ||
468 | { | ||
469 | struct drm_file *file_priv = filp->private_data; | ||
470 | struct drm_device *dev; | ||
471 | long ret; | ||
472 | dev = file_priv->minor->dev; | ||
473 | ret = pm_runtime_get_sync(dev->dev); | ||
474 | if (ret < 0) | ||
475 | return ret; | ||
476 | |||
477 | ret = drm_ioctl(filp, cmd, arg); | ||
478 | |||
479 | pm_runtime_mark_last_busy(dev->dev); | ||
480 | pm_runtime_put_autosuspend(dev->dev); | ||
481 | return ret; | ||
381 | } | 482 | } |
382 | 483 | ||
383 | static const struct dev_pm_ops radeon_pm_ops = { | 484 | static const struct dev_pm_ops radeon_pm_ops = { |
@@ -387,13 +488,16 @@ static const struct dev_pm_ops radeon_pm_ops = { | |||
387 | .thaw = radeon_pmops_thaw, | 488 | .thaw = radeon_pmops_thaw, |
388 | .poweroff = radeon_pmops_freeze, | 489 | .poweroff = radeon_pmops_freeze, |
389 | .restore = radeon_pmops_resume, | 490 | .restore = radeon_pmops_resume, |
491 | .runtime_suspend = radeon_pmops_runtime_suspend, | ||
492 | .runtime_resume = radeon_pmops_runtime_resume, | ||
493 | .runtime_idle = radeon_pmops_runtime_idle, | ||
390 | }; | 494 | }; |
391 | 495 | ||
392 | static const struct file_operations radeon_driver_kms_fops = { | 496 | static const struct file_operations radeon_driver_kms_fops = { |
393 | .owner = THIS_MODULE, | 497 | .owner = THIS_MODULE, |
394 | .open = drm_open, | 498 | .open = drm_open, |
395 | .release = drm_release, | 499 | .release = drm_release, |
396 | .unlocked_ioctl = drm_ioctl, | 500 | .unlocked_ioctl = radeon_drm_ioctl, |
397 | .mmap = radeon_mmap, | 501 | .mmap = radeon_mmap, |
398 | .poll = drm_poll, | 502 | .poll = drm_poll, |
399 | .read = drm_read, | 503 | .read = drm_read, |