drm/amd/display: Register DCN as a PMFW DF C-state client on DCN42

[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 <nicholas.kazlauskas@amd.com>
Signed-off-by: Leo Chen <leo.chen@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Leo Chen 2026-07-09 14:47:38 -04:00 committed by Alex Deucher
parent a63f63f473
commit 53845307d5
8 changed files with 91 additions and 1 deletions

View File

@ -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 = {

View File

@ -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);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -6543,6 +6543,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);
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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);