AMD General

Reviewed-by: Kenneth Feng <[email protected]>


-----Original Message-----
From: Wang, Kevin <[email protected]>
Sent: Thursday, October 15, 2026 4:01 PM
To: [email protected]
Cc: Deucher, Alexander <[email protected]>; Zhang, Hawking 
<[email protected]>; Feng, Kenneth <[email protected]>
Subject: [PATCH] drm/amd/pm: reapply umd profile state after recovery

resume and full gpu reset retain the selected umd profile level in driver state.

add a generic dpm reapply path after cg and pg restoration.
it restores the umd gfx state and asks the pm backend to replay the retained 
level.

backend callbacks for swsmu and powerplay include swsmu gpo, gfx ulv, and 
deep-sleep controls.

this covers:
- pci runtime resume
- full gpu reset recovery
- system resume, including s3 and s2idle

Signed-off-by: Kevin Wang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    |  6 ++
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c       | 14 -----
 .../gpu/drm/amd/include/kgd_pp_interface.h    |  2 +
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c           | 63 +++++++++++++++++++
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h       |  1 +
 .../gpu/drm/amd/pm/powerplay/amd_powerplay.c  | 15 +++++
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 61 +++++++++++++++---
 7 files changed, 138 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index e0014611f039..c4f807ca7eaa 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -2776,6 +2776,12 @@ static int amdgpu_device_ip_late_init(struct 
amdgpu_device *adev)
        amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE);
        amdgpu_device_set_pg_state(adev, AMD_PG_STATE_GATE);

+       r = amdgpu_dpm_reapply_umd_profile(adev);
+       if (r) {
+               dev_err(adev->dev, "Failed to restore UMD profile state: %d\n", 
r);
+               return r;
+       }
+
        amdgpu_device_fill_reset_magic(adev);

        r = amdgpu_device_enable_mgpu_fan_boost();
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 2712580a5663..d4267358156a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2987,19 +2987,6 @@ static int amdgpu_pmops_runtime_suspend(struct device 
*dev)
        return 0;
 }

-static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device 
*adev) -{
-       enum amd_dpm_forced_level level;
-       uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
-               AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
-               AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
-               AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
-
-       level = amdgpu_dpm_get_performance_level(adev);
-       if (level & profile_mode_mask)
-               amdgpu_asic_update_umd_stable_pstate(adev, true);
-}
-
 static int amdgpu_pmops_runtime_resume(struct device *dev)  {
        struct pci_dev *pdev = to_pci_dev(dev); @@ -3045,7 +3032,6 @@ static 
int amdgpu_pmops_runtime_resume(struct device *dev)
        if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
                drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;

-       amdgpu_restore_umd_profile_pstate_after_runpm(adev);
        adev->in_runpm = false;
        return 0;
 }
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h 
b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index ca818965afe1..7db44fa24042 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -449,6 +449,8 @@ struct amd_pm_funcs {
        int (*print_clock_levels)(void *handle, enum pp_clock_type type, char 
*buf);
        int (*emit_clock_levels)(void *handle, enum pp_clock_type type, char 
*buf, int *offset);
        int (*force_performance_level)(void *handle, enum amd_dpm_forced_level 
level);
+       int (*reapply_performance_level)(void *handle,
+                                        enum amd_dpm_forced_level level);
        int (*get_sclk_od)(void *handle);
        int (*set_sclk_od)(void *handle, uint32_t value);
        int (*get_mclk_od)(void *handle);
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index 0394085a0328..01d2049d8224 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -1082,6 +1082,69 @@ static void amdgpu_dpm_exit_umd_state(struct 
amdgpu_device *adev)
                                               AMD_PG_STATE_GATE);
 }

