]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/amd/display: Full exit out of IPS2 when all allow signals have been cleared
authorLeo Chen <leo.chen@amd.com>
Thu, 3 Oct 2024 16:20:23 +0000 (12:20 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 19:03:46 +0000 (20:03 +0100)
[ Upstream commit 0fe33e115fec305c35c66b78ad26e3755ab54b9c ]

[Why]
A race condition occurs between cursor movement and vertical interrupt control
thread from OS, with both threads trying to exit IPS2.
Vertical interrupt control thread clears the prev driver allow signal while not fully
finishing the IPS2 exit process.

[How]
We want to detect all the allow signals have been cleared before we perform the full exit.

Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Leo Chen <leo.chen@amd.com>
Signed-off-by: Roman Li <roman.li@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h

index 1e7de0f03290a30e9083f869b36a3d14906b2ecb..ec5009f411eb0dc6cf9772b2098619ab14b8f7b2 100644 (file)
@@ -1294,6 +1294,8 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle)
 
                memset(&new_signals, 0, sizeof(new_signals));
 
+               new_signals.bits.allow_idle = 1; /* always set */
+
                if (dc->config.disable_ips == DMUB_IPS_ENABLE ||
                    dc->config.disable_ips == DMUB_IPS_DISABLE_DYNAMIC) {
                        new_signals.bits.allow_pg = 1;
@@ -1389,7 +1391,7 @@ static void dc_dmub_srv_exit_low_power_state(const struct dc *dc)
                 */
                dc_dmub_srv->needs_idle_wake = false;
 
-               if (prev_driver_signals.bits.allow_ips2 &&
+               if ((prev_driver_signals.bits.allow_ips2 || prev_driver_signals.all == 0) &&
                    (!dc->debug.optimize_ips_handshake ||
                     ips_fw->signals.bits.ips2_commit || !ips_fw->signals.bits.in_idle)) {
                        DC_LOG_IPS(
@@ -1450,7 +1452,7 @@ static void dc_dmub_srv_exit_low_power_state(const struct dc *dc)
                }
 
                dc_dmub_srv_notify_idle(dc, false);
-               if (prev_driver_signals.bits.allow_ips1) {
+               if (prev_driver_signals.bits.allow_ips1 || prev_driver_signals.all == 0) {
                        DC_LOG_IPS(
                                "wait for IPS1 commit clear (ips1_commit=%u ips2_commit=%u)",
                                ips_fw->signals.bits.ips1_commit,
index ebcf68bfae2b32d2e6e5b27e47ba0b876087dcfe..7835100b37c41e52a695241cd4fe5e878f547d5f 100644 (file)
@@ -747,7 +747,8 @@ union dmub_shared_state_ips_driver_signals {
                uint32_t allow_ips1 : 1; /**< 1 is IPS1 is allowed */
                uint32_t allow_ips2 : 1; /**< 1 is IPS1 is allowed */
                uint32_t allow_z10 : 1; /**< 1 if Z10 is allowed */
-               uint32_t reserved_bits : 28; /**< Reversed bits */
+               uint32_t allow_idle : 1; /**< 1 if driver is allowing idle */
+               uint32_t reserved_bits : 27; /**< Reversed bits */
        } bits;
        uint32_t all;
 };