162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 362306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 462306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 562306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 662306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 762306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1062306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1362306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1462306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1562306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1662306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1762306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1862306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Authors: Rafał Miłecki <zajec5@gmail.com> 2162306a36Sopenharmony_ci * Alex Deucher <alexdeucher@gmail.com> 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 2562306a36Sopenharmony_ci#include <linux/hwmon.h> 2662306a36Sopenharmony_ci#include <linux/pci.h> 2762306a36Sopenharmony_ci#include <linux/power_supply.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "atom.h" 3262306a36Sopenharmony_ci#include "avivod.h" 3362306a36Sopenharmony_ci#include "r600_dpm.h" 3462306a36Sopenharmony_ci#include "radeon.h" 3562306a36Sopenharmony_ci#include "radeon_pm.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define RADEON_IDLE_LOOP_MS 100 3862306a36Sopenharmony_ci#define RADEON_RECLOCK_DELAY_MS 200 3962306a36Sopenharmony_ci#define RADEON_WAIT_VBLANK_TIMEOUT 200 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const char *radeon_pm_state_type_name[5] = { 4262306a36Sopenharmony_ci "", 4362306a36Sopenharmony_ci "Powersave", 4462306a36Sopenharmony_ci "Battery", 4562306a36Sopenharmony_ci "Balanced", 4662306a36Sopenharmony_ci "Performance", 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void radeon_dynpm_idle_work_handler(struct work_struct *work); 5062306a36Sopenharmony_cistatic void radeon_debugfs_pm_init(struct radeon_device *rdev); 5162306a36Sopenharmony_cistatic bool radeon_pm_in_vbl(struct radeon_device *rdev); 5262306a36Sopenharmony_cistatic bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish); 5362306a36Sopenharmony_cistatic void radeon_pm_update_profile(struct radeon_device *rdev); 5462306a36Sopenharmony_cistatic void radeon_pm_set_clocks(struct radeon_device *rdev); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciint radeon_pm_get_type_index(struct radeon_device *rdev, 5762306a36Sopenharmony_ci enum radeon_pm_state_type ps_type, 5862306a36Sopenharmony_ci int instance) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci int i; 6162306a36Sopenharmony_ci int found_instance = -1; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci for (i = 0; i < rdev->pm.num_power_states; i++) { 6462306a36Sopenharmony_ci if (rdev->pm.power_state[i].type == ps_type) { 6562306a36Sopenharmony_ci found_instance++; 6662306a36Sopenharmony_ci if (found_instance == instance) 6762306a36Sopenharmony_ci return i; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci /* return default if no match */ 7162306a36Sopenharmony_ci return rdev->pm.default_power_state_index; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_civoid radeon_pm_acpi_event_handler(struct radeon_device *rdev) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 7762306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 7862306a36Sopenharmony_ci if (power_supply_is_system_supplied() > 0) 7962306a36Sopenharmony_ci rdev->pm.dpm.ac_power = true; 8062306a36Sopenharmony_ci else 8162306a36Sopenharmony_ci rdev->pm.dpm.ac_power = false; 8262306a36Sopenharmony_ci if (rdev->family == CHIP_ARUBA) { 8362306a36Sopenharmony_ci if (rdev->asic->dpm.enable_bapm) 8462306a36Sopenharmony_ci radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 8762306a36Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 8862306a36Sopenharmony_ci if (rdev->pm.profile == PM_PROFILE_AUTO) { 8962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 9062306a36Sopenharmony_ci radeon_pm_update_profile(rdev); 9162306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 9262306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void radeon_pm_update_profile(struct radeon_device *rdev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci switch (rdev->pm.profile) { 10062306a36Sopenharmony_ci case PM_PROFILE_DEFAULT: 10162306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case PM_PROFILE_AUTO: 10462306a36Sopenharmony_ci if (power_supply_is_system_supplied() > 0) { 10562306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 10662306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 11162306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case PM_PROFILE_LOW: 11762306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 11862306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case PM_PROFILE_MID: 12362306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 12462306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case PM_PROFILE_HIGH: 12962306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) 13062306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (rdev->pm.active_crtc_count == 0) { 13762306a36Sopenharmony_ci rdev->pm.requested_power_state_index = 13862306a36Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx; 13962306a36Sopenharmony_ci rdev->pm.requested_clock_mode_index = 14062306a36Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx; 14162306a36Sopenharmony_ci } else { 14262306a36Sopenharmony_ci rdev->pm.requested_power_state_index = 14362306a36Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx; 14462306a36Sopenharmony_ci rdev->pm.requested_clock_mode_index = 14562306a36Sopenharmony_ci rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void radeon_unmap_vram_bos(struct radeon_device *rdev) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct radeon_bo *bo, *n; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (list_empty(&rdev->gem.objects)) 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) { 15762306a36Sopenharmony_ci if (bo->tbo.resource->mem_type == TTM_PL_VRAM) 15862306a36Sopenharmony_ci ttm_bo_unmap_virtual(&bo->tbo); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void radeon_sync_with_vblank(struct radeon_device *rdev) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci if (rdev->pm.active_crtcs) { 16562306a36Sopenharmony_ci rdev->pm.vblank_sync = false; 16662306a36Sopenharmony_ci wait_event_timeout( 16762306a36Sopenharmony_ci rdev->irq.vblank_queue, rdev->pm.vblank_sync, 16862306a36Sopenharmony_ci msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT)); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void radeon_set_power_state(struct radeon_device *rdev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u32 sclk, mclk; 17562306a36Sopenharmony_ci bool misc_after = false; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 17862306a36Sopenharmony_ci (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (radeon_gui_idle(rdev)) { 18262306a36Sopenharmony_ci sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 18362306a36Sopenharmony_ci clock_info[rdev->pm.requested_clock_mode_index].sclk; 18462306a36Sopenharmony_ci if (sclk > rdev->pm.default_sclk) 18562306a36Sopenharmony_ci sclk = rdev->pm.default_sclk; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* starting with BTC, there is one state that is used for both 18862306a36Sopenharmony_ci * MH and SH. Difference is that we always use the high clock index for 18962306a36Sopenharmony_ci * mclk and vddci. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_PROFILE) && 19262306a36Sopenharmony_ci (rdev->family >= CHIP_BARTS) && 19362306a36Sopenharmony_ci rdev->pm.active_crtc_count && 19462306a36Sopenharmony_ci ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) || 19562306a36Sopenharmony_ci (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX))) 19662306a36Sopenharmony_ci mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 19762306a36Sopenharmony_ci clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk; 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. 20062306a36Sopenharmony_ci clock_info[rdev->pm.requested_clock_mode_index].mclk; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (mclk > rdev->pm.default_mclk) 20362306a36Sopenharmony_ci mclk = rdev->pm.default_mclk; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* upvolt before raising clocks, downvolt after lowering clocks */ 20662306a36Sopenharmony_ci if (sclk < rdev->pm.current_sclk) 20762306a36Sopenharmony_ci misc_after = true; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci radeon_sync_with_vblank(rdev); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 21262306a36Sopenharmony_ci if (!radeon_pm_in_vbl(rdev)) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci radeon_pm_prepare(rdev); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!misc_after) 21962306a36Sopenharmony_ci /* voltage, pcie lanes, etc.*/ 22062306a36Sopenharmony_ci radeon_pm_misc(rdev); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* set engine clock */ 22362306a36Sopenharmony_ci if (sclk != rdev->pm.current_sclk) { 22462306a36Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, false); 22562306a36Sopenharmony_ci radeon_set_engine_clock(rdev, sclk); 22662306a36Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, true); 22762306a36Sopenharmony_ci rdev->pm.current_sclk = sclk; 22862306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* set memory clock */ 23262306a36Sopenharmony_ci if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) { 23362306a36Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, false); 23462306a36Sopenharmony_ci radeon_set_memory_clock(rdev, mclk); 23562306a36Sopenharmony_ci radeon_pm_debug_check_in_vbl(rdev, true); 23662306a36Sopenharmony_ci rdev->pm.current_mclk = mclk; 23762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (misc_after) 24162306a36Sopenharmony_ci /* voltage, pcie lanes, etc.*/ 24262306a36Sopenharmony_ci radeon_pm_misc(rdev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci radeon_pm_finish(rdev); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; 24762306a36Sopenharmony_ci rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; 24862306a36Sopenharmony_ci } else 24962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n"); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void radeon_pm_set_clocks(struct radeon_device *rdev) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct drm_crtc *crtc; 25562306a36Sopenharmony_ci int i, r; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* no need to take locks, etc. if nothing's going to change */ 25862306a36Sopenharmony_ci if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && 25962306a36Sopenharmony_ci (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci down_write(&rdev->pm.mclk_lock); 26362306a36Sopenharmony_ci mutex_lock(&rdev->ring_lock); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* wait for the rings to drain */ 26662306a36Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) { 26762306a36Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 26862306a36Sopenharmony_ci if (!ring->ready) { 26962306a36Sopenharmony_ci continue; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci r = radeon_fence_wait_empty(rdev, i); 27262306a36Sopenharmony_ci if (r) { 27362306a36Sopenharmony_ci /* needs a GPU reset dont reset here */ 27462306a36Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 27562306a36Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci radeon_unmap_vram_bos(rdev); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (rdev->irq.installed) { 28362306a36Sopenharmony_ci i = 0; 28462306a36Sopenharmony_ci drm_for_each_crtc(crtc, rdev->ddev) { 28562306a36Sopenharmony_ci if (rdev->pm.active_crtcs & (1 << i)) { 28662306a36Sopenharmony_ci /* This can fail if a modeset is in progress */ 28762306a36Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 28862306a36Sopenharmony_ci rdev->pm.req_vblank |= (1 << i); 28962306a36Sopenharmony_ci else 29062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("crtc %d no vblank, can glitch\n", 29162306a36Sopenharmony_ci i); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci i++; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci radeon_set_power_state(rdev); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (rdev->irq.installed) { 30062306a36Sopenharmony_ci i = 0; 30162306a36Sopenharmony_ci drm_for_each_crtc(crtc, rdev->ddev) { 30262306a36Sopenharmony_ci if (rdev->pm.req_vblank & (1 << i)) { 30362306a36Sopenharmony_ci rdev->pm.req_vblank &= ~(1 << i); 30462306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci i++; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* update display watermarks based on new power state */ 31162306a36Sopenharmony_ci radeon_update_bandwidth_info(rdev); 31262306a36Sopenharmony_ci if (rdev->pm.active_crtc_count) 31362306a36Sopenharmony_ci radeon_bandwidth_update(rdev); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 31862306a36Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void radeon_pm_print_states(struct radeon_device *rdev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i, j; 32462306a36Sopenharmony_ci struct radeon_power_state *power_state; 32562306a36Sopenharmony_ci struct radeon_pm_clock_info *clock_info; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states); 32862306a36Sopenharmony_ci for (i = 0; i < rdev->pm.num_power_states; i++) { 32962306a36Sopenharmony_ci power_state = &rdev->pm.power_state[i]; 33062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("State %d: %s\n", i, 33162306a36Sopenharmony_ci radeon_pm_state_type_name[power_state->type]); 33262306a36Sopenharmony_ci if (i == rdev->pm.default_power_state_index) 33362306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\tDefault"); 33462306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP)) 33562306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes); 33662306a36Sopenharmony_ci if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) 33762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\tSingle display only\n"); 33862306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes); 33962306a36Sopenharmony_ci for (j = 0; j < power_state->num_clock_modes; j++) { 34062306a36Sopenharmony_ci clock_info = &(power_state->clock_info[j]); 34162306a36Sopenharmony_ci if (rdev->flags & RADEON_IS_IGP) 34262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\t\t%d e: %d\n", 34362306a36Sopenharmony_ci j, 34462306a36Sopenharmony_ci clock_info->sclk * 10); 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n", 34762306a36Sopenharmony_ci j, 34862306a36Sopenharmony_ci clock_info->sclk * 10, 34962306a36Sopenharmony_ci clock_info->mclk * 10, 35062306a36Sopenharmony_ci clock_info->voltage.voltage); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic ssize_t radeon_get_pm_profile(struct device *dev, 35662306a36Sopenharmony_ci struct device_attribute *attr, 35762306a36Sopenharmony_ci char *buf) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 36062306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 36162306a36Sopenharmony_ci int cp = rdev->pm.profile; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (cp == PM_PROFILE_AUTO) ? "auto" : 36462306a36Sopenharmony_ci (cp == PM_PROFILE_LOW) ? "low" : 36562306a36Sopenharmony_ci (cp == PM_PROFILE_MID) ? "mid" : 36662306a36Sopenharmony_ci (cp == PM_PROFILE_HIGH) ? "high" : "default"); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic ssize_t radeon_set_pm_profile(struct device *dev, 37062306a36Sopenharmony_ci struct device_attribute *attr, 37162306a36Sopenharmony_ci const char *buf, 37262306a36Sopenharmony_ci size_t count) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 37562306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Can't set profile when the card is off */ 37862306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 37962306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 38362306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 38462306a36Sopenharmony_ci if (strncmp("default", buf, strlen("default")) == 0) 38562306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 38662306a36Sopenharmony_ci else if (strncmp("auto", buf, strlen("auto")) == 0) 38762306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_AUTO; 38862306a36Sopenharmony_ci else if (strncmp("low", buf, strlen("low")) == 0) 38962306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_LOW; 39062306a36Sopenharmony_ci else if (strncmp("mid", buf, strlen("mid")) == 0) 39162306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_MID; 39262306a36Sopenharmony_ci else if (strncmp("high", buf, strlen("high")) == 0) 39362306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_HIGH; 39462306a36Sopenharmony_ci else { 39562306a36Sopenharmony_ci count = -EINVAL; 39662306a36Sopenharmony_ci goto fail; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci radeon_pm_update_profile(rdev); 39962306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 40062306a36Sopenharmony_ci } else 40162306a36Sopenharmony_ci count = -EINVAL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cifail: 40462306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return count; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic ssize_t radeon_get_pm_method(struct device *dev, 41062306a36Sopenharmony_ci struct device_attribute *attr, 41162306a36Sopenharmony_ci char *buf) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 41462306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 41562306a36Sopenharmony_ci int pm = rdev->pm.pm_method; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (pm == PM_METHOD_DYNPM) ? "dynpm" : 41862306a36Sopenharmony_ci (pm == PM_METHOD_PROFILE) ? "profile" : "dpm"); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic ssize_t radeon_set_pm_method(struct device *dev, 42262306a36Sopenharmony_ci struct device_attribute *attr, 42362306a36Sopenharmony_ci const char *buf, 42462306a36Sopenharmony_ci size_t count) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 42762306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Can't set method when the card is off */ 43062306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 43162306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 43262306a36Sopenharmony_ci count = -EINVAL; 43362306a36Sopenharmony_ci goto fail; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* we don't support the legacy modes with dpm */ 43762306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) { 43862306a36Sopenharmony_ci count = -EINVAL; 43962306a36Sopenharmony_ci goto fail; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { 44362306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 44462306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DYNPM; 44562306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 44662306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 44762306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 44862306a36Sopenharmony_ci } else if (strncmp("profile", buf, strlen("profile")) == 0) { 44962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 45062306a36Sopenharmony_ci /* disable dynpm */ 45162306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 45262306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 45362306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 45462306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 45562306a36Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci count = -EINVAL; 45862306a36Sopenharmony_ci goto fail; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 46162306a36Sopenharmony_cifail: 46262306a36Sopenharmony_ci return count; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic ssize_t radeon_get_dpm_state(struct device *dev, 46662306a36Sopenharmony_ci struct device_attribute *attr, 46762306a36Sopenharmony_ci char *buf) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 47062306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 47162306a36Sopenharmony_ci enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", 47462306a36Sopenharmony_ci (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : 47562306a36Sopenharmony_ci (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic ssize_t radeon_set_dpm_state(struct device *dev, 47962306a36Sopenharmony_ci struct device_attribute *attr, 48062306a36Sopenharmony_ci const char *buf, 48162306a36Sopenharmony_ci size_t count) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 48462306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 48762306a36Sopenharmony_ci if (strncmp("battery", buf, strlen("battery")) == 0) 48862306a36Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; 48962306a36Sopenharmony_ci else if (strncmp("balanced", buf, strlen("balanced")) == 0) 49062306a36Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 49162306a36Sopenharmony_ci else if (strncmp("performance", buf, strlen("performance")) == 0) 49262306a36Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; 49362306a36Sopenharmony_ci else { 49462306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 49562306a36Sopenharmony_ci count = -EINVAL; 49662306a36Sopenharmony_ci goto fail; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Can't set dpm state when the card is off */ 50162306a36Sopenharmony_ci if (!(rdev->flags & RADEON_IS_PX) || 50262306a36Sopenharmony_ci (ddev->switch_power_state == DRM_SWITCH_POWER_ON)) 50362306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cifail: 50662306a36Sopenharmony_ci return count; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, 51062306a36Sopenharmony_ci struct device_attribute *attr, 51162306a36Sopenharmony_ci char *buf) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 51462306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 51562306a36Sopenharmony_ci enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 51862306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 51962306a36Sopenharmony_ci return sysfs_emit(buf, "off\n"); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", 52262306a36Sopenharmony_ci (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : 52362306a36Sopenharmony_ci (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, 52762306a36Sopenharmony_ci struct device_attribute *attr, 52862306a36Sopenharmony_ci const char *buf, 52962306a36Sopenharmony_ci size_t count) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct drm_device *ddev = dev_get_drvdata(dev); 53262306a36Sopenharmony_ci struct radeon_device *rdev = ddev->dev_private; 53362306a36Sopenharmony_ci enum radeon_dpm_forced_level level; 53462306a36Sopenharmony_ci int ret = 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Can't force performance level when the card is off */ 53762306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 53862306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 54262306a36Sopenharmony_ci if (strncmp("low", buf, strlen("low")) == 0) { 54362306a36Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_LOW; 54462306a36Sopenharmony_ci } else if (strncmp("high", buf, strlen("high")) == 0) { 54562306a36Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_HIGH; 54662306a36Sopenharmony_ci } else if (strncmp("auto", buf, strlen("auto")) == 0) { 54762306a36Sopenharmony_ci level = RADEON_DPM_FORCED_LEVEL_AUTO; 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci count = -EINVAL; 55062306a36Sopenharmony_ci goto fail; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (rdev->asic->dpm.force_performance_level) { 55362306a36Sopenharmony_ci if (rdev->pm.dpm.thermal_active) { 55462306a36Sopenharmony_ci count = -EINVAL; 55562306a36Sopenharmony_ci goto fail; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci ret = radeon_dpm_force_performance_level(rdev, level); 55862306a36Sopenharmony_ci if (ret) 55962306a36Sopenharmony_ci count = -EINVAL; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_cifail: 56262306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return count; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev, 56862306a36Sopenharmony_ci struct device_attribute *attr, 56962306a36Sopenharmony_ci char *buf) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 57262306a36Sopenharmony_ci u32 pwm_mode = 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (rdev->asic->dpm.fan_ctrl_get_mode) 57562306a36Sopenharmony_ci pwm_mode = rdev->asic->dpm.fan_ctrl_get_mode(rdev); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* never 0 (full-speed), fuse or smc-controlled always */ 57862306a36Sopenharmony_ci return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev, 58262306a36Sopenharmony_ci struct device_attribute *attr, 58362306a36Sopenharmony_ci const char *buf, 58462306a36Sopenharmony_ci size_t count) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 58762306a36Sopenharmony_ci int err; 58862306a36Sopenharmony_ci int value; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if(!rdev->asic->dpm.fan_ctrl_set_mode) 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci err = kstrtoint(buf, 10, &value); 59462306a36Sopenharmony_ci if (err) 59562306a36Sopenharmony_ci return err; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (value) { 59862306a36Sopenharmony_ci case 1: /* manual, percent-based */ 59962306a36Sopenharmony_ci rdev->asic->dpm.fan_ctrl_set_mode(rdev, FDO_PWM_MODE_STATIC); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: /* disable */ 60262306a36Sopenharmony_ci rdev->asic->dpm.fan_ctrl_set_mode(rdev, 0); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return count; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_min(struct device *dev, 61062306a36Sopenharmony_ci struct device_attribute *attr, 61162306a36Sopenharmony_ci char *buf) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci return sprintf(buf, "%i\n", 0); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1_max(struct device *dev, 61762306a36Sopenharmony_ci struct device_attribute *attr, 61862306a36Sopenharmony_ci char *buf) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci return sprintf(buf, "%i\n", 255); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic ssize_t radeon_hwmon_set_pwm1(struct device *dev, 62462306a36Sopenharmony_ci struct device_attribute *attr, 62562306a36Sopenharmony_ci const char *buf, size_t count) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 62862306a36Sopenharmony_ci int err; 62962306a36Sopenharmony_ci u32 value; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci err = kstrtou32(buf, 10, &value); 63262306a36Sopenharmony_ci if (err) 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci value = (value * 100) / 255; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci err = rdev->asic->dpm.set_fan_speed_percent(rdev, value); 63862306a36Sopenharmony_ci if (err) 63962306a36Sopenharmony_ci return err; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return count; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic ssize_t radeon_hwmon_get_pwm1(struct device *dev, 64562306a36Sopenharmony_ci struct device_attribute *attr, 64662306a36Sopenharmony_ci char *buf) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 64962306a36Sopenharmony_ci int err; 65062306a36Sopenharmony_ci u32 speed; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci err = rdev->asic->dpm.get_fan_speed_percent(rdev, &speed); 65362306a36Sopenharmony_ci if (err) 65462306a36Sopenharmony_ci return err; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci speed = (speed * 255) / 100; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return sprintf(buf, "%i\n", speed); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); 66262306a36Sopenharmony_cistatic DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); 66362306a36Sopenharmony_cistatic DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state); 66462306a36Sopenharmony_cistatic DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, 66562306a36Sopenharmony_ci radeon_get_dpm_forced_performance_level, 66662306a36Sopenharmony_ci radeon_set_dpm_forced_performance_level); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic ssize_t radeon_hwmon_show_temp(struct device *dev, 66962306a36Sopenharmony_ci struct device_attribute *attr, 67062306a36Sopenharmony_ci char *buf) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 67362306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 67462306a36Sopenharmony_ci int temp; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Can't get temperature when the card is off */ 67762306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 67862306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (rdev->asic->pm.get_temperature) 68262306a36Sopenharmony_ci temp = radeon_get_temperature(rdev); 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci temp = 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", temp); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, 69062306a36Sopenharmony_ci struct device_attribute *attr, 69162306a36Sopenharmony_ci char *buf) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 69462306a36Sopenharmony_ci int hyst = to_sensor_dev_attr(attr)->index; 69562306a36Sopenharmony_ci int temp; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (hyst) 69862306a36Sopenharmony_ci temp = rdev->pm.dpm.thermal.min_temp; 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci temp = rdev->pm.dpm.thermal.max_temp; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", temp); 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); 70662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0); 70762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1); 70862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0); 70962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0); 71062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0); 71162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic ssize_t radeon_hwmon_show_sclk(struct device *dev, 71462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 71762306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 71862306a36Sopenharmony_ci u32 sclk = 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Can't get clock frequency when the card is off */ 72162306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 72262306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 72362306a36Sopenharmony_ci return -EINVAL; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (rdev->asic->dpm.get_current_sclk) 72662306a36Sopenharmony_ci sclk = radeon_dpm_get_current_sclk(rdev); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Value returned by dpm is in 10 KHz units, need to convert it into Hz 72962306a36Sopenharmony_ci for hwmon */ 73062306a36Sopenharmony_ci sclk *= 10000; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", sclk); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(freq1_input, S_IRUGO, radeon_hwmon_show_sclk, NULL, 73662306a36Sopenharmony_ci 0); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic ssize_t radeon_hwmon_show_vddc(struct device *dev, 73962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 74262306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 74362306a36Sopenharmony_ci u16 vddc = 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Can't get vddc when the card is off */ 74662306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 74762306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) 74862306a36Sopenharmony_ci return -EINVAL; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (rdev->asic->dpm.get_current_vddc) 75162306a36Sopenharmony_ci vddc = rdev->asic->dpm.get_current_vddc(rdev); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", vddc); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, radeon_hwmon_show_vddc, NULL, 75762306a36Sopenharmony_ci 0); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic struct attribute *hwmon_attributes[] = { 76062306a36Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 76162306a36Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 76262306a36Sopenharmony_ci &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, 76362306a36Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 76462306a36Sopenharmony_ci &sensor_dev_attr_pwm1_enable.dev_attr.attr, 76562306a36Sopenharmony_ci &sensor_dev_attr_pwm1_min.dev_attr.attr, 76662306a36Sopenharmony_ci &sensor_dev_attr_pwm1_max.dev_attr.attr, 76762306a36Sopenharmony_ci &sensor_dev_attr_freq1_input.dev_attr.attr, 76862306a36Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 76962306a36Sopenharmony_ci NULL 77062306a36Sopenharmony_ci}; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic umode_t hwmon_attributes_visible(struct kobject *kobj, 77362306a36Sopenharmony_ci struct attribute *attr, int index) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 77662306a36Sopenharmony_ci struct radeon_device *rdev = dev_get_drvdata(dev); 77762306a36Sopenharmony_ci umode_t effective_mode = attr->mode; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Skip attributes if DPM is not enabled */ 78062306a36Sopenharmony_ci if (rdev->pm.pm_method != PM_METHOD_DPM && 78162306a36Sopenharmony_ci (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || 78262306a36Sopenharmony_ci attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr || 78362306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr || 78462306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || 78562306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 78662306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr || 78762306a36Sopenharmony_ci attr == &sensor_dev_attr_freq1_input.dev_attr.attr || 78862306a36Sopenharmony_ci attr == &sensor_dev_attr_in0_input.dev_attr.attr)) 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Skip vddc attribute if get_current_vddc is not implemented */ 79262306a36Sopenharmony_ci if(attr == &sensor_dev_attr_in0_input.dev_attr.attr && 79362306a36Sopenharmony_ci !rdev->asic->dpm.get_current_vddc) 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Skip fan attributes if fan is not present */ 79762306a36Sopenharmony_ci if (rdev->pm.no_fan && 79862306a36Sopenharmony_ci (attr == &sensor_dev_attr_pwm1.dev_attr.attr || 79962306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || 80062306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 80162306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* mask fan attributes if we have no bindings for this asic to expose */ 80562306a36Sopenharmony_ci if ((!rdev->asic->dpm.get_fan_speed_percent && 80662306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */ 80762306a36Sopenharmony_ci (!rdev->asic->dpm.fan_ctrl_get_mode && 80862306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */ 80962306a36Sopenharmony_ci effective_mode &= ~S_IRUGO; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if ((!rdev->asic->dpm.set_fan_speed_percent && 81262306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */ 81362306a36Sopenharmony_ci (!rdev->asic->dpm.fan_ctrl_set_mode && 81462306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */ 81562306a36Sopenharmony_ci effective_mode &= ~S_IWUSR; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* hide max/min values if we can't both query and manage the fan */ 81862306a36Sopenharmony_ci if ((!rdev->asic->dpm.set_fan_speed_percent && 81962306a36Sopenharmony_ci !rdev->asic->dpm.get_fan_speed_percent) && 82062306a36Sopenharmony_ci (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || 82162306a36Sopenharmony_ci attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return effective_mode; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic const struct attribute_group hwmon_attrgroup = { 82862306a36Sopenharmony_ci .attrs = hwmon_attributes, 82962306a36Sopenharmony_ci .is_visible = hwmon_attributes_visible, 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic const struct attribute_group *hwmon_groups[] = { 83362306a36Sopenharmony_ci &hwmon_attrgroup, 83462306a36Sopenharmony_ci NULL 83562306a36Sopenharmony_ci}; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int radeon_hwmon_init(struct radeon_device *rdev) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci int err = 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci switch (rdev->pm.int_thermal_type) { 84262306a36Sopenharmony_ci case THERMAL_TYPE_RV6XX: 84362306a36Sopenharmony_ci case THERMAL_TYPE_RV770: 84462306a36Sopenharmony_ci case THERMAL_TYPE_EVERGREEN: 84562306a36Sopenharmony_ci case THERMAL_TYPE_NI: 84662306a36Sopenharmony_ci case THERMAL_TYPE_SUMO: 84762306a36Sopenharmony_ci case THERMAL_TYPE_SI: 84862306a36Sopenharmony_ci case THERMAL_TYPE_CI: 84962306a36Sopenharmony_ci case THERMAL_TYPE_KV: 85062306a36Sopenharmony_ci if (rdev->asic->pm.get_temperature == NULL) 85162306a36Sopenharmony_ci return err; 85262306a36Sopenharmony_ci rdev->pm.int_hwmon_dev = hwmon_device_register_with_groups(rdev->dev, 85362306a36Sopenharmony_ci "radeon", rdev, 85462306a36Sopenharmony_ci hwmon_groups); 85562306a36Sopenharmony_ci if (IS_ERR(rdev->pm.int_hwmon_dev)) { 85662306a36Sopenharmony_ci err = PTR_ERR(rdev->pm.int_hwmon_dev); 85762306a36Sopenharmony_ci dev_err(rdev->dev, 85862306a36Sopenharmony_ci "Unable to register hwmon device: %d\n", err); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci default: 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return err; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic void radeon_hwmon_fini(struct radeon_device *rdev) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci if (rdev->pm.int_hwmon_dev) 87162306a36Sopenharmony_ci hwmon_device_unregister(rdev->pm.int_hwmon_dev); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void radeon_dpm_thermal_work_handler(struct work_struct *work) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct radeon_device *rdev = 87762306a36Sopenharmony_ci container_of(work, struct radeon_device, 87862306a36Sopenharmony_ci pm.dpm.thermal.work); 87962306a36Sopenharmony_ci /* switch to the thermal state */ 88062306a36Sopenharmony_ci enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (!rdev->pm.dpm_enabled) 88362306a36Sopenharmony_ci return; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (rdev->asic->pm.get_temperature) { 88662306a36Sopenharmony_ci int temp = radeon_get_temperature(rdev); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (temp < rdev->pm.dpm.thermal.min_temp) 88962306a36Sopenharmony_ci /* switch back the user state */ 89062306a36Sopenharmony_ci dpm_state = rdev->pm.dpm.user_state; 89162306a36Sopenharmony_ci } else { 89262306a36Sopenharmony_ci if (rdev->pm.dpm.thermal.high_to_low) 89362306a36Sopenharmony_ci /* switch back the user state */ 89462306a36Sopenharmony_ci dpm_state = rdev->pm.dpm.user_state; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 89762306a36Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) 89862306a36Sopenharmony_ci rdev->pm.dpm.thermal_active = true; 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci rdev->pm.dpm.thermal_active = false; 90162306a36Sopenharmony_ci rdev->pm.dpm.state = dpm_state; 90262306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic bool radeon_dpm_single_display(struct radeon_device *rdev) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ? 91062306a36Sopenharmony_ci true : false; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* check if the vblank period is too short to adjust the mclk */ 91362306a36Sopenharmony_ci if (single_display && rdev->asic->dpm.vblank_too_short) { 91462306a36Sopenharmony_ci if (radeon_dpm_vblank_too_short(rdev)) 91562306a36Sopenharmony_ci single_display = false; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* 120hz tends to be problematic even if they are under the 91962306a36Sopenharmony_ci * vblank limit. 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ci if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120)) 92262306a36Sopenharmony_ci single_display = false; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return single_display; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, 92862306a36Sopenharmony_ci enum radeon_pm_state_type dpm_state) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci int i; 93162306a36Sopenharmony_ci struct radeon_ps *ps; 93262306a36Sopenharmony_ci u32 ui_class; 93362306a36Sopenharmony_ci bool single_display = radeon_dpm_single_display(rdev); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* certain older asics have a separare 3D performance state, 93662306a36Sopenharmony_ci * so try that first if the user selected performance 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) 93962306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; 94062306a36Sopenharmony_ci /* balanced states don't exist at the moment */ 94162306a36Sopenharmony_ci if (dpm_state == POWER_STATE_TYPE_BALANCED) 94262306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cirestart_search: 94562306a36Sopenharmony_ci /* Pick the best power state based on current conditions */ 94662306a36Sopenharmony_ci for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 94762306a36Sopenharmony_ci ps = &rdev->pm.dpm.ps[i]; 94862306a36Sopenharmony_ci ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; 94962306a36Sopenharmony_ci switch (dpm_state) { 95062306a36Sopenharmony_ci /* user states */ 95162306a36Sopenharmony_ci case POWER_STATE_TYPE_BATTERY: 95262306a36Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { 95362306a36Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 95462306a36Sopenharmony_ci if (single_display) 95562306a36Sopenharmony_ci return ps; 95662306a36Sopenharmony_ci } else 95762306a36Sopenharmony_ci return ps; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci case POWER_STATE_TYPE_BALANCED: 96162306a36Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { 96262306a36Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 96362306a36Sopenharmony_ci if (single_display) 96462306a36Sopenharmony_ci return ps; 96562306a36Sopenharmony_ci } else 96662306a36Sopenharmony_ci return ps; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci case POWER_STATE_TYPE_PERFORMANCE: 97062306a36Sopenharmony_ci if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { 97162306a36Sopenharmony_ci if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { 97262306a36Sopenharmony_ci if (single_display) 97362306a36Sopenharmony_ci return ps; 97462306a36Sopenharmony_ci } else 97562306a36Sopenharmony_ci return ps; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci /* internal states */ 97962306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD: 98062306a36Sopenharmony_ci if (rdev->pm.dpm.uvd_ps) 98162306a36Sopenharmony_ci return rdev->pm.dpm.uvd_ps; 98262306a36Sopenharmony_ci else 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_SD: 98562306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) 98662306a36Sopenharmony_ci return ps; 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD: 98962306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) 99062306a36Sopenharmony_ci return ps; 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 99362306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) 99462306a36Sopenharmony_ci return ps; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 99762306a36Sopenharmony_ci if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) 99862306a36Sopenharmony_ci return ps; 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_BOOT: 100162306a36Sopenharmony_ci return rdev->pm.dpm.boot_ps; 100262306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_THERMAL: 100362306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) 100462306a36Sopenharmony_ci return ps; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ACPI: 100762306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) 100862306a36Sopenharmony_ci return ps; 100962306a36Sopenharmony_ci break; 101062306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ULV: 101162306a36Sopenharmony_ci if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) 101262306a36Sopenharmony_ci return ps; 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_3DPERF: 101562306a36Sopenharmony_ci if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) 101662306a36Sopenharmony_ci return ps; 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci default: 101962306a36Sopenharmony_ci break; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci /* use a fallback state if we didn't match */ 102362306a36Sopenharmony_ci switch (dpm_state) { 102462306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_SD: 102562306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 102662306a36Sopenharmony_ci goto restart_search; 102762306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD: 102862306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_HD2: 102962306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_UVD_MVC: 103062306a36Sopenharmony_ci if (rdev->pm.dpm.uvd_ps) { 103162306a36Sopenharmony_ci return rdev->pm.dpm.uvd_ps; 103262306a36Sopenharmony_ci } else { 103362306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 103462306a36Sopenharmony_ci goto restart_search; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_THERMAL: 103762306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; 103862306a36Sopenharmony_ci goto restart_search; 103962306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_ACPI: 104062306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_BATTERY; 104162306a36Sopenharmony_ci goto restart_search; 104262306a36Sopenharmony_ci case POWER_STATE_TYPE_BATTERY: 104362306a36Sopenharmony_ci case POWER_STATE_TYPE_BALANCED: 104462306a36Sopenharmony_ci case POWER_STATE_TYPE_INTERNAL_3DPERF: 104562306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_PERFORMANCE; 104662306a36Sopenharmony_ci goto restart_search; 104762306a36Sopenharmony_ci default: 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return NULL; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int i; 105762306a36Sopenharmony_ci struct radeon_ps *ps; 105862306a36Sopenharmony_ci enum radeon_pm_state_type dpm_state; 105962306a36Sopenharmony_ci int ret; 106062306a36Sopenharmony_ci bool single_display = radeon_dpm_single_display(rdev); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* if dpm init failed */ 106362306a36Sopenharmony_ci if (!rdev->pm.dpm_enabled) 106462306a36Sopenharmony_ci return; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) { 106762306a36Sopenharmony_ci /* add other state override checks here */ 106862306a36Sopenharmony_ci if ((!rdev->pm.dpm.thermal_active) && 106962306a36Sopenharmony_ci (!rdev->pm.dpm.uvd_active)) 107062306a36Sopenharmony_ci rdev->pm.dpm.state = rdev->pm.dpm.user_state; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci dpm_state = rdev->pm.dpm.state; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci ps = radeon_dpm_pick_power_state(rdev, dpm_state); 107562306a36Sopenharmony_ci if (ps) 107662306a36Sopenharmony_ci rdev->pm.dpm.requested_ps = ps; 107762306a36Sopenharmony_ci else 107862306a36Sopenharmony_ci return; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* no need to reprogram if nothing changed unless we are on BTC+ */ 108162306a36Sopenharmony_ci if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) { 108262306a36Sopenharmony_ci /* vce just modifies an existing state so force a change */ 108362306a36Sopenharmony_ci if (ps->vce_active != rdev->pm.dpm.vce_active) 108462306a36Sopenharmony_ci goto force; 108562306a36Sopenharmony_ci /* user has made a display change (such as timing) */ 108662306a36Sopenharmony_ci if (rdev->pm.dpm.single_display != single_display) 108762306a36Sopenharmony_ci goto force; 108862306a36Sopenharmony_ci if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { 108962306a36Sopenharmony_ci /* for pre-BTC and APUs if the num crtcs changed but state is the same, 109062306a36Sopenharmony_ci * all we need to do is update the display configuration. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { 109362306a36Sopenharmony_ci /* update display watermarks based on new power state */ 109462306a36Sopenharmony_ci radeon_bandwidth_update(rdev); 109562306a36Sopenharmony_ci /* update displays */ 109662306a36Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 109762306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 109862306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci return; 110162306a36Sopenharmony_ci } else { 110262306a36Sopenharmony_ci /* for BTC+ if the num crtcs hasn't changed and state is the same, 110362306a36Sopenharmony_ci * nothing to do, if the num crtcs is > 1 and state is the same, 110462306a36Sopenharmony_ci * update display configuration. 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_ci if (rdev->pm.dpm.new_active_crtcs == 110762306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtcs) { 110862306a36Sopenharmony_ci return; 110962306a36Sopenharmony_ci } else { 111062306a36Sopenharmony_ci if ((rdev->pm.dpm.current_active_crtc_count > 1) && 111162306a36Sopenharmony_ci (rdev->pm.dpm.new_active_crtc_count > 1)) { 111262306a36Sopenharmony_ci /* update display watermarks based on new power state */ 111362306a36Sopenharmony_ci radeon_bandwidth_update(rdev); 111462306a36Sopenharmony_ci /* update displays */ 111562306a36Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 111662306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 111762306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 111862306a36Sopenharmony_ci return; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ciforce: 112562306a36Sopenharmony_ci if (radeon_dpm == 1) { 112662306a36Sopenharmony_ci printk("switching from power state:\n"); 112762306a36Sopenharmony_ci radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps); 112862306a36Sopenharmony_ci printk("switching to power state:\n"); 112962306a36Sopenharmony_ci radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci down_write(&rdev->pm.mclk_lock); 113362306a36Sopenharmony_ci mutex_lock(&rdev->ring_lock); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* update whether vce is active */ 113662306a36Sopenharmony_ci ps->vce_active = rdev->pm.dpm.vce_active; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ret = radeon_dpm_pre_set_power_state(rdev); 113962306a36Sopenharmony_ci if (ret) 114062306a36Sopenharmony_ci goto done; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* update display watermarks based on new power state */ 114362306a36Sopenharmony_ci radeon_bandwidth_update(rdev); 114462306a36Sopenharmony_ci /* update displays */ 114562306a36Sopenharmony_ci radeon_dpm_display_configuration_changed(rdev); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* wait for the rings to drain */ 114862306a36Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) { 114962306a36Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 115062306a36Sopenharmony_ci if (ring->ready) 115162306a36Sopenharmony_ci radeon_fence_wait_empty(rdev, i); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* program the new power state */ 115562306a36Sopenharmony_ci radeon_dpm_set_power_state(rdev); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* update current power state */ 115862306a36Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci radeon_dpm_post_set_power_state(rdev); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; 116362306a36Sopenharmony_ci rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; 116462306a36Sopenharmony_ci rdev->pm.dpm.single_display = single_display; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (rdev->asic->dpm.force_performance_level) { 116762306a36Sopenharmony_ci if (rdev->pm.dpm.thermal_active) { 116862306a36Sopenharmony_ci enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; 116962306a36Sopenharmony_ci /* force low perf level for thermal */ 117062306a36Sopenharmony_ci radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW); 117162306a36Sopenharmony_ci /* save the user's level */ 117262306a36Sopenharmony_ci rdev->pm.dpm.forced_level = level; 117362306a36Sopenharmony_ci } else { 117462306a36Sopenharmony_ci /* otherwise, user selected level */ 117562306a36Sopenharmony_ci radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cidone: 118062306a36Sopenharmony_ci mutex_unlock(&rdev->ring_lock); 118162306a36Sopenharmony_ci up_write(&rdev->pm.mclk_lock); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_civoid radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci enum radeon_pm_state_type dpm_state; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (rdev->asic->dpm.powergate_uvd) { 118962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 119062306a36Sopenharmony_ci /* don't powergate anything if we 119162306a36Sopenharmony_ci have active but pause streams */ 119262306a36Sopenharmony_ci enable |= rdev->pm.dpm.sd > 0; 119362306a36Sopenharmony_ci enable |= rdev->pm.dpm.hd > 0; 119462306a36Sopenharmony_ci /* enable/disable UVD */ 119562306a36Sopenharmony_ci radeon_dpm_powergate_uvd(rdev, !enable); 119662306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 119762306a36Sopenharmony_ci } else { 119862306a36Sopenharmony_ci if (enable) { 119962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 120062306a36Sopenharmony_ci rdev->pm.dpm.uvd_active = true; 120162306a36Sopenharmony_ci /* disable this for now */ 120262306a36Sopenharmony_ci#if 0 120362306a36Sopenharmony_ci if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0)) 120462306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD; 120562306a36Sopenharmony_ci else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0)) 120662306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 120762306a36Sopenharmony_ci else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1)) 120862306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; 120962306a36Sopenharmony_ci else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2)) 121062306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2; 121162306a36Sopenharmony_ci else 121262306a36Sopenharmony_ci#endif 121362306a36Sopenharmony_ci dpm_state = POWER_STATE_TYPE_INTERNAL_UVD; 121462306a36Sopenharmony_ci rdev->pm.dpm.state = dpm_state; 121562306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 121662306a36Sopenharmony_ci } else { 121762306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 121862306a36Sopenharmony_ci rdev->pm.dpm.uvd_active = false; 121962306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_civoid radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci if (enable) { 122962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 123062306a36Sopenharmony_ci rdev->pm.dpm.vce_active = true; 123162306a36Sopenharmony_ci /* XXX select vce level based on ring/task */ 123262306a36Sopenharmony_ci rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL; 123362306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 123462306a36Sopenharmony_ci } else { 123562306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 123662306a36Sopenharmony_ci rdev->pm.dpm.vce_active = false; 123762306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic void radeon_pm_suspend_old(struct radeon_device *rdev) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 124662306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 124762306a36Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) 124862306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic void radeon_pm_suspend_dpm(struct radeon_device *rdev) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 125862306a36Sopenharmony_ci /* disable dpm */ 125962306a36Sopenharmony_ci radeon_dpm_disable(rdev); 126062306a36Sopenharmony_ci /* reset the power state */ 126162306a36Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 126262306a36Sopenharmony_ci rdev->pm.dpm_enabled = false; 126362306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_civoid radeon_pm_suspend(struct radeon_device *rdev) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 126962306a36Sopenharmony_ci radeon_pm_suspend_dpm(rdev); 127062306a36Sopenharmony_ci else 127162306a36Sopenharmony_ci radeon_pm_suspend_old(rdev); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic void radeon_pm_resume_old(struct radeon_device *rdev) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci /* set up the default clocks if the MC ucode is loaded */ 127762306a36Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 127862306a36Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 127962306a36Sopenharmony_ci rdev->mc_fw) { 128062306a36Sopenharmony_ci if (rdev->pm.default_vddc) 128162306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 128262306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 128362306a36Sopenharmony_ci if (rdev->pm.default_vddci) 128462306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 128562306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 128662306a36Sopenharmony_ci if (rdev->pm.default_sclk) 128762306a36Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 128862306a36Sopenharmony_ci if (rdev->pm.default_mclk) 128962306a36Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci /* asic init will reset the default power state */ 129262306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 129362306a36Sopenharmony_ci rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; 129462306a36Sopenharmony_ci rdev->pm.current_clock_mode_index = 0; 129562306a36Sopenharmony_ci rdev->pm.current_sclk = rdev->pm.default_sclk; 129662306a36Sopenharmony_ci rdev->pm.current_mclk = rdev->pm.default_mclk; 129762306a36Sopenharmony_ci if (rdev->pm.power_state) { 129862306a36Sopenharmony_ci rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; 129962306a36Sopenharmony_ci rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DYNPM 130262306a36Sopenharmony_ci && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) { 130362306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 130462306a36Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 130562306a36Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 130862306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void radeon_pm_resume_dpm(struct radeon_device *rdev) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci int ret; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci /* asic init will reset to the boot state */ 131662306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 131762306a36Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 131862306a36Sopenharmony_ci radeon_dpm_setup_asic(rdev); 131962306a36Sopenharmony_ci ret = radeon_dpm_enable(rdev); 132062306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 132162306a36Sopenharmony_ci if (ret) 132262306a36Sopenharmony_ci goto dpm_resume_fail; 132362306a36Sopenharmony_ci rdev->pm.dpm_enabled = true; 132462306a36Sopenharmony_ci return; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cidpm_resume_fail: 132762306a36Sopenharmony_ci DRM_ERROR("radeon: dpm resume failed\n"); 132862306a36Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 132962306a36Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 133062306a36Sopenharmony_ci rdev->mc_fw) { 133162306a36Sopenharmony_ci if (rdev->pm.default_vddc) 133262306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 133362306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 133462306a36Sopenharmony_ci if (rdev->pm.default_vddci) 133562306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 133662306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 133762306a36Sopenharmony_ci if (rdev->pm.default_sclk) 133862306a36Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 133962306a36Sopenharmony_ci if (rdev->pm.default_mclk) 134062306a36Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_civoid radeon_pm_resume(struct radeon_device *rdev) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 134762306a36Sopenharmony_ci radeon_pm_resume_dpm(rdev); 134862306a36Sopenharmony_ci else 134962306a36Sopenharmony_ci radeon_pm_resume_old(rdev); 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic int radeon_pm_init_old(struct radeon_device *rdev) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci int ret; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 135762306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 135862306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 135962306a36Sopenharmony_ci rdev->pm.dynpm_can_upclock = true; 136062306a36Sopenharmony_ci rdev->pm.dynpm_can_downclock = true; 136162306a36Sopenharmony_ci rdev->pm.default_sclk = rdev->clock.default_sclk; 136262306a36Sopenharmony_ci rdev->pm.default_mclk = rdev->clock.default_mclk; 136362306a36Sopenharmony_ci rdev->pm.current_sclk = rdev->clock.default_sclk; 136462306a36Sopenharmony_ci rdev->pm.current_mclk = rdev->clock.default_mclk; 136562306a36Sopenharmony_ci rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if (rdev->bios) { 136862306a36Sopenharmony_ci if (rdev->is_atom_bios) 136962306a36Sopenharmony_ci radeon_atombios_get_power_modes(rdev); 137062306a36Sopenharmony_ci else 137162306a36Sopenharmony_ci radeon_combios_get_power_modes(rdev); 137262306a36Sopenharmony_ci radeon_pm_print_states(rdev); 137362306a36Sopenharmony_ci radeon_pm_init_profile(rdev); 137462306a36Sopenharmony_ci /* set up the default clocks if the MC ucode is loaded */ 137562306a36Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 137662306a36Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 137762306a36Sopenharmony_ci rdev->mc_fw) { 137862306a36Sopenharmony_ci if (rdev->pm.default_vddc) 137962306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 138062306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 138162306a36Sopenharmony_ci if (rdev->pm.default_vddci) 138262306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 138362306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 138462306a36Sopenharmony_ci if (rdev->pm.default_sclk) 138562306a36Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 138662306a36Sopenharmony_ci if (rdev->pm.default_mclk) 138762306a36Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* set up the internal thermal sensor if applicable */ 139262306a36Sopenharmony_ci ret = radeon_hwmon_init(rdev); 139362306a36Sopenharmony_ci if (ret) 139462306a36Sopenharmony_ci return ret; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 139962306a36Sopenharmony_ci radeon_debugfs_pm_init(rdev); 140062306a36Sopenharmony_ci DRM_INFO("radeon: power management initialized\n"); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci return 0; 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic void radeon_dpm_print_power_states(struct radeon_device *rdev) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci int i; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci for (i = 0; i < rdev->pm.dpm.num_ps; i++) { 141162306a36Sopenharmony_ci printk("== power state %d ==\n", i); 141262306a36Sopenharmony_ci radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic int radeon_pm_init_dpm(struct radeon_device *rdev) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci int ret; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* default to balanced state */ 142162306a36Sopenharmony_ci rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; 142262306a36Sopenharmony_ci rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; 142362306a36Sopenharmony_ci rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; 142462306a36Sopenharmony_ci rdev->pm.default_sclk = rdev->clock.default_sclk; 142562306a36Sopenharmony_ci rdev->pm.default_mclk = rdev->clock.default_mclk; 142662306a36Sopenharmony_ci rdev->pm.current_sclk = rdev->clock.default_sclk; 142762306a36Sopenharmony_ci rdev->pm.current_mclk = rdev->clock.default_mclk; 142862306a36Sopenharmony_ci rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (rdev->bios && rdev->is_atom_bios) 143162306a36Sopenharmony_ci radeon_atombios_get_power_modes(rdev); 143262306a36Sopenharmony_ci else 143362306a36Sopenharmony_ci return -EINVAL; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* set up the internal thermal sensor if applicable */ 143662306a36Sopenharmony_ci ret = radeon_hwmon_init(rdev); 143762306a36Sopenharmony_ci if (ret) 143862306a36Sopenharmony_ci return ret; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); 144162306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 144262306a36Sopenharmony_ci radeon_dpm_init(rdev); 144362306a36Sopenharmony_ci rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; 144462306a36Sopenharmony_ci if (radeon_dpm == 1) 144562306a36Sopenharmony_ci radeon_dpm_print_power_states(rdev); 144662306a36Sopenharmony_ci radeon_dpm_setup_asic(rdev); 144762306a36Sopenharmony_ci ret = radeon_dpm_enable(rdev); 144862306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 144962306a36Sopenharmony_ci if (ret) 145062306a36Sopenharmony_ci goto dpm_failed; 145162306a36Sopenharmony_ci rdev->pm.dpm_enabled = true; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci radeon_debugfs_pm_init(rdev); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci DRM_INFO("radeon: dpm initialized\n"); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci return 0; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_cidpm_failed: 146062306a36Sopenharmony_ci rdev->pm.dpm_enabled = false; 146162306a36Sopenharmony_ci if ((rdev->family >= CHIP_BARTS) && 146262306a36Sopenharmony_ci (rdev->family <= CHIP_CAYMAN) && 146362306a36Sopenharmony_ci rdev->mc_fw) { 146462306a36Sopenharmony_ci if (rdev->pm.default_vddc) 146562306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, 146662306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDC); 146762306a36Sopenharmony_ci if (rdev->pm.default_vddci) 146862306a36Sopenharmony_ci radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, 146962306a36Sopenharmony_ci SET_VOLTAGE_TYPE_ASIC_VDDCI); 147062306a36Sopenharmony_ci if (rdev->pm.default_sclk) 147162306a36Sopenharmony_ci radeon_set_engine_clock(rdev, rdev->pm.default_sclk); 147262306a36Sopenharmony_ci if (rdev->pm.default_mclk) 147362306a36Sopenharmony_ci radeon_set_memory_clock(rdev, rdev->pm.default_mclk); 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci DRM_ERROR("radeon: dpm initialization failed\n"); 147662306a36Sopenharmony_ci return ret; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistruct radeon_dpm_quirk { 148062306a36Sopenharmony_ci u32 chip_vendor; 148162306a36Sopenharmony_ci u32 chip_device; 148262306a36Sopenharmony_ci u32 subsys_vendor; 148362306a36Sopenharmony_ci u32 subsys_device; 148462306a36Sopenharmony_ci}; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci/* cards with dpm stability problems */ 148762306a36Sopenharmony_cistatic struct radeon_dpm_quirk radeon_dpm_quirk_list[] = { 148862306a36Sopenharmony_ci /* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */ 148962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 }, 149062306a36Sopenharmony_ci /* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */ 149162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 }, 149262306a36Sopenharmony_ci { 0, 0, 0, 0 }, 149362306a36Sopenharmony_ci}; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ciint radeon_pm_init(struct radeon_device *rdev) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci struct radeon_dpm_quirk *p = radeon_dpm_quirk_list; 149862306a36Sopenharmony_ci bool disable_dpm = false; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* Apply dpm quirks */ 150162306a36Sopenharmony_ci while (p && p->chip_device != 0) { 150262306a36Sopenharmony_ci if (rdev->pdev->vendor == p->chip_vendor && 150362306a36Sopenharmony_ci rdev->pdev->device == p->chip_device && 150462306a36Sopenharmony_ci rdev->pdev->subsystem_vendor == p->subsys_vendor && 150562306a36Sopenharmony_ci rdev->pdev->subsystem_device == p->subsys_device) { 150662306a36Sopenharmony_ci disable_dpm = true; 150762306a36Sopenharmony_ci break; 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci ++p; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* enable dpm on rv6xx+ */ 151362306a36Sopenharmony_ci switch (rdev->family) { 151462306a36Sopenharmony_ci case CHIP_RV610: 151562306a36Sopenharmony_ci case CHIP_RV630: 151662306a36Sopenharmony_ci case CHIP_RV620: 151762306a36Sopenharmony_ci case CHIP_RV635: 151862306a36Sopenharmony_ci case CHIP_RV670: 151962306a36Sopenharmony_ci case CHIP_RS780: 152062306a36Sopenharmony_ci case CHIP_RS880: 152162306a36Sopenharmony_ci case CHIP_RV770: 152262306a36Sopenharmony_ci /* DPM requires the RLC, RV770+ dGPU requires SMC */ 152362306a36Sopenharmony_ci if (!rdev->rlc_fw) 152462306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 152562306a36Sopenharmony_ci else if ((rdev->family >= CHIP_RV770) && 152662306a36Sopenharmony_ci (!(rdev->flags & RADEON_IS_IGP)) && 152762306a36Sopenharmony_ci (!rdev->smc_fw)) 152862306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 152962306a36Sopenharmony_ci else if (radeon_dpm == 1) 153062306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DPM; 153162306a36Sopenharmony_ci else 153262306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 153362306a36Sopenharmony_ci break; 153462306a36Sopenharmony_ci case CHIP_RV730: 153562306a36Sopenharmony_ci case CHIP_RV710: 153662306a36Sopenharmony_ci case CHIP_RV740: 153762306a36Sopenharmony_ci case CHIP_CEDAR: 153862306a36Sopenharmony_ci case CHIP_REDWOOD: 153962306a36Sopenharmony_ci case CHIP_JUNIPER: 154062306a36Sopenharmony_ci case CHIP_CYPRESS: 154162306a36Sopenharmony_ci case CHIP_HEMLOCK: 154262306a36Sopenharmony_ci case CHIP_PALM: 154362306a36Sopenharmony_ci case CHIP_SUMO: 154462306a36Sopenharmony_ci case CHIP_SUMO2: 154562306a36Sopenharmony_ci case CHIP_BARTS: 154662306a36Sopenharmony_ci case CHIP_TURKS: 154762306a36Sopenharmony_ci case CHIP_CAICOS: 154862306a36Sopenharmony_ci case CHIP_CAYMAN: 154962306a36Sopenharmony_ci case CHIP_ARUBA: 155062306a36Sopenharmony_ci case CHIP_TAHITI: 155162306a36Sopenharmony_ci case CHIP_PITCAIRN: 155262306a36Sopenharmony_ci case CHIP_VERDE: 155362306a36Sopenharmony_ci case CHIP_OLAND: 155462306a36Sopenharmony_ci case CHIP_HAINAN: 155562306a36Sopenharmony_ci case CHIP_BONAIRE: 155662306a36Sopenharmony_ci case CHIP_KABINI: 155762306a36Sopenharmony_ci case CHIP_KAVERI: 155862306a36Sopenharmony_ci case CHIP_HAWAII: 155962306a36Sopenharmony_ci case CHIP_MULLINS: 156062306a36Sopenharmony_ci /* DPM requires the RLC, RV770+ dGPU requires SMC */ 156162306a36Sopenharmony_ci if (!rdev->rlc_fw) 156262306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 156362306a36Sopenharmony_ci else if ((rdev->family >= CHIP_RV770) && 156462306a36Sopenharmony_ci (!(rdev->flags & RADEON_IS_IGP)) && 156562306a36Sopenharmony_ci (!rdev->smc_fw)) 156662306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 156762306a36Sopenharmony_ci else if (disable_dpm && (radeon_dpm == -1)) 156862306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 156962306a36Sopenharmony_ci else if (radeon_dpm == 0) 157062306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 157162306a36Sopenharmony_ci else 157262306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_DPM; 157362306a36Sopenharmony_ci break; 157462306a36Sopenharmony_ci default: 157562306a36Sopenharmony_ci /* default to profile method */ 157662306a36Sopenharmony_ci rdev->pm.pm_method = PM_METHOD_PROFILE; 157762306a36Sopenharmony_ci break; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 158162306a36Sopenharmony_ci return radeon_pm_init_dpm(rdev); 158262306a36Sopenharmony_ci else 158362306a36Sopenharmony_ci return radeon_pm_init_old(rdev); 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ciint radeon_pm_late_init(struct radeon_device *rdev) 158762306a36Sopenharmony_ci{ 158862306a36Sopenharmony_ci int ret = 0; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) { 159162306a36Sopenharmony_ci if (rdev->pm.dpm_enabled) { 159262306a36Sopenharmony_ci if (!rdev->pm.sysfs_initialized) { 159362306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); 159462306a36Sopenharmony_ci if (ret) 159562306a36Sopenharmony_ci DRM_ERROR("failed to create device file for dpm state\n"); 159662306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 159762306a36Sopenharmony_ci if (ret) 159862306a36Sopenharmony_ci DRM_ERROR("failed to create device file for dpm state\n"); 159962306a36Sopenharmony_ci /* XXX: these are noops for dpm but are here for backwards compat */ 160062306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_profile); 160162306a36Sopenharmony_ci if (ret) 160262306a36Sopenharmony_ci DRM_ERROR("failed to create device file for power profile\n"); 160362306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_method); 160462306a36Sopenharmony_ci if (ret) 160562306a36Sopenharmony_ci DRM_ERROR("failed to create device file for power method\n"); 160662306a36Sopenharmony_ci rdev->pm.sysfs_initialized = true; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 161062306a36Sopenharmony_ci ret = radeon_dpm_late_enable(rdev); 161162306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 161262306a36Sopenharmony_ci if (ret) { 161362306a36Sopenharmony_ci rdev->pm.dpm_enabled = false; 161462306a36Sopenharmony_ci DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n"); 161562306a36Sopenharmony_ci } else { 161662306a36Sopenharmony_ci /* set the dpm state for PX since there won't be 161762306a36Sopenharmony_ci * a modeset to call this. 161862306a36Sopenharmony_ci */ 161962306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci } else { 162362306a36Sopenharmony_ci if ((rdev->pm.num_power_states > 1) && 162462306a36Sopenharmony_ci (!rdev->pm.sysfs_initialized)) { 162562306a36Sopenharmony_ci /* where's the best place to put these? */ 162662306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_profile); 162762306a36Sopenharmony_ci if (ret) 162862306a36Sopenharmony_ci DRM_ERROR("failed to create device file for power profile\n"); 162962306a36Sopenharmony_ci ret = device_create_file(rdev->dev, &dev_attr_power_method); 163062306a36Sopenharmony_ci if (ret) 163162306a36Sopenharmony_ci DRM_ERROR("failed to create device file for power method\n"); 163262306a36Sopenharmony_ci else 163362306a36Sopenharmony_ci rdev->pm.sysfs_initialized = true; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci return ret; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic void radeon_pm_fini_old(struct radeon_device *rdev) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 164262306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 164362306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 164462306a36Sopenharmony_ci rdev->pm.profile = PM_PROFILE_DEFAULT; 164562306a36Sopenharmony_ci radeon_pm_update_profile(rdev); 164662306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 164762306a36Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 164862306a36Sopenharmony_ci /* reset default clocks */ 164962306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; 165062306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 165162306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_profile); 165862306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_method); 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci radeon_hwmon_fini(rdev); 166262306a36Sopenharmony_ci kfree(rdev->pm.power_state); 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cistatic void radeon_pm_fini_dpm(struct radeon_device *rdev) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci if (rdev->pm.num_power_states > 1) { 166862306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 166962306a36Sopenharmony_ci radeon_dpm_disable(rdev); 167062306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_dpm_state); 167362306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); 167462306a36Sopenharmony_ci /* XXX backwards compat */ 167562306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_profile); 167662306a36Sopenharmony_ci device_remove_file(rdev->dev, &dev_attr_power_method); 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci radeon_dpm_fini(rdev); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci radeon_hwmon_fini(rdev); 168162306a36Sopenharmony_ci kfree(rdev->pm.power_state); 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_civoid radeon_pm_fini(struct radeon_device *rdev) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 168762306a36Sopenharmony_ci radeon_pm_fini_dpm(rdev); 168862306a36Sopenharmony_ci else 168962306a36Sopenharmony_ci radeon_pm_fini_old(rdev); 169062306a36Sopenharmony_ci} 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_cistatic void radeon_pm_compute_clocks_old(struct radeon_device *rdev) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 169562306a36Sopenharmony_ci struct drm_crtc *crtc; 169662306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (rdev->pm.num_power_states < 2) 169962306a36Sopenharmony_ci return; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci rdev->pm.active_crtcs = 0; 170462306a36Sopenharmony_ci rdev->pm.active_crtc_count = 0; 170562306a36Sopenharmony_ci if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 170662306a36Sopenharmony_ci list_for_each_entry(crtc, 170762306a36Sopenharmony_ci &ddev->mode_config.crtc_list, head) { 170862306a36Sopenharmony_ci radeon_crtc = to_radeon_crtc(crtc); 170962306a36Sopenharmony_ci if (radeon_crtc->enabled) { 171062306a36Sopenharmony_ci rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); 171162306a36Sopenharmony_ci rdev->pm.active_crtc_count++; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_PROFILE) { 171762306a36Sopenharmony_ci radeon_pm_update_profile(rdev); 171862306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 171962306a36Sopenharmony_ci } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { 172062306a36Sopenharmony_ci if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) { 172162306a36Sopenharmony_ci if (rdev->pm.active_crtc_count > 1) { 172262306a36Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 172362306a36Sopenharmony_ci cancel_delayed_work(&rdev->pm.dynpm_idle_work); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; 172662306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; 172762306a36Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 172862306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n"); 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci } else if (rdev->pm.active_crtc_count == 1) { 173362306a36Sopenharmony_ci /* TODO: Increase clocks if needed for current mode */ 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) { 173662306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 173762306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK; 173862306a36Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 173962306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 174262306a36Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 174362306a36Sopenharmony_ci } else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) { 174462306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; 174562306a36Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 174662306a36Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 174762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n"); 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci } else { /* count == 0 */ 175062306a36Sopenharmony_ci if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) { 175162306a36Sopenharmony_ci cancel_delayed_work(&rdev->pm.dynpm_idle_work); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM; 175462306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM; 175562306a36Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 175662306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 176862306a36Sopenharmony_ci struct drm_crtc *crtc; 176962306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc; 177062306a36Sopenharmony_ci struct radeon_connector *radeon_connector; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (!rdev->pm.dpm_enabled) 177362306a36Sopenharmony_ci return; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci /* update active crtc counts */ 177862306a36Sopenharmony_ci rdev->pm.dpm.new_active_crtcs = 0; 177962306a36Sopenharmony_ci rdev->pm.dpm.new_active_crtc_count = 0; 178062306a36Sopenharmony_ci rdev->pm.dpm.high_pixelclock_count = 0; 178162306a36Sopenharmony_ci if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { 178262306a36Sopenharmony_ci list_for_each_entry(crtc, 178362306a36Sopenharmony_ci &ddev->mode_config.crtc_list, head) { 178462306a36Sopenharmony_ci radeon_crtc = to_radeon_crtc(crtc); 178562306a36Sopenharmony_ci if (crtc->enabled) { 178662306a36Sopenharmony_ci rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id); 178762306a36Sopenharmony_ci rdev->pm.dpm.new_active_crtc_count++; 178862306a36Sopenharmony_ci if (!radeon_crtc->connector) 178962306a36Sopenharmony_ci continue; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci radeon_connector = to_radeon_connector(radeon_crtc->connector); 179262306a36Sopenharmony_ci if (radeon_connector->pixelclock_for_modeset > 297000) 179362306a36Sopenharmony_ci rdev->pm.dpm.high_pixelclock_count++; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* update battery/ac status */ 179962306a36Sopenharmony_ci if (power_supply_is_system_supplied() > 0) 180062306a36Sopenharmony_ci rdev->pm.dpm.ac_power = true; 180162306a36Sopenharmony_ci else 180262306a36Sopenharmony_ci rdev->pm.dpm.ac_power = false; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci radeon_dpm_change_power_state_locked(rdev); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci} 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_civoid radeon_pm_compute_clocks(struct radeon_device *rdev) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci if (rdev->pm.pm_method == PM_METHOD_DPM) 181362306a36Sopenharmony_ci radeon_pm_compute_clocks_dpm(rdev); 181462306a36Sopenharmony_ci else 181562306a36Sopenharmony_ci radeon_pm_compute_clocks_old(rdev); 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic bool radeon_pm_in_vbl(struct radeon_device *rdev) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci int crtc, vpos, hpos, vbl_status; 182162306a36Sopenharmony_ci bool in_vbl = true; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci /* Iterate over all active crtc's. All crtc's must be in vblank, 182462306a36Sopenharmony_ci * otherwise return in_vbl == false. 182562306a36Sopenharmony_ci */ 182662306a36Sopenharmony_ci for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { 182762306a36Sopenharmony_ci if (rdev->pm.active_crtcs & (1 << crtc)) { 182862306a36Sopenharmony_ci vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, 182962306a36Sopenharmony_ci crtc, 183062306a36Sopenharmony_ci USE_REAL_VBLANKSTART, 183162306a36Sopenharmony_ci &vpos, &hpos, NULL, NULL, 183262306a36Sopenharmony_ci &rdev->mode_info.crtcs[crtc]->base.hwmode); 183362306a36Sopenharmony_ci if ((vbl_status & DRM_SCANOUTPOS_VALID) && 183462306a36Sopenharmony_ci !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK)) 183562306a36Sopenharmony_ci in_vbl = false; 183662306a36Sopenharmony_ci } 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci return in_vbl; 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci u32 stat_crtc = 0; 184562306a36Sopenharmony_ci bool in_vbl = radeon_pm_in_vbl(rdev); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (!in_vbl) 184862306a36Sopenharmony_ci DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc, 184962306a36Sopenharmony_ci finish ? "exit" : "entry"); 185062306a36Sopenharmony_ci return in_vbl; 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_cistatic void radeon_dynpm_idle_work_handler(struct work_struct *work) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci struct radeon_device *rdev; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci rdev = container_of(work, struct radeon_device, 185862306a36Sopenharmony_ci pm.dynpm_idle_work.work); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 186162306a36Sopenharmony_ci if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { 186262306a36Sopenharmony_ci int not_processed = 0; 186362306a36Sopenharmony_ci int i; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; ++i) { 186662306a36Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[i]; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (ring->ready) { 186962306a36Sopenharmony_ci not_processed += radeon_fence_count_emitted(rdev, i); 187062306a36Sopenharmony_ci if (not_processed >= 3) 187162306a36Sopenharmony_ci break; 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (not_processed >= 3) { /* should upclock */ 187662306a36Sopenharmony_ci if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) { 187762306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 187862306a36Sopenharmony_ci } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 187962306a36Sopenharmony_ci rdev->pm.dynpm_can_upclock) { 188062306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = 188162306a36Sopenharmony_ci DYNPM_ACTION_UPCLOCK; 188262306a36Sopenharmony_ci rdev->pm.dynpm_action_timeout = jiffies + 188362306a36Sopenharmony_ci msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci } else if (not_processed == 0) { /* should downclock */ 188662306a36Sopenharmony_ci if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) { 188762306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; 188862306a36Sopenharmony_ci } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && 188962306a36Sopenharmony_ci rdev->pm.dynpm_can_downclock) { 189062306a36Sopenharmony_ci rdev->pm.dynpm_planned_action = 189162306a36Sopenharmony_ci DYNPM_ACTION_DOWNCLOCK; 189262306a36Sopenharmony_ci rdev->pm.dynpm_action_timeout = jiffies + 189362306a36Sopenharmony_ci msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* Note, radeon_pm_set_clocks is called with static_switch set 189862306a36Sopenharmony_ci * to false since we want to wait for vbl to avoid flicker. 189962306a36Sopenharmony_ci */ 190062306a36Sopenharmony_ci if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE && 190162306a36Sopenharmony_ci time_after(jiffies, rdev->pm.dynpm_action_timeout)) { 190262306a36Sopenharmony_ci radeon_pm_get_dynpm_state(rdev); 190362306a36Sopenharmony_ci radeon_pm_set_clocks(rdev); 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci schedule_delayed_work(&rdev->pm.dynpm_idle_work, 190762306a36Sopenharmony_ci msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 191062306a36Sopenharmony_ci} 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci/* 191362306a36Sopenharmony_ci * Debugfs info 191462306a36Sopenharmony_ci */ 191562306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic int radeon_debugfs_pm_info_show(struct seq_file *m, void *unused) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci struct radeon_device *rdev = m->private; 192062306a36Sopenharmony_ci struct drm_device *ddev = rdev->ddev; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if ((rdev->flags & RADEON_IS_PX) && 192362306a36Sopenharmony_ci (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { 192462306a36Sopenharmony_ci seq_printf(m, "PX asic powered off\n"); 192562306a36Sopenharmony_ci } else if (rdev->pm.dpm_enabled) { 192662306a36Sopenharmony_ci mutex_lock(&rdev->pm.mutex); 192762306a36Sopenharmony_ci if (rdev->asic->dpm.debugfs_print_current_performance_level) 192862306a36Sopenharmony_ci radeon_dpm_debugfs_print_current_performance_level(rdev, m); 192962306a36Sopenharmony_ci else 193062306a36Sopenharmony_ci seq_printf(m, "Debugfs support not implemented for this asic\n"); 193162306a36Sopenharmony_ci mutex_unlock(&rdev->pm.mutex); 193262306a36Sopenharmony_ci } else { 193362306a36Sopenharmony_ci seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); 193462306a36Sopenharmony_ci /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ 193562306a36Sopenharmony_ci if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) 193662306a36Sopenharmony_ci seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); 193762306a36Sopenharmony_ci else 193862306a36Sopenharmony_ci seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); 193962306a36Sopenharmony_ci seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); 194062306a36Sopenharmony_ci if (rdev->asic->pm.get_memory_clock) 194162306a36Sopenharmony_ci seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); 194262306a36Sopenharmony_ci if (rdev->pm.current_vddc) 194362306a36Sopenharmony_ci seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); 194462306a36Sopenharmony_ci if (rdev->asic->pm.get_pcie_lanes) 194562306a36Sopenharmony_ci seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci return 0; 194962306a36Sopenharmony_ci} 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(radeon_debugfs_pm_info); 195262306a36Sopenharmony_ci#endif 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_cistatic void radeon_debugfs_pm_init(struct radeon_device *rdev) 195562306a36Sopenharmony_ci{ 195662306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 195762306a36Sopenharmony_ci struct dentry *root = rdev->ddev->primary->debugfs_root; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci debugfs_create_file("radeon_pm_info", 0444, root, rdev, 196062306a36Sopenharmony_ci &radeon_debugfs_pm_info_fops); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci#endif 196362306a36Sopenharmony_ci} 1964