[PATCH 10/11] drm/amd/display: Add dsc pre-validation in atomic check

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Roman Li <Roman.Li@xxxxxxx>

[Why]
The previous change:
"Add affected crtcs to atomic state for dsc mst unplug"
forces modeset on all added crctc regardless whether timing changed or not.
Per our implementation of dsc we need modeset only if timing changed.
Otherwise dsc can be programmed incorrectly leading to dsc engine hang.

[How]
During atomic_check pre-compute dsc params.
Only set mode_changed if timing is changed.

Reviewed-by: Hersen Wu <hersenxs.wu@xxxxxxx>
Acked-by: Jasdeep Dhillon <jdhillon@xxxxxxx>
Signed-off-by: Roman Li <Roman.Li@xxxxxxx>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  11 +-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  13 ++
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 189 ++++++++++++++++++
 .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   4 +
 .../gpu/drm/amd/display/dc/core/dc_resource.c |   2 +-
 5 files changed, 213 insertions(+), 6 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 e26217e46c57..e69c3415801e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2406,7 +2406,7 @@ static int dm_suspend(void *handle)
 	return 0;
 }
 
-static struct amdgpu_dm_connector *
+struct amdgpu_dm_connector *
 amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_state *state,
 					     struct drm_crtc *crtc)
 {
@@ -3723,7 +3723,7 @@ static int register_outbox_irq_handlers(struct amdgpu_device *adev)
  *
  * This should only be called during atomic check.
  */
-static int dm_atomic_get_state(struct drm_atomic_state *state,
+int dm_atomic_get_state(struct drm_atomic_state *state,
 			       struct dm_atomic_state **dm_state)
 {
 	struct drm_device *dev = state->dev;
@@ -6349,7 +6349,7 @@ static bool is_freesync_video_mode(const struct drm_display_mode *mode,
 		return true;
 }
 
-static struct dc_stream_state *
+struct dc_stream_state *
 create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
 		       const struct drm_display_mode *drm_mode,
 		       const struct dm_connector_state *dm_state,
@@ -7002,7 +7002,7 @@ static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
 	create_eml_sink(aconnector);
 }
 
-static struct dc_stream_state *
+struct dc_stream_state *
 create_validate_stream_for_sink(struct amdgpu_dm_connector *aconnector,
 				const struct drm_display_mode *drm_mode,
 				const struct dm_connector_state *dm_state,
@@ -10176,7 +10176,7 @@ static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) {
 	dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
 }
 
-static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
+int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 				struct drm_atomic_state *state,
 				struct drm_crtc *crtc,
 				struct drm_crtc_state *old_crtc_state,
@@ -10964,6 +10964,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 				}
 			}
 		}
+		pre_validate_dsc(state, &dm_state, vars);
 	}
 #endif
 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
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 e35977fda5c1..03d457f8a5f1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -737,4 +737,17 @@ int amdgpu_dm_process_dmub_aux_transfer_sync(bool is_cmd_aux,
 
 bool check_seamless_boot_capability(struct amdgpu_device *adev);
 
+struct dc_stream_state *create_validate_stream_for_sink(
+				struct amdgpu_dm_connector *aconnector,
+				const struct drm_display_mode *drm_mode,
+				const struct dm_connector_state *dm_state,
+				const struct dc_stream_state *old_stream);
+
+int dm_atomic_get_state(
+			struct drm_atomic_state *state,
+			struct dm_atomic_state **dm_state);
+
+struct amdgpu_dm_connector *amdgpu_dm_find_first_crtc_matching_connector(
+					struct drm_atomic_state *state,
+					struct drm_crtc *crtc);
 #endif /* __AMDGPU_DM_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 52ce5cc6462f..d881b094ce28 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -47,6 +47,9 @@
 
 #if defined(CONFIG_DRM_AMD_DC_DCN)
 #include "dc/dcn20/dcn20_resource.h"
+bool is_timing_changed(struct dc_stream_state *cur_stream,
+			struct dc_stream_state *new_stream);
+
 #endif
 
 static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
@@ -1079,5 +1082,191 @@ bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
 
 	return true;
 }
