18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 38c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 48c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 58c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 68c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 78c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 108c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 138c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 148c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 158c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 168c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 178c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 188c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Authors: Rafał Miłecki <zajec5@gmail.com> 218c2ecf20Sopenharmony_ci * Alex Deucher <alexdeucher@gmail.com> 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 258c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 268c2ecf20Sopenharmony_ci#include <linux/pci.h> 278c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "atom.h" 338c2ecf20Sopenharmony_ci#include "avivod.h" 348c2ecf20Sopenharmony_ci#include "r600_dpm.h" 358c2ecf20Sopenharmony_ci#include "radeon.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define RADEON_IDLE_LOOP_MS 100 388c2ecf20Sopenharmony_ci#define RADEON_RECLOCK_DELAY_MS 200 398c2ecf20Sopenharmony_ci#define RADEON_WAIT_VBLANK_TIMEOUT 200 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const char *radeon_pm_state_type_name[5] = { 428c2ecf20Sopenharmony_ci "", 438c2ecf20Sopenharmony_ci "Powersave", 448c2ecf20Sopenharmony_ci "Battery", 458c2ecf20Sopenharmony_ci "Balanced", 468c2ecf20Sopenharmony_ci "Performance", 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void radeon_dynpm_idle_work_handler(struct work_struct *work); 508c2ecf20Sopenharmony_cistatic int radeon_debugfs_pm_init(struct radeon_device *rdev); 518c2ecf20Sopenharmony_cistatic bool radeon_pm_in_vbl(struct radeon_device *rdev); 528c2ecf20Sopenharmony_cistatic bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish); 538c2ecf20Sopenharmony_cistatic void radeon_pm_update_profile(struct radeon_device *rdev); 548c2ecf20Sopenharmony_cistatic void radeon_pm_set_clocks(struct radeon_device *rdev); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciint radeon_pm_get_type_index(struct radeon_device *rdev, 578c2ecf20Sopenharmony_ci enum radeon_pm_state_type ps_type, 588c2ecf20Sopenharmony_ci int instance) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int i; 618c2ecf20Sopenharmony_ci int found_instance = -1; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (i = 0; i < rdev->pm.num_power_states; i++) { 648c2ecf20Sopenharmony_ci if (rdev->pm.power_state[i].type == ps_type) { 658c2ecf20Sopenharmony_ci found_instance++; 668c2ecf20Sopenharmony_ci if (found_instance == instance) 678c2ecf20Sopenharmony_ci return i; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci /* return default if no match */ 718c2ecf20Sopenharmony_ci return rdev->pm.default_power_state_index; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_civoid radeon_pm_acpi_event_handler(struct radeon_device *rdev) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 778c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 788c2ecf20Sopenharmony_ci if (power_supply_is_system_supplied() > 0) 798c2ecf20Sopenharmony_ci rdev->pm.dpm.ac_power = true; 808c2ecf20Sopenharmony_ci else 818c2ecf20Sopenharmony_ci rdev->pm.dpm.ac_power = false; 828c2ecf20Sopenharmony_ci if (rdev->family == CHIP_ARUBA) { 838c2ecf20Sopenharmony_ci if (rdev->asic->dpm.enable_bapm) 848c2ecf20Sopenharmony_ci radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 878c2ecf20Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 888c2ecf20Sopenharmony_ci if (rdev->pm.profile == PM_PROFILE_AUTO) { 898c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 908c2ecf20Sopenharmony_ci radeon_pm_update_profile(rdev); 918c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 928c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void radeon_pm_update_profile(struct radeon_device *rdev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci switch (rdev->pm.profile) { 1008c2ecf20Sopenharmony_ci case PM_PROFILE_DEFAULT: 1018c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX; 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case PM_PROFILE_AUTO: 1048c2ecf20Sopenharmony_ci if (power_supply_is_system_supplied() > 0) { 1058c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 1068c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 1118c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci case PM_PROFILE_LOW: 1178c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 1188c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX; 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case PM_PROFILE_MID: 1238c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 1248c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case PM_PROFILE_HIGH: 1298c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 1308c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count == 0) { 1378c2ecf20Sopenharmony_ci rdev->pm.requested_power_state_index = 1388c2ecf20Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx; 1398c2ecf20Sopenharmony_ci rdev->pm.requested_clock_mode_index = 1408c2ecf20Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx; 1418c2ecf20Sopenharmony_ci } else { 1428c2ecf20Sopenharmony_ci rdev->pm.requested_power_state_index = 1438c2ecf20Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx; 1448c2ecf20Sopenharmony_ci rdev->pm.requested_clock_mode_index = 1458c2ecf20Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void radeon_unmap_vram_bos(struct radeon_device *rdev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct radeon_bo *bo, *n; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (list_empty(&rdev->gem.objects)) 1548c2ecf20Sopenharmony_ci return; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) { 1578c2ecf20Sopenharmony_ci if (bo->tbo.mem.mem_type == TTM_PL_VRAM) 1588c2ecf20Sopenharmony_ci ttm_bo_unmap_virtual(&bo->tbo); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void radeon_sync_with_vblank(struct radeon_device *rdev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci if (rdev->pm.active_crtcs) { 1658c2ecf20Sopenharmony_ci rdev->pm.vblank_sync = false; 1668c2ecf20Sopenharmony_ci wait_event_timeout( 1678c2ecf20Sopenharmony_ci rdev->irq.vblank_queue, rdev->pm.vblank_sync, 1688c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT)); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void radeon_set_power_state(struct radeon_device *rdev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u32 sclk, mclk; 1758c2ecf20Sopenharmony_ci bool misc_after = false; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 1788c2ecf20Sopenharmony_ci (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (radeon_gui_idle(rdev)) { 1828c2ecf20Sopenharmony_ci sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 1838c2ecf20Sopenharmony_ci clock_info[rdev->pm.requested_clock_mode_index].sclk; 1848c2ecf20Sopenharmony_ci if (sclk > rdev->pm.default_sclk) 1858c2ecf20Sopenharmony_ci sclk = rdev->pm.default_sclk; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* starting with BTC, there is one state that is used for both 1888c2ecf20Sopenharmony_ci * MH and SH. Difference is that we always use the high clock index for 1898c2ecf20Sopenharmony_ci * mclk and vddci. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_PROFILE) && 1928c2ecf20Sopenharmony_ci (rdev->family >= CHIP_BARTS) && 1938c2ecf20Sopenharmony_ci rdev->pm.active_crtc_count && 1948c2ecf20Sopenharmony_ci ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) || 1958c2ecf20Sopenharmony_ci (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX))) 1968c2ecf20Sopenharmony_ci mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 1978c2ecf20Sopenharmony_ci clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk; 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 2008c2ecf20Sopenharmony_ci clock_info[rdev->pm.requested_clock_mode_index].mclk; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (mclk > rdev->pm.default_mclk) 2038c2ecf20Sopenharmony_ci mclk = rdev->pm.default_mclk; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* upvolt before raising clocks, downvolt after lowering clocks */ 2068c2ecf20Sopenharmony_ci if (sclk < rdev->pm.current_sclk) 2078c2ecf20Sopenharmony_ci misc_after = true; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci radeon_sync_with_vblank(rdev); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 2128c2ecf20Sopenharmony_ci if (!radeon_pm_in_vbl(rdev)) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci radeon_pm_prepare(rdev); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!misc_after) 2198c2ecf20Sopenharmony_ci /* voltage, pcie lanes, etc.*/ 2208c2ecf20Sopenharmony_ci radeon_pm_misc(rdev); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* set engine clock */ 2238c2ecf20Sopenharmony_ci if (sclk != rdev->pm.current_sclk) { 2248c2ecf20Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, false); 2258c2ecf20Sopenharmony_ci radeon_set_engine_clock(rdev, sclk); 2268c2ecf20Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, true); 2278c2ecf20Sopenharmony_ci rdev->pm.current_sclk = sclk; 2288c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* set memory clock */ 2328c2ecf20Sopenharmony_ci if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) { 2338c2ecf20Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, false); 2348c2ecf20Sopenharmony_ci radeon_set_memory_clock(rdev, mclk); 2358c2ecf20Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, true); 2368c2ecf20Sopenharmony_ci rdev->pm.current_mclk = mclk; 2378c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (misc_after) 2418c2ecf20Sopenharmony_ci /* voltage, pcie lanes, etc.*/ 2428c2ecf20Sopenharmony_ci radeon_pm_misc(rdev); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci radeon_pm_finish(rdev); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; 2478c2ecf20Sopenharmony_ci rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; 2488c2ecf20Sopenharmony_ci } else 2498c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n"); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void radeon_pm_set_clocks(struct radeon_device *rdev) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2558c2ecf20Sopenharmony_ci int i, r; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* no need to take locks, etc. if nothing's going to change */ 2588c2ecf20Sopenharmony_ci if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 2598c2ecf20Sopenharmony_ci (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 2608c2ecf20Sopenharmony_ci return; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci down_write(&rdev->pm.mclk_lock); 2638c2ecf20Sopenharmony_ci mutex_lock(&rdev->ring_lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* wait for the rings to drain */ 2668c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) { 2678c2ecf20Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 2688c2ecf20Sopenharmony_ci if (!ring->ready) { 2698c2ecf20Sopenharmony_ci continue; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci r = radeon_fence_wait_empty(rdev, i); 2728c2ecf20Sopenharmony_ci if (r) { 2738c2ecf20Sopenharmony_ci /* needs a GPU reset dont reset here */ 2748c2ecf20Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 2758c2ecf20Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci radeon_unmap_vram_bos(rdev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (rdev->irq.installed) { 2838c2ecf20Sopenharmony_ci i = 0; 2848c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, rdev->ddev) { 2858c2ecf20Sopenharmony_ci if (rdev->pm.active_crtcs & (1 << i)) { 2868c2ecf20Sopenharmony_ci /* This can fail if a modeset is in progress */ 2878c2ecf20Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 2888c2ecf20Sopenharmony_ci rdev->pm.req_vblank |= (1 << i); 2898c2ecf20Sopenharmony_ci else 2908c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("crtc %d no vblank, can glitch\n", 2918c2ecf20Sopenharmony_ci i); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci i++; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci radeon_set_power_state(rdev); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (rdev->irq.installed) { 3008c2ecf20Sopenharmony_ci i = 0; 3018c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, rdev->ddev) { 3028c2ecf20Sopenharmony_ci if (rdev->pm.req_vblank & (1 << i)) { 3038c2ecf20Sopenharmony_ci rdev->pm.req_vblank &= ~(1 << i); 3048c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci i++; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* update display watermarks based on new power state */ 3118c2ecf20Sopenharmony_ci radeon_update_bandwidth_info(rdev); 3128c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count) 3138c2ecf20Sopenharmony_ci radeon_bandwidth_update(rdev); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 3188c2ecf20Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void radeon_pm_print_states(struct radeon_device *rdev) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int i, j; 3248c2ecf20Sopenharmony_ci struct radeon_power_state *power_state; 3258c2ecf20Sopenharmony_ci struct radeon_pm_clock_info *clock_info; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states); 3288c2ecf20Sopenharmony_ci for (i = 0; i < rdev->pm.num_power_states; i++) { 3298c2ecf20Sopenharmony_ci power_state = &rdev->pm.power_state[i]; 3308c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("State %d: %s\n", i, 3318c2ecf20Sopenharmony_ci radeon_pm_state_type_name[power_state->type]); 3328c2ecf20Sopenharmony_ci if (i == rdev->pm.default_power_state_index) 3338c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\tDefault"); 3348c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP)) 3358c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes); 3368c2ecf20Sopenharmony_ci if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) 3378c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\tSingle display only\n"); 3388c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes); 3398c2ecf20Sopenharmony_ci for (j = 0; j < power_state->num_clock_modes; j++) { 3408c2ecf20Sopenharmony_ci clock_info = &(power_state->clock_info[j]); 3418c2ecf20Sopenharmony_ci if (rdev->flags & RADEON_IS_IGP) 3428c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\t\t%d e: %d\n", 3438c2ecf20Sopenharmony_ci j, 3448c2ecf20Sopenharmony_ci clock_info->sclk * 10); 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n", 3478c2ecf20Sopenharmony_ci j, 3488c2ecf20Sopenharmony_ci clock_info->sclk * 10, 3498c2ecf20Sopenharmony_ci clock_info->mclk * 10, 3508c2ecf20Sopenharmony_ci clock_info->voltage.voltage); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic ssize_t radeon_get_pm_profile(struct device *dev, 3568c2ecf20Sopenharmony_ci struct device_attribute *attr, 3578c2ecf20Sopenharmony_ci char *buf) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 3608c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 3618c2ecf20Sopenharmony_ci int cp = rdev->pm.profile; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 3648c2ecf20Sopenharmony_ci (cp == PM_PROFILE_AUTO) ? "auto" : 3658c2ecf20Sopenharmony_ci (cp == PM_PROFILE_LOW) ? "low" : 3668c2ecf20Sopenharmony_ci (cp == PM_PROFILE_MID) ? "mid" : 3678c2ecf20Sopenharmony_ci (cp == PM_PROFILE_HIGH) ? "high" : "default"); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic ssize_t radeon_set_pm_profile(struct device *dev, 3718c2ecf20Sopenharmony_ci struct device_attribute *attr, 3728c2ecf20Sopenharmony_ci const char *buf, 3738c2ecf20Sopenharmony_ci size_t count) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 3768c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Can't set profile when the card is off */ 3798c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 3808c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 3848c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 3858c2ecf20Sopenharmony_ci if (strncmp("default", buf, strlen("default")) == 0) 3868c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 3878c2ecf20Sopenharmony_ci else if (strncmp("auto", buf, strlen("auto")) == 0) 3888c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_AUTO; 3898c2ecf20Sopenharmony_ci else if (strncmp("low", buf, strlen("low")) == 0) 3908c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_LOW; 3918c2ecf20Sopenharmony_ci else if (strncmp("mid", buf, strlen("mid")) == 0) 3928c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_MID; 3938c2ecf20Sopenharmony_ci else if (strncmp("high", buf, strlen("high")) == 0) 3948c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_HIGH; 3958c2ecf20Sopenharmony_ci else { 3968c2ecf20Sopenharmony_ci count = -EINVAL; 3978c2ecf20Sopenharmony_ci goto fail; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci radeon_pm_update_profile(rdev); 4008c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 4018c2ecf20Sopenharmony_ci } else 4028c2ecf20Sopenharmony_ci count = -EINVAL; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cifail: 4058c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return count; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic ssize_t radeon_get_pm_method(struct device *dev, 4118c2ecf20Sopenharmony_ci struct device_attribute *attr, 4128c2ecf20Sopenharmony_ci char *buf) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 4158c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 4168c2ecf20Sopenharmony_ci int pm = rdev->pm.pm_method; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 4198c2ecf20Sopenharmony_ci (pm == PM_METHOD_DYNPM) ? "dynpm" : 4208c2ecf20Sopenharmony_ci (pm == PM_METHOD_PROFILE) ? "profile" : "dpm"); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic ssize_t radeon_set_pm_method(struct device *dev, 4248c2ecf20Sopenharmony_ci struct device_attribute *attr, 4258c2ecf20Sopenharmony_ci const char *buf, 4268c2ecf20Sopenharmony_ci size_t count) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 4298c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Can't set method when the card is off */ 4328c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 4338c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 4348c2ecf20Sopenharmony_ci count = -EINVAL; 4358c2ecf20Sopenharmony_ci goto fail; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* we don't support the legacy modes with dpm */ 4398c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) { 4408c2ecf20Sopenharmony_ci count = -EINVAL; 4418c2ecf20Sopenharmony_ci goto fail; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { 4458c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 4468c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DYNPM; 4478c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 4488c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 4498c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 4508c2ecf20Sopenharmony_ci } else if (strncmp("profile", buf, strlen("profile")) == 0) { 4518c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 4528c2ecf20Sopenharmony_ci /* disable dynpm */ 4538c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 4548c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 4558c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 4568c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 4578c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci count = -EINVAL; 4608c2ecf20Sopenharmony_ci goto fail; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 4638c2ecf20Sopenharmony_cifail: 4648c2ecf20Sopenharmony_ci return count; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic ssize_t radeon_get_dpm_state(struct device *dev, 4688c2ecf20Sopenharmony_ci struct device_attribute *attr, 4698c2ecf20Sopenharmony_ci char *buf) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 4728c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 4738c2ecf20Sopenharmony_ci enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 4768c2ecf20Sopenharmony_ci (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : 4778c2ecf20Sopenharmony_ci (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic ssize_t radeon_set_dpm_state(struct device *dev, 4818c2ecf20Sopenharmony_ci struct device_attribute *attr, 4828c2ecf20Sopenharmony_ci const char *buf, 4838c2ecf20Sopenharmony_ci size_t count) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 4868c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 4898c2ecf20Sopenharmony_ci if (strncmp("battery", buf, strlen("battery")) == 0) 4908c2ecf20Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; 4918c2ecf20Sopenharmony_ci else if (strncmp("balanced", buf, strlen("balanced")) == 0) 4928c2ecf20Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 4938c2ecf20Sopenharmony_ci else if (strncmp("performance", buf, strlen("performance")) == 0) 4948c2ecf20Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; 4958c2ecf20Sopenharmony_ci else { 4968c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 4978c2ecf20Sopenharmony_ci count = -EINVAL; 4988c2ecf20Sopenharmony_ci goto fail; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Can't set dpm state when the card is off */ 5038c2ecf20Sopenharmony_ci if (!(rdev->flags & RADEON_IS_PX) || 5048c2ecf20Sopenharmony_ci (ddev->switch_power_state == DRM_SWITCH_POWER_ON)) 5058c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cifail: 5088c2ecf20Sopenharmony_ci return count; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, 5128c2ecf20Sopenharmony_ci struct device_attribute *attr, 5138c2ecf20Sopenharmony_ci char *buf) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 5168c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 5178c2ecf20Sopenharmony_ci enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 5208c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 5218c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "off\n"); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 5248c2ecf20Sopenharmony_ci (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : 5258c2ecf20Sopenharmony_ci (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, 5298c2ecf20Sopenharmony_ci struct device_attribute *attr, 5308c2ecf20Sopenharmony_ci const char *buf, 5318c2ecf20Sopenharmony_ci size_t count) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 5348c2ecf20Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 5358c2ecf20Sopenharmony_ci enum radeon_dpm_forced_level level; 5368c2ecf20Sopenharmony_ci int ret = 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Can't force performance level when the card is off */ 5398c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 5408c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 5418c2ecf20Sopenharmony_ci return -EINVAL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 5448c2ecf20Sopenharmony_ci if (strncmp("low", buf, strlen("low")) == 0) { 5458c2ecf20Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_LOW; 5468c2ecf20Sopenharmony_ci } else if (strncmp("high", buf, strlen("high")) == 0) { 5478c2ecf20Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_HIGH; 5488c2ecf20Sopenharmony_ci } else if (strncmp("auto", buf, strlen("auto")) == 0) { 5498c2ecf20Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_AUTO; 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci count = -EINVAL; 5528c2ecf20Sopenharmony_ci goto fail; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci if (rdev->asic->dpm.force_performance_level) { 5558c2ecf20Sopenharmony_ci if (rdev->pm.dpm.thermal_active) { 5568c2ecf20Sopenharmony_ci count = -EINVAL; 5578c2ecf20Sopenharmony_ci goto fail; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci ret = radeon_dpm_force_performance_level(rdev, level); 5608c2ecf20Sopenharmony_ci if (ret) 5618c2ecf20Sopenharmony_ci count = -EINVAL; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_cifail: 5648c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return count; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev, 5708c2ecf20Sopenharmony_ci struct device_attribute *attr, 5718c2ecf20Sopenharmony_ci char *buf) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 5748c2ecf20Sopenharmony_ci u32 pwm_mode = 0; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (rdev->asic->dpm.fan_ctrl_get_mode) 5778c2ecf20Sopenharmony_ci pwm_mode = rdev->asic->dpm.fan_ctrl_get_mode(rdev); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* never 0 (full-speed), fuse or smc-controlled always */ 5808c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev, 5848c2ecf20Sopenharmony_ci struct device_attribute *attr, 5858c2ecf20Sopenharmony_ci const char *buf, 5868c2ecf20Sopenharmony_ci size_t count) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 5898c2ecf20Sopenharmony_ci int err; 5908c2ecf20Sopenharmony_ci int value; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if(!rdev->asic->dpm.fan_ctrl_set_mode) 5938c2ecf20Sopenharmony_ci return -EINVAL; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci err = kstrtoint(buf, 10, &value); 5968c2ecf20Sopenharmony_ci if (err) 5978c2ecf20Sopenharmony_ci return err; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci switch (value) { 6008c2ecf20Sopenharmony_ci case 1: /* manual, percent-based */ 6018c2ecf20Sopenharmony_ci rdev->asic->dpm.fan_ctrl_set_mode(rdev, FDO_PWM_MODE_STATIC); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci default: /* disable */ 6048c2ecf20Sopenharmony_ci rdev->asic->dpm.fan_ctrl_set_mode(rdev, 0); 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return count; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_min(struct device *dev, 6128c2ecf20Sopenharmony_ci struct device_attribute *attr, 6138c2ecf20Sopenharmony_ci char *buf) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", 0); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_max(struct device *dev, 6198c2ecf20Sopenharmony_ci struct device_attribute *attr, 6208c2ecf20Sopenharmony_ci char *buf) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", 255); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_set_pwm1(struct device *dev, 6268c2ecf20Sopenharmony_ci struct device_attribute *attr, 6278c2ecf20Sopenharmony_ci const char *buf, size_t count) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 6308c2ecf20Sopenharmony_ci int err; 6318c2ecf20Sopenharmony_ci u32 value; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci err = kstrtou32(buf, 10, &value); 6348c2ecf20Sopenharmony_ci if (err) 6358c2ecf20Sopenharmony_ci return err; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci value = (value * 100) / 255; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci err = rdev->asic->dpm.set_fan_speed_percent(rdev, value); 6408c2ecf20Sopenharmony_ci if (err) 6418c2ecf20Sopenharmony_ci return err; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return count; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1(struct device *dev, 6478c2ecf20Sopenharmony_ci struct device_attribute *attr, 6488c2ecf20Sopenharmony_ci char *buf) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 6518c2ecf20Sopenharmony_ci int err; 6528c2ecf20Sopenharmony_ci u32 speed; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci err = rdev->asic->dpm.get_fan_speed_percent(rdev, &speed); 6558c2ecf20Sopenharmony_ci if (err) 6568c2ecf20Sopenharmony_ci return err; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci speed = (speed * 255) / 100; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", speed); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); 6648c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); 6658c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state); 6668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, 6678c2ecf20Sopenharmony_ci radeon_get_dpm_forced_performance_level, 6688c2ecf20Sopenharmony_ci radeon_set_dpm_forced_performance_level); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_show_temp(struct device *dev, 6718c2ecf20Sopenharmony_ci struct device_attribute *attr, 6728c2ecf20Sopenharmony_ci char *buf) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 6758c2ecf20Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 6768c2ecf20Sopenharmony_ci int temp; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Can't get temperature when the card is off */ 6798c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 6808c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 6818c2ecf20Sopenharmony_ci return -EINVAL; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (rdev->asic->pm.get_temperature) 6848c2ecf20Sopenharmony_ci temp = radeon_get_temperature(rdev); 6858c2ecf20Sopenharmony_ci else 6868c2ecf20Sopenharmony_ci temp = 0; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", temp); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, 6928c2ecf20Sopenharmony_ci struct device_attribute *attr, 6938c2ecf20Sopenharmony_ci char *buf) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 6968c2ecf20Sopenharmony_ci int hyst = to_sensor_dev_attr(attr)->index; 6978c2ecf20Sopenharmony_ci int temp; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (hyst) 7008c2ecf20Sopenharmony_ci temp = rdev->pm.dpm.thermal.min_temp; 7018c2ecf20Sopenharmony_ci else 7028c2ecf20Sopenharmony_ci temp = rdev->pm.dpm.thermal.max_temp; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", temp); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); 7088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0); 7098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1); 7108c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0); 7118c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0); 7128c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0); 7138c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic ssize_t radeon_hwmon_show_sclk(struct device *dev, 7168c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 7198c2ecf20Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 7208c2ecf20Sopenharmony_ci u32 sclk = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Can't get clock frequency when the card is off */ 7238c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 7248c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 7258c2ecf20Sopenharmony_ci return -EINVAL; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (rdev->asic->dpm.get_current_sclk) 7288c2ecf20Sopenharmony_ci sclk = radeon_dpm_get_current_sclk(rdev); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Value returned by dpm is in 10 KHz units, need to convert it into Hz 7318c2ecf20Sopenharmony_ci for hwmon */ 7328c2ecf20Sopenharmony_ci sclk *= 10000; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", sclk); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(freq1_input, S_IRUGO, radeon_hwmon_show_sclk, NULL, 7388c2ecf20Sopenharmony_ci 0); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic struct attribute *hwmon_attributes[] = { 7428c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 7438c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 7448c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, 7458c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 7468c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_enable.dev_attr.attr, 7478c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_min.dev_attr.attr, 7488c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_max.dev_attr.attr, 7498c2ecf20Sopenharmony_ci &sensor_dev_attr_freq1_input.dev_attr.attr, 7508c2ecf20Sopenharmony_ci NULL 7518c2ecf20Sopenharmony_ci}; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic umode_t hwmon_attributes_visible(struct kobject *kobj, 7548c2ecf20Sopenharmony_ci struct attribute *attr, int index) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 7578c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 7588c2ecf20Sopenharmony_ci umode_t effective_mode = attr->mode; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* Skip attributes if DPM is not enabled */ 7618c2ecf20Sopenharmony_ci if (rdev->pm.pm_method != PM_METHOD_DPM && 7628c2ecf20Sopenharmony_ci (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || 7638c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr || 7648c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr || 7658c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || 7668c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 7678c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr || 7688c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_freq1_input.dev_attr.attr)) 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* Skip fan attributes if fan is not present */ 7728c2ecf20Sopenharmony_ci if (rdev->pm.no_fan && 7738c2ecf20Sopenharmony_ci (attr == &sensor_dev_attr_pwm1.dev_attr.attr || 7748c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || 7758c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 7768c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* mask fan attributes if we have no bindings for this asic to expose */ 7808c2ecf20Sopenharmony_ci if ((!rdev->asic->dpm.get_fan_speed_percent && 7818c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */ 7828c2ecf20Sopenharmony_ci (!rdev->asic->dpm.fan_ctrl_get_mode && 7838c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */ 7848c2ecf20Sopenharmony_ci effective_mode &= ~S_IRUGO; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if ((!rdev->asic->dpm.set_fan_speed_percent && 7878c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */ 7888c2ecf20Sopenharmony_ci (!rdev->asic->dpm.fan_ctrl_set_mode && 7898c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */ 7908c2ecf20Sopenharmony_ci effective_mode &= ~S_IWUSR; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* hide max/min values if we can't both query and manage the fan */ 7938c2ecf20Sopenharmony_ci if ((!rdev->asic->dpm.set_fan_speed_percent && 7948c2ecf20Sopenharmony_ci !rdev->asic->dpm.get_fan_speed_percent) && 7958c2ecf20Sopenharmony_ci (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 7968c2ecf20Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) 7978c2ecf20Sopenharmony_ci return 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return effective_mode; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic const struct attribute_group hwmon_attrgroup = { 8038c2ecf20Sopenharmony_ci .attrs = hwmon_attributes, 8048c2ecf20Sopenharmony_ci .is_visible = hwmon_attributes_visible, 8058c2ecf20Sopenharmony_ci}; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic const struct attribute_group *hwmon_groups[] = { 8088c2ecf20Sopenharmony_ci &hwmon_attrgroup, 8098c2ecf20Sopenharmony_ci NULL 8108c2ecf20Sopenharmony_ci}; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int radeon_hwmon_init(struct radeon_device *rdev) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci int err = 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci switch (rdev->pm.int_thermal_type) { 8178c2ecf20Sopenharmony_ci case THERMAL_TYPE_RV6XX: 8188c2ecf20Sopenharmony_ci case THERMAL_TYPE_RV770: 8198c2ecf20Sopenharmony_ci case THERMAL_TYPE_EVERGREEN: 8208c2ecf20Sopenharmony_ci case THERMAL_TYPE_NI: 8218c2ecf20Sopenharmony_ci case THERMAL_TYPE_SUMO: 8228c2ecf20Sopenharmony_ci case THERMAL_TYPE_SI: 8238c2ecf20Sopenharmony_ci case THERMAL_TYPE_CI: 8248c2ecf20Sopenharmony_ci case THERMAL_TYPE_KV: 8258c2ecf20Sopenharmony_ci if (rdev->asic->pm.get_temperature == NULL) 8268c2ecf20Sopenharmony_ci return err; 8278c2ecf20Sopenharmony_ci rdev->pm.int_hwmon_dev = hwmon_device_register_with_groups(rdev->dev, 8288c2ecf20Sopenharmony_ci "radeon", rdev, 8298c2ecf20Sopenharmony_ci hwmon_groups); 8308c2ecf20Sopenharmony_ci if (IS_ERR(rdev->pm.int_hwmon_dev)) { 8318c2ecf20Sopenharmony_ci err = PTR_ERR(rdev->pm.int_hwmon_dev); 8328c2ecf20Sopenharmony_ci dev_err(rdev->dev, 8338c2ecf20Sopenharmony_ci "Unable to register hwmon device: %d\n", err); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci default: 8378c2ecf20Sopenharmony_ci break; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return err; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic void radeon_hwmon_fini(struct radeon_device *rdev) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci if (rdev->pm.int_hwmon_dev) 8468c2ecf20Sopenharmony_ci hwmon_device_unregister(rdev->pm.int_hwmon_dev); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void radeon_dpm_thermal_work_handler(struct work_struct *work) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct radeon_device *rdev = 8528c2ecf20Sopenharmony_ci container_of(work, struct radeon_device, 8538c2ecf20Sopenharmony_ci pm.dpm.thermal.work); 8548c2ecf20Sopenharmony_ci /* switch to the thermal state */ 8558c2ecf20Sopenharmony_ci enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (!rdev->pm.dpm_enabled) 8588c2ecf20Sopenharmony_ci return; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (rdev->asic->pm.get_temperature) { 8618c2ecf20Sopenharmony_ci int temp = radeon_get_temperature(rdev); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (temp < rdev->pm.dpm.thermal.min_temp) 8648c2ecf20Sopenharmony_ci /* switch back the user state */ 8658c2ecf20Sopenharmony_ci dpm_state = rdev->pm.dpm.user_state; 8668c2ecf20Sopenharmony_ci } else { 8678c2ecf20Sopenharmony_ci if (rdev->pm.dpm.thermal.high_to_low) 8688c2ecf20Sopenharmony_ci /* switch back the user state */ 8698c2ecf20Sopenharmony_ci dpm_state = rdev->pm.dpm.user_state; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 8728c2ecf20Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) 8738c2ecf20Sopenharmony_ci rdev->pm.dpm.thermal_active = true; 8748c2ecf20Sopenharmony_ci else 8758c2ecf20Sopenharmony_ci rdev->pm.dpm.thermal_active = false; 8768c2ecf20Sopenharmony_ci rdev->pm.dpm.state = dpm_state; 8778c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic bool radeon_dpm_single_display(struct radeon_device *rdev) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ? 8858c2ecf20Sopenharmony_ci true : false; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* check if the vblank period is too short to adjust the mclk */ 8888c2ecf20Sopenharmony_ci if (single_display && rdev->asic->dpm.vblank_too_short) { 8898c2ecf20Sopenharmony_ci if (radeon_dpm_vblank_too_short(rdev)) 8908c2ecf20Sopenharmony_ci single_display = false; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 120hz tends to be problematic even if they are under the 8948c2ecf20Sopenharmony_ci * vblank limit. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120)) 8978c2ecf20Sopenharmony_ci single_display = false; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return single_display; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, 9038c2ecf20Sopenharmony_ci enum radeon_pm_state_type dpm_state) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci int i; 9068c2ecf20Sopenharmony_ci struct radeon_ps *ps; 9078c2ecf20Sopenharmony_ci u32 ui_class; 9088c2ecf20Sopenharmony_ci bool single_display = radeon_dpm_single_display(rdev); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* certain older asics have a separare 3D performance state, 9118c2ecf20Sopenharmony_ci * so try that first if the user selected performance 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) 9148c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; 9158c2ecf20Sopenharmony_ci /* balanced states don't exist at the moment */ 9168c2ecf20Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_BALANCED) 9178c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cirestart_search: 9208c2ecf20Sopenharmony_ci /* Pick the best power state based on current conditions */ 9218c2ecf20Sopenharmony_ci for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 9228c2ecf20Sopenharmony_ci ps = &rdev->pm.dpm.ps[i]; 9238c2ecf20Sopenharmony_ci ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; 9248c2ecf20Sopenharmony_ci switch (dpm_state) { 9258c2ecf20Sopenharmony_ci /* user states */ 9268c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_BATTERY: 9278c2ecf20Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { 9288c2ecf20Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 9298c2ecf20Sopenharmony_ci if (single_display) 9308c2ecf20Sopenharmony_ci return ps; 9318c2ecf20Sopenharmony_ci } else 9328c2ecf20Sopenharmony_ci return ps; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_BALANCED: 9368c2ecf20Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { 9378c2ecf20Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 9388c2ecf20Sopenharmony_ci if (single_display) 9398c2ecf20Sopenharmony_ci return ps; 9408c2ecf20Sopenharmony_ci } else 9418c2ecf20Sopenharmony_ci return ps; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_PERFORMANCE: 9458c2ecf20Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { 9468c2ecf20Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 9478c2ecf20Sopenharmony_ci if (single_display) 9488c2ecf20Sopenharmony_ci return ps; 9498c2ecf20Sopenharmony_ci } else 9508c2ecf20Sopenharmony_ci return ps; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci /* internal states */ 9548c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD: 9558c2ecf20Sopenharmony_ci if (rdev->pm.dpm.uvd_ps) 9568c2ecf20Sopenharmony_ci return rdev->pm.dpm.uvd_ps; 9578c2ecf20Sopenharmony_ci else 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_SD: 9608c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) 9618c2ecf20Sopenharmony_ci return ps; 9628c2ecf20Sopenharmony_ci break; 9638c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD: 9648c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) 9658c2ecf20Sopenharmony_ci return ps; 9668c2ecf20Sopenharmony_ci break; 9678c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 9688c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) 9698c2ecf20Sopenharmony_ci return ps; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 9728c2ecf20Sopenharmony_ci if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) 9738c2ecf20Sopenharmony_ci return ps; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_BOOT: 9768c2ecf20Sopenharmony_ci return rdev->pm.dpm.boot_ps; 9778c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_THERMAL: 9788c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) 9798c2ecf20Sopenharmony_ci return ps; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ACPI: 9828c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) 9838c2ecf20Sopenharmony_ci return ps; 9848c2ecf20Sopenharmony_ci break; 9858c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ULV: 9868c2ecf20Sopenharmony_ci if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) 9878c2ecf20Sopenharmony_ci return ps; 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_3DPERF: 9908c2ecf20Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) 9918c2ecf20Sopenharmony_ci return ps; 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci default: 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci /* use a fallback state if we didn't match */ 9988c2ecf20Sopenharmony_ci switch (dpm_state) { 9998c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_SD: 10008c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 10018c2ecf20Sopenharmony_ci goto restart_search; 10028c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD: 10038c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 10048c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 10058c2ecf20Sopenharmony_ci if (rdev->pm.dpm.uvd_ps) { 10068c2ecf20Sopenharmony_ci return rdev->pm.dpm.uvd_ps; 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 10098c2ecf20Sopenharmony_ci goto restart_search; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_THERMAL: 10128c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; 10138c2ecf20Sopenharmony_ci goto restart_search; 10148c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ACPI: 10158c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_BATTERY; 10168c2ecf20Sopenharmony_ci goto restart_search; 10178c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_BATTERY: 10188c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_BALANCED: 10198c2ecf20Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_3DPERF: 10208c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 10218c2ecf20Sopenharmony_ci goto restart_search; 10228c2ecf20Sopenharmony_ci default: 10238c2ecf20Sopenharmony_ci break; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return NULL; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci int i; 10328c2ecf20Sopenharmony_ci struct radeon_ps *ps; 10338c2ecf20Sopenharmony_ci enum radeon_pm_state_type dpm_state; 10348c2ecf20Sopenharmony_ci int ret; 10358c2ecf20Sopenharmony_ci bool single_display = radeon_dpm_single_display(rdev); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* if dpm init failed */ 10388c2ecf20Sopenharmony_ci if (!rdev->pm.dpm_enabled) 10398c2ecf20Sopenharmony_ci return; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) { 10428c2ecf20Sopenharmony_ci /* add other state override checks here */ 10438c2ecf20Sopenharmony_ci if ((!rdev->pm.dpm.thermal_active) && 10448c2ecf20Sopenharmony_ci (!rdev->pm.dpm.uvd_active)) 10458c2ecf20Sopenharmony_ci rdev->pm.dpm.state = rdev->pm.dpm.user_state; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci dpm_state = rdev->pm.dpm.state; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci ps = radeon_dpm_pick_power_state(rdev, dpm_state); 10508c2ecf20Sopenharmony_ci if (ps) 10518c2ecf20Sopenharmony_ci rdev->pm.dpm.requested_ps = ps; 10528c2ecf20Sopenharmony_ci else 10538c2ecf20Sopenharmony_ci return; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* no need to reprogram if nothing changed unless we are on BTC+ */ 10568c2ecf20Sopenharmony_ci if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) { 10578c2ecf20Sopenharmony_ci /* vce just modifies an existing state so force a change */ 10588c2ecf20Sopenharmony_ci if (ps->vce_active != rdev->pm.dpm.vce_active) 10598c2ecf20Sopenharmony_ci goto force; 10608c2ecf20Sopenharmony_ci /* user has made a display change (such as timing) */ 10618c2ecf20Sopenharmony_ci if (rdev->pm.dpm.single_display != single_display) 10628c2ecf20Sopenharmony_ci goto force; 10638c2ecf20Sopenharmony_ci if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { 10648c2ecf20Sopenharmony_ci /* for pre-BTC and APUs if the num crtcs changed but state is the same, 10658c2ecf20Sopenharmony_ci * all we need to do is update the display configuration. 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { 10688c2ecf20Sopenharmony_ci /* update display watermarks based on new power state */ 10698c2ecf20Sopenharmony_ci radeon_bandwidth_update(rdev); 10708c2ecf20Sopenharmony_ci /* update displays */ 10718c2ecf20Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 10728c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 10738c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci return; 10768c2ecf20Sopenharmony_ci } else { 10778c2ecf20Sopenharmony_ci /* for BTC+ if the num crtcs hasn't changed and state is the same, 10788c2ecf20Sopenharmony_ci * nothing to do, if the num crtcs is > 1 and state is the same, 10798c2ecf20Sopenharmony_ci * update display configuration. 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci if (rdev->pm.dpm.new_active_crtcs == 10828c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtcs) { 10838c2ecf20Sopenharmony_ci return; 10848c2ecf20Sopenharmony_ci } else { 10858c2ecf20Sopenharmony_ci if ((rdev->pm.dpm.current_active_crtc_count > 1) && 10868c2ecf20Sopenharmony_ci (rdev->pm.dpm.new_active_crtc_count > 1)) { 10878c2ecf20Sopenharmony_ci /* update display watermarks based on new power state */ 10888c2ecf20Sopenharmony_ci radeon_bandwidth_update(rdev); 10898c2ecf20Sopenharmony_ci /* update displays */ 10908c2ecf20Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 10918c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 10928c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 10938c2ecf20Sopenharmony_ci return; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ciforce: 11008c2ecf20Sopenharmony_ci if (radeon_dpm == 1) { 11018c2ecf20Sopenharmony_ci printk("switching from power state:\n"); 11028c2ecf20Sopenharmony_ci radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps); 11038c2ecf20Sopenharmony_ci printk("switching to power state:\n"); 11048c2ecf20Sopenharmony_ci radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci down_write(&rdev->pm.mclk_lock); 11088c2ecf20Sopenharmony_ci mutex_lock(&rdev->ring_lock); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* update whether vce is active */ 11118c2ecf20Sopenharmony_ci ps->vce_active = rdev->pm.dpm.vce_active; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci ret = radeon_dpm_pre_set_power_state(rdev); 11148c2ecf20Sopenharmony_ci if (ret) 11158c2ecf20Sopenharmony_ci goto done; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* update display watermarks based on new power state */ 11188c2ecf20Sopenharmony_ci radeon_bandwidth_update(rdev); 11198c2ecf20Sopenharmony_ci /* update displays */ 11208c2ecf20Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* wait for the rings to drain */ 11238c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) { 11248c2ecf20Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 11258c2ecf20Sopenharmony_ci if (ring->ready) 11268c2ecf20Sopenharmony_ci radeon_fence_wait_empty(rdev, i); 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* program the new power state */ 11308c2ecf20Sopenharmony_ci radeon_dpm_set_power_state(rdev); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* update current power state */ 11338c2ecf20Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci radeon_dpm_post_set_power_state(rdev); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 11388c2ecf20Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 11398c2ecf20Sopenharmony_ci rdev->pm.dpm.single_display = single_display; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (rdev->asic->dpm.force_performance_level) { 11428c2ecf20Sopenharmony_ci if (rdev->pm.dpm.thermal_active) { 11438c2ecf20Sopenharmony_ci enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 11448c2ecf20Sopenharmony_ci /* force low perf level for thermal */ 11458c2ecf20Sopenharmony_ci radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW); 11468c2ecf20Sopenharmony_ci /* save the user's level */ 11478c2ecf20Sopenharmony_ci rdev->pm.dpm.forced_level = level; 11488c2ecf20Sopenharmony_ci } else { 11498c2ecf20Sopenharmony_ci /* otherwise, user selected level */ 11508c2ecf20Sopenharmony_ci radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cidone: 11558c2ecf20Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 11568c2ecf20Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_civoid radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci enum radeon_pm_state_type dpm_state; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (rdev->asic->dpm.powergate_uvd) { 11648c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 11658c2ecf20Sopenharmony_ci /* don't powergate anything if we 11668c2ecf20Sopenharmony_ci have active but pause streams */ 11678c2ecf20Sopenharmony_ci enable |= rdev->pm.dpm.sd > 0; 11688c2ecf20Sopenharmony_ci enable |= rdev->pm.dpm.hd > 0; 11698c2ecf20Sopenharmony_ci /* enable/disable UVD */ 11708c2ecf20Sopenharmony_ci radeon_dpm_powergate_uvd(rdev, !enable); 11718c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 11728c2ecf20Sopenharmony_ci } else { 11738c2ecf20Sopenharmony_ci if (enable) { 11748c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 11758c2ecf20Sopenharmony_ci rdev->pm.dpm.uvd_active = true; 11768c2ecf20Sopenharmony_ci /* disable this for now */ 11778c2ecf20Sopenharmony_ci#if 0 11788c2ecf20Sopenharmony_ci if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0)) 11798c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD; 11808c2ecf20Sopenharmony_ci else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0)) 11818c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 11828c2ecf20Sopenharmony_ci else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1)) 11838c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 11848c2ecf20Sopenharmony_ci else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2)) 11858c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2; 11868c2ecf20Sopenharmony_ci else 11878c2ecf20Sopenharmony_ci#endif 11888c2ecf20Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD; 11898c2ecf20Sopenharmony_ci rdev->pm.dpm.state = dpm_state; 11908c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 11918c2ecf20Sopenharmony_ci } else { 11928c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 11938c2ecf20Sopenharmony_ci rdev->pm.dpm.uvd_active = false; 11948c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_civoid radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci if (enable) { 12048c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12058c2ecf20Sopenharmony_ci rdev->pm.dpm.vce_active = true; 12068c2ecf20Sopenharmony_ci /* XXX select vce level based on ring/task */ 12078c2ecf20Sopenharmony_ci rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL; 12088c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12098c2ecf20Sopenharmony_ci } else { 12108c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12118c2ecf20Sopenharmony_ci rdev->pm.dpm.vce_active = false; 12128c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic void radeon_pm_suspend_old(struct radeon_device *rdev) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12218c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 12228c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) 12238c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic void radeon_pm_suspend_dpm(struct radeon_device *rdev) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12338c2ecf20Sopenharmony_ci /* disable dpm */ 12348c2ecf20Sopenharmony_ci radeon_dpm_disable(rdev); 12358c2ecf20Sopenharmony_ci /* reset the power state */ 12368c2ecf20Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 12378c2ecf20Sopenharmony_ci rdev->pm.dpm_enabled = false; 12388c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_civoid radeon_pm_suspend(struct radeon_device *rdev) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 12448c2ecf20Sopenharmony_ci radeon_pm_suspend_dpm(rdev); 12458c2ecf20Sopenharmony_ci else 12468c2ecf20Sopenharmony_ci radeon_pm_suspend_old(rdev); 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void radeon_pm_resume_old(struct radeon_device *rdev) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci /* set up the default clocks if the MC ucode is loaded */ 12528c2ecf20Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 12538c2ecf20Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 12548c2ecf20Sopenharmony_ci rdev->mc_fw) { 12558c2ecf20Sopenharmony_ci if (rdev->pm.default_vddc) 12568c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 12578c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 12588c2ecf20Sopenharmony_ci if (rdev->pm.default_vddci) 12598c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 12608c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 12618c2ecf20Sopenharmony_ci if (rdev->pm.default_sclk) 12628c2ecf20Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 12638c2ecf20Sopenharmony_ci if (rdev->pm.default_mclk) 12648c2ecf20Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci /* asic init will reset the default power state */ 12678c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12688c2ecf20Sopenharmony_ci rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; 12698c2ecf20Sopenharmony_ci rdev->pm.current_clock_mode_index = 0; 12708c2ecf20Sopenharmony_ci rdev->pm.current_sclk = rdev->pm.default_sclk; 12718c2ecf20Sopenharmony_ci rdev->pm.current_mclk = rdev->pm.default_mclk; 12728c2ecf20Sopenharmony_ci if (rdev->pm.power_state) { 12738c2ecf20Sopenharmony_ci rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; 12748c2ecf20Sopenharmony_ci rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM 12778c2ecf20Sopenharmony_ci && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) { 12788c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 12798c2ecf20Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 12808c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12838c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic void radeon_pm_resume_dpm(struct radeon_device *rdev) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci int ret; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* asic init will reset to the boot state */ 12918c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 12928c2ecf20Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 12938c2ecf20Sopenharmony_ci radeon_dpm_setup_asic(rdev); 12948c2ecf20Sopenharmony_ci ret = radeon_dpm_enable(rdev); 12958c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 12968c2ecf20Sopenharmony_ci if (ret) 12978c2ecf20Sopenharmony_ci goto dpm_resume_fail; 12988c2ecf20Sopenharmony_ci rdev->pm.dpm_enabled = true; 12998c2ecf20Sopenharmony_ci return; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cidpm_resume_fail: 13028c2ecf20Sopenharmony_ci DRM_ERROR("radeon: dpm resume failed\n"); 13038c2ecf20Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 13048c2ecf20Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 13058c2ecf20Sopenharmony_ci rdev->mc_fw) { 13068c2ecf20Sopenharmony_ci if (rdev->pm.default_vddc) 13078c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 13088c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 13098c2ecf20Sopenharmony_ci if (rdev->pm.default_vddci) 13108c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 13118c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 13128c2ecf20Sopenharmony_ci if (rdev->pm.default_sclk) 13138c2ecf20Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 13148c2ecf20Sopenharmony_ci if (rdev->pm.default_mclk) 13158c2ecf20Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_civoid radeon_pm_resume(struct radeon_device *rdev) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 13228c2ecf20Sopenharmony_ci radeon_pm_resume_dpm(rdev); 13238c2ecf20Sopenharmony_ci else 13248c2ecf20Sopenharmony_ci radeon_pm_resume_old(rdev); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic int radeon_pm_init_old(struct radeon_device *rdev) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci int ret; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 13328c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 13338c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 13348c2ecf20Sopenharmony_ci rdev->pm.dynpm_can_upclock = true; 13358c2ecf20Sopenharmony_ci rdev->pm.dynpm_can_downclock = true; 13368c2ecf20Sopenharmony_ci rdev->pm.default_sclk = rdev->clock.default_sclk; 13378c2ecf20Sopenharmony_ci rdev->pm.default_mclk = rdev->clock.default_mclk; 13388c2ecf20Sopenharmony_ci rdev->pm.current_sclk = rdev->clock.default_sclk; 13398c2ecf20Sopenharmony_ci rdev->pm.current_mclk = rdev->clock.default_mclk; 13408c2ecf20Sopenharmony_ci rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (rdev->bios) { 13438c2ecf20Sopenharmony_ci if (rdev->is_atom_bios) 13448c2ecf20Sopenharmony_ci radeon_atombios_get_power_modes(rdev); 13458c2ecf20Sopenharmony_ci else 13468c2ecf20Sopenharmony_ci radeon_combios_get_power_modes(rdev); 13478c2ecf20Sopenharmony_ci radeon_pm_print_states(rdev); 13488c2ecf20Sopenharmony_ci radeon_pm_init_profile(rdev); 13498c2ecf20Sopenharmony_ci /* set up the default clocks if the MC ucode is loaded */ 13508c2ecf20Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 13518c2ecf20Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 13528c2ecf20Sopenharmony_ci rdev->mc_fw) { 13538c2ecf20Sopenharmony_ci if (rdev->pm.default_vddc) 13548c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 13558c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 13568c2ecf20Sopenharmony_ci if (rdev->pm.default_vddci) 13578c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 13588c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 13598c2ecf20Sopenharmony_ci if (rdev->pm.default_sclk) 13608c2ecf20Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 13618c2ecf20Sopenharmony_ci if (rdev->pm.default_mclk) 13628c2ecf20Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* set up the internal thermal sensor if applicable */ 13678c2ecf20Sopenharmony_ci ret = radeon_hwmon_init(rdev); 13688c2ecf20Sopenharmony_ci if (ret) 13698c2ecf20Sopenharmony_ci return ret; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 13748c2ecf20Sopenharmony_ci if (radeon_debugfs_pm_init(rdev)) { 13758c2ecf20Sopenharmony_ci DRM_ERROR("Failed to register debugfs file for PM!\n"); 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci DRM_INFO("radeon: power management initialized\n"); 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic void radeon_dpm_print_power_states(struct radeon_device *rdev) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci int i; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 13898c2ecf20Sopenharmony_ci printk("== power state %d ==\n", i); 13908c2ecf20Sopenharmony_ci radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic int radeon_pm_init_dpm(struct radeon_device *rdev) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci int ret; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* default to balanced state */ 13998c2ecf20Sopenharmony_ci rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; 14008c2ecf20Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 14018c2ecf20Sopenharmony_ci rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; 14028c2ecf20Sopenharmony_ci rdev->pm.default_sclk = rdev->clock.default_sclk; 14038c2ecf20Sopenharmony_ci rdev->pm.default_mclk = rdev->clock.default_mclk; 14048c2ecf20Sopenharmony_ci rdev->pm.current_sclk = rdev->clock.default_sclk; 14058c2ecf20Sopenharmony_ci rdev->pm.current_mclk = rdev->clock.default_mclk; 14068c2ecf20Sopenharmony_ci rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (rdev->bios && rdev->is_atom_bios) 14098c2ecf20Sopenharmony_ci radeon_atombios_get_power_modes(rdev); 14108c2ecf20Sopenharmony_ci else 14118c2ecf20Sopenharmony_ci return -EINVAL; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* set up the internal thermal sensor if applicable */ 14148c2ecf20Sopenharmony_ci ret = radeon_hwmon_init(rdev); 14158c2ecf20Sopenharmony_ci if (ret) 14168c2ecf20Sopenharmony_ci return ret; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); 14198c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 14208c2ecf20Sopenharmony_ci radeon_dpm_init(rdev); 14218c2ecf20Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 14228c2ecf20Sopenharmony_ci if (radeon_dpm == 1) 14238c2ecf20Sopenharmony_ci radeon_dpm_print_power_states(rdev); 14248c2ecf20Sopenharmony_ci radeon_dpm_setup_asic(rdev); 14258c2ecf20Sopenharmony_ci ret = radeon_dpm_enable(rdev); 14268c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 14278c2ecf20Sopenharmony_ci if (ret) 14288c2ecf20Sopenharmony_ci goto dpm_failed; 14298c2ecf20Sopenharmony_ci rdev->pm.dpm_enabled = true; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (radeon_debugfs_pm_init(rdev)) { 14328c2ecf20Sopenharmony_ci DRM_ERROR("Failed to register debugfs file for dpm!\n"); 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci DRM_INFO("radeon: dpm initialized\n"); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci return 0; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cidpm_failed: 14408c2ecf20Sopenharmony_ci rdev->pm.dpm_enabled = false; 14418c2ecf20Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 14428c2ecf20Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 14438c2ecf20Sopenharmony_ci rdev->mc_fw) { 14448c2ecf20Sopenharmony_ci if (rdev->pm.default_vddc) 14458c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 14468c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 14478c2ecf20Sopenharmony_ci if (rdev->pm.default_vddci) 14488c2ecf20Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 14498c2ecf20Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 14508c2ecf20Sopenharmony_ci if (rdev->pm.default_sclk) 14518c2ecf20Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 14528c2ecf20Sopenharmony_ci if (rdev->pm.default_mclk) 14538c2ecf20Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci DRM_ERROR("radeon: dpm initialization failed\n"); 14568c2ecf20Sopenharmony_ci return ret; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistruct radeon_dpm_quirk { 14608c2ecf20Sopenharmony_ci u32 chip_vendor; 14618c2ecf20Sopenharmony_ci u32 chip_device; 14628c2ecf20Sopenharmony_ci u32 subsys_vendor; 14638c2ecf20Sopenharmony_ci u32 subsys_device; 14648c2ecf20Sopenharmony_ci}; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci/* cards with dpm stability problems */ 14678c2ecf20Sopenharmony_cistatic struct radeon_dpm_quirk radeon_dpm_quirk_list[] = { 14688c2ecf20Sopenharmony_ci /* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */ 14698c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 }, 14708c2ecf20Sopenharmony_ci /* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */ 14718c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 }, 14728c2ecf20Sopenharmony_ci { 0, 0, 0, 0 }, 14738c2ecf20Sopenharmony_ci}; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ciint radeon_pm_init(struct radeon_device *rdev) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci struct radeon_dpm_quirk *p = radeon_dpm_quirk_list; 14788c2ecf20Sopenharmony_ci bool disable_dpm = false; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* Apply dpm quirks */ 14818c2ecf20Sopenharmony_ci while (p && p->chip_device != 0) { 14828c2ecf20Sopenharmony_ci if (rdev->pdev->vendor == p->chip_vendor && 14838c2ecf20Sopenharmony_ci rdev->pdev->device == p->chip_device && 14848c2ecf20Sopenharmony_ci rdev->pdev->subsystem_vendor == p->subsys_vendor && 14858c2ecf20Sopenharmony_ci rdev->pdev->subsystem_device == p->subsys_device) { 14868c2ecf20Sopenharmony_ci disable_dpm = true; 14878c2ecf20Sopenharmony_ci break; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci ++p; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci /* enable dpm on rv6xx+ */ 14938c2ecf20Sopenharmony_ci switch (rdev->family) { 14948c2ecf20Sopenharmony_ci case CHIP_RV610: 14958c2ecf20Sopenharmony_ci case CHIP_RV630: 14968c2ecf20Sopenharmony_ci case CHIP_RV620: 14978c2ecf20Sopenharmony_ci case CHIP_RV635: 14988c2ecf20Sopenharmony_ci case CHIP_RV670: 14998c2ecf20Sopenharmony_ci case CHIP_RS780: 15008c2ecf20Sopenharmony_ci case CHIP_RS880: 15018c2ecf20Sopenharmony_ci case CHIP_RV770: 15028c2ecf20Sopenharmony_ci /* DPM requires the RLC, RV770+ dGPU requires SMC */ 15038c2ecf20Sopenharmony_ci if (!rdev->rlc_fw) 15048c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15058c2ecf20Sopenharmony_ci else if ((rdev->family >= CHIP_RV770) && 15068c2ecf20Sopenharmony_ci (!(rdev->flags & RADEON_IS_IGP)) && 15078c2ecf20Sopenharmony_ci (!rdev->smc_fw)) 15088c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15098c2ecf20Sopenharmony_ci else if (radeon_dpm == 1) 15108c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DPM; 15118c2ecf20Sopenharmony_ci else 15128c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15138c2ecf20Sopenharmony_ci break; 15148c2ecf20Sopenharmony_ci case CHIP_RV730: 15158c2ecf20Sopenharmony_ci case CHIP_RV710: 15168c2ecf20Sopenharmony_ci case CHIP_RV740: 15178c2ecf20Sopenharmony_ci case CHIP_CEDAR: 15188c2ecf20Sopenharmony_ci case CHIP_REDWOOD: 15198c2ecf20Sopenharmony_ci case CHIP_JUNIPER: 15208c2ecf20Sopenharmony_ci case CHIP_CYPRESS: 15218c2ecf20Sopenharmony_ci case CHIP_HEMLOCK: 15228c2ecf20Sopenharmony_ci case CHIP_PALM: 15238c2ecf20Sopenharmony_ci case CHIP_SUMO: 15248c2ecf20Sopenharmony_ci case CHIP_SUMO2: 15258c2ecf20Sopenharmony_ci case CHIP_BARTS: 15268c2ecf20Sopenharmony_ci case CHIP_TURKS: 15278c2ecf20Sopenharmony_ci case CHIP_CAICOS: 15288c2ecf20Sopenharmony_ci case CHIP_CAYMAN: 15298c2ecf20Sopenharmony_ci case CHIP_ARUBA: 15308c2ecf20Sopenharmony_ci case CHIP_TAHITI: 15318c2ecf20Sopenharmony_ci case CHIP_PITCAIRN: 15328c2ecf20Sopenharmony_ci case CHIP_VERDE: 15338c2ecf20Sopenharmony_ci case CHIP_OLAND: 15348c2ecf20Sopenharmony_ci case CHIP_HAINAN: 15358c2ecf20Sopenharmony_ci case CHIP_BONAIRE: 15368c2ecf20Sopenharmony_ci case CHIP_KABINI: 15378c2ecf20Sopenharmony_ci case CHIP_KAVERI: 15388c2ecf20Sopenharmony_ci case CHIP_HAWAII: 15398c2ecf20Sopenharmony_ci case CHIP_MULLINS: 15408c2ecf20Sopenharmony_ci /* DPM requires the RLC, RV770+ dGPU requires SMC */ 15418c2ecf20Sopenharmony_ci if (!rdev->rlc_fw) 15428c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15438c2ecf20Sopenharmony_ci else if ((rdev->family >= CHIP_RV770) && 15448c2ecf20Sopenharmony_ci (!(rdev->flags & RADEON_IS_IGP)) && 15458c2ecf20Sopenharmony_ci (!rdev->smc_fw)) 15468c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15478c2ecf20Sopenharmony_ci else if (disable_dpm && (radeon_dpm == -1)) 15488c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15498c2ecf20Sopenharmony_ci else if (radeon_dpm == 0) 15508c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15518c2ecf20Sopenharmony_ci else 15528c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DPM; 15538c2ecf20Sopenharmony_ci break; 15548c2ecf20Sopenharmony_ci default: 15558c2ecf20Sopenharmony_ci /* default to profile method */ 15568c2ecf20Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 15578c2ecf20Sopenharmony_ci break; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 15618c2ecf20Sopenharmony_ci return radeon_pm_init_dpm(rdev); 15628c2ecf20Sopenharmony_ci else 15638c2ecf20Sopenharmony_ci return radeon_pm_init_old(rdev); 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ciint radeon_pm_late_init(struct radeon_device *rdev) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci int ret = 0; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) { 15718c2ecf20Sopenharmony_ci if (rdev->pm.dpm_enabled) { 15728c2ecf20Sopenharmony_ci if (!rdev->pm.sysfs_initialized) { 15738c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); 15748c2ecf20Sopenharmony_ci if (ret) 15758c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for dpm state\n"); 15768c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 15778c2ecf20Sopenharmony_ci if (ret) 15788c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for dpm state\n"); 15798c2ecf20Sopenharmony_ci /* XXX: these are noops for dpm but are here for backwards compat */ 15808c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_profile); 15818c2ecf20Sopenharmony_ci if (ret) 15828c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for power profile\n"); 15838c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_method); 15848c2ecf20Sopenharmony_ci if (ret) 15858c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for power method\n"); 15868c2ecf20Sopenharmony_ci rdev->pm.sysfs_initialized = true; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 15908c2ecf20Sopenharmony_ci ret = radeon_dpm_late_enable(rdev); 15918c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 15928c2ecf20Sopenharmony_ci if (ret) { 15938c2ecf20Sopenharmony_ci rdev->pm.dpm_enabled = false; 15948c2ecf20Sopenharmony_ci DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n"); 15958c2ecf20Sopenharmony_ci } else { 15968c2ecf20Sopenharmony_ci /* set the dpm state for PX since there won't be 15978c2ecf20Sopenharmony_ci * a modeset to call this. 15988c2ecf20Sopenharmony_ci */ 15998c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 16008c2ecf20Sopenharmony_ci } 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci } else { 16038c2ecf20Sopenharmony_ci if ((rdev->pm.num_power_states > 1) && 16048c2ecf20Sopenharmony_ci (!rdev->pm.sysfs_initialized)) { 16058c2ecf20Sopenharmony_ci /* where's the best place to put these? */ 16068c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_profile); 16078c2ecf20Sopenharmony_ci if (ret) 16088c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for power profile\n"); 16098c2ecf20Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_method); 16108c2ecf20Sopenharmony_ci if (ret) 16118c2ecf20Sopenharmony_ci DRM_ERROR("failed to create device file for power method\n"); 16128c2ecf20Sopenharmony_ci if (!ret) 16138c2ecf20Sopenharmony_ci rdev->pm.sysfs_initialized = true; 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci return ret; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic void radeon_pm_fini_old(struct radeon_device *rdev) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 16228c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 16238c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 16248c2ecf20Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 16258c2ecf20Sopenharmony_ci radeon_pm_update_profile(rdev); 16268c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 16278c2ecf20Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 16288c2ecf20Sopenharmony_ci /* reset default clocks */ 16298c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 16308c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 16318c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_profile); 16388c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_method); 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci radeon_hwmon_fini(rdev); 16428c2ecf20Sopenharmony_ci kfree(rdev->pm.power_state); 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic void radeon_pm_fini_dpm(struct radeon_device *rdev) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 16488c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 16498c2ecf20Sopenharmony_ci radeon_dpm_disable(rdev); 16508c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_dpm_state); 16538c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 16548c2ecf20Sopenharmony_ci /* XXX backwards compat */ 16558c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_profile); 16568c2ecf20Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_method); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci radeon_dpm_fini(rdev); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci radeon_hwmon_fini(rdev); 16618c2ecf20Sopenharmony_ci kfree(rdev->pm.power_state); 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_civoid radeon_pm_fini(struct radeon_device *rdev) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 16678c2ecf20Sopenharmony_ci radeon_pm_fini_dpm(rdev); 16688c2ecf20Sopenharmony_ci else 16698c2ecf20Sopenharmony_ci radeon_pm_fini_old(rdev); 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic void radeon_pm_compute_clocks_old(struct radeon_device *rdev) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 16758c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 16768c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (rdev->pm.num_power_states < 2) 16798c2ecf20Sopenharmony_ci return; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci rdev->pm.active_crtcs = 0; 16848c2ecf20Sopenharmony_ci rdev->pm.active_crtc_count = 0; 16858c2ecf20Sopenharmony_ci if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 16868c2ecf20Sopenharmony_ci list_for_each_entry(crtc, 16878c2ecf20Sopenharmony_ci &ddev->mode_config.crtc_list, head) { 16888c2ecf20Sopenharmony_ci radeon_crtc = to_radeon_crtc(crtc); 16898c2ecf20Sopenharmony_ci if (radeon_crtc->enabled) { 16908c2ecf20Sopenharmony_ci rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); 16918c2ecf20Sopenharmony_ci rdev->pm.active_crtc_count++; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 16978c2ecf20Sopenharmony_ci radeon_pm_update_profile(rdev); 16988c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 16998c2ecf20Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 17008c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) { 17018c2ecf20Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) { 17028c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 17038c2ecf20Sopenharmony_ci cancel_delayed_work(&rdev->pm.dynpm_idle_work); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 17068c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 17078c2ecf20Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 17088c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n"); 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci } else if (rdev->pm.active_crtc_count == 1) { 17138c2ecf20Sopenharmony_ci /* TODO: Increase clocks if needed for current mode */ 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) { 17168c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 17178c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK; 17188c2ecf20Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 17198c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 17228c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 17238c2ecf20Sopenharmony_ci } else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) { 17248c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 17258c2ecf20Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 17268c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 17278c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n"); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci } else { /* count == 0 */ 17308c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) { 17318c2ecf20Sopenharmony_ci cancel_delayed_work(&rdev->pm.dynpm_idle_work); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM; 17348c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM; 17358c2ecf20Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 17368c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 17488c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 17498c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc; 17508c2ecf20Sopenharmony_ci struct radeon_connector *radeon_connector; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (!rdev->pm.dpm_enabled) 17538c2ecf20Sopenharmony_ci return; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* update active crtc counts */ 17588c2ecf20Sopenharmony_ci rdev->pm.dpm.new_active_crtcs = 0; 17598c2ecf20Sopenharmony_ci rdev->pm.dpm.new_active_crtc_count = 0; 17608c2ecf20Sopenharmony_ci rdev->pm.dpm.high_pixelclock_count = 0; 17618c2ecf20Sopenharmony_ci if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 17628c2ecf20Sopenharmony_ci list_for_each_entry(crtc, 17638c2ecf20Sopenharmony_ci &ddev->mode_config.crtc_list, head) { 17648c2ecf20Sopenharmony_ci radeon_crtc = to_radeon_crtc(crtc); 17658c2ecf20Sopenharmony_ci if (crtc->enabled) { 17668c2ecf20Sopenharmony_ci rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id); 17678c2ecf20Sopenharmony_ci rdev->pm.dpm.new_active_crtc_count++; 17688c2ecf20Sopenharmony_ci if (!radeon_crtc->connector) 17698c2ecf20Sopenharmony_ci continue; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci radeon_connector = to_radeon_connector(radeon_crtc->connector); 17728c2ecf20Sopenharmony_ci if (radeon_connector->pixelclock_for_modeset > 297000) 17738c2ecf20Sopenharmony_ci rdev->pm.dpm.high_pixelclock_count++; 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* update battery/ac status */ 17798c2ecf20Sopenharmony_ci if (power_supply_is_system_supplied() > 0) 17808c2ecf20Sopenharmony_ci rdev->pm.dpm.ac_power = true; 17818c2ecf20Sopenharmony_ci else 17828c2ecf20Sopenharmony_ci rdev->pm.dpm.ac_power = false; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci radeon_dpm_change_power_state_locked(rdev); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_civoid radeon_pm_compute_clocks(struct radeon_device *rdev) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 17938c2ecf20Sopenharmony_ci radeon_pm_compute_clocks_dpm(rdev); 17948c2ecf20Sopenharmony_ci else 17958c2ecf20Sopenharmony_ci radeon_pm_compute_clocks_old(rdev); 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic bool radeon_pm_in_vbl(struct radeon_device *rdev) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci int crtc, vpos, hpos, vbl_status; 18018c2ecf20Sopenharmony_ci bool in_vbl = true; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci /* Iterate over all active crtc's. All crtc's must be in vblank, 18048c2ecf20Sopenharmony_ci * otherwise return in_vbl == false. 18058c2ecf20Sopenharmony_ci */ 18068c2ecf20Sopenharmony_ci for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { 18078c2ecf20Sopenharmony_ci if (rdev->pm.active_crtcs & (1 << crtc)) { 18088c2ecf20Sopenharmony_ci vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, 18098c2ecf20Sopenharmony_ci crtc, 18108c2ecf20Sopenharmony_ci USE_REAL_VBLANKSTART, 18118c2ecf20Sopenharmony_ci &vpos, &hpos, NULL, NULL, 18128c2ecf20Sopenharmony_ci &rdev->mode_info.crtcs[crtc]->base.hwmode); 18138c2ecf20Sopenharmony_ci if ((vbl_status & DRM_SCANOUTPOS_VALID) && 18148c2ecf20Sopenharmony_ci !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK)) 18158c2ecf20Sopenharmony_ci in_vbl = false; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci return in_vbl; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish) 18238c2ecf20Sopenharmony_ci{ 18248c2ecf20Sopenharmony_ci u32 stat_crtc = 0; 18258c2ecf20Sopenharmony_ci bool in_vbl = radeon_pm_in_vbl(rdev); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (!in_vbl) 18288c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc, 18298c2ecf20Sopenharmony_ci finish ? "exit" : "entry"); 18308c2ecf20Sopenharmony_ci return in_vbl; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic void radeon_dynpm_idle_work_handler(struct work_struct *work) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct radeon_device *rdev; 18368c2ecf20Sopenharmony_ci int resched; 18378c2ecf20Sopenharmony_ci rdev = container_of(work, struct radeon_device, 18388c2ecf20Sopenharmony_ci pm.dynpm_idle_work.work); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); 18418c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 18428c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 18438c2ecf20Sopenharmony_ci int not_processed = 0; 18448c2ecf20Sopenharmony_ci int i; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; ++i) { 18478c2ecf20Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (ring->ready) { 18508c2ecf20Sopenharmony_ci not_processed += radeon_fence_count_emitted(rdev, i); 18518c2ecf20Sopenharmony_ci if (not_processed >= 3) 18528c2ecf20Sopenharmony_ci break; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci if (not_processed >= 3) { /* should upclock */ 18578c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) { 18588c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 18598c2ecf20Sopenharmony_ci } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 18608c2ecf20Sopenharmony_ci rdev->pm.dynpm_can_upclock) { 18618c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = 18628c2ecf20Sopenharmony_ci DYNPM_ACTION_UPCLOCK; 18638c2ecf20Sopenharmony_ci rdev->pm.dynpm_action_timeout = jiffies + 18648c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci } else if (not_processed == 0) { /* should downclock */ 18678c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) { 18688c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 18698c2ecf20Sopenharmony_ci } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 18708c2ecf20Sopenharmony_ci rdev->pm.dynpm_can_downclock) { 18718c2ecf20Sopenharmony_ci rdev->pm.dynpm_planned_action = 18728c2ecf20Sopenharmony_ci DYNPM_ACTION_DOWNCLOCK; 18738c2ecf20Sopenharmony_ci rdev->pm.dynpm_action_timeout = jiffies + 18748c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci /* Note, radeon_pm_set_clocks is called with static_switch set 18798c2ecf20Sopenharmony_ci * to false since we want to wait for vbl to avoid flicker. 18808c2ecf20Sopenharmony_ci */ 18818c2ecf20Sopenharmony_ci if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE && 18828c2ecf20Sopenharmony_ci jiffies > rdev->pm.dynpm_action_timeout) { 18838c2ecf20Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 18848c2ecf20Sopenharmony_ci radeon_pm_set_clocks(rdev); 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 18888c2ecf20Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 18918c2ecf20Sopenharmony_ci ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci/* 18958c2ecf20Sopenharmony_ci * Debugfs info 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cistatic int radeon_debugfs_pm_info(struct seq_file *m, void *data) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *) m->private; 19028c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 19038c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 19048c2ecf20Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 19078c2ecf20Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 19088c2ecf20Sopenharmony_ci seq_printf(m, "PX asic powered off\n"); 19098c2ecf20Sopenharmony_ci } else if (rdev->pm.dpm_enabled) { 19108c2ecf20Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 19118c2ecf20Sopenharmony_ci if (rdev->asic->dpm.debugfs_print_current_performance_level) 19128c2ecf20Sopenharmony_ci radeon_dpm_debugfs_print_current_performance_level(rdev, m); 19138c2ecf20Sopenharmony_ci else 19148c2ecf20Sopenharmony_ci seq_printf(m, "Debugfs support not implemented for this asic\n"); 19158c2ecf20Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 19168c2ecf20Sopenharmony_ci } else { 19178c2ecf20Sopenharmony_ci seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); 19188c2ecf20Sopenharmony_ci /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ 19198c2ecf20Sopenharmony_ci if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) 19208c2ecf20Sopenharmony_ci seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); 19218c2ecf20Sopenharmony_ci else 19228c2ecf20Sopenharmony_ci seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); 19238c2ecf20Sopenharmony_ci seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); 19248c2ecf20Sopenharmony_ci if (rdev->asic->pm.get_memory_clock) 19258c2ecf20Sopenharmony_ci seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); 19268c2ecf20Sopenharmony_ci if (rdev->pm.current_vddc) 19278c2ecf20Sopenharmony_ci seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); 19288c2ecf20Sopenharmony_ci if (rdev->asic->pm.get_pcie_lanes) 19298c2ecf20Sopenharmony_ci seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci return 0; 19338c2ecf20Sopenharmony_ci} 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_cistatic struct drm_info_list radeon_pm_info_list[] = { 19368c2ecf20Sopenharmony_ci {"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL}, 19378c2ecf20Sopenharmony_ci}; 19388c2ecf20Sopenharmony_ci#endif 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_cistatic int radeon_debugfs_pm_init(struct radeon_device *rdev) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 19438c2ecf20Sopenharmony_ci return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); 19448c2ecf20Sopenharmony_ci#else 19458c2ecf20Sopenharmony_ci return 0; 19468c2ecf20Sopenharmony_ci#endif 19478c2ecf20Sopenharmony_ci} 1948