From: Leo Chen <[email protected]> [Why] DCN must not gate DF C-state locally via the DCHUBBUB ALLOW_SELF_REFRESH controls: forcing a local "disallow" during MM-stutter re-entry can wedge the fabric and hang boot. When DCN is disallowing c-state or has invalid watermarks we should be explicit about it rather than using the watermark force selector.
[How] Register DCN as a client of PMFW's DF C-state arbiter and signal over DALSMC whether DCN permits DF C-state; PMFW allows DF C-state only once every client (including DCN) has voted "allow". - dc_clocks.cstate_allow: last DCN vote acked by PMFW - clk_mgr_funcs::notify_cstate_disable(clk_mgr, disable) prepare_bandwidth and headless dc_power_down_on_boot vote Allow; hardware_release votes Disallow; init_clocks (D0 entry) only clears the cache (cstate_allow) so the next allow re-syncs with PMFW. Reviewed-by: Nicholas Kazlauskas <[email protected]> Signed-off-by: Leo Chen <[email protected]> Signed-off-by: Wayne Lin <[email protected]> --- .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 28 ++++++++++++++++++ .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h | 1 + .../amd/display/dc/clk_mgr/dcn42/dcn42_smu.c | 29 ++++++++++++++++++- .../amd/display/dc/clk_mgr/dcn42/dcn42_smu.h | 2 ++ drivers/gpu/drm/amd/display/dc/core/dc.c | 3 ++ drivers/gpu/drm/amd/display/dc/dc.h | 9 ++++++ .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 10 +++++++ .../gpu/drm/amd/display/dc/inc/hw/clk_mgr.h | 10 +++++++ 8 files changed, 91 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c index 3baac7fa313a..c7b9bad93a93 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c @@ -375,6 +375,24 @@ void dcn42_enable_pme_wa(struct clk_mgr *clk_mgr_base) dcn42_smu_enable_pme_wa(clk_mgr); } +void dcn42_notify_cstate_disable(struct clk_mgr *clk_mgr_base, bool disable) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + bool target_allow = !disable; + + DC_LOGGER_INIT(clk_mgr_base->ctx->logger); + + /* Idempotent: only send when the cached vote actually changes. */ + if (clk_mgr_base->clks.cstate_allow == target_allow) + return; + + if (dcn42_smu_set_df_cstate_disable(clk_mgr, disable)) + clk_mgr_base->clks.cstate_allow = target_allow; + else + DC_LOG_WARNING("%s: PMFW did not ack DfCstateDisable(%s); leaving cstate_allow=%d to retry\n", + __func__, disable ? "Disable" : "Allow", clk_mgr_base->clks.cstate_allow); +} + bool dcn42_are_clock_states_equal(struct dc_clocks *a, struct dc_clocks *b) @@ -594,6 +612,15 @@ void dcn42_init_clocks(struct clk_mgr *clk_mgr_base) init_clk_states(clk_mgr_base); + /* + * DF C-state policy + * D0 entry must NOT send a PMFW message, but must unconditionally clear + * the cached vote so the next allow-side transition (prepare_bandwidth + * or dc_power_down_on_boot) is guaranteed to issue a fresh + * DfCstateDisable(Allow) and resync DAL with PMFW. + */ + clk_mgr_base->clks.cstate_allow = false; + // to adjust dp_dto reference clock if ssc is enable otherwise to apply dprefclk if (dcn42_is_spll_ssc_enabled(clk_mgr_base)) clk_mgr_base->dp_dto_source_clock_in_khz = @@ -1038,6 +1065,7 @@ static struct clk_mgr_funcs dcn42_funcs = { .get_max_clock_khz = dcn42_get_max_clock_khz, .get_dispclk_from_dentist = dcn42_get_dispclk_from_dentist, .is_smu_present = dcn42_is_smu_present, + .notify_cstate_disable = dcn42_notify_cstate_disable, }; struct clk_mgr_funcs dcn42_fpga_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h index 9568ca06f00f..330242747fff 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h @@ -67,6 +67,7 @@ unsigned int dcn42_convert_wck_ratio(uint8_t wck_ratio); extern struct dcn42_ss_info_table dcn42_ss_info_table; void dcn42_build_watermark_ranges(struct clk_bw_params *bw_params, struct dcn42_watermarks *table); void dcn42_enable_pme_wa(struct clk_mgr *clk_mgr_base); +void dcn42_notify_cstate_disable(struct clk_mgr *clk_mgr_base, bool disable); void dcn42_notify_wm_ranges(struct clk_mgr *clk_mgr_base); void dcn42_set_low_power_state(struct clk_mgr *clk_mgr_base); void dcn42_exit_low_power_state(struct clk_mgr *clk_mgr_base); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c index 6d0012b7d6dc..5e8d979f25f0 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c @@ -85,7 +85,8 @@ #define DALSMC_MSG_DispIPS2Exit 0x11 ///< Display IPS2 exit #define DALSMC_MSG_QueryIPS2Support 0x12 ///< Return 1: support; else not supported -#define DALSMC_Message_Count 0x13 ///< Total number of VBIS and DAL messages +#define DALSMC_MSG_DfCstateDisable 0x13 ///< DCN DF C-state vote (PMFW FWDEV-193711): param 0 = Allow, 1 = Disable +#define DALSMC_Message_Count 0x14 ///< Total number of VBIS and DAL messages /** @}*/ @@ -428,3 +429,29 @@ void dcn42_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) enable); smu_print("%s: smu_set_dtbclk = %d\n", __func__, enable ? 1 : 0); } + +/* + * Vote DCN's DF C-state policy to PMFW. param: 0 = Allow, 1 = Disable. + * Returns true only when PMFW acknowledged the vote (or there is no SMU to + * talk to, in which case there is no DF arbiter to satisfy). On a non-OK + * response the caller must NOT update its cached cstate_allow so the next + * transition retries + */ +bool dcn42_smu_set_df_cstate_disable(struct clk_mgr_internal *clk_mgr, bool disable) +{ + int retv; + + if (!clk_mgr->smu_present) + return true; + + retv = dcn42_smu_send_msg_with_param( + clk_mgr, + DALSMC_MSG_DfCstateDisable, + disable ? 1 : 0); + + smu_print("%s: DfCstateDisable param = %d, return = %d\n", + __func__, disable ? 1 : 0, retv); + + /* dcn42_smu_send_msg_with_param() returns -1 on a non-OK PMFW response. */ + return retv != -1; +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h index 8ba7ff04dc05..10c67326ff7e 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h @@ -187,4 +187,6 @@ void dcn42_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *cl int dcn42_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr); int dcn42_smu_get_dprefclk(struct clk_mgr_internal *clk_mgr); +bool dcn42_smu_set_df_cstate_disable(struct clk_mgr_internal *clk_mgr, bool disable); + #endif /* DAL_DC_42_SMU_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 318c3b28e918..24a01e07a44c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -6544,6 +6544,9 @@ void dc_power_down_on_boot(struct dc *dc) if (dc->caps.ips_support) dc_exit_ips_for_hw_access(dc); dc->hwss.power_down_on_boot(dc); + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, false); } } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 743dde3d10ab..fcaa17b9ab1a 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -759,6 +759,15 @@ struct dc_clocks { * Elements below are not compared for the purposes of * optimization required */ + + /* + * @cstate_allow + * + * DCN's DF C-state vote as last successfully acknowledged by PMFW. + * false = DCN does NOT permit DF C-state; + * true = DCN permits DF C-state; + */ + bool cstate_allow; bool prev_p_state_change_support; bool fclk_prev_p_state_change_support; int num_ways; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 4376bf26f4ce..151a29bf0e9d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -946,7 +946,11 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, } void dcn42_hardware_release(struct dc *dc) { + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, true); + dcn35_hardware_release(dc); + dc_dmub_srv_release_hw(dc); } @@ -1082,6 +1086,12 @@ void dcn42_prepare_bandwidth( } dcn401_prepare_bandwidth(dc, context); + + /* valid C-state watermarks have now been committed to HW, so it + * is safe to vote "allow" to PMFW. + */ + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, false); } void dcn42_optimize_bandwidth(struct dc *dc, struct dc_state *context) diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index 68dc2d4ba7ca..ec678bd249ef 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -412,6 +412,16 @@ struct clk_mgr_funcs { struct clk_mgr *clk_mgr, struct dc_requested_memory_qos *qos); + /** + * notify_cstate_disable - Vote DCN's DF C-state policy to PMFW. + * @disable: true -> vote "disable" + * false -> vote "allow" + * Sends the message only when the cached dc_clocks.cstate_allow would + * change, then updates the cache on an OK response (idempotent no-op + * otherwise). + */ + void (*notify_cstate_disable)(struct clk_mgr *clk_mgr, bool disable); + void (*build_clock_update_for_bls)(struct clk_mgr *clk_mgr, struct dc_state *context, bool safe_to_lower, struct block_sequence_state *seq_state); -- 2.43.0