+/**
+ * amdgpu_dpm_reapply_umd_profile - Restore the active UMD profile
+ * @adev: amdgpu device
+ *
+ * Resume and reset retain the selected DPM level in software, but the
+ * underlying GFX and PMFW state is lost. Reapply a retained UMD
+profile
+ * after the common resume path has restored its default CG and PG state.
+ */
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev) {
+       const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+       enum amd_dpm_forced_level level;
+       int ret;
+
+       if (!pp_funcs || !pp_funcs->reapply_performance_level)
+               return 0;
+
+       /* Serialize the retained level with the GFX and PMFW reprogramming. */
+       mutex_lock(&adev->pm.mutex);
+       if (adev->pm.dpm.thermal_active) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       if (pp_funcs->get_performance_level)
+               level = 
pp_funcs->get_performance_level(adev->powerplay.pp_handle);
+       else
+               level = adev->pm.dpm.forced_level;
+
+       switch (level) {
+       case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+               break;
+       default:
+               ret = 0;
+               goto unlock;
+       }
+
+       amdgpu_dpm_enter_umd_state(adev);
+
+       ret = amdgpu_asic_update_umd_stable_pstate(adev, true);
+       if (ret)
+               goto exit_umd_state_unlock;
+
+       ret = pp_funcs->reapply_performance_level(adev->powerplay.pp_handle,
+                                                    level);
+       if (ret)
+               goto exit_umd_state_unlock;
+
+unlock:
+       mutex_unlock(&adev->pm.mutex);
+
+       return ret;
+
+exit_umd_state_unlock:
+       amdgpu_dpm_exit_umd_state(adev);
+       mutex_unlock(&adev->pm.mutex);
+
+       return ret;
+}
+
 int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
                                       enum amd_dpm_forced_level level)  { diff 
--git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h 
b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 72b13ece22ef..ba8624d28f28 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -484,6 +484,7 @@ void amdgpu_dpm_set_power_state(struct amdgpu_device *adev, 
 enum amd_dpm_forced_level amdgpu_dpm_get_performance_level(struct 
amdgpu_device *adev);  int amdgpu_dpm_force_performance_level(struct 
amdgpu_device *adev,
                                       enum amd_dpm_forced_level level);
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev);
 int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,
                                 struct pp_states_info *states);
 int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, diff --git 
a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c 
b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
index 239c64b8edca..bddf75dfbc22 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -376,6 +376,20 @@ static int pp_dpm_force_performance_level(void *handle,
        return 0;
 }

