Commit 2665f63a authored by Mikita Lipski's avatar Mikita Lipski Committed by Alex Deucher
Browse files

drm/amd/display: Enable DSC over eDP



[why]
- Adding a DM interface to enable DSC over eDP on Linux
- DSC over eDP will allow to power savings by reducing
the bandwidth required to support panel's modes
- Apply link optimization algorithm to reduce link bandwidth
when DSC is enabled

[how]
- Read eDP panel's DSC capabilities
- Apply DSC policy on eDP panel based on its DSC capabilities
- Enable DSC encoder's on the pipe
- Enable DSC on panel's side by setting DSC_ENABLE DPCD register
- Adding link optimization algorithm to reduce link rate or lane
count based

Reviewed-by: default avatarNicholas Kazlauskas <Nicholas.Kazlauskas@amd.com>
Acked-by: default avatarWayne Lin <wayne.lin@amd.com>
Signed-off-by: default avatarMikita Lipski <mikita.lipski@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 2430be71
Loading
Loading
Loading
Loading
+70 −3
Original line number Diff line number Diff line
@@ -1478,8 +1478,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
	if (amdgpu_dc_debug_mask & DC_DISABLE_STUTTER)
		adev->dm.dc->debug.disable_stutter = true;

	if (amdgpu_dc_debug_mask & DC_DISABLE_DSC)
	if (amdgpu_dc_debug_mask & DC_DISABLE_DSC) {
		adev->dm.dc->debug.disable_dsc = true;
		adev->dm.dc->debug.disable_dsc_edp = true;
	}

	if (amdgpu_dc_debug_mask & DC_DISABLE_CLOCK_GATING)
		adev->dm.dc->debug.disable_clock_gate = true;
@@ -6034,7 +6036,8 @@ static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
{
	stream->timing.flags.DSC = 0;

	if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
	if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
		sink->sink_signal == SIGNAL_TYPE_EDP)) {
		dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
				      aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
				      aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
@@ -6042,6 +6045,64 @@ static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
	}
}

static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
				    struct dc_sink *sink, struct dc_stream_state *stream,
				    struct dsc_dec_dpcd_caps *dsc_caps,
				    uint32_t max_dsc_target_bpp_limit_override)
{
	const struct dc_link_settings *verified_link_cap = NULL;
	uint32_t link_bw_in_kbps;
	uint32_t edp_min_bpp_x16, edp_max_bpp_x16;
	struct dc *dc = sink->ctx->dc;
	struct dc_dsc_bw_range bw_range = {0};
	struct dc_dsc_config dsc_cfg = {0};

	verified_link_cap = dc_link_get_link_cap(stream->link);
	link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
	edp_min_bpp_x16 = 8 * 16;
	edp_max_bpp_x16 = 8 * 16;

	if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
		edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;

	if (edp_max_bpp_x16 < edp_min_bpp_x16)
		edp_min_bpp_x16 = edp_max_bpp_x16;

	if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
				dc->debug.dsc_min_slice_height_override,
				edp_min_bpp_x16, edp_max_bpp_x16,
				dsc_caps,
				&stream->timing,
				&bw_range)) {

		if (bw_range.max_kbps < link_bw_in_kbps) {
			if (dc_dsc_compute_config(dc->res_pool->dscs[0],
					dsc_caps,
					dc->debug.dsc_min_slice_height_override,
					max_dsc_target_bpp_limit_override,
					0,
					&stream->timing,
					&dsc_cfg)) {
				stream->timing.dsc_cfg = dsc_cfg;
				stream->timing.flags.DSC = 1;
				stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
			}
			return;
		}
	}

	if (dc_dsc_compute_config(dc->res_pool->dscs[0],
				dsc_caps,
				dc->debug.dsc_min_slice_height_override,
				max_dsc_target_bpp_limit_override,
				link_bw_in_kbps,
				&stream->timing,
				&dsc_cfg)) {
		stream->timing.dsc_cfg = dsc_cfg;
		stream->timing.flags.DSC = 1;
	}
}

static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
										struct dc_sink *sink, struct dc_stream_state *stream,
										struct dsc_dec_dpcd_caps *dsc_caps)
@@ -6049,6 +6110,7 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
	struct drm_connector *drm_connector = &aconnector->base;
	uint32_t link_bandwidth_kbps;
	uint32_t max_dsc_target_bpp_limit_override = 0;
	struct dc *dc = sink->ctx->dc;

	link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
							dc_link_get_link_cap(aconnector->dc_link));
