diff options
author | Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> | 2023-10-05 18:48:44 +0300 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2023-10-27 01:57:58 +0300 |
commit | da2d16fcdda344b18ec9a4a55dff9805d5d781d2 (patch) | |
tree | 11ed56064172a1f5ddfe954b56111967cec09c1b /drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | |
parent | 488bb99d42e607a40524ee1514b0b1246b1f69c8 (diff) | |
download | linux-da2d16fcdda344b18ec9a4a55dff9805d5d781d2.tar.xz |
drm/amd/display: Fix IPS handshake for idle optimizations
[Why]
Intermittent reboot hangs are observed introduced by
"Improve x86 and dmub ips handshake".
[How]
Bring back the commit but fix the polling.
Avoid hanging in place forever by bounding the delay and ensure that
we still message DMCUB on IPS2 exit to notify driver idle has been
cleared.
Reviewed-by: Duncan Ma <duncan.ma@amd.com>
Reviewed-by: Jun Lei <jun.lei@amd.com>
Acked-by: Roman Li <roman.li@amd.com>
Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 73 |
1 files changed, 61 insertions, 12 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index a388f34c6d04..ba142bef626b 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -1100,31 +1100,80 @@ 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) { + const uint32_t max_num_polls = 10000; + uint32_t allow_state = 0; + uint32_t commit_state = 0; + uint32_t i; + 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); + + for (i = 0; i < max_num_polls; ++i) { + commit_state = dc->hwss.get_idle_state(dc); + if (!(commit_state & DMUB_IPS2_COMMIT_MASK)) + break; + + udelay(1); + } + ASSERT(i < max_num_polls); + + if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true)) + ASSERT(0); + + /* TODO: See if we can return early here - IPS2 should go + * back directly to IPS0 and clear the flags, but it will + * be safer to directly notify DMCUB of this. + */ + allow_state = dc->hwss.get_idle_state(dc); + } + } - // Wait for dmcub to load up - dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true); + dc_dmub_srv_notify_idle(dc, false); + if (allow_state & DMUB_IPS1_ALLOW_MASK) { + for (i = 0; i < max_num_polls; ++i) { + commit_state = dc->hwss.get_idle_state(dc); + if (!(commit_state & DMUB_IPS1_COMMIT_MASK)) + break; - // Notify dmcub disallow idle - dc_dmub_srv_notify_idle(dc, false); + udelay(1); + } + ASSERT(i < max_num_polls); + } + } - // 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); } |