Commit 1c982c9f authored by Alvin Lee's avatar Alvin Lee Committed by Alex Deucher
Browse files

drm/amd/display: SubVP high refresh only if all displays >= 120hz



[Description]
- SubVP high refresh should only be enabled if all displays
  are >= 120hz. We do not want to accidentally enables configs
  such as 60hz[SubVP] + 120hz[SubVP]
- Ensure that the SubVP config generation code does not produce
  configs such as 60hz[SubVP] + 120hz[SubVP]
- Also add admissibility checks to ensure these configs do not
  pass as valid configs

Acked-by: default avatarStylon Wang <stylon.wang@amd.com>
Signed-off-by: default avatarAlvin Lee <alvin.lee2@amd.com>
Reviewed-by: default avatarDillon Varone <Dillon.Varone@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent e8c49e9e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -169,6 +169,10 @@ double dcn32_determine_max_vratio_prefetch(struct dc *dc, struct dc_state *conte

bool dcn32_check_native_scaling_for_res(struct pipe_ctx *pipe, unsigned int width, unsigned int height);

bool dcn32_subvp_drr_admissable(struct dc *dc, struct dc_state *context);

bool dcn32_subvp_vblank_admissable(struct dc *dc, struct dc_state *context, int vlevel);

/* definitions for run time init of reg offsets */

/* CLK SRC */
+101 −0
Original line number Diff line number Diff line
@@ -660,3 +660,104 @@ bool dcn32_check_native_scaling_for_res(struct pipe_ctx *pipe, unsigned int widt

	return is_native_scaling;
}

/**
 * ************************************************************************************************
 * dcn32_subvp_drr_admissable: Determine if SubVP + DRR config is admissible
 *
 * @param [in]: dc: Current DC state
 * @param [in]: context: New DC state to be programmed
 *
 * SubVP + DRR is admissible under the following conditions:
 * - Config must have 2 displays (i.e., 2 non-phantom master pipes)
 * - One display is SubVP
 * - Other display must have Freesync enabled
 *
 * @return: True if admissible, false otherwise
 *
 * ************************************************************************************************
 */
bool dcn32_subvp_drr_admissable(struct dc *dc, struct dc_state *context)
{
	bool result = false;
	uint32_t i;
	uint8_t subvp_count = 0;
	uint8_t non_subvp_pipes = 0;
	bool drr_pipe_found = false;

	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];

		if (!pipe->stream)
			continue;

		if (pipe->plane_state && !pipe->top_pipe) {
			if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
				subvp_count++;
			if (pipe->stream->mall_stream_config.type == SUBVP_NONE) {
				non_subvp_pipes++;
				if (pipe->stream->ignore_msa_timing_param &&
						(pipe->stream->allow_freesync || pipe->stream->vrr_active_variable)) {
					drr_pipe_found = true;
				}
			}
		}
	}

	if (subvp_count == 1 && non_subvp_pipes == 1 && drr_pipe_found)
		result = true;

	return result;
}

/**
 * ************************************************************************************************
 * dcn32_subvp_vblank_admissable: Determine if SubVP + Vblank config is admissible
 *
 * @param [in]: dc: Current DC state
 * @param [in]: context: New DC state to be programmed
 *
 * SubVP + Vblank is admissible under the following conditions:
 * - Config must have 2 displays (i.e., 2 non-phantom master pipes)
 * - One display is SubVP
 * - Other display must not have Freesync capability
 * - DML must have output DRAM clock change support as SubVP + Vblank
 *
 * @return: True if admissible, false otherwise
 *
 * ************************************************************************************************
 */
