diff options
author | Roman Li <Roman.Li@amd.com> | 2022-01-28 12:29:01 -0500 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2022-02-16 17:30:02 -0500 |
commit | 17ce8a6907f77b7ac97ddaa071d8a1f6e06ce85b (patch) | |
tree | 06c9476f09b51fdc338aebea08c9f5c7fe4b9d7d /drivers/gpu/drm/amd | |
parent | aa79d3808e8cf1f5fd0f1c20c2e6a6865b5b940c (diff) | |
download | linux-17ce8a6907f77b7ac97ddaa071d8a1f6e06ce85b.tar.bz2 |
drm/amd/display: Add dsc pre-validation in atomic check
[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 <hersenwu@amd.com>
Acked-by: Jasdeep Dhillon <jdhillon@amd.com>
Signed-off-by: Roman Li <Roman.Li@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd')
5 files changed, 229 insertions, 16 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 e496bedc1a7a..e7c7105e134d 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,8 +3723,8 @@ 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, - struct dm_atomic_state **dm_state) +int dm_atomic_get_state(struct drm_atomic_state *state, + struct dm_atomic_state **dm_state) { struct drm_device *dev = state->dev; struct amdgpu_device *adev = drm_to_adev(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,13 +10176,13 @@ 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, - struct drm_atomic_state *state, - struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state, - struct drm_crtc_state *new_crtc_state, - bool enable, - bool *lock_and_validation_needed) +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, + struct drm_crtc_state *new_crtc_state, + bool enable, + bool *lock_and_validation_needed) { struct dm_atomic_state *dm_state = NULL; struct dm_crtc_state *dm_old_crtc_state, *dm_new_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 28a2b9d476b4..6a908d736d6a 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,16 @@ 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 9305630e9505..1feb06020666 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, @@ -1080,4 +1083,197 @@ 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) +{ + int 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)) { + 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) +{ + int 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 71b393194c55..0d0df7271732 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1614,8 +1614,8 @@ 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, - struct dc_stream_state *new_stream) +bool is_timing_changed(struct dc_stream_state *cur_stream, + struct dc_stream_state *new_stream) { if (cur_stream == NULL) return true; @@ -1679,8 +1679,8 @@ bool dc_is_stream_unchanged( /* * dc_is_stream_scaling_unchanged() - Compare scaling rectangles of two streams. */ -bool dc_is_stream_scaling_unchanged( - struct dc_stream_state *old_stream, struct dc_stream_state *stream) +bool dc_is_stream_scaling_unchanged(struct dc_stream_state *old_stream, + struct dc_stream_state *stream) { if (old_stream == stream) return true; |