The adev->pm.mutx is already held at the beginning of
amdgpu_dpm_compute_clocks/amdgpu_dpm_enable_uvd/amdgpu_dpm_enable_vce.
But on their calling path, amdgpu_display_bandwidth_update will be
called and thus its sub functions amdgpu_dpm_get_sclk/mclk. They
will then try to acquire the same adev->pm.mutex and deadlock will
occur.
By placing amdgpu_display_bandwidth_update outside of adev->pm.mutex
protection(considering logically they do not need such protection) and
restructuring the call flow accordingly, we can eliminate the deadlock
issue. This comes with no real logics change.
Fixes: 3712e7a49459 ("drm/amd/pm: unified lock protections in amdgpu_dpm.c")
Reported-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reported-by: Arthur Marsh <arthur.marsh@internode.on.net>
Link: https://lore.kernel.org/all/9e689fea-6c69-f4b0-8dee-32c4cf7d8f9c@molgen.mpg.de/
BugLink: https://gitlab.freedesktop.org/drm/amd/-/issues/1957
Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Lijo Lazar <lijo.lazar@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
 void amdgpu_dpm_compute_clocks(struct amdgpu_device *adev)
 {
        const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+       int i;
 
        if (!adev->pm.dpm_enabled)
                return;
        if (!pp_funcs->pm_compute_clocks)
                return;
 
+       if (adev->mode_info.num_crtc)
+               amdgpu_display_bandwidth_update(adev);
+
+       for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
+               struct amdgpu_ring *ring = adev->rings[i];
+               if (ring && ring->sched.ready)
+                       amdgpu_fence_wait_empty(ring);
+       }
+
        mutex_lock(&adev->pm.mutex);
        pp_funcs->pm_compute_clocks(adev->powerplay.pp_handle);
        mutex_unlock(&adev->pm.mutex);
 {
        int ret = 0;
 
+       if (adev->family == AMDGPU_FAMILY_SI) {
+               mutex_lock(&adev->pm.mutex);
+               if (enable) {
+                       adev->pm.dpm.uvd_active = true;
+                       adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD;
+               } else {
+                       adev->pm.dpm.uvd_active = false;
+               }
+               mutex_unlock(&adev->pm.mutex);
+
+               amdgpu_dpm_compute_clocks(adev);
+               return;
+       }
+
        ret = amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_UVD, !enable);
        if (ret)
                DRM_ERROR("Dpm %s uvd failed, ret = %d. \n",
 {
        int ret = 0;
 
+       if (adev->family == AMDGPU_FAMILY_SI) {
+               mutex_lock(&adev->pm.mutex);
+               if (enable) {
+                       adev->pm.dpm.vce_active = true;
+                       /* XXX select vce level based on ring/task */
+                       adev->pm.dpm.vce_level = AMD_VCE_LEVEL_AC_ALL;
+               } else {
+                       adev->pm.dpm.vce_active = false;
+               }
+               mutex_unlock(&adev->pm.mutex);
+
+               amdgpu_dpm_compute_clocks(adev);
+               return;
+       }
+
        ret = amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_VCE, !enable);
        if (ret)
                DRM_ERROR("Dpm %s vce failed, ret = %d. \n",
 
 void amdgpu_legacy_dpm_compute_clocks(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-       int i = 0;
-
-       if (adev->mode_info.num_crtc)
-               amdgpu_display_bandwidth_update(adev);
-
-       for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
-               struct amdgpu_ring *ring = adev->rings[i];
-               if (ring && ring->sched.ready)
-                       amdgpu_fence_wait_empty(ring);
-       }
 
        amdgpu_dpm_get_active_displays(adev);
 
 
 }
 #endif
 
-static int si_set_powergating_by_smu(void *handle,
-                                    uint32_t block_type,
-                                    bool gate)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       switch (block_type) {
-       case AMD_IP_BLOCK_TYPE_UVD:
-               if (!gate) {
-                       adev->pm.dpm.uvd_active = true;
-                       adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD;
-               } else {
-                       adev->pm.dpm.uvd_active = false;
-               }
-
-               amdgpu_legacy_dpm_compute_clocks(handle);
-               break;
-       case AMD_IP_BLOCK_TYPE_VCE:
-               if (!gate) {
-                       adev->pm.dpm.vce_active = true;
-                       /* XXX select vce level based on ring/task */
-                       adev->pm.dpm.vce_level = AMD_VCE_LEVEL_AC_ALL;
-               } else {
-                       adev->pm.dpm.vce_active = false;
-               }
-
-               amdgpu_legacy_dpm_compute_clocks(handle);
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
 static int si_set_sw_state(struct amdgpu_device *adev)
 {
        return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_SwitchToSwState) == PPSMC_Result_OK) ?
        .print_power_state = &si_dpm_print_power_state,
        .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level,
        .force_performance_level = &si_dpm_force_performance_level,
-       .set_powergating_by_smu = &si_set_powergating_by_smu,
        .vblank_too_short = &si_dpm_vblank_too_short,
        .set_fan_control_mode = &si_dpm_set_fan_control_mode,
        .get_fan_control_mode = &si_dpm_get_fan_control_mode,
 
 {
        struct pp_hwmgr *hwmgr = handle;
        struct amdgpu_device *adev = hwmgr->adev;
-       int i = 0;
-
-       if (adev->mode_info.num_crtc)
-               amdgpu_display_bandwidth_update(adev);
-
-       for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
-               struct amdgpu_ring *ring = adev->rings[i];
-               if (ring && ring->sched.ready)
-                       amdgpu_fence_wait_empty(ring);
-       }
 
        if (!amdgpu_device_has_dc_support(adev)) {
                amdgpu_dpm_get_active_displays(adev);