The DPM user profile records driver-programmed policy, not the effective
PMFW limits. Replaying it after recovery can restore stale policy.

Maintain a PMFW PPT snapshot:
- Query supported PPT controllers before suspend and mode1 reset.
- Save successful queries with the current OS AC/DC source.
- Clear the selected source mask before capture to prevent stale replay.
- Refresh the OS power source before recovery.
- Synchronize the current source with PMFW before restoring PPT limits.
- Restore PPT limits only when the snapshot source still matches.
- Do not replay PPT policy on normal AC/DC changes.

Capture reset state before DPM and PMFW teardown.
Add debug logging for the snapshot, restore order, and PMFW AC/DC sync.

Signed-off-by: Yang Wang <[email protected]>
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c           |   2 +-
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 152 +++++++++++++-----
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |   8 +-
 3 files changed, 117 insertions(+), 45 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index ce526db4d24a..c3688b3b12cc 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -508,7 +508,7 @@ void amdgpu_pm_acpi_event_handler(struct amdgpu_device 
*adev)
                        amdgpu_dpm_notify_ac_dc(adev);
 
                if (is_support_sw_smu(adev))
-                       smu_set_ac_dc(adev->powerplay.pp_handle, true);
+                       smu_set_ac_dc(adev->powerplay.pp_handle);
 
                mutex_unlock(&adev->pm.mutex);
        }
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c 
b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index bfe2c0bf426d..e5cb8d0420ab 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -489,44 +489,122 @@ static void smu_set_user_clk_dependencies(struct 
smu_context *smu, enum smu_clk_
                return;
 }
 
