<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<p style="font-family:Arial;font-size:10pt;color:#0078D7;margin:15pt;" align="Left">
[AMD Official Use Only - Internal Distribution Only]<br>
</p>
<br>
<div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Reviewed-by: <font size="2"><span style="font-size:11pt">Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com></span></font><br>
</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> Nicholas Kazlauskas <nicholas.kazlauskas@amd.com><br>
<b>Sent:</b> July 13, 2020 11:39 AM<br>
<b>To:</b> amd-gfx@lists.freedesktop.org <amd-gfx@lists.freedesktop.org><br>
<b>Cc:</b> Kazlauskas, Nicholas <Nicholas.Kazlauskas@amd.com>; Lakha, Bhawanpreet <Bhawanpreet.Lakha@amd.com><br>
<b>Subject:</b> [PATCH 2/2] drm/amd/display: Allow for vblank enabled with no active planes</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">[Why]<br>
CRC capture doesn't work when the active plane count is 0 since we<br>
currently tie both vblank and pageflip interrupts to active_plane_count<br>
greater than 0.<br>
<br>
[How]<br>
The frontend is what generates the vblank interrupts while the backend<br>
is what generates pageflip interrupts. Both have a requirement for<br>
the CRTC to be active, so control the overall interrupt state based<br>
on that instead.<br>
<br>
Pageflip interrupts need to be enabled based on active plane count, but<br>
we actually rely on power gating to take care of disabling the interrupt<br>
for us on pipes that can be power gated.<br>
<br>
For pipes that can't be power gated it's still fine to leave it enabled<br>
since the interrupt only triggers after the address has been written<br>
to that particular pipe - which we won't be doing without an active<br>
plane.<br>
<br>
The issue we had before with this setup was that we couldn't force<br>
the state back on. We were essentially manipulating the refcount<br>
to enable or disable as needed in a two pass approach.<br>
<br>
However, there is a function that solves this problem more elegantly:<br>
amdgpu_irq_update() will unconditionally call the set based on what it<br>
thinks the current enablement state is.<br>
<br>
This leaves two future TODO items for our IRQ handling:<br>
- Disabling IRQs in commit tail instead of atomic commit<br>
- Mapping the pageflip interrupt to VUPDATE or something that's tied to<br>
  the frontend instead of the backend since the mapping to CRTC is not<br>
  correct<br>
<br>
Cc: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com><br>
Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com><br>
---<br>
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 168 ++++++++----------<br>
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |   1 -<br>
 2 files changed, 78 insertions(+), 91 deletions(-)<br>
<br>
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c<br>
index d9f8e9c26390..05160c6bbc03 100644<br>
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c<br>
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c<br>
@@ -4719,7 +4719,6 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)<br>
         }<br>
 <br>
         state->active_planes = cur->active_planes;<br>
-       state->interrupts_enabled = cur->interrupts_enabled;<br>
         state->vrr_params = cur->vrr_params;<br>
         state->vrr_infopacket = cur->vrr_infopacket;<br>
         state->abm_level = cur->abm_level;<br>
@@ -5393,29 +5392,19 @@ static int count_crtc_active_planes(struct drm_crtc_state *new_crtc_state)<br>
         return num_active;<br>
 }<br>
 <br>
-/*<br>
- * Sets whether interrupts should be enabled on a specific CRTC.<br>
- * We require that the stream be enabled and that there exist active<br>
- * DC planes on the stream.<br>
- */<br>
-static void<br>
-dm_update_crtc_interrupt_state(struct drm_crtc *crtc,<br>
-                              struct drm_crtc_state *new_crtc_state)<br>
+static void dm_update_crtc_active_planes(struct drm_crtc *crtc,<br>
+                                        struct drm_crtc_state *new_crtc_state)<br>
 {<br>
         struct dm_crtc_state *dm_new_crtc_state =<br>
                 to_dm_crtc_state(new_crtc_state);<br>
 <br>
         dm_new_crtc_state->active_planes = 0;<br>
-       dm_new_crtc_state->interrupts_enabled = false;<br>
 <br>
         if (!dm_new_crtc_state->stream)<br>
                 return;<br>
 <br>
         dm_new_crtc_state->active_planes =<br>
                 count_crtc_active_planes(new_crtc_state);<br>
-<br>
-       dm_new_crtc_state->interrupts_enabled =<br>
-               dm_new_crtc_state->active_planes > 0;<br>
 }<br>
 <br>
 static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc,<br>
@@ -5426,13 +5415,7 @@ static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc,<br>
         struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(state);<br>
         int ret = -EINVAL;<br>
 <br>
