summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/display/amdgpu_dm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c168
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h1
2 files changed, 78 insertions, 91 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 9c283531e1a2..ba0f53816057 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4651,7 +4651,6 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
}
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;
@@ -5312,29 +5311,19 @@ static int count_crtc_active_planes(struct drm_crtc_state *new_crtc_state)
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,
@@ -5345,13 +5334,7 @@ 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))) {
@@ -6462,8 +6445,10 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
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(
@@ -6486,6 +6471,19 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
}
}
+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)
@@ -7070,7 +7068,16 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *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);
@@ -7139,6 +7146,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
&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)
@@ -7240,64 +7265,6 @@ static void amdgpu_dm_commit_audio(struct drm_device *dev,
}
/*
- * 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
* @stream_state: the DC stream state.
@@ -7336,12 +7303,10 @@ static int amdgpu_dm_atomic_commit(struct drm_device *dev,
* 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);
}
@@ -7620,8 +7585,34 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
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)
@@ -7636,9 +7627,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
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);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index 3f50328fe537..dd1559c743c2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -414,7 +414,6 @@ struct dm_crtc_state {
int update_type;
int active_planes;
- bool interrupts_enabled;
int crc_skip_count;
enum amdgpu_dm_pipe_crc_source crc_src;