]> www.infradead.org Git - nvme.git/commitdiff
drm/amd/display: Improve x86 and dmub ips handshake
authorDuncan Ma <duncan.ma@amd.com>
Mon, 28 Aug 2023 19:48:27 +0000 (15:48 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 26 Sep 2023 21:00:21 +0000 (17:00 -0400)
[Why]
There is a race condition between x86 and dmcub fw when attempting to exit
IPS2. Scenarios including exiting IPS2 before IPS2 has been entered. This
can cause unexpected hang when DMCUB attempt to exit while PMFW still
tries to enter IPS2.

[How]
A new design has been introduced to remove race conditions and improve the
handshake between x86 and DMCUB. An AON scratch register is borrowed from
PMFW to determine whether DMCUB has committed to IPS entry or not.

In the case when dmcub has committed IPS entry, x86 must poll until an exit
event occurred either from DMCUB(IPS1) or PMFW(IPS2). x86 will wait
upperbound of evaluation and IPS entry time to ensure IPS2 exit event has
been sent to PMFW.

Reviewed-by: Charlene Liu <charlene.liu@amd.com>
Acked-by: Wayne Lin <wayne.lin@amd.com>
Signed-off-by: Duncan Ma <duncan.ma@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.c
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.h
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
drivers/gpu/drm/amd/display/dc/dcn35/dcn35_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn35/dcn35_hwseq.h
drivers/gpu/drm/amd/display/dc/dcn35/dcn35_init.c
drivers/gpu/drm/amd/display/dc/dcn35/dcn35_resource.c
drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h

index 819d273f011d77d05728e0ce699ac720803df89d..c9c7baf45be36746434406a3d5074518390287a9 100644 (file)
@@ -738,6 +738,34 @@ static void dcn35_set_low_power_state(struct clk_mgr *clk_mgr_base)
        }
 }
 
+static void dcn35_set_idle_state(struct clk_mgr *clk_mgr_base, bool allow_idle)
+{
+       struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+       struct dc *dc = clk_mgr_base->ctx->dc;
+       uint32_t val = dcn35_smu_read_ips_scratch(clk_mgr);
+
+       if (dc->debug.disable_ips == 0) {
+               val |= DMUB_IPS1_ALLOW_MASK;
+               val |= DMUB_IPS2_ALLOW_MASK;
+       } else if (dc->debug.disable_ips == DMUB_IPS_DISABLE_IPS1) {
+               val = val & ~DMUB_IPS1_ALLOW_MASK;
+               val = val & ~DMUB_IPS2_ALLOW_MASK;
+       } else if (dc->debug.disable_ips == DMUB_IPS_DISABLE_IPS2) {
+               val |= DMUB_IPS1_ALLOW_MASK;
+               val = val & ~DMUB_IPS2_ALLOW_MASK;
+       } else if (dc->debug.disable_ips == DMUB_IPS_DISABLE_IPS2_Z10) {
+               val |= DMUB_IPS1_ALLOW_MASK;
+               val |= DMUB_IPS2_ALLOW_MASK;
+       }
+
+       if (!allow_idle) {
+               val = val & ~DMUB_IPS1_ALLOW_MASK;
+               val = val & ~DMUB_IPS2_ALLOW_MASK;
+       }
+
+       dcn35_smu_write_ips_scratch(clk_mgr, val);
+}
+
 static void dcn35_exit_low_power_state(struct clk_mgr *clk_mgr_base)
 {
        struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
@@ -757,6 +785,13 @@ static bool dcn35_is_ips_supported(struct clk_mgr *clk_mgr_base)
        return ips_supported;
 }
 
+static uint32_t dcn35_get_idle_state(struct clk_mgr *clk_mgr_base)
+{
+       struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+
+       return dcn35_smu_read_ips_scratch(clk_mgr);
+}
+
 static void dcn35_init_clocks_fpga(struct clk_mgr *clk_mgr)
 {
        dcn35_init_clocks(clk_mgr);
@@ -844,6 +879,8 @@ static struct clk_mgr_funcs dcn35_funcs = {
        .set_low_power_state = dcn35_set_low_power_state,
        .exit_low_power_state = dcn35_exit_low_power_state,
        .is_ips_supported = dcn35_is_ips_supported,
+       .set_idle_state = dcn35_set_idle_state,
+       .get_idle_state = dcn35_get_idle_state
 };
 
 struct clk_mgr_funcs dcn35_fpga_funcs = {
index f42c0a727dc837f8dc1a5e63b92906087e134cf5..cf74e69cb2a168ec9134952f0be1ad75ecd56986 100644 (file)
@@ -444,9 +444,9 @@ void dcn35_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *cl
                        enable);
 }
 
