On 12-Oct-26 6:57 AM, Kevin Wang wrote:
PMFW can report clocks outside or between advertised DPM levels.

Report a clock below DPM0 as a special active S level.
Mark the nearest level active for a discrete DPM table.
Clamp a clock above the table range to the maximum DPM level.
Keep reporting the actual clock for a fine-grained DPM table.

Signed-off-by: Kevin Wang <[email protected]>
---
  drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c | 35 ++++++++++++++++++++------
  1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c 
b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
index 361657c19ea3..8cf616517ddd 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
@@ -1391,11 +1391,12 @@ int smu_cmn_print_dpm_clk_levels(struct smu_context 
*smu,
                                 uint32_t cur_clk, char *buf, int *offset)
  {
        uint32_t min_clk, max_clk, level_index, count;
+       uint32_t nearest_level = 0, nearest_diff = U32_MAX;
        uint32_t freq_values[3];
+       uint32_t clk_diff, level_clk;
        int size, lvl, i;
        bool is_fine_grained;
        bool is_deep_sleep;
-       bool freq_match;
if (!dpm_table || !buf)
                return -EINVAL;
@@ -1407,23 +1408,41 @@ int smu_cmn_print_dpm_clk_levels(struct smu_context 
*smu,
        min_clk = SMU_DPM_TABLE_MIN(dpm_table);
        max_clk = SMU_DPM_TABLE_MAX(dpm_table);
- /* Deep sleep - current clock < min_clock/2, TBD: cur_clk = 0 as GFXOFF */
-       is_deep_sleep = cur_clk < min_clk / 2;
+       /* A clock below DPM0 is reported as a special deep-sleep level. */
+       is_deep_sleep = cur_clk < min_clk;
        if (is_deep_sleep) {
                size += sysfs_emit_at(buf, size, "S: %uMhz *\n", cur_clk);
                level_index = 1;
        }
+ /*
+        * Discrete tables mark the level nearest to the current clock.  Clamp
+        * clocks above the advertised range to the last level.  Fine-grained
+        * tables keep reporting the actual current clock between min and max.
+        */
        if (!is_fine_grained || count == 1) {
+               if (!is_deep_sleep) {
+                       if (cur_clk >= max_clk) {
+                               nearest_level = count - 1;
+                       } else {
+                               for (i = 0; i < count; i++) {
+                                       level_clk = 
dpm_table->dpm_levels[i].value;
+                                       clk_diff = cur_clk > level_clk ?
+                                                  cur_clk - level_clk : 
level_clk - cur_clk;
+                                       if (clk_diff < nearest_diff) {
+                                               nearest_diff = clk_diff;
+                                               nearest_level = i;
+                                       }
+                               }
+                       }
+               }
+
                for (i = 0; i < count; i++) {
-                       freq_match = !is_deep_sleep &&
-                                    smu_cmn_freqs_match(
-                                            cur_clk,
-                                            dpm_table->dpm_levels[i].value);

By this change, if this function is no longer used, it can be removed as well.

Thanks,
Lijo

                        size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
                                              level_index + i,
                                              dpm_table->dpm_levels[i].value,
-                                             freq_match ? "*" : "");
+                                             (!is_deep_sleep &&
+                                              i == nearest_level) ? "*" : "");
                }
        } else {
                count = 2;

Reply via email to