]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/radeon/r100.c
drm/radeon/kms/pm: rework power management
[karo-tx-linux.git] / drivers / gpu / drm / radeon / r100.c
index b076b96f8b6c23b6c5cd7d60a9b576a8f2794128..7675827c3a8d50d024fbcba002e511521d1447b1 100644 (file)
@@ -37,6 +37,7 @@
 #include "rs100d.h"
 #include "rv200d.h"
 #include "rv250d.h"
+#include "atom.h"
 
 #include <linux/firmware.h>
 #include <linux/platform_device.h>
@@ -67,41 +68,296 @@ MODULE_FIRMWARE(FIRMWARE_R520);
  * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
  */
 
-void r100_set_power_state(struct radeon_device *rdev)
+void r100_get_power_state(struct radeon_device *rdev,
+                         enum radeon_pm_action action)
 {
-       /* if *_clock_mode are the same, *_power_state are as well */
-       if (rdev->pm.requested_clock_mode == rdev->pm.current_clock_mode)
+       int i;
+       rdev->pm.can_upclock = true;
+       rdev->pm.can_downclock = true;
+
+       switch (action) {
+       case PM_ACTION_MINIMUM:
+               rdev->pm.requested_power_state_index = 0;
+               rdev->pm.can_downclock = false;
+               break;
+       case PM_ACTION_DOWNCLOCK:
+               if (rdev->pm.current_power_state_index == 0) {
+                       rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
+                       rdev->pm.can_downclock = false;
+               } else {
+                       if (rdev->pm.active_crtc_count > 1) {
+                               for (i = 0; i < rdev->pm.num_power_states; i++) {
+                                       if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
+                                               continue;
+                                       else if (i >= rdev->pm.current_power_state_index) {
+                                               rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
+                                               break;
+                                       } else {
+                                               rdev->pm.requested_power_state_index = i;
+                                               break;
+                                       }
+                               }
+                       } else
+                               rdev->pm.requested_power_state_index =
+                                       rdev->pm.current_power_state_index - 1;
+               }
+               break;
+       case PM_ACTION_UPCLOCK:
+               if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) {
+                       rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
+                       rdev->pm.can_upclock = false;
+               } else {
+                       if (rdev->pm.active_crtc_count > 1) {
+                               for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
+                                       if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
+                                               continue;
+                                       else if (i <= rdev->pm.current_power_state_index) {
+                                               rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
+                                               break;
+                                       } else {
+                                               rdev->pm.requested_power_state_index = i;
+                                               break;
+                                       }
+                               }
+                       } else
+                               rdev->pm.requested_power_state_index =
+                                       rdev->pm.current_power_state_index + 1;
+               }
+               break;
+       case PM_ACTION_DEFAULT:
+               rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
+               rdev->pm.can_upclock = false;
+               break;
+       case PM_ACTION_NONE:
+       default:
+               DRM_ERROR("Requested mode for not defined action\n");
                return;
+       }
+       /* only one clock mode per power state */
+       rdev->pm.requested_clock_mode_index = 0;
 
-       DRM_INFO("Setting: e: %d m: %d p: %d\n",
-                rdev->pm.requested_clock_mode->sclk,
-                rdev->pm.requested_clock_mode->mclk,
-                rdev->pm.requested_power_state->non_clock_info.pcie_lanes);
+       DRM_INFO("Requested: e: %d m: %d p: %d\n",
+                rdev->pm.power_state[rdev->pm.requested_power_state_index].
+                clock_info[rdev->pm.requested_clock_mode_index].sclk,
+                rdev->pm.power_state[rdev->pm.requested_power_state_index].
+                clock_info[rdev->pm.requested_clock_mode_index].mclk,
+                rdev->pm.power_state[rdev->pm.requested_power_state_index].
+                pcie_lanes);
+}
 
