radeon: Try harder to ensure we reclock in vblank
[safe/jmp/linux-2.6] / drivers / gpu / drm / radeon / r600.c
index 1c85dcb..d3a79e0 100644 (file)
@@ -92,6 +92,262 @@ void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 void r600_irq_disable(struct radeon_device *rdev);
 
+void r600_get_power_state(struct radeon_device *rdev,
+                         enum radeon_pm_action action)
+{
+       int i;
+
+       rdev->pm.can_upclock = true;
+       rdev->pm.can_downclock = true;
+
+       /* power state array is low to high, default is first */
+       if ((rdev->flags & RADEON_IS_IGP) || (rdev->family == CHIP_R600)) {
+               int min_power_state_index = 0;
+
+               if (rdev->pm.num_power_states > 2)
+                       min_power_state_index = 1;
+
+               switch (action) {
+               case PM_ACTION_MINIMUM:
+                       rdev->pm.requested_power_state_index = min_power_state_index;
+                       rdev->pm.requested_clock_mode_index = 0;
+                       rdev->pm.can_downclock = false;
+                       break;
+               case PM_ACTION_DOWNCLOCK:
+                       if (rdev->pm.current_power_state_index == min_power_state_index) {
+                               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;
+                       }
+                       rdev->pm.requested_clock_mode_index = 0;
+                       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;
+                       }
+                       rdev->pm.requested_clock_mode_index = 0;
+                       break;
+               case PM_ACTION_DEFAULT:
+                       rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
+                       rdev->pm.requested_clock_mode_index = 0;
+                       rdev->pm.can_upclock = false;
+                       break;
+               case PM_ACTION_NONE:
+               default:
+                       DRM_ERROR("Requested mode for not defined action\n");
+                       return;
+               }
+       } else {
+               /* XXX select a power state based on AC/DC, single/dualhead, etc. */
+               /* for now just select the first power state and switch between clock modes */
+               /* power state array is low to high, default is first (0) */
+               if (rdev->pm.active_crtc_count > 1) {
+                       rdev->pm.requested_power_state_index = -1;
+                       /* start at 1 as we don't want the default mode */
+                       for (i = 1; i < rdev->pm.num_power_states; i++) {
+                               if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
+                                       continue;
+                               else if ((rdev->pm.power_state[i].type == POWER_STATE_TYPE_PERFORMANCE) ||
+                                        (rdev->pm.power_state[i].type == POWER_STATE_TYPE_BATTERY)) {
+                                       rdev->pm.requested_power_state_index = i;
+                                       break;
+                               }
+                       }
+                       /* if nothing selected, grab the default state. */
+                       if (rdev->pm.requested_power_state_index == -1)
+                               rdev->pm.requested_power_state_index = 0;
+               } else
+                       rdev->pm.requested_power_state_index = 1;
+
+               switch (action) {
+               case PM_ACTION_MINIMUM:
+                       rdev->pm.requested_clock_mode_index = 0;
+                       rdev->pm.can_downclock = false;
+                       break;
+               case PM_ACTION_DOWNCLOCK:
+                       if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
+                               if (rdev->pm.current_clock_mode_index == 0) {
+                                       rdev->pm.requested_clock_mode_index = 0;
+                                       rdev->pm.can_downclock = false;
+                               } else
+                                       rdev->pm.requested_clock_mode_index =
+                                               rdev->pm.current_clock_mode_index - 1;
+                       } else {
+                               rdev->pm.requested_clock_mode_index = 0;
+                               rdev->pm.can_downclock = false;
+                       }
+                       break;
+               case PM_ACTION_UPCLOCK:
+                       if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
+                               if (rdev->pm.current_clock_mode_index ==
+                                   (rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1)) {
+                                       rdev->pm.requested_clock_mode_index = rdev->pm.current_clock_mode_index;
+                                       rdev->pm.can_upclock = false;
+                               } else
+                                       rdev->pm.requested_clock_mode_index =
+                                               rdev->pm.current_clock_mode_index + 1;
+                       } else {
+                               rdev->pm.requested_clock_mode_index =
+                                       rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1;
+                               rdev->pm.can_upclock = false;
+                       }
+                       break;
+               case PM_ACTION_DEFAULT:
+                       rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
+                       rdev->pm.requested_clock_mode_index = 0;
+                       rdev->pm.can_upclock = false;
+                       break;
+               case PM_ACTION_NONE:
+               default:
+                       DRM_ERROR("Requested mode for not defined action\n");
+                       return;
+               }
+       }
+
+       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);
+}
+
+void r600_set_power_state(struct radeon_device *rdev, bool static_switch)
+{
+       u32 sclk, mclk;
+
+       if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
+           (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
+               return;
+
+       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;
+
+               /* voltage, pcie lanes, etc.*/
+               radeon_pm_misc(rdev);
+
+               if (static_switch) {
+
+                       /* 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);
+                       }
+
+                       /* set memory clock */
+                       if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+                               radeon_pm_prepare(rdev);
+                               radeon_set_memory_clock(rdev, mclk);
+                               radeon_pm_finish(rdev);
+                               rdev->pm.current_mclk = mclk;
+                               DRM_INFO("Setting: m: %d\n", mclk);
+                       }
+
+               } else {
+                       u32 position;
+                       u32 vbl;
+
+                       radeon_sync_with_vblank(rdev);
+
+                       if (!radeon_pm_in_vbl(rdev))
+                               return;
+
+                       if (rdev->pm.active_crtcs & (1 << 0)) {
+                               vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
+                               position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
+                               position &= 0xfff;
+                               vbl &= 0xfff;
+
+                               if (position < vbl && position > 1)
+                                       return;
+                       }
+
+                       if (rdev->pm.active_crtcs & (1 << 1)) {
+                               vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
+                               position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
+                               position &= 0xfff;
+                               vbl &= 0xfff;
+
+                               if (position < vbl && position > 1)
+                                       return;
+                       }
+
+                       if (sclk != rdev->pm.current_sclk) {
+                               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);
+                       }
+
+                       /* set memory clock */
+                       if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+                               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);
+                       }
+               }
+
+               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 r600_pm_misc(struct radeon_device *rdev)
+{
+
+}
+
 bool r600_gui_idle(struct radeon_device *rdev)
 {
        if (RREG32(GRBM_STATUS) & GUI_ACTIVE)
@@ -2535,6 +2791,7 @@ int r600_irq_set(struct radeon_device *rdev)
        u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
        u32 mode_int = 0;
        u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
+       u32 grbm_int_cntl = 0;
        u32 hdmi1, hdmi2;
 
        if (!rdev->irq.installed) {
@@ -2611,9 +2868,14 @@ int r600_irq_set(struct radeon_device *rdev)
                DRM_DEBUG("r600_irq_set: hdmi 2\n");
                hdmi2 |= R600_HDMI_INT_EN;
        }
+       if (rdev->irq.gui_idle) {
+               DRM_DEBUG("gui idle\n");
+               grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
+       }
 
        WREG32(CP_INT_CNTL, cp_int_cntl);
        WREG32(DxMODE_INT_MASK, mode_int);
+       WREG32(GRBM_INT_CNTL, grbm_int_cntl);
        WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
        if (ASIC_IS_DCE3(rdev)) {
                WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
@@ -2929,6 +3191,11 @@ restart_ih:
                case 181: /* CP EOP event */
                        DRM_DEBUG("IH: CP EOP\n");
                        break;
+               case 233: /* GUI IDLE */
+                       DRM_DEBUG("IH: CP EOP\n");
+                       rdev->pm.gui_idle = true;
+                       wake_up(&rdev->irq.idle_queue);
+                       break;
                default:
                        DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
                        break;