On Thu, Aug 24, 2023 at 10:02 PM Quan, Evan <Evan.Quan@xxxxxxx> wrote: > > [AMD Official Use Only - General] > > > -----Original Message----- > > From: Alex Deucher <alexdeucher@xxxxxxxxx> > > Sent: Thursday, August 24, 2023 10:51 PM > > To: Quan, Evan <Evan.Quan@xxxxxxx> > > Cc: amd-gfx@xxxxxxxxxxxxxxxxxxxxx; Deucher, Alexander > > <Alexander.Deucher@xxxxxxx> > > Subject: Re: [V2 3/8] drm/amd/pm: add fan mode OD setting support for > > SMU13 > > > > On Wed, Aug 23, 2023 at 10:28 PM Quan, Evan <Evan.Quan@xxxxxxx> > > wrote: > > > > > > [AMD Official Use Only - General] > > > > > > > -----Original Message----- > > > > From: Alex Deucher <alexdeucher@xxxxxxxxx> > > > > Sent: Wednesday, August 23, 2023 9:29 PM > > > > To: Quan, Evan <Evan.Quan@xxxxxxx> > > > > Cc: amd-gfx@xxxxxxxxxxxxxxxxxxxxx; Deucher, Alexander > > > > <Alexander.Deucher@xxxxxxx> > > > > Subject: Re: [V2 3/8] drm/amd/pm: add fan mode OD setting support > > > > for > > > > SMU13 > > > > > > > > On Wed, Aug 23, 2023 at 2:45 AM Evan Quan <evan.quan@xxxxxxx> > > > > wrote: > > > > > > > > > > Add SMU13 fan mode OD setting support. > > > > > > > > > > Signed-off-by: Evan Quan <evan.quan@xxxxxxx> > > > > > -- > > > > > v1->v2: > > > > > - add missing kerneldoc for the new interface(Alex) > > > > > --- > > > > > Documentation/gpu/amdgpu/thermal.rst | 6 + > > > > > .../gpu/drm/amd/include/kgd_pp_interface.h | 4 +- > > > > > drivers/gpu/drm/amd/pm/amdgpu_pm.c | 200 > > > > +++++++++++++++++- > > > > > drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 4 + > > > > > drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 2 + > > > > > drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h | 1 + > > > > > .../drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 35 ++- > > > > > .../drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 35 ++- > > > > > 8 files changed, 279 insertions(+), 8 deletions(-) > > > > > > > > > > diff --git a/Documentation/gpu/amdgpu/thermal.rst > > > > > b/Documentation/gpu/amdgpu/thermal.rst > > > > > index 5e27e4eb3959..8757ec7f0136 100644 > > > > > --- a/Documentation/gpu/amdgpu/thermal.rst > > > > > +++ b/Documentation/gpu/amdgpu/thermal.rst > > > > > @@ -64,6 +64,12 @@ gpu_metrics > > > > > .. kernel-doc:: drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > :doc: gpu_metrics > > > > > > > > > > +fan_mode > > > > > +-------- > > > > > + > > > > > +.. kernel-doc:: drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > + :doc: fan_mode > > > > > + > > > > > GFXOFF > > > > > ====== > > > > > > > > > > diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h > > > > > b/drivers/gpu/drm/amd/include/kgd_pp_interface.h > > > > > index 84c5224d994c..020c9ce1f735 100644 > > > > > --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h > > > > > +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h > > > > > @@ -113,6 +113,7 @@ enum pp_clock_type { > > > > > OD_RANGE, > > > > > OD_VDDGFX_OFFSET, > > > > > OD_CCLK, > > > > > + OD_FAN_MODE, > > > > > }; > > > > > > > > > > enum amd_pp_sensors { > > > > > @@ -186,7 +187,8 @@ enum PP_OD_DPM_TABLE_COMMAND { > > > > > PP_OD_EDIT_VDDC_CURVE, > > > > > PP_OD_RESTORE_DEFAULT_TABLE, > > > > > PP_OD_COMMIT_DPM_TABLE, > > > > > - PP_OD_EDIT_VDDGFX_OFFSET > > > > > + PP_OD_EDIT_VDDGFX_OFFSET, > > > > > + PP_OD_EDIT_FAN_MODE, > > > > > }; > > > > > > > > > > struct pp_states_info { > > > > > diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > b/drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > index beb3303fc832..d53d60903fe9 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c > > > > > @@ -3383,7 +3383,205 @@ static const struct attribute_group > > > > *hwmon_groups[] = { > > > > > NULL > > > > > }; > > > > > > > > > > -static struct od_feature_set amdgpu_od_set; > > > > > +static int amdgpu_retrieve_od_settings(struct amdgpu_device *adev, > > > > > + enum pp_clock_type od_type, > > > > > + char *buf) { > > > > > + int size = 0; > > > > > + int ret; > > > > > + > > > > > + if (amdgpu_in_reset(adev)) > > > > > + return -EPERM; > > > > > + if (adev->in_suspend && !adev->in_runpm) > > > > > + return -EPERM; > > > > > + > > > > > + ret = pm_runtime_get_sync(adev->dev); > > > > > + if (ret < 0) { > > > > > + pm_runtime_put_autosuspend(adev->dev); > > > > > + return ret; > > > > > + } > > > > > + > > > > > + size = amdgpu_dpm_print_clock_levels(adev, od_type, buf); > > > > > + if (size == 0) > > > > > + size = sysfs_emit(buf, "\n"); > > > > > + > > > > > + pm_runtime_mark_last_busy(adev->dev); > > > > > + pm_runtime_put_autosuspend(adev->dev); > > > > > + > > > > > + return size; > > > > > +} > > > > > + > > > > > +static int parse_input_od_command_lines(const char *buf, > > > > > + size_t count, > > > > > + u32 *type, > > > > > + long *params, > > > > > + uint32_t *num_of_params) { > > > > > + const char delimiter[3] = {' ', '\n', '\0'}; > > > > > + uint32_t parameter_size = 0; > > > > > + char buf_cpy[128] = {0}; > > > > > + char *tmp_str, *sub_str; > > > > > + int ret; > > > > > + > > > > > + if (count > sizeof(buf_cpy) - 1) > > > > > + return -EINVAL; > > > > > + > > > > > + memcpy(buf_cpy, buf, count); > > > > > + tmp_str = buf_cpy; > > > > > + > > > > > + /* skip heading spaces */ > > > > > + while (isspace(*tmp_str)) > > > > > + tmp_str++; > > > > > + > > > > > + switch (*tmp_str) { > > > > > + case 'c': > > > > > + *type = PP_OD_COMMIT_DPM_TABLE; > > > > > + return 0; > > > > > + default: > > > > > + break; > > > > > + } > > > > > + > > > > > + while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { > > > > > + if (strlen(sub_str) == 0) > > > > > + continue; > > > > > + > > > > > + ret = kstrtol(sub_str, 0, ¶ms[parameter_size]); > > > > > + if (ret) > > > > > + return -EINVAL; > > > > > + parameter_size++; > > > > > + > > > > > + while (isspace(*tmp_str)) > > > > > + tmp_str++; > > > > > + } > > > > > + > > > > > + *num_of_params = parameter_size; > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static int > > > > > +amdgpu_distribute_custom_od_settings(struct amdgpu_device *adev, > > > > > + enum PP_OD_DPM_TABLE_COMMAND cmd_type, > > > > > + const char *in_buf, > > > > > + size_t count) { > > > > > + uint32_t parameter_size = 0; > > > > > + long parameter[64]; > > > > > + int ret; > > > > > + > > > > > + if (amdgpu_in_reset(adev)) > > > > > + return -EPERM; > > > > > + if (adev->in_suspend && !adev->in_runpm) > > > > > + return -EPERM; > > > > > + > > > > > + ret = parse_input_od_command_lines(in_buf, > > > > > + count, > > > > > + &cmd_type, > > > > > + parameter, > > > > > + ¶meter_size); > > > > > + if (ret) > > > > > + return ret; > > > > > + > > > > > + ret = pm_runtime_get_sync(adev->dev); > > > > > + if (ret < 0) > > > > > + goto err_out0; > > > > > + > > > > > + ret = amdgpu_dpm_odn_edit_dpm_table(adev, > > > > > + cmd_type, > > > > > + parameter, > > > > > + parameter_size); > > > > > + if (ret) > > > > > + goto err_out1; > > > > > + > > > > > + if (cmd_type == PP_OD_COMMIT_DPM_TABLE) { > > > > > + ret = amdgpu_dpm_dispatch_task(adev, > > > > > + AMD_PP_TASK_READJUST_POWER_STATE, > > > > > + NULL); > > > > > + if (ret) > > > > > + goto err_out1; > > > > > + } > > > > > + > > > > > + pm_runtime_mark_last_busy(adev->dev); > > > > > + pm_runtime_put_autosuspend(adev->dev); > > > > > + > > > > > + return count; > > > > > + > > > > > +err_out1: > > > > > + pm_runtime_mark_last_busy(adev->dev); > > > > > +err_out0: > > > > > + pm_runtime_put_autosuspend(adev->dev); > > > > > + > > > > > + return ret; > > > > > +} > > > > > + > > > > > +/** > > > > > + * DOC: fan_mode > > > > > + * > > > > > + * The amdgpu driver provides a sysfs API for checking and > > > > > +adjusting the fan > > > > > + * control mode. > > > > > + * > > > > > + * Reading back the file shows you current fan control mode (e.g. '0' > > > > > +for auto > > > > > + * mode and '1' for manual mode) and permitted settings if changable. > > > > > > > > typo: changeable > > > > > > > > Also what does manual vs auto mean in this case? Is the idea that > > > > you need to switch to manual mode before you make any other > > > > adjustments (fan curve or > > > > acoustic) or is this some independent setting? > > > Auto mode here means PMFW handling the fan speed control in response to > > the asic temperature. While driver/user can do some fine tuning like acoustic > > limit/target, fan target-temperature and minimum pwm. > > > Manual mode here means the fan curve setting specifically. Driver/user can > > customize how the fan speed in response to the asic temperature. > > > The two modes are mutually exclusive. Switching to one mode will invalid all > > those settings installed for the other mode. > > > Per current implementation for SMU13.0.0 and 13.0.7, user does not need > > to adjust the fan mode manually. Driver will handle that. For example, on > > user's setting of fan curve, driver will handle the fan mode switching to manual > > mode. > > > > > > > If I understand things correctly, the driver will automatically set manual mode > > if the user adjusts the fan curve? If so, do we need a separate attribute for > > manual vs automatic? For the documentation, I'd like to see all of the fan > > options and what sets are required to enable them and what combinations are > > allowed. > > > > 1. temp/pwm fan curve - requires setting manual mode. Mutually exclusive > > with 2, 3. > > 2. acoustic targets - requires setting manual mode. Mutually exclusive with 1, > > 3. > > 3. temperature targets - requires setting manual mode. Mutually exclusive > > with 1, 2. > > 4. minimum fan speed - requires setting manual mode. works with 1,2,3. > > > > If the first 3 are mutually exclusive, then maybe we need a fan mode attribute > > with the following options: > > 0. auto - let the SMU manage the fan > > 1. manual - temp/pwm curve > > 2. manual - acoustic targets > > 3. manual - temp targets > [Quan, Evan] No, not such complicated. There are only two fan control mode: auto and manual. > The settings working under manual mode: temp/pwm curve settings > The settings working under auto mode: all others (acoustic limit/target, temperature target, minimum pwm...) > And those settings for auto mode can co-work with each other. They affect the auto fan control via different manners and are not mutually exclusive. > > > > If they are not mutually exclusive, then how do they interact? E.g., what > > happens if you set a custom temp/pwm curve and acoustic targets and they > > are not compatible? Does that make sense? > [Quan, Evan] For the sequence above: > 1. set a custom temp/pwm curve > - this will switch the fan control to manual mode in order to make the settings to take effect > - this will invalid all other settings working under auto mode only > 2. acoustic targets > - this will switch the fan control to auto mode in order to make the setting to take effect > - this will invalid the temp/pwm curve settings working under manual mode only > Thanks for clarifying. Can you include this information in the kerneldoc? Also, I think we should either: 1. make the auto/manual required to select enable their respective subfeatures. (e.g., fail to set the fan curve if auto and vice versa for the other features) 2. remove the auto/manual attribute and make whatever was last set take priority. and document it. I think 1 is the most obvious from a user perspective, but I don't have that strong of an opinion. Alex > Evan > > > > Alex > > > > > > > > > Please check whether the explanations above make sense to you. I can add > > them in the code comment part. > > > > > > Evan > > > > > > > > Alex > > > > > > > > > + * > > > > > + * Writing an integer to the file, sets the fan control mode > > correspondingly. > > > > > + * > > > > > + * When you have finished the editing, write "c" (commit) to the > > > > > +file to commit > > > > > + * your changes. > > > > > + */ > > > > > +static ssize_t fan_mode_show(struct kobject *kobj, > > > > > + struct kobj_attribute *attr, > > > > > + char *buf) { > > > > > + struct od_kobj *container = container_of(kobj, struct od_kobj, kobj); > > > > > + struct amdgpu_device *adev = (struct amdgpu_device > > > > > +*)container->priv; > > > > > + > > > > > + return (ssize_t)amdgpu_retrieve_od_settings(adev, > > > > > +OD_FAN_MODE, buf); } > > > > > + > > > > > +static ssize_t fan_mode_store(struct kobject *kobj, > > > > > + struct kobj_attribute *attr, > > > > > + const char *buf, > > > > > + size_t count) { > > > > > + struct od_kobj *container = container_of(kobj, struct od_kobj, kobj); > > > > > + struct amdgpu_device *adev = (struct amdgpu_device > > > > > +*)container->priv; > > > > > + > > > > > + return (ssize_t)amdgpu_distribute_custom_od_settings(adev, > > > > > + PP_OD_EDIT_FAN_MODE, > > > > > + buf, > > > > > + > > > > > + count); } > > > > > + > > > > > +static umode_t fan_mode_visible(struct amdgpu_device *adev) { > > > > > + umode_t umode = 0000; > > > > > + > > > > > + if (adev->pm.od_feature_mask & > > > > OD_OPS_SUPPORT_FAN_MODE_RETRIEVE) > > > > > + umode |= S_IRUSR | S_IRGRP | S_IROTH; > > > > > + > > > > > + if (adev->pm.od_feature_mask & > > OD_OPS_SUPPORT_FAN_MODE_SET) > > > > > + umode |= S_IWUSR; > > > > > + > > > > > + return umode; > > > > > +} > > > > > + > > > > > +static struct od_feature_set amdgpu_od_set = { > > > > > + .containers = { > > > > > + [0] = { > > > > > + .name = "fan_ctrl", > > > > > + .sub_feature = { > > > > > + [0] = { > > > > > + .name = "fan_mode", > > > > > + .ops = { > > > > > + .is_visible = fan_mode_visible, > > > > > + .show = fan_mode_show, > > > > > + .store = fan_mode_store, > > > > > + }, > > > > > + }, > > > > > + }, > > > > > + }, > > > > > + }, > > > > > +}; > > > > > > > > > > static void od_kobj_release(struct kobject *kobj) { diff --git > > > > > a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h > > > > > b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h > > > > > index 4cab6a2efb63..b54f84e2408a 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h > > > > > +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h > > > > > @@ -314,6 +314,9 @@ struct config_table_setting > > > > > uint16_t fclk_average_tau; }; > > > > > > > > > > +#define OD_OPS_SUPPORT_FAN_MODE_RETRIEVE BIT(0) > > > > > +#define OD_OPS_SUPPORT_FAN_MODE_SET BIT(1) > > > > > + > > > > > struct amdgpu_pm { > > > > > struct mutex mutex; > > > > > u32 current_sclk; > > > > > @@ -368,6 +371,7 @@ struct amdgpu_pm { > > > > > enum amdgpu_runpm_mode rpm_mode; > > > > > > > > > > struct list_head od_kobj_list; > > > > > + uint32_t od_feature_mask; > > > > > }; > > > > > > > > > > int amdgpu_dpm_read_sensor(struct amdgpu_device *adev, enum > > > > > amd_pp_sensors sensor, diff --git > > > > > a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > > > > > b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > > > > > index f005a90c35af..01ccfd219d6b 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > > > > > +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > > > > > @@ -2481,6 +2481,8 @@ static enum smu_clk_type > > > > smu_convert_to_smuclk(enum pp_clock_type type) > > > > > clk_type = SMU_OD_VDDGFX_OFFSET; break; > > > > > case OD_CCLK: > > > > > clk_type = SMU_OD_CCLK; break; > > > > > + case OD_FAN_MODE: > > > > > + clk_type = SMU_OD_FAN_MODE; break; > > > > > default: > > > > > clk_type = SMU_CLK_COUNT; break; > > > > > } > > > > > diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h > > > > > b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h > > > > > index e57265cf637c..498000850212 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h > > > > > +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h > > > > > @@ -280,6 +280,7 @@ enum smu_clk_type { > > > > > SMU_OD_VDDC_CURVE, > > > > > SMU_OD_RANGE, > > > > > SMU_OD_VDDGFX_OFFSET, > > > > > + SMU_OD_FAN_MODE, > > > > > SMU_CLK_COUNT, > > > > > }; > > > > > > > > > > diff --git > > a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c > > > > > b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c > > > > > index 1041ba22f8f8..45b42e43f9a3 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c > > > > > +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c > > > > > @@ -101,6 +101,7 @@ > > > > > #define PP_OD_FEATURE_UCLK_FMIN 2 > > > > > #define PP_OD_FEATURE_UCLK_FMAX 3 > > > > > #define PP_OD_FEATURE_GFX_VF_CURVE 4 > > > > > +#define PP_OD_FEATURE_FAN_MODE 5 > > > > > > > > > > #define LINK_SPEED_MAX 3 > > > > > > > > > > @@ -1110,6 +1111,10 @@ static void > > > > smu_v13_0_0_get_od_setting_limits(struct smu_context *smu, > > > > > od_min_setting = overdrive_lowerlimits- > > > > >VoltageOffsetPerZoneBoundary; > > > > > od_max_setting = overdrive_upperlimits- > > > > >VoltageOffsetPerZoneBoundary; > > > > > break; > > > > > + case PP_OD_FEATURE_FAN_MODE: > > > > > + od_min_setting = overdrive_lowerlimits->FanMode; > > > > > + od_max_setting = overdrive_upperlimits->FanMode; > > > > > + break; > > > > > default: > > > > > od_min_setting = od_max_setting = INT_MAX; > > > > > break; > > > > > @@ -1329,6 +1334,16 @@ static int > > > > > smu_v13_0_0_print_clk_levels(struct > > > > smu_context *smu, > > > > > od_table- > > > > >OverDriveTable.VoltageOffsetPerZoneBoundary[0]); > > > > > break; > > > > > > > > > > + case SMU_OD_FAN_MODE: > > > > > + if (!smu_v13_0_0_is_od_feature_supported(smu, > > > > > + PP_OD_FEATURE_FAN_CURVE_BIT)) > > > > > + break; > > > > > + > > > > > + size += sysfs_emit_at(buf, size, "OD_FAN_MODE:\n"); > > > > > + size += sysfs_emit_at(buf, size, "%d\n", > > > > > + (int)od_table->OverDriveTable.FanMode); > > > > > + break; > > > > > + > > > > > case SMU_OD_RANGE: > > > > > if (!smu_v13_0_0_is_od_feature_supported(smu, > > > > PP_OD_FEATURE_GFXCLK_BIT) && > > > > > !smu_v13_0_0_is_od_feature_supported(smu, > > > > > PP_OD_FEATURE_UCLK_BIT) && @@ -1789,6 +1804,15 @@ static > > ssize_t > > > > smu_v13_0_0_get_gpu_metrics(struct smu_context *smu, > > > > > return sizeof(struct gpu_metrics_v1_3); } > > > > > > > > > > +static void smu_v13_0_0_set_supported_od_feature_mask(struct > > > > > +smu_context *smu) { > > > > > + struct amdgpu_device *adev = smu->adev; > > > > > + > > > > > + if (smu_v13_0_0_is_od_feature_supported(smu, > > > > > + PP_OD_FEATURE_FAN_CURVE_BIT)) > > > > > + adev->pm.od_feature_mask |= > > > > > +OD_OPS_SUPPORT_FAN_MODE_RETRIEVE; } > > > > > + > > > > > static int smu_v13_0_0_set_default_od_settings(struct smu_context > > > > > *smu) { > > > > > OverDriveTableExternal_t *od_table = @@ -1838,8 +1862,12 > > > > >@@ static int smu_v13_0_0_set_default_od_settings(struct > > smu_context *smu) > > > > > for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++) > > > > > user_od_table- > > > > >OverDriveTable.VoltageOffsetPerZoneBoundary[i] = > > > > > > > > > > user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i]; > > > > > + user_od_table->OverDriveTable.FanMode = > > > > > + user_od_table_bak.OverDriveTable.FanMode; > > > > > } > > > > > > > > > > + smu_v13_0_0_set_supported_od_feature_mask(smu); > > > > > + > > > > > return 0; > > > > > } > > > > > > > > > > @@ -1850,9 +1878,10 @@ static int > > > > smu_v13_0_0_restore_user_od_settings(struct smu_context *smu) > > > > > OverDriveTableExternal_t *user_od_table = table_context- > > > > >user_overdrive_table; > > > > > int res; > > > > > > > > > > - user_od_table->OverDriveTable.FeatureCtrlMask = 1U << > > > > PP_OD_FEATURE_GFXCLK_BIT | > > > > > - 1U << PP_OD_FEATURE_UCLK_BIT | > > > > > - 1U << > > PP_OD_FEATURE_GFX_VF_CURVE_BIT; > > > > > + user_od_table->OverDriveTable.FeatureCtrlMask = > > > > BIT(PP_OD_FEATURE_GFXCLK_BIT) | > > > > > + BIT(PP_OD_FEATURE_UCLK_BIT) | > > > > > + > > > > > + BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) | > > > > > + > > > > > + BIT(PP_OD_FEATURE_FAN_CURVE_BIT); > > > > > res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table); > > > > > user_od_table->OverDriveTable.FeatureCtrlMask = 0; > > > > > if (res == 0) > > > > > diff --git > > a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c > > > > > b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c > > > > > index 328765f89c94..28c2e4b0e55d 100644 > > > > > --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c > > > > > +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c > > > > > @@ -77,6 +77,7 @@ > > > > > #define PP_OD_FEATURE_UCLK_FMIN 2 > > > > > #define PP_OD_FEATURE_UCLK_FMAX 3 > > > > > #define PP_OD_FEATURE_GFX_VF_CURVE 4 > > > > > +#define PP_OD_FEATURE_FAN_MODE 5 > > > > > > > > > > #define LINK_SPEED_MAX 3 > > > > > > > > > > @@ -1096,6 +1097,10 @@ static void > > > > smu_v13_0_7_get_od_setting_limits(struct smu_context *smu, > > > > > od_min_setting = overdrive_lowerlimits- > > > > >VoltageOffsetPerZoneBoundary; > > > > > od_max_setting = overdrive_upperlimits- > > > > >VoltageOffsetPerZoneBoundary; > > > > > break; > > > > > + case PP_OD_FEATURE_FAN_MODE: > > > > > + od_min_setting = overdrive_lowerlimits->FanMode; > > > > > + od_max_setting = overdrive_upperlimits->FanMode; > > > > > + break; > > > > > default: > > > > > od_min_setting = od_max_setting = INT_MAX; > > > > > break; > > > > > @@ -1315,6 +1320,16 @@ static int > > > > > smu_v13_0_7_print_clk_levels(struct > > > > smu_context *smu, > > > > > od_table- > > > > >OverDriveTable.VoltageOffsetPerZoneBoundary[0]); > > > > > break; > > > > > > > > > > + case SMU_OD_FAN_MODE: > > > > > + if (!smu_v13_0_7_is_od_feature_supported(smu, > > > > > + PP_OD_FEATURE_FAN_CURVE_BIT)) > > > > > + break; > > > > > + > > > > > + size += sysfs_emit_at(buf, size, "OD_FAN_MODE:\n"); > > > > > + size += sysfs_emit_at(buf, size, "%d\n", > > > > > + (int)od_table->OverDriveTable.FanMode); > > > > > + break; > > > > > + > > > > > case SMU_OD_RANGE: > > > > > if (!smu_v13_0_7_is_od_feature_supported(smu, > > > > PP_OD_FEATURE_GFXCLK_BIT) && > > > > > !smu_v13_0_7_is_od_feature_supported(smu, > > > > > PP_OD_FEATURE_UCLK_BIT) && @@ -1770,6 +1785,15 @@ static > > ssize_t > > > > smu_v13_0_7_get_gpu_metrics(struct smu_context *smu, > > > > > return sizeof(struct gpu_metrics_v1_3); } > > > > > > > > > > +static void smu_v13_0_7_set_supported_od_feature_mask(struct > > > > > +smu_context *smu) { > > > > > + struct amdgpu_device *adev = smu->adev; > > > > > + > > > > > + if (smu_v13_0_7_is_od_feature_supported(smu, > > > > > + PP_OD_FEATURE_FAN_CURVE_BIT)) > > > > > + adev->pm.od_feature_mask |= > > > > > +OD_OPS_SUPPORT_FAN_MODE_RETRIEVE; } > > > > > + > > > > > static int smu_v13_0_7_set_default_od_settings(struct smu_context > > > > > *smu) { > > > > > OverDriveTableExternal_t *od_table = @@ -1819,8 +1843,12 > > > > >@@ static int smu_v13_0_7_set_default_od_settings(struct > > smu_context *smu) > > > > > for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++) > > > > > user_od_table- > > > > >OverDriveTable.VoltageOffsetPerZoneBoundary[i] = > > > > > > > > > > user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i]; > > > > > + user_od_table->OverDriveTable.FanMode = > > > > > + user_od_table_bak.OverDriveTable.FanMode; > > > > > } > > > > > > > > > > + smu_v13_0_7_set_supported_od_feature_mask(smu); > > > > > + > > > > > return 0; > > > > > } > > > > > > > > > > @@ -1831,9 +1859,10 @@ static int > > > > smu_v13_0_7_restore_user_od_settings(struct smu_context *smu) > > > > > OverDriveTableExternal_t *user_od_table = table_context- > > > > >user_overdrive_table; > > > > > int res; > > > > > > > > > > - user_od_table->OverDriveTable.FeatureCtrlMask = 1U << > > > > PP_OD_FEATURE_GFXCLK_BIT | > > > > > - 1U << PP_OD_FEATURE_UCLK_BIT | > > > > > - 1U << > > PP_OD_FEATURE_GFX_VF_CURVE_BIT; > > > > > + user_od_table->OverDriveTable.FeatureCtrlMask = > > > > BIT(PP_OD_FEATURE_GFXCLK_BIT) | > > > > > + BIT(PP_OD_FEATURE_UCLK_BIT) | > > > > > + > > > > > + BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) | > > > > > + > > > > > + BIT(PP_OD_FEATURE_FAN_CURVE_BIT); > > > > > res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table); > > > > > user_od_table->OverDriveTable.FeatureCtrlMask = 0; > > > > > if (res == 0) > > > > > -- > > > > > 2.34.1 > > > > >