-void dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr)
+int dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr)
 {
-       dcn35_smu_send_msg_with_param(
+       return dcn35_smu_send_msg_with_param(
                clk_mgr,
                VBIOSSMC_MSG_DispPsrExit,
                0);
@@ -459,3 +459,13 @@ int dcn35_smu_get_ips_supported(struct clk_mgr_internal *clk_mgr)
                        VBIOSSMC_MSG_QueryIPS2Support,
                        0);
 }
+
+void dcn35_smu_write_ips_scratch(struct clk_mgr_internal *clk_mgr, uint32_t param)
+{
+       REG_WRITE(MP1_SMN_C2PMSG_71, param);
+}
+
+uint32_t dcn35_smu_read_ips_scratch(struct clk_mgr_internal *clk_mgr)
+{
+       return REG_READ(MP1_SMN_C2PMSG_71);
+}
index 4d441d6db8d0018554e28ef90e7310d10dda8401..8fb65a49351b288c35069052655e9e9e4bb31cdd 100644 (file)
@@ -174,8 +174,10 @@ void dcn35_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zst
 void dcn35_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable);
 void dcn35_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable);
 
-void dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr);
+int dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr);
 int dcn35_smu_get_ips_supported(struct clk_mgr_internal *clk_mgr);
 int dcn35_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr);
 int dcn35_smu_get_dprefclk(struct clk_mgr_internal *clk_mgr);
+void dcn35_smu_write_ips_scratch(struct clk_mgr_internal *clk_mgr, uint32_t param);
+uint32_t dcn35_smu_read_ips_scratch(struct clk_mgr_internal *clk_mgr);
 #endif /* DAL_DC_35_SMU_H_ */
index c0081d756694422a7564327b5d00348ee79f2dc3..c15b4d5c4e6c78e62d902847402a655c56070cea 100644 (file)
@@ -965,6 +965,8 @@ struct dc_debug_options {
        bool replay_skip_crtc_disabled;
        bool ignore_pg;/*do nothing, let pmfw control it*/
        bool psp_disabled_wa;
+       unsigned int ips2_eval_delay_us;
+       unsigned int ips2_entry_delay_us;
 };
 
 struct gpu_info_soc_bounding_box_v1_0;
index 91b3a4610957bf7d05f43a4dd2c82a72d22e6473..973d7938f47b89404f6172897663c0ec9c60e583 100644 (file)
@@ -1100,31 +1100,64 @@ void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle)
 
        cmd.idle_opt_notify_idle.cntl_data.driver_idle = allow_idle;
 
-       dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
+       if (allow_idle) {
+               if (dc->hwss.set_idle_state)
+                       dc->hwss.set_idle_state(dc, true);
+       }
 
-       if (allow_idle)
-               udelay(500);
+       dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
 }
 
 void dc_dmub_srv_exit_low_power_state(const struct dc *dc)
 {
+       uint32_t allow_state = 0;
+       uint32_t commit_state = 0;
+
        if (dc->debug.dmcub_emulation)
                return;
 
        if (!dc->idle_optimizations_allowed)
                return;
 
-       // Tell PMFW to exit low power state
-       if (dc->clk_mgr->funcs->exit_low_power_state)
-               dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
+       if (dc->hwss.get_idle_state &&
+               dc->hwss.set_idle_state &&
+               dc->clk_mgr->funcs->exit_low_power_state) {
+
+               allow_state = dc->hwss.get_idle_state(dc);
+               dc->hwss.set_idle_state(dc, false);
+
+               if (allow_state & DMUB_IPS2_ALLOW_MASK) {
+                       // Wait for evaluation time
+                       udelay(dc->debug.ips2_eval_delay_us);
+                       commit_state = dc->hwss.get_idle_state(dc);
+                       if (commit_state & DMUB_IPS2_COMMIT_MASK) {
+                               // Tell PMFW to exit low power state
+                               dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
+
+                               // Wait for IPS2 entry upper bound
+                               udelay(dc->debug.ips2_entry_delay_us);
+                               dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
 
-       // Wait for dmcub to load up
-       dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
+                               do {
+                                       commit_state = dc->hwss.get_idle_state(dc);
+                               } while (commit_state & DMUB_IPS2_COMMIT_MASK);
 
-       // Notify dmcub disallow idle
-       dc_dmub_srv_notify_idle(dc, false);
+                               if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true))
+                                       ASSERT(0);
+
+                               return;
+                       }
+               }
+
+               dc_dmub_srv_notify_idle(dc, false);
+               if (allow_state & DMUB_IPS1_ALLOW_MASK) {
+                       do {
+                               commit_state = dc->hwss.get_idle_state(dc);
+                       } while (commit_state & DMUB_IPS1_COMMIT_MASK);
+               }
+       }
 
-       // Confirm dmu is powered up
-       dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
+       if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true))
+               ASSERT(0);
 }
 