@@ -6061,7 +6123,12 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
	dc_dsc_policy_set_enable_dsc_when_not_needed(
		aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);

	if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
	if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_EDP && !dc->debug.disable_dsc_edp &&
	    dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {

		apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);

	} else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {

		if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
						dsc_caps,
+1 −1
Original line number Diff line number Diff line
@@ -584,7 +584,7 @@ bool dm_helpers_dp_write_dsc_enable(
		ret = drm_dp_dpcd_write(aconnector->dsc_aux, DP_DSC_ENABLE, &enable_dsc, 1);
	}

	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT) {
	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || SIGNAL_TYPE_EDP) {
		ret = dm_helpers_dp_write_dpcd(ctx, stream->link, DP_DSC_ENABLE, &enable_dsc, 1);
		DC_LOG_DC("Send DSC %s to sst display\n", enable_dsc ? "enable" : "disable");
	}
+2 −0
Original line number Diff line number Diff line
@@ -4792,6 +4792,8 @@ bool dc_link_should_enable_fec(const struct dc_link *link)
			link->local_sink &&
			link->local_sink->edid_caps.panel_patch.disable_fec) ||
			(link->connector_signal == SIGNAL_TYPE_EDP
				// enable FEC for EDP if DSC is supported
				&& link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT == false
				))
		is_fec_disable = true;

+161 −1
Original line number Diff line number Diff line
@@ -3346,6 +3346,148 @@ bool decide_edp_link_settings(struct dc_link *link, struct dc_link_settings *lin
	return false;
}

static bool decide_edp_link_settings_with_dsc(struct dc_link *link,
		struct dc_link_settings *link_setting,
		uint32_t req_bw,
		enum dc_link_rate max_link_rate)
{
	struct dc_link_settings initial_link_setting;
	struct dc_link_settings current_link_setting;
	uint32_t link_bw;

	unsigned int policy = 0;

	policy = link->ctx->dc->debug.force_dsc_edp_policy;
	if (max_link_rate == LINK_RATE_UNKNOWN)
		max_link_rate = link->verified_link_cap.link_rate;
	/*
	 * edp_supported_link_rates_count is only valid for eDP v1.4 or higher.
	 * Per VESA eDP spec, "The DPCD revision for eDP v1.4 is 13h"
	 */
	if ((link->dpcd_caps.dpcd_rev.raw < DPCD_REV_13 ||
			link->dpcd_caps.edp_supported_link_rates_count == 0)) {
		/* for DSC enabled case, we search for minimum lane count */
		memset(&initial_link_setting, 0, sizeof(initial_link_setting));
		initial_link_setting.lane_count = LANE_COUNT_ONE;
		initial_link_setting.link_rate = LINK_RATE_LOW;
		initial_link_setting.link_spread = LINK_SPREAD_DISABLED;
		initial_link_setting.use_link_rate_set = false;
		initial_link_setting.link_rate_set = 0;
		current_link_setting = initial_link_setting;
		if (req_bw > dc_link_bandwidth_kbps(link, &link->verified_link_cap))
			return false;

		/* search for the minimum link setting that:
		 * 1. is supported according to the link training result
		 * 2. could support the b/w requested by the timing
		 */
		while (current_link_setting.link_rate <=
				max_link_rate) {
			link_bw = dc_link_bandwidth_kbps(
					link,
					&current_link_setting);
			if (req_bw <= link_bw) {
				*link_setting = current_link_setting;
				return true;
			}
			if (policy) {
				/* minimize lane */
				if (current_link_setting.link_rate < max_link_rate) {
					current_link_setting.link_rate =
							increase_link_rate(
									current_link_setting.link_rate);
				} else {
					if (current_link_setting.lane_count <
									link->verified_link_cap.lane_count) {
						current_link_setting.lane_count =
								increase_lane_count(
										current_link_setting.lane_count);
						current_link_setting.link_rate = initial_link_setting.link_rate;
					} else
						break;
				}
			} else {
				/* minimize link rate */
				if (current_link_setting.lane_count <
						link->verified_link_cap.lane_count) {
					current_link_setting.lane_count =
							increase_lane_count(
									current_link_setting.lane_count);
				} else {
					current_link_setting.link_rate =
							increase_link_rate(
									current_link_setting.link_rate);
					current_link_setting.lane_count =
							initial_link_setting.lane_count;
				}
			}
		}
		return false;
	}

