aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch')
-rw-r--r--common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch268
1 files changed, 268 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch b/common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch
new file mode 100644
index 00000000..7a51dfd2
--- /dev/null
+++ b/common/recipes-kernel/linux/files/0693-drm-amd-dal-S3-implementation-using-atomic-commit.patch
@@ -0,0 +1,268 @@
+From 6b7df58f42c838b25452e13e26b054564d352730 Mon Sep 17 00:00:00 2001
+From: Eric Yang <eric.yang2@amd.com>
+Date: Tue, 12 Jan 2016 12:02:08 -0500
+Subject: [PATCH 0693/1110] drm/amd/dal: S3 implementation using atomic commit
+
+Allow suspend resume by reconstructing the drm_atomic_state on
+resume and calling atomic commit. This implementation works
+with display configurationas up to 3 displays, but cannot handle
+display swapping due to modelist not being properly updated.
+Modes in the mode list are only emptied during disconnect, the
+swap display during S3 use case never goes through a disconnect state
+
+Signed-off-by: Eric Yang <eric.yang2@amd.com>
+Acked-by: Jordan Lazare <Jordan.Lazare@amd.com>
+---
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c | 164 ++++++++++++++++++---
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c | 17 ++-
+ 2 files changed, 151 insertions(+), 30 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 0f281b6..6de5703 100644
+--- a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+@@ -451,20 +451,6 @@ static int dm_set_powergating_state(void *handle,
+ /* Prototypes of private functions */
+ static int dm_early_init(void* handle);
+
+-static void detect_on_all_dc_links(struct amdgpu_display_manager *dm)
+-{
+- uint32_t i;
+- const struct dc_link *dc_link;
+- struct dc_caps caps = { 0 };
+-
+- dc_get_caps(dm->dc, &caps);
+-
+- for (i = 0; i < caps.max_links; i++) {
+- dc_link = dc_get_link_at_index(dm->dc, i);
+- dc_link_detect(dc_link, false);
+- }
+-}
+-
+ static void hotplug_notify_work_func(struct work_struct *work)
+ {
+ struct amdgpu_display_manager *dm = container_of(work, struct amdgpu_display_manager, mst_hotplug_work);
+@@ -663,11 +649,78 @@ static int dm_hw_fini(void *handle)
+ return 0;
+ }
+
++static int dm_display_suspend(struct drm_device *ddev)
++{
++ struct drm_mode_config *config = &ddev->mode_config;
++ struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
++ struct drm_atomic_state *state;
++ struct drm_crtc *crtc;
++ unsigned crtc_mask = 0;
++ int ret = 0;
++
++ if (WARN_ON(!ctx))
++ return 0;
++
++ lockdep_assert_held(&ctx->ww_ctx);
++
++ state = drm_atomic_state_alloc(ddev);
++ if (WARN_ON(!state))
++ return -ENOMEM;
++
++ state->acquire_ctx = ctx;
++ state->allow_modeset = true;
++
++ /* Set all active crtcs to inactive, to turn off displays*/
++ list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) {
++ struct drm_crtc_state *crtc_state =
++ drm_atomic_get_crtc_state(state, crtc);
++
++ ret = PTR_ERR_OR_ZERO(crtc_state);
++ if (ret)
++ goto free;
++
++ if (!crtc_state->active)
++ continue;
++
++ crtc_state->active = false;
++ crtc_mask |= (1 << drm_crtc_index(crtc));
++ }
++
++ if (crtc_mask) {
++ ret = drm_atomic_commit(state);
++
++ /* In case of failure, revert everything we did*/
++ if (!ret) {
++ list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head)
++ if (crtc_mask & (1 << drm_crtc_index(crtc)))
++ crtc->state->active = true;
++
++ return ret;
++ }
++ }
++
++free:
++ if (ret) {
++ DRM_ERROR("Suspending crtc's failed with %i\n", ret);
++ drm_atomic_state_free(state);
++ return ret;
++ }
++
++ return 0;
++}
+ static int dm_suspend(void *handle)
+ {
+ struct amdgpu_device *adev = handle;
+ struct amdgpu_display_manager *dm = &adev->dm;
+- struct drm_crtc *crtc;
++ struct drm_device *ddev = adev->ddev;
++ int ret = 0;
++
++ drm_modeset_lock_all(ddev);
++ ret = dm_display_suspend(ddev);
++ drm_modeset_unlock_all(ddev);
++
++ if (ret)
++ goto fail;
+
+ dc_set_power_state(
+ dm->dc,
+@@ -675,32 +728,95 @@ static int dm_suspend(void *handle)
+ DC_VIDEO_POWER_SUSPEND);
+
+ amdgpu_dm_irq_suspend(adev);
++fail:
++ return ret;
++}
++
++static int dm_display_resume(struct drm_device *ddev)
++{
++ int ret = 0;
++ struct drm_connector *connector;
++
++ struct drm_atomic_state *state = drm_atomic_state_alloc(ddev);
++ struct drm_plane *plane;
++ struct drm_crtc *crtc;
+
+- list_for_each_entry(crtc, &dm->ddev->mode_config.crtc_list, head) {
+- crtc->mode.clock = 0;
++ if (!state)
++ return ENOMEM;
++
++ state->acquire_ctx = ddev->mode_config.acquire_ctx;
++
++ /* Construct an atomic state to restore previous display setting*/
++ /* Attach crtcs to drm_atomic_state*/
++ list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) {
++ struct drm_crtc_state *crtc_state =
++ drm_atomic_get_crtc_state(state, crtc);
++
++ ret = PTR_ERR_OR_ZERO(crtc_state);
++ if (ret)
++ goto err;
++
++ /* force a restore */
++ crtc_state->mode_changed = true;
+ }
+
+- return 0;
++ /* Attach planes to drm_atomic_state*/
++ list_for_each_entry(plane, &ddev->mode_config.plane_list, head) {
++ ret = PTR_ERR_OR_ZERO(drm_atomic_get_plane_state(state, plane));
++ if (ret)
++ goto err;
++ }
++
++ /* Attach connectors to drm_atomic_state*/
++ list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
++ ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(state, connector));
++ if (ret)
++ goto err;
++ }
++
++ /* Call commit internally with the state we just constructed */
++ ret = drm_atomic_commit(state);
++ if (!ret)
++ return 0;
++
++err:
++ DRM_ERROR("Restoring old state failed with %i\n", ret);
++ drm_atomic_state_free(state);
++
++ return ret;
+ }
+
+ static int dm_resume(void *handle)
+ {
+- int ret = 0;
+ struct amdgpu_device *adev = handle;
++ struct drm_device *ddev = adev->ddev;
+ struct amdgpu_display_manager *dm = &adev->dm;
++ struct amdgpu_connector *aconnector;
++ struct drm_connector *connector;
++ int ret = 0;
+
++ /* power on hardware */
+ dc_set_power_state(
+ dm->dc,
+ DC_ACPI_CM_POWER_STATE_D0,
+ DC_VIDEO_POWER_ON);
+
+- amdgpu_dm_irq_resume(adev);
+-
++ /* program HPD filter*/
+ dc_resume(dm->dc);
++ /* resume IRQ */
++ amdgpu_dm_irq_resume(adev);
++ /* Do detection*/
++ list_for_each_entry(connector,
++ &ddev->mode_config.connector_list, head) {
++ aconnector = to_amdgpu_connector(connector);
++ dc_link_detect(aconnector->dc_link, false);
++ aconnector->dc_sink = NULL;
++ amdgpu_dm_update_connector_after_detect(aconnector);
++ }
+
+- detect_on_all_dc_links(dm);
+-
+- drm_mode_config_reset(adev->ddev);
++ drm_modeset_lock_all(ddev);
++ ret = dm_display_resume(ddev);
++ drm_modeset_unlock_all(ddev);
+
+ return ret;
+ }
+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 7df2d28..04044ff 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
+@@ -2171,6 +2171,8 @@ int amdgpu_dm_atomic_commit(
+ * during resume sequence ended
+ */
+ new_state->planes_changed = false;
++ DRM_DEBUG_KMS("%s: Failed to create new target for crtc %d\n",
++ __func__, acrtc->base.base.id);
+ break;
+ }
+
+@@ -2458,13 +2460,16 @@ int amdgpu_dm_atomic_check(struct drm_device *dev,
+ aconnector,
+ &mode);
+
++ /*
++ * we can have no target on ACTION_SET if a display
++ * was disconnected during S3, in this case it not and
++ * error, the OS will be updated after detection, and
++ * do the right thing on next atomic commit
++ */
+ if (!new_target) {
+- DRM_ERROR(
+- "%s: Can't create target for crtc %d\n",
+- __func__,
+- acrtc->crtc_id);
+- goto connector_not_found;
+-
++ DRM_DEBUG_KMS("%s: Failed to create new target for crtc %d\n",
++ __func__, acrtc->base.base.id);
++ break;
+ }
+
+ new_targets[new_target_count] = new_target;
+--
+2.7.4
+