index 06960fada059750c5f87ea1d13abeb4a9c7e5eca..03ef7820139a1c103dbf9e160fbf5b946189c91f 100644 (file)
@@ -647,18 +647,10 @@ bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable)
 
        // TODO: review other cases when idle optimization is allowed
 
-       if (!enable) {
-               // Tell PMFW to exit low power state
-               if (dc->clk_mgr->funcs->exit_low_power_state)
-                       dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
-
-               dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
-       }
-
-       dc_dmub_srv_notify_idle(dc, enable);
-
        if (!enable)
-               dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
+               dc_dmub_srv_exit_low_power_state(dc);
+       else
+               dc_dmub_srv_notify_idle(dc, enable);
 
        return true;
 }
@@ -1192,3 +1184,19 @@ void dcn35_optimize_bandwidth(
                        dc->hwss.root_clock_control(dc, &pg_update_state, false);
        }
 }
+
+void dcn35_set_idle_state(const struct dc *dc, bool allow_idle)
+{
+       // TODO: Find a more suitable communcation
+       if (dc->clk_mgr->funcs->set_idle_state)
+               dc->clk_mgr->funcs->set_idle_state(dc->clk_mgr, allow_idle);
+}
+
+uint32_t dcn35_get_idle_state(const struct dc *dc)
+{
+       // TODO: Find a more suitable communcation
+       if (dc->clk_mgr->funcs->get_idle_state)
+               return dc->clk_mgr->funcs->get_idle_state(dc->clk_mgr);
+
+       return 0;
+}
index 7c0ff7b163a91af05e18c259348f39d1ccd2010c..14bbdb0fa634d74a2c4ade181cdcbe3f7e8077a1 100644 (file)
@@ -79,4 +79,7 @@ void dcn35_dsc_pg_control(
                struct dce_hwseq *hws,
                unsigned int dsc_inst,
                bool power_on);
+
+void dcn35_set_idle_state(const struct dc *dc, bool allow_idle);
+uint32_t dcn35_get_idle_state(const struct dc *dc);
 #endif /* __DC_HWSS_DCN35_H__ */
index d68efe5c64a403d3c5199769a7c566aae3da403f..a9553a87b9599c3d7c077cb688fe386107036a12 100644 (file)
@@ -117,7 +117,9 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
        .calc_blocks_to_gate = dcn35_calc_blocks_to_gate,
        .calc_blocks_to_ungate = dcn35_calc_blocks_to_ungate,
        .block_power_control = dcn35_block_power_control,
-       .root_clock_control = dcn35_root_clock_control
+       .root_clock_control = dcn35_root_clock_control,
+       .set_idle_state = dcn35_set_idle_state,
+       .get_idle_state = dcn35_get_idle_state
 };
 
 static const struct hwseq_private_funcs dcn35_private_funcs = {
index 06fd02084d7c9f5ea8ed20a6e4d96a7f187da0ec..67ff19d4116d848a3bdee96c727b9792a167b086 100644 (file)
@@ -748,6 +748,8 @@ static const struct dc_debug_options debug_defaults_drv = {
        .ignore_pg = true,
        .psp_disabled_wa = true,
        .disable_ips = true,
+       .ips2_eval_delay_us = 200,
+       .ips2_entry_delay_us = 400
 };
 
 static const struct dc_panel_config panel_config_defaults = {
index cb2dc3f75ae2888cb1807599ea8fae767750b29c..fa9614bcb1605f6b75f116ca5387df338bc3dc4c 100644 (file)
@@ -262,6 +262,8 @@ struct clk_mgr_funcs {
        void (*set_low_power_state)(struct clk_mgr *clk_mgr);
        void (*exit_low_power_state)(struct clk_mgr *clk_mgr);
        bool (*is_ips_supported)(struct clk_mgr *clk_mgr);
+       void (*set_idle_state)(struct clk_mgr *clk_mgr, bool allow_idle);
+       uint32_t (*get_idle_state)(struct clk_mgr *clk_mgr);
 
        void (*init_clocks)(struct clk_mgr *clk_mgr);
 
index e0dd56182841269d09d87befc1368c3cb51d2412..d4d59a91666894d0c7e03e1de0d8457bca558724 100644 (file)
@@ -422,7 +422,8 @@ struct hw_sequencer_funcs {
                struct pg_block_update *update_state, bool power_on);
        void (*root_clock_control)(struct dc *dc,
                struct pg_block_update *update_state, bool power_on);
-
+       void (*set_idle_state)(const struct dc *dc, bool allow_idle);
+       uint32_t (*get_idle_state)(const struct dc *dc);
        void (*commit_subvp_config)(struct dc *dc, struct dc_state *context);
        void (*enable_phantom_streams)(struct dc *dc, struct dc_state *context);
        void (*subvp_pipe_control_lock)(struct dc *dc,