If ddc fails, presumably the i2c mux (and hopefully the signal mux) are switched to the other GPU so don't fetch the edid from the vbios so that the connector reports disconnected.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_connectors.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 300c4b3..26baa9c 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -322,6 +322,12 @@ static void radeon_connector_get_edid(struct drm_connector *connector) }
if (!radeon_connector->edid) { + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + return; + if (rdev->is_atom_bios) { /* some laptops provide a hardcoded edid in rom for LCDs */ if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) || @@ -826,6 +832,8 @@ static int radeon_lvds_mode_valid(struct drm_connector *connector, static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = radeon_best_single_encoder(connector); enum drm_connector_status ret = connector_status_disconnected; @@ -842,7 +850,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; - + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; }
/* check for edid as well */ @@ -1589,6 +1601,11 @@ radeon_dp_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; } /* eDP is always DP */ radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
Always need to set bit 0 of RLC_CGTT_MGCG_OVERRIDE to avoid unreliable doorbell updates in some cases.
Signed-off-by: Alex Deucher alexander.deucher@amd.com Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/cik.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 57a359d..20fb1ad 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -6323,6 +6323,7 @@ static void cik_enable_mgcg(struct radeon_device *rdev, bool enable) }
orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE); + data |= 0x00000001; data &= 0xfffffffd; if (orig != data) WREG32(RLC_CGTT_MGCG_OVERRIDE, data); @@ -6354,7 +6355,7 @@ static void cik_enable_mgcg(struct radeon_device *rdev, bool enable) } } else { orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE); - data |= 0x00000002; + data |= 0x00000003; if (orig != data) WREG32(RLC_CGTT_MGCG_OVERRIDE, data);
Value needs to be swapped on BE.
Signed-off-by: Alex Deucher alexander.deucher@amd.com Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/r600_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index f6309bd..b5c73df 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -1256,7 +1256,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); rdev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = - ppt->usMaximumPowerDeliveryLimit; + le16_to_cpu(ppt->usMaximumPowerDeliveryLimit); pt = &ppt->power_tune_table; } else { ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *)
Enable smc fan control for SI boards. Should reduce the fan noise on systems with a higher default fan profile.
v2: disable by default, add rpm controls
bug: https://bugs.freedesktop.org/show_bug.cgi?id=73338
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ppsmc.h | 5 + drivers/gpu/drm/radeon/r600_dpm.h | 3 + drivers/gpu/drm/radeon/si_dpm.c | 330 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/si_dpm.h | 5 + drivers/gpu/drm/radeon/sid.h | 40 ++++- drivers/gpu/drm/radeon/sislands_smc.h | 25 +++ 6 files changed, 401 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 11c0e4d..0c4eaa6 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -56,6 +56,9 @@ #define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20 #define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS 0x40
+#define FDO_MODE_HARDWARE 0 +#define FDO_MODE_PIECE_WISE_LINEAR 1 + #define PPSMC_Result_OK ((uint8_t)0x01) #define PPSMC_Result_Failed ((uint8_t)0xFF)
@@ -79,6 +82,8 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_DisableCac ((uint8_t)0x54) #define PPSMC_TDPClampingActive ((uint8_t)0x59) #define PPSMC_TDPClampingInactive ((uint8_t)0x5A) +#define PPSMC_StartFanControl ((uint8_t)0x5B) +#define PPSMC_StopFanControl ((uint8_t)0x5C) #define PPSMC_MSG_NoDisplay ((uint8_t)0x5D) #define PPSMC_MSG_HasDisplay ((uint8_t)0x5E) #define PPSMC_MSG_UVDPowerOFF ((uint8_t)0x60) diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h index 46b9d2a..bd499d7 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.h +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -96,6 +96,9 @@ #define R600_TEMP_RANGE_MIN (90 * 1000) #define R600_TEMP_RANGE_MAX (120 * 1000)
+#define FDO_PWM_MODE_STATIC 1 +#define FDO_PWM_MODE_STATIC_RPM 5 + enum r600_power_level { R600_POWER_LEVEL_LOW = 0, R600_POWER_LEVEL_MEDIUM = 1, diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index b59e1d6..cf4c420 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -3398,6 +3398,15 @@ static int si_process_firmware_header(struct radeon_device *rdev)
ret = si_read_smc_sram_dword(rdev, SISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + SISLANDS_SMC_FIRMWARE_HEADER_fanTable, + &tmp, si_pi->sram_end); + if (ret) + return ret; + + si_pi->fan_table_start = tmp; + + ret = si_read_smc_sram_dword(rdev, + SISLANDS_SMC_FIRMWARE_HEADER_LOCATION + SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable, &tmp, si_pi->sram_end); if (ret) @@ -5825,20 +5834,20 @@ static int si_thermal_enable_alert(struct radeon_device *rdev, if (enable) { PPSMC_Result result;
- thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; - rdev->irq.dpm_thermal = true; + thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + WREG32(CG_THERMAL_INT, thermal_int); + rdev->irq.dpm_thermal = false; result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt); if (result != PPSMC_Result_OK) { DRM_DEBUG_KMS("Could not enable thermal interrupts.\n"); return -EINVAL; } } else { - thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); - rdev->irq.dpm_thermal = false; + thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; + WREG32(CG_THERMAL_INT, thermal_int); + rdev->irq.dpm_thermal = true; }
- WREG32(CG_THERMAL_INT, thermal_int); - return 0; }
@@ -5867,6 +5876,309 @@ static int si_thermal_set_temperature_range(struct radeon_device *rdev, return 0; }
+static void si_fan_ctrl_set_static_mode(struct radeon_device *rdev, u32 mode) +{ + struct si_power_info *si_pi = si_get_pi(rdev); + u32 tmp; + + if (si_pi->fan_ctrl_is_in_default_mode) { + tmp = (RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT; + si_pi->fan_ctrl_default_mode = tmp; + tmp = (RREG32(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT; + si_pi->t_min = tmp; + si_pi->fan_ctrl_is_in_default_mode = false; + } + + tmp = RREG32(CG_FDO_CTRL2) & ~TMIN_MASK; + tmp |= TMIN(0); + WREG32(CG_FDO_CTRL2, tmp); + + tmp = RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK; + tmp |= FDO_PWM_MODE(mode); + WREG32(CG_FDO_CTRL2, tmp); +} + +static int si_thermal_setup_fan_table(struct radeon_device *rdev) +{ + struct si_power_info *si_pi = si_get_pi(rdev); + PP_SIslands_FanTable fan_table = { FDO_MODE_HARDWARE }; + u32 duty100; + u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2; + u16 fdo_min, slope1, slope2; + u32 reference_clock, tmp; + int ret; + u64 tmp64; + + if (!si_pi->fan_table_start) { + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; + } + + duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + + if (duty100 == 0) { + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; + } + + tmp64 = (u64)rdev->pm.dpm.fan.pwm_min * duty100; + do_div(tmp64, 10000); + fdo_min = (u16)tmp64; + + t_diff1 = rdev->pm.dpm.fan.t_med - rdev->pm.dpm.fan.t_min; + t_diff2 = rdev->pm.dpm.fan.t_high - rdev->pm.dpm.fan.t_med; + + pwm_diff1 = rdev->pm.dpm.fan.pwm_med - rdev->pm.dpm.fan.pwm_min; + pwm_diff2 = rdev->pm.dpm.fan.pwm_high - rdev->pm.dpm.fan.pwm_med; + + slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); + slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); + + fan_table.slope1 = cpu_to_be16(slope1); + fan_table.slope2 = cpu_to_be16(slope2); + + fan_table.fdo_min = cpu_to_be16(fdo_min); + + fan_table.hys_down = cpu_to_be16(rdev->pm.dpm.fan.t_hyst); + + fan_table.hys_up = cpu_to_be16(1); + + fan_table.hys_slope = cpu_to_be16(1); + + fan_table.temp_resp_lim = cpu_to_be16(5); + + reference_clock = radeon_get_xclk(rdev); + + fan_table.refresh_period = cpu_to_be32((rdev->pm.dpm.fan.cycle_delay * + reference_clock) / 1600); + + fan_table.fdo_max = cpu_to_be16((u16)duty100); + + tmp = (RREG32(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT; + fan_table.temp_src = (uint8_t)tmp; + + ret = si_copy_bytes_to_smc(rdev, + si_pi->fan_table_start, + (u8 *)(&fan_table), + sizeof(fan_table), + si_pi->sram_end); + + if (ret) { + DRM_ERROR("Failed to load fan table to the SMC."); + rdev->pm.dpm.fan.ucode_fan_control = false; + } + + return 0; +} + +static int si_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev) +{ + PPSMC_Result ret; + + ret = si_send_msg_to_smc(rdev, PPSMC_StartFanControl); + if (ret == PPSMC_Result_OK) + return 0; + else + return -EINVAL; +} + +static int si_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev) +{ + PPSMC_Result ret; + + ret = si_send_msg_to_smc(rdev, PPSMC_StopFanControl); + if (ret == PPSMC_Result_OK) + return 0; + else + return -EINVAL; +} + +#if 0 +static int si_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, + u32 *speed) +{ + u32 duty, duty100; + u64 tmp64; + + if (rdev->pm.no_fan) + return -ENOENT; + + duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + duty = (RREG32(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT; + + if (duty100 == 0) + return -EINVAL; + + tmp64 = (u64)duty * 100; + do_div(tmp64, duty100); + *speed = (u32)tmp64; + + if (*speed > 100) + *speed = 100; + + return 0; +} + +static int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, + u32 speed) +{ + u32 tmp; + u32 duty, duty100; + u64 tmp64; + + if (rdev->pm.no_fan) + return -ENOENT; + + if (speed > 100) + return -EINVAL; + + if (rdev->pm.dpm.fan.ucode_fan_control) + si_fan_ctrl_stop_smc_fan_control(rdev); + + duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + + if (duty100 == 0) + return -EINVAL; + + tmp64 = (u64)speed * duty100; + do_div(tmp64, 100); + duty = (u32)tmp64; + + tmp = RREG32(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK; + tmp |= FDO_STATIC_DUTY(duty); + WREG32(CG_FDO_CTRL0, tmp); + + si_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + + return 0; +} + +static int si_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev, + u32 *speed) +{ + u32 tach_period; + u32 xclk = radeon_get_xclk(rdev); + + if (rdev->pm.no_fan) + return -ENOENT; + + if (rdev->pm.fan_pulses_per_revolution == 0) + return -ENOENT; + + tach_period = (RREG32(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT; + if (tach_period == 0) + return -ENOENT; + + *speed = 60 * xclk * 10000 / tach_period; + + return 0; +} + +static int si_fan_ctrl_set_fan_speed_rpm(struct radeon_device *rdev, + u32 speed) +{ + u32 tach_period, tmp; + u32 xclk = radeon_get_xclk(rdev); + + if (rdev->pm.no_fan) + return -ENOENT; + + if (rdev->pm.fan_pulses_per_revolution == 0) + return -ENOENT; + + if ((speed < rdev->pm.fan_min_rpm) || + (speed > rdev->pm.fan_max_rpm)) + return -EINVAL; + + if (rdev->pm.dpm.fan.ucode_fan_control) + si_fan_ctrl_stop_smc_fan_control(rdev); + + tach_period = 60 * xclk * 10000 / (8 * speed); + tmp = RREG32(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK; + tmp |= TARGET_PERIOD(tach_period); + WREG32(CG_TACH_CTRL, tmp); + + si_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + + return 0; +} +#endif + +static void si_fan_ctrl_set_default_mode(struct radeon_device *rdev) +{ + struct si_power_info *si_pi = si_get_pi(rdev); + u32 tmp; + + if (!si_pi->fan_ctrl_is_in_default_mode) { + tmp = RREG32(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK; + tmp |= FDO_PWM_MODE(si_pi->fan_ctrl_default_mode); + WREG32(CG_FDO_CTRL2, tmp); + + tmp = RREG32(CG_FDO_CTRL2) & TMIN_MASK; + tmp |= TMIN(si_pi->t_min); + WREG32(CG_FDO_CTRL2, tmp); + si_pi->fan_ctrl_is_in_default_mode = true; + } +} + +static void si_thermal_start_smc_fan_control(struct radeon_device *rdev) +{ + if (rdev->pm.dpm.fan.ucode_fan_control) { + si_fan_ctrl_start_smc_fan_control(rdev); + si_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + } +} + +static void si_thermal_initialize(struct radeon_device *rdev) +{ + u32 tmp; + + if (rdev->pm.fan_pulses_per_revolution) { + tmp = RREG32(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK; + tmp |= EDGE_PER_REV(rdev->pm.fan_pulses_per_revolution -1); + WREG32(CG_TACH_CTRL, tmp); + } + + tmp = RREG32(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK; + tmp |= TACH_PWM_RESP_RATE(0x28); + WREG32(CG_FDO_CTRL2, tmp); +} + +static int si_thermal_start_thermal_controller(struct radeon_device *rdev) +{ + int ret; + + si_thermal_initialize(rdev); + ret = si_thermal_set_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + if (ret) + return ret; + ret = si_thermal_enable_alert(rdev, true); + if (ret) + return ret; + if (rdev->pm.dpm.fan.ucode_fan_control) { + ret = si_halt_smc(rdev); + if (ret) + return ret; + ret = si_thermal_setup_fan_table(rdev); + if (ret) + return ret; + ret = si_resume_smc(rdev); + if (ret) + return ret; + si_thermal_start_smc_fan_control(rdev); + } + + return 0; +} + +static void si_thermal_stop_thermal_controller(struct radeon_device *rdev) +{ + if (!rdev->pm.no_fan) { + si_fan_ctrl_set_default_mode(rdev); + si_fan_ctrl_stop_smc_fan_control(rdev); + } +} + int si_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); @@ -5979,6 +6291,8 @@ int si_dpm_enable(struct radeon_device *rdev)
si_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+ si_thermal_start_thermal_controller(rdev); + ni_update_current_ps(rdev, boot_ps);
return 0; @@ -6019,6 +6333,7 @@ void si_dpm_disable(struct radeon_device *rdev)
if (!si_is_smc_running(rdev)) return; + si_thermal_stop_thermal_controller(rdev); si_disable_ulv(rdev); si_clear_vc(rdev); if (pi->thermal_protection) @@ -6557,6 +6872,9 @@ int si_dpm_init(struct radeon_device *rdev) rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc = rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+ si_pi->fan_ctrl_is_in_default_mode = true; + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; }
diff --git a/drivers/gpu/drm/radeon/si_dpm.h b/drivers/gpu/drm/radeon/si_dpm.h index 8b5c06a..d16bb1b 100644 --- a/drivers/gpu/drm/radeon/si_dpm.h +++ b/drivers/gpu/drm/radeon/si_dpm.h @@ -182,6 +182,7 @@ struct si_power_info { u32 dte_table_start; u32 spll_table_start; u32 papm_cfg_table_start; + u32 fan_table_start; /* CAC stuff */ const struct si_cac_config_reg *cac_weights; const struct si_cac_config_reg *lcac_config; @@ -197,6 +198,10 @@ struct si_power_info { /* SVI2 */ u8 svd_gpio_id; u8 svc_gpio_id; + /* fan control */ + bool fan_ctrl_is_in_default_mode; + u32 t_min; + u32 fan_ctrl_default_mode; };
#define SISLANDS_INITIAL_STATE_ARB_INDEX 0 diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 6635da9..c549c16a 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -180,7 +180,10 @@ #define DIG_THERM_DPM(x) ((x) << 14) #define DIG_THERM_DPM_MASK 0x003FC000 #define DIG_THERM_DPM_SHIFT 14 - +#define CG_THERMAL_STATUS 0x704 +#define FDO_PWM_DUTY(x) ((x) << 9) +#define FDO_PWM_DUTY_MASK (0xff << 9) +#define FDO_PWM_DUTY_SHIFT 9 #define CG_THERMAL_INT 0x708 #define DIG_THERM_INTH(x) ((x) << 8) #define DIG_THERM_INTH_MASK 0x0000FF00 @@ -191,6 +194,10 @@ #define THERM_INT_MASK_HIGH (1 << 24) #define THERM_INT_MASK_LOW (1 << 25)
+#define CG_MULT_THERMAL_CTRL 0x710 +#define TEMP_SEL(x) ((x) << 20) +#define TEMP_SEL_MASK (0xff << 20) +#define TEMP_SEL_SHIFT 20 #define CG_MULT_THERMAL_STATUS 0x714 #define ASIC_MAX_TEMP(x) ((x) << 0) #define ASIC_MAX_TEMP_MASK 0x000001ff @@ -199,6 +206,37 @@ #define CTF_TEMP_MASK 0x0003fe00 #define CTF_TEMP_SHIFT 9
+#define CG_FDO_CTRL0 0x754 +#define FDO_STATIC_DUTY(x) ((x) << 0) +#define FDO_STATIC_DUTY_MASK 0x0000000F +#define FDO_STATIC_DUTY_SHIFT 0 +#define CG_FDO_CTRL1 0x758 +#define FMAX_DUTY100(x) ((x) << 0) +#define FMAX_DUTY100_MASK 0x0000000F +#define FMAX_DUTY100_SHIFT 0 +#define CG_FDO_CTRL2 0x75C +#define TMIN(x) ((x) << 0) +#define TMIN_MASK 0x0000000F +#define TMIN_SHIFT 0 +#define FDO_PWM_MODE(x) ((x) << 11) +#define FDO_PWM_MODE_MASK (3 << 11) +#define FDO_PWM_MODE_SHIFT 11 +#define TACH_PWM_RESP_RATE(x) ((x) << 25) +#define TACH_PWM_RESP_RATE_MASK (0x7f << 25) +#define TACH_PWM_RESP_RATE_SHIFT 25 + +#define CG_TACH_CTRL 0x770 +# define EDGE_PER_REV(x) ((x) << 0) +# define EDGE_PER_REV_MASK (0x7 << 0) +# define EDGE_PER_REV_SHIFT 0 +# define TARGET_PERIOD(x) ((x) << 3) +# define TARGET_PERIOD_MASK 0xfffffff8 +# define TARGET_PERIOD_SHIFT 3 +#define CG_TACH_STATUS 0x774 +# define TACH_PERIOD(x) ((x) << 0) +# define TACH_PERIOD_MASK 0xffffffff +# define TACH_PERIOD_SHIFT 0 + #define GENERAL_PWRMGT 0x780 # define GLOBAL_PWRMGT_EN (1 << 0) # define STATIC_PM_EN (1 << 1) diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h index 623a0b1..3c77983 100644 --- a/drivers/gpu/drm/radeon/sislands_smc.h +++ b/drivers/gpu/drm/radeon/sislands_smc.h @@ -245,6 +245,31 @@ typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE; #define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd 0x11c #define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc 0x120
+struct PP_SIslands_FanTable +{ + uint8_t fdo_mode; + uint8_t padding; + int16_t temp_min; + int16_t temp_med; + int16_t temp_max; + int16_t slope1; + int16_t slope2; + int16_t fdo_min; + int16_t hys_up; + int16_t hys_down; + int16_t hys_slope; + int16_t temp_resp_lim; + int16_t temp_curr; + int16_t slope_curr; + int16_t pwm_curr; + uint32_t refresh_period; + int16_t fdo_max; + uint8_t temp_src; + int8_t padding2; +}; + +typedef struct PP_SIslands_FanTable PP_SIslands_FanTable; + #define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16 #define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
Enable smc fan control for CI boards. Should reduce the fan noise on systems with a higher default fan profile.
v2: disable by default, add additional fan setup, rpm control
bug: https://bugs.freedesktop.org/show_bug.cgi?id=73338
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 345 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/ci_dpm.h | 5 + drivers/gpu/drm/radeon/cikd.h | 40 +++- drivers/gpu/drm/radeon/ppsmc.h | 6 + drivers/gpu/drm/radeon/pptable.h | 8 + drivers/gpu/drm/radeon/r600_dpm.c | 9 + drivers/gpu/drm/radeon/radeon.h | 4 + drivers/gpu/drm/radeon/smu7_discrete.h | 30 ++- 8 files changed, 443 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 9dbc52f..4581d6c 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -184,6 +184,9 @@ static int ci_set_overdrive_target_tdp(struct radeon_device *rdev, u32 target_tdp); static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate);
+static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev, + PPSMC_Msg msg, u32 parameter); + static struct ci_power_info *ci_get_pi(struct radeon_device *rdev) { struct ci_power_info *pi = rdev->pm.dpm.priv; @@ -355,6 +358,21 @@ static int ci_populate_dw8(struct radeon_device *rdev) return 0; }
+static int ci_populate_fuzzy_fan(struct radeon_device *rdev) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + + if ((rdev->pm.dpm.fan.fan_output_sensitivity & (1 << 15)) || + (rdev->pm.dpm.fan.fan_output_sensitivity == 0)) + rdev->pm.dpm.fan.fan_output_sensitivity = + rdev->pm.dpm.fan.default_fan_output_sensitivity; + + pi->smc_powertune_table.FuzzyFan_PwmSetDelta = + cpu_to_be16(rdev->pm.dpm.fan.fan_output_sensitivity); + + return 0; +} + static int ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(struct radeon_device *rdev) { struct ci_power_info *pi = ci_get_pi(rdev); @@ -480,6 +498,9 @@ static int ci_populate_pm_base(struct radeon_device *rdev) ret = ci_populate_dw8(rdev); if (ret) return ret; + ret = ci_populate_fuzzy_fan(rdev); + if (ret) + return ret; ret = ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(rdev); if (ret) return ret; @@ -859,6 +880,7 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev,
if (enable) { thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + WREG32_SMC(CG_THERMAL_INT, thermal_int); rdev->irq.dpm_thermal = false; result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Enable); if (result != PPSMC_Result_OK) { @@ -867,6 +889,7 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev, } } else { thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; + WREG32_SMC(CG_THERMAL_INT, thermal_int); rdev->irq.dpm_thermal = true; result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Disable); if (result != PPSMC_Result_OK) { @@ -875,12 +898,325 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev, } }
- WREG32_SMC(CG_THERMAL_INT, thermal_int); + return 0; +} + +static void ci_fan_ctrl_set_static_mode(struct radeon_device *rdev, u32 mode) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + u32 tmp; + + if (pi->fan_ctrl_is_in_default_mode) { + tmp = (RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT; + pi->fan_ctrl_default_mode = tmp; + tmp = (RREG32_SMC(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT; + pi->t_min = tmp; + pi->fan_ctrl_is_in_default_mode = false; + } + + tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TMIN_MASK; + tmp |= TMIN(0); + WREG32_SMC(CG_FDO_CTRL2, tmp); + + tmp = RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK; + tmp |= FDO_PWM_MODE(mode); + WREG32_SMC(CG_FDO_CTRL2, tmp); +} + +static int ci_thermal_setup_fan_table(struct radeon_device *rdev) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + SMU7_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE }; + u32 duty100; + u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2; + u16 fdo_min, slope1, slope2; + u32 reference_clock, tmp; + int ret; + u64 tmp64; + + if (!pi->fan_table_start) { + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; + } + + duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + + if (duty100 == 0) { + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; + } + + tmp64 = (u64)rdev->pm.dpm.fan.pwm_min * duty100; + do_div(tmp64, 10000); + fdo_min = (u16)tmp64; + + t_diff1 = rdev->pm.dpm.fan.t_med - rdev->pm.dpm.fan.t_min; + t_diff2 = rdev->pm.dpm.fan.t_high - rdev->pm.dpm.fan.t_med; + + pwm_diff1 = rdev->pm.dpm.fan.pwm_med - rdev->pm.dpm.fan.pwm_min; + pwm_diff2 = rdev->pm.dpm.fan.pwm_high - rdev->pm.dpm.fan.pwm_med; + + slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); + slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); + + fan_table.TempMin = cpu_to_be16((50 + rdev->pm.dpm.fan.t_min) / 100); + fan_table.TempMed = cpu_to_be16((50 + rdev->pm.dpm.fan.t_med) / 100); + fan_table.TempMax = cpu_to_be16((50 + rdev->pm.dpm.fan.t_max) / 100); + + fan_table.Slope1 = cpu_to_be16(slope1); + fan_table.Slope2 = cpu_to_be16(slope2); + + fan_table.FdoMin = cpu_to_be16(fdo_min); + + fan_table.HystDown = cpu_to_be16(rdev->pm.dpm.fan.t_hyst); + + fan_table.HystUp = cpu_to_be16(1); + + fan_table.HystSlope = cpu_to_be16(1); + + fan_table.TempRespLim = cpu_to_be16(5); + + reference_clock = radeon_get_xclk(rdev); + + fan_table.RefreshPeriod = cpu_to_be32((rdev->pm.dpm.fan.cycle_delay * + reference_clock) / 1600); + + fan_table.FdoMax = cpu_to_be16((u16)duty100); + + tmp = (RREG32_SMC(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT; + fan_table.TempSrc = (uint8_t)tmp; + + ret = ci_copy_bytes_to_smc(rdev, + pi->fan_table_start, + (u8 *)(&fan_table), + sizeof(fan_table), + pi->sram_end); + + if (ret) { + DRM_ERROR("Failed to load fan table to the SMC."); + rdev->pm.dpm.fan.ucode_fan_control = false; + } + + return 0; +} + +static int ci_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + PPSMC_Result ret; + + if (pi->caps_od_fuzzy_fan_control_support) { + ret = ci_send_msg_to_smc_with_parameter(rdev, + PPSMC_StartFanControl, + FAN_CONTROL_FUZZY); + if (ret != PPSMC_Result_OK) + return -EINVAL; + ret = ci_send_msg_to_smc_with_parameter(rdev, + PPSMC_MSG_SetFanPwmMax, + rdev->pm.dpm.fan.default_max_fan_pwm); + if (ret != PPSMC_Result_OK) + return -EINVAL; + } else { + ret = ci_send_msg_to_smc_with_parameter(rdev, + PPSMC_StartFanControl, + FAN_CONTROL_TABLE); + if (ret != PPSMC_Result_OK) + return -EINVAL; + }
return 0; }
#if 0 +static int ci_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev) +{ + PPSMC_Result ret; + + ret = ci_send_msg_to_smc(rdev, PPSMC_StopFanControl); + if (ret == PPSMC_Result_OK) + return 0; + else + return -EINVAL; +} + +static int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, + u32 *speed) +{ + u32 duty, duty100; + u64 tmp64; + + if (rdev->pm.no_fan) + return -ENOENT; + + duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + duty = (RREG32_SMC(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT; + + if (duty100 == 0) + return -EINVAL; + + tmp64 = (u64)duty * 100; + do_div(tmp64, duty100); + *speed = (u32)tmp64; + + if (*speed > 100) + *speed = 100; + + return 0; +} + +static int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, + u32 speed) +{ + u32 tmp; + u32 duty, duty100; + u64 tmp64; + + if (rdev->pm.no_fan) + return -ENOENT; + + if (speed > 100) + return -EINVAL; + + if (rdev->pm.dpm.fan.ucode_fan_control) + ci_fan_ctrl_stop_smc_fan_control(rdev); + + duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; + + if (duty100 == 0) + return -EINVAL; + + tmp64 = (u64)speed * duty100; + do_div(tmp64, 100); + duty = (u32)tmp64; + + tmp = RREG32_SMC(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK; + tmp |= FDO_STATIC_DUTY(duty); + WREG32_SMC(CG_FDO_CTRL0, tmp); + + ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + + return 0; +} + +static int ci_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev, + u32 *speed) +{ + u32 tach_period; + u32 xclk = radeon_get_xclk(rdev); + + if (rdev->pm.no_fan) + return -ENOENT; + + if (rdev->pm.fan_pulses_per_revolution == 0) + return -ENOENT; + + tach_period = (RREG32_SMC(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT; + if (tach_period == 0) + return -ENOENT; + + *speed = 60 * xclk * 10000 / tach_period; + + return 0; +} + +static int ci_fan_ctrl_set_fan_speed_rpm(struct radeon_device *rdev, + u32 speed) +{ + u32 tach_period, tmp; + u32 xclk = radeon_get_xclk(rdev); + + if (rdev->pm.no_fan) + return -ENOENT; + + if (rdev->pm.fan_pulses_per_revolution == 0) + return -ENOENT; + + if ((speed < rdev->pm.fan_min_rpm) || + (speed > rdev->pm.fan_max_rpm)) + return -EINVAL; + + if (rdev->pm.dpm.fan.ucode_fan_control) + ci_fan_ctrl_stop_smc_fan_control(rdev); + + tach_period = 60 * xclk * 10000 / (8 * speed); + tmp = RREG32_SMC(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK; + tmp |= TARGET_PERIOD(tach_period); + WREG32_SMC(CG_TACH_CTRL, tmp); + + ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + + return 0; +} +#endif + +static void ci_fan_ctrl_set_default_mode(struct radeon_device *rdev) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + u32 tmp; + + if (!pi->fan_ctrl_is_in_default_mode) { + tmp = RREG32_SMC(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK; + tmp |= FDO_PWM_MODE(pi->fan_ctrl_default_mode); + WREG32_SMC(CG_FDO_CTRL2, tmp); + + tmp = RREG32_SMC(CG_FDO_CTRL2) & TMIN_MASK; + tmp |= TMIN(pi->t_min); + WREG32_SMC(CG_FDO_CTRL2, tmp); + pi->fan_ctrl_is_in_default_mode = true; + } +} + +static void ci_thermal_start_smc_fan_control(struct radeon_device *rdev) +{ + if (rdev->pm.dpm.fan.ucode_fan_control) { + ci_fan_ctrl_start_smc_fan_control(rdev); + ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); + } +} + +static void ci_thermal_initialize(struct radeon_device *rdev) +{ + u32 tmp; + + if (rdev->pm.fan_pulses_per_revolution) { + tmp = RREG32_SMC(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK; + tmp |= EDGE_PER_REV(rdev->pm.fan_pulses_per_revolution -1); + WREG32_SMC(CG_TACH_CTRL, tmp); + } + + tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK; + tmp |= TACH_PWM_RESP_RATE(0x28); + WREG32_SMC(CG_FDO_CTRL2, tmp); +} + +static int ci_thermal_start_thermal_controller(struct radeon_device *rdev) +{ + int ret; + + ci_thermal_initialize(rdev); + ret = ci_thermal_set_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + if (ret) + return ret; + ret = ci_thermal_enable_alert(rdev, true); + if (ret) + return ret; + if (rdev->pm.dpm.fan.ucode_fan_control) { + ret = ci_thermal_setup_fan_table(rdev); + if (ret) + return ret; + ci_thermal_start_smc_fan_control(rdev); + } + + return 0; +} + +static void ci_thermal_stop_thermal_controller(struct radeon_device *rdev) +{ + if (!rdev->pm.no_fan) + ci_fan_ctrl_set_default_mode(rdev); +} + +#if 0 static int ci_read_smc_soft_register(struct radeon_device *rdev, u16 reg_offset, u32 *value) { @@ -4841,6 +5177,8 @@ int ci_dpm_enable(struct radeon_device *rdev)
ci_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+ ci_thermal_start_thermal_controller(rdev); + ci_update_current_ps(rdev, boot_ps);
return 0; @@ -4886,6 +5224,8 @@ void ci_dpm_disable(struct radeon_device *rdev) if (!ci_is_smc_running(rdev)) return;
+ ci_thermal_stop_thermal_controller(rdev); + if (pi->thermal_protection) ci_enable_thermal_protection(rdev, false); ci_enable_power_containment(rdev, false); @@ -5473,6 +5813,9 @@ int ci_dpm_init(struct radeon_device *rdev) rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc = rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+ pi->fan_ctrl_is_in_default_mode = true; + rdev->pm.dpm.fan.ucode_fan_control = false; + return 0; }
diff --git a/drivers/gpu/drm/radeon/ci_dpm.h b/drivers/gpu/drm/radeon/ci_dpm.h index 615cb2c..bb19fbf 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.h +++ b/drivers/gpu/drm/radeon/ci_dpm.h @@ -266,6 +266,7 @@ struct ci_power_info { bool caps_automatic_dc_transition; bool caps_sclk_throttle_low_notification; bool caps_dynamic_ac_timing; + bool caps_od_fuzzy_fan_control_support; /* flags */ bool thermal_protection; bool pcie_performance_request; @@ -287,6 +288,10 @@ struct ci_power_info { struct ci_ps current_ps; struct radeon_ps requested_rps; struct ci_ps requested_ps; + /* fan control */ + bool fan_ctrl_is_in_default_mode; + u32 t_min; + u32 fan_ctrl_default_mode; };
#define CISLANDS_VOLTAGE_CONTROL_NONE 0x0 diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 0c6e1b5..8d2bac6 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -184,7 +184,10 @@ #define DIG_THERM_DPM(x) ((x) << 14) #define DIG_THERM_DPM_MASK 0x003FC000 #define DIG_THERM_DPM_SHIFT 14 - +#define CG_THERMAL_STATUS 0xC0300008 +#define FDO_PWM_DUTY(x) ((x) << 9) +#define FDO_PWM_DUTY_MASK (0xff << 9) +#define FDO_PWM_DUTY_SHIFT 9 #define CG_THERMAL_INT 0xC030000C #define CI_DIG_THERM_INTH(x) ((x) << 8) #define CI_DIG_THERM_INTH_MASK 0x0000FF00 @@ -194,7 +197,10 @@ #define CI_DIG_THERM_INTL_SHIFT 16 #define THERM_INT_MASK_HIGH (1 << 24) #define THERM_INT_MASK_LOW (1 << 25) - +#define CG_MULT_THERMAL_CTRL 0xC0300010 +#define TEMP_SEL(x) ((x) << 20) +#define TEMP_SEL_MASK (0xff << 20) +#define TEMP_SEL_SHIFT 20 #define CG_MULT_THERMAL_STATUS 0xC0300014 #define ASIC_MAX_TEMP(x) ((x) << 0) #define ASIC_MAX_TEMP_MASK 0x000001ff @@ -203,6 +209,36 @@ #define CTF_TEMP_MASK 0x0003fe00 #define CTF_TEMP_SHIFT 9
+#define CG_FDO_CTRL0 0xC0300064 +#define FDO_STATIC_DUTY(x) ((x) << 0) +#define FDO_STATIC_DUTY_MASK 0x0000000F +#define FDO_STATIC_DUTY_SHIFT 0 +#define CG_FDO_CTRL1 0xC0300068 +#define FMAX_DUTY100(x) ((x) << 0) +#define FMAX_DUTY100_MASK 0x0000000F +#define FMAX_DUTY100_SHIFT 0 +#define CG_FDO_CTRL2 0xC030006C +#define TMIN(x) ((x) << 0) +#define TMIN_MASK 0x0000000F +#define TMIN_SHIFT 0 +#define FDO_PWM_MODE(x) ((x) << 11) +#define FDO_PWM_MODE_MASK (3 << 11) +#define FDO_PWM_MODE_SHIFT 11 +#define TACH_PWM_RESP_RATE(x) ((x) << 25) +#define TACH_PWM_RESP_RATE_MASK (0x7f << 25) +#define TACH_PWM_RESP_RATE_SHIFT 25 +#define CG_TACH_CTRL 0xC0300070 +# define EDGE_PER_REV(x) ((x) << 0) +# define EDGE_PER_REV_MASK (0x7 << 0) +# define EDGE_PER_REV_SHIFT 0 +# define TARGET_PERIOD(x) ((x) << 3) +# define TARGET_PERIOD_MASK 0xfffffff8 +# define TARGET_PERIOD_SHIFT 3 +#define CG_TACH_STATUS 0xC0300074 +# define TACH_PERIOD(x) ((x) << 0) +# define TACH_PERIOD_MASK 0xffffffff +# define TACH_PERIOD_SHIFT 0 + #define CG_ECLK_CNTL 0xC05000AC # define ECLK_DIVIDER_MASK 0x7f # define ECLK_DIR_CNTL_EN (1 << 8) diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 0c4eaa6..ff698b0 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -59,6 +59,11 @@ #define FDO_MODE_HARDWARE 0 #define FDO_MODE_PIECE_WISE_LINEAR 1
+enum FAN_CONTROL { + FAN_CONTROL_FUZZY, + FAN_CONTROL_TABLE +}; + #define PPSMC_Result_OK ((uint8_t)0x01) #define PPSMC_Result_Failed ((uint8_t)0xFF)
@@ -155,6 +160,7 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_MASTER_DeepSleep_ON ((uint16_t) 0x18F) #define PPSMC_MSG_MASTER_DeepSleep_OFF ((uint16_t) 0x190) #define PPSMC_MSG_Remove_DC_Clamp ((uint16_t) 0x191) +#define PPSMC_MSG_SetFanPwmMax ((uint16_t) 0x19A)
#define PPSMC_MSG_API_GetSclkFrequency ((uint16_t) 0x200) #define PPSMC_MSG_API_GetMclkFrequency ((uint16_t) 0x201) diff --git a/drivers/gpu/drm/radeon/pptable.h b/drivers/gpu/drm/radeon/pptable.h index 2d53299..4c2eec4 100644 --- a/drivers/gpu/drm/radeon/pptable.h +++ b/drivers/gpu/drm/radeon/pptable.h @@ -96,6 +96,14 @@ typedef struct _ATOM_PPLIB_FANTABLE2 USHORT usTMax; // The max temperature } ATOM_PPLIB_FANTABLE2;
+typedef struct _ATOM_PPLIB_FANTABLE3 +{ + ATOM_PPLIB_FANTABLE2 basicTable2; + UCHAR ucFanControlMode; + USHORT usFanPWMMax; + USHORT usFanOutputSensitivity; +} ATOM_PPLIB_FANTABLE3; + typedef struct _ATOM_PPLIB_EXTENDEDHEADER { USHORT usSize; diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index b5c73df..843b65f 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -811,6 +811,7 @@ union power_info { union fan_info { struct _ATOM_PPLIB_FANTABLE fan; struct _ATOM_PPLIB_FANTABLE2 fan2; + struct _ATOM_PPLIB_FANTABLE3 fan3; };
static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependency_table *radeon_table, @@ -900,6 +901,14 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) else rdev->pm.dpm.fan.t_max = 10900; rdev->pm.dpm.fan.cycle_delay = 100000; + if (fan_info->fan.ucFanTableFormat >= 3) { + rdev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode; + rdev->pm.dpm.fan.default_max_fan_pwm = + le16_to_cpu(fan_info->fan3.usFanPWMMax); + rdev->pm.dpm.fan.default_fan_output_sensitivity = 4836; + rdev->pm.dpm.fan.fan_output_sensitivity = + le16_to_cpu(fan_info->fan3.usFanOutputSensitivity); + } rdev->pm.dpm.fan.ucode_fan_control = true; } } diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 908f349..0a42615 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1490,6 +1490,10 @@ struct radeon_dpm_fan { u8 t_hyst; u32 cycle_delay; u16 t_max; + u8 control_mode; + u16 default_max_fan_pwm; + u16 default_fan_output_sensitivity; + u16 fan_output_sensitivity; bool ucode_fan_control; };
diff --git a/drivers/gpu/drm/radeon/smu7_discrete.h b/drivers/gpu/drm/radeon/smu7_discrete.h index 82f70c9..0b0b404 100644 --- a/drivers/gpu/drm/radeon/smu7_discrete.h +++ b/drivers/gpu/drm/radeon/smu7_discrete.h @@ -431,6 +431,31 @@ struct SMU7_Discrete_MCRegisters
typedef struct SMU7_Discrete_MCRegisters SMU7_Discrete_MCRegisters;
+struct SMU7_Discrete_FanTable +{ + uint16_t FdoMode; + int16_t TempMin; + int16_t TempMed; + int16_t TempMax; + int16_t Slope1; + int16_t Slope2; + int16_t FdoMin; + int16_t HystUp; + int16_t HystDown; + int16_t HystSlope; + int16_t TempRespLim; + int16_t TempCurr; + int16_t SlopeCurr; + int16_t PwmCurr; + uint32_t RefreshPeriod; + int16_t FdoMax; + uint8_t TempSrc; + int8_t Padding; +}; + +typedef struct SMU7_Discrete_FanTable SMU7_Discrete_FanTable; + + struct SMU7_Discrete_PmFuses { // dw0-dw1 uint8_t BapmVddCVidHiSidd[8]; @@ -462,7 +487,10 @@ struct SMU7_Discrete_PmFuses { uint8_t BapmVddCVidHiSidd2[8];
// dw11-dw12 - uint32_t Reserved6[2]; + int16_t FuzzyFan_ErrorSetDelta; + int16_t FuzzyFan_ErrorRateSetDelta; + int16_t FuzzyFan_PwmSetDelta; + uint16_t CalcMeasPowerBlend;
// dw13-dw16 uint8_t GnbLPML[16];
Not currently used.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/radeon/ci_dpm.h | 1 + drivers/gpu/drm/radeon/ppsmc.h | 3 +++ 3 files changed, 31 insertions(+)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 4581d6c..8312140 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -714,6 +714,25 @@ static int ci_enable_smc_cac(struct radeon_device *rdev, bool enable) return ret; }
+static int ci_enable_thermal_based_sclk_dpm(struct radeon_device *rdev, + bool enable) +{ + struct ci_power_info *pi = ci_get_pi(rdev); + PPSMC_Result smc_result = PPSMC_Result_OK; + + if (pi->thermal_sclk_dpm_enabled) { + if (enable) + smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_ENABLE_THERMAL_DPM); + else + smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DISABLE_THERMAL_DPM); + } + + if (smc_result == PPSMC_Result_OK) + return 0; + else + return -EINVAL; +} + static int ci_power_control_set_level(struct radeon_device *rdev) { struct ci_power_info *pi = ci_get_pi(rdev); @@ -5177,6 +5196,12 @@ int ci_dpm_enable(struct radeon_device *rdev)
ci_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+ ret = ci_enable_thermal_based_sclk_dpm(rdev, true); + if (ret) { + DRM_ERROR("ci_enable_thermal_based_sclk_dpm failed\n"); + return ret; + } + ci_thermal_start_thermal_controller(rdev);
ci_update_current_ps(rdev, boot_ps); @@ -5240,6 +5265,7 @@ void ci_dpm_disable(struct radeon_device *rdev) ci_reset_to_default(rdev); ci_dpm_stop_smc(rdev); ci_force_switch_to_arb_f0(rdev); + ci_enable_thermal_based_sclk_dpm(rdev, false);
ci_update_current_ps(rdev, boot_ps); } @@ -5639,6 +5665,7 @@ int ci_dpm_init(struct radeon_device *rdev) pi->sclk_dpm_key_disabled = 0; pi->mclk_dpm_key_disabled = 0; pi->pcie_dpm_key_disabled = 0; + pi->thermal_sclk_dpm_enabled = 0;
/* mclk dpm is unstable on some R7 260X cards with the old mc ucode */ if ((rdev->pdev->device == 0x6658) && diff --git a/drivers/gpu/drm/radeon/ci_dpm.h b/drivers/gpu/drm/radeon/ci_dpm.h index bb19fbf..84e3d3b 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.h +++ b/drivers/gpu/drm/radeon/ci_dpm.h @@ -239,6 +239,7 @@ struct ci_power_info { u32 sclk_dpm_key_disabled; u32 mclk_dpm_key_disabled; u32 pcie_dpm_key_disabled; + u32 thermal_sclk_dpm_enabled; struct ci_pcie_perf_range pcie_gen_performance; struct ci_pcie_perf_range pcie_lane_performance; struct ci_pcie_perf_range pcie_gen_powersaving; diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index ff698b0..7e5724a 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -162,6 +162,9 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_Remove_DC_Clamp ((uint16_t) 0x191) #define PPSMC_MSG_SetFanPwmMax ((uint16_t) 0x19A)
+#define PPSMC_MSG_ENABLE_THERMAL_DPM ((uint16_t) 0x19C) +#define PPSMC_MSG_DISABLE_THERMAL_DPM ((uint16_t) 0x19D) + #define PPSMC_MSG_API_GetSclkFrequency ((uint16_t) 0x200) #define PPSMC_MSG_API_GetMclkFrequency ((uint16_t) 0x201)
Add missing newline and print the bad gpio shift.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 8312140..c9963b5 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -5773,7 +5773,7 @@ int ci_dpm_init(struct radeon_device *rdev) tmp |= DPM_ENABLED; break; default: - DRM_ERROR("Invalid PCC GPIO!"); + DRM_ERROR("Invalid PCC GPIO: %u!\n", gpio.shift); break; } WREG32_SMC(CNB_PWRMGT_CNTL, tmp);
Apply voltage changes for displays before changing clocks.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index c9963b5..e781628 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -3752,6 +3752,8 @@ static int ci_upload_dpm_level_enable_mask(struct radeon_device *rdev) struct ci_power_info *pi = ci_get_pi(rdev); PPSMC_Result result;
+ ci_apply_disp_minimum_voltage_request(rdev); + if (!pi->sclk_dpm_key_disabled) { if (pi->dpm_level_enable_mask.sclk_dpm_enable_mask) { result = ci_send_msg_to_smc_with_parameter(rdev, @@ -3782,8 +3784,6 @@ static int ci_upload_dpm_level_enable_mask(struct radeon_device *rdev) } }
- ci_apply_disp_minimum_voltage_request(rdev); - return 0; }
Use unforce levels rather than enable mask instruction. This is the preferred method.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index e781628..73f8c4b 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -3773,7 +3773,7 @@ static int ci_upload_dpm_level_enable_mask(struct radeon_device *rdev) return -EINVAL; } } - +#if 0 if (!pi->pcie_dpm_key_disabled) { if (pi->dpm_level_enable_mask.pcie_dpm_enable_mask) { result = ci_send_msg_to_smc_with_parameter(rdev, @@ -3783,7 +3783,7 @@ static int ci_upload_dpm_level_enable_mask(struct radeon_device *rdev) return -EINVAL; } } - +#endif return 0; }
@@ -4247,6 +4247,14 @@ int ci_dpm_force_performance_level(struct radeon_device *rdev, } } } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) { + if (!pi->pcie_dpm_key_disabled) { + PPSMC_Result smc_result; + + smc_result = ci_send_msg_to_smc(rdev, + PPSMC_MSG_PCIeDPM_UnForceLevel); + if (smc_result != PPSMC_Result_OK) + return -EINVAL; + } ret = ci_upload_dpm_level_enable_mask(rdev); if (ret) return ret;
Preferred ordering.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 73f8c4b..630434c 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -4143,57 +4143,57 @@ int ci_dpm_force_performance_level(struct radeon_device *rdev, int ret;
if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { - if ((!pi->sclk_dpm_key_disabled) && - pi->dpm_level_enable_mask.sclk_dpm_enable_mask) { + if ((!pi->pcie_dpm_key_disabled) && + pi->dpm_level_enable_mask.pcie_dpm_enable_mask) { levels = 0; - tmp = pi->dpm_level_enable_mask.sclk_dpm_enable_mask; + tmp = pi->dpm_level_enable_mask.pcie_dpm_enable_mask; while (tmp >>= 1) levels++; if (levels) { - ret = ci_dpm_force_state_sclk(rdev, levels); + ret = ci_dpm_force_state_pcie(rdev, level); if (ret) return ret; for (i = 0; i < rdev->usec_timeout; i++) { - tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) & - CURR_SCLK_INDEX_MASK) >> CURR_SCLK_INDEX_SHIFT; + tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX_1) & + CURR_PCIE_INDEX_MASK) >> CURR_PCIE_INDEX_SHIFT; if (tmp == levels) break; udelay(1); } } } - if ((!pi->mclk_dpm_key_disabled) && - pi->dpm_level_enable_mask.mclk_dpm_enable_mask) { + if ((!pi->sclk_dpm_key_disabled) && + pi->dpm_level_enable_mask.sclk_dpm_enable_mask) { levels = 0; - tmp = pi->dpm_level_enable_mask.mclk_dpm_enable_mask; + tmp = pi->dpm_level_enable_mask.sclk_dpm_enable_mask; while (tmp >>= 1) levels++; if (levels) { - ret = ci_dpm_force_state_mclk(rdev, levels); + ret = ci_dpm_force_state_sclk(rdev, levels); if (ret) return ret; for (i = 0; i < rdev->usec_timeout; i++) { tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) & - CURR_MCLK_INDEX_MASK) >> CURR_MCLK_INDEX_SHIFT; + CURR_SCLK_INDEX_MASK) >> CURR_SCLK_INDEX_SHIFT; if (tmp == levels) break; udelay(1); } } } - if ((!pi->pcie_dpm_key_disabled) && - pi->dpm_level_enable_mask.pcie_dpm_enable_mask) { + if ((!pi->mclk_dpm_key_disabled) && + pi->dpm_level_enable_mask.mclk_dpm_enable_mask) { levels = 0; - tmp = pi->dpm_level_enable_mask.pcie_dpm_enable_mask; + tmp = pi->dpm_level_enable_mask.mclk_dpm_enable_mask; while (tmp >>= 1) levels++; if (levels) { - ret = ci_dpm_force_state_pcie(rdev, level); + ret = ci_dpm_force_state_mclk(rdev, levels); if (ret) return ret; for (i = 0; i < rdev->usec_timeout; i++) { - tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX_1) & - CURR_PCIE_INDEX_MASK) >> CURR_PCIE_INDEX_SHIFT; + tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) & + CURR_MCLK_INDEX_MASK) >> CURR_MCLK_INDEX_SHIFT; if (tmp == levels) break; udelay(1);
The current code always reprogrammed the sclk levels, but we don't currently handle disp sclk requirements so just skip it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ci_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 630434c..3f898d0 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -3809,7 +3809,7 @@ static void ci_find_dpm_states_clocks_in_dpm_table(struct radeon_device *rdev, pi->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK; } else { /* XXX check display min clock requirements */ - if (0 != CISLAND_MINIMUM_ENGINE_CLOCK) + if (CISLAND_MINIMUM_ENGINE_CLOCK != CISLAND_MINIMUM_ENGINE_CLOCK) pi->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK; }
dri-devel@lists.freedesktop.org