Skip to content

Commit

Permalink
drm/amd/display: Refactor SubVP cursor limiting logic
Browse files Browse the repository at this point in the history
[WHY]
There are several gaps that can result in SubVP being enabled with
incompatible HW cursor sizes, and unjust restrictions to cursor size due
to wrong predictions on future usage of SubVP.

[HOW]
- remove "prediction" logic in favor of tagging based on previous SubVP
  usage
- block SubVP if current HW cursor settings are incompatible
- provide interface for DM to determine if HW cursor should be disabled
  due to an attempt to enable SubVP

Reviewed-by: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
  • Loading branch information
Dillon Varone authored and Alex Deucher committed May 5, 2025
1 parent fe3250f commit 4465dd0
Show file tree
Hide file tree
Showing 39 changed files with 418 additions and 135 deletions.
57 changes: 46 additions & 11 deletions drivers/gpu/drm/amd/display/dc/core/dc.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "dc_state.h"
#include "dc_state_priv.h"
#include "dc_plane_priv.h"
#include "dc_stream_priv.h"

#include "gpio_service_interface.h"
#include "clk_mgr.h"
Expand Down Expand Up @@ -2886,7 +2887,7 @@ static enum surface_update_type check_update_surfaces_for_stream(
int i;
enum surface_update_type overall_type = UPDATE_TYPE_FAST;

if (dc->idle_optimizations_allowed)
if (dc->idle_optimizations_allowed || dc_can_clear_cursor_limit(dc))
overall_type = UPDATE_TYPE_FULL;

if (stream_status == NULL || stream_status->plane_count != surface_count)
Expand Down Expand Up @@ -3290,7 +3291,7 @@ static void copy_stream_update_to_stream(struct dc *dc,
if (dsc_validate_context) {
stream->timing.dsc_cfg = *update->dsc_config;
stream->timing.flags.DSC = enable_dsc;
if (!dc->res_pool->funcs->validate_bandwidth(dc, dsc_validate_context, true)) {
if (dc->res_pool->funcs->validate_bandwidth(dc, dsc_validate_context, true) != DC_OK) {
stream->timing.dsc_cfg = old_dsc_cfg;
stream->timing.flags.DSC = old_dsc_enabled;
update->dsc_config = NULL;
Expand Down Expand Up @@ -3515,7 +3516,7 @@ static bool update_planes_and_stream_state(struct dc *dc,
}

if (update_type == UPDATE_TYPE_FULL) {
if (!dc->res_pool->funcs->validate_bandwidth(dc, context, false)) {
if (dc->res_pool->funcs->validate_bandwidth(dc, context, false) != DC_OK) {
BREAK_TO_DEBUGGER();
goto fail;
}
Expand Down Expand Up @@ -4608,7 +4609,7 @@ static struct dc_state *create_minimal_transition_state(struct dc *dc,

backup_and_set_minimal_pipe_split_policy(dc, base_context, policy);
/* commit minimal state */
if (dc->res_pool->funcs->validate_bandwidth(dc, minimal_transition_context, false)) {
if (dc->res_pool->funcs->validate_bandwidth(dc, minimal_transition_context, false) == DC_OK) {
/* prevent underflow and corruption when reconfiguring pipes */
force_vsync_flip_in_minimal_transition_context(minimal_transition_context);
} else {
Expand Down Expand Up @@ -5043,6 +5044,9 @@ static bool full_update_required(struct dc *dc,
if (dc->idle_optimizations_allowed)
return true;

if (dc_can_clear_cursor_limit(dc))
return true;

return false;
}

Expand Down Expand Up @@ -5128,7 +5132,7 @@ static bool update_planes_and_stream_v1(struct dc *dc,
copy_stream_update_to_stream(dc, context, stream, stream_update);

if (update_type >= UPDATE_TYPE_FULL) {
if (!dc->res_pool->funcs->validate_bandwidth(dc, context, false)) {
if (dc->res_pool->funcs->validate_bandwidth(dc, context, false) != DC_OK) {
DC_ERROR("Mode validation failed for stream update!\n");
dc_state_release(context);
return false;
Expand Down Expand Up @@ -6272,15 +6276,22 @@ bool dc_abm_save_restore(
void dc_query_current_properties(struct dc *dc, struct dc_current_properties *properties)
{
unsigned int i;
bool subvp_sw_cursor_req = false;
unsigned int max_cursor_size = dc->caps.max_cursor_size;
unsigned int stream_cursor_size;

for (i = 0; i < dc->current_state->stream_count; i++) {
if (check_subvp_sw_cursor_fallback_req(dc, dc->current_state->streams[i]) && !dc->current_state->streams[i]->hw_cursor_req) {
subvp_sw_cursor_req = true;
break;
if (dc->debug.allow_sw_cursor_fallback && dc->res_pool->funcs->get_max_hw_cursor_size) {
for (i = 0; i < dc->current_state->stream_count; i++) {
stream_cursor_size = dc->res_pool->funcs->get_max_hw_cursor_size(dc,
dc->current_state,
dc->current_state->streams[i]);

if (stream_cursor_size < max_cursor_size) {
max_cursor_size = stream_cursor_size;
}
}
}
properties->cursor_size_limit = subvp_sw_cursor_req ? 64 : dc->caps.max_cursor_size;

properties->cursor_size_limit = max_cursor_size;
}

/**
Expand Down Expand Up @@ -6346,3 +6357,27 @@ unsigned int dc_get_det_buffer_size_from_state(const struct dc_state *context)
else
return 0;
}

bool dc_is_cursor_limit_pending(struct dc *dc)
{
uint32_t i;

for (i = 0; i < dc->current_state->stream_count; i++) {
if (dc_stream_is_cursor_limit_pending(dc, dc->current_state->streams[i]))
return true;
}

return false;
}

bool dc_can_clear_cursor_limit(struct dc *dc)
{
uint32_t i;

for (i = 0; i < dc->current_state->stream_count; i++) {
if (dc_state_can_clear_stream_cursor_subvp_limit(dc->current_state->streams[i], dc->current_state))
return true;
}

return false;
}
2 changes: 2 additions & 0 deletions drivers/gpu/drm/amd/display/dc/core/dc_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ char *dc_status_to_str(enum dc_status status)
return "Fail dp payload allocation";
case DC_FAIL_DP_LINK_BANDWIDTH:
return "Insufficient DP link bandwidth";
case DC_FAIL_HW_CURSOR_SUPPORT:
return "HW Cursor not supported";
case DC_ERROR_UNEXPECTED:
return "Unexpected error";
}
Expand Down
48 changes: 6 additions & 42 deletions drivers/gpu/drm/amd/display/dc/core/dc_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -1342,32 +1342,6 @@ static void calculate_inits_and_viewports(struct pipe_ctx *pipe_ctx)
data->viewport_c.y += src.y / vpc_div;
}

static bool is_subvp_high_refresh_candidate(struct dc_stream_state *stream)
{
uint32_t refresh_rate;
struct dc *dc = stream->ctx->dc;

refresh_rate = (stream->timing.pix_clk_100hz * (uint64_t)100 +
stream->timing.v_total * stream->timing.h_total - (uint64_t)1);
refresh_rate = div_u64(refresh_rate, stream->timing.v_total);
refresh_rate = div_u64(refresh_rate, stream->timing.h_total);

/* If there's any stream that fits the SubVP high refresh criteria,
* we must return true. This is because cursor updates are asynchronous
* with full updates, so we could transition into a SubVP config and
* remain in HW cursor mode if there's no cursor update which will
* then cause corruption.
*/
if ((refresh_rate >= 120 && refresh_rate <= 175 &&
stream->timing.v_addressable >= 1080 &&
stream->timing.v_addressable <= 2160) &&
(dc->current_state->stream_count > 1 ||
(dc->current_state->stream_count == 1 && !stream->allow_freesync)))
return true;

return false;
}

static enum controller_dp_test_pattern convert_dp_to_controller_test_pattern(
enum dp_test_pattern test_pattern)
{
Expand Down Expand Up @@ -4259,6 +4233,11 @@ enum dc_status dc_validate_with_context(struct dc *dc,
}
}

/* clear subvp cursor limitations */
for (i = 0; i < context->stream_count; i++) {
dc_state_set_stream_subvp_cursor_limit(context->streams[i], context, false);
}

res = dc_validate_global_state(dc, context, fast_validate);

/* calculate pixel rate divider after deciding pxiel clock & odm combine */
Expand Down Expand Up @@ -4385,8 +4364,7 @@ enum dc_status dc_validate_global_state(
result = resource_build_scaling_params_for_context(dc, new_ctx);

if (result == DC_OK)
if (!dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, fast_validate))
result = DC_FAIL_BANDWIDTH_VALIDATE;
result = dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, fast_validate);

return result;
}
Expand Down Expand Up @@ -5538,20 +5516,6 @@ enum dc_status update_dp_encoder_resources_for_test_harness(const struct dc *dc,
return DC_OK;
}

bool check_subvp_sw_cursor_fallback_req(const struct dc *dc, struct dc_stream_state *stream)
{
if (!dc->debug.disable_subvp_high_refresh && is_subvp_high_refresh_candidate(stream))
return true;
if (dc->current_state->stream_count == 1 && stream->timing.v_addressable >= 2880 &&
((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120)
return true;
else if (dc->current_state->stream_count > 1 && stream->timing.v_addressable >= 1080 &&
((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120)
return true;

return false;
}

struct dscl_prog_data *resource_get_dscl_prog_data(struct pipe_ctx *pipe_ctx)
{
return &pipe_ctx->plane_res.scl_data.dscl_prog_data;
Expand Down
111 changes: 107 additions & 4 deletions drivers/gpu/drm/amd/display/dc/core/dc_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* Authors: AMD
*
*/
#include "dc_types.h"
#include "core_types.h"
#include "core_status.h"
#include "dc_state.h"
Expand Down Expand Up @@ -812,8 +813,12 @@ enum dc_status dc_state_add_phantom_stream(const struct dc *dc,
if (phantom_stream_status) {
phantom_stream_status->mall_stream_config.type = SUBVP_PHANTOM;
phantom_stream_status->mall_stream_config.paired_stream = main_stream;
phantom_stream_status->mall_stream_config.subvp_limit_cursor_size = false;
phantom_stream_status->mall_stream_config.cursor_size_limit_subvp = false;
}

dc_state_set_stream_subvp_cursor_limit(main_stream, state, true);

return res;
}

Expand Down Expand Up @@ -939,13 +944,20 @@ void dc_state_release_phantom_streams_and_planes(
const struct dc *dc,
struct dc_state *state)
{
unsigned int phantom_count;
struct dc_stream_state *phantom_streams[MAX_PHANTOM_PIPES];
struct dc_plane_state *phantom_planes[MAX_PHANTOM_PIPES];
int i;

for (i = 0; i < state->phantom_stream_count; i++)
dc_state_release_phantom_stream(dc, state, state->phantom_streams[i]);
phantom_count = state->phantom_stream_count;
memcpy(phantom_streams, state->phantom_streams, sizeof(struct dc_stream_state *) * MAX_PHANTOM_PIPES);
for (i = 0; i < phantom_count; i++)
dc_state_release_phantom_stream(dc, state, phantom_streams[i]);

for (i = 0; i < state->phantom_plane_count; i++)
dc_state_release_phantom_plane(dc, state, state->phantom_planes[i]);
phantom_count = state->phantom_plane_count;
memcpy(phantom_planes, state->phantom_planes, sizeof(struct dc_plane_state *) * MAX_PHANTOM_PIPES);
for (i = 0; i < phantom_count; i++)
dc_state_release_phantom_plane(dc, state, phantom_planes[i]);
}

struct dc_stream_state *dc_state_get_stream_from_id(const struct dc_state *state, unsigned int id)
Expand Down Expand Up @@ -977,3 +989,94 @@ bool dc_state_is_fams2_in_use(

return is_fams2_in_use;
}

void dc_state_set_stream_subvp_cursor_limit(const struct dc_stream_state *stream,
struct dc_state *state,
bool limit)
{
struct dc_stream_status *stream_status;

stream_status = dc_state_get_stream_status(state, stream);

if (stream_status) {
stream_status->mall_stream_config.subvp_limit_cursor_size = limit;
}
}

bool dc_state_get_stream_subvp_cursor_limit(const struct dc_stream_state *stream,
struct dc_state *state)
{
bool limit = false;

struct dc_stream_status *stream_status;

stream_status = dc_state_get_stream_status(state, stream);

if (stream_status) {
limit = stream_status->mall_stream_config.subvp_limit_cursor_size;
}

return limit;
}

void dc_state_set_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
struct dc_state *state,
bool limit)
{
struct dc_stream_status *stream_status;

stream_status = dc_state_get_stream_status(state, stream);

if (stream_status) {
stream_status->mall_stream_config.cursor_size_limit_subvp = limit;
}
}

bool dc_state_get_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
struct dc_state *state)
{
bool limit = false;

struct dc_stream_status *stream_status;

stream_status = dc_state_get_stream_status(state, stream);

if (stream_status) {
limit = stream_status->mall_stream_config.cursor_size_limit_subvp;
}

return limit;
}

bool dc_state_can_clear_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
struct dc_state *state)
{
bool can_clear_limit = false;

struct dc_stream_status *stream_status;

stream_status = dc_state_get_stream_status(state, stream);

if (stream_status) {
can_clear_limit = dc_state_get_stream_cursor_subvp_limit(stream, state) &&
(stream_status->mall_stream_config.type == SUBVP_PHANTOM ||
stream->hw_cursor_req ||
!stream_status->mall_stream_config.subvp_limit_cursor_size ||
!stream->cursor_position.enable ||
dc_stream_check_cursor_attributes(stream, state, &stream->cursor_attributes));
}

return can_clear_limit;
}

bool dc_state_is_subvp_in_use(struct dc_state *state)
{
uint32_t i;

for (i = 0; i < state->stream_count; i++) {
if (dc_state_get_stream_subvp_type(state, state->streams[i]) != SUBVP_NONE)
return true;
}

return false;
}
Loading

0 comments on commit 4465dd0

Please sign in to comment.