bool dcn32_subvp_vblank_admissable(struct dc *dc, struct dc_state *context, int vlevel)
{
	bool result = false;
	uint32_t i;
	uint8_t subvp_count = 0;
	uint8_t non_subvp_pipes = 0;
	bool drr_pipe_found = false;
	struct vba_vars_st *vba = &context->bw_ctx.dml.vba;

	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];

		if (!pipe->stream)
			continue;

		if (pipe->plane_state && !pipe->top_pipe) {
			if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
				subvp_count++;
			if (pipe->stream->mall_stream_config.type == SUBVP_NONE) {
				non_subvp_pipes++;
				if (pipe->stream->ignore_msa_timing_param &&
						(pipe->stream->allow_freesync || pipe->stream->vrr_active_variable)) {
					drr_pipe_found = true;
				}
			}
		}
	}

	if (subvp_count == 1 && non_subvp_pipes == 1 && !drr_pipe_found &&
			vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp)
		result = true;

	return result;
}
+112 −66
Original line number Diff line number Diff line
@@ -679,7 +679,6 @@ static bool dcn32_assign_subvp_pipe(struct dc *dc,
	unsigned int max_frame_time = 0;
	bool valid_assignment_found = false;
	unsigned int free_pipes = dcn32_get_num_free_pipes(dc, context);
	bool current_assignment_freesync = false;
	struct vba_vars_st *vba = &context->bw_ctx.dml.vba;

	for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
@@ -720,19 +719,10 @@ static bool dcn32_assign_subvp_pipe(struct dc *dc,
				struct dc_stream_state *stream = pipe->stream;
				unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /
						(double)(stream->timing.pix_clk_100hz * 100)) * 1000000;
				if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) {
				if (frame_us > max_frame_time) {
					*index = i;
					max_frame_time = frame_us;
					valid_assignment_found = true;
					current_assignment_freesync = false;
				/* For the 2-Freesync display case, still choose the one with the
			     * longest frame time
			     */
				} else if (stream->ignore_msa_timing_param && (!valid_assignment_found ||
						(current_assignment_freesync && frame_us > max_frame_time))) {
					*index = i;
					valid_assignment_found = true;
					current_assignment_freesync = true;
				}
			}
		}
@@ -878,11 +868,12 @@ static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context)
 *
 * Return: True if the SubVP + DRR config is schedulable, false otherwise
 */
static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context, struct pipe_ctx *drr_pipe)
static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context)
{
	bool schedulable = false;
	uint32_t i;
	struct pipe_ctx *pipe = NULL;
	struct pipe_ctx *drr_pipe = NULL;
	struct dc_crtc_timing *main_timing = NULL;
	struct dc_crtc_timing *phantom_timing = NULL;
	struct dc_crtc_timing *drr_timing = NULL;
@@ -908,6 +899,19 @@ static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context, struc
			break;
	}

	// Find the DRR pipe
	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		drr_pipe = &context->res_ctx.pipe_ctx[i];

		// We check for master pipe only
		if (!drr_pipe->stream || !drr_pipe->plane_state || drr_pipe->top_pipe || drr_pipe->prev_odm_pipe)
			continue;

		if (drr_pipe->stream->mall_stream_config.type == SUBVP_NONE && drr_pipe->stream->ignore_msa_timing_param &&
				(drr_pipe->stream->allow_freesync || drr_pipe->stream->vrr_active_variable))
			break;
	}

	main_timing = &pipe->stream->timing;
	phantom_timing = &pipe->stream->mall_stream_config.paired_stream->timing;
	drr_timing = &drr_pipe->stream->timing;
@@ -993,13 +997,7 @@ static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context)
		if (!subvp_pipe && pipe->stream->mall_stream_config.type == SUBVP_MAIN)
			subvp_pipe = pipe;
	}
	// Use ignore_msa_timing_param and VRR active, or Freesync flag to identify as DRR On
	if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param &&
			(context->res_ctx.pipe_ctx[vblank_index].stream->allow_freesync ||
			context->res_ctx.pipe_ctx[vblank_index].stream->vrr_active_variable)) {
		// SUBVP + DRR case -- only allowed if run through DRR validation path
		schedulable = false;
	} else if (found) {
	if (found) {
		main_timing = &subvp_pipe->stream->timing;
		phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing;
		vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;
@@ -1028,6 +1026,56 @@ static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context)
	return schedulable;
}

/**
 * ************************************************************************************************
 * subvp_subvp_admissable: Determine if subvp + subvp config is admissible
 *
 * @param [in]: dc: Current DC state
 * @param [in]: context: New DC state to be programmed
 *
 * SubVP + SubVP is admissible under the following conditions:
 * - All SubVP pipes are < 120Hz OR
 * - All SubVP pipes are >= 120hz
 *
 * @return: True if admissible, false otherwise
 *
 * ************************************************************************************************
 */
