From: Dillon Varone <dillon.varone@xxxxxxx> [WHY & HOW] Refactor complex code into manageable functions. This also cleans up some updating logics. Reviewed-by: Alvin Lee <alvin.lee2@xxxxxxx> Acked-by: Alex Hung <alex.hung@xxxxxxx> Signed-off-by: Dillon Varone <dillon.varone@xxxxxxx> --- .../amd/display/dc/clk_mgr/dcn401/dalsmc.h | 8 +- .../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 523 +++++++++++++++++- .../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 94 ++++ .../clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c | 80 ++- .../clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h | 10 + .../gpu/drm/amd/display/dc/core/dc_resource.c | 3 +- drivers/gpu/drm/amd/display/dc/dc.h | 3 + .../gpu/drm/amd/display/include/dal_asic_id.h | 3 + 8 files changed, 718 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dalsmc.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dalsmc.h index 0d2584437934..5653c7991c62 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dalsmc.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dalsmc.h @@ -36,7 +36,13 @@ #define DALSMC_MSG_SetFclkSwitchAllow 0x11 #define DALSMC_MSG_SetCabForUclkPstate 0x12 #define DALSMC_MSG_SetWorstCaseUclkLatency 0x13 -#define DALSMC_Message_Count 0x14 +#define DALSMC_MSG_DcnExitReset 0x14 +#define DALSMC_MSG_ReturnHardMinStatus 0x15 +#define DALSMC_MSG_SetAlwaysWaitDmcubResp 0x16 +#define DALSMC_MSG_IndicateDrrStatus 0x17 // PMFW 15811 +#define DALSMC_MSG_ActiveUclkFclk 0x18 +#define DALSMC_MSG_IdleUclkFclk 0x19 +#define DALSMC_Message_Count 0x1A typedef enum { FCLK_SWITCH_DISALLOW, diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c index bd74ff47fb37..d2abc00a60c5 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c @@ -67,6 +67,9 @@ static const struct clk_mgr_mask clk_mgr_mask_dcn401 = { CLK_COMMON_MASK_SH_LIST_DCN401(_MASK) }; +#define TO_DCN401_CLK_MGR(clk_mgr)\ + container_of(clk_mgr, struct dcn401_clk_mgr, base) + static bool dcn401_is_ppclk_dpm_enabled(struct clk_mgr_internal *clk_mgr, PPCLK_e clk) { bool ppclk_dpm_enabled = false; @@ -112,6 +115,30 @@ static bool dcn401_is_ppclk_dpm_enabled(struct clk_mgr_internal *clk_mgr, PPCLK_ return ppclk_dpm_enabled; } +static bool dcn401_is_ppclk_idle_dpm_enabled(struct clk_mgr_internal *clk_mgr, PPCLK_e clk) +{ + bool ppclk_idle_dpm_enabled = false; + + switch (clk) { + case PPCLK_UCLK: + case PPCLK_FCLK: + if (ASICREV_IS_GC_12_0_0_A0(clk_mgr->base.ctx->asic_id.hw_internal_rev) && + clk_mgr->smu_ver >= 0x681800) { + ppclk_idle_dpm_enabled = true; + } else if (ASICREV_IS_GC_12_0_1_A0(clk_mgr->base.ctx->asic_id.hw_internal_rev) && + clk_mgr->smu_ver >= 0x661300) { + ppclk_idle_dpm_enabled = true; + } + break; + default: + ppclk_idle_dpm_enabled = false; + } + + ppclk_idle_dpm_enabled &= clk_mgr->smu_present; + + return ppclk_idle_dpm_enabled; +} + /* Query SMU for all clock states for a particular clock */ static void dcn401_init_single_clock(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, unsigned int *entry_0, unsigned int *num_levels) @@ -470,7 +497,7 @@ static void dcn401_update_clocks_update_dentist( } -static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, +static void dcn401_update_clocks_legacy(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool safe_to_lower) { @@ -512,7 +539,7 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, if (clk_mgr->smu_present) { if (enter_display_off == safe_to_lower) - dcn30_smu_set_num_of_displays(clk_mgr, display_count); + dcn401_smu_set_num_of_displays(clk_mgr, display_count); clk_mgr_base->clks.fclk_prev_p_state_change_support = clk_mgr_base->clks.fclk_p_state_change_support; @@ -542,7 +569,7 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, if (should_set_clock(safe_to_lower, new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) { clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz; if (dcn401_is_ppclk_dpm_enabled(clk_mgr, PPCLK_DCFCLK)) - dcn30_smu_set_min_deep_sleep_dcef_clk(clk_mgr, khz_to_mhz_ceil(clk_mgr_base->clks.dcfclk_deep_sleep_khz)); + dcn401_smu_set_min_deep_sleep_dcef_clk(clk_mgr, khz_to_mhz_ceil(clk_mgr_base->clks.dcfclk_deep_sleep_khz)); } if (should_set_clock(safe_to_lower, new_clocks->socclk_khz, clk_mgr_base->clks.socclk_khz)) @@ -668,6 +695,496 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, clk_mgr_base->clks.dispclk_khz / 1000 / 7); } +static void dcn401_build_update_clocks_sequence( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower, + unsigned int *num_steps) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal); + struct dc *dc = clk_mgr_base->ctx->dc; + struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu; + struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk; + struct dcn401_clk_mgr_block_sequence *block_sequence = clk_mgr401->block_sequence; + bool force_reset = false; + bool enter_display_off = false; + bool update_active_fclk = false; + bool update_active_uclk = false; + bool update_idle_fclk = false; + bool update_idle_uclk = false; + bool update_dispclk = false; + bool update_dppclk = false; + bool dppclk_lowered = false; + bool is_idle_dpm_enabled = dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_UCLK) && + dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_FCLK) && + dcn401_is_ppclk_idle_dpm_enabled(clk_mgr_internal, PPCLK_UCLK) && + dcn401_is_ppclk_idle_dpm_enabled(clk_mgr_internal, PPCLK_FCLK); + int total_plane_count = clk_mgr_helper_get_active_plane_cnt(dc, context); + int active_uclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.dramclk_khz); + int active_fclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.fclk_khz); + int idle_uclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.idle_dramclk_khz); + int idle_fclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.idle_fclk_khz); + + int display_count; + bool fclk_p_state_change_support, uclk_p_state_change_support; + + *num_steps = 0; + + /* CLK_MGR401_READ_CLOCKS_FROM_DENTIST */ + if (clk_mgr_base->clks.dispclk_khz == 0 || + (dc->debug.force_clock_mode & 0x1)) { + /* This is from resume or boot up, if forced_clock cfg option used, + * we bypass program dispclk and DPPCLK, but need set them for S3. + * Force_clock_mode 0x1: force reset the clock even it is the same clock + * as long as it is in Passive level. + */ + force_reset = true; + + block_sequence[*num_steps].func = CLK_MGR401_READ_CLOCKS_FROM_DENTIST; + (*num_steps)++; + } + + /* CLK_MGR401_UPDATE_NUM_DISPLAYS */ + if (clk_mgr_internal->smu_present) { + display_count = clk_mgr_helper_get_active_display_cnt(dc, context); + + if (display_count == 0) + enter_display_off = true; + + if (enter_display_off == safe_to_lower) { + block_sequence[*num_steps].params.update_num_displays_params.num_displays = display_count; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_NUM_DISPLAYS; + (*num_steps)++; + } + } + + /* CLK_MGR401_UPDATE_FCLK_PSTATE_SUPPORT */ + clk_mgr_base->clks.fclk_prev_p_state_change_support = clk_mgr_base->clks.fclk_p_state_change_support; + fclk_p_state_change_support = new_clocks->fclk_p_state_change_support || (total_plane_count == 0); + if (should_update_pstate_support(safe_to_lower, fclk_p_state_change_support, clk_mgr_base->clks.fclk_p_state_change_support)) { + clk_mgr_base->clks.fclk_p_state_change_support = fclk_p_state_change_support; + update_active_fclk = true; + update_idle_fclk = true; + + /* To enable FCLK P-state switching, send FCLK_PSTATE_SUPPORTED message to PMFW */ + if (clk_mgr_base->clks.fclk_p_state_change_support) { + /* Handle the code for sending a message to PMFW that FCLK P-state change is supported */ + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_FCLK)) { + block_sequence[*num_steps].params.update_fclk_pstate_support_params.support = FCLK_PSTATE_SUPPORTED; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_FCLK_PSTATE_SUPPORT; + (*num_steps)++; + } + } else { + /* P-State is not supported so force max clocks */ + idle_fclk_mhz = + clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_fclk_levels - 1].fclk_mhz; + active_fclk_mhz = idle_fclk_mhz; + } + } + + /* UPDATE DCFCLK */ + if (dc->debug.force_min_dcfclk_mhz > 0) + new_clocks->dcfclk_khz = (new_clocks->dcfclk_khz > (dc->debug.force_min_dcfclk_mhz * 1000)) ? + new_clocks->dcfclk_khz : (dc->debug.force_min_dcfclk_mhz * 1000); + + if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr_base->clks.dcfclk_khz)) { + clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz; + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DCFCLK)) { + block_sequence[*num_steps].params.update_hardmin_params.ppclk = PPCLK_DCFCLK; + block_sequence[*num_steps].params.update_hardmin_params.freq_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.dcfclk_khz); + block_sequence[*num_steps].params.update_hardmin_params.response = NULL; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK; + (*num_steps)++; + } + } + + /* CLK_MGR401_UPDATE_DEEP_SLEEP_DCFCLK */ + if (should_set_clock(safe_to_lower, new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) { + clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz; + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DCFCLK)) { + block_sequence[*num_steps].params.update_deep_sleep_dcfclk_params.freq_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.dcfclk_deep_sleep_khz); + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DEEP_SLEEP_DCFCLK; + (*num_steps)++; + } + } + + /* SOCCLK */ + if (should_set_clock(safe_to_lower, new_clocks->socclk_khz, clk_mgr_base->clks.socclk_khz)) + /* We don't actually care about socclk, don't notify SMU of hard min */ + clk_mgr_base->clks.socclk_khz = new_clocks->socclk_khz; + + /* CLK_MGR401_UPDATE_CAB_FOR_UCLK */ + clk_mgr_base->clks.prev_p_state_change_support = clk_mgr_base->clks.p_state_change_support; + clk_mgr_base->clks.prev_num_ways = clk_mgr_base->clks.num_ways; + + if (clk_mgr_base->clks.num_ways != new_clocks->num_ways && + clk_mgr_base->clks.num_ways < new_clocks->num_ways) { + clk_mgr_base->clks.num_ways = new_clocks->num_ways; + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_UCLK)) { + block_sequence[*num_steps].params.update_cab_for_uclk_params.num_ways = clk_mgr_base->clks.num_ways; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_CAB_FOR_UCLK; + (*num_steps)++; + } + } + + /* UCLK */ + uclk_p_state_change_support = new_clocks->p_state_change_support || (total_plane_count == 0); + if (should_update_pstate_support(safe_to_lower, uclk_p_state_change_support, clk_mgr_base->clks.p_state_change_support)) { + clk_mgr_base->clks.p_state_change_support = uclk_p_state_change_support; + update_active_uclk = true; + update_idle_uclk = true; + + /* to disable P-State switching, set UCLK min = max */ + if (!clk_mgr_base->clks.p_state_change_support) { + if (dc->clk_mgr->dc_mode_softmax_enabled) { + /* will never have the functional UCLK min above the softmax + * since we calculate mode support based on softmax being the max UCLK + * frequency. + */ + active_uclk_mhz = clk_mgr_base->bw_params->dc_mode_softmax_memclk; + } else { + active_uclk_mhz = clk_mgr_base->bw_params->max_memclk_mhz; + } + idle_uclk_mhz = active_uclk_mhz; + } + } + + /* Always update saved value, even if new value not set due to P-State switching unsupported */ + if (should_set_clock(safe_to_lower, new_clocks->dramclk_khz, clk_mgr_base->clks.dramclk_khz)) { + clk_mgr_base->clks.dramclk_khz = new_clocks->dramclk_khz; + + if (clk_mgr_base->clks.p_state_change_support) { + update_active_uclk = true; + active_uclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.dramclk_khz); + } + } + if (should_set_clock(safe_to_lower, new_clocks->idle_dramclk_khz, clk_mgr_base->clks.idle_dramclk_khz)) { + clk_mgr_base->clks.idle_dramclk_khz = new_clocks->idle_dramclk_khz; + + if (clk_mgr_base->clks.p_state_change_support) { + update_idle_uclk = true; + idle_uclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.idle_dramclk_khz); + } + } + + /* set UCLK to requested value */ + if ((update_active_uclk || update_idle_uclk) && + dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_UCLK) && + !is_idle_dpm_enabled) { + block_sequence[*num_steps].params.update_hardmin_params.ppclk = PPCLK_UCLK; + block_sequence[*num_steps].params.update_hardmin_params.freq_mhz = active_uclk_mhz; + block_sequence[*num_steps].params.update_hardmin_params.response = NULL; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK; + (*num_steps)++; + } + + /* FCLK */ + /* Always update saved value, even if new value not set due to P-State switching unsupported */ + if (should_set_clock(safe_to_lower, new_clocks->fclk_khz, clk_mgr_base->clks.fclk_khz)) { + clk_mgr_base->clks.fclk_khz = new_clocks->fclk_khz; + + if (clk_mgr_base->clks.fclk_p_state_change_support) { + update_active_fclk = true; + active_fclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.fclk_khz); + } + } + + if (should_set_clock(safe_to_lower, new_clocks->idle_fclk_khz, clk_mgr_base->clks.idle_fclk_khz)) { + clk_mgr_base->clks.idle_fclk_khz = new_clocks->idle_fclk_khz; + + if (clk_mgr_base->clks.fclk_p_state_change_support) { + update_idle_fclk = true; + idle_fclk_mhz = khz_to_mhz_ceil(clk_mgr_base->clks.idle_fclk_khz); + } + } + + /* When idle DPM is enabled, need to send active and idle hardmins separately */ + /* CLK_MGR401_UPDATE_ACTIVE_HARDMINS */ + if ((update_active_uclk || update_active_fclk) && is_idle_dpm_enabled) { + block_sequence[*num_steps].params.update_idle_hardmin_params.uclk_mhz = active_uclk_mhz; + block_sequence[*num_steps].params.update_idle_hardmin_params.fclk_mhz = active_fclk_mhz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_ACTIVE_HARDMINS; + (*num_steps)++; + } + + /* CLK_MGR401_UPDATE_IDLE_HARDMINS */ + if ((update_idle_uclk || update_idle_uclk) && is_idle_dpm_enabled) { + block_sequence[*num_steps].params.update_idle_hardmin_params.uclk_mhz = idle_uclk_mhz; + block_sequence[*num_steps].params.update_idle_hardmin_params.fclk_mhz = idle_fclk_mhz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_IDLE_HARDMINS; + (*num_steps)++; + } + + /* CLK_MGR401_UPDATE_WAIT_FOR_DMUB_ACK, CLK_MGR401_INDICATE_DRR_STATUS*/ + if (clk_mgr_base->clks.fw_based_mclk_switching != new_clocks->fw_based_mclk_switching) { + clk_mgr_base->clks.fw_based_mclk_switching = new_clocks->fw_based_mclk_switching; + + block_sequence[*num_steps].params.update_wait_for_dmub_ack_params.enable = clk_mgr_base->clks.fw_based_mclk_switching; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_WAIT_FOR_DMUB_ACK; + (*num_steps)++; + + block_sequence[*num_steps].params.indicate_drr_status_params.mod_drr_for_pstate = clk_mgr_base->clks.fw_based_mclk_switching; + block_sequence[*num_steps].func = CLK_MGR401_INDICATE_DRR_STATUS; + (*num_steps)++; + } + + /* set FCLK to requested value if P-State switching is supported, or to re-enable P-State switching */ + if ((update_active_fclk || update_idle_fclk)) { + /* disable FCLK P-State support if needed */ + if (clk_mgr_base->clks.fclk_p_state_change_support != clk_mgr_base->clks.fclk_prev_p_state_change_support && + dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_FCLK)) { + block_sequence[*num_steps].params.update_fclk_pstate_support_params.support = FCLK_PSTATE_NOTSUPPORTED; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_FCLK_PSTATE_SUPPORT; + (*num_steps)++; + } + + /* No need to send active FCLK hardmin, automatically set based on DCFCLK */ + // block_sequence[*num_steps].update_hardmin_params.clk_mgr = clk_mgr; + // block_sequence[*num_steps].update_hardmin_params.ppclk = PPCLK_FCLK; + // block_sequence[*num_steps].update_hardmin_params.freq_mhz = active_fclk_mhz; + // block_sequence[*num_steps].update_hardmin_params.response = NULL; + // block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK; + // (*num_steps)++; + } + + /* CLK_MGR401_UPDATE_CAB_FOR_UCLK */ + if (clk_mgr_base->clks.num_ways != new_clocks->num_ways && + clk_mgr_base->clks.num_ways > new_clocks->num_ways) { + clk_mgr_base->clks.num_ways = new_clocks->num_ways; + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_UCLK)) { + block_sequence[*num_steps].params.update_cab_for_uclk_params.num_ways = clk_mgr_base->clks.num_ways; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_CAB_FOR_UCLK; + (*num_steps)++; + } + } + + /* DTBCLK */ + if (!new_clocks->dtbclk_en && dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DTBCLK)) + new_clocks->ref_dtbclk_khz = clk_mgr_base->bw_params->clk_table.entries[0].dtbclk_mhz * 1000; + + /* clock limits are received with MHz precision, divide by 1000 to prevent setting clocks at every call */ + if (!dc->debug.disable_dtb_ref_clk_switch && + should_set_clock(safe_to_lower, new_clocks->ref_dtbclk_khz / 1000, clk_mgr_base->clks.ref_dtbclk_khz / 1000) && //TODO these should be ceiled + dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DTBCLK)) { + /* DCCG requires KHz precision for DTBCLK */ + block_sequence[*num_steps].params.update_hardmin_params.ppclk = PPCLK_DTBCLK; + block_sequence[*num_steps].params.update_hardmin_params.freq_mhz = khz_to_mhz_ceil(new_clocks->ref_dtbclk_khz); + block_sequence[*num_steps].params.update_hardmin_params.response = &clk_mgr_base->clks.ref_dtbclk_khz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK; + (*num_steps)++; + + /* Update DTO in DCCG */ + block_sequence[*num_steps].params.update_dtbclk_dto_params.context = context; + block_sequence[*num_steps].params.update_dtbclk_dto_params.ref_dtbclk_khz = clk_mgr_base->clks.ref_dtbclk_khz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DTBCLK_DTO; + (*num_steps)++; + } + + if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) { + if (clk_mgr_base->clks.dppclk_khz > new_clocks->dppclk_khz) + dppclk_lowered = true; + + clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz; + clk_mgr_base->clks.actual_dppclk_khz = new_clocks->dppclk_khz; + + update_dppclk = true; + } + + if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz)) { + clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; + + update_dispclk = true; + } + + if (dc->config.forced_clocks == false || (force_reset && safe_to_lower)) { + if (dppclk_lowered) { + /* if clock is being lowered, increase DTO before lowering refclk */ + block_sequence[*num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[*num_steps].params.update_dppclk_dto_params.dppclk_khz = clk_mgr_base->clks.dppclk_khz; + block_sequence[*num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO; + (*num_steps)++; + + block_sequence[*num_steps].params.update_dentist_params.context = context; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DENTIST; + (*num_steps)++; + + if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DPPCLK)) { + block_sequence[*num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DPPCLK; + block_sequence[*num_steps].params.update_hardmin_optimized_params.freq_khz = clk_mgr_base->clks.dppclk_khz; + block_sequence[*num_steps].params.update_hardmin_optimized_params.response = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK_OPTIMIZED; + (*num_steps)++; + + block_sequence[*num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[*num_steps].params.update_dppclk_dto_params.dppclk_khz = clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[*num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO; + (*num_steps)++; + } + } else { + /* if clock is being raised, increase refclk before lowering DTO */ + if (update_dppclk && dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DPPCLK)) { + block_sequence[*num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DPPCLK; + block_sequence[*num_steps].params.update_hardmin_optimized_params.freq_khz = clk_mgr_base->clks.dppclk_khz; + block_sequence[*num_steps].params.update_hardmin_optimized_params.response = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK_OPTIMIZED; + (*num_steps)++; + } + + if (update_dppclk || update_dispclk) { + block_sequence[*num_steps].params.update_dentist_params.context = context; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DENTIST; + (*num_steps)++; + } + + block_sequence[*num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[*num_steps].params.update_dppclk_dto_params.dppclk_khz = clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[*num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO; + (*num_steps)++; + } + } + + if (update_dispclk && dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) { + /*update dmcu for wait_loop count*/ + block_sequence[*num_steps].params.update_psr_wait_loop_params.dmcu = dmcu; + block_sequence[*num_steps].params.update_psr_wait_loop_params.wait = clk_mgr_base->clks.dispclk_khz / 1000 / 7; + block_sequence[*num_steps].func = CLK_MGR401_UPDATE_PSR_WAIT_LOOP; + (*num_steps)++; + } +} + +static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal); + struct dc *dc = clk_mgr_base->ctx->dc; + + unsigned int num_steps = 0; + + unsigned int i; + union dcn401_clk_mgr_block_sequence_params *params; + + if (dc->work_arounds.skip_clock_update) + return; + + if (dc->debug.enable_legacy_clock_update) { + dcn401_update_clocks_legacy(clk_mgr_base, context, safe_to_lower); + return; + } + + /* build clock update sequence */ + dcn401_build_update_clocks_sequence(clk_mgr_base, + context, + safe_to_lower, + &num_steps); + + /* execute sequence */ + for (i = 0; i < num_steps; i++) { + params = &clk_mgr401->block_sequence[i].params; + + switch (clk_mgr401->block_sequence[i].func) { + case CLK_MGR401_READ_CLOCKS_FROM_DENTIST: + dcn2_read_clocks_from_hw_dentist(clk_mgr_base); + break; + case CLK_MGR401_UPDATE_NUM_DISPLAYS: + dcn401_smu_set_num_of_displays(clk_mgr_internal, + params->update_num_displays_params.num_displays); + break; + case CLK_MGR401_UPDATE_HARDMIN_PPCLK: + if (params->update_hardmin_params.response) + *params->update_hardmin_params.response = dcn401_smu_set_hard_min_by_freq( + clk_mgr_internal, + params->update_hardmin_params.ppclk, + params->update_hardmin_params.freq_mhz); + else + dcn401_smu_set_hard_min_by_freq(clk_mgr_internal, + params->update_hardmin_params.ppclk, + params->update_hardmin_params.freq_mhz); + break; + case CLK_MGR401_UPDATE_HARDMIN_PPCLK_OPTIMIZED: + if (params->update_hardmin_optimized_params.response) + *params->update_hardmin_optimized_params.response = dcn401_set_hard_min_by_freq_optimized( + clk_mgr_internal, + params->update_hardmin_optimized_params.ppclk, + params->update_hardmin_optimized_params.freq_khz); + else + dcn401_set_hard_min_by_freq_optimized(clk_mgr_internal, + params->update_hardmin_optimized_params.ppclk, + params->update_hardmin_optimized_params.freq_khz); + break; + case CLK_MGR401_UPDATE_ACTIVE_HARDMINS: + dcn401_smu_set_active_uclk_fclk_hardmin( + clk_mgr_internal, + params->update_idle_hardmin_params.uclk_mhz, + params->update_idle_hardmin_params.fclk_mhz); + break; + case CLK_MGR401_UPDATE_IDLE_HARDMINS: + dcn401_smu_set_idle_uclk_fclk_hardmin( + clk_mgr_internal, + params->update_idle_hardmin_params.uclk_mhz, + params->update_idle_hardmin_params.fclk_mhz); + break; + case CLK_MGR401_UPDATE_DEEP_SLEEP_DCFCLK: + dcn401_smu_set_min_deep_sleep_dcef_clk( + clk_mgr_internal, + params->update_deep_sleep_dcfclk_params.freq_mhz); + break; + case CLK_MGR401_UPDATE_FCLK_PSTATE_SUPPORT: + dcn401_smu_send_fclk_pstate_message( + clk_mgr_internal, + params->update_fclk_pstate_support_params.support); + break; + case CLK_MGR401_UPDATE_CAB_FOR_UCLK: + dcn401_smu_send_cab_for_uclk_message( + clk_mgr_internal, + params->update_cab_for_uclk_params.num_ways); + break; + case CLK_MGR401_UPDATE_WAIT_FOR_DMUB_ACK: + dcn401_smu_wait_for_dmub_ack_mclk( + clk_mgr_internal, + params->update_wait_for_dmub_ack_params.enable); + break; + case CLK_MGR401_INDICATE_DRR_STATUS: + dcn401_smu_indicate_drr_status( + clk_mgr_internal, + params->indicate_drr_status_params.mod_drr_for_pstate); + break; + case CLK_MGR401_UPDATE_DPPCLK_DTO: + dcn401_update_clocks_update_dpp_dto( + clk_mgr_internal, + params->update_dppclk_dto_params.context, + params->update_dppclk_dto_params.safe_to_lower, + params->update_dppclk_dto_params.dppclk_khz); + break; + case CLK_MGR401_UPDATE_DTBCLK_DTO: + dcn401_update_clocks_update_dtb_dto( + clk_mgr_internal, + params->update_dtbclk_dto_params.context, + params->update_dtbclk_dto_params.ref_dtbclk_khz); + break; + case CLK_MGR401_UPDATE_DENTIST: + dcn401_update_clocks_update_dentist( + clk_mgr_internal, + params->update_dentist_params.context); + break; + case CLK_MGR401_UPDATE_PSR_WAIT_LOOP: + params->update_psr_wait_loop_params.dmcu->funcs->set_psr_wait_loop( + params->update_psr_wait_loop_params.dmcu, + params->update_psr_wait_loop_params.wait); + break; + default: + /* this should never happen */ + BREAK_TO_DEBUGGER(); + break; + } + } +} + static uint32_t dcn401_get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr) { struct fixed31_32 pll_req; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h index dad203de0dd4..e2a3fbb6c3c3 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h @@ -5,8 +5,102 @@ #ifndef __DCN401_CLK_MGR_H_ #define __DCN401_CLK_MGR_H_ +#define DCN401_CLK_MGR_MAX_SEQUENCE_SIZE 30 + +union dcn401_clk_mgr_block_sequence_params { + struct { + /* inputs */ + uint32_t num_displays; + } update_num_displays_params; + struct { + /* inputs */ + uint32_t ppclk; + uint16_t freq_mhz; + /* outputs */ + uint32_t *response; + } update_hardmin_params; + struct { + /* inputs */ + uint32_t ppclk; + int freq_khz; + /* outputs */ + uint32_t *response; + } update_hardmin_optimized_params; + struct { + /* inputs */ + uint16_t uclk_mhz; + uint16_t fclk_mhz; + } update_idle_hardmin_params; + struct { + /* inputs */ + uint16_t freq_mhz; + } update_deep_sleep_dcfclk_params; + struct { + /* inputs */ + bool support; + } update_fclk_pstate_support_params; + struct { + /* inputs */ + unsigned int num_ways; + } update_cab_for_uclk_params; + struct { + /* inputs */ + bool enable; + } update_wait_for_dmub_ack_params; + struct { + /* inputs */ + bool mod_drr_for_pstate; + } indicate_drr_status_params; + struct { + /* inputs */ + struct dc_state *context; + int dppclk_khz; + bool safe_to_lower; + } update_dppclk_dto_params; + struct { + /* inputs */ + struct dc_state *context; + int ref_dtbclk_khz; + } update_dtbclk_dto_params; + struct { + /* inputs */ + struct dc_state *context; + int ref_dtbclk_khz; + } update_dentist_params; + struct { + /* inputs */ + struct dmcu *dmcu; + unsigned int wait; + } update_psr_wait_loop_params; +}; + +enum dcn401_clk_mgr_block_sequence_func { + CLK_MGR401_READ_CLOCKS_FROM_DENTIST, + CLK_MGR401_UPDATE_NUM_DISPLAYS, + CLK_MGR401_UPDATE_HARDMIN_PPCLK, + CLK_MGR401_UPDATE_HARDMIN_PPCLK_OPTIMIZED, + CLK_MGR401_UPDATE_ACTIVE_HARDMINS, + CLK_MGR401_UPDATE_IDLE_HARDMINS, + CLK_MGR401_UPDATE_DEEP_SLEEP_DCFCLK, + CLK_MGR401_UPDATE_FCLK_PSTATE_SUPPORT, + CLK_MGR401_UPDATE_CAB_FOR_UCLK, + CLK_MGR401_UPDATE_WAIT_FOR_DMUB_ACK, + CLK_MGR401_INDICATE_DRR_STATUS, + CLK_MGR401_UPDATE_DPPCLK_DTO, + CLK_MGR401_UPDATE_DTBCLK_DTO, + CLK_MGR401_UPDATE_DENTIST, + CLK_MGR401_UPDATE_PSR_WAIT_LOOP, +}; + +struct dcn401_clk_mgr_block_sequence { + union dcn401_clk_mgr_block_sequence_params params; + enum dcn401_clk_mgr_block_sequence_func func; +}; + struct dcn401_clk_mgr { struct clk_mgr_internal base; + + struct dcn401_clk_mgr_block_sequence block_sequence[DCN401_CLK_MGR_MAX_SEQUENCE_SIZE]; }; void dcn401_init_clocks(struct clk_mgr *clk_mgr_base); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c index 054e8bd686f1..a3d0a48ed7ce 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c @@ -105,6 +105,7 @@ void dcn401_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) unsigned int dcn401_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz) { uint32_t response = 0; + bool hard_min_done = false; /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ uint32_t param = (clk << 16) | freq_mhz; @@ -114,7 +115,84 @@ unsigned int dcn401_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, u dcn401_smu_send_msg_with_param(clk_mgr, DALSMC_MSG_SetHardMinByFreq, param, &response); - smu_print("SMU Frequency set = %d KHz\n", response); + /* wait until hardmin acknowledged */ + //hard_min_done = dcn401_smu_wait_get_hard_min_status(clk_mgr, clk); + smu_print("SMU Frequency set = %d KHz hard_min_done %d\n", response, hard_min_done); return response; } + +void dcn401_smu_wait_for_dmub_ack_mclk(struct clk_mgr_internal *clk_mgr, bool enable) +{ + smu_print("SMU to wait for DMCUB ack for MCLK : %d\n", enable); + + dcn401_smu_send_msg_with_param(clk_mgr, DALSMC_MSG_SetAlwaysWaitDmcubResp, enable ? 1 : 0, NULL); +} + +void dcn401_smu_indicate_drr_status(struct clk_mgr_internal *clk_mgr, bool mod_drr_for_pstate) +{ + smu_print("SMU Set indicate drr status = %d\n", mod_drr_for_pstate); + + dcn401_smu_send_msg_with_param(clk_mgr, + DALSMC_MSG_IndicateDrrStatus, mod_drr_for_pstate ? 1 : 0, NULL); +} + +bool dcn401_smu_set_idle_uclk_fclk_hardmin(struct clk_mgr_internal *clk_mgr, + uint16_t uclk_freq_mhz, + uint16_t fclk_freq_mhz) +{ + uint32_t response = 0; + bool success; + + /* 15:0 for uclk, 32:16 for fclk */ + uint32_t param = (fclk_freq_mhz << 16) | uclk_freq_mhz; + + smu_print("SMU Set idle hardmin by freq: uclk_freq_mhz = %d MHz, fclk_freq_mhz = %d MHz\n", uclk_freq_mhz, fclk_freq_mhz); + + success = dcn401_smu_send_msg_with_param(clk_mgr, + DALSMC_MSG_IdleUclkFclk, param, &response); + + /* wait until hardmin acknowledged */ + //success &= dcn401_smu_wait_get_hard_min_status(clk_mgr, PPCLK_UCLK); + smu_print("SMU hard_min_done %d\n", success); + + return success; +} + +bool dcn401_smu_set_active_uclk_fclk_hardmin(struct clk_mgr_internal *clk_mgr, + uint16_t uclk_freq_mhz, + uint16_t fclk_freq_mhz) +{ + uint32_t response = 0; + bool success; + + /* 15:0 for uclk, 32:16 for fclk */ + uint32_t param = (fclk_freq_mhz << 16) | uclk_freq_mhz; + + smu_print("SMU Set active hardmin by freq: uclk_freq_mhz = %d MHz, fclk_freq_mhz = %d MHz\n", uclk_freq_mhz, fclk_freq_mhz); + + success = dcn401_smu_send_msg_with_param(clk_mgr, + DALSMC_MSG_ActiveUclkFclk, param, &response); + + /* wait until hardmin acknowledged */ + //success &= dcn401_smu_wait_get_hard_min_status(clk_mgr, PPCLK_UCLK); + smu_print("SMU hard_min_done %d\n", success); + + return success; +} + +void dcn401_smu_set_min_deep_sleep_dcef_clk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz) +{ + smu_print("SMU Set min deep sleep dcef clk: freq_mhz = %d MHz\n", freq_mhz); + + dcn401_smu_send_msg_with_param(clk_mgr, + DALSMC_MSG_SetMinDeepSleepDcfclk, freq_mhz, NULL); +} + +void dcn401_smu_set_num_of_displays(struct clk_mgr_internal *clk_mgr, uint32_t num_displays) +{ + smu_print("SMU Set num of displays: num_displays = %d\n", num_displays); + + dcn401_smu_send_msg_with_param(clk_mgr, + DALSMC_MSG_NumOfDisplays, num_displays, NULL); +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h index 8918bc52c2af..892881d11ce8 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h @@ -17,5 +17,15 @@ void dcn401_smu_send_cab_for_uclk_message(struct clk_mgr_internal *clk_mgr, unsi void dcn401_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr); void dcn401_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr); unsigned int dcn401_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz); +void dcn401_smu_wait_for_dmub_ack_mclk(struct clk_mgr_internal *clk_mgr, bool enable); +void dcn401_smu_indicate_drr_status(struct clk_mgr_internal *clk_mgr, bool mod_drr_for_pstate); +bool dcn401_smu_set_idle_uclk_fclk_hardmin(struct clk_mgr_internal *clk_mgr, + uint16_t uclk_freq_mhz, + uint16_t fclk_freq_mhz); +bool dcn401_smu_set_active_uclk_fclk_hardmin(struct clk_mgr_internal *clk_mgr, + uint16_t uclk_freq_mhz, + uint16_t fclk_freq_mhz); +void dcn401_smu_set_min_deep_sleep_dcef_clk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz); +void dcn401_smu_set_num_of_displays(struct clk_mgr_internal *clk_mgr, uint32_t num_displays); #endif /* __DCN401_CLK_MGR_SMU_MSG_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index fd624b1fee25..8a5cc8b80217 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -204,7 +204,8 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) dc_version = DCN_VERSION_3_51; break; case AMDGPU_FAMILY_GC_12_0_0: - if (ASICREV_IS_DCN401(asic_id.hw_internal_rev)) + if (ASICREV_IS_GC_12_0_1_A0(asic_id.hw_internal_rev) || + ASICREV_IS_GC_12_0_0_A0(asic_id.hw_internal_rev)) dc_version = DCN_VERSION_4_01; break; default: diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index eef2f357fe14..d550b6f97039 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -610,6 +610,8 @@ struct dc_clocks { int max_supported_dispclk_khz; int bw_dppclk_khz; /*a copy of dppclk_khz*/ int bw_dispclk_khz; + int idle_dramclk_khz; + int idle_fclk_khz; }; struct dc_bw_validation_profile { @@ -1035,6 +1037,7 @@ struct dc_debug_options { uint32_t dml21_force_pstate_method_value; uint32_t dml21_disable_pstate_method_mask; union dmub_fams2_global_feature_config fams2_config; + bool enable_legacy_clock_update; unsigned int force_cositing; }; diff --git a/drivers/gpu/drm/amd/display/include/dal_asic_id.h b/drivers/gpu/drm/amd/display/include/dal_asic_id.h index 25333337461e..090230d29df8 100644 --- a/drivers/gpu/drm/amd/display/include/dal_asic_id.h +++ b/drivers/gpu/drm/amd/display/include/dal_asic_id.h @@ -266,6 +266,9 @@ enum { GC_12_UNKNOWN = 0xFF, }; +#define ASICREV_IS_GC_12_0_1_A0(eChipRev) (eChipRev >= GC_12_0_1_A0 && eChipRev < GC_12_0_0_A0) +#define ASICREV_IS_GC_12_0_0_A0(eChipRev) (eChipRev >= GC_12_0_0_A0 && eChipRev < 0xFF) + #define ASICREV_IS_DCN4(eChipRev) (eChipRev >= GC_12_0_1_A0 && eChipRev < GC_12_0_0_A0) #define ASICREV_IS_DCN401(eChipRev) (eChipRev >= GC_12_0_0_A0 && eChipRev < GC_12_UNKNOWN) -- 2.34.1