int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
 {
-       struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state);
+       enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
+       struct drm_crtc_commit *commit;
+       struct dm_crtc_state *crtc_state;
        struct drm_dp_aux *aux = NULL;
        bool enable = false;
        bool enabled = false;
-       int ret;
-
-       enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
+       int ret = 0;
 
        if (source < 0) {
                DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
                return -EINVAL;
        }
 
+       ret = drm_modeset_lock(&crtc->mutex, NULL);
+       if (ret)
+               return ret;
+
+       spin_lock(&crtc->commit_lock);
+       commit = list_first_entry_or_null(&crtc->commit_list,
+                                         struct drm_crtc_commit, commit_entry);
+       if (commit)
+               drm_crtc_commit_get(commit);
+       spin_unlock(&crtc->commit_lock);
+
+       if (commit) {
+               /*
+                * Need to wait for all outstanding programming to complete
+                * in commit tail since it can modify CRC related fields and
+                * hardware state. Since we're holding the CRTC lock we're
+                * guaranteed that no other commit work can be queued off
+                * before we modify the state below.
+                */
+               ret = wait_for_completion_interruptible_timeout(
+                       &commit->hw_done, 10 * HZ);
+               if (ret)
+                       goto cleanup;
+       }
+
        enable = amdgpu_dm_is_valid_crc_source(source);
+       crtc_state = to_dm_crtc_state(crtc->state);
 
        /*
         * USER REQ SRC | CURRENT SRC | BEHAVIOR
 
                if (!aconn) {
                        DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto cleanup;
                }
 
                aux = &aconn->dm_dp_aux.aux;
 
                if (!aux) {
                        DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto cleanup;
                }
        }
 
-       if (amdgpu_dm_crtc_configure_crc_source(crtc, crtc_state, source))
-               return -EINVAL;
+       if (amdgpu_dm_crtc_configure_crc_source(crtc, crtc_state, source)) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
 
        /*
         * Reading the CRC requires the vblank interrupt handler to be
        if (!enabled && enable) {
                ret = drm_crtc_vblank_get(crtc);
                if (ret)
-                       return ret;
+                       goto cleanup;
 
                if (dm_is_crc_source_dprx(source)) {
                        if (drm_dp_start_crc(aux, crtc)) {
                                DRM_DEBUG_DRIVER("dp start crc failed\n");
-                               return -EINVAL;
+                               ret = -EINVAL;
+                               goto cleanup;
                        }
                }
        } else if (enabled && !enable) {
                if (dm_is_crc_source_dprx(source)) {
                        if (drm_dp_stop_crc(aux)) {
                                DRM_DEBUG_DRIVER("dp stop crc failed\n");
-                               return -EINVAL;
+                               ret = -EINVAL;
+                               goto cleanup;
                        }
                }
        }
 
        /* Reset crc_skipped on dm state */
        crtc_state->crc_skip_count = 0;
-       return 0;
+
+cleanup:
+       if (commit)
+               drm_crtc_commit_put(commit);
+
+       drm_modeset_unlock(&crtc->mutex);
+
+       return ret;
 }
 
 /**