]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/amd/pm/smu7: Fix SMU7 voltage dependency on display clock
authorTimur Kristóf <timur.kristof@gmail.com>
Sun, 29 Mar 2026 16:03:00 +0000 (18:03 +0200)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 30 Mar 2026 20:45:22 +0000 (16:45 -0400)
The DCE (display controller engine) requires a minimum voltage
in order to function correctly, depending on which clock level
it currently uses.

Add a new table that contains display clock frequency levels
and the corresponding required voltages. The clock frequency
levels are taken from DC (and the old radeon driver's voltage
dependency table for CI in cases where its values were lower).
The voltage levels are taken from the following function:
phm_initializa_dynamic_state_adjustment_rule_settings().
Furthermore, in case of CI, call smu7_patch_vddc() on the new
table to account for leakage voltage (like in radeon).

Use the display clock value from amd_pp_display_configuration
to look up the voltage level needed by the DCE. Send the
voltage to the SMU via the PPSMC_MSG_VddC_Request command.

The previous implementation of this feature was non-functional
because it relied on a "dal_power_level" field which was never
assigned; and it was not at all implemented for CI ASICs.

I verified this on a Radeon R9 M380 which previously booted to
a black screen with DC enabled (default since Linux 6.19), but
now works correctly.

Fixes: 599a7e9fe1b6 ("drm/amd/powerplay: implement smu7 hwmgr to manager asics with smu ip version 7.")
Signed-off-by: Timur Kristóf <timur.kristof@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h

index e38222877f7ef2ebe24d140fcbd074739e1dc354..563482f5d35fd4718542a6274eb08dc65adc3bd8 100644 (file)
@@ -2802,6 +2802,10 @@ static int smu7_patch_dependency_tables_with_leakage(struct pp_hwmgr *hwmgr)
        if (tmp)
                return -EINVAL;
 
+       tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_display_clock);
+       if (tmp)
+               return -EINVAL;
+
        tmp = smu7_patch_vce_vddc(hwmgr, hwmgr->dyn_state.vce_clock_voltage_dependency_table);
        if (tmp)
                return -EINVAL;
@@ -2885,6 +2889,8 @@ static int smu7_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
 {
        kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
        hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+       kfree(hwmgr->dyn_state.vddc_dependency_on_display_clock);
+       hwmgr->dyn_state.vddc_dependency_on_display_clock = NULL;
        kfree(hwmgr->backend);
        hwmgr->backend = NULL;
 
@@ -2955,6 +2961,51 @@ static int smu7_update_edc_leakage_table(struct pp_hwmgr *hwmgr)
        return ret;
 }
 
+static int smu7_init_voltage_dependency_on_display_clock_table(struct pp_hwmgr *hwmgr)
+{
+       struct phm_clock_voltage_dependency_table *table;
+
+       if (!amdgpu_device_ip_get_ip_block(hwmgr->adev, AMD_IP_BLOCK_TYPE_DCE))
+               return 0;
+
+       table = kzalloc(struct_size(table, entries, 4), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       if (hwmgr->chip_id >= CHIP_POLARIS10) {
+               table->entries[0].clk = 38918;
+               table->entries[1].clk = 45900;
+               table->entries[2].clk = 66700;
+               table->entries[3].clk = 113200;
+
+               table->entries[0].v = 700;
+               table->entries[1].v = 740;
+               table->entries[2].v = 800;
+               table->entries[3].v = 900;
+       } else {
+               if (hwmgr->chip_family == AMDGPU_FAMILY_CZ) {
+                       table->entries[0].clk = 35200;
+                       table->entries[1].clk = 35200;
+                       table->entries[2].clk = 46700;
+                       table->entries[3].clk = 64300;
+               } else {
+                       table->entries[0].clk = 0;
+                       table->entries[1].clk = 35200;
+                       table->entries[2].clk = 54000;
+                       table->entries[3].clk = 62500;
+               }
+
+               table->entries[0].v = 0;
+               table->entries[1].v = 720;
+               table->entries[2].v = 810;
+               table->entries[3].v = 900;
+       }
+
+       table->count = 4;
+       hwmgr->dyn_state.vddc_dependency_on_display_clock = table;
+       return 0;
+}
+
 static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
 {
        struct amdgpu_device *adev = hwmgr->adev;
@@ -2983,6 +3034,10 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
                smu7_get_elb_voltages(hwmgr);
        }
 
+       result = smu7_init_voltage_dependency_on_display_clock_table(hwmgr);
+       if (result)
+               goto fail;
+
        if (hwmgr->pp_table_version == PP_TABLE_V1) {
                smu7_complete_dependency_tables(hwmgr);
                smu7_set_private_data_based_on_pptable_v1(hwmgr);
@@ -3079,13 +3134,40 @@ static int smu7_force_dpm_highest(struct pp_hwmgr *hwmgr)
        return 0;
 }
 
+static uint32_t smu7_lookup_vddc_from_dispclk(struct pp_hwmgr *hwmgr)
+{
+       const struct amd_pp_display_configuration *cfg = hwmgr->display_config;
+       const struct phm_clock_voltage_dependency_table *vddc_dep_on_dispclk =
+                       hwmgr->dyn_state.vddc_dependency_on_display_clock;
+       uint32_t i;
+
+       if (!vddc_dep_on_dispclk || !vddc_dep_on_dispclk->count ||
+           !cfg || !cfg->num_display || !cfg->display_clk)
+               return 0;
+
+       /* Start from 1 because ClocksStateUltraLow should not be used according to DC. */
+       for (i = 1; i < vddc_dep_on_dispclk->count; ++i)
+               if (vddc_dep_on_dispclk->entries[i].clk >= cfg->display_clk)
+                       return vddc_dep_on_dispclk->entries[i].v;
+
+       return vddc_dep_on_dispclk->entries[vddc_dep_on_dispclk->count - 1].v;
+}
+
+static void smu7_apply_minimum_dce_voltage_request(struct pp_hwmgr *hwmgr)
+{
+       uint32_t req_vddc = smu7_lookup_vddc_from_dispclk(hwmgr);
+
+       smum_send_msg_to_smc_with_parameter(hwmgr,
+                       PPSMC_MSG_VddC_Request,
+                       req_vddc * VOLTAGE_SCALE,
+                       NULL);
+}
+
 static int smu7_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
 {
        struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
 
-       if (hwmgr->pp_table_version == PP_TABLE_V1)
-               phm_apply_dal_min_voltage_request(hwmgr);
-/* TO DO  for v0 iceland and Ci*/
+       smu7_apply_minimum_dce_voltage_request(hwmgr);
 
        if (!data->sclk_dpm_key_disabled) {
                if (data->dpm_level_enable_mask.sclk_dpm_enable_mask)
index c661185753b428041b192680c35b255836eb14ed..2f49c95342a142be9ce12427d3b27ad6a0a4485b 100644 (file)
@@ -631,6 +631,7 @@ struct phm_dynamic_state_info {
        struct phm_clock_voltage_dependency_table *vddci_dependency_on_mclk;
        struct phm_clock_voltage_dependency_table *vddc_dependency_on_mclk;
        struct phm_clock_voltage_dependency_table *mvdd_dependency_on_mclk;
+       struct phm_clock_voltage_dependency_table *vddc_dependency_on_display_clock;
        struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl;
        struct phm_clock_array                    *valid_sclk_values;
        struct phm_clock_array                    *valid_mclk_values;