AMD General

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

-----Original Message-----
From: Wang, Kevin <[email protected]>
Sent: Friday, October 16, 2026 9:26 AM
To: [email protected]
Cc: Deucher, Alexander <[email protected]>; Zhang, Hawking 
<[email protected]>; Feng, Kenneth <[email protected]>; Stamenkovic, 
Strahinja <[email protected]>
Subject: [PATCH v2] drm/amd/pm: reapply umd profile state after recovery

Resume and full GPU reset retain the selected UMD profile level in driver state.
The GFX and PMFW state behind it is reinitialized.

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.

v2:
- Reapply only GFX CG to preserve the profile-owned GFX PG reference.
- Fall back to AUTO when reapply fails without failing device recovery.
- Propagate PowerPlay DPM errors before accepting AUTO fallback.
- Limit workload-profile error reporting to the swSMU reapply path.

This covers:
- PCI runtime resume
- Full GPU reset recovery
- System resume, including S3 and s2idle

Signed-off-by: Kevin Wang <[email protected]>
Reviewed-by: Kenneth Feng <[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           | 85 ++++++++++++++++++
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h       |  1 +
 .../gpu/drm/amd/pm/powerplay/amd_powerplay.c  | 33 +++++--
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 87 +++++++++++++++++--
 7 files changed, 201 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 96cb4d34157e..b74f9136aa2e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -2774,6 +2774,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..75d8bb4868e8 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -1082,6 +1082,91 @@ 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, fallback_ret;
+
+       if (!pp_funcs || !pp_funcs->reapply_performance_level ||
+           !pp_funcs->force_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) {
+               dev_warn(adev->dev,
+                        "Cannot restore UMD profile while thermal control is 
active\n");
+               ret = 0;
+               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;
+       }
+
+       /* The active profile already owns the GFX PG reference. */
+       ret = amdgpu_device_ip_set_clockgating_state(adev, 
AMD_IP_BLOCK_TYPE_GFX,
+                                             AMD_CG_STATE_UNGATE);
+       if (ret)
+               goto fallback_auto;
+
+       ret = amdgpu_asic_update_umd_stable_pstate(adev, true);
+       if (ret)
+               goto fallback_auto;
+
+       ret = pp_funcs->reapply_performance_level(adev->powerplay.pp_handle,
+                                                    level);
+       if (ret)
+               goto fallback_auto;
+
+unlock:
+       mutex_unlock(&adev->pm.mutex);
+
+       return ret;
+
+fallback_auto:
+       dev_warn(adev->dev, "Failed to restore UMD profile state: %d\n", ret);
+
+       /* Restore normal CG before releasing the profile's PG reference. */
+       amdgpu_asic_update_umd_stable_pstate(adev, false);
+       amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
+                                              AMD_CG_STATE_GATE);
+       mutex_unlock(&adev->pm.mutex);
+
+       mutex_lock(&adev->pm.stable_pstate_ctx_lock);
+       fallback_ret = amdgpu_dpm_force_performance_level(adev,
+                                                  AMD_DPM_FORCED_LEVEL_AUTO);
+       if (fallback_ret)
+               dev_warn(adev->dev, "Failed to fall back to AUTO performance 
level: %d\n",
+                        fallback_ret);
+       else
+               adev->pm.stable_pstate_ctx = NULL;
+       mutex_unlock(&adev->pm.stable_pstate_ctx_lock);
+
+       return 0;
+}
+
 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..7b0ff5a0993e 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -358,24 +358,46 @@ static void pp_dpm_en_umd_pstate(struct pp_hwmgr  *hwmgr,
        }
 }

-static int pp_dpm_force_performance_level(void *handle,
-                                       enum amd_dpm_forced_level level)
+static int pp_dpm_apply_performance_level(struct pp_hwmgr *hwmgr,
+                                         enum amd_dpm_forced_level level,
+                                         bool reapply)
 {
-       struct pp_hwmgr *hwmgr = handle;
+       int ret;

        if (!hwmgr || !hwmgr->pm_en)
                return -EINVAL;

-       if (level == hwmgr->dpm_level)
+       if (!reapply && level == hwmgr->dpm_level)
                return 0;

        pp_dpm_en_umd_pstate(hwmgr, &level);
        hwmgr->request_dpm_level = level;
-       hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+       ret = hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+       if (ret)
+               return ret;
+
+       ret = phm_force_dpm_levels(hwmgr, level);
+       if (ret)
+               return ret;
+
+       hwmgr->dpm_level = level;

        return 0;
 }

+static int pp_dpm_force_performance_level(void *handle,
+                                       enum amd_dpm_forced_level level)
+{
+       return pp_dpm_apply_performance_level(handle, level, false); }
+
+static int
+pp_dpm_reapply_performance_level(void *handle,
+                                        enum amd_dpm_forced_level level)
+{
+       return pp_dpm_apply_performance_level(handle, level, true); }
+
 static enum amd_dpm_forced_level pp_dpm_get_performance_level(
                                                                void *handle)
 {
@@ -1536,6 +1558,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..1915413ccac3 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)
@@ -2527,6 +2533,39 @@ static int smu_adjust_power_state_dynamic(struct 
smu_context *smu,
        return ret;
 }

+static int
+smu_reapply_power_state_dynamic(struct smu_context *smu,
+                               enum amd_dpm_forced_level level)
+{
+       struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+       int ret;
+
+       ret = smu_apply_clocks_adjust_rules(smu);
+       if (ret) {
+               dev_err(smu->adev->dev, "Failed to apply clocks adjust rules!");
+               return ret;
+       }
+
+       ret = smu_asic_set_performance_level(smu, level);
+       if (ret) {
+               if (ret == -EOPNOTSUPP)
+                       dev_info(smu->adev->dev, "set performance level %d not 
supported",
+                                level);
+               else
+                       dev_err(smu->adev->dev, "Failed to set performance 
level %d",
+                               level);
+               return ret;
+       }
+
+       smu_dpm_ctx->dpm_level = level;
+
+       if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL ||
+           smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM)
+               return 0;
+
+       return smu_bump_power_profile_mode(smu, NULL, 0); }
+
 static int smu_handle_task(struct smu_context *smu,
                           enum amd_dpm_forced_level level,
                           enum amd_pp_task task_id)
@@ -2541,13 +2580,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 +2715,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_reapply_power_state_dynamic(smu, level); }
+
 static int smu_set_display_count(void *handle, uint32_t count)  {
        struct smu_context *smu = handle;
@@ -4062,6 +4132,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