-static void smu_restore_ppt_limits(struct smu_context *smu,
-                                  bool restore_defaults)
+static void smu_save_ppt_limits(struct smu_context *smu)
 {
+       struct amdgpu_device *adev = smu->adev;
        enum smu_power_src_type power_source;
-       struct smu_ppt_limit_range *range;
-       uint32_t restore_mask;
-       uint32_t limit;
+       u32 limit;
        int i, ret;
 
-       power_source = smu->adev->pm.ac_power ?
+       /* AC/DC is maintained by the OS and driver power-source events. */
+       smu->ppt_limits.saved_ac_power = adev->pm.ac_power;
+       power_source = adev->pm.ac_power ?
                SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC;
-       restore_mask = smu->user_dpm_profile.ppt_limit_user_mask[power_source] &
-               smu->ppt_limits.supported_mask;
-       if (!restore_mask && !restore_defaults)
-               return;
-
-       smu->user_dpm_profile.flags |= SMU_DPM_USER_PROFILE_RESTORE;
+       smu->ppt_limits.saved_mask[power_source] = 0;
+       dev_dbg(adev->dev, "PPT snapshot: source=%s\n",
+               adev->pm.ac_power ? "AC" : "DC");
 
        for (i = SMU_PPT_LIMIT_PPT0; i < SMU_LIMIT_TYPE_COUNT; i++) {
                if (!(smu->ppt_limits.supported_mask & BIT(i)))
                        continue;
 
-               if (restore_mask & BIT(i)) {
-                       limit = 
smu->user_dpm_profile.ppt_limits[power_source][i];
-               } else if (restore_defaults) {
-                       range = &smu->ppt_limits.range[power_source][i];
-                       limit = range->default_value;
-               } else {
+               ret = smu_get_asic_ppt_limit(smu, i, &limit);
+               if (ret) {
+                       dev_err(adev->dev,
+                               "Failed to save PPT%d limit: %d\n", i, ret);
                        continue;
                }
 
+               smu->ppt_limits.saved_values[power_source][i] = limit;
+               smu->ppt_limits.saved_mask[power_source] |= BIT(i);
+               dev_dbg(adev->dev, "PPT%d snapshot: source=%s limit=%u\n",
+                       i, adev->pm.ac_power ? "AC" : "DC", limit);
+       }
+
+       dev_dbg(adev->dev, "PPT snapshot complete: source=%s mask=%#x\n",
+               adev->pm.ac_power ? "AC" : "DC",
+               smu->ppt_limits.saved_mask[power_source]);
+}
+
+static int smu_restore_ac_dc(struct smu_context *smu, u32 *restore_mask)
+{
+       struct amdgpu_device *adev = smu->adev;
+       enum smu_power_src_type current_source;
+       enum smu_power_src_type saved_source;
+
+       *restore_mask = 0;
+
+       if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) {
+               /* The power source may change while the device is suspended. */
+               adev->pm.ac_power = power_supply_is_system_supplied() > 0;
+               current_source = adev->pm.ac_power ?
+                       SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC;
+               saved_source = smu->ppt_limits.saved_ac_power ?
+                       SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC;
+               *restore_mask = smu->ppt_limits.saved_mask[saved_source] &
+                       smu->ppt_limits.supported_mask;
+               dev_dbg(adev->dev,
+                       "PPT restore: saved source=%s mask=%#x, current 
source=%s\n",
+                       smu->ppt_limits.saved_ac_power ? "AC" : "DC",
+                       *restore_mask, adev->pm.ac_power ? "AC" : "DC");
+               /*
+                * A snapshot is valid only for the source under which it was
+                * captured. Do not replay an AC snapshot after switching to DC,
+                * or vice versa. The current OS power source takes precedence.
+                */
+               if (current_source != saved_source) {
+                       dev_dbg(adev->dev,
+                               "Discard PPT snapshot after source change: %s 
to %s\n",
+                               smu->ppt_limits.saved_ac_power ? "AC" : "DC",
+                               adev->pm.ac_power ? "AC" : "DC");
+                       *restore_mask = 0;
+               }
+       }
+
+       /*
+        * NOTE: Synchronize PMFW with the OS AC/DC source before restoring PPT
+        * limits so both sides use the same power policy. GPIO-controlled
+        * platforms perform the synchronization in PMFW.
+        */
+       return smu_set_ac_dc(smu);
+}
+
+static int smu_restore_ppt_limits(struct smu_context *smu)
+{
+       struct amdgpu_device *adev = smu->adev;
+       enum smu_power_src_type power_source;
+       u32 restore_mask;
+       u32 limit;
+       int i, ret;
+
+       ret = smu_restore_ac_dc(smu, &restore_mask);
+       if (ret)
+               return ret;
+       if (!restore_mask)
+               return 0;
+
+       power_source = adev->pm.ac_power ?
+               SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC;
+
+       dev_dbg(adev->dev, "PPT restore: source=%s mask=%#x\n",
+               adev->pm.ac_power ? "AC" : "DC", restore_mask);
+
+       for (i = SMU_PPT_LIMIT_PPT0; i < SMU_LIMIT_TYPE_COUNT; i++) {
+               if (!(restore_mask & BIT(i)))
+                       continue;
+
+               limit = smu->ppt_limits.saved_values[power_source][i];
                ret = smu_set_ppt_limit(smu, i, limit);
                if (ret)
-                       dev_err(smu->adev->dev,
+                       dev_err(adev->dev,
                                "Failed to restore PPT%d limit: %d\n", i, ret);
+               else
+                       dev_dbg(adev->dev,
+                               "PPT%d restored: source=%s limit=%u\n", i,
+                               adev->pm.ac_power ? "AC" : "DC", limit);
        }
 
-       smu->user_dpm_profile.flags &= ~SMU_DPM_USER_PROFILE_RESTORE;
+       return 0;
 }
 
 /**
@@ -954,15 +1032,9 @@ static int smu_late_init(struct amdgpu_ip_block *ip_block)
                return ret;
        }
 
-       /*
-        * Explicitly notify PMFW the power mode the system in. Since
-        * the PMFW may boot the ASIC with a different mode.
-        * For those supporting ACDC switch via gpio, PMFW will
-        * handle the switch automatically. Driver involvement
-        * is unnecessary.
-        */
-       adev->pm.ac_power = power_supply_is_system_supplied() > 0;
-       smu_set_ac_dc(smu, false);
+       ret = smu_restore_ppt_limits(smu);
+       if (ret)
+               return ret;
 
        if ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 1)) ||
            (amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 3)))
@@ -997,8 +1069,6 @@ static int smu_late_init(struct amdgpu_ip_block *ip_block)
                return ret;
        }
 
