--- /dev/null
+From stable+bounces-179564-greg=kroah.com@vger.kernel.org Sun Sep 14 13:48:28 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 14 Sep 2025 07:48:17 -0400
+Subject: drm/amd/display: Destroy cached state in complete() callback
+To: stable@vger.kernel.org
+Cc: Mario Limonciello <mario.limonciello@amd.com>, Alex Hung <alex.hung@amd.com>, Alex Deucher <alexander.deucher@amd.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20250914114818.12813-1-sashal@kernel.org>
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit 45cc102f8e6520ac07637c7e09f61dcc3772e125 ]
+
+[Why]
+When the suspend sequence has been aborted after prepare() but
+before suspend() the resume() callback never gets called. The PM core
+will call complete() when this happens. As the state has been cached
+in prepare() it needs to be destroyed in complete() if it's still around.
+
+[How]
+Create a helper for destroying cached state and call it both in resume()
+and complete() callbacks. If resume has been called the state will be
+destroyed and it's a no-op for complete(). If resume hasn't been called
+(such as an aborted suspend) then destroy the state in complete().
+
+Fixes: 50e0bae34fa6 ("drm/amd/display: Add and use new dm_prepare_suspend() callback")
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Link: https://lore.kernel.org/r/20250602014432.3538345-4-superm1@kernel.org
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: 60f71f0db7b1 ("drm/amd/display: Drop dm_prepare_suspend() and dm_complete()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 100 +++++++++++++---------
+ 1 file changed, 60 insertions(+), 40 deletions(-)
+
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -3084,6 +3084,64 @@ static int dm_cache_state(struct amdgpu_
+ return adev->dm.cached_state ? 0 : r;
+ }
+
++static void dm_destroy_cached_state(struct amdgpu_device *adev)
++{
++ struct amdgpu_display_manager *dm = &adev->dm;
++ struct drm_device *ddev = adev_to_drm(adev);
++ struct dm_plane_state *dm_new_plane_state;
++ struct drm_plane_state *new_plane_state;
++ struct dm_crtc_state *dm_new_crtc_state;
++ struct drm_crtc_state *new_crtc_state;
++ struct drm_plane *plane;
++ struct drm_crtc *crtc;
++ int i;
++
++ if (!dm->cached_state)
++ return;
++
++ /* Force mode set in atomic commit */
++ for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
++ new_crtc_state->active_changed = true;
++ dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
++ reset_freesync_config_for_crtc(dm_new_crtc_state);
++ }
++
++ /*
++ * atomic_check is expected to create the dc states. We need to release
++ * them here, since they were duplicated as part of the suspend
++ * procedure.
++ */
++ for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
++ dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
++ if (dm_new_crtc_state->stream) {
++ WARN_ON(kref_read(&dm_new_crtc_state->stream->refcount) > 1);
++ dc_stream_release(dm_new_crtc_state->stream);
++ dm_new_crtc_state->stream = NULL;
++ }
++ dm_new_crtc_state->base.color_mgmt_changed = true;
++ }
++
++ for_each_new_plane_in_state(dm->cached_state, plane, new_plane_state, i) {
++ dm_new_plane_state = to_dm_plane_state(new_plane_state);
++ if (dm_new_plane_state->dc_state) {
++ WARN_ON(kref_read(&dm_new_plane_state->dc_state->refcount) > 1);
++ dc_plane_state_release(dm_new_plane_state->dc_state);
++ dm_new_plane_state->dc_state = NULL;
++ }
++ }
++
++ drm_atomic_helper_resume(ddev, dm->cached_state);
++
++ dm->cached_state = NULL;
++}
++
++static void dm_complete(struct amdgpu_ip_block *ip_block)
++{
++ struct amdgpu_device *adev = ip_block->adev;
++
++ dm_destroy_cached_state(adev);
++}
++
+ static int dm_prepare_suspend(struct amdgpu_ip_block *ip_block)
+ {
+ struct amdgpu_device *adev = ip_block->adev;
+@@ -3317,12 +3375,6 @@ static int dm_resume(struct amdgpu_ip_bl
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter iter;
+- struct drm_crtc *crtc;
+- struct drm_crtc_state *new_crtc_state;
+- struct dm_crtc_state *dm_new_crtc_state;
+- struct drm_plane *plane;
+- struct drm_plane_state *new_plane_state;
+- struct dm_plane_state *dm_new_plane_state;
+ struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state);
+ enum dc_connection_type new_connection_type = dc_connection_none;
+ struct dc_state *dc_state;
+@@ -3481,40 +3533,7 @@ static int dm_resume(struct amdgpu_ip_bl
+ }
+ drm_connector_list_iter_end(&iter);
+
+- /* Force mode set in atomic commit */
+- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
+- new_crtc_state->active_changed = true;
+- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
+- reset_freesync_config_for_crtc(dm_new_crtc_state);
+- }
+-
+- /*
+- * atomic_check is expected to create the dc states. We need to release
+- * them here, since they were duplicated as part of the suspend
+- * procedure.
+- */
+- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
+- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
+- if (dm_new_crtc_state->stream) {
+- WARN_ON(kref_read(&dm_new_crtc_state->stream->refcount) > 1);
+- dc_stream_release(dm_new_crtc_state->stream);
+- dm_new_crtc_state->stream = NULL;
+- }
+- dm_new_crtc_state->base.color_mgmt_changed = true;
+- }
+-
+- for_each_new_plane_in_state(dm->cached_state, plane, new_plane_state, i) {
+- dm_new_plane_state = to_dm_plane_state(new_plane_state);
+- if (dm_new_plane_state->dc_state) {
+- WARN_ON(kref_read(&dm_new_plane_state->dc_state->refcount) > 1);
+- dc_plane_state_release(dm_new_plane_state->dc_state);
+- dm_new_plane_state->dc_state = NULL;
+- }
+- }
+-
+- drm_atomic_helper_resume(ddev, dm->cached_state);
+-
+- dm->cached_state = NULL;
++ dm_destroy_cached_state(adev);
+
+ /* Do mst topology probing after resuming cached state*/
+ drm_connector_list_iter_begin(ddev, &iter);
+@@ -3563,6 +3582,7 @@ static const struct amd_ip_funcs amdgpu_
+ .prepare_suspend = dm_prepare_suspend,
+ .suspend = dm_suspend,
+ .resume = dm_resume,
++ .complete = dm_complete,
+ .is_idle = dm_is_idle,
+ .wait_for_idle = dm_wait_for_idle,
+ .check_soft_reset = dm_check_soft_reset,
--- /dev/null
+From stable+bounces-179565-greg=kroah.com@vger.kernel.org Sun Sep 14 13:48:29 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 14 Sep 2025 07:48:18 -0400
+Subject: drm/amd/display: Drop dm_prepare_suspend() and dm_complete()
+To: stable@vger.kernel.org
+Cc: "Mario Limonciello (AMD)" <mario.limonciello@amd.com>, "Harry Wentland" <harry.wentland@amd.com>, "Przemysław Kopa" <prz.kopa@gmail.com>, Kalvin <hikaph+oss@gmail.com>, "Alex Deucher" <alexander.deucher@amd.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20250914114818.12813-2-sashal@kernel.org>
+
+From: "Mario Limonciello (AMD)" <mario.limonciello@amd.com>
+
+[ Upstream commit 60f71f0db7b12f303789ef59949e38ee5838ee8b ]
+
+[Why]
+dm_prepare_suspend() was added in commit 50e0bae34fa6b
+("drm/amd/display: Add and use new dm_prepare_suspend() callback")
+to allow display to turn off earlier in the suspend sequence.
+
+This caused a regression that HDMI audio sometimes didn't work
+properly after resume unless audio was playing during suspend.
+
+[How]
+Drop dm_prepare_suspend() callback. All code in it will still run
+during dm_suspend(). Also drop unnecessary dm_complete() callback.
+dm_complete() was used for failed prepare and also for any case
+of successful resume. The code in it already runs in dm_resume().
+
+This change will introduce more time that the display is turned on
+during suspend sequence. The compositor can turn it off sooner if
+desired.
+
+Cc: Harry Wentland <harry.wentland@amd.com>
+Reported-by: Przemysław Kopa <prz.kopa@gmail.com>
+Closes: https://lore.kernel.org/amd-gfx/1cea0d56-7739-4ad9-bf8e-c9330faea2bb@kernel.org/T/#m383d9c08397043a271b36c32b64bb80e524e4b0f
+Reported-by: Kalvin <hikaph+oss@gmail.com>
+Closes: https://github.com/alsa-project/alsa-lib/issues/465
+Closes: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4809
+Fixes: 50e0bae34fa6b ("drm/amd/display: Add and use new dm_prepare_suspend() callback")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Acked-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 2fd653b9bb5aacec5d4c421ab290905898fe85a2)
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 21 ---------------------
+ 1 file changed, 21 deletions(-)
+
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -3135,25 +3135,6 @@ static void dm_destroy_cached_state(stru
+ dm->cached_state = NULL;
+ }
+
+-static void dm_complete(struct amdgpu_ip_block *ip_block)
+-{
+- struct amdgpu_device *adev = ip_block->adev;
+-
+- dm_destroy_cached_state(adev);
+-}
+-
+-static int dm_prepare_suspend(struct amdgpu_ip_block *ip_block)
+-{
+- struct amdgpu_device *adev = ip_block->adev;
+-
+- if (amdgpu_in_reset(adev))
+- return 0;
+-
+- WARN_ON(adev->dm.cached_state);
+-
+- return dm_cache_state(adev);
+-}
+-
+ static int dm_suspend(struct amdgpu_ip_block *ip_block)
+ {
+ struct amdgpu_device *adev = ip_block->adev;
+@@ -3579,10 +3560,8 @@ static const struct amd_ip_funcs amdgpu_
+ .early_fini = amdgpu_dm_early_fini,
+ .hw_init = dm_hw_init,
+ .hw_fini = dm_hw_fini,
+- .prepare_suspend = dm_prepare_suspend,
+ .suspend = dm_suspend,
+ .resume = dm_resume,
+- .complete = dm_complete,
+ .is_idle = dm_is_idle,
+ .wait_for_idle = dm_wait_for_idle,
+ .check_soft_reset = dm_check_soft_reset,