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

Reply via email to