diff options
author | Rafał Miłecki <zajec5@gmail.com> | 2009-12-22 17:02:16 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-02-08 18:32:26 -0500 |
commit | c913e23a145ae07b6f9f88aae8cd5ad06b5729ff (patch) | |
tree | aebde8632096f5b24f0a1592800e1ec1e7926c0b /drivers | |
parent | 18917b60bca094e7830e4046e336d024f73f1c90 (diff) |
drm/radeon/kms: add dynamic engine reclocking (V9)
V2: reorganize functions, fix modesetting calls
V3: rebase patch, use radeon's workqueue
V4: enable on tested chipsets only, request VBLANK IRQs
V5: enable PM on older hardware (IRQs, mode_fixup, dpms)
V6: use separate dynpm module parameter
V7: drop RADEON_ prefix, set minimum mode for dpms off
V8: update legacy encoder call, fix order in rs600 IRQ
V9: update compute_clocks call in legacy, not only DPMS_OFF
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/radeon/r100.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/r600.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 28 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_encoders.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_legacy_encoders.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_pm.c | 241 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/rs600.c | 10 |
9 files changed, 314 insertions, 4 deletions
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 597f85b283bc..05502bf042b9 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c | |||
@@ -312,9 +312,13 @@ int r100_irq_process(struct radeon_device *rdev) | |||
312 | /* Vertical blank interrupts */ | 312 | /* Vertical blank interrupts */ |
313 | if (status & RADEON_CRTC_VBLANK_STAT) { | 313 | if (status & RADEON_CRTC_VBLANK_STAT) { |
314 | drm_handle_vblank(rdev->ddev, 0); | 314 | drm_handle_vblank(rdev->ddev, 0); |
315 | if (rdev->pm.vblank_callback) | ||
316 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
315 | } | 317 | } |
316 | if (status & RADEON_CRTC2_VBLANK_STAT) { | 318 | if (status & RADEON_CRTC2_VBLANK_STAT) { |
317 | drm_handle_vblank(rdev->ddev, 1); | 319 | drm_handle_vblank(rdev->ddev, 1); |
320 | if (rdev->pm.vblank_callback) | ||
321 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
318 | } | 322 | } |
319 | if (status & RADEON_FP_DETECT_STAT) { | 323 | if (status & RADEON_FP_DETECT_STAT) { |
320 | queue_hotplug = true; | 324 | queue_hotplug = true; |
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 7c32a233b236..65daf55af2d9 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c | |||
@@ -2744,6 +2744,8 @@ restart_ih: | |||
2744 | case 0: /* D1 vblank */ | 2744 | case 0: /* D1 vblank */ |
2745 | if (disp_int & LB_D1_VBLANK_INTERRUPT) { | 2745 | if (disp_int & LB_D1_VBLANK_INTERRUPT) { |
2746 | drm_handle_vblank(rdev->ddev, 0); | 2746 | drm_handle_vblank(rdev->ddev, 0); |
2747 | if (rdev->pm.vblank_callback) | ||
2748 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
2747 | disp_int &= ~LB_D1_VBLANK_INTERRUPT; | 2749 | disp_int &= ~LB_D1_VBLANK_INTERRUPT; |
2748 | DRM_DEBUG("IH: D1 vblank\n"); | 2750 | DRM_DEBUG("IH: D1 vblank\n"); |
2749 | } | 2751 | } |
@@ -2764,6 +2766,8 @@ restart_ih: | |||
2764 | case 0: /* D2 vblank */ | 2766 | case 0: /* D2 vblank */ |
2765 | if (disp_int & LB_D2_VBLANK_INTERRUPT) { | 2767 | if (disp_int & LB_D2_VBLANK_INTERRUPT) { |
2766 | drm_handle_vblank(rdev->ddev, 1); | 2768 | drm_handle_vblank(rdev->ddev, 1); |
2769 | if (rdev->pm.vblank_callback) | ||
2770 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
2767 | disp_int &= ~LB_D2_VBLANK_INTERRUPT; | 2771 | disp_int &= ~LB_D2_VBLANK_INTERRUPT; |
2768 | DRM_DEBUG("IH: D2 vblank\n"); | 2772 | DRM_DEBUG("IH: D2 vblank\n"); |
2769 | } | 2773 | } |
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 29ae3839c212..d7da6e742de8 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -89,6 +89,7 @@ extern int radeon_testing; | |||
89 | extern int radeon_connector_table; | 89 | extern int radeon_connector_table; |
90 | extern int radeon_tv; | 90 | extern int radeon_tv; |
91 | extern int radeon_new_pll; | 91 | extern int radeon_new_pll; |
92 | extern int radeon_dynpm; | ||
92 | extern int radeon_audio; | 93 | extern int radeon_audio; |
93 | 94 | ||
94 | /* | 95 | /* |
@@ -148,6 +149,7 @@ struct radeon_clock { | |||
148 | * Power management | 149 | * Power management |
149 | */ | 150 | */ |
150 | int radeon_pm_init(struct radeon_device *rdev); | 151 | int radeon_pm_init(struct radeon_device *rdev); |
152 | void radeon_pm_compute_clocks(struct radeon_device *rdev); | ||
151 | 153 | ||
152 | /* | 154 | /* |
153 | * Fences. | 155 | * Fences. |
@@ -569,7 +571,33 @@ struct radeon_wb { | |||
569 | * Equation between gpu/memory clock and available bandwidth is hw dependent | 571 | * Equation between gpu/memory clock and available bandwidth is hw dependent |
570 | * (type of memory, bus size, efficiency, ...) | 572 | * (type of memory, bus size, efficiency, ...) |
571 | */ | 573 | */ |
574 | enum radeon_pm_state { | ||
575 | PM_STATE_DISABLED, | ||
576 | PM_STATE_MINIMUM, | ||
577 | PM_STATE_PAUSED, | ||
578 | PM_STATE_ACTIVE | ||
579 | }; | ||
580 | enum radeon_pm_action { | ||
581 | PM_ACTION_NONE, | ||
582 | PM_ACTION_MINIMUM, | ||
583 | PM_ACTION_DOWNCLOCK, | ||
584 | PM_ACTION_UPCLOCK | ||
585 | }; | ||
572 | struct radeon_pm { | 586 | struct radeon_pm { |
587 | struct mutex mutex; | ||
588 | struct work_struct reclock_work; | ||
589 | struct delayed_work idle_work; | ||
590 | enum radeon_pm_state state; | ||
591 | enum radeon_pm_action planned_action; | ||
592 | unsigned long action_timeout; | ||
593 | bool downclocked; | ||
594 | bool vblank_callback; | ||
595 | int active_crtcs; | ||
596 | int req_vblank; | ||
597 | uint32_t min_gpu_engine_clock; | ||
598 | uint32_t min_gpu_memory_clock; | ||
599 | uint32_t min_mode_engine_clock; | ||
600 | uint32_t min_mode_memory_clock; | ||
573 | fixed20_12 max_bandwidth; | 601 | fixed20_12 max_bandwidth; |
574 | fixed20_12 igp_sideport_mclk; | 602 | fixed20_12 igp_sideport_mclk; |
575 | fixed20_12 igp_system_mclk; | 603 | fixed20_12 igp_system_mclk; |
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 18ac29c505cf..a1c937d03845 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c | |||
@@ -642,6 +642,7 @@ int radeon_device_init(struct radeon_device *rdev, | |||
642 | if (rdev->family >= CHIP_R600) | 642 | if (rdev->family >= CHIP_R600) |
643 | spin_lock_init(&rdev->ih.lock); | 643 | spin_lock_init(&rdev->ih.lock); |
644 | mutex_init(&rdev->gem.mutex); | 644 | mutex_init(&rdev->gem.mutex); |
645 | mutex_init(&rdev->pm.mutex); | ||
645 | rwlock_init(&rdev->fence_drv.lock); | 646 | rwlock_init(&rdev->fence_drv.lock); |
646 | INIT_LIST_HEAD(&rdev->gem.objects); | 647 | INIT_LIST_HEAD(&rdev->gem.objects); |
647 | 648 | ||
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8ba3de7994d4..a9572e6d4d64 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -87,6 +87,7 @@ int radeon_testing = 0; | |||
87 | int radeon_connector_table = 0; | 87 | int radeon_connector_table = 0; |
88 | int radeon_tv = 1; | 88 | int radeon_tv = 1; |
89 | int radeon_new_pll = 1; | 89 | int radeon_new_pll = 1; |
90 | int radeon_dynpm = -1; | ||
90 | int radeon_audio = 1; | 91 | int radeon_audio = 1; |
91 | 92 | ||
92 | MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); | 93 | MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); |
@@ -125,6 +126,9 @@ module_param_named(tv, radeon_tv, int, 0444); | |||
125 | MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips"); | 126 | MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips"); |
126 | module_param_named(new_pll, radeon_new_pll, int, 0444); | 127 | module_param_named(new_pll, radeon_new_pll, int, 0444); |
127 | 128 | ||
129 | MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)"); | ||
130 | module_param_named(dynpm, radeon_dynpm, int, 0444); | ||
131 | |||
128 | MODULE_PARM_DESC(audio, "Audio enable (0 = disable)"); | 132 | MODULE_PARM_DESC(audio, "Audio enable (0 = disable)"); |
129 | module_param_named(audio, radeon_audio, int, 0444); | 133 | module_param_named(audio, radeon_audio, int, 0444); |
130 | 134 | ||
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index c5ba1e30a43f..f7d6078876c5 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c | |||
@@ -262,6 +262,9 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, | |||
262 | struct drm_device *dev = encoder->dev; | 262 | struct drm_device *dev = encoder->dev; |
263 | struct radeon_device *rdev = dev->dev_private; | 263 | struct radeon_device *rdev = dev->dev_private; |
264 | 264 | ||
265 | /* adjust pm to upcoming mode change */ | ||
266 | radeon_pm_compute_clocks(rdev); | ||
267 | |||
265 | /* set the active encoder to connector routing */ | 268 | /* set the active encoder to connector routing */ |
266 | radeon_encoder_set_active_device(encoder); | 269 | radeon_encoder_set_active_device(encoder); |
267 | drm_mode_set_crtcinfo(adjusted_mode, 0); | 270 | drm_mode_set_crtcinfo(adjusted_mode, 0); |
@@ -1013,6 +1016,9 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) | |||
1013 | atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); | 1016 | atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
1014 | } | 1017 | } |
1015 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 1018 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
1019 | |||
1020 | /* adjust pm to dpms change */ | ||
1021 | radeon_pm_compute_clocks(rdev); | ||
1016 | } | 1022 | } |
1017 | 1023 | ||
1018 | union crtc_source_param { | 1024 | union crtc_source_param { |
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 38e45e231ef5..cf389ce50a8a 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c | |||
@@ -115,6 +115,9 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) | |||
115 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 115 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
116 | else | 116 | else |
117 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 117 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
118 | |||
119 | /* adjust pm to dpms change */ | ||
120 | radeon_pm_compute_clocks(rdev); | ||
118 | } | 121 | } |
119 | 122 | ||
120 | static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) | 123 | static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) |
@@ -214,6 +217,11 @@ static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder, | |||
214 | struct drm_display_mode *adjusted_mode) | 217 | struct drm_display_mode *adjusted_mode) |
215 | { | 218 | { |
216 | struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); | 219 | struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
220 | struct drm_device *dev = encoder->dev; | ||
221 | struct radeon_device *rdev = dev->dev_private; | ||
222 | |||
223 | /* adjust pm to upcoming mode change */ | ||
224 | radeon_pm_compute_clocks(rdev); | ||
217 | 225 | ||
218 | /* set the active encoder to connector routing */ | 226 | /* set the active encoder to connector routing */ |
219 | radeon_encoder_set_active_device(encoder); | 227 | radeon_encoder_set_active_device(encoder); |
@@ -285,6 +293,9 @@ static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode | |||
285 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 293 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
286 | else | 294 | else |
287 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 295 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
296 | |||
297 | /* adjust pm to dpms change */ | ||
298 | radeon_pm_compute_clocks(rdev); | ||
288 | } | 299 | } |
289 | 300 | ||
290 | static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) | 301 | static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) |
@@ -470,6 +481,9 @@ static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode) | |||
470 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 481 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
471 | else | 482 | else |
472 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 483 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
484 | |||
485 | /* adjust pm to dpms change */ | ||
486 | radeon_pm_compute_clocks(rdev); | ||
473 | } | 487 | } |
474 | 488 | ||
475 | static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) | 489 | static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) |
@@ -635,6 +649,9 @@ static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode) | |||
635 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 649 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
636 | else | 650 | else |
637 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 651 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
652 | |||
653 | /* adjust pm to dpms change */ | ||
654 | radeon_pm_compute_clocks(rdev); | ||
638 | } | 655 | } |
639 | 656 | ||
640 | static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) | 657 | static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) |
@@ -842,6 +859,9 @@ static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode) | |||
842 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 859 | radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
843 | else | 860 | else |
844 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); | 861 | radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
862 | |||
863 | /* adjust pm to dpms change */ | ||
864 | radeon_pm_compute_clocks(rdev); | ||
845 | } | 865 | } |
846 | 866 | ||
847 | static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) | 867 | static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) |
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 8bce64cdc320..a9c61f435c06 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c | |||
@@ -22,17 +22,253 @@ | |||
22 | #include "drmP.h" | 22 | #include "drmP.h" |
23 | #include "radeon.h" | 23 | #include "radeon.h" |
24 | 24 | ||
25 | int radeon_debugfs_pm_init(struct radeon_device *rdev); | 25 | #define RADEON_IDLE_LOOP_MS 100 |
26 | #define RADEON_RECLOCK_DELAY_MS 200 | ||
27 | |||
28 | static void radeon_pm_check_limits(struct radeon_device *rdev); | ||
29 | static void radeon_pm_set_clocks_locked(struct radeon_device *rdev); | ||
30 | static void radeon_pm_set_clocks(struct radeon_device *rdev); | ||
31 | static void radeon_pm_reclock_work_handler(struct work_struct *work); | ||
32 | static void radeon_pm_idle_work_handler(struct work_struct *work); | ||
33 | static int radeon_debugfs_pm_init(struct radeon_device *rdev); | ||
34 | |||
35 | static const char *pm_state_names[4] = { | ||
36 | "PM_STATE_DISABLED", | ||
37 | "PM_STATE_MINIMUM", | ||
38 | "PM_STATE_PAUSED", | ||
39 | "PM_STATE_ACTIVE" | ||
40 | }; | ||
26 | 41 | ||
27 | int radeon_pm_init(struct radeon_device *rdev) | 42 | int radeon_pm_init(struct radeon_device *rdev) |
28 | { | 43 | { |
44 | rdev->pm.state = PM_STATE_DISABLED; | ||
45 | rdev->pm.planned_action = PM_ACTION_NONE; | ||
46 | rdev->pm.downclocked = false; | ||
47 | rdev->pm.vblank_callback = false; | ||
48 | |||
49 | radeon_pm_check_limits(rdev); | ||
50 | |||
29 | if (radeon_debugfs_pm_init(rdev)) { | 51 | if (radeon_debugfs_pm_init(rdev)) { |
30 | DRM_ERROR("Failed to register debugfs file for PM!\n"); | 52 | DRM_ERROR("Failed to register debugfs file for PM!\n"); |
31 | } | 53 | } |
32 | 54 | ||
55 | INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler); | ||
56 | INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler); | ||
57 | |||
58 | if (radeon_dynpm != -1 && radeon_dynpm) { | ||
59 | rdev->pm.state = PM_STATE_PAUSED; | ||
60 | DRM_INFO("radeon: dynamic power management enabled\n"); | ||
61 | } | ||
62 | |||
63 | DRM_INFO("radeon: power management initialized\n"); | ||
64 | |||
33 | return 0; | 65 | return 0; |
34 | } | 66 | } |
35 | 67 | ||
68 | static void radeon_pm_check_limits(struct radeon_device *rdev) | ||
69 | { | ||
70 | rdev->pm.min_gpu_engine_clock = rdev->clock.default_sclk - 5000; | ||
71 | rdev->pm.min_gpu_memory_clock = rdev->clock.default_mclk - 5000; | ||
72 | } | ||
73 | |||
74 | void radeon_pm_compute_clocks(struct radeon_device *rdev) | ||
75 | { | ||
76 | struct drm_device *ddev = rdev->ddev; | ||
77 | struct drm_connector *connector; | ||
78 | struct radeon_crtc *radeon_crtc; | ||
79 | int count = 0; | ||
80 | |||
81 | if (rdev->pm.state == PM_STATE_DISABLED) | ||
82 | return; | ||
83 | |||
84 | mutex_lock(&rdev->pm.mutex); | ||
85 | |||
86 | rdev->pm.active_crtcs = 0; | ||
87 | list_for_each_entry(connector, | ||
88 | &ddev->mode_config.connector_list, head) { | ||
89 | if (connector->encoder && | ||
90 | connector->dpms != DRM_MODE_DPMS_OFF) { | ||
91 | radeon_crtc = to_radeon_crtc(connector->encoder->crtc); | ||
92 | rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); | ||
93 | ++count; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | if (count > 1) { | ||
98 | if (rdev->pm.state == PM_STATE_ACTIVE) { | ||
99 | wait_queue_head_t wait; | ||
100 | init_waitqueue_head(&wait); | ||
101 | |||
102 | cancel_delayed_work(&rdev->pm.idle_work); | ||
103 | |||
104 | rdev->pm.state = PM_STATE_PAUSED; | ||
105 | rdev->pm.planned_action = PM_ACTION_UPCLOCK; | ||
106 | rdev->pm.vblank_callback = true; | ||
107 | |||
108 | mutex_unlock(&rdev->pm.mutex); | ||
109 | |||
110 | wait_event_timeout(wait, !rdev->pm.downclocked, | ||
111 | msecs_to_jiffies(300)); | ||
112 | if (!rdev->pm.downclocked) | ||
113 | radeon_pm_set_clocks(rdev); | ||
114 | |||
115 | DRM_DEBUG("radeon: dynamic power management deactivated\n"); | ||
116 | } else { | ||
117 | mutex_unlock(&rdev->pm.mutex); | ||
118 | } | ||
119 | } else if (count == 1) { | ||
120 | rdev->pm.min_mode_engine_clock = rdev->pm.min_gpu_engine_clock; | ||
121 | rdev->pm.min_mode_memory_clock = rdev->pm.min_gpu_memory_clock; | ||
122 | /* TODO: Increase clocks if needed for current mode */ | ||
123 | |||
124 | if (rdev->pm.state == PM_STATE_MINIMUM) { | ||
125 | rdev->pm.state = PM_STATE_ACTIVE; | ||
126 | rdev->pm.planned_action = PM_ACTION_UPCLOCK; | ||
127 | radeon_pm_set_clocks_locked(rdev); | ||
128 | |||
129 | queue_delayed_work(rdev->wq, &rdev->pm.idle_work, | ||
130 | msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); | ||
131 | } | ||
132 | else if (rdev->pm.state == PM_STATE_PAUSED) { | ||
133 | rdev->pm.state = PM_STATE_ACTIVE; | ||
134 | queue_delayed_work(rdev->wq, &rdev->pm.idle_work, | ||
135 | msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); | ||
136 | DRM_DEBUG("radeon: dynamic power management activated\n"); | ||
137 | } | ||
138 | |||
139 | mutex_unlock(&rdev->pm.mutex); | ||
140 | } | ||
141 | else { /* count == 0 */ | ||
142 | if (rdev->pm.state != PM_STATE_MINIMUM) { | ||
143 | cancel_delayed_work(&rdev->pm.idle_work); | ||
144 | |||
145 | rdev->pm.state = PM_STATE_MINIMUM; | ||
146 | rdev->pm.planned_action = PM_ACTION_MINIMUM; | ||
147 | radeon_pm_set_clocks_locked(rdev); | ||
148 | } | ||
149 | |||
150 | mutex_unlock(&rdev->pm.mutex); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void radeon_pm_set_clocks_locked(struct radeon_device *rdev) | ||
155 | { | ||
156 | /*radeon_fence_wait_last(rdev);*/ | ||
157 | switch (rdev->pm.planned_action) { | ||
158 | case PM_ACTION_UPCLOCK: | ||
159 | radeon_set_engine_clock(rdev, rdev->clock.default_sclk); | ||
160 | rdev->pm.downclocked = false; | ||
161 | break; | ||
162 | case PM_ACTION_DOWNCLOCK: | ||
163 | radeon_set_engine_clock(rdev, | ||
164 | rdev->pm.min_mode_engine_clock); | ||
165 | rdev->pm.downclocked = true; | ||
166 | break; | ||
167 | case PM_ACTION_MINIMUM: | ||
168 | radeon_set_engine_clock(rdev, | ||
169 | rdev->pm.min_gpu_engine_clock); | ||
170 | break; | ||
171 | case PM_ACTION_NONE: | ||
172 | DRM_ERROR("%s: PM_ACTION_NONE\n", __func__); | ||
173 | break; | ||
174 | } | ||
175 | |||
176 | rdev->pm.planned_action = PM_ACTION_NONE; | ||
177 | } | ||
178 | |||
179 | static void radeon_pm_set_clocks(struct radeon_device *rdev) | ||
180 | { | ||
181 | mutex_lock(&rdev->pm.mutex); | ||
182 | /* new VBLANK irq may come before handling previous one */ | ||
183 | if (rdev->pm.vblank_callback) { | ||
184 | mutex_lock(&rdev->cp.mutex); | ||
185 | if (rdev->pm.req_vblank & (1 << 0)) { | ||
186 | rdev->pm.req_vblank &= ~(1 << 0); | ||
187 | drm_vblank_put(rdev->ddev, 0); | ||
188 | } | ||
189 | if (rdev->pm.req_vblank & (1 << 1)) { | ||
190 | rdev->pm.req_vblank &= ~(1 << 1); | ||
191 | drm_vblank_put(rdev->ddev, 1); | ||
192 | } | ||
193 | rdev->pm.vblank_callback = false; | ||
194 | radeon_pm_set_clocks_locked(rdev); | ||
195 | mutex_unlock(&rdev->cp.mutex); | ||
196 | } | ||
197 | mutex_unlock(&rdev->pm.mutex); | ||
198 | } | ||
199 | |||
200 | static void radeon_pm_reclock_work_handler(struct work_struct *work) | ||
201 | { | ||
202 | struct radeon_device *rdev; | ||
203 | rdev = container_of(work, struct radeon_device, | ||
204 | pm.reclock_work); | ||
205 | radeon_pm_set_clocks(rdev); | ||
206 | } | ||
207 | |||
208 | static void radeon_pm_idle_work_handler(struct work_struct *work) | ||
209 | { | ||
210 | struct radeon_device *rdev; | ||
211 | rdev = container_of(work, struct radeon_device, | ||
212 | pm.idle_work.work); | ||
213 | |||
214 | mutex_lock(&rdev->pm.mutex); | ||
215 | if (rdev->pm.state == PM_STATE_ACTIVE && | ||
216 | !rdev->pm.vblank_callback) { | ||
217 | unsigned long irq_flags; | ||
218 | int not_processed = 0; | ||
219 | |||
220 | read_lock_irqsave(&rdev->fence_drv.lock, irq_flags); | ||
221 | if (!list_empty(&rdev->fence_drv.emited)) { | ||
222 | struct list_head *ptr; | ||
223 | list_for_each(ptr, &rdev->fence_drv.emited) { | ||
224 | /* count up to 3, that's enought info */ | ||
225 | if (++not_processed >= 3) | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | read_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags); | ||
230 | |||
231 | if (not_processed >= 3) { /* should upclock */ | ||
232 | if (rdev->pm.planned_action == PM_ACTION_DOWNCLOCK) { | ||
233 | rdev->pm.planned_action = PM_ACTION_NONE; | ||
234 | } else if (rdev->pm.planned_action == PM_ACTION_NONE && | ||
235 | rdev->pm.downclocked) { | ||
236 | rdev->pm.planned_action = | ||
237 | PM_ACTION_UPCLOCK; | ||
238 | rdev->pm.action_timeout = jiffies + | ||
239 | msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); | ||
240 | } | ||
241 | } else if (not_processed == 0) { /* should downclock */ | ||
242 | if (rdev->pm.planned_action == PM_ACTION_UPCLOCK) { | ||
243 | rdev->pm.planned_action = PM_ACTION_NONE; | ||
244 | } else if (rdev->pm.planned_action == PM_ACTION_NONE && | ||
245 | !rdev->pm.downclocked) { | ||
246 | rdev->pm.planned_action = | ||
247 | PM_ACTION_DOWNCLOCK; | ||
248 | rdev->pm.action_timeout = jiffies + | ||
249 | msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if (rdev->pm.planned_action != PM_ACTION_NONE && | ||
254 | jiffies > rdev->pm.action_timeout) { | ||
255 | if (rdev->pm.active_crtcs & (1 << 0)) { | ||
256 | rdev->pm.req_vblank |= (1 << 0); | ||
257 | drm_vblank_get(rdev->ddev, 0); | ||
258 | } | ||
259 | if (rdev->pm.active_crtcs & (1 << 1)) { | ||
260 | rdev->pm.req_vblank |= (1 << 1); | ||
261 | drm_vblank_get(rdev->ddev, 1); | ||
262 | } | ||
263 | rdev->pm.vblank_callback = true; | ||
264 | } | ||
265 | } | ||
266 | mutex_unlock(&rdev->pm.mutex); | ||
267 | |||
268 | queue_delayed_work(rdev->wq, &rdev->pm.idle_work, | ||
269 | msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); | ||
270 | } | ||
271 | |||
36 | /* | 272 | /* |
37 | * Debugfs info | 273 | * Debugfs info |
38 | */ | 274 | */ |
@@ -44,6 +280,7 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) | |||
44 | struct drm_device *dev = node->minor->dev; | 280 | struct drm_device *dev = node->minor->dev; |
45 | struct radeon_device *rdev = dev->dev_private; | 281 | struct radeon_device *rdev = dev->dev_private; |
46 | 282 | ||
283 | seq_printf(m, "state: %s\n", pm_state_names[rdev->pm.state]); | ||
47 | seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk); | 284 | seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk); |
48 | seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); | 285 | seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); |
49 | seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk); | 286 | seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk); |
@@ -58,7 +295,7 @@ static struct drm_info_list radeon_pm_info_list[] = { | |||
58 | }; | 295 | }; |
59 | #endif | 296 | #endif |
60 | 297 | ||
61 | int radeon_debugfs_pm_init(struct radeon_device *rdev) | 298 | static int radeon_debugfs_pm_init(struct radeon_device *rdev) |
62 | { | 299 | { |
63 | #if defined(CONFIG_DEBUG_FS) | 300 | #if defined(CONFIG_DEBUG_FS) |
64 | return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); | 301 | return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); |
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index c3818562a13e..a581fdead4dd 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c | |||
@@ -406,10 +406,16 @@ int rs600_irq_process(struct radeon_device *rdev) | |||
406 | if (G_000044_SW_INT(status)) | 406 | if (G_000044_SW_INT(status)) |
407 | radeon_fence_process(rdev); | 407 | radeon_fence_process(rdev); |
408 | /* Vertical blank interrupts */ | 408 | /* Vertical blank interrupts */ |
409 | if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) | 409 | if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) { |
410 | drm_handle_vblank(rdev->ddev, 0); | 410 | drm_handle_vblank(rdev->ddev, 0); |
411 | if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) | 411 | if (rdev->pm.vblank_callback) |
412 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
413 | } | ||
414 | if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) { | ||
412 | drm_handle_vblank(rdev->ddev, 1); | 415 | drm_handle_vblank(rdev->ddev, 1); |
416 | if (rdev->pm.vblank_callback) | ||
417 | queue_work(rdev->wq, &rdev->pm.reclock_work); | ||
418 | } | ||
413 | if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) { | 419 | if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) { |
414 | queue_hotplug = true; | 420 | queue_hotplug = true; |
415 | DRM_DEBUG("HPD1\n"); | 421 | DRM_DEBUG("HPD1\n"); |