*/
        for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
                struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
+               struct dm_crtc_state *dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
                struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
 
-               if (drm_atomic_crtc_needs_modeset(new_crtc_state) && dm_old_crtc_state->stream)
+               if (drm_atomic_crtc_needs_modeset(new_crtc_state)
+                   && dm_old_crtc_state->stream) {
+                       /*
+                        * CRC capture was enabled but not disabled.
+                        * Release the vblank reference.
+                        */
+                       if (dm_new_crtc_state->crc_enabled) {
+                               drm_crtc_vblank_put(crtc);
+                               dm_new_crtc_state->crc_enabled = false;
+                       }
+
                        manage_dm_interrupts(adev, acrtc, false);
+               }
        }
        /*
         * Add check here for SoC's that support hardware cursor plane, to
 
 {
        struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state);
        struct dc_stream_state *stream_state = crtc_state->stream;
+       bool enable;
 
        enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
 
                return -EINVAL;
        }
 
+       enable = (source == AMDGPU_DM_PIPE_CRC_SOURCE_AUTO);
+
+       if (!dc_stream_configure_crc(stream_state->ctx->dc, stream_state,
+                                    enable, enable))
+               return -EINVAL;
+
        /* When enabling CRC, we should also disable dithering. */
-       if (source == AMDGPU_DM_PIPE_CRC_SOURCE_AUTO) {
-               if (dc_stream_configure_crc(stream_state->ctx->dc,
-                                           stream_state,
-                                           true, true)) {
-                       crtc_state->crc_enabled = true;
-                       dc_stream_set_dither_option(stream_state,
-                                                   DITHER_OPTION_TRUN8);
-               }
-               else
-                       return -EINVAL;
-       } else {
-               if (dc_stream_configure_crc(stream_state->ctx->dc,
-                                           stream_state,
-                                           false, false)) {
-                       crtc_state->crc_enabled = false;
-                       dc_stream_set_dither_option(stream_state,
-                                                   DITHER_OPTION_DEFAULT);
-               }
-               else
-                       return -EINVAL;
-       }
+       dc_stream_set_dither_option(stream_state,
+                                   enable ? DITHER_OPTION_TRUN8
+                                          : DITHER_OPTION_DEFAULT);
+
+       /*
+        * Reading the CRC requires the vblank interrupt handler to be
+        * enabled. Keep a reference until CRC capture stops.
+        */
+       if (!crtc_state->crc_enabled && enable)
+               drm_crtc_vblank_get(crtc);
+       else if (crtc_state->crc_enabled && !enable)
+               drm_crtc_vblank_put(crtc);
+
+       crtc_state->crc_enabled = enable;
 
        /* Reset crc_skipped on dm state */
        crtc_state->crc_skip_count = 0;