-       /*<br>
-        * Update interrupt state for the CRTC. This needs to happen whenever<br>
-        * the CRTC has changed or whenever any of its planes have changed.<br>
-        * Atomic check satisfies both of these requirements since the CRTC<br>
-        * is added to the state by DRM during drm_atomic_helper_check_planes.<br>
-        */<br>
-       dm_update_crtc_interrupt_state(crtc, state);<br>
+       dm_update_crtc_active_planes(crtc, state);<br>
 <br>
         if (unlikely(!dm_crtc_state->stream &&<br>
                      modeset_required(state, NULL, dm_crtc_state->stream))) {<br>
@@ -6547,8 +6530,10 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,<br>
                                  bool enable)<br>
 {<br>
         /*<br>
-        * this is not correct translation but will work as soon as VBLANK<br>
-        * constant is the same as PFLIP<br>
+        * We have no guarantee that the frontend index maps to the same<br>
+        * backend index - some even map to more than one.<br>
+        *<br>
+        * TODO: Use a different interrupt or check DC itself for the mapping.<br>
          */<br>
         int irq_type =<br>
                 amdgpu_display_crtc_idx_to_irq_type(<br>
@@ -6571,6 +6556,19 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,<br>
         }<br>
 }<br>
 <br>
+static void dm_update_pflip_irq_state(struct amdgpu_device *adev,<br>
+                                     struct amdgpu_crtc *acrtc)<br>
+{<br>
+       int irq_type =<br>
+               amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);<br>
+<br>
+       /**<br>
+        * This reads the current state for the IRQ and force reapplies<br>
+        * the setting to hardware.<br>
+        */<br>
+       amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);<br>
+}<br>
+<br>
 static bool<br>
 is_scaling_state_different(const struct dm_connector_state *dm_state,<br>
                            const struct dm_connector_state *old_dm_state)<br>
@@ -7154,7 +7152,16 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,<br>
                         usleep_range(1000, 1100);<br>
                 }<br>
 <br>
-               if (acrtc_attach->base.state->event) {<br>
+               /**<br>
+                * Prepare the flip event for the pageflip interrupt to handle.<br>
+                *<br>
+                * This only works in the case where we've already turned on the<br>
+                * appropriate hardware blocks (eg. HUBP) so in the transition case<br>
+                * from 0 -> n planes we have to skip a hardware generated event<br>
+                * and rely on sending it from software.<br>
+                */<br>
+               if (acrtc_attach->base.state->event &&<br>
+                   acrtc_state->active_planes > 0) {<br>
                         drm_crtc_vblank_get(pcrtc);<br>
 <br>
                         spin_lock_irqsave(&pcrtc->dev->event_lock, flags);<br>
@@ -7223,6 +7230,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,<br>
                                                      &bundle->stream_update,<br>
                                                      dc_state);<br>
 <br>
+               /**<br>
+                * Enable or disable the interrupts on the backend.<br>
+                *<br>
+                * Most pipes are put into power gating when unused.<br>
+                *<br>
+                * When power gating is enabled on a pipe we lose the<br>
+                * interrupt enablement state when power gating is disabled.<br>
+                *<br>
+                * So we need to update the IRQ control state in hardware<br>
+                * whenever the pipe turns on (since it could be previously<br>
+                * power gated) or off (since some pipes can't be power gated<br>
+                * on some ASICs).<br>
+                */<br>
+               if (dm_old_crtc_state->active_planes != acrtc_state->active_planes)<br>
+                       dm_update_pflip_irq_state(<br>
+                               (struct amdgpu_device *)dev->dev_private,<br>
+                               acrtc_attach);<br>
+<br>
                 if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&<br>
                                 acrtc_state->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED &&<br>
                                 !acrtc_state->stream->link->psr_settings.psr_feature_enabled)<br>
@@ -7323,64 +7348,6 @@ static void amdgpu_dm_commit_audio(struct drm_device *dev,<br>
         }<br>
 }<br>
 <br>