static bool subvp_subvp_admissable(struct dc *dc,
				struct dc_state *context)
{
	bool result = false;
	uint32_t i;
	uint8_t subvp_count = 0;
	uint32_t min_refresh = subvp_high_refresh_list.min_refresh, max_refresh = 0;
	uint32_t refresh_rate = 0;

	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];

		if (!pipe->stream)
			continue;

		if (pipe->plane_state && !pipe->top_pipe &&
				pipe->stream->mall_stream_config.type == SUBVP_MAIN) {
			refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +
					pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)
					/ (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);
			if (refresh_rate < min_refresh)
				min_refresh = refresh_rate;
			if (refresh_rate > max_refresh)
				max_refresh = refresh_rate;
			subvp_count++;
		}
	}

	if (subvp_count == 2 && ((min_refresh < 120 && max_refresh < 120) ||
			(min_refresh >= 120 && max_refresh >= 120)))
		result = true;

	return result;
}

/**
 * subvp_validate_static_schedulability - Check which SubVP case is calculated
 * and handle static analysis based on the case.
@@ -1046,11 +1094,12 @@ static bool subvp_validate_static_schedulability(struct dc *dc,
				struct dc_state *context,
				int vlevel)
{
	bool schedulable = true;	// true by default for single display case
	bool schedulable = false;
	struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
	uint32_t i, pipe_idx;
	uint8_t subvp_count = 0;
	uint8_t vactive_count = 0;
	uint8_t non_subvp_pipes = 0;

	for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
@@ -1058,14 +1107,18 @@ static bool subvp_validate_static_schedulability(struct dc *dc,
		if (!pipe->stream)
			continue;

		if (pipe->plane_state && !pipe->top_pipe &&
				pipe->stream->mall_stream_config.type == SUBVP_MAIN)
		if (pipe->plane_state && !pipe->top_pipe) {
			if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
				subvp_count++;
			if (pipe->stream->mall_stream_config.type == SUBVP_NONE) {
				non_subvp_pipes++;
			}
		}

		// Count how many planes that aren't SubVP/phantom are capable of VACTIVE
		// switching (SubVP + VACTIVE unsupported). In situations where we force
		// SubVP for a VACTIVE plane, we don't want to increment the vactive_count.
		if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 &&
		if (vba->ActiveDRAMClockChangeLatencyMarginPerState[vlevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] > 0 &&
		    pipe->stream->mall_stream_config.type == SUBVP_NONE) {
			vactive_count++;
		}
@@ -1074,13 +1127,14 @@ static bool subvp_validate_static_schedulability(struct dc *dc,

	if (subvp_count == 2) {
		// Static schedulability check for SubVP + SubVP case
		schedulable = subvp_subvp_schedulable(dc, context);
	} else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp) {
		// Static schedulability check for SubVP + VBLANK case. Also handle the case where
		// DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK)
		if (vactive_count > 0)
			schedulable = false;
		else
		schedulable = subvp_subvp_admissable(dc, context) && subvp_subvp_schedulable(dc, context);
	} else if (subvp_count == 1 && non_subvp_pipes == 0) {
		// Single SubVP configs will be supported by default as long as it's suppported by DML
		schedulable = true;
	} else if (subvp_count == 1 && non_subvp_pipes == 1) {
		if (dcn32_subvp_drr_admissable(dc, context))
			schedulable = subvp_drr_schedulable(dc, context);
		else if (dcn32_subvp_vblank_admissable(dc, context, vlevel))
			schedulable = subvp_vblank_schedulable(dc, context);
	} else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vactive_w_mall_sub_vp &&
			vactive_count > 0) {
@@ -1104,10 +1158,6 @@ static void dcn32_full_validate_bw_helper(struct dc *dc,
	unsigned int dc_pipe_idx = 0;
	int i = 0;
	bool found_supported_config = false;
	struct pipe_ctx *pipe = NULL;
	uint32_t non_subvp_pipes = 0;
	bool drr_pipe_found = false;
	uint32_t drr_pipe_index = 0;

	dc_assert_fp_enabled();

@@ -1197,31 +1247,12 @@ static void dcn32_full_validate_bw_helper(struct dc *dc,
				}
			}

			if (*vlevel < context->bw_ctx.dml.soc.num_states &&
			    vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported
			    && subvp_validate_static_schedulability(dc, context, *vlevel)) {
			if (*vlevel < context->bw_ctx.dml.soc.num_states
			    && subvp_validate_static_schedulability(dc, context, *vlevel))
				found_supported_config = true;
			} else if (*vlevel < context->bw_ctx.dml.soc.num_states) {
				/* Case where 1 SubVP is added, and DML reports MCLK unsupported or DRR is allowed.
				 * This handles the case for SubVP + DRR, where the DRR display does not support MCLK
				 * switch at it's native refresh rate / timing, or DRR is allowed for the non-subvp
				 * display.
				 */
				for (i = 0; i < dc->res_pool->pipe_count; i++) {
					pipe = &context->res_ctx.pipe_ctx[i];
					if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
					    pipe->stream->mall_stream_config.type == SUBVP_NONE) {
						non_subvp_pipes++;
						// Use ignore_msa_timing_param flag to identify as DRR
						if (pipe->stream->ignore_msa_timing_param && pipe->stream->allow_freesync) {
							drr_pipe_found = true;
							drr_pipe_index = i;
						}
					}
				}
				// If there is only 1 remaining non SubVP pipe that is DRR, check static
				// schedulability for SubVP + DRR.
				if (non_subvp_pipes == 1 && drr_pipe_found) {
			if (found_supported_config) {
				// For SubVP + DRR cases, we can force the lowest vlevel that supports the mode
				if (dcn32_subvp_drr_admissable(dc, context) && subvp_drr_schedulable(dc, context)) {
					/* find lowest vlevel that supports the config */
					for (i = *vlevel; i >= 0; i--) {
						if (vba->ModeSupport[i][vba->maxMpcComb]) {
@@ -1230,9 +1261,6 @@ static void dcn32_full_validate_bw_helper(struct dc *dc,
							break;
						}
					}

					found_supported_config = subvp_drr_schedulable(dc, context,
										       &context->res_ctx.pipe_ctx[drr_pipe_index]);
				}
			}
		}
@@ -2882,16 +2910,34 @@ bool dcn32_allow_subvp_high_refresh_rate(struct dc *dc, struct dc_state *context
{
	bool allow = false;
	uint32_t refresh_rate = 0;
	uint32_t min_refresh = subvp_high_refresh_list.min_refresh;
	uint32_t max_refresh = subvp_high_refresh_list.max_refresh;
	uint32_t subvp_min_refresh = subvp_high_refresh_list.min_refresh;
	uint32_t subvp_max_refresh = subvp_high_refresh_list.max_refresh;
	uint32_t min_refresh = subvp_max_refresh;
	uint32_t i;

	if (!dc->debug.disable_subvp_high_refresh && pipe->stream &&
	/* Only allow SubVP on high refresh displays if all connected displays
	 * are considered "high refresh" (i.e. >= 120hz). We do not want to
	 * allow combinations such as 120hz (SubVP) + 60hz (SubVP).
	 */
	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

		if (!pipe_ctx->stream)
			continue;
		refresh_rate = (pipe_ctx->stream->timing.pix_clk_100hz * 100 +
				pipe_ctx->stream->timing.v_total * pipe_ctx->stream->timing.h_total - 1)
						/ (double)(pipe_ctx->stream->timing.v_total * pipe_ctx->stream->timing.h_total);

		if (refresh_rate < min_refresh)
			min_refresh = refresh_rate;
	}

	if (!dc->debug.disable_subvp_high_refresh && min_refresh >= subvp_min_refresh && pipe->stream &&
			pipe->plane_state && !(pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) {
		refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +
						pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)
						/ (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);
		if (refresh_rate >= min_refresh && refresh_rate <= max_refresh) {
		if (refresh_rate >= subvp_min_refresh && refresh_rate <= subvp_max_refresh) {
			for (i = 0; i < SUBVP_HIGH_REFRESH_LIST_LEN; i++) {
				uint32_t width = subvp_high_refresh_list.res[i].width;
				uint32_t height = subvp_high_refresh_list.res[i].height;