1926deccbSFrançois Tigeot /* 2926deccbSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 3926deccbSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 4926deccbSFrançois Tigeot * to deal in the Software without restriction, including without limitation 5926deccbSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 6926deccbSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 7926deccbSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 8926deccbSFrançois Tigeot * 9926deccbSFrançois Tigeot * The above copyright notice and this permission notice shall be included in 10926deccbSFrançois Tigeot * all copies or substantial portions of the Software. 11926deccbSFrançois Tigeot * 12926deccbSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13926deccbSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14926deccbSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 15926deccbSFrançois Tigeot * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 16926deccbSFrançois Tigeot * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 17926deccbSFrançois Tigeot * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 18926deccbSFrançois Tigeot * OTHER DEALINGS IN THE SOFTWARE. 19926deccbSFrançois Tigeot * 20926deccbSFrançois Tigeot * Authors: Rafał Miłecki <zajec5@gmail.com> 21926deccbSFrançois Tigeot * Alex Deucher <alexdeucher@gmail.com> 22926deccbSFrançois Tigeot * 23926deccbSFrançois Tigeot * $FreeBSD: head/sys/dev/drm2/radeon/radeon_pm.c 254885 2013-08-25 19:37:15Z dumbbell $ 24926deccbSFrançois Tigeot */ 25926deccbSFrançois Tigeot 2657e252bfSMichael Neumann #include <sys/power.h> 27926deccbSFrançois Tigeot #include <drm/drmP.h> 282c86cb5bSImre Vadász #include <sys/sensors.h> 29926deccbSFrançois Tigeot #include "radeon.h" 30926deccbSFrançois Tigeot #include "avivod.h" 31926deccbSFrançois Tigeot #include "atom.h" 32926deccbSFrançois Tigeot 33926deccbSFrançois Tigeot #define RADEON_IDLE_LOOP_MS 100 34926deccbSFrançois Tigeot #define RADEON_RECLOCK_DELAY_MS 200 35926deccbSFrançois Tigeot #define RADEON_WAIT_VBLANK_TIMEOUT 200 36926deccbSFrançois Tigeot 37926deccbSFrançois Tigeot static const char *radeon_pm_state_type_name[5] = { 38926deccbSFrançois Tigeot "", 39926deccbSFrançois Tigeot "Powersave", 40926deccbSFrançois Tigeot "Battery", 41926deccbSFrançois Tigeot "Balanced", 42926deccbSFrançois Tigeot "Performance", 43926deccbSFrançois Tigeot }; 44926deccbSFrançois Tigeot 45926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 46926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work); 47926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 48926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev); 49926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev); 50926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish); 51926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev); 52926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev); 53926deccbSFrançois Tigeot 54926deccbSFrançois Tigeot int radeon_pm_get_type_index(struct radeon_device *rdev, 55926deccbSFrançois Tigeot enum radeon_pm_state_type ps_type, 56926deccbSFrançois Tigeot int instance) 57926deccbSFrançois Tigeot { 58926deccbSFrançois Tigeot int i; 59926deccbSFrançois Tigeot int found_instance = -1; 60926deccbSFrançois Tigeot 61926deccbSFrançois Tigeot for (i = 0; i < rdev->pm.num_power_states; i++) { 62926deccbSFrançois Tigeot if (rdev->pm.power_state[i].type == ps_type) { 63926deccbSFrançois Tigeot found_instance++; 64926deccbSFrançois Tigeot if (found_instance == instance) 65926deccbSFrançois Tigeot return i; 66926deccbSFrançois Tigeot } 67926deccbSFrançois Tigeot } 68926deccbSFrançois Tigeot /* return default if no match */ 69926deccbSFrançois Tigeot return rdev->pm.default_power_state_index; 70926deccbSFrançois Tigeot } 71926deccbSFrançois Tigeot 72926deccbSFrançois Tigeot void radeon_pm_acpi_event_handler(struct radeon_device *rdev) 73926deccbSFrançois Tigeot { 744cd92098Szrj if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 754cd92098Szrj lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 76c6f73aabSFrançois Tigeot if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) 774cd92098Szrj rdev->pm.dpm.ac_power = true; 784cd92098Szrj else 794cd92098Szrj rdev->pm.dpm.ac_power = false; 80c6f73aabSFrançois Tigeot if (rdev->family == CHIP_ARUBA) { 814cd92098Szrj if (rdev->asic->dpm.enable_bapm) 824cd92098Szrj radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power); 83c6f73aabSFrançois Tigeot } 844cd92098Szrj lockmgr(&rdev->pm.mutex, LK_RELEASE); 854cd92098Szrj } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 86926deccbSFrançois Tigeot if (rdev->pm.profile == PM_PROFILE_AUTO) { 87926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 88926deccbSFrançois Tigeot radeon_pm_update_profile(rdev); 89926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 90926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 91926deccbSFrançois Tigeot } 92926deccbSFrançois Tigeot } 93926deccbSFrançois Tigeot } 94926deccbSFrançois Tigeot 95926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev) 96926deccbSFrançois Tigeot { 97926deccbSFrançois Tigeot switch (rdev->pm.profile) { 98926deccbSFrançois Tigeot case PM_PROFILE_DEFAULT: 99926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX; 100926deccbSFrançois Tigeot break; 101926deccbSFrançois Tigeot case PM_PROFILE_AUTO: 10257e252bfSMichael Neumann if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) { 103926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) 104926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 105926deccbSFrançois Tigeot else 106926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 107926deccbSFrançois Tigeot } else { 108926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) 109926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 110926deccbSFrançois Tigeot else 111926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 112926deccbSFrançois Tigeot } 113926deccbSFrançois Tigeot break; 114926deccbSFrançois Tigeot case PM_PROFILE_LOW: 115926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) 116926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX; 117926deccbSFrançois Tigeot else 118926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX; 119926deccbSFrançois Tigeot break; 120926deccbSFrançois Tigeot case PM_PROFILE_MID: 121926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) 122926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 123926deccbSFrançois Tigeot else 124926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 125926deccbSFrançois Tigeot break; 126926deccbSFrançois Tigeot case PM_PROFILE_HIGH: 127926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) 128926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 129926deccbSFrançois Tigeot else 130926deccbSFrançois Tigeot rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 131926deccbSFrançois Tigeot break; 132926deccbSFrançois Tigeot } 133926deccbSFrançois Tigeot 134926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count == 0) { 135926deccbSFrançois Tigeot rdev->pm.requested_power_state_index = 136926deccbSFrançois Tigeot rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx; 137926deccbSFrançois Tigeot rdev->pm.requested_clock_mode_index = 138926deccbSFrançois Tigeot rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx; 139926deccbSFrançois Tigeot } else { 140926deccbSFrançois Tigeot rdev->pm.requested_power_state_index = 141926deccbSFrançois Tigeot rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx; 142926deccbSFrançois Tigeot rdev->pm.requested_clock_mode_index = 143926deccbSFrançois Tigeot rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx; 144926deccbSFrançois Tigeot } 145926deccbSFrançois Tigeot } 146926deccbSFrançois Tigeot 147926deccbSFrançois Tigeot static void radeon_unmap_vram_bos(struct radeon_device *rdev) 148926deccbSFrançois Tigeot { 149926deccbSFrançois Tigeot struct radeon_bo *bo, *n; 150926deccbSFrançois Tigeot 151926deccbSFrançois Tigeot if (list_empty(&rdev->gem.objects)) 152926deccbSFrançois Tigeot return; 153926deccbSFrançois Tigeot 154926deccbSFrançois Tigeot list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) { 155926deccbSFrançois Tigeot if (bo->tbo.mem.mem_type == TTM_PL_VRAM) 156926deccbSFrançois Tigeot ttm_bo_unmap_virtual(&bo->tbo); 157926deccbSFrançois Tigeot } 158926deccbSFrançois Tigeot } 159926deccbSFrançois Tigeot 160926deccbSFrançois Tigeot static void radeon_sync_with_vblank(struct radeon_device *rdev) 161926deccbSFrançois Tigeot { 162926deccbSFrançois Tigeot if (rdev->pm.active_crtcs) { 163926deccbSFrançois Tigeot rdev->pm.vblank_sync = false; 164926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 165926deccbSFrançois Tigeot wait_event_timeout( 166926deccbSFrançois Tigeot rdev->irq.vblank_queue, rdev->pm.vblank_sync, 167926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT)); 168926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 169926deccbSFrançois Tigeot } 170926deccbSFrançois Tigeot } 171926deccbSFrançois Tigeot 172926deccbSFrançois Tigeot static void radeon_set_power_state(struct radeon_device *rdev) 173926deccbSFrançois Tigeot { 174926deccbSFrançois Tigeot u32 sclk, mclk; 175926deccbSFrançois Tigeot bool misc_after = false; 176926deccbSFrançois Tigeot 177926deccbSFrançois Tigeot if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 178926deccbSFrançois Tigeot (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 179926deccbSFrançois Tigeot return; 180926deccbSFrançois Tigeot 181926deccbSFrançois Tigeot if (radeon_gui_idle(rdev)) { 182926deccbSFrançois Tigeot sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 183926deccbSFrançois Tigeot clock_info[rdev->pm.requested_clock_mode_index].sclk; 184926deccbSFrançois Tigeot if (sclk > rdev->pm.default_sclk) 185926deccbSFrançois Tigeot sclk = rdev->pm.default_sclk; 186926deccbSFrançois Tigeot 187926deccbSFrançois Tigeot /* starting with BTC, there is one state that is used for both 188926deccbSFrançois Tigeot * MH and SH. Difference is that we always use the high clock index for 189b403bed8SMichael Neumann * mclk and vddci. 190926deccbSFrançois Tigeot */ 191926deccbSFrançois Tigeot if ((rdev->pm.pm_method == PM_METHOD_PROFILE) && 192926deccbSFrançois Tigeot (rdev->family >= CHIP_BARTS) && 193926deccbSFrançois Tigeot rdev->pm.active_crtc_count && 194926deccbSFrançois Tigeot ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) || 195926deccbSFrançois Tigeot (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX))) 196926deccbSFrançois Tigeot mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 197926deccbSFrançois Tigeot clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk; 198926deccbSFrançois Tigeot else 199926deccbSFrançois Tigeot mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 200926deccbSFrançois Tigeot clock_info[rdev->pm.requested_clock_mode_index].mclk; 201926deccbSFrançois Tigeot 202926deccbSFrançois Tigeot if (mclk > rdev->pm.default_mclk) 203926deccbSFrançois Tigeot mclk = rdev->pm.default_mclk; 204926deccbSFrançois Tigeot 205926deccbSFrançois Tigeot /* upvolt before raising clocks, downvolt after lowering clocks */ 206926deccbSFrançois Tigeot if (sclk < rdev->pm.current_sclk) 207926deccbSFrançois Tigeot misc_after = true; 208926deccbSFrançois Tigeot 209926deccbSFrançois Tigeot radeon_sync_with_vblank(rdev); 210926deccbSFrançois Tigeot 211926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 212926deccbSFrançois Tigeot if (!radeon_pm_in_vbl(rdev)) 213926deccbSFrançois Tigeot return; 214926deccbSFrançois Tigeot } 215926deccbSFrançois Tigeot 216926deccbSFrançois Tigeot radeon_pm_prepare(rdev); 217926deccbSFrançois Tigeot 218926deccbSFrançois Tigeot if (!misc_after) 219926deccbSFrançois Tigeot /* voltage, pcie lanes, etc.*/ 220926deccbSFrançois Tigeot radeon_pm_misc(rdev); 221926deccbSFrançois Tigeot 222926deccbSFrançois Tigeot /* set engine clock */ 223926deccbSFrançois Tigeot if (sclk != rdev->pm.current_sclk) { 224926deccbSFrançois Tigeot radeon_pm_debug_check_in_vbl(rdev, false); 225926deccbSFrançois Tigeot radeon_set_engine_clock(rdev, sclk); 226926deccbSFrançois Tigeot radeon_pm_debug_check_in_vbl(rdev, true); 227926deccbSFrançois Tigeot rdev->pm.current_sclk = sclk; 228926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk); 229926deccbSFrançois Tigeot } 230926deccbSFrançois Tigeot 231926deccbSFrançois Tigeot /* set memory clock */ 232926deccbSFrançois Tigeot if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) { 233926deccbSFrançois Tigeot radeon_pm_debug_check_in_vbl(rdev, false); 234926deccbSFrançois Tigeot radeon_set_memory_clock(rdev, mclk); 235926deccbSFrançois Tigeot radeon_pm_debug_check_in_vbl(rdev, true); 236926deccbSFrançois Tigeot rdev->pm.current_mclk = mclk; 237926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk); 238926deccbSFrançois Tigeot } 239926deccbSFrançois Tigeot 240926deccbSFrançois Tigeot if (misc_after) 241926deccbSFrançois Tigeot /* voltage, pcie lanes, etc.*/ 242926deccbSFrançois Tigeot radeon_pm_misc(rdev); 243926deccbSFrançois Tigeot 244926deccbSFrançois Tigeot radeon_pm_finish(rdev); 245926deccbSFrançois Tigeot 246926deccbSFrançois Tigeot rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; 247926deccbSFrançois Tigeot rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; 248926deccbSFrançois Tigeot } else 249926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n"); 250926deccbSFrançois Tigeot } 251926deccbSFrançois Tigeot 252926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev) 253926deccbSFrançois Tigeot { 254926deccbSFrançois Tigeot int i, r; 255926deccbSFrançois Tigeot 256926deccbSFrançois Tigeot /* no need to take locks, etc. if nothing's going to change */ 257926deccbSFrançois Tigeot if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 258926deccbSFrançois Tigeot (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 259926deccbSFrançois Tigeot return; 260926deccbSFrançois Tigeot 261926deccbSFrançois Tigeot DRM_LOCK(rdev->ddev); 26257e252bfSMichael Neumann lockmgr(&rdev->pm.mclk_lock, LK_EXCLUSIVE); // down_write 263926deccbSFrançois Tigeot lockmgr(&rdev->ring_lock, LK_EXCLUSIVE); 264926deccbSFrançois Tigeot 265926deccbSFrançois Tigeot /* wait for the rings to drain */ 266926deccbSFrançois Tigeot for (i = 0; i < RADEON_NUM_RINGS; i++) { 267926deccbSFrançois Tigeot struct radeon_ring *ring = &rdev->ring[i]; 268926deccbSFrançois Tigeot if (!ring->ready) { 269926deccbSFrançois Tigeot continue; 270926deccbSFrançois Tigeot } 271c6f73aabSFrançois Tigeot r = radeon_fence_wait_empty(rdev, i); 272926deccbSFrançois Tigeot if (r) { 273926deccbSFrançois Tigeot /* needs a GPU reset dont reset here */ 274926deccbSFrançois Tigeot lockmgr(&rdev->ring_lock, LK_RELEASE); 27557e252bfSMichael Neumann lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write 276926deccbSFrançois Tigeot DRM_UNLOCK(rdev->ddev); 277926deccbSFrançois Tigeot return; 278926deccbSFrançois Tigeot } 279926deccbSFrançois Tigeot } 280926deccbSFrançois Tigeot 281926deccbSFrançois Tigeot radeon_unmap_vram_bos(rdev); 282926deccbSFrançois Tigeot 283926deccbSFrançois Tigeot if (rdev->irq.installed) { 284926deccbSFrançois Tigeot for (i = 0; i < rdev->num_crtc; i++) { 285926deccbSFrançois Tigeot if (rdev->pm.active_crtcs & (1 << i)) { 286926deccbSFrançois Tigeot rdev->pm.req_vblank |= (1 << i); 287926deccbSFrançois Tigeot drm_vblank_get(rdev->ddev, i); 288926deccbSFrançois Tigeot } 289926deccbSFrançois Tigeot } 290926deccbSFrançois Tigeot } 291926deccbSFrançois Tigeot 292926deccbSFrançois Tigeot radeon_set_power_state(rdev); 293926deccbSFrançois Tigeot 294926deccbSFrançois Tigeot if (rdev->irq.installed) { 295926deccbSFrançois Tigeot for (i = 0; i < rdev->num_crtc; i++) { 296926deccbSFrançois Tigeot if (rdev->pm.req_vblank & (1 << i)) { 297926deccbSFrançois Tigeot rdev->pm.req_vblank &= ~(1 << i); 298926deccbSFrançois Tigeot drm_vblank_put(rdev->ddev, i); 299926deccbSFrançois Tigeot } 300926deccbSFrançois Tigeot } 301926deccbSFrançois Tigeot } 302926deccbSFrançois Tigeot 303926deccbSFrançois Tigeot /* update display watermarks based on new power state */ 304926deccbSFrançois Tigeot radeon_update_bandwidth_info(rdev); 305926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count) 306926deccbSFrançois Tigeot radeon_bandwidth_update(rdev); 307926deccbSFrançois Tigeot 308926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 309926deccbSFrançois Tigeot 310926deccbSFrançois Tigeot lockmgr(&rdev->ring_lock, LK_RELEASE); 31157e252bfSMichael Neumann lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write 312926deccbSFrançois Tigeot DRM_UNLOCK(rdev->ddev); 313926deccbSFrançois Tigeot } 314926deccbSFrançois Tigeot 315926deccbSFrançois Tigeot static void radeon_pm_print_states(struct radeon_device *rdev) 316926deccbSFrançois Tigeot { 317926deccbSFrançois Tigeot int i, j; 318926deccbSFrançois Tigeot struct radeon_power_state *power_state; 319926deccbSFrançois Tigeot struct radeon_pm_clock_info *clock_info; 320926deccbSFrançois Tigeot 321926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states); 322926deccbSFrançois Tigeot for (i = 0; i < rdev->pm.num_power_states; i++) { 323926deccbSFrançois Tigeot power_state = &rdev->pm.power_state[i]; 324926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("State %d: %s\n", i, 325926deccbSFrançois Tigeot radeon_pm_state_type_name[power_state->type]); 326926deccbSFrançois Tigeot if (i == rdev->pm.default_power_state_index) 327926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\tDefault"); 328926deccbSFrançois Tigeot if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP)) 329926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes); 330926deccbSFrançois Tigeot if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) 331926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\tSingle display only\n"); 332926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes); 333926deccbSFrançois Tigeot for (j = 0; j < power_state->num_clock_modes; j++) { 334926deccbSFrançois Tigeot clock_info = &(power_state->clock_info[j]); 335926deccbSFrançois Tigeot if (rdev->flags & RADEON_IS_IGP) 336926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\t\t%d e: %d\n", 337926deccbSFrançois Tigeot j, 338926deccbSFrançois Tigeot clock_info->sclk * 10); 339926deccbSFrançois Tigeot else 340926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n", 341926deccbSFrançois Tigeot j, 342926deccbSFrançois Tigeot clock_info->sclk * 10, 343926deccbSFrançois Tigeot clock_info->mclk * 10, 344926deccbSFrançois Tigeot clock_info->voltage.voltage); 345926deccbSFrançois Tigeot } 346926deccbSFrançois Tigeot } 347926deccbSFrançois Tigeot } 348926deccbSFrançois Tigeot 349926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 350926deccbSFrançois Tigeot static ssize_t radeon_get_pm_profile(struct device *dev, 351926deccbSFrançois Tigeot struct device_attribute *attr, 352926deccbSFrançois Tigeot char *buf) 353926deccbSFrançois Tigeot { 354c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 355926deccbSFrançois Tigeot struct radeon_device *rdev = ddev->dev_private; 356926deccbSFrançois Tigeot int cp = rdev->pm.profile; 357926deccbSFrançois Tigeot 358926deccbSFrançois Tigeot return ksnprintf(buf, PAGE_SIZE, "%s\n", 359926deccbSFrançois Tigeot (cp == PM_PROFILE_AUTO) ? "auto" : 360926deccbSFrançois Tigeot (cp == PM_PROFILE_LOW) ? "low" : 361926deccbSFrançois Tigeot (cp == PM_PROFILE_MID) ? "mid" : 362926deccbSFrançois Tigeot (cp == PM_PROFILE_HIGH) ? "high" : "default"); 363926deccbSFrançois Tigeot } 364926deccbSFrançois Tigeot 365926deccbSFrançois Tigeot static ssize_t radeon_set_pm_profile(struct device *dev, 366926deccbSFrançois Tigeot struct device_attribute *attr, 367926deccbSFrançois Tigeot const char *buf, 368926deccbSFrançois Tigeot size_t count) 369926deccbSFrançois Tigeot { 370c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 371926deccbSFrançois Tigeot struct radeon_device *rdev = ddev->dev_private; 372926deccbSFrançois Tigeot 373c6f73aabSFrançois Tigeot /* Can't set profile when the card is off */ 374c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 375c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 376c6f73aabSFrançois Tigeot return -EINVAL; 377c6f73aabSFrançois Tigeot 378926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 379926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 380926deccbSFrançois Tigeot if (strncmp("default", buf, strlen("default")) == 0) 381926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_DEFAULT; 382926deccbSFrançois Tigeot else if (strncmp("auto", buf, strlen("auto")) == 0) 383926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_AUTO; 384926deccbSFrançois Tigeot else if (strncmp("low", buf, strlen("low")) == 0) 385926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_LOW; 386926deccbSFrançois Tigeot else if (strncmp("mid", buf, strlen("mid")) == 0) 387926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_MID; 388926deccbSFrançois Tigeot else if (strncmp("high", buf, strlen("high")) == 0) 389926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_HIGH; 390926deccbSFrançois Tigeot else { 391926deccbSFrançois Tigeot count = -EINVAL; 392926deccbSFrançois Tigeot goto fail; 393926deccbSFrançois Tigeot } 394926deccbSFrançois Tigeot radeon_pm_update_profile(rdev); 395926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 396926deccbSFrançois Tigeot } else 397926deccbSFrançois Tigeot count = -EINVAL; 398926deccbSFrançois Tigeot 399926deccbSFrançois Tigeot fail: 400926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 401926deccbSFrançois Tigeot 402926deccbSFrançois Tigeot return count; 403926deccbSFrançois Tigeot } 404926deccbSFrançois Tigeot 405926deccbSFrançois Tigeot static ssize_t radeon_get_pm_method(struct device *dev, 406926deccbSFrançois Tigeot struct device_attribute *attr, 407926deccbSFrançois Tigeot char *buf) 408926deccbSFrançois Tigeot { 409c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 410926deccbSFrançois Tigeot struct radeon_device *rdev = ddev->dev_private; 411926deccbSFrançois Tigeot int pm = rdev->pm.pm_method; 412926deccbSFrançois Tigeot 413926deccbSFrançois Tigeot return ksnprintf(buf, PAGE_SIZE, "%s\n", 41457e252bfSMichael Neumann (pm == PM_METHOD_DYNPM) ? "dynpm" : 41557e252bfSMichael Neumann (pm == PM_METHOD_PROFILE) ? "profile" : "dpm"); 416926deccbSFrançois Tigeot } 417926deccbSFrançois Tigeot 418926deccbSFrançois Tigeot static ssize_t radeon_set_pm_method(struct device *dev, 419926deccbSFrançois Tigeot struct device_attribute *attr, 420926deccbSFrançois Tigeot const char *buf, 421926deccbSFrançois Tigeot size_t count) 422926deccbSFrançois Tigeot { 423c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 424926deccbSFrançois Tigeot struct radeon_device *rdev = ddev->dev_private; 425926deccbSFrançois Tigeot 426c6f73aabSFrançois Tigeot /* Can't set method when the card is off */ 427c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 428c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 429c6f73aabSFrançois Tigeot count = -EINVAL; 430c6f73aabSFrançois Tigeot goto fail; 431c6f73aabSFrançois Tigeot } 432c6f73aabSFrançois Tigeot 43357e252bfSMichael Neumann /* we don't support the legacy modes with dpm */ 43457e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) { 43557e252bfSMichael Neumann count = -EINVAL; 43657e252bfSMichael Neumann goto fail; 43757e252bfSMichael Neumann } 438926deccbSFrançois Tigeot 439926deccbSFrançois Tigeot if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { 440926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 441926deccbSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_DYNPM; 442926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 443926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 444926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 445926deccbSFrançois Tigeot } else if (strncmp("profile", buf, strlen("profile")) == 0) { 446926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 447926deccbSFrançois Tigeot /* disable dynpm */ 448926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 449926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 450926deccbSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_PROFILE; 451926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 452926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 453926deccbSFrançois Tigeot cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 454926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 455926deccbSFrançois Tigeot } else { 456926deccbSFrançois Tigeot count = -EINVAL; 457926deccbSFrançois Tigeot goto fail; 458926deccbSFrançois Tigeot } 459926deccbSFrançois Tigeot radeon_pm_compute_clocks(rdev); 460926deccbSFrançois Tigeot fail: 461926deccbSFrançois Tigeot return count; 462926deccbSFrançois Tigeot } 463926deccbSFrançois Tigeot 46457e252bfSMichael Neumann static ssize_t radeon_get_dpm_state(struct device *dev, 46557e252bfSMichael Neumann struct device_attribute *attr, 46657e252bfSMichael Neumann char *buf) 46757e252bfSMichael Neumann { 468c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 46957e252bfSMichael Neumann struct radeon_device *rdev = ddev->dev_private; 47057e252bfSMichael Neumann enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; 47157e252bfSMichael Neumann 47257e252bfSMichael Neumann return snprintf(buf, PAGE_SIZE, "%s\n", 47357e252bfSMichael Neumann (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : 47457e252bfSMichael Neumann (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); 47557e252bfSMichael Neumann } 47657e252bfSMichael Neumann 47757e252bfSMichael Neumann static ssize_t radeon_set_dpm_state(struct device *dev, 47857e252bfSMichael Neumann struct device_attribute *attr, 47957e252bfSMichael Neumann const char *buf, 48057e252bfSMichael Neumann size_t count) 48157e252bfSMichael Neumann { 482c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 48357e252bfSMichael Neumann struct radeon_device *rdev = ddev->dev_private; 48457e252bfSMichael Neumann 48557e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 48657e252bfSMichael Neumann if (strncmp("battery", buf, strlen("battery")) == 0) 48757e252bfSMichael Neumann rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; 48857e252bfSMichael Neumann else if (strncmp("balanced", buf, strlen("balanced")) == 0) 48957e252bfSMichael Neumann rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 49057e252bfSMichael Neumann else if (strncmp("performance", buf, strlen("performance")) == 0) 49157e252bfSMichael Neumann rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; 49257e252bfSMichael Neumann else { 49357e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 49457e252bfSMichael Neumann count = -EINVAL; 49557e252bfSMichael Neumann goto fail; 49657e252bfSMichael Neumann } 49757e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 498c6f73aabSFrançois Tigeot 499c6f73aabSFrançois Tigeot /* Can't set dpm state when the card is off */ 500c6f73aabSFrançois Tigeot if (!(rdev->flags & RADEON_IS_PX) || 501c6f73aabSFrançois Tigeot (ddev->switch_power_state == DRM_SWITCH_POWER_ON)) 50257e252bfSMichael Neumann radeon_pm_compute_clocks(rdev); 503c6f73aabSFrançois Tigeot 50457e252bfSMichael Neumann fail: 50557e252bfSMichael Neumann return count; 50657e252bfSMichael Neumann } 50757e252bfSMichael Neumann 50857e252bfSMichael Neumann static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, 50957e252bfSMichael Neumann struct device_attribute *attr, 51057e252bfSMichael Neumann char *buf) 51157e252bfSMichael Neumann { 512c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 51357e252bfSMichael Neumann struct radeon_device *rdev = ddev->dev_private; 51457e252bfSMichael Neumann enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 51557e252bfSMichael Neumann 516c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 517c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 518c6f73aabSFrançois Tigeot return snprintf(buf, PAGE_SIZE, "off\n"); 519c6f73aabSFrançois Tigeot 52057e252bfSMichael Neumann return snprintf(buf, PAGE_SIZE, "%s\n", 52157e252bfSMichael Neumann (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : 52257e252bfSMichael Neumann (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); 52357e252bfSMichael Neumann } 52457e252bfSMichael Neumann 52557e252bfSMichael Neumann static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, 52657e252bfSMichael Neumann struct device_attribute *attr, 52757e252bfSMichael Neumann const char *buf, 52857e252bfSMichael Neumann size_t count) 52957e252bfSMichael Neumann { 530c6f73aabSFrançois Tigeot struct drm_device *ddev = dev_get_drvdata(dev); 53157e252bfSMichael Neumann struct radeon_device *rdev = ddev->dev_private; 53257e252bfSMichael Neumann enum radeon_dpm_forced_level level; 53357e252bfSMichael Neumann int ret = 0; 53457e252bfSMichael Neumann 535c6f73aabSFrançois Tigeot /* Can't force performance level when the card is off */ 536c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 537c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 538c6f73aabSFrançois Tigeot return -EINVAL; 539c6f73aabSFrançois Tigeot 54057e252bfSMichael Neumann spin_lock(&rdev->pm.mutex); 54157e252bfSMichael Neumann if (strncmp("low", buf, strlen("low")) == 0) { 54257e252bfSMichael Neumann level = RADEON_DPM_FORCED_LEVEL_LOW; 54357e252bfSMichael Neumann } else if (strncmp("high", buf, strlen("high")) == 0) { 54457e252bfSMichael Neumann level = RADEON_DPM_FORCED_LEVEL_HIGH; 54557e252bfSMichael Neumann } else if (strncmp("auto", buf, strlen("auto")) == 0) { 54657e252bfSMichael Neumann level = RADEON_DPM_FORCED_LEVEL_AUTO; 54757e252bfSMichael Neumann } else { 54857e252bfSMichael Neumann count = -EINVAL; 54957e252bfSMichael Neumann goto fail; 55057e252bfSMichael Neumann } 55157e252bfSMichael Neumann if (rdev->asic->dpm.force_performance_level) { 552c6f73aabSFrançois Tigeot if (rdev->pm.dpm.thermal_active) { 553c6f73aabSFrançois Tigeot count = -EINVAL; 554c6f73aabSFrançois Tigeot goto fail; 555c6f73aabSFrançois Tigeot } 55657e252bfSMichael Neumann ret = radeon_dpm_force_performance_level(rdev, level); 55757e252bfSMichael Neumann if (ret) 55857e252bfSMichael Neumann count = -EINVAL; 55957e252bfSMichael Neumann } 56057e252bfSMichael Neumann fail: 561c6f73aabSFrançois Tigeot spin_unlock(&rdev->pm.mutex); 562c6f73aabSFrançois Tigeot 56357e252bfSMichael Neumann return count; 56457e252bfSMichael Neumann } 56557e252bfSMichael Neumann 566926deccbSFrançois Tigeot static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); 567926deccbSFrançois Tigeot static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); 56857e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state); 56957e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, 57057e252bfSMichael Neumann radeon_get_dpm_forced_performance_level, 57157e252bfSMichael Neumann radeon_set_dpm_forced_performance_level); 572926deccbSFrançois Tigeot 573926deccbSFrançois Tigeot static ssize_t radeon_hwmon_show_temp(struct device *dev, 574926deccbSFrançois Tigeot struct device_attribute *attr, 575926deccbSFrançois Tigeot char *buf) 576926deccbSFrançois Tigeot { 577c6f73aabSFrançois Tigeot struct radeon_device *rdev = dev_get_drvdata(dev); 578c6f73aabSFrançois Tigeot struct drm_device *ddev = rdev->ddev; 579926deccbSFrançois Tigeot int temp; 580926deccbSFrançois Tigeot 581c6f73aabSFrançois Tigeot /* Can't get temperature when the card is off */ 582c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 583c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 584c6f73aabSFrançois Tigeot return -EINVAL; 585c6f73aabSFrançois Tigeot 58657e252bfSMichael Neumann if (rdev->asic->pm.get_temperature) 58757e252bfSMichael Neumann temp = radeon_get_temperature(rdev); 58857e252bfSMichael Neumann else 589926deccbSFrançois Tigeot temp = 0; 590926deccbSFrançois Tigeot 591926deccbSFrançois Tigeot return ksnprintf(buf, PAGE_SIZE, "%d\n", temp); 592926deccbSFrançois Tigeot } 593926deccbSFrançois Tigeot 594c6f73aabSFrançois Tigeot static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, 595926deccbSFrançois Tigeot struct device_attribute *attr, 596926deccbSFrançois Tigeot char *buf) 597926deccbSFrançois Tigeot { 598c6f73aabSFrançois Tigeot struct radeon_device *rdev = dev_get_drvdata(dev); 599c6f73aabSFrançois Tigeot int hyst = to_sensor_dev_attr(attr)->index; 600c6f73aabSFrançois Tigeot int temp; 601c6f73aabSFrançois Tigeot 602c6f73aabSFrançois Tigeot if (hyst) 603c6f73aabSFrançois Tigeot temp = rdev->pm.dpm.thermal.min_temp; 604c6f73aabSFrançois Tigeot else 605c6f73aabSFrançois Tigeot temp = rdev->pm.dpm.thermal.max_temp; 606c6f73aabSFrançois Tigeot 607c6f73aabSFrançois Tigeot return ksnprintf(buf, PAGE_SIZE, "%d\n", temp); 608926deccbSFrançois Tigeot } 609926deccbSFrançois Tigeot 610926deccbSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); 611c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0); 612c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1); 613926deccbSFrançois Tigeot 614926deccbSFrançois Tigeot static struct attribute *hwmon_attributes[] = { 615926deccbSFrançois Tigeot &sensor_dev_attr_temp1_input.dev_attr.attr, 616c6f73aabSFrançois Tigeot &sensor_dev_attr_temp1_crit.dev_attr.attr, 617c6f73aabSFrançois Tigeot &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, 618926deccbSFrançois Tigeot NULL 619926deccbSFrançois Tigeot }; 620926deccbSFrançois Tigeot 621c6f73aabSFrançois Tigeot static umode_t hwmon_attributes_visible(struct kobject *kobj, 622c6f73aabSFrançois Tigeot struct attribute *attr, int index) 623c6f73aabSFrançois Tigeot { 624c6f73aabSFrançois Tigeot struct device *dev = container_of(kobj, struct device, kobj); 625c6f73aabSFrançois Tigeot struct radeon_device *rdev = dev_get_drvdata(dev); 626c6f73aabSFrançois Tigeot 627c6f73aabSFrançois Tigeot /* Skip limit attributes if DPM is not enabled */ 628c6f73aabSFrançois Tigeot if (rdev->pm.pm_method != PM_METHOD_DPM && 629c6f73aabSFrançois Tigeot (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || 630c6f73aabSFrançois Tigeot attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) 631c6f73aabSFrançois Tigeot return 0; 632c6f73aabSFrançois Tigeot 633c6f73aabSFrançois Tigeot return attr->mode; 634c6f73aabSFrançois Tigeot } 635c6f73aabSFrançois Tigeot 636926deccbSFrançois Tigeot static const struct attribute_group hwmon_attrgroup = { 637926deccbSFrançois Tigeot .attrs = hwmon_attributes, 638c6f73aabSFrançois Tigeot .is_visible = hwmon_attributes_visible, 639c6f73aabSFrançois Tigeot }; 640c6f73aabSFrançois Tigeot 641c6f73aabSFrançois Tigeot static const struct attribute_group *hwmon_groups[] = { 642c6f73aabSFrançois Tigeot &hwmon_attrgroup, 643c6f73aabSFrançois Tigeot NULL 644926deccbSFrançois Tigeot }; 645926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 646926deccbSFrançois Tigeot 6472c86cb5bSImre Vadász static void 6482c86cb5bSImre Vadász radeon_hwmon_refresh(void *arg) 6492c86cb5bSImre Vadász { 6502c86cb5bSImre Vadász struct radeon_device *rdev = (struct radeon_device *)arg; 651*26b5dbf2SImre Vadász struct drm_device *ddev = rdev->ddev; 6522c86cb5bSImre Vadász struct ksensor *s = rdev->pm.int_sensor; 6532c86cb5bSImre Vadász int temp; 654*26b5dbf2SImre Vadász enum sensor_status stat; 6552c86cb5bSImre Vadász 656*26b5dbf2SImre Vadász /* Can't get temperature when the card is off */ 657*26b5dbf2SImre Vadász if ((rdev->flags & RADEON_IS_PX) && 658*26b5dbf2SImre Vadász (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 659*26b5dbf2SImre Vadász sensor_set_unknown(s); 660*26b5dbf2SImre Vadász s->status = SENSOR_S_OK; 661*26b5dbf2SImre Vadász return; 662*26b5dbf2SImre Vadász } 663*26b5dbf2SImre Vadász 664*26b5dbf2SImre Vadász if (rdev->asic->pm.get_temperature == NULL) { 665*26b5dbf2SImre Vadász sensor_set_invalid(s); 666*26b5dbf2SImre Vadász return; 667*26b5dbf2SImre Vadász } 668*26b5dbf2SImre Vadász 6692c86cb5bSImre Vadász temp = radeon_get_temperature(rdev); 670*26b5dbf2SImre Vadász if (temp >= rdev->pm.dpm.thermal.max_temp) 671*26b5dbf2SImre Vadász stat = SENSOR_S_CRIT; 672*26b5dbf2SImre Vadász else if (temp >= rdev->pm.dpm.thermal.min_temp) 673*26b5dbf2SImre Vadász stat = SENSOR_S_WARN; 6742c86cb5bSImre Vadász else 675*26b5dbf2SImre Vadász stat = SENSOR_S_OK; 6762c86cb5bSImre Vadász 677*26b5dbf2SImre Vadász sensor_set(s, temp * 1000 + 273150000, stat); 6782c86cb5bSImre Vadász } 6792c86cb5bSImre Vadász 680926deccbSFrançois Tigeot static int radeon_hwmon_init(struct radeon_device *rdev) 681926deccbSFrançois Tigeot { 682926deccbSFrançois Tigeot int err = 0; 683926deccbSFrançois Tigeot 6842c86cb5bSImre Vadász rdev->pm.int_sensor = NULL; 6852c86cb5bSImre Vadász rdev->pm.int_sensordev = NULL; 686926deccbSFrançois Tigeot 687926deccbSFrançois Tigeot switch (rdev->pm.int_thermal_type) { 688926deccbSFrançois Tigeot case THERMAL_TYPE_RV6XX: 689926deccbSFrançois Tigeot case THERMAL_TYPE_RV770: 690926deccbSFrançois Tigeot case THERMAL_TYPE_EVERGREEN: 691926deccbSFrançois Tigeot case THERMAL_TYPE_NI: 692926deccbSFrançois Tigeot case THERMAL_TYPE_SUMO: 693926deccbSFrançois Tigeot case THERMAL_TYPE_SI: 6944cd92098Szrj case THERMAL_TYPE_CI: 6954cd92098Szrj case THERMAL_TYPE_KV: 69657e252bfSMichael Neumann if (rdev->asic->pm.get_temperature == NULL) 697926deccbSFrançois Tigeot return err; 6982c86cb5bSImre Vadász 6992c86cb5bSImre Vadász rdev->pm.int_sensor = kmalloc(sizeof(*rdev->pm.int_sensor), 7002c86cb5bSImre Vadász M_DRM, M_ZERO | M_WAITOK); 7012c86cb5bSImre Vadász rdev->pm.int_sensordev = kmalloc( 7022c86cb5bSImre Vadász sizeof(*rdev->pm.int_sensordev), M_DRM, 7032c86cb5bSImre Vadász M_ZERO | M_WAITOK); 7042c86cb5bSImre Vadász strlcpy(rdev->pm.int_sensordev->xname, 7052c86cb5bSImre Vadász device_get_nameunit(rdev->dev), 7062c86cb5bSImre Vadász sizeof(rdev->pm.int_sensordev->xname)); 7072c86cb5bSImre Vadász rdev->pm.int_sensor->type = SENSOR_TEMP; 708*26b5dbf2SImre Vadász rdev->pm.int_sensor->flags |= SENSOR_FINVALID; 7092c86cb5bSImre Vadász sensor_attach(rdev->pm.int_sensordev, rdev->pm.int_sensor); 7102c86cb5bSImre Vadász sensor_task_register(rdev, radeon_hwmon_refresh, 5); 7112c86cb5bSImre Vadász sensordev_install(rdev->pm.int_sensordev); 712926deccbSFrançois Tigeot break; 713926deccbSFrançois Tigeot default: 714926deccbSFrançois Tigeot break; 715926deccbSFrançois Tigeot } 716926deccbSFrançois Tigeot 717926deccbSFrançois Tigeot return err; 718926deccbSFrançois Tigeot } 719926deccbSFrançois Tigeot 720926deccbSFrançois Tigeot static void radeon_hwmon_fini(struct radeon_device *rdev) 721926deccbSFrançois Tigeot { 7222c86cb5bSImre Vadász if (rdev->pm.int_sensor != NULL && rdev->pm.int_sensordev != NULL) { 7232c86cb5bSImre Vadász sensordev_deinstall(rdev->pm.int_sensordev); 7242c86cb5bSImre Vadász sensor_task_unregister(rdev); 7252c86cb5bSImre Vadász kfree(rdev->pm.int_sensor); 7262c86cb5bSImre Vadász kfree(rdev->pm.int_sensordev); 7272c86cb5bSImre Vadász rdev->pm.int_sensor = NULL; 7282c86cb5bSImre Vadász rdev->pm.int_sensordev = NULL; 729926deccbSFrançois Tigeot } 730926deccbSFrançois Tigeot } 731926deccbSFrançois Tigeot 73257e252bfSMichael Neumann static void radeon_dpm_thermal_work_handler(void *arg, int pending) 73357e252bfSMichael Neumann { 73457e252bfSMichael Neumann struct radeon_device *rdev = arg; 73557e252bfSMichael Neumann /* switch to the thermal state */ 73657e252bfSMichael Neumann enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; 73757e252bfSMichael Neumann 73857e252bfSMichael Neumann if (!rdev->pm.dpm_enabled) 73957e252bfSMichael Neumann return; 74057e252bfSMichael Neumann 74157e252bfSMichael Neumann if (rdev->asic->pm.get_temperature) { 74257e252bfSMichael Neumann int temp = radeon_get_temperature(rdev); 74357e252bfSMichael Neumann 74457e252bfSMichael Neumann if (temp < rdev->pm.dpm.thermal.min_temp) 74557e252bfSMichael Neumann /* switch back the user state */ 74657e252bfSMichael Neumann dpm_state = rdev->pm.dpm.user_state; 74757e252bfSMichael Neumann } else { 74857e252bfSMichael Neumann if (rdev->pm.dpm.thermal.high_to_low) 74957e252bfSMichael Neumann /* switch back the user state */ 75057e252bfSMichael Neumann dpm_state = rdev->pm.dpm.user_state; 75157e252bfSMichael Neumann } 7524cd92098Szrj mutex_lock(&rdev->pm.mutex); 7534cd92098Szrj if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) 7544cd92098Szrj rdev->pm.dpm.thermal_active = true; 7554cd92098Szrj else 7564cd92098Szrj rdev->pm.dpm.thermal_active = false; 7574cd92098Szrj rdev->pm.dpm.state = dpm_state; 7584cd92098Szrj mutex_unlock(&rdev->pm.mutex); 7594cd92098Szrj 7604cd92098Szrj radeon_pm_compute_clocks(rdev); 76157e252bfSMichael Neumann } 76257e252bfSMichael Neumann 76357e252bfSMichael Neumann static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, 76457e252bfSMichael Neumann enum radeon_pm_state_type dpm_state) 76557e252bfSMichael Neumann { 76657e252bfSMichael Neumann int i; 76757e252bfSMichael Neumann struct radeon_ps *ps; 76857e252bfSMichael Neumann u32 ui_class; 76957e252bfSMichael Neumann bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ? 77057e252bfSMichael Neumann true : false; 77157e252bfSMichael Neumann 77257e252bfSMichael Neumann /* check if the vblank period is too short to adjust the mclk */ 77357e252bfSMichael Neumann if (single_display && rdev->asic->dpm.vblank_too_short) { 77457e252bfSMichael Neumann if (radeon_dpm_vblank_too_short(rdev)) 77557e252bfSMichael Neumann single_display = false; 77657e252bfSMichael Neumann } 77757e252bfSMichael Neumann 77857e252bfSMichael Neumann /* certain older asics have a separare 3D performance state, 77957e252bfSMichael Neumann * so try that first if the user selected performance 78057e252bfSMichael Neumann */ 78157e252bfSMichael Neumann if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) 78257e252bfSMichael Neumann dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; 78357e252bfSMichael Neumann /* balanced states don't exist at the moment */ 78457e252bfSMichael Neumann if (dpm_state == POWER_STATE_TYPE_BALANCED) 78557e252bfSMichael Neumann dpm_state = POWER_STATE_TYPE_PERFORMANCE; 78657e252bfSMichael Neumann 78757e252bfSMichael Neumann restart_search: 78857e252bfSMichael Neumann /* Pick the best power state based on current conditions */ 78957e252bfSMichael Neumann for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 79057e252bfSMichael Neumann ps = &rdev->pm.dpm.ps[i]; 79157e252bfSMichael Neumann ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; 79257e252bfSMichael Neumann switch (dpm_state) { 79357e252bfSMichael Neumann /* user states */ 79457e252bfSMichael Neumann case POWER_STATE_TYPE_BATTERY: 79557e252bfSMichael Neumann if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { 79657e252bfSMichael Neumann if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 79757e252bfSMichael Neumann if (single_display) 79857e252bfSMichael Neumann return ps; 79957e252bfSMichael Neumann } else 80057e252bfSMichael Neumann return ps; 80157e252bfSMichael Neumann } 80257e252bfSMichael Neumann break; 80357e252bfSMichael Neumann case POWER_STATE_TYPE_BALANCED: 80457e252bfSMichael Neumann if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { 80557e252bfSMichael Neumann if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 80657e252bfSMichael Neumann if (single_display) 80757e252bfSMichael Neumann return ps; 80857e252bfSMichael Neumann } else 80957e252bfSMichael Neumann return ps; 81057e252bfSMichael Neumann } 81157e252bfSMichael Neumann break; 81257e252bfSMichael Neumann case POWER_STATE_TYPE_PERFORMANCE: 81357e252bfSMichael Neumann if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { 81457e252bfSMichael Neumann if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 81557e252bfSMichael Neumann if (single_display) 81657e252bfSMichael Neumann return ps; 81757e252bfSMichael Neumann } else 81857e252bfSMichael Neumann return ps; 81957e252bfSMichael Neumann } 82057e252bfSMichael Neumann break; 82157e252bfSMichael Neumann /* internal states */ 82257e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD: 8234cd92098Szrj if (rdev->pm.dpm.uvd_ps) 82457e252bfSMichael Neumann return rdev->pm.dpm.uvd_ps; 8254cd92098Szrj else 8264cd92098Szrj break; 82757e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_SD: 82857e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) 82957e252bfSMichael Neumann return ps; 83057e252bfSMichael Neumann break; 83157e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_HD: 83257e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) 83357e252bfSMichael Neumann return ps; 83457e252bfSMichael Neumann break; 83557e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 83657e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) 83757e252bfSMichael Neumann return ps; 83857e252bfSMichael Neumann break; 83957e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 84057e252bfSMichael Neumann if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) 84157e252bfSMichael Neumann return ps; 84257e252bfSMichael Neumann break; 84357e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_BOOT: 84457e252bfSMichael Neumann return rdev->pm.dpm.boot_ps; 84557e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_THERMAL: 84657e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) 84757e252bfSMichael Neumann return ps; 84857e252bfSMichael Neumann break; 84957e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_ACPI: 85057e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) 85157e252bfSMichael Neumann return ps; 85257e252bfSMichael Neumann break; 85357e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_ULV: 85457e252bfSMichael Neumann if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) 85557e252bfSMichael Neumann return ps; 85657e252bfSMichael Neumann break; 85757e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_3DPERF: 85857e252bfSMichael Neumann if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) 85957e252bfSMichael Neumann return ps; 86057e252bfSMichael Neumann break; 86157e252bfSMichael Neumann default: 86257e252bfSMichael Neumann break; 86357e252bfSMichael Neumann } 86457e252bfSMichael Neumann } 86557e252bfSMichael Neumann /* use a fallback state if we didn't match */ 86657e252bfSMichael Neumann switch (dpm_state) { 86757e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_SD: 8684cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 8694cd92098Szrj goto restart_search; 87057e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_HD: 87157e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 87257e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 8734cd92098Szrj if (rdev->pm.dpm.uvd_ps) { 87457e252bfSMichael Neumann return rdev->pm.dpm.uvd_ps; 8754cd92098Szrj } else { 8764cd92098Szrj dpm_state = POWER_STATE_TYPE_PERFORMANCE; 8774cd92098Szrj goto restart_search; 8784cd92098Szrj } 87957e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_THERMAL: 88057e252bfSMichael Neumann dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; 88157e252bfSMichael Neumann goto restart_search; 88257e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_ACPI: 88357e252bfSMichael Neumann dpm_state = POWER_STATE_TYPE_BATTERY; 88457e252bfSMichael Neumann goto restart_search; 88557e252bfSMichael Neumann case POWER_STATE_TYPE_BATTERY: 88657e252bfSMichael Neumann case POWER_STATE_TYPE_BALANCED: 88757e252bfSMichael Neumann case POWER_STATE_TYPE_INTERNAL_3DPERF: 88857e252bfSMichael Neumann dpm_state = POWER_STATE_TYPE_PERFORMANCE; 88957e252bfSMichael Neumann goto restart_search; 89057e252bfSMichael Neumann default: 89157e252bfSMichael Neumann break; 89257e252bfSMichael Neumann } 89357e252bfSMichael Neumann 89457e252bfSMichael Neumann return NULL; 89557e252bfSMichael Neumann } 89657e252bfSMichael Neumann 89757e252bfSMichael Neumann static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) 89857e252bfSMichael Neumann { 89957e252bfSMichael Neumann int i; 90057e252bfSMichael Neumann struct radeon_ps *ps; 90157e252bfSMichael Neumann enum radeon_pm_state_type dpm_state; 90257e252bfSMichael Neumann int ret; 90357e252bfSMichael Neumann 90457e252bfSMichael Neumann /* if dpm init failed */ 90557e252bfSMichael Neumann if (!rdev->pm.dpm_enabled) 90657e252bfSMichael Neumann return; 90757e252bfSMichael Neumann 90857e252bfSMichael Neumann if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) { 90957e252bfSMichael Neumann /* add other state override checks here */ 91057e252bfSMichael Neumann if ((!rdev->pm.dpm.thermal_active) && 91157e252bfSMichael Neumann (!rdev->pm.dpm.uvd_active)) 91257e252bfSMichael Neumann rdev->pm.dpm.state = rdev->pm.dpm.user_state; 91357e252bfSMichael Neumann } 91457e252bfSMichael Neumann dpm_state = rdev->pm.dpm.state; 91557e252bfSMichael Neumann 91657e252bfSMichael Neumann ps = radeon_dpm_pick_power_state(rdev, dpm_state); 91757e252bfSMichael Neumann if (ps) 91857e252bfSMichael Neumann rdev->pm.dpm.requested_ps = ps; 91957e252bfSMichael Neumann else 92057e252bfSMichael Neumann return; 92157e252bfSMichael Neumann 92257e252bfSMichael Neumann /* no need to reprogram if nothing changed unless we are on BTC+ */ 92357e252bfSMichael Neumann if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) { 924c6f73aabSFrançois Tigeot /* vce just modifies an existing state so force a change */ 925c6f73aabSFrançois Tigeot if (ps->vce_active != rdev->pm.dpm.vce_active) 926c6f73aabSFrançois Tigeot goto force; 92757e252bfSMichael Neumann if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { 92857e252bfSMichael Neumann /* for pre-BTC and APUs if the num crtcs changed but state is the same, 92957e252bfSMichael Neumann * all we need to do is update the display configuration. 93057e252bfSMichael Neumann */ 93157e252bfSMichael Neumann if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { 93257e252bfSMichael Neumann /* update display watermarks based on new power state */ 93357e252bfSMichael Neumann radeon_bandwidth_update(rdev); 93457e252bfSMichael Neumann /* update displays */ 93557e252bfSMichael Neumann radeon_dpm_display_configuration_changed(rdev); 93657e252bfSMichael Neumann rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 93757e252bfSMichael Neumann rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 93857e252bfSMichael Neumann } 93957e252bfSMichael Neumann return; 94057e252bfSMichael Neumann } else { 94157e252bfSMichael Neumann /* for BTC+ if the num crtcs hasn't changed and state is the same, 94257e252bfSMichael Neumann * nothing to do, if the num crtcs is > 1 and state is the same, 94357e252bfSMichael Neumann * update display configuration. 94457e252bfSMichael Neumann */ 94557e252bfSMichael Neumann if (rdev->pm.dpm.new_active_crtcs == 94657e252bfSMichael Neumann rdev->pm.dpm.current_active_crtcs) { 94757e252bfSMichael Neumann return; 94857e252bfSMichael Neumann } else { 94957e252bfSMichael Neumann if ((rdev->pm.dpm.current_active_crtc_count > 1) && 95057e252bfSMichael Neumann (rdev->pm.dpm.new_active_crtc_count > 1)) { 95157e252bfSMichael Neumann /* update display watermarks based on new power state */ 95257e252bfSMichael Neumann radeon_bandwidth_update(rdev); 95357e252bfSMichael Neumann /* update displays */ 95457e252bfSMichael Neumann radeon_dpm_display_configuration_changed(rdev); 95557e252bfSMichael Neumann rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 95657e252bfSMichael Neumann rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 95757e252bfSMichael Neumann return; 95857e252bfSMichael Neumann } 95957e252bfSMichael Neumann } 96057e252bfSMichael Neumann } 96157e252bfSMichael Neumann } 96257e252bfSMichael Neumann 963c6f73aabSFrançois Tigeot force: 964c6f73aabSFrançois Tigeot if (radeon_dpm == 1) { 96557e252bfSMichael Neumann printk("switching from power state:\n"); 96657e252bfSMichael Neumann radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps); 96757e252bfSMichael Neumann printk("switching to power state:\n"); 96857e252bfSMichael Neumann radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps); 969c6f73aabSFrançois Tigeot } 97057e252bfSMichael Neumann 97157e252bfSMichael Neumann lockmgr(&rdev->ddev->struct_mutex, LK_EXCLUSIVE); 97257e252bfSMichael Neumann lockmgr(&rdev->pm.mclk_lock, LK_EXCLUSIVE); // down_write 97357e252bfSMichael Neumann lockmgr(&rdev->ring_lock, LK_EXCLUSIVE); 97457e252bfSMichael Neumann 975c6f73aabSFrançois Tigeot /* update whether vce is active */ 976c6f73aabSFrançois Tigeot ps->vce_active = rdev->pm.dpm.vce_active; 977c6f73aabSFrançois Tigeot 97857e252bfSMichael Neumann ret = radeon_dpm_pre_set_power_state(rdev); 97957e252bfSMichael Neumann if (ret) 98057e252bfSMichael Neumann goto done; 98157e252bfSMichael Neumann 98257e252bfSMichael Neumann /* update display watermarks based on new power state */ 98357e252bfSMichael Neumann radeon_bandwidth_update(rdev); 98457e252bfSMichael Neumann /* update displays */ 98557e252bfSMichael Neumann radeon_dpm_display_configuration_changed(rdev); 98657e252bfSMichael Neumann 98757e252bfSMichael Neumann rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 98857e252bfSMichael Neumann rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 98957e252bfSMichael Neumann 99057e252bfSMichael Neumann /* wait for the rings to drain */ 99157e252bfSMichael Neumann for (i = 0; i < RADEON_NUM_RINGS; i++) { 99257e252bfSMichael Neumann struct radeon_ring *ring = &rdev->ring[i]; 99357e252bfSMichael Neumann if (ring->ready) 994c6f73aabSFrançois Tigeot radeon_fence_wait_empty(rdev, i); 99557e252bfSMichael Neumann } 99657e252bfSMichael Neumann 99757e252bfSMichael Neumann /* program the new power state */ 99857e252bfSMichael Neumann radeon_dpm_set_power_state(rdev); 99957e252bfSMichael Neumann 100057e252bfSMichael Neumann /* update current power state */ 100157e252bfSMichael Neumann rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps; 100257e252bfSMichael Neumann 100357e252bfSMichael Neumann radeon_dpm_post_set_power_state(rdev); 100457e252bfSMichael Neumann 10054cd92098Szrj if (rdev->asic->dpm.force_performance_level) { 1006c6f73aabSFrançois Tigeot if (rdev->pm.dpm.thermal_active) { 1007c6f73aabSFrançois Tigeot enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 10084cd92098Szrj /* force low perf level for thermal */ 10094cd92098Szrj radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW); 1010c6f73aabSFrançois Tigeot /* save the user's level */ 1011c6f73aabSFrançois Tigeot rdev->pm.dpm.forced_level = level; 1012c6f73aabSFrançois Tigeot } else { 1013c6f73aabSFrançois Tigeot /* otherwise, user selected level */ 1014c6f73aabSFrançois Tigeot radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level); 1015c6f73aabSFrançois Tigeot } 10164cd92098Szrj } 10174cd92098Szrj 101857e252bfSMichael Neumann done: 101957e252bfSMichael Neumann lockmgr(&rdev->ring_lock, LK_RELEASE); 102057e252bfSMichael Neumann lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write 102157e252bfSMichael Neumann lockmgr(&rdev->ddev->struct_mutex, LK_RELEASE); 102257e252bfSMichael Neumann } 102357e252bfSMichael Neumann 10244cd92098Szrj void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable) 102557e252bfSMichael Neumann { 10264cd92098Szrj enum radeon_pm_state_type dpm_state; 102757e252bfSMichael Neumann 10284cd92098Szrj if (rdev->asic->dpm.powergate_uvd) { 10294cd92098Szrj mutex_lock(&rdev->pm.mutex); 1030c6f73aabSFrançois Tigeot /* don't powergate anything if we 1031c6f73aabSFrançois Tigeot have active but pause streams */ 1032c6f73aabSFrançois Tigeot enable |= rdev->pm.dpm.sd > 0; 1033c6f73aabSFrançois Tigeot enable |= rdev->pm.dpm.hd > 0; 10344cd92098Szrj /* enable/disable UVD */ 10354cd92098Szrj radeon_dpm_powergate_uvd(rdev, !enable); 10364cd92098Szrj mutex_unlock(&rdev->pm.mutex); 10374cd92098Szrj } else { 10384cd92098Szrj if (enable) { 10394cd92098Szrj mutex_lock(&rdev->pm.mutex); 104057e252bfSMichael Neumann rdev->pm.dpm.uvd_active = true; 10414cd92098Szrj /* disable this for now */ 10424cd92098Szrj #if 0 10434cd92098Szrj if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0)) 10444cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD; 10454cd92098Szrj else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0)) 10464cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 10474cd92098Szrj else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1)) 10484cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 10494cd92098Szrj else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2)) 10504cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2; 10514cd92098Szrj else 10524cd92098Szrj #endif 10534cd92098Szrj dpm_state = POWER_STATE_TYPE_INTERNAL_UVD; 105457e252bfSMichael Neumann rdev->pm.dpm.state = dpm_state; 10554cd92098Szrj mutex_unlock(&rdev->pm.mutex); 10564cd92098Szrj } else { 10574cd92098Szrj mutex_lock(&rdev->pm.mutex); 10584cd92098Szrj rdev->pm.dpm.uvd_active = false; 10594cd92098Szrj mutex_unlock(&rdev->pm.mutex); 10604cd92098Szrj } 10614cd92098Szrj 106257e252bfSMichael Neumann radeon_pm_compute_clocks(rdev); 106357e252bfSMichael Neumann } 10644cd92098Szrj } 106557e252bfSMichael Neumann 1066c6f73aabSFrançois Tigeot void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable) 1067c6f73aabSFrançois Tigeot { 1068c6f73aabSFrançois Tigeot if (enable) { 1069c6f73aabSFrançois Tigeot mutex_lock(&rdev->pm.mutex); 1070c6f73aabSFrançois Tigeot rdev->pm.dpm.vce_active = true; 1071c6f73aabSFrançois Tigeot /* XXX select vce level based on ring/task */ 1072c6f73aabSFrançois Tigeot rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL; 1073c6f73aabSFrançois Tigeot mutex_unlock(&rdev->pm.mutex); 1074c6f73aabSFrançois Tigeot } else { 1075c6f73aabSFrançois Tigeot mutex_lock(&rdev->pm.mutex); 1076c6f73aabSFrançois Tigeot rdev->pm.dpm.vce_active = false; 1077c6f73aabSFrançois Tigeot mutex_unlock(&rdev->pm.mutex); 1078c6f73aabSFrançois Tigeot } 1079c6f73aabSFrançois Tigeot 1080c6f73aabSFrançois Tigeot radeon_pm_compute_clocks(rdev); 1081c6f73aabSFrançois Tigeot } 1082c6f73aabSFrançois Tigeot 108357e252bfSMichael Neumann static void radeon_pm_suspend_old(struct radeon_device *rdev) 1084926deccbSFrançois Tigeot { 1085926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1086926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 1087926deccbSFrançois Tigeot if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) 1088926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED; 1089926deccbSFrançois Tigeot } 1090926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1091926deccbSFrançois Tigeot 1092926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1093926deccbSFrançois Tigeot cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 1094926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1095926deccbSFrançois Tigeot } 1096926deccbSFrançois Tigeot 109757e252bfSMichael Neumann static void radeon_pm_suspend_dpm(struct radeon_device *rdev) 109857e252bfSMichael Neumann { 109957e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 110057e252bfSMichael Neumann /* disable dpm */ 110157e252bfSMichael Neumann radeon_dpm_disable(rdev); 110257e252bfSMichael Neumann /* reset the power state */ 110357e252bfSMichael Neumann rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 110457e252bfSMichael Neumann rdev->pm.dpm_enabled = false; 110557e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 110657e252bfSMichael Neumann } 110757e252bfSMichael Neumann 110857e252bfSMichael Neumann void radeon_pm_suspend(struct radeon_device *rdev) 110957e252bfSMichael Neumann { 111057e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) 111157e252bfSMichael Neumann radeon_pm_suspend_dpm(rdev); 111257e252bfSMichael Neumann else 111357e252bfSMichael Neumann radeon_pm_suspend_old(rdev); 111457e252bfSMichael Neumann } 111557e252bfSMichael Neumann 111657e252bfSMichael Neumann static void radeon_pm_resume_old(struct radeon_device *rdev) 1117926deccbSFrançois Tigeot { 1118926deccbSFrançois Tigeot /* set up the default clocks if the MC ucode is loaded */ 1119926deccbSFrançois Tigeot if ((rdev->family >= CHIP_BARTS) && 11204cd92098Szrj (rdev->family <= CHIP_CAYMAN) && 1121926deccbSFrançois Tigeot rdev->mc_fw) { 1122926deccbSFrançois Tigeot if (rdev->pm.default_vddc) 1123926deccbSFrançois Tigeot radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 1124926deccbSFrançois Tigeot SET_VOLTAGE_TYPE_ASIC_VDDC); 1125926deccbSFrançois Tigeot if (rdev->pm.default_vddci) 1126926deccbSFrançois Tigeot radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 1127926deccbSFrançois Tigeot SET_VOLTAGE_TYPE_ASIC_VDDCI); 1128926deccbSFrançois Tigeot if (rdev->pm.default_sclk) 1129926deccbSFrançois Tigeot radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 1130926deccbSFrançois Tigeot if (rdev->pm.default_mclk) 1131926deccbSFrançois Tigeot radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 1132926deccbSFrançois Tigeot } 1133926deccbSFrançois Tigeot /* asic init will reset the default power state */ 1134926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1135926deccbSFrançois Tigeot rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; 1136926deccbSFrançois Tigeot rdev->pm.current_clock_mode_index = 0; 1137926deccbSFrançois Tigeot rdev->pm.current_sclk = rdev->pm.default_sclk; 1138926deccbSFrançois Tigeot rdev->pm.current_mclk = rdev->pm.default_mclk; 1139c6f73aabSFrançois Tigeot if (rdev->pm.power_state) { 1140926deccbSFrançois Tigeot rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; 1141926deccbSFrançois Tigeot rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; 1142c6f73aabSFrançois Tigeot } 1143926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_DYNPM 1144926deccbSFrançois Tigeot && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) { 1145926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 1146926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1147926deccbSFrançois Tigeot schedule_delayed_work(&rdev->pm.dynpm_idle_work, 1148926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 1149926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1150926deccbSFrançois Tigeot } 1151926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1152926deccbSFrançois Tigeot radeon_pm_compute_clocks(rdev); 1153926deccbSFrançois Tigeot } 1154926deccbSFrançois Tigeot 115557e252bfSMichael Neumann static void radeon_pm_resume_dpm(struct radeon_device *rdev) 1156926deccbSFrançois Tigeot { 1157926deccbSFrançois Tigeot int ret; 1158926deccbSFrançois Tigeot 115957e252bfSMichael Neumann /* asic init will reset to the boot state */ 116057e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 116157e252bfSMichael Neumann rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 116257e252bfSMichael Neumann radeon_dpm_setup_asic(rdev); 116357e252bfSMichael Neumann ret = radeon_dpm_enable(rdev); 116457e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 1165c6f73aabSFrançois Tigeot if (ret) 1166c6f73aabSFrançois Tigeot goto dpm_resume_fail; 1167c6f73aabSFrançois Tigeot rdev->pm.dpm_enabled = true; 1168c6f73aabSFrançois Tigeot return; 1169c6f73aabSFrançois Tigeot 1170c6f73aabSFrançois Tigeot dpm_resume_fail: 117157e252bfSMichael Neumann DRM_ERROR("radeon: dpm resume failed\n"); 117257e252bfSMichael Neumann if ((rdev->family >= CHIP_BARTS) && 11734cd92098Szrj (rdev->family <= CHIP_CAYMAN) && 117457e252bfSMichael Neumann rdev->mc_fw) { 117557e252bfSMichael Neumann if (rdev->pm.default_vddc) 117657e252bfSMichael Neumann radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 117757e252bfSMichael Neumann SET_VOLTAGE_TYPE_ASIC_VDDC); 117857e252bfSMichael Neumann if (rdev->pm.default_vddci) 117957e252bfSMichael Neumann radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 118057e252bfSMichael Neumann SET_VOLTAGE_TYPE_ASIC_VDDCI); 118157e252bfSMichael Neumann if (rdev->pm.default_sclk) 118257e252bfSMichael Neumann radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 118357e252bfSMichael Neumann if (rdev->pm.default_mclk) 118457e252bfSMichael Neumann radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 118557e252bfSMichael Neumann } 118657e252bfSMichael Neumann } 118757e252bfSMichael Neumann 118857e252bfSMichael Neumann void radeon_pm_resume(struct radeon_device *rdev) 118957e252bfSMichael Neumann { 119057e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) 119157e252bfSMichael Neumann radeon_pm_resume_dpm(rdev); 119257e252bfSMichael Neumann else 119357e252bfSMichael Neumann radeon_pm_resume_old(rdev); 119457e252bfSMichael Neumann } 119557e252bfSMichael Neumann 119657e252bfSMichael Neumann static int radeon_pm_init_old(struct radeon_device *rdev) 119757e252bfSMichael Neumann { 119857e252bfSMichael Neumann int ret; 119957e252bfSMichael Neumann 1200926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_DEFAULT; 1201926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 1202926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 1203926deccbSFrançois Tigeot rdev->pm.dynpm_can_upclock = true; 1204926deccbSFrançois Tigeot rdev->pm.dynpm_can_downclock = true; 1205926deccbSFrançois Tigeot rdev->pm.default_sclk = rdev->clock.default_sclk; 1206926deccbSFrançois Tigeot rdev->pm.default_mclk = rdev->clock.default_mclk; 1207926deccbSFrançois Tigeot rdev->pm.current_sclk = rdev->clock.default_sclk; 1208926deccbSFrançois Tigeot rdev->pm.current_mclk = rdev->clock.default_mclk; 1209926deccbSFrançois Tigeot rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 1210926deccbSFrançois Tigeot 1211926deccbSFrançois Tigeot if (rdev->bios) { 1212926deccbSFrançois Tigeot if (rdev->is_atom_bios) 1213926deccbSFrançois Tigeot radeon_atombios_get_power_modes(rdev); 1214926deccbSFrançois Tigeot else 1215926deccbSFrançois Tigeot radeon_combios_get_power_modes(rdev); 1216926deccbSFrançois Tigeot radeon_pm_print_states(rdev); 1217926deccbSFrançois Tigeot radeon_pm_init_profile(rdev); 1218926deccbSFrançois Tigeot /* set up the default clocks if the MC ucode is loaded */ 1219926deccbSFrançois Tigeot if ((rdev->family >= CHIP_BARTS) && 12204cd92098Szrj (rdev->family <= CHIP_CAYMAN) && 1221926deccbSFrançois Tigeot rdev->mc_fw) { 1222926deccbSFrançois Tigeot if (rdev->pm.default_vddc) 1223926deccbSFrançois Tigeot radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 1224926deccbSFrançois Tigeot SET_VOLTAGE_TYPE_ASIC_VDDC); 1225926deccbSFrançois Tigeot if (rdev->pm.default_vddci) 1226926deccbSFrançois Tigeot radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 1227926deccbSFrançois Tigeot SET_VOLTAGE_TYPE_ASIC_VDDCI); 1228926deccbSFrançois Tigeot if (rdev->pm.default_sclk) 1229926deccbSFrançois Tigeot radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 1230926deccbSFrançois Tigeot if (rdev->pm.default_mclk) 1231926deccbSFrançois Tigeot radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 1232926deccbSFrançois Tigeot } 1233926deccbSFrançois Tigeot } 1234926deccbSFrançois Tigeot 1235926deccbSFrançois Tigeot /* set up the internal thermal sensor if applicable */ 1236926deccbSFrançois Tigeot ret = radeon_hwmon_init(rdev); 1237926deccbSFrançois Tigeot if (ret) 1238926deccbSFrançois Tigeot return ret; 1239926deccbSFrançois Tigeot 1240926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1241926deccbSFrançois Tigeot INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler); 1242926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1243926deccbSFrançois Tigeot 1244926deccbSFrançois Tigeot if (rdev->pm.num_power_states > 1) { 1245926deccbSFrançois Tigeot /* where's the best place to put these? */ 1246926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1247926deccbSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_profile); 1248926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1249926deccbSFrançois Tigeot if (ret) 1250926deccbSFrançois Tigeot DRM_ERROR("failed to create device file for power profile\n"); 1251926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1252926deccbSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_method); 1253926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1254926deccbSFrançois Tigeot if (ret) 1255926deccbSFrançois Tigeot DRM_ERROR("failed to create device file for power method\n"); 1256926deccbSFrançois Tigeot 1257926deccbSFrançois Tigeot if (radeon_debugfs_pm_init(rdev)) { 1258926deccbSFrançois Tigeot DRM_ERROR("Failed to register debugfs file for PM!\n"); 1259926deccbSFrançois Tigeot } 1260926deccbSFrançois Tigeot 1261926deccbSFrançois Tigeot DRM_INFO("radeon: power management initialized\n"); 1262926deccbSFrançois Tigeot } 1263926deccbSFrançois Tigeot 1264926deccbSFrançois Tigeot return 0; 1265926deccbSFrançois Tigeot } 1266926deccbSFrançois Tigeot 126757e252bfSMichael Neumann static void radeon_dpm_print_power_states(struct radeon_device *rdev) 126857e252bfSMichael Neumann { 126957e252bfSMichael Neumann int i; 127057e252bfSMichael Neumann 127157e252bfSMichael Neumann for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 127257e252bfSMichael Neumann printk("== power state %d ==\n", i); 127357e252bfSMichael Neumann radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]); 127457e252bfSMichael Neumann } 127557e252bfSMichael Neumann } 127657e252bfSMichael Neumann 127757e252bfSMichael Neumann static int radeon_pm_init_dpm(struct radeon_device *rdev) 127857e252bfSMichael Neumann { 127957e252bfSMichael Neumann int ret; 128057e252bfSMichael Neumann 12814cd92098Szrj /* default to balanced state */ 128257e252bfSMichael Neumann rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; 128357e252bfSMichael Neumann rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 12844cd92098Szrj rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; 128557e252bfSMichael Neumann rdev->pm.default_sclk = rdev->clock.default_sclk; 128657e252bfSMichael Neumann rdev->pm.default_mclk = rdev->clock.default_mclk; 128757e252bfSMichael Neumann rdev->pm.current_sclk = rdev->clock.default_sclk; 128857e252bfSMichael Neumann rdev->pm.current_mclk = rdev->clock.default_mclk; 128957e252bfSMichael Neumann rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 129057e252bfSMichael Neumann 129157e252bfSMichael Neumann if (rdev->bios && rdev->is_atom_bios) 129257e252bfSMichael Neumann radeon_atombios_get_power_modes(rdev); 129357e252bfSMichael Neumann else 129457e252bfSMichael Neumann return -EINVAL; 129557e252bfSMichael Neumann 129657e252bfSMichael Neumann /* set up the internal thermal sensor if applicable */ 129757e252bfSMichael Neumann ret = radeon_hwmon_init(rdev); 129857e252bfSMichael Neumann if (ret) 129957e252bfSMichael Neumann return ret; 130057e252bfSMichael Neumann 130157e252bfSMichael Neumann TASK_INIT(&rdev->pm.dpm.thermal.work, 0, radeon_dpm_thermal_work_handler, rdev); 130257e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 130357e252bfSMichael Neumann radeon_dpm_init(rdev); 130457e252bfSMichael Neumann rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 1305c6f73aabSFrançois Tigeot if (radeon_dpm == 1) 130657e252bfSMichael Neumann radeon_dpm_print_power_states(rdev); 130757e252bfSMichael Neumann radeon_dpm_setup_asic(rdev); 130857e252bfSMichael Neumann ret = radeon_dpm_enable(rdev); 130957e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 1310c6f73aabSFrançois Tigeot if (ret) 1311c6f73aabSFrançois Tigeot goto dpm_failed; 1312c6f73aabSFrançois Tigeot rdev->pm.dpm_enabled = true; 1313c6f73aabSFrançois Tigeot 1314c6f73aabSFrançois Tigeot #ifdef TODO_DEVICE_FILE 1315c6f73aabSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); 1316c6f73aabSFrançois Tigeot if (ret) 1317c6f73aabSFrançois Tigeot DRM_ERROR("failed to create device file for dpm state\n"); 1318c6f73aabSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 1319c6f73aabSFrançois Tigeot if (ret) 1320c6f73aabSFrançois Tigeot DRM_ERROR("failed to create device file for dpm state\n"); 1321c6f73aabSFrançois Tigeot /* XXX: these are noops for dpm but are here for backwards compat */ 1322c6f73aabSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_profile); 1323c6f73aabSFrançois Tigeot if (ret) 1324c6f73aabSFrançois Tigeot DRM_ERROR("failed to create device file for power profile\n"); 1325c6f73aabSFrançois Tigeot ret = device_create_file(rdev->dev, &dev_attr_power_method); 1326c6f73aabSFrançois Tigeot if (ret) 1327c6f73aabSFrançois Tigeot DRM_ERROR("failed to create device file for power method\n"); 1328c6f73aabSFrançois Tigeot 1329c6f73aabSFrançois Tigeot if (radeon_debugfs_pm_init(rdev)) { 1330c6f73aabSFrançois Tigeot DRM_ERROR("Failed to register debugfs file for dpm!\n"); 1331c6f73aabSFrançois Tigeot } 1332c6f73aabSFrançois Tigeot #endif 1333c6f73aabSFrançois Tigeot 1334c6f73aabSFrançois Tigeot DRM_INFO("radeon: dpm initialized\n"); 1335c6f73aabSFrançois Tigeot 1336c6f73aabSFrançois Tigeot return 0; 1337c6f73aabSFrançois Tigeot 1338c6f73aabSFrançois Tigeot dpm_failed: 133957e252bfSMichael Neumann rdev->pm.dpm_enabled = false; 134057e252bfSMichael Neumann if ((rdev->family >= CHIP_BARTS) && 13414cd92098Szrj (rdev->family <= CHIP_CAYMAN) && 134257e252bfSMichael Neumann rdev->mc_fw) { 134357e252bfSMichael Neumann if (rdev->pm.default_vddc) 134457e252bfSMichael Neumann radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 134557e252bfSMichael Neumann SET_VOLTAGE_TYPE_ASIC_VDDC); 134657e252bfSMichael Neumann if (rdev->pm.default_vddci) 134757e252bfSMichael Neumann radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 134857e252bfSMichael Neumann SET_VOLTAGE_TYPE_ASIC_VDDCI); 134957e252bfSMichael Neumann if (rdev->pm.default_sclk) 135057e252bfSMichael Neumann radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 135157e252bfSMichael Neumann if (rdev->pm.default_mclk) 135257e252bfSMichael Neumann radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 135357e252bfSMichael Neumann } 135457e252bfSMichael Neumann DRM_ERROR("radeon: dpm initialization failed\n"); 135557e252bfSMichael Neumann return ret; 135657e252bfSMichael Neumann } 135757e252bfSMichael Neumann 135857e252bfSMichael Neumann int radeon_pm_init(struct radeon_device *rdev) 135957e252bfSMichael Neumann { 136057e252bfSMichael Neumann /* enable dpm on rv6xx+ */ 136157e252bfSMichael Neumann switch (rdev->family) { 136257e252bfSMichael Neumann case CHIP_RV610: 136357e252bfSMichael Neumann case CHIP_RV630: 136457e252bfSMichael Neumann case CHIP_RV620: 136557e252bfSMichael Neumann case CHIP_RV635: 136657e252bfSMichael Neumann case CHIP_RV670: 136757e252bfSMichael Neumann case CHIP_RS780: 136857e252bfSMichael Neumann case CHIP_RS880: 136957e252bfSMichael Neumann case CHIP_RV770: 1370c6f73aabSFrançois Tigeot /* DPM requires the RLC, RV770+ dGPU requires SMC */ 1371c6f73aabSFrançois Tigeot if (!rdev->rlc_fw) 1372c6f73aabSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_PROFILE; 1373c6f73aabSFrançois Tigeot else if ((rdev->family >= CHIP_RV770) && 1374c6f73aabSFrançois Tigeot (!(rdev->flags & RADEON_IS_IGP)) && 1375c6f73aabSFrançois Tigeot (!rdev->smc_fw)) 1376c6f73aabSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_PROFILE; 1377c6f73aabSFrançois Tigeot else if (radeon_dpm == 1) 1378c6f73aabSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_DPM; 1379c6f73aabSFrançois Tigeot else 1380c6f73aabSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_PROFILE; 1381c6f73aabSFrançois Tigeot break; 138257e252bfSMichael Neumann case CHIP_RV730: 138357e252bfSMichael Neumann case CHIP_RV710: 138457e252bfSMichael Neumann case CHIP_RV740: 138557e252bfSMichael Neumann case CHIP_CEDAR: 138657e252bfSMichael Neumann case CHIP_REDWOOD: 138757e252bfSMichael Neumann case CHIP_JUNIPER: 138857e252bfSMichael Neumann case CHIP_CYPRESS: 138957e252bfSMichael Neumann case CHIP_HEMLOCK: 139057e252bfSMichael Neumann case CHIP_PALM: 139157e252bfSMichael Neumann case CHIP_SUMO: 139257e252bfSMichael Neumann case CHIP_SUMO2: 139357e252bfSMichael Neumann case CHIP_BARTS: 139457e252bfSMichael Neumann case CHIP_TURKS: 139557e252bfSMichael Neumann case CHIP_CAICOS: 139657e252bfSMichael Neumann case CHIP_CAYMAN: 139757e252bfSMichael Neumann case CHIP_ARUBA: 139857e252bfSMichael Neumann case CHIP_TAHITI: 139957e252bfSMichael Neumann case CHIP_PITCAIRN: 140057e252bfSMichael Neumann case CHIP_VERDE: 140157e252bfSMichael Neumann case CHIP_OLAND: 140257e252bfSMichael Neumann case CHIP_HAINAN: 14034cd92098Szrj case CHIP_BONAIRE: 14044cd92098Szrj case CHIP_KABINI: 14054cd92098Szrj case CHIP_KAVERI: 1406c6f73aabSFrançois Tigeot case CHIP_HAWAII: 1407c6f73aabSFrançois Tigeot case CHIP_MULLINS: 140857e252bfSMichael Neumann /* DPM requires the RLC, RV770+ dGPU requires SMC */ 140957e252bfSMichael Neumann if (!rdev->rlc_fw) 141057e252bfSMichael Neumann rdev->pm.pm_method = PM_METHOD_PROFILE; 141157e252bfSMichael Neumann else if ((rdev->family >= CHIP_RV770) && 141257e252bfSMichael Neumann (!(rdev->flags & RADEON_IS_IGP)) && 141357e252bfSMichael Neumann (!rdev->smc_fw)) 141457e252bfSMichael Neumann rdev->pm.pm_method = PM_METHOD_PROFILE; 1415c6f73aabSFrançois Tigeot else if (radeon_dpm == 0) 141657e252bfSMichael Neumann rdev->pm.pm_method = PM_METHOD_PROFILE; 1417c6f73aabSFrançois Tigeot else 1418c6f73aabSFrançois Tigeot rdev->pm.pm_method = PM_METHOD_DPM; 141957e252bfSMichael Neumann break; 142057e252bfSMichael Neumann default: 142157e252bfSMichael Neumann /* default to profile method */ 142257e252bfSMichael Neumann rdev->pm.pm_method = PM_METHOD_PROFILE; 142357e252bfSMichael Neumann break; 142457e252bfSMichael Neumann } 142557e252bfSMichael Neumann 142657e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) 142757e252bfSMichael Neumann return radeon_pm_init_dpm(rdev); 142857e252bfSMichael Neumann else 142957e252bfSMichael Neumann return radeon_pm_init_old(rdev); 143057e252bfSMichael Neumann } 143157e252bfSMichael Neumann 1432c6f73aabSFrançois Tigeot int radeon_pm_late_init(struct radeon_device *rdev) 1433c6f73aabSFrançois Tigeot { 1434c6f73aabSFrançois Tigeot int ret = 0; 1435c6f73aabSFrançois Tigeot 1436c6f73aabSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_DPM) { 1437c6f73aabSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1438c6f73aabSFrançois Tigeot ret = radeon_dpm_late_enable(rdev); 1439c6f73aabSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1440c6f73aabSFrançois Tigeot } 1441c6f73aabSFrançois Tigeot return ret; 1442c6f73aabSFrançois Tigeot } 1443c6f73aabSFrançois Tigeot 144457e252bfSMichael Neumann static void radeon_pm_fini_old(struct radeon_device *rdev) 1445926deccbSFrançois Tigeot { 1446926deccbSFrançois Tigeot if (rdev->pm.num_power_states > 1) { 1447926deccbSFrançois Tigeot DRM_UNLOCK(rdev->ddev); /* Work around LOR. */ 1448926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1449926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 1450926deccbSFrançois Tigeot rdev->pm.profile = PM_PROFILE_DEFAULT; 1451926deccbSFrançois Tigeot radeon_pm_update_profile(rdev); 1452926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1453926deccbSFrançois Tigeot } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 1454926deccbSFrançois Tigeot /* reset default clocks */ 1455926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 1456926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 1457926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1458926deccbSFrançois Tigeot } 1459926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1460926deccbSFrançois Tigeot DRM_LOCK(rdev->ddev); 1461926deccbSFrançois Tigeot 1462926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1463926deccbSFrançois Tigeot cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 1464926deccbSFrançois Tigeot 1465926deccbSFrançois Tigeot device_remove_file(rdev->dev, &dev_attr_power_profile); 1466926deccbSFrançois Tigeot device_remove_file(rdev->dev, &dev_attr_power_method); 1467926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1468926deccbSFrançois Tigeot } 1469926deccbSFrançois Tigeot 1470926deccbSFrançois Tigeot if (rdev->pm.power_state) { 1471926deccbSFrançois Tigeot int i; 1472926deccbSFrançois Tigeot for (i = 0; i < rdev->pm.num_power_states; ++i) { 1473c4ef309bSzrj kfree(rdev->pm.power_state[i].clock_info); 1474926deccbSFrançois Tigeot } 1475c4ef309bSzrj kfree(rdev->pm.power_state); 1476926deccbSFrançois Tigeot rdev->pm.power_state = NULL; 1477926deccbSFrançois Tigeot rdev->pm.num_power_states = 0; 1478926deccbSFrançois Tigeot } 1479926deccbSFrançois Tigeot 1480926deccbSFrançois Tigeot radeon_hwmon_fini(rdev); 1481926deccbSFrançois Tigeot } 1482926deccbSFrançois Tigeot 148357e252bfSMichael Neumann static void radeon_pm_fini_dpm(struct radeon_device *rdev) 148457e252bfSMichael Neumann { 148557e252bfSMichael Neumann if (rdev->pm.num_power_states > 1) { 148657e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 148757e252bfSMichael Neumann radeon_dpm_disable(rdev); 148857e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 148957e252bfSMichael Neumann 149057e252bfSMichael Neumann #ifdef TODO_DEVICE_FILE 149157e252bfSMichael Neumann device_remove_file(rdev->dev, &dev_attr_power_dpm_state); 149257e252bfSMichael Neumann device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 149357e252bfSMichael Neumann /* XXX backwards compat */ 149457e252bfSMichael Neumann device_remove_file(rdev->dev, &dev_attr_power_profile); 149557e252bfSMichael Neumann device_remove_file(rdev->dev, &dev_attr_power_method); 149657e252bfSMichael Neumann #endif 149757e252bfSMichael Neumann } 149857e252bfSMichael Neumann radeon_dpm_fini(rdev); 149957e252bfSMichael Neumann 150057e252bfSMichael Neumann kfree(rdev->pm.power_state); 150157e252bfSMichael Neumann 150257e252bfSMichael Neumann radeon_hwmon_fini(rdev); 150357e252bfSMichael Neumann } 150457e252bfSMichael Neumann 150557e252bfSMichael Neumann void radeon_pm_fini(struct radeon_device *rdev) 150657e252bfSMichael Neumann { 150757e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) 150857e252bfSMichael Neumann radeon_pm_fini_dpm(rdev); 150957e252bfSMichael Neumann else 151057e252bfSMichael Neumann radeon_pm_fini_old(rdev); 151157e252bfSMichael Neumann } 151257e252bfSMichael Neumann 151357e252bfSMichael Neumann static void radeon_pm_compute_clocks_old(struct radeon_device *rdev) 1514926deccbSFrançois Tigeot { 1515926deccbSFrançois Tigeot struct drm_device *ddev = rdev->ddev; 1516926deccbSFrançois Tigeot struct drm_crtc *crtc; 1517926deccbSFrançois Tigeot struct radeon_crtc *radeon_crtc; 1518926deccbSFrançois Tigeot 1519926deccbSFrançois Tigeot if (rdev->pm.num_power_states < 2) 1520926deccbSFrançois Tigeot return; 1521926deccbSFrançois Tigeot 1522926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1523926deccbSFrançois Tigeot 1524926deccbSFrançois Tigeot rdev->pm.active_crtcs = 0; 1525926deccbSFrançois Tigeot rdev->pm.active_crtc_count = 0; 1526c6f73aabSFrançois Tigeot if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 1527926deccbSFrançois Tigeot list_for_each_entry(crtc, 1528926deccbSFrançois Tigeot &ddev->mode_config.crtc_list, head) { 1529926deccbSFrançois Tigeot radeon_crtc = to_radeon_crtc(crtc); 1530926deccbSFrançois Tigeot if (radeon_crtc->enabled) { 1531926deccbSFrançois Tigeot rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); 1532926deccbSFrançois Tigeot rdev->pm.active_crtc_count++; 1533926deccbSFrançois Tigeot } 1534926deccbSFrançois Tigeot } 1535c6f73aabSFrançois Tigeot } 1536926deccbSFrançois Tigeot 1537926deccbSFrançois Tigeot if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 1538926deccbSFrançois Tigeot radeon_pm_update_profile(rdev); 1539926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1540926deccbSFrançois Tigeot } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 1541926deccbSFrançois Tigeot if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) { 1542926deccbSFrançois Tigeot if (rdev->pm.active_crtc_count > 1) { 1543926deccbSFrançois Tigeot if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 1544926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1545926deccbSFrançois Tigeot cancel_delayed_work(&rdev->pm.dynpm_idle_work); 1546926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1547926deccbSFrançois Tigeot 1548926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 1549926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 1550926deccbSFrançois Tigeot radeon_pm_get_dynpm_state(rdev); 1551926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1552926deccbSFrançois Tigeot 1553926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n"); 1554926deccbSFrançois Tigeot } 1555926deccbSFrançois Tigeot } else if (rdev->pm.active_crtc_count == 1) { 1556926deccbSFrançois Tigeot /* TODO: Increase clocks if needed for current mode */ 1557926deccbSFrançois Tigeot 1558926deccbSFrançois Tigeot if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) { 1559926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 1560926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK; 1561926deccbSFrançois Tigeot radeon_pm_get_dynpm_state(rdev); 1562926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1563926deccbSFrançois Tigeot 1564926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1565926deccbSFrançois Tigeot schedule_delayed_work(&rdev->pm.dynpm_idle_work, 1566926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 1567926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1568926deccbSFrançois Tigeot } else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) { 1569926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 1570926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1571926deccbSFrançois Tigeot schedule_delayed_work(&rdev->pm.dynpm_idle_work, 1572926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 1573926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1574926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n"); 1575926deccbSFrançois Tigeot } 1576926deccbSFrançois Tigeot } else { /* count == 0 */ 1577926deccbSFrançois Tigeot if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) { 1578926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1579926deccbSFrançois Tigeot cancel_delayed_work(&rdev->pm.dynpm_idle_work); 1580926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1581926deccbSFrançois Tigeot 1582926deccbSFrançois Tigeot rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM; 1583926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM; 1584926deccbSFrançois Tigeot radeon_pm_get_dynpm_state(rdev); 1585926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1586926deccbSFrançois Tigeot } 1587926deccbSFrançois Tigeot } 1588926deccbSFrançois Tigeot } 1589926deccbSFrançois Tigeot } 1590926deccbSFrançois Tigeot 1591926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1592926deccbSFrançois Tigeot } 1593926deccbSFrançois Tigeot 159457e252bfSMichael Neumann static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) 159557e252bfSMichael Neumann { 159657e252bfSMichael Neumann struct drm_device *ddev = rdev->ddev; 159757e252bfSMichael Neumann struct drm_crtc *crtc; 159857e252bfSMichael Neumann struct radeon_crtc *radeon_crtc; 159957e252bfSMichael Neumann 1600c6f73aabSFrançois Tigeot if (!rdev->pm.dpm_enabled) 1601c6f73aabSFrançois Tigeot return; 1602c6f73aabSFrançois Tigeot 160357e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 160457e252bfSMichael Neumann 160557e252bfSMichael Neumann /* update active crtc counts */ 160657e252bfSMichael Neumann rdev->pm.dpm.new_active_crtcs = 0; 160757e252bfSMichael Neumann rdev->pm.dpm.new_active_crtc_count = 0; 1608c6f73aabSFrançois Tigeot if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 160957e252bfSMichael Neumann list_for_each_entry(crtc, 161057e252bfSMichael Neumann &ddev->mode_config.crtc_list, head) { 161157e252bfSMichael Neumann radeon_crtc = to_radeon_crtc(crtc); 161257e252bfSMichael Neumann if (crtc->enabled) { 161357e252bfSMichael Neumann rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id); 161457e252bfSMichael Neumann rdev->pm.dpm.new_active_crtc_count++; 161557e252bfSMichael Neumann } 161657e252bfSMichael Neumann } 1617c6f73aabSFrançois Tigeot } 161857e252bfSMichael Neumann 161957e252bfSMichael Neumann /* update battery/ac status */ 162057e252bfSMichael Neumann if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) 162157e252bfSMichael Neumann rdev->pm.dpm.ac_power = true; 162257e252bfSMichael Neumann else 162357e252bfSMichael Neumann rdev->pm.dpm.ac_power = false; 162457e252bfSMichael Neumann 162557e252bfSMichael Neumann radeon_dpm_change_power_state_locked(rdev); 162657e252bfSMichael Neumann 162757e252bfSMichael Neumann lockmgr(&rdev->pm.mutex, LK_RELEASE); 1628c6f73aabSFrançois Tigeot 162957e252bfSMichael Neumann } 163057e252bfSMichael Neumann 163157e252bfSMichael Neumann void radeon_pm_compute_clocks(struct radeon_device *rdev) 163257e252bfSMichael Neumann { 163357e252bfSMichael Neumann if (rdev->pm.pm_method == PM_METHOD_DPM) 163457e252bfSMichael Neumann radeon_pm_compute_clocks_dpm(rdev); 163557e252bfSMichael Neumann else 163657e252bfSMichael Neumann radeon_pm_compute_clocks_old(rdev); 163757e252bfSMichael Neumann } 163857e252bfSMichael Neumann 1639926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev) 1640926deccbSFrançois Tigeot { 1641926deccbSFrançois Tigeot int crtc, vpos, hpos, vbl_status; 1642926deccbSFrançois Tigeot bool in_vbl = true; 1643926deccbSFrançois Tigeot 1644926deccbSFrançois Tigeot /* Iterate over all active crtc's. All crtc's must be in vblank, 1645926deccbSFrançois Tigeot * otherwise return in_vbl == false. 1646926deccbSFrançois Tigeot */ 1647926deccbSFrançois Tigeot for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { 1648926deccbSFrançois Tigeot if (rdev->pm.active_crtcs & (1 << crtc)) { 1649782e40d3SFrançois Tigeot vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL); 1650926deccbSFrançois Tigeot if ((vbl_status & DRM_SCANOUTPOS_VALID) && 1651926deccbSFrançois Tigeot !(vbl_status & DRM_SCANOUTPOS_INVBL)) 1652926deccbSFrançois Tigeot in_vbl = false; 1653926deccbSFrançois Tigeot } 1654926deccbSFrançois Tigeot } 1655926deccbSFrançois Tigeot 1656926deccbSFrançois Tigeot return in_vbl; 1657926deccbSFrançois Tigeot } 1658926deccbSFrançois Tigeot 1659926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish) 1660926deccbSFrançois Tigeot { 1661926deccbSFrançois Tigeot u32 stat_crtc = 0; 1662926deccbSFrançois Tigeot bool in_vbl = radeon_pm_in_vbl(rdev); 1663926deccbSFrançois Tigeot 1664926deccbSFrançois Tigeot if (in_vbl == false) 1665926deccbSFrançois Tigeot DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc, 1666926deccbSFrançois Tigeot finish ? "exit" : "entry"); 1667926deccbSFrançois Tigeot return in_vbl; 1668926deccbSFrançois Tigeot } 1669926deccbSFrançois Tigeot 1670926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP 1671926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work) 1672926deccbSFrançois Tigeot { 1673926deccbSFrançois Tigeot struct radeon_device *rdev; 1674926deccbSFrançois Tigeot int resched; 1675926deccbSFrançois Tigeot rdev = container_of(work, struct radeon_device, 1676926deccbSFrançois Tigeot pm.dynpm_idle_work.work); 1677926deccbSFrançois Tigeot 1678926deccbSFrançois Tigeot resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); 1679926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE); 1680926deccbSFrançois Tigeot if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 1681926deccbSFrançois Tigeot int not_processed = 0; 1682926deccbSFrançois Tigeot int i; 1683926deccbSFrançois Tigeot 1684926deccbSFrançois Tigeot for (i = 0; i < RADEON_NUM_RINGS; ++i) { 1685926deccbSFrançois Tigeot struct radeon_ring *ring = &rdev->ring[i]; 1686926deccbSFrançois Tigeot 1687926deccbSFrançois Tigeot if (ring->ready) { 1688926deccbSFrançois Tigeot not_processed += radeon_fence_count_emitted(rdev, i); 1689926deccbSFrançois Tigeot if (not_processed >= 3) 1690926deccbSFrançois Tigeot break; 1691926deccbSFrançois Tigeot } 1692926deccbSFrançois Tigeot } 1693926deccbSFrançois Tigeot 1694926deccbSFrançois Tigeot if (not_processed >= 3) { /* should upclock */ 1695926deccbSFrançois Tigeot if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) { 1696926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 1697926deccbSFrançois Tigeot } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 1698926deccbSFrançois Tigeot rdev->pm.dynpm_can_upclock) { 1699926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = 1700926deccbSFrançois Tigeot DYNPM_ACTION_UPCLOCK; 1701926deccbSFrançois Tigeot rdev->pm.dynpm_action_timeout = jiffies + 1702926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 1703926deccbSFrançois Tigeot } 1704926deccbSFrançois Tigeot } else if (not_processed == 0) { /* should downclock */ 1705926deccbSFrançois Tigeot if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) { 1706926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 1707926deccbSFrançois Tigeot } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 1708926deccbSFrançois Tigeot rdev->pm.dynpm_can_downclock) { 1709926deccbSFrançois Tigeot rdev->pm.dynpm_planned_action = 1710926deccbSFrançois Tigeot DYNPM_ACTION_DOWNCLOCK; 1711926deccbSFrançois Tigeot rdev->pm.dynpm_action_timeout = jiffies + 1712926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 1713926deccbSFrançois Tigeot } 1714926deccbSFrançois Tigeot } 1715926deccbSFrançois Tigeot 1716926deccbSFrançois Tigeot /* Note, radeon_pm_set_clocks is called with static_switch set 1717926deccbSFrançois Tigeot * to false since we want to wait for vbl to avoid flicker. 1718926deccbSFrançois Tigeot */ 1719926deccbSFrançois Tigeot if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE && 1720926deccbSFrançois Tigeot jiffies > rdev->pm.dynpm_action_timeout) { 1721926deccbSFrançois Tigeot radeon_pm_get_dynpm_state(rdev); 1722926deccbSFrançois Tigeot radeon_pm_set_clocks(rdev); 1723926deccbSFrançois Tigeot } 1724926deccbSFrançois Tigeot 1725926deccbSFrançois Tigeot schedule_delayed_work(&rdev->pm.dynpm_idle_work, 1726926deccbSFrançois Tigeot msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 1727926deccbSFrançois Tigeot } 1728926deccbSFrançois Tigeot lockmgr(&rdev->pm.mutex, LK_RELEASE); 1729926deccbSFrançois Tigeot ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); 1730926deccbSFrançois Tigeot } 1731926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */ 1732926deccbSFrançois Tigeot 1733926deccbSFrançois Tigeot /* 1734926deccbSFrançois Tigeot * Debugfs info 1735926deccbSFrançois Tigeot */ 1736926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS) 1737926deccbSFrançois Tigeot 1738926deccbSFrançois Tigeot static int radeon_debugfs_pm_info(struct seq_file *m, void *data) 1739926deccbSFrançois Tigeot { 1740926deccbSFrançois Tigeot struct drm_info_node *node = (struct drm_info_node *) m->private; 1741926deccbSFrançois Tigeot struct drm_device *dev = node->minor->dev; 1742926deccbSFrançois Tigeot struct radeon_device *rdev = dev->dev_private; 1743c6f73aabSFrançois Tigeot struct drm_device *ddev = rdev->ddev; 1744926deccbSFrançois Tigeot 1745c6f73aabSFrançois Tigeot if ((rdev->flags & RADEON_IS_PX) && 1746c6f73aabSFrançois Tigeot (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 1747c6f73aabSFrançois Tigeot seq_printf(m, "PX asic powered off\n"); 1748c6f73aabSFrançois Tigeot } else if (rdev->pm.dpm_enabled) { 174957e252bfSMichael Neumann spin_lock(&rdev->pm.mutex); 175057e252bfSMichael Neumann if (rdev->asic->dpm.debugfs_print_current_performance_level) 175157e252bfSMichael Neumann radeon_dpm_debugfs_print_current_performance_level(rdev, m); 175257e252bfSMichael Neumann else 175357e252bfSMichael Neumann seq_printf(m, "Debugfs support not implemented for this asic\n"); 175457e252bfSMichael Neumann spin_unlock(&rdev->pm.mutex); 175557e252bfSMichael Neumann } else { 1756926deccbSFrançois Tigeot seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); 1757f43cf1b1SMichael Neumann /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ 1758f43cf1b1SMichael Neumann if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) 1759f43cf1b1SMichael Neumann seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); 1760f43cf1b1SMichael Neumann else 1761926deccbSFrançois Tigeot seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); 1762926deccbSFrançois Tigeot seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); 1763926deccbSFrançois Tigeot if (rdev->asic->pm.get_memory_clock) 1764926deccbSFrançois Tigeot seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); 1765926deccbSFrançois Tigeot if (rdev->pm.current_vddc) 1766926deccbSFrançois Tigeot seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); 1767926deccbSFrançois Tigeot if (rdev->asic->pm.get_pcie_lanes) 1768926deccbSFrançois Tigeot seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); 176957e252bfSMichael Neumann } 1770926deccbSFrançois Tigeot 1771926deccbSFrançois Tigeot return 0; 1772926deccbSFrançois Tigeot } 1773926deccbSFrançois Tigeot 1774926deccbSFrançois Tigeot static struct drm_info_list radeon_pm_info_list[] = { 1775926deccbSFrançois Tigeot {"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL}, 1776926deccbSFrançois Tigeot }; 1777926deccbSFrançois Tigeot #endif 1778926deccbSFrançois Tigeot 1779926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev) 1780926deccbSFrançois Tigeot { 1781926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS) 1782926deccbSFrançois Tigeot return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); 1783926deccbSFrançois Tigeot #else 1784926deccbSFrançois Tigeot return 0; 1785926deccbSFrançois Tigeot #endif 1786926deccbSFrançois Tigeot } 1787