Refactor smu_cmn_print_dpm_clk_levels() to build clock entries before
emitting sysfs output.

For discrete DPM tables, mark the level closest to the reported current
clock. This avoids losing the active '*' marker when the SMU-reported
clock does not fall within the previous fixed tolerance.

Keep fine-grained output explicit by reporting the current clock on an
'F' line, and keep deep sleep represented by the 'S' line without marking
a discrete level.

Active marker placement:

| Mode         | '*' marker location       | Reason                    |
| ------------ | ------------------------- | ------------------------- |
| discrete     | closest/current DPM level | entries are real levels   |
| fine-grained | 'F:' current clock line   | min/max are range bounds  |
| deep sleep   | 'S:' line                 | outside normal DPM range  |

Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5295
Signed-off-by: Yang Wang <[email protected]>
---
 drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c | 148 +++++++++++++++++--------
 1 file changed, 101 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c 
b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
index d365f06ac1ac..872c0328f290 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
@@ -1376,77 +1376,131 @@ void smu_cmn_reset_custom_level(struct smu_context 
*smu)
        pstate_table->uclk_pstate.custom.max = 0;
 }
 
-static inline bool smu_cmn_freqs_match(uint32_t freq1, uint32_t freq2)
+struct smu_clk_print_entry {
+       uint32_t freq;
+       bool selected;
+};
+
+static inline uint32_t smu_cmn_freq_distance(uint32_t freq1, uint32_t freq2)
+{
+       return freq1 > freq2 ? freq1 - freq2 : freq2 - freq1;
+}
+
+static inline uint32_t smu_cmn_get_dpm_level_count(struct smu_dpm_table 
*dpm_table)
+{
+       return min_t(uint32_t, dpm_table->count, SMU_MAX_DPM_LEVELS);
+}
+
+static uint32_t smu_cmn_get_closest_clk_level(struct smu_dpm_table *dpm_table, 
uint32_t cur_clk)
+{
+       uint32_t min_distance, distance;
+       uint32_t closest_level = 0;
+       uint32_t count;
+       uint32_t i;
+
+       count = smu_cmn_get_dpm_level_count(dpm_table);
+       if (!count)
+               return SMU_MAX_DPM_LEVELS;
+
+       min_distance = smu_cmn_freq_distance(cur_clk, 
dpm_table->dpm_levels[0].value);
+       for (i = 1; i < count; i++) {
+               distance = smu_cmn_freq_distance(cur_clk, 
dpm_table->dpm_levels[i].value);
+               if (distance < min_distance) {
+                       min_distance = distance;
+                       closest_level = i;
+               }
+       }
+
+       return closest_level;
+}
+
+static inline int smu_cmn_emit_clk_line(char *buf, int size,
+                                       int level_index, uint32_t freq, bool 
selected)
+{
+       return sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+                            level_index, freq, selected ? "*" : "");
+}
+
+static void smu_cmn_build_fine_grained_levels(uint32_t min_clk, uint32_t 
max_clk,
+                                             struct smu_clk_print_entry 
*entries,
+                                             uint32_t *entry_count)
+{
+       *entry_count = 2;
+       entries[0].freq = min_clk;
+       entries[0].selected = false;
+       entries[1].freq = max_clk;
+       entries[1].selected = false;
+}
+
+static void smu_cmn_build_discrete_levels(struct smu_dpm_table *dpm_table,
+                                         uint32_t selected_level,
+                                         struct smu_clk_print_entry *entries,
+                                         uint32_t *entry_count)
+{
+       uint32_t i;
+
+       *entry_count = smu_cmn_get_dpm_level_count(dpm_table);
+
+       for (i = 0; i < *entry_count; i++) {
+               entries[i].freq = dpm_table->dpm_levels[i].value;
+               entries[i].selected = (i == selected_level);
+       }
+}
+
+static int smu_cmn_emit_clk_prefix(char *buf, int size,
+                                  bool is_fine_grained, bool is_deep_sleep,
+                                  uint32_t cur_clk)
 {
-       /* Frequencies within 25 MHz are considered equal */
-       return (abs((int)freq1 - (int)freq2) <= 25);
+       if (is_deep_sleep)
+               size += sysfs_emit_at(buf, size, "S: %uMhz *\n", cur_clk);
+       else if (is_fine_grained)
+               size += sysfs_emit_at(buf, size, "F: %uMhz *\n", cur_clk);
+
+       return size;
 }
 
 int smu_cmn_print_dpm_clk_levels(struct smu_context *smu,
                                 struct smu_dpm_table *dpm_table,
                                 uint32_t cur_clk, char *buf, int *offset)
 {
-       uint32_t min_clk, max_clk, level_index, count;
-       uint32_t freq_values[3];
-       int size, lvl, i;
+       struct smu_clk_print_entry entries[SMU_MAX_DPM_LEVELS];
+       uint32_t min_clk, max_clk, count, entry_count = 0;
+       uint32_t selected_level = SMU_MAX_DPM_LEVELS;
+       int size, i;
        bool is_fine_grained;
        bool is_deep_sleep;
-       bool freq_match;
 
        if (!dpm_table || !buf)
                return -EINVAL;
 
-       level_index = 0;
        size = *offset;
-       count = dpm_table->count;
        is_fine_grained = dpm_table->flags & SMU_DPM_TABLE_FINE_GRAINED;
-       min_clk = SMU_DPM_TABLE_MIN(dpm_table);
-       max_clk = SMU_DPM_TABLE_MAX(dpm_table);
+       count = smu_cmn_get_dpm_level_count(dpm_table);
+       min_clk = count ? dpm_table->dpm_levels[0].value : 0;
+       max_clk = count ? dpm_table->dpm_levels[count - 1].value : 0;
 
        /* Deep sleep - current clock < min_clock/2, TBD: cur_clk = 0 as GFXOFF 
*/
        is_deep_sleep = cur_clk < min_clk / 2;
-       if (is_deep_sleep) {
-               size += sysfs_emit_at(buf, size, "S: %uMhz *\n", cur_clk);
-               level_index = 1;
-       }
 
        if (!is_fine_grained || count == 1) {
-               for (i = 0; i < count; i++) {
-                       freq_match = !is_deep_sleep &&
-                                    smu_cmn_freqs_match(
-                                            cur_clk,
-                                            dpm_table->dpm_levels[i].value);
-                       size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
-                                             level_index + i,
-                                             dpm_table->dpm_levels[i].value,
-                                             freq_match ? "*" : "");
+               if (!is_deep_sleep) {
+                       selected_level =
+                               smu_cmn_get_closest_clk_level(dpm_table, 
cur_clk);
                }
+               smu_cmn_build_discrete_levels(dpm_table, selected_level,
+                                                     entries, &entry_count);
        } else {
-               count = 2;
-               freq_values[0] = min_clk;
-               freq_values[1] = max_clk;
+               smu_cmn_build_fine_grained_levels(min_clk, max_clk,
+                                                 entries, &entry_count);
+       }
 
-               if (!is_deep_sleep) {
-                       if (smu_cmn_freqs_match(cur_clk, min_clk)) {
-                               lvl = 0;
-                       } else if (smu_cmn_freqs_match(cur_clk, max_clk)) {
-                               lvl = 1;
-                       } else {
-                               /* NOTE: use index '1' to show current clock 
value */
-                               lvl = 1;
-                               count = 3;
-                               freq_values[1] = cur_clk;
-                               freq_values[2] = max_clk;
-                       }
-               }
+       size = smu_cmn_emit_clk_prefix(buf, size, is_fine_grained,
+                                      is_deep_sleep, cur_clk);
 
-               for (i = 0; i < count; i++) {
-                       size += sysfs_emit_at(
-                               buf, size, "%d: %uMhz %s\n", level_index + i,
-                               freq_values[i],
-                               (!is_deep_sleep && i == lvl) ? "*" : "");
-               }
-       }
+       for (i = 0; i < entry_count; i++)
+               size += smu_cmn_emit_clk_line(buf, size, i,
+                                            entries[i].freq,
+                                            entries[i].selected);
 
        *offset = size;
 
-- 
2.47.3

Reply via email to