-       /* set pcie lanes */
-       /* TODO */
+void r100_set_power_state(struct radeon_device *rdev, bool static_switch)
+{
+       u32 sclk, mclk;
 
-       /* set voltage */
-       /* TODO */
+       if (rdev->pm.current_power_state_index == rdev->pm.requested_power_state_index)
+               return;
 
-       /* set engine clock */
-       radeon_sync_with_vblank(rdev);
-       radeon_pm_debug_check_in_vbl(rdev, false);
-       radeon_set_engine_clock(rdev, rdev->pm.requested_clock_mode->sclk);
-       radeon_pm_debug_check_in_vbl(rdev, true);
+       if (radeon_gui_idle(rdev)) {
+
+               sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
+                       clock_info[rdev->pm.requested_clock_mode_index].sclk;
+               if (sclk > rdev->clock.default_sclk)
+                       sclk = rdev->clock.default_sclk;
+
+               mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
+                       clock_info[rdev->pm.requested_clock_mode_index].mclk;
+               if (mclk > rdev->clock.default_mclk)
+                       mclk = rdev->clock.default_mclk;
+               /* don't change the mclk with multiple crtcs */
+               if (rdev->pm.active_crtc_count > 1)
+                       mclk = rdev->clock.default_mclk;
+
+               /* voltage, pcie lanes, etc.*/
+               radeon_pm_misc(rdev);
+
+               if (static_switch) {
+                       radeon_pm_prepare(rdev);
+                       /* set engine clock */
+                       if (sclk != rdev->pm.current_sclk) {
+                               radeon_set_engine_clock(rdev, sclk);
+                               rdev->pm.current_sclk = sclk;
+                               DRM_INFO("Setting: e: %d\n", sclk);
+                       }
+#if 0
+                       /* set memory clock */
+                       if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+                               radeon_set_memory_clock(rdev, mclk);
+                               rdev->pm.current_mclk = mclk;
+                               DRM_INFO("Setting: m: %d\n", mclk);
+                       }
+#endif
+                       radeon_pm_finish(rdev);
+               } else {
+                       /* set engine clock */
+                       if (sclk != rdev->pm.current_sclk) {
+                               radeon_sync_with_vblank(rdev);
+                               radeon_pm_debug_check_in_vbl(rdev, false);
+                               radeon_set_engine_clock(rdev, sclk);
+                               radeon_pm_debug_check_in_vbl(rdev, true);
+                               rdev->pm.current_sclk = sclk;
+                               DRM_INFO("Setting: e: %d\n", sclk);
+                       }
 
 #if 0
-       /* set memory clock */
-       if (rdev->asic->set_memory_clock) {
-               radeon_sync_with_vblank(rdev);
-               radeon_pm_debug_check_in_vbl(rdev, false);
-               radeon_set_memory_clock(rdev, rdev->pm.requested_clock_mode->mclk);
-               radeon_pm_debug_check_in_vbl(rdev, true);
+                       /* set memory clock */
+                       if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+                               radeon_sync_with_vblank(rdev);
+                               radeon_pm_debug_check_in_vbl(rdev, false);
+                               radeon_pm_prepare(rdev);
+                               radeon_set_memory_clock(rdev, mclk);
+                               radeon_pm_finish(rdev);
+                               radeon_pm_debug_check_in_vbl(rdev, true);
+                               rdev->pm.current_mclk = mclk;
+                               DRM_INFO("Setting: m: %d\n", mclk);
+                       }
+#endif
+               }
+
+               rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
+               rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
+       } else
+               DRM_INFO("GUI not idle!!!\n");
+}
+
+void r100_pm_misc(struct radeon_device *rdev)
+{
+#if 0
+       int requested_index = rdev->pm.requested_power_state_index;
+       struct radeon_power_state *ps = &rdev->pm.power_state[requested_index];
+       struct radeon_voltage *voltage = &ps->clock_info[0].voltage;
+       u32 tmp, sclk_cntl, sclk_cntl2, sclk_more_cntl;
+
+       if ((voltage->type == VOLTAGE_GPIO) && (voltage->gpio.valid)) {
+               if (ps->misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                       tmp = RREG32(voltage->gpio.reg);
+                       if (voltage->active_high)
+                               tmp |= voltage->gpio.mask;
+                       else
+                               tmp &= ~(voltage->gpio.mask);
+                       WREG32(voltage->gpio.reg, tmp);
+                       if (voltage->delay)
+                               udelay(voltage->delay);
+               } else {
+                       tmp = RREG32(voltage->gpio.reg);
+                       if (voltage->active_high)
+                               tmp &= ~voltage->gpio.mask;
+                       else
+                               tmp |= voltage->gpio.mask;
+                       WREG32(voltage->gpio.reg, tmp);
+                       if (voltage->delay)
+                               udelay(voltage->delay);
+               }
+       }
+
+       sclk_cntl = RREG32_PLL(SCLK_CNTL);
+       sclk_cntl2 = RREG32_PLL(SCLK_CNTL2);
+       sclk_cntl2 &= ~REDUCED_SPEED_SCLK_SEL(3);
+       sclk_more_cntl = RREG32_PLL(SCLK_MORE_CNTL);
+       sclk_more_cntl &= ~VOLTAGE_DELAY_SEL(3);
+       if (ps->misc & ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN) {
+               sclk_more_cntl |= REDUCED_SPEED_SCLK_EN;
+               if (ps->misc & ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE)
+                       sclk_cntl2 |= REDUCED_SPEED_SCLK_MODE;
+               else
+                       sclk_cntl2 &= ~REDUCED_SPEED_SCLK_MODE;
+               if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2)
+                       sclk_cntl2 |= REDUCED_SPEED_SCLK_SEL(0);
+               else if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4)
+                       sclk_cntl2 |= REDUCED_SPEED_SCLK_SEL(2);
+       } else
+               sclk_more_cntl &= ~REDUCED_SPEED_SCLK_EN;
+
+       if (ps->misc & ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN) {
+               sclk_more_cntl |= IO_CG_VOLTAGE_DROP;
+               if (voltage->delay) {
+                       sclk_more_cntl |= VOLTAGE_DROP_SYNC;
+                       switch (voltage->delay) {
+                       case 33:
+                               sclk_more_cntl |= VOLTAGE_DELAY_SEL(0);
+                               break;
+                       case 66:
+                               sclk_more_cntl |= VOLTAGE_DELAY_SEL(1);
+                               break;
+                       case 99:
+                               sclk_more_cntl |= VOLTAGE_DELAY_SEL(2);
+                               break;
+                       case 132:
+                               sclk_more_cntl |= VOLTAGE_DELAY_SEL(3);
+                               break;
+                       }
+               } else
+                       sclk_more_cntl &= ~VOLTAGE_DROP_SYNC;
+       } else
+               sclk_more_cntl &= ~IO_CG_VOLTAGE_DROP;
+
+       if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN)
+               sclk_cntl &= ~FORCE_HDP;
+       else
+               sclk_cntl |= FORCE_HDP;
+
+       WREG32_PLL(SCLK_CNTL, sclk_cntl);
+       WREG32_PLL(SCLK_CNTL2, sclk_cntl2);
+       WREG32_PLL(SCLK_MORE_CNTL, sclk_more_cntl);
+
+       /* set pcie lanes */
+       if ((rdev->flags & RADEON_IS_PCIE) &&
+           !(rdev->flags & RADEON_IS_IGP) &&
+           rdev->asic->set_pcie_lanes &&
+           (ps->pcie_lanes !=
+            rdev->pm.power_state[rdev->pm.current_power_state_index].pcie_lanes)) {
+               radeon_set_pcie_lanes(rdev,
+                                     ps->pcie_lanes);
+               DRM_INFO("Setting: p: %d\n", ps->pcie_lanes);
        }
 #endif
