aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch')
-rw-r--r--common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch279
1 files changed, 279 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch b/common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch
new file mode 100644
index 00000000..98146592
--- /dev/null
+++ b/common/recipes-kernel/linux/files/0883-drm-amd-dal-add-logic-to-handle-hw-and-sw-state-inco.patch
@@ -0,0 +1,279 @@
+From 5a1c2aeefdef4941aedb5f719f159d6b16fb8b49 Mon Sep 17 00:00:00 2001
+From: Eric Yang <eric.yang2@amd.com>
+Date: Fri, 4 Mar 2016 14:34:46 -0500
+Subject: [PATCH 0883/1110] drm/amd/dal: add logic to handle hw and sw state
+ inconsitency
+
+Signed-off-by: Eric Yang <eric.yang2@amd.com>
+Acked-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+---
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c | 6 +
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c | 162 ++++++++++++---------
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h | 2 +
+ 3 files changed, 103 insertions(+), 67 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+index c12ef99..a9973ed 100644
+--- a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+@@ -697,8 +697,14 @@ static void handle_hpd_irq(void *param)
+ */
+ if (dc_link_detect(aconnector->dc_link, false)) {
+ amdgpu_dm_update_connector_after_detect(aconnector);
++
++ drm_modeset_lock_all(dev);
++ dm_restore_drm_connector_state(dev, connector);
++ drm_modeset_unlock_all(dev);
++
+ drm_kms_helper_hotplug_event(dev);
+ }
++
+ }
+
+ static void handle_hpd_rx_irq(void *param)
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
+index ca553ce..dcffffe 100644
+--- a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
+@@ -884,15 +884,20 @@ static struct dc_target *create_target_for_sink(
+ &aconnector->base.modes,
+ struct drm_display_mode,
+ head);
++
+ if (NULL == preferred_mode) {
+- DRM_ERROR("No preferred mode found\n");
+- goto stream_create_fail;
++ /* This may not be an error, the use case is when we we have no
++ * usermode calls to reset and set mode upon hotplug. In this
++ * case, we call set mode ourselves to restore the previous mode
++ * and the modelist may not be filled in in time.
++ */
++ DRM_INFO("No preferred mode found\n");
++ } else {
++ decide_crtc_timing_for_drm_display_mode(
++ &mode, preferred_mode,
++ dm_state->scaling != RMX_OFF);
+ }
+
+- decide_crtc_timing_for_drm_display_mode(
+- &mode, preferred_mode,
+- dm_state->scaling != RMX_OFF);
+-
+ dc_timing_from_drm_display_mode(&stream->timing,
+ &mode, &aconnector->base);
+
+@@ -2004,6 +2009,15 @@ static enum dm_commit_action get_dm_commit_action(struct drm_crtc_state *state)
+ {
+ /* mode changed means either actually mode changed or enabled changed */
+ /* active changed means dpms changed */
++
++ DRM_DEBUG_KMS("crtc_state_flags: enable:%d, active:%d, planes_changed:%d, mode_changed:%d,active_changed:%d,connectors_changed:%d\n",
++ state->enable,
++ state->active,
++ state->planes_changed,
++ state->mode_changed,
++ state->active_changed,
++ state->connectors_changed);
++
+ if (state->mode_changed) {
+ /* if it is got disabled - call reset mode */
+ if (!state->enable)
+@@ -2020,6 +2034,9 @@ static enum dm_commit_action get_dm_commit_action(struct drm_crtc_state *state)
+ if (!state->enable)
+ return DM_COMMIT_ACTION_NOTHING;
+
++ if (state->active && state->connectors_changed)
++ return DM_COMMIT_ACTION_SET;
++
+ if (state->active_changed) {
+ if (state->active) {
+ return DM_COMMIT_ACTION_DPMS_ON;
+@@ -2089,64 +2106,6 @@ static void manage_dm_interrupts(
+ }
+ }
+
+-/*
+- * Handle headless hotplug workaround
+- *
+- * In case of headless hotplug, if plugging the same monitor to the same
+- * DDI, DRM consider it as mode unchanged. We should check whether the
+- * sink pointer changed, and set mode_changed properly to
+- * make sure commit is doing everything.
+- */
+-static void handle_headless_hotplug(
+- const struct amdgpu_crtc *acrtc,
+- struct drm_crtc_state *state,
+- struct amdgpu_connector **aconnector)
+-{
+- struct amdgpu_connector *old_connector =
+- aconnector_from_drm_crtc_id(&acrtc->base);
+-
+- /*
+- * TODO Revisit this. This code is kinda hacky and might break things.
+- */
+-
+- if (!old_connector)
+- return;
+-
+- if (!*aconnector)
+- *aconnector = old_connector;
+-
+- if (acrtc->target && (*aconnector)->dc_sink) {
+- if ((*aconnector)->dc_sink !=
+- acrtc->target->streams[0]->sink) {
+- state->mode_changed = true;
+- }
+- }
+-
+- if (!acrtc->target) {
+- /* In case of headless with DPMS on, when system waked up,
+- * if no monitor connected, target is null and will not create
+- * new target, on that condition, we should check
+- * if any connector is connected, if connected,
+- * it means a hot plug happened after wake up,
+- * mode_changed should be set to true to make sure
+- * commit targets will do everything.
+- */
+- state->mode_changed =
+- (*aconnector)->base.status ==
+- connector_status_connected;
+- } else {
+- /* In case of headless hotplug, if plug same monitor to same
+- * DDI, DRM consider it as mode unchanged, we should check
+- * sink pointer changed, and set mode changed properly to
+- * make sure commit doing everything.
+- */
+- /* check if sink has changed from last commit */
+- if ((*aconnector)->dc_sink && (*aconnector)->dc_sink !=
+- acrtc->target->streams[0]->sink)
+- state->mode_changed = true;
+- }
+-}
+-
+ int amdgpu_dm_atomic_commit(
+ struct drm_device *dev,
+ struct drm_atomic_state *state,
+@@ -2217,7 +2176,6 @@ int amdgpu_dm_atomic_commit(
+ /* handles headless hotplug case, updating new_state and
+ * aconnector as needed
+ */
+- handle_headless_hotplug(acrtc, new_state, &aconnector);
+
+ action = get_dm_commit_action(new_state);
+
+@@ -2229,7 +2187,7 @@ int amdgpu_dm_atomic_commit(
+ aconnector,
+ &crtc->state->mode);
+
+- DRM_DEBUG_KMS("Atomic commit: SET.\n");
++ DRM_INFO("Atomic commit: SET.\n");
+
+ if (!new_target) {
+ /*
+@@ -2275,11 +2233,12 @@ int amdgpu_dm_atomic_commit(
+ }
+
+ case DM_COMMIT_ACTION_NOTHING:
++ DRM_DEBUG_KMS("Atomic commit: Nothing.\n");
+ break;
+
+ case DM_COMMIT_ACTION_DPMS_OFF:
+ case DM_COMMIT_ACTION_RESET:
+- DRM_DEBUG_KMS("Atomic commit: RESET.\n");
++ DRM_INFO("Atomic commit: RESET.\n");
+ /* i.e. reset mode */
+ if (acrtc->target) {
+ manage_dm_interrupts(adev, acrtc, false);
+@@ -2399,6 +2358,75 @@ int amdgpu_dm_atomic_commit(
+
+ return 0;
+ }
++/*
++ * This functions handle all cases when set mode does not come upon hotplug.
++ * This include when the same display is unplugged then plugged back into the
++ * same port and when we are running without usermode desktop manager supprot
++ */
++void dm_restore_drm_connector_state(struct drm_device *dev, struct drm_connector *connector)
++{
++ struct drm_crtc *crtc;
++ struct amdgpu_device *adev = dev->dev_private;
++ struct dc *dc = adev->dm.dc;
++ struct amdgpu_connector *aconnector = to_amdgpu_connector(connector);
++ struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->state->crtc);
++ const struct dc_sink *sink;
++ struct dc_target *commit_targets[6];
++ uint32_t commit_targets_count = 0;
++
++ if (!aconnector->dc_sink || !connector->state || !connector->state->crtc)
++ return;
++
++ if (!disconnected_acrtc->target)
++ return;
++
++ sink = disconnected_acrtc->target->streams[0]->sink;
++
++ /*
++ * If the previous sink is not released and different from the current,
++ * we deduce we are in a state where we can not rely on usermode call
++ * to turn on the display, so we do it here
++ */
++ if (sink != aconnector->dc_sink) {
++ struct dc_target *new_target =
++ create_target_for_sink(
++ aconnector,
++ &disconnected_acrtc->base.state->mode);
++ /*
++ * we evade vblanks and pflips on crtc that
++ * should be changed
++ */
++ manage_dm_interrupts(adev, disconnected_acrtc, false);
++ /* this is the update mode case */
++ dc_target_release(disconnected_acrtc->target);
++
++ disconnected_acrtc->target = new_target;
++ disconnected_acrtc->enabled = true;
++ disconnected_acrtc->hw_mode = disconnected_acrtc->base.state->mode;
++
++ commit_targets_count = 0;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
++
++ if (acrtc->target) {
++ commit_targets[commit_targets_count] = acrtc->target;
++ ++commit_targets_count;
++ }
++ }
++
++ /* DC is optimized not to do anything if 'targets' didn't change. */
++ dc_commit_targets(dc, commit_targets, commit_targets_count);
++
++ dm_dc_surface_commit(dc, &disconnected_acrtc->base,
++ to_dm_connector_state(
++ connector->state));
++
++ manage_dm_interrupts(adev, disconnected_acrtc, true);
++ dm_crtc_cursor_reset(&disconnected_acrtc->base);
++
++ }
++}
+
+ static uint32_t add_val_sets_surface(
+ struct dc_validation_set *val_sets,
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
+index 2cf7cd2..8f65194 100644
+--- a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
+@@ -94,6 +94,8 @@ int amdgpu_dm_connector_mode_valid(
+ struct drm_connector *connector,
+ struct drm_display_mode *mode);
+
++void dm_restore_drm_connector_state(struct drm_device *dev, struct drm_connector *connector);
++
+ extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
+
+ #endif /* __AMDGPU_DM_TYPES_H__ */
+--
+2.7.4
+