Commit 4465dd0e authored by Dillon Varone's avatar Dillon Varone Committed by Alex Deucher
Browse files

drm/amd/display: Refactor SubVP cursor limiting logic



[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: default avatarAlvin Lee <alvin.lee2@amd.com>
Signed-off-by: default avatarDillon Varone <dillon.varone@amd.com>
Signed-off-by: default avatarRay Wu <ray.wu@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent fe3250f1
Loading
Loading
Loading
Loading
+46 −11
Original line number Diff line number Diff line
@@ -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"
@@ -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)
@@ -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;
@@ -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;
		}
@@ -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 {
@@ -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;
}

@@ -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;
@@ -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;

	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++) {
		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;
			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;
}

/**
@@ -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 −0
Original line number Diff line number Diff line
@@ -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";
	}
+6 −42
Original line number Diff line number Diff line
@@ -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)
{
@@ -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  */
@@ -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;
}
@@ -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;
+107 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
 * Authors: AMD
 *
 */
#include "dc_types.h"
#include "core_types.h"
#include "core_status.h"
#include "dc_state.h"
@@ -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;
}

@@ -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)
@@ -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;
}
+55 −15
Original line number Diff line number Diff line
@@ -265,13 +265,16 @@ void program_cursor_attributes(
}

/*
 * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
 * dc_stream_check_cursor_attributes() - Check validitity of cursor attributes and surface address
 */
bool dc_stream_set_cursor_attributes(
	struct dc_stream_state *stream,
bool dc_stream_check_cursor_attributes(
	const struct dc_stream_state *stream,
	struct dc_state *state,
	const struct dc_cursor_attributes *attributes)
{
	struct dc  *dc;
	const struct dc *dc;

	unsigned int max_cursor_size;

	if (NULL == stream) {
		dm_error("DC: dc_stream is NULL!\n");
@@ -289,22 +292,36 @@ bool dc_stream_set_cursor_attributes(

	dc = stream->ctx->dc;

	/* SubVP is not compatible with HW cursor larger than 64 x 64 x 4.
	 * Therefore, if cursor is greater than 64 x 64 x 4, fallback to SW cursor in the following case:
	 * 1. If the config is a candidate for SubVP high refresh (both single an dual display configs)
	 * 2. If not subvp high refresh, for single display cases, if resolution is >= 5K and refresh rate < 120hz
	 * 3. If not subvp high refresh, for multi display cases, if resolution is >= 4K and refresh rate < 120hz
	/* SubVP is not compatible with HW cursor larger than what can fit in cursor SRAM.
	 * Therefore, if cursor is greater than this, fallback to SW cursor.
	 */
	if (dc->debug.allow_sw_cursor_fallback &&
		attributes->height * attributes->width * 4 > 16384 &&
		!stream->hw_cursor_req) {
		if (check_subvp_sw_cursor_fallback_req(dc, stream))
	if (dc->debug.allow_sw_cursor_fallback && dc->res_pool->funcs->get_max_hw_cursor_size) {
		max_cursor_size = dc->res_pool->funcs->get_max_hw_cursor_size(dc, state, stream);
		max_cursor_size = max_cursor_size * max_cursor_size * 4;

		if (attributes->height * attributes->width * 4 > max_cursor_size) {
			return false;
		}
	}

	return true;
}

/*
 * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
 */
bool dc_stream_set_cursor_attributes(
	struct dc_stream_state *stream,
	const struct dc_cursor_attributes *attributes)
{
	bool result = false;

	if (dc_stream_check_cursor_attributes(stream, stream->ctx->dc->current_state, attributes)) {
		stream->cursor_attributes = *attributes;
		result = true;
	}

	return true;
	return result;
}

bool dc_stream_program_cursor_attributes(
@@ -1109,3 +1126,26 @@ unsigned int dc_stream_get_max_flickerless_instant_vtotal_increase(struct dc_str

	return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, false);
}

bool dc_stream_is_cursor_limit_pending(struct dc *dc, struct dc_stream_state *stream)
{
	bool is_limit_pending = false;

	if (dc->current_state)
		is_limit_pending = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state);

	return is_limit_pending;
}

bool dc_stream_can_clear_cursor_limit(struct dc *dc, struct dc_stream_state *stream)
{
	bool can_clear_limit = false;

	if (dc->current_state)
		can_clear_limit = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state) &&
				(stream->hw_cursor_req ||
				!stream->cursor_position.enable ||
				dc_stream_check_cursor_attributes(stream, dc->current_state, &stream->cursor_attributes));

	return can_clear_limit;
}
Loading