+static bool pre_compute_mst_dsc_configs_for_state(
+					struct drm_atomic_state *state,
+					struct dc_state *dc_state,
+					struct dsc_mst_fairness_vars *vars)
+{
+	int i, j;
+	struct dc_stream_state *stream;
+	bool computed_streams[MAX_PIPES];
+	struct amdgpu_dm_connector *aconnector;
+	int link_vars_start_index = 0;
+
+	for (i = 0; i < dc_state->stream_count; i++)
+		computed_streams[i] = false;
+
+	for (i = 0; i < dc_state->stream_count; i++) {
+		stream = dc_state->streams[i];
+
+		if (stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST)
+			continue;
+
+		aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
+
+		if (!aconnector || !aconnector->dc_sink)
+			continue;
+
+		if (!aconnector->dc_sink->dsc_caps.dsc_dec_caps.is_dsc_supported)
+			continue;
+
+		if (computed_streams[i])
+			continue;
+
+		if (!is_dsc_need_re_compute(state, dc_state, stream->link))
+			continue;
+
+		mutex_lock(&aconnector->mst_mgr.lock);
+		if (!compute_mst_dsc_configs_for_link(state, dc_state, stream->link,
+			vars, &link_vars_start_index)) {
+			mutex_unlock(&aconnector->mst_mgr.lock);
+		return false;
+		}
+		mutex_unlock(&aconnector->mst_mgr.lock);
+
+		for (j = 0; j < dc_state->stream_count; j++) {
+			if (dc_state->streams[j]->link == stream->link)
+				computed_streams[j] = true;
+		}
+	}
+
+	return true;
+}
+
+static int find_crtc_index_in_state_by_stream(struct drm_atomic_state *state,
+					struct dc_stream_state *stream)
+{
+	int i;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_state, *old_state;
+
+	for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, i) {
+		struct dm_crtc_state *dm_state = to_dm_crtc_state(new_state);
 
+		if (dm_state->stream == stream)
+			return i;
+	}
+	return -1;
+}
+static bool is_link_to_dschub(struct dc_link *dc_link)
+{
+	union dpcd_dsc_basic_capabilities *dsc_caps =
+			&dc_link->dpcd_caps.dsc_caps.dsc_basic_caps;
+
+	/* only check phy used by dsc mst branch */
+	if (dc_link->type != dc_connection_mst_branch)
+		return false;
+
+	if (!(dsc_caps->fields.dsc_support.DSC_SUPPORT ||
+			dsc_caps->fields.dsc_support.DSC_PASSTHROUGH_SUPPORT))
+		return false;
+	return true;
+}
+
+static bool is_dsc_precompute_needed(struct drm_atomic_state *state)
+{
+	uint32_t i;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+	bool ret = false;
+
+	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+		struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(new_crtc_state);
+
+		if (amdgpu_dm_find_first_crtc_matching_connector(state, crtc) == NULL) {
+			ret =  false;
+			break;
+		}
+		if (dm_crtc_state->stream && dm_crtc_state->stream->link)
+			if (is_link_to_dschub(dm_crtc_state->stream->link))
+				ret = true;
+	}
+	return ret;
+}
+
+void pre_validate_dsc(struct drm_atomic_state *state,
+			struct dm_atomic_state **dm_state_ptr,
+			struct dsc_mst_fairness_vars *vars)
+{
+	uint32_t i;
+	struct dm_atomic_state *dm_state;
+	struct dc_state *local_dc_state = NULL;
+
+	if (!is_dsc_precompute_needed(state)) {
+		DRM_DEBUG_DRIVER("DSC precompute is not needed.\n");
+		return;
+	}
+	if (dm_atomic_get_state(state, dm_state_ptr)) {
+		DRM_DEBUG_DRIVER("dm_atomic_get_state() failed\n");
+		return;
+	}
+	dm_state = *dm_state_ptr;
+
+	/*
+	 * create local vailable for dc_state. copy content of streams of dm_state->context
+	 * to local variable. make sure stream pointer of local variable not the same as stream
+	 * from dm_state->context.
+	 */
+
+	local_dc_state = kmemdup(dm_state->context, sizeof(struct dc_state), GFP_KERNEL);
+	if (!local_dc_state)
+		return;
+
+	for (i = 0; i < local_dc_state->stream_count; i++) {
+		struct dc_stream_state *stream = dm_state->context->streams[i];
+		int ind = find_crtc_index_in_state_by_stream(state, stream);
+
+		if (ind >= 0) {
+			struct amdgpu_dm_connector *aconnector;
+			struct drm_connector_state *drm_new_conn_state;
+			struct dm_connector_state *dm_new_conn_state;
+			struct dm_crtc_state *dm_old_crtc_state;
+
+			aconnector = amdgpu_dm_find_first_crtc_matching_connector(state,
+									state->crtcs[ind].ptr);
+			drm_new_conn_state = drm_atomic_get_new_connector_state(state,
+									&aconnector->base);
+			dm_new_conn_state = to_dm_connector_state(drm_new_conn_state);
+			dm_old_crtc_state = to_dm_crtc_state(state->crtcs[ind].old_state);
+
+			local_dc_state->streams[i] = create_validate_stream_for_sink(aconnector,
+								&state->crtcs[ind].new_state->mode,
+								dm_new_conn_state,
+								dm_old_crtc_state->stream);
+		}
+	}
+
+	if (!pre_compute_mst_dsc_configs_for_state(state, local_dc_state, vars)) {
+		DRM_DEBUG_DRIVER("pre_compute_mst_dsc_configs_for_state() failed\n");
+		goto clean_exit;
+	}
+
+
+	/*
+	 * compare local_streams -> timing  with dm_state->context,
+	 * if the same set crtc_state->mode-change = 0;
+	 */
+	for (i = 0; i < local_dc_state->stream_count; i++) {
+		struct dc_stream_state *stream = dm_state->context->streams[i];
+
+		if (local_dc_state->streams[i] &&
+				is_timing_changed(stream, local_dc_state->streams[i])) {
+			DRM_DEBUG_DRIVER("crtc[%d] needs mode_changed\n", i);
+		} else {
+			int ind = find_crtc_index_in_state_by_stream(state, stream);
+
+			if (ind >= 0)
+				state->crtcs[ind].new_state->mode_changed = 0;
+		}
+	}
+clean_exit:
+	for (i = 0; i < local_dc_state->stream_count; i++) {
+		struct dc_stream_state *stream = dm_state->context->streams[i];
+
+		if (local_dc_state->streams[i] != stream)
+			dc_stream_release(local_dc_state->streams[i]);
+	}
+
+	kfree(local_dc_state);
+}
 #endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index 5da28ca03372..c561e0d872d6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -60,6 +60,10 @@ bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
 				       struct dsc_mst_fairness_vars *vars);
 
 bool needs_dsc_aux_workaround(struct dc_link *link);
+
+void pre_validate_dsc(struct drm_atomic_state *state,
+		      struct dm_atomic_state **dm_state_ptr,
+		      struct dsc_mst_fairness_vars *vars);
 #endif
 
 #endif
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index b36bae4b5bc9..23e113dc0790 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -1614,7 +1614,7 @@ bool dc_add_all_planes_for_stream(
 	return add_all_planes_for_stream(dc, stream, &set, 1, context);
 }
 
-static bool is_timing_changed(struct dc_stream_state *cur_stream,
+bool is_timing_changed(struct dc_stream_state *cur_stream,
 		struct dc_stream_state *new_stream)
 {
 	if (cur_stream == NULL)
-- 
2.25.1




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux