}
 
        state->active_planes = cur->active_planes;
-       state->interrupts_enabled = cur->interrupts_enabled;
        state->vrr_params = cur->vrr_params;
        state->vrr_infopacket = cur->vrr_infopacket;
        state->abm_level = cur->abm_level;
        return num_active;
 }
 
-/*
- * Sets whether interrupts should be enabled on a specific CRTC.
- * We require that the stream be enabled and that there exist active
- * DC planes on the stream.
- */
-static void
-dm_update_crtc_interrupt_state(struct drm_crtc *crtc,
-                              struct drm_crtc_state *new_crtc_state)
+static void dm_update_crtc_active_planes(struct drm_crtc *crtc,
+                                        struct drm_crtc_state *new_crtc_state)
 {
        struct dm_crtc_state *dm_new_crtc_state =
                to_dm_crtc_state(new_crtc_state);
 
        dm_new_crtc_state->active_planes = 0;
-       dm_new_crtc_state->interrupts_enabled = false;
 
        if (!dm_new_crtc_state->stream)
                return;
 
        dm_new_crtc_state->active_planes =
                count_crtc_active_planes(new_crtc_state);
-
-       dm_new_crtc_state->interrupts_enabled =
-               dm_new_crtc_state->active_planes > 0;
 }
 
 static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
        struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(state);
        int ret = -EINVAL;
 
-       /*
-        * Update interrupt state for the CRTC. This needs to happen whenever
-        * the CRTC has changed or whenever any of its planes have changed.
-        * Atomic check satisfies both of these requirements since the CRTC
-        * is added to the state by DRM during drm_atomic_helper_check_planes.
-        */
-       dm_update_crtc_interrupt_state(crtc, state);
+       dm_update_crtc_active_planes(crtc, state);
 
        if (unlikely(!dm_crtc_state->stream &&
                     modeset_required(state, NULL, dm_crtc_state->stream))) {
                                 bool enable)
 {
        /*
-        * this is not correct translation but will work as soon as VBLANK
-        * constant is the same as PFLIP
+        * We have no guarantee that the frontend index maps to the same
+        * backend index - some even map to more than one.
+        *
+        * TODO: Use a different interrupt or check DC itself for the mapping.
         */
        int irq_type =
                amdgpu_display_crtc_idx_to_irq_type(
        }
 }
 
+static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
+                                     struct amdgpu_crtc *acrtc)
+{
+       int irq_type =
+               amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
+
+       /**
+        * This reads the current state for the IRQ and force reapplies
+        * the setting to hardware.
+        */
+       amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
+}
+
 static bool
 is_scaling_state_different(const struct dm_connector_state *dm_state,
                           const struct dm_connector_state *old_dm_state)
                        usleep_range(1000, 1100);
                }
 
-               if (acrtc_attach->base.state->event) {
+               /**
+                * Prepare the flip event for the pageflip interrupt to handle.
+                *
+                * This only works in the case where we've already turned on the
+                * appropriate hardware blocks (eg. HUBP) so in the transition case
+                * from 0 -> n planes we have to skip a hardware generated event
+                * and rely on sending it from software.
+                */
+               if (acrtc_attach->base.state->event &&
+                   acrtc_state->active_planes > 0) {
                        drm_crtc_vblank_get(pcrtc);
 
                        spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
                                                     &bundle->stream_update,
                                                     dc_state);
 
+               /**
+                * Enable or disable the interrupts on the backend.
+                *
+                * Most pipes are put into power gating when unused.
+                *
+                * When power gating is enabled on a pipe we lose the
+                * interrupt enablement state when power gating is disabled.
+                *
+                * So we need to update the IRQ control state in hardware
+                * whenever the pipe turns on (since it could be previously
+                * power gated) or off (since some pipes can't be power gated
+                * on some ASICs).
+                */
+               if (dm_old_crtc_state->active_planes != acrtc_state->active_planes)
+                       dm_update_pflip_irq_state(
+                               (struct amdgpu_device *)dev->dev_private,
+                               acrtc_attach);
+
                if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
                                acrtc_state->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED &&
                                !acrtc_state->stream->link->psr_settings.psr_feature_enabled)
        }
 }
 
-/*
- * Enable interrupts on CRTCs that are newly active, undergone
- * a modeset, or have active planes again.
- *
- * Done in two passes, based on the for_modeset flag:
- * Pass 1: For CRTCs going through modeset
- * Pass 2: For CRTCs going from 0 to n active planes
- *
- * Interrupts can only be enabled after the planes are programmed,
- * so this requires a two-pass approach since we don't want to
- * just defer the interrupts until after commit planes every time.
- */
-static void amdgpu_dm_enable_crtc_interrupts(struct drm_device *dev,
-                                            struct drm_atomic_state *state,
-                                            bool for_modeset)
-{
-       struct amdgpu_device *adev = dev->dev_private;
-       struct drm_crtc *crtc;
-       struct drm_crtc_state *old_crtc_state, *new_crtc_state;
-       int i;
-#ifdef CONFIG_DEBUG_FS
-       enum amdgpu_dm_pipe_crc_source source;
-#endif
-
-       for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,
-                                     new_crtc_state, i) {
-               struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-               struct dm_crtc_state *dm_new_crtc_state =
-                       to_dm_crtc_state(new_crtc_state);
-               struct dm_crtc_state *dm_old_crtc_state =
-                       to_dm_crtc_state(old_crtc_state);
-               bool modeset = drm_atomic_crtc_needs_modeset(new_crtc_state);
-               bool run_pass;
-
-               run_pass = (for_modeset && modeset) ||
-                          (!for_modeset && !modeset &&
-                           !dm_old_crtc_state->interrupts_enabled);
-
-               if (!run_pass)
-                       continue;
-
-               if (!dm_new_crtc_state->interrupts_enabled)
-                       continue;
-
-               manage_dm_interrupts(adev, acrtc, true);
-
-#ifdef CONFIG_DEBUG_FS
-               /* The stream has changed so CRC capture needs to re-enabled. */
-               source = dm_new_crtc_state->crc_src;
-               if (amdgpu_dm_is_valid_crc_source(source)) {
-                       amdgpu_dm_crtc_configure_crc_source(
-                               crtc, dm_new_crtc_state,
-                               dm_new_crtc_state->crc_src);
-               }
-#endif
-       }
-}
-
 /*
  * amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC
  * @crtc_state: the DRM CRTC state
         * in atomic check.
         */
        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 (dm_old_crtc_state->interrupts_enabled &&
-                   (!dm_new_crtc_state->interrupts_enabled ||
+               if (old_crtc_state->active &&
+                   (!new_crtc_state->active ||
                     drm_atomic_crtc_needs_modeset(new_crtc_state)))
                        manage_dm_interrupts(adev, acrtc, false);
        }
                                                dm_new_crtc_state);
        }
 
-       /* Enable interrupts for CRTCs going through a modeset. */
-       amdgpu_dm_enable_crtc_interrupts(dev, state, true);
+       /**
+        * Enable interrupts for CRTCs that are newly enabled or went through
+        * a modeset. It was intentionally deferred until after the front end
+        * state was modified to wait until the OTG was on and so the IRQ
+        * handlers didn't access stale or invalid state.
+        */
+       for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+               struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
+
+               if (new_crtc_state->active &&
+                   (!old_crtc_state->active ||
+                    drm_atomic_crtc_needs_modeset(new_crtc_state))) {
+                       manage_dm_interrupts(adev, acrtc, true);
+#ifdef CONFIG_DEBUG_FS
+                       /**
+                        * Frontend may have changed so reapply the CRC capture
+                        * settings for the stream.
+                        */
+                       dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
+
+                       if (amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src)) {
+                               amdgpu_dm_crtc_configure_crc_source(
+                                       crtc, dm_new_crtc_state,
+                                       dm_new_crtc_state->crc_src);
+                       }
+#endif
+               }
+       }
 
        for_each_new_crtc_in_state(state, crtc, new_crtc_state, j)
                if (new_crtc_state->async_flip)
                                                dm, crtc, wait_for_vblank);
        }
 
-       /* Enable interrupts for CRTCs going from 0 to n active planes. */
-       amdgpu_dm_enable_crtc_interrupts(dev, state, false);
-
        /* Update audio instances for each connector. */
        amdgpu_dm_commit_audio(dev, state);