+}
 
-       rdev->pm.current_power_state = rdev->pm.requested_power_state;
-       rdev->pm.current_clock_mode = rdev->pm.requested_clock_mode;
+void r100_pm_prepare(struct radeon_device *rdev)
+{
+       struct drm_device *ddev = rdev->ddev;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+       u32 tmp;
+
+       /* disable any active CRTCs */
+       list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) {
+               radeon_crtc = to_radeon_crtc(crtc);
+               if (radeon_crtc->enabled) {
+                       if (radeon_crtc->crtc_id) {
+                               tmp = RREG32(RADEON_CRTC2_GEN_CNTL);
+                               tmp |= RADEON_CRTC2_DISP_REQ_EN_B;
+                               WREG32(RADEON_CRTC2_GEN_CNTL, tmp);
+                       } else {
+                               tmp = RREG32(RADEON_CRTC_GEN_CNTL);
+                               tmp |= RADEON_CRTC_DISP_REQ_EN_B;
+                               WREG32(RADEON_CRTC_GEN_CNTL, tmp);
+                       }
+               }
+       }
+}
+
+void r100_pm_finish(struct radeon_device *rdev)
+{
+       struct drm_device *ddev = rdev->ddev;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+       u32 tmp;
+
+       /* enable any active CRTCs */
+       list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) {
+               radeon_crtc = to_radeon_crtc(crtc);
+               if (radeon_crtc->enabled) {
+                       if (radeon_crtc->crtc_id) {
+                               tmp = RREG32(RADEON_CRTC2_GEN_CNTL);
+                               tmp &= ~RADEON_CRTC2_DISP_REQ_EN_B;
+                               WREG32(RADEON_CRTC2_GEN_CNTL, tmp);
+                       } else {
+                               tmp = RREG32(RADEON_CRTC_GEN_CNTL);
+                               tmp &= ~RADEON_CRTC_DISP_REQ_EN_B;
+                               WREG32(RADEON_CRTC_GEN_CNTL, tmp);
+                       }
+               }
+       }
 }
 
 bool r100_gui_idle(struct radeon_device *rdev)