-       if (adev->in_suspend || amdgpu_reset_in_recovery(adev))
-               smu_restore_ppt_limits(smu, false);
        smu_restore_dpm_user_profile(smu);
 
        return 0;
@@ -2202,6 +2272,9 @@ static int smu_hw_fini(struct amdgpu_ip_block *ip_block)
        if (!smu->pm_enabled)
                return 0;
 
+       if (amdgpu_in_reset(adev))
+               smu_save_ppt_limits(smu);
+
        adev->pm.dpm_enabled = false;
 
        ret = smu_smc_hw_cleanup(smu);
@@ -2263,6 +2336,8 @@ static int smu_suspend(struct amdgpu_ip_block *ip_block)
        if (!smu->pm_enabled)
                return 0;
 
+       smu_save_ppt_limits(smu);
+
        adev->pm.dpm_enabled = false;
 
        ret = smu_smc_hw_cleanup(smu);
@@ -2778,7 +2853,7 @@ static int smu_set_watermarks_for_clock_ranges(void 
*handle,
        return smu_set_watermarks_table(smu, clock_ranges);
 }
 
-int smu_set_ac_dc(struct smu_context *smu, bool restore_ppt_policy)
+int smu_set_ac_dc(struct smu_context *smu)
 {
        int ret = 0;
 
@@ -2796,11 +2871,12 @@ int smu_set_ac_dc(struct smu_context *smu, bool 
restore_ppt_policy)
                                smu->adev->pm.ac_power ? "AC" : "DC");
                        return ret;
                }
+       } else {
+               dev_dbg(smu->adev->dev,
+                       "PMFW controls %s mode through GPIO\n",
+                       smu->adev->pm.ac_power ? "AC" : "DC");
        }
 
-       if (restore_ppt_policy)
-               smu_restore_ppt_limits(smu, true);
-
        return 0;
 }
 
@@ -3057,12 +3133,6 @@ static int smu_set_ppt_limit(void *handle, uint32_t 
limit_type, uint32_t limit)
        ret = smu->ppt_funcs->set_ppt_limit(smu, limit_type, limit);
        if (ret)
                return ret;
-       if (!(smu->user_dpm_profile.flags & SMU_DPM_USER_PROFILE_RESTORE)) {
-               smu->user_dpm_profile.ppt_limits[power_source][limit_type] = 
limit;
-               smu->user_dpm_profile.ppt_limit_user_mask[power_source] |=
-                       BIT(limit_type);
-       }
-
        return 0;
 }
 
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h 
b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index d30951121e1f..9f85b302060e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -238,6 +238,10 @@ struct smu_ppt_limit_range {
 struct smu_ppt_limit_context {
        struct smu_ppt_limit_range
                range[SMU_POWER_SOURCE_COUNT][SMU_LIMIT_TYPE_COUNT];
+       /* PMFW state saved before suspend or mode1 reset. */
+       u32 saved_values[SMU_POWER_SOURCE_COUNT][SMU_LIMIT_TYPE_COUNT];
+       u32 saved_mask[SMU_POWER_SOURCE_COUNT];
+       bool saved_ac_power;
        uint32_t supported_mask;
 };
 
@@ -251,8 +255,6 @@ enum smu_memory_pool_size {
 
 struct smu_user_dpm_profile {
        uint32_t fan_mode;
-       uint32_t ppt_limits[SMU_POWER_SOURCE_COUNT][SMU_LIMIT_TYPE_COUNT];
-       uint32_t ppt_limit_user_mask[SMU_POWER_SOURCE_COUNT];
        uint32_t fan_speed_pwm;
        uint32_t fan_speed_rpm;
        uint32_t flags;
@@ -1954,7 +1956,7 @@ int smu_set_soft_freq_range(struct smu_context *smu, enum 
pp_clock_type clk_type
 
 int smu_set_gfx_power_up_by_imu(struct smu_context *smu);
 
-int smu_set_ac_dc(struct smu_context *smu, bool restore_ppt_policy);
+int smu_set_ac_dc(struct smu_context *smu);
 
 int smu_set_xgmi_plpd_mode(struct smu_context *smu,
                           enum pp_xgmi_plpd_mode mode);
-- 
2.54.0

Reply via email to