<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>