+static int
+pp_dpm_reapply_performance_level(void *handle,
+                                enum amd_dpm_forced_level level)
+{
+       struct pp_hwmgr *hwmgr = handle;
+
+       if (!hwmgr || !hwmgr->pm_en || level != hwmgr->dpm_level)
+               return -EINVAL;
+
+       hwmgr->request_dpm_level = level;
+
+       return hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE,
+NULL); }
+
 static enum amd_dpm_forced_level pp_dpm_get_performance_level(
                                                                void *handle)
 {
@@ -1536,6 +1550,7 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
        .load_firmware = pp_dpm_load_fw,
        .wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
        .force_performance_level = pp_dpm_force_performance_level,
+       .reapply_performance_level = pp_dpm_reapply_performance_level,
        .get_performance_level = pp_dpm_get_performance_level,
        .get_current_power_state = pp_dpm_get_current_power_state,
        .dispatch_tasks = pp_dpm_dispatch_tasks, diff --git 
a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c 
b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 3cdf7d1e8d5b..b8057878a400 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -2398,6 +2398,13 @@ static int smu_set_powergating_state(struct 
amdgpu_ip_block *ip_block,
        return 0;
 }

+static void smu_enter_umd_pstate(struct smu_context *smu) {
+       smu_gpo_control(smu, false);
+       smu_gfx_ulv_control(smu, false);
+       smu_deep_sleep_control(smu, false);
+}
+
 static int smu_enable_umd_pstate(void *handle,
                      enum amd_dpm_forced_level *level)  { @@ -2416,9 +2423,7 
@@ static int smu_enable_umd_pstate(void *handle,
                /* enter umd pstate, save current level, disable gfx cg*/
                if (*level & profile_mode_mask) {
                        smu_dpm_ctx->saved_dpm_level = smu_dpm_ctx->dpm_level;
-                       smu_gpo_control(smu, false);
-                       smu_gfx_ulv_control(smu, false);
-                       smu_deep_sleep_control(smu, false);
+                       smu_enter_umd_pstate(smu);
                        amdgpu_asic_update_umd_stable_pstate(smu->adev, true);
                }
        } else {
@@ -2477,7 +2482,8 @@ static void smu_power_profile_mode_put(struct smu_context 
*smu,

 static int smu_adjust_power_state_dynamic(struct smu_context *smu,
                                          enum amd_dpm_forced_level level,
-                                         bool skip_display_settings)
+                                         bool skip_display_settings,
+                                         bool force_performance_level)
 {
        int ret = 0;
        struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm); @@ -2504,7 
+2510,7 @@ static int smu_adjust_power_state_dynamic(struct smu_context *smu,
                }
        }

-       if (smu_dpm_ctx->dpm_level != level) {
+       if (force_performance_level || smu_dpm_ctx->dpm_level != level) {
                ret = smu_asic_set_performance_level(smu, level);
                if (ret) {
                        if (ret == -EOPNOTSUPP)
@@ -2521,8 +2527,11 @@ static int smu_adjust_power_state_dynamic(struct 
smu_context *smu,
        }

        if (smu_dpm_ctx->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL &&
-           smu_dpm_ctx->dpm_level != AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM)
-               smu_bump_power_profile_mode(smu, NULL, 0);
+           smu_dpm_ctx->dpm_level != AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM) {
+               ret = smu_bump_power_profile_mode(smu, NULL, 0);
+               if (ret)
+                       return ret;
+       }

        return ret;
 }
@@ -2541,13 +2550,13 @@ static int smu_handle_task(struct smu_context *smu,
                ret = smu_pre_display_config_changed(smu);
                if (ret)
                        return ret;
-               ret = smu_adjust_power_state_dynamic(smu, level, false);
+               ret = smu_adjust_power_state_dynamic(smu, level, false, false);
                break;
        case AMD_PP_TASK_COMPLETE_INIT:
-               ret = smu_adjust_power_state_dynamic(smu, level, true);
+               ret = smu_adjust_power_state_dynamic(smu, level, true, false);
                break;
        case AMD_PP_TASK_READJUST_POWER_STATE:
-               ret = smu_adjust_power_state_dynamic(smu, level, true);
+               ret = smu_adjust_power_state_dynamic(smu, level, true, false);
                break;
        default:
                break;
@@ -2676,6 +2685,37 @@ static int smu_force_performance_level(void *handle,
        return ret;
 }

+static int
+smu_reapply_performance_level(void *handle,
+                             enum amd_dpm_forced_level level) {
+       struct smu_context *smu = handle;
+       struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+
+       if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
+               return -EOPNOTSUPP;
+
+       if (!smu->is_apu && !smu_dpm_ctx->dpm_context)
+               return -EINVAL;
+
+       switch (level) {
+       case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+       case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (smu_dpm_ctx->dpm_level != level)
+               return -EINVAL;
+
+       smu_enter_umd_pstate(smu);
+
+       return smu_adjust_power_state_dynamic(smu, level, true, true); }
+
 static int smu_set_display_count(void *handle, uint32_t count)  {
        struct smu_context *smu = handle;
@@ -4062,6 +4102,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
        .force_clock_level       = smu_force_ppclk_levels,
        .emit_clock_levels       = smu_emit_ppclk_levels,
        .force_performance_level = smu_force_performance_level,
+       .reapply_performance_level = smu_reapply_performance_level,
        .read_sensor             = smu_read_sensor,
        .get_apu_thermal_limit       = smu_get_apu_thermal_limit,
        .set_apu_thermal_limit       = smu_set_apu_thermal_limit,
--
2.55.0

Reply via email to