-/*<br>
- * Enable interrupts on CRTCs that are newly active, undergone<br>
- * a modeset, or have active planes again.<br>
- *<br>
- * Done in two passes, based on the for_modeset flag:<br>
- * Pass 1: For CRTCs going through modeset<br>
- * Pass 2: For CRTCs going from 0 to n active planes<br>
- *<br>
- * Interrupts can only be enabled after the planes are programmed,<br>
- * so this requires a two-pass approach since we don't want to<br>
- * just defer the interrupts until after commit planes every time.<br>
- */<br>
-static void amdgpu_dm_enable_crtc_interrupts(struct drm_device *dev,<br>
-                                            struct drm_atomic_state *state,<br>
-                                            bool for_modeset)<br>
-{<br>
-       struct amdgpu_device *adev = dev->dev_private;<br>
-       struct drm_crtc *crtc;<br>
-       struct drm_crtc_state *old_crtc_state, *new_crtc_state;<br>
-       int i;<br>
-#ifdef CONFIG_DEBUG_FS<br>
-       enum amdgpu_dm_pipe_crc_source source;<br>
-#endif<br>
-<br>
-       for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,<br>
-                                     new_crtc_state, i) {<br>
-               struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);<br>
-               struct dm_crtc_state *dm_new_crtc_state =<br>
-                       to_dm_crtc_state(new_crtc_state);<br>
-               struct dm_crtc_state *dm_old_crtc_state =<br>
-                       to_dm_crtc_state(old_crtc_state);<br>
-               bool modeset = drm_atomic_crtc_needs_modeset(new_crtc_state);<br>
-               bool run_pass;<br>
-<br>
-               run_pass = (for_modeset && modeset) ||<br>
-                          (!for_modeset && !modeset &&<br>
-                           !dm_old_crtc_state->interrupts_enabled);<br>
-<br>
-               if (!run_pass)<br>
-                       continue;<br>
-<br>
-               if (!dm_new_crtc_state->interrupts_enabled)<br>
-                       continue;<br>
-<br>
-               manage_dm_interrupts(adev, acrtc, true);<br>
-<br>
-#ifdef CONFIG_DEBUG_FS<br>
-               /* The stream has changed so CRC capture needs to re-enabled. */<br>
-               source = dm_new_crtc_state->crc_src;<br>
-               if (amdgpu_dm_is_valid_crc_source(source)) {<br>
-                       amdgpu_dm_crtc_configure_crc_source(<br>
-                               crtc, dm_new_crtc_state,<br>
-                               dm_new_crtc_state->crc_src);<br>
-               }<br>
-#endif<br>
-       }<br>
-}<br>
-<br>
 /*<br>
  * amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC<br>
  * @crtc_state: the DRM CRTC state<br>
@@ -7420,12 +7387,10 @@ static int amdgpu_dm_atomic_commit(struct drm_device *dev,<br>
          * in atomic check.<br>
          */<br>
         for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {<br>
-               struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);<br>
-               struct dm_crtc_state *dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);<br>
                 struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);<br>
 <br>
-               if (dm_old_crtc_state->interrupts_enabled &&<br>
-                   (!dm_new_crtc_state->interrupts_enabled ||<br>
+               if (old_crtc_state->active &&<br>
+                   (!new_crtc_state->active ||<br>
                      drm_atomic_crtc_needs_modeset(new_crtc_state)))<br>
                         manage_dm_interrupts(adev, acrtc, false);<br>
         }<br>
@@ -7704,8 +7669,34 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)<br>
                                                 dm_new_crtc_state);<br>
         }<br>
 <br>
-       /* Enable interrupts for CRTCs going through a modeset. */<br>
-       amdgpu_dm_enable_crtc_interrupts(dev, state, true);<br>
+       /**<br>
+        * Enable interrupts for CRTCs that are newly enabled or went through<br>
+        * a modeset. It was intentionally deferred until after the front end<br>
+        * state was modified to wait until the OTG was on and so the IRQ<br>
+        * handlers didn't access stale or invalid state.<br>
+        */<br>
+       for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {<br>
+               struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);<br>
+<br>
+               if (new_crtc_state->active &&<br>
+                   (!old_crtc_state->active ||<br>
+                    drm_atomic_crtc_needs_modeset(new_crtc_state))) {<br>
+                       manage_dm_interrupts(adev, acrtc, true);<br>
+#ifdef CONFIG_DEBUG_FS<br>
+                       /**<br>
+                        * Frontend may have changed so reapply the CRC capture<br>
+                        * settings for the stream.<br>
+                        */<br>
+                       dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);<br>
+<br>
+                       if (amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src)) {<br>
+                               amdgpu_dm_crtc_configure_crc_source(<br>
+                                       crtc, dm_new_crtc_state,<br>
+                                       dm_new_crtc_state->crc_src);<br>
+                       }<br>
+#endif<br>
+               }<br>
+       }<br>
 <br>
         for_each_new_crtc_in_state(state, crtc, new_crtc_state, j)<br>
                 if (new_crtc_state->async_flip)<br>
@@ -7720,9 +7711,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)<br>
                                                 dm, crtc, wait_for_vblank);<br>
         }<br>
 <br>
-       /* Enable interrupts for CRTCs going from 0 to n active planes. */<br>
-       amdgpu_dm_enable_crtc_interrupts(dev, state, false);<br>
-<br>
         /* Update audio instances for each connector. */<br>
         amdgpu_dm_commit_audio(dev, state);<br>
 <br>
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h<br>
index 94d3ff727c25..82cdf07b4bd0 100644<br>
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h<br>
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h<br>
@@ -414,7 +414,6 @@ struct dm_crtc_state {<br>
 <br>
         int update_type;<br>
         int active_planes;<br>
-       bool interrupts_enabled;<br>
 <br>
         int crc_skip_count;<br>
         enum amdgpu_dm_pipe_crc_source crc_src;<br>
-- <br>
2.25.1<br>
<br>
</div>
</span></font></div>
</div>
</body>
</html>