	/* if optimize edp link is supported */
	memset(&initial_link_setting, 0, sizeof(initial_link_setting));
	initial_link_setting.lane_count = LANE_COUNT_ONE;
	initial_link_setting.link_rate = link->dpcd_caps.edp_supported_link_rates[0];
	initial_link_setting.link_spread = LINK_SPREAD_DISABLED;
	initial_link_setting.use_link_rate_set = true;
	initial_link_setting.link_rate_set = 0;
	current_link_setting = initial_link_setting;

	/* search for the minimum link setting that:
	 * 1. is supported according to the link training result
	 * 2. could support the b/w requested by the timing
	 */
	while (current_link_setting.link_rate <=
			max_link_rate) {
		link_bw = dc_link_bandwidth_kbps(
				link,
				&current_link_setting);
		if (req_bw <= link_bw) {
			*link_setting = current_link_setting;
			return true;
		}
		if (policy) {
			/* minimize lane */
			if (current_link_setting.link_rate_set <
					link->dpcd_caps.edp_supported_link_rates_count
					&& current_link_setting.link_rate < max_link_rate) {
				current_link_setting.link_rate_set++;
				current_link_setting.link_rate =
					link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
			} else {
				if (current_link_setting.lane_count < link->verified_link_cap.lane_count) {
					current_link_setting.lane_count =
							increase_lane_count(
									current_link_setting.lane_count);
					current_link_setting.link_rate_set = initial_link_setting.link_rate_set;
					current_link_setting.link_rate =
						link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
				} else
					break;
			}
		} else {
			/* minimize link rate */
			if (current_link_setting.lane_count <
					link->verified_link_cap.lane_count) {
				current_link_setting.lane_count =
						increase_lane_count(
								current_link_setting.lane_count);
			} else {
				if (current_link_setting.link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) {
					current_link_setting.link_rate_set++;
					current_link_setting.link_rate =
						link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
					current_link_setting.lane_count =
						initial_link_setting.lane_count;
				} else
					break;
			}
		}
	}
	return false;
}

static bool decide_mst_link_settings(const struct dc_link *link, struct dc_link_settings *link_setting)
{
	*link_setting = link->verified_link_cap;
@@ -3380,7 +3522,25 @@ void decide_link_settings(struct dc_stream_state *stream,
		if (decide_mst_link_settings(link, link_setting))
			return;
	} else if (link->connector_signal == SIGNAL_TYPE_EDP) {
		if (decide_edp_link_settings(link, link_setting, req_bw))
		/* enable edp link optimization for DSC eDP case */
		if (stream->timing.flags.DSC) {
			enum dc_link_rate max_link_rate = LINK_RATE_UNKNOWN;

			if (link->ctx->dc->debug.force_dsc_edp_policy) {
				/* calculate link max link rate cap*/
				struct dc_link_settings tmp_link_setting;
				struct dc_crtc_timing tmp_timing = stream->timing;
				uint32_t orig_req_bw;

				tmp_link_setting.link_rate = LINK_RATE_UNKNOWN;
				tmp_timing.flags.DSC = 0;
				orig_req_bw = dc_bandwidth_in_kbps_from_timing(&tmp_timing);
				decide_edp_link_settings(link, &tmp_link_setting, orig_req_bw);
				max_link_rate = tmp_link_setting.link_rate;
			}
			if (decide_edp_link_settings_with_dsc(link, link_setting, req_bw, max_link_rate))
				return;
		} else if (decide_edp_link_settings(link, link_setting, req_bw))
			return;
	} else if (decide_dp_link_settings(link, link_setting, req_bw))
		return;
+3 −0
Original line number Diff line number Diff line
@@ -188,6 +188,7 @@ struct dc_caps {
#if defined(CONFIG_DRM_AMD_DC_DCN)
	bool dp_hpo;
#endif
	bool edp_dsc_support;
	bool vbios_lttpr_aware;
	bool vbios_lttpr_enable;
};
@@ -667,6 +668,8 @@ struct dc_debug_options {
	bool validate_dml_output;
	bool enable_dmcub_surface_flip;
	bool usbc_combo_phy_reset_wa;
	bool disable_dsc_edp;
	unsigned int  force_dsc_edp_policy;
	bool enable_dram_clock_change_one_display_vactive;
#if defined(CONFIG_DRM_AMD_DC_DCN)
	/* TODO - remove once tested */
Loading