On 09-Jun-26 12:21 PM, Yang Wang wrote:
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);
What about keeping the else part as C: <cur_clk> in all cases - instead
of just fine grained? * indicates the closest level matched and cur_clk
will give the exact frequency.
Thanks,
Lijo
+
+ 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;