--- /dev/null
+From 887f75cfd0da44c19dda93b2ff9e70ca8792cdc1 Mon Sep 17 00:00:00 2001
+From: Kai-Heng Feng <kai.heng.feng@canonical.com>
+Date: Thu, 7 Apr 2022 20:12:28 +0800
+Subject: drm/amdgpu: Ensure HDA function is suspended before ASIC reset
+
+From: Kai-Heng Feng <kai.heng.feng@canonical.com>
+
+commit 887f75cfd0da44c19dda93b2ff9e70ca8792cdc1 upstream.
+
+DP/HDMI audio on AMD PRO VII stops working after S3:
+[ 149.450391] amdgpu 0000:63:00.0: amdgpu: MODE1 reset
+[ 149.450395] amdgpu 0000:63:00.0: amdgpu: GPU mode1 reset
+[ 149.450494] amdgpu 0000:63:00.0: amdgpu: GPU psp mode1 reset
+[ 149.983693] snd_hda_intel 0000:63:00.1: refused to change power state from D0 to D3hot
+[ 150.003439] amdgpu 0000:63:00.0: refused to change power state from D0 to D3hot
+...
+[ 155.432975] snd_hda_intel 0000:63:00.1: CORB reset timeout#2, CORBRP = 65535
+
+The offending commit is daf8de0874ab5b ("drm/amdgpu: always reset the asic in
+suspend (v2)"). Commit 34452ac3038a7 ("drm/amdgpu: don't use BACO for
+reset in S3 ") doesn't help, so the issue is something different.
+
+Assuming that to make HDA resume to D0 fully realized, it needs to be
+successfully put to D3 first. And this guesswork proves working, by
+moving amdgpu_asic_reset() to noirq callback, so it's called after HDA
+function is in D3.
+
+Fixes: daf8de0874ab5b ("drm/amdgpu: always reset the asic in suspend (v2)")
+Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: stable@vger.kernel.org
+Cc: "Limonciello, Mario" <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+@@ -2246,18 +2246,23 @@ static int amdgpu_pmops_suspend(struct d
+ {
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ struct amdgpu_device *adev = drm_to_adev(drm_dev);
+- int r;
+
+ if (amdgpu_acpi_is_s0ix_active(adev))
+ adev->in_s0ix = true;
+ else
+ adev->in_s3 = true;
+- r = amdgpu_device_suspend(drm_dev, true);
+- if (r)
+- return r;
++ return amdgpu_device_suspend(drm_dev, true);
++}
++
++static int amdgpu_pmops_suspend_noirq(struct device *dev)
++{
++ struct drm_device *drm_dev = dev_get_drvdata(dev);
++ struct amdgpu_device *adev = drm_to_adev(drm_dev);
++
+ if (!adev->in_s0ix)
+- r = amdgpu_asic_reset(adev);
+- return r;
++ return amdgpu_asic_reset(adev);
++
++ return 0;
+ }
+
+ static int amdgpu_pmops_resume(struct device *dev)
+@@ -2494,6 +2499,7 @@ static const struct dev_pm_ops amdgpu_pm
+ .prepare = amdgpu_pmops_prepare,
+ .complete = amdgpu_pmops_complete,
+ .suspend = amdgpu_pmops_suspend,
++ .suspend_noirq = amdgpu_pmops_suspend_noirq,
+ .resume = amdgpu_pmops_resume,
+ .freeze = amdgpu_pmops_freeze,
+ .thaw = amdgpu_pmops_thaw,
--- /dev/null
+From e53d9665ab003df0ece8f869fcd3c2bbbecf7190 Mon Sep 17 00:00:00 2001
+From: Mario Limonciello <mario.limonciello@amd.com>
+Date: Wed, 29 Dec 2021 13:58:02 -0600
+Subject: drm/amdgpu: explicitly check for s0ix when evicting resources
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+commit e53d9665ab003df0ece8f869fcd3c2bbbecf7190 upstream.
+
+This codepath should be running in both s0ix and s3, but only does
+currently because s3 and s0ix are both set in the s0ix case.
+
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Acked-by: Evan Quan <evan.quan@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: "Limonciello, Mario" <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -3939,8 +3939,8 @@ void amdgpu_device_fini_sw(struct amdgpu
+ */
+ static void amdgpu_device_evict_resources(struct amdgpu_device *adev)
+ {
+- /* No need to evict vram on APUs for suspend to ram */
+- if (adev->in_s3 && (adev->flags & AMD_IS_APU))
++ /* No need to evict vram on APUs for suspend to ram or s2idle */
++ if ((adev->in_s3 || adev->in_s0ix) && (adev->flags & AMD_IS_APU))
+ return;
+
+ if (amdgpu_ttm_evict_resources(adev, TTM_PL_VRAM))
--- /dev/null
+From 58144d283712c9e80e528e001af6ac5aeee71af2 Mon Sep 17 00:00:00 2001
+From: Nirmoy Das <nirmoy.das@amd.com>
+Date: Wed, 6 Oct 2021 17:55:00 +0200
+Subject: drm/amdgpu: unify BO evicting method in amdgpu_ttm
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Nirmoy Das <nirmoy.das@amd.com>
+
+commit 58144d283712c9e80e528e001af6ac5aeee71af2 upstream.
+
+Unify BO evicting functionality for possible memory
+types in amdgpu_ttm.c.
+
+Signed-off-by: Nirmoy Das <nirmoy.das@amd.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: "Limonciello, Mario" <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 8 ++-----
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 30 ++++++++++++++++++++++------
+ drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 23 ---------------------
+ drivers/gpu/drm/amd/amdgpu/amdgpu_object.h | 1
+ drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 30 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 1
+ 6 files changed, 58 insertions(+), 35 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+@@ -1176,7 +1176,7 @@ static int amdgpu_debugfs_evict_vram(voi
+ return r;
+ }
+
+- *val = amdgpu_bo_evict_vram(adev);
++ *val = amdgpu_ttm_evict_resources(adev, TTM_PL_VRAM);
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+@@ -1189,17 +1189,15 @@ static int amdgpu_debugfs_evict_gtt(void
+ {
+ struct amdgpu_device *adev = (struct amdgpu_device *)data;
+ struct drm_device *dev = adev_to_drm(adev);
+- struct ttm_resource_manager *man;
+ int r;
+
+ r = pm_runtime_get_sync(dev->dev);
+ if (r < 0) {
+- pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
++ pm_runtime_put_autosuspend(dev->dev);
+ return r;
+ }
+
+- man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
+- *val = ttm_resource_manager_evict_all(&adev->mman.bdev, man);
++ *val = amdgpu_ttm_evict_resources(adev, TTM_PL_TT);
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -3928,6 +3928,25 @@ void amdgpu_device_fini_sw(struct amdgpu
+
+ }
+
++/**
++ * amdgpu_device_evict_resources - evict device resources
++ * @adev: amdgpu device object
++ *
++ * Evicts all ttm device resources(vram BOs, gart table) from the lru list
++ * of the vram memory type. Mainly used for evicting device resources
++ * at suspend time.
++ *
++ */
++static void amdgpu_device_evict_resources(struct amdgpu_device *adev)
++{
++ /* No need to evict vram on APUs for suspend to ram */
++ if (adev->in_s3 && (adev->flags & AMD_IS_APU))
++ return;
++
++ if (amdgpu_ttm_evict_resources(adev, TTM_PL_VRAM))
++ DRM_WARN("evicting device resources failed\n");
++
++}
+
+ /*
+ * Suspend & resume.
+@@ -3968,17 +3987,16 @@ int amdgpu_device_suspend(struct drm_dev
+ if (!adev->in_s0ix)
+ amdgpu_amdkfd_suspend(adev, adev->in_runpm);
+
+- /* evict vram memory */
+- amdgpu_bo_evict_vram(adev);
++ /* First evict vram memory */
++ amdgpu_device_evict_resources(adev);
+
+ amdgpu_fence_driver_hw_fini(adev);
+
+ amdgpu_device_ip_suspend_phase2(adev);
+- /* evict remaining vram memory
+- * This second call to evict vram is to evict the gart page table
+- * using the CPU.
++ /* This second call to evict device resources is to evict
++ * the gart page table using the CPU.
+ */
+- amdgpu_bo_evict_vram(adev);
++ amdgpu_device_evict_resources(adev);
+
+ return 0;
+ }
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+@@ -1038,29 +1038,6 @@ void amdgpu_bo_unpin(struct amdgpu_bo *b
+ }
+ }
+
+-/**
+- * amdgpu_bo_evict_vram - evict VRAM buffers
+- * @adev: amdgpu device object
+- *
+- * Evicts all VRAM buffers on the lru list of the memory type.
+- * Mainly used for evicting vram at suspend time.
+- *
+- * Returns:
+- * 0 for success or a negative error code on failure.
+- */
+-int amdgpu_bo_evict_vram(struct amdgpu_device *adev)
+-{
+- struct ttm_resource_manager *man;
+-
+- if (adev->in_s3 && (adev->flags & AMD_IS_APU)) {
+- /* No need to evict vram on APUs for suspend to ram */
+- return 0;
+- }
+-
+- man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
+- return ttm_resource_manager_evict_all(&adev->mman.bdev, man);
+-}
+-
+ static const char *amdgpu_vram_names[] = {
+ "UNKNOWN",
+ "GDDR1",
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
+@@ -304,7 +304,6 @@ int amdgpu_bo_pin(struct amdgpu_bo *bo,
+ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
+ u64 min_offset, u64 max_offset);
+ void amdgpu_bo_unpin(struct amdgpu_bo *bo);
+-int amdgpu_bo_evict_vram(struct amdgpu_device *adev);
+ int amdgpu_bo_init(struct amdgpu_device *adev);
+ void amdgpu_bo_fini(struct amdgpu_device *adev);
+ int amdgpu_bo_set_tiling_flags(struct amdgpu_bo *bo, u64 tiling_flags);
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+@@ -2036,6 +2036,36 @@ error_free:
+ return r;
+ }
+
++/**
++ * amdgpu_ttm_evict_resources - evict memory buffers
++ * @adev: amdgpu device object
++ * @mem_type: evicted BO's memory type
++ *
++ * Evicts all @mem_type buffers on the lru list of the memory type.
++ *
++ * Returns:
++ * 0 for success or a negative error code on failure.
++ */
++int amdgpu_ttm_evict_resources(struct amdgpu_device *adev, int mem_type)
++{
++ struct ttm_resource_manager *man;
++
++ switch (mem_type) {
++ case TTM_PL_VRAM:
++ case TTM_PL_TT:
++ case AMDGPU_PL_GWS:
++ case AMDGPU_PL_GDS:
++ case AMDGPU_PL_OA:
++ man = ttm_manager_type(&adev->mman.bdev, mem_type);
++ break;
++ default:
++ DRM_ERROR("Trying to evict invalid memory type\n");
++ return -EINVAL;
++ }
++
++ return ttm_resource_manager_evict_all(&adev->mman.bdev, man);
++}
++
+ #if defined(CONFIG_DEBUG_FS)
+
+ static int amdgpu_mm_vram_table_show(struct seq_file *m, void *unused)
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
+@@ -190,6 +190,7 @@ bool amdgpu_ttm_tt_is_readonly(struct tt
+ uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem);
+ uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
+ struct ttm_resource *mem);
++int amdgpu_ttm_evict_resources(struct amdgpu_device *adev, int mem_type);
+
+ void amdgpu_ttm_debugfs_init(struct amdgpu_device *adev);
+