Commit 68910c2a authored by Ankit Nautiyal's avatar Ankit Nautiyal Committed by Ville Syrjälä
Browse files

drm/i915/dp: Replace intel_dp.dfp members with the new crtc_state sink_format



The decision to use DFP output format conversion capabilities should be
during compute_config phase.

This patch uses the members of intel_dp->dfp to only store the
format conversion capabilities of the DP device and uses the crtc_state
sink_format member, to program the protocol-converter for
colorspace/format conversion.

v2: Use sink_format to determine the color conversion config for the
pcon (Ville).

v3: Fix typo: missing 'break' in switch case (lkp kernel test robot).

v4: Add helper to check if DP supports YCBCR420.

v5: Simplify logic for computing output_format, based on the given
sink_format. (Ville).
Added scaler constraint for YCbCr420 output.

v6: Split the patch for Scaler constraint for Ycbcr420.

v7: Simplify the policy for selecting output_format:
Always try for RGB first, followed by YCBCR444, and finally by YCBCR420.

v8: Removed redundant comments, minor refactoring. (Ville)

v9: Added member for ycbcr420 passthrough cap, fixed minor issues. (Ville)

Signed-off-by: default avatarAnkit Nautiyal <ankit.k.nautiyal@intel.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230427125605.487769-3-ankit.k.nautiyal@intel.com
parent a04d27cd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1775,6 +1775,7 @@ struct intel_dp {
		int pcon_max_frl_bw;
		u8 max_bpc;
		bool ycbcr_444_to_420;
		bool ycbcr420_passthrough;
		bool rgb_to_ycbcr;
	} dfp;

+118 −53
Original line number Diff line number Diff line
@@ -849,27 +849,88 @@ u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp,
	return 0;
}

static bool source_can_output(struct intel_dp *intel_dp,
			      enum intel_output_format format)
{
	struct drm_i915_private *i915 = dp_to_i915(intel_dp);

	switch (format) {
	case INTEL_OUTPUT_FORMAT_RGB:
		return true;

	case INTEL_OUTPUT_FORMAT_YCBCR444:
		/*
		 * No YCbCr output support on gmch platforms.
		 * Also, ILK doesn't seem capable of DP YCbCr output.
		 * The displayed image is severly corrupted. SNB+ is fine.
		 */
		return !HAS_GMCH(i915) && !IS_IRONLAKE(i915);

	case INTEL_OUTPUT_FORMAT_YCBCR420:
		/* Platform < Gen 11 cannot output YCbCr420 format */
		return DISPLAY_VER(i915) >= 11;

	default:
		MISSING_CASE(format);
		return false;
	}
}

static bool
dfp_can_convert_from_rgb(struct intel_dp *intel_dp,
			 enum intel_output_format sink_format)
{
	if (!drm_dp_is_branch(intel_dp->dpcd))
		return false;

	if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR444)
		return intel_dp->dfp.rgb_to_ycbcr;

	if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420)
		return intel_dp->dfp.rgb_to_ycbcr &&
			intel_dp->dfp.ycbcr_444_to_420;

	return false;
}

static bool
dfp_can_convert_from_ycbcr444(struct intel_dp *intel_dp,
			      enum intel_output_format sink_format)
{
	if (!drm_dp_is_branch(intel_dp->dpcd))
		return false;

	if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420)
		return intel_dp->dfp.ycbcr_444_to_420;

	return false;
}

static enum intel_output_format
intel_dp_output_format(struct intel_connector *connector,
		       enum intel_output_format sink_format)
{
	struct intel_dp *intel_dp = intel_attached_dp(connector);
	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
	enum intel_output_format output_format;

	if (intel_dp->force_dsc_output_format)
		return intel_dp->force_dsc_output_format;

	if (!connector->base.ycbcr_420_allowed ||
	    sink_format != INTEL_OUTPUT_FORMAT_YCBCR420)
		return INTEL_OUTPUT_FORMAT_RGB;
	if (sink_format == INTEL_OUTPUT_FORMAT_RGB ||
	    dfp_can_convert_from_rgb(intel_dp, sink_format))
		output_format = INTEL_OUTPUT_FORMAT_RGB;

	if (intel_dp->dfp.rgb_to_ycbcr &&
	    intel_dp->dfp.ycbcr_444_to_420)
		return INTEL_OUTPUT_FORMAT_RGB;
	else if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR444 ||
		 dfp_can_convert_from_ycbcr444(intel_dp, sink_format))
		output_format = INTEL_OUTPUT_FORMAT_YCBCR444;

	if (intel_dp->dfp.ycbcr_444_to_420)
		return INTEL_OUTPUT_FORMAT_YCBCR444;
	else
		return INTEL_OUTPUT_FORMAT_YCBCR420;
		output_format = INTEL_OUTPUT_FORMAT_YCBCR420;

	drm_WARN_ON(&i915->drm, !source_can_output(intel_dp, output_format));

	return output_format;
}

int intel_dp_min_bpp(enum intel_output_format output_format)
@@ -2829,6 +2890,8 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
					   const struct intel_crtc_state *crtc_state)
{
	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
	bool ycbcr444_to_420 = false;
	bool rgb_to_ycbcr = false;
	u8 tmp;

	if (intel_dp->dpcd[DP_DPCD_REV] < 0x13)
@@ -2845,8 +2908,24 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
		drm_dbg_kms(&i915->drm, "Failed to %s protocol converter HDMI mode\n",
			    str_enable_disable(intel_dp->has_hdmi_sink));

	tmp = crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 &&
		intel_dp->dfp.ycbcr_444_to_420 ? DP_CONVERSION_TO_YCBCR420_ENABLE : 0;
	if (crtc_state->sink_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
		switch (crtc_state->output_format) {
		case INTEL_OUTPUT_FORMAT_YCBCR420:
			break;
		case INTEL_OUTPUT_FORMAT_YCBCR444:
			ycbcr444_to_420 = true;
			break;
		case INTEL_OUTPUT_FORMAT_RGB:
			rgb_to_ycbcr = true;
			ycbcr444_to_420 = true;
			break;
		default:
			MISSING_CASE(crtc_state->output_format);
			break;
		}
	}

	tmp = ycbcr444_to_420 ? DP_CONVERSION_TO_YCBCR420_ENABLE : 0;

	if (drm_dp_dpcd_writeb(&intel_dp->aux,
			       DP_PROTOCOL_CONVERTER_CONTROL_1, tmp) != 1)
@@ -2854,8 +2933,7 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
			    "Failed to %s protocol converter YCbCr 4:2:0 conversion mode\n",
			    str_enable_disable(intel_dp->dfp.ycbcr_444_to_420));

	tmp = intel_dp->dfp.rgb_to_ycbcr ?
		DP_CONVERSION_BT709_RGB_YCBCR_ENABLE : 0;
	tmp = rgb_to_ycbcr ? DP_CONVERSION_BT709_RGB_YCBCR_ENABLE : 0;

	if (drm_dp_pcon_convert_rgb_to_ycbcr(&intel_dp->aux, tmp) < 0)
		drm_dbg_kms(&i915->drm,
@@ -4650,57 +4728,44 @@ intel_dp_update_dfp(struct intel_dp *intel_dp,
	intel_dp_get_pcon_dsc_cap(intel_dp);
}

static bool
intel_dp_can_ycbcr420(struct intel_dp *intel_dp)
{
	if (source_can_output(intel_dp, INTEL_OUTPUT_FORMAT_YCBCR420) &&
	    (!drm_dp_is_branch(intel_dp->dpcd) || intel_dp->dfp.ycbcr420_passthrough))
		return true;

	if (source_can_output(intel_dp, INTEL_OUTPUT_FORMAT_RGB) &&
	    dfp_can_convert_from_rgb(intel_dp, INTEL_OUTPUT_FORMAT_YCBCR420))
		return true;

	if (source_can_output(intel_dp, INTEL_OUTPUT_FORMAT_YCBCR444) &&
	    dfp_can_convert_from_ycbcr444(intel_dp, INTEL_OUTPUT_FORMAT_YCBCR420))
		return true;

	return false;
}

static void
intel_dp_update_420(struct intel_dp *intel_dp)
{
	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
	struct intel_connector *connector = intel_dp->attached_connector;
	bool is_branch, ycbcr_420_passthrough, ycbcr_444_to_420, rgb_to_ycbcr;

	/* No YCbCr output support on gmch platforms */
	if (HAS_GMCH(i915))
		return;

	/*
	 * ILK doesn't seem capable of DP YCbCr output. The
	 * displayed image is severly corrupted. SNB+ is fine.
	 */
	if (IS_IRONLAKE(i915))
		return;

	is_branch = drm_dp_is_branch(intel_dp->dpcd);
	ycbcr_420_passthrough =
	intel_dp->dfp.ycbcr420_passthrough =
		drm_dp_downstream_420_passthrough(intel_dp->dpcd,
						  intel_dp->downstream_ports);
	/* on-board LSPCON always assumed to support 4:4:4->4:2:0 conversion */
	ycbcr_444_to_420 =
	intel_dp->dfp.ycbcr_444_to_420 =
		dp_to_dig_port(intel_dp)->lspcon.active ||
		drm_dp_downstream_444_to_420_conversion(intel_dp->dpcd,
							intel_dp->downstream_ports);
	rgb_to_ycbcr = drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd,
	intel_dp->dfp.rgb_to_ycbcr =
		drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd,
							  intel_dp->downstream_ports,
							  DP_DS_HDMI_BT709_RGB_YCBCR_CONV);

	if (DISPLAY_VER(i915) >= 11) {
		/* Let PCON convert from RGB->YCbCr if possible */
		if (is_branch && rgb_to_ycbcr && ycbcr_444_to_420) {
			intel_dp->dfp.rgb_to_ycbcr = true;
			intel_dp->dfp.ycbcr_444_to_420 = true;
			connector->base.ycbcr_420_allowed = true;
		} else {
		/* Prefer 4:2:0 passthrough over 4:4:4->4:2:0 conversion */
			intel_dp->dfp.ycbcr_444_to_420 =
				ycbcr_444_to_420 && !ycbcr_420_passthrough;

			connector->base.ycbcr_420_allowed =
				!is_branch || ycbcr_444_to_420 || ycbcr_420_passthrough;
		}
	} else {
		/* 4:4:4->4:2:0 conversion is the only way */
		intel_dp->dfp.ycbcr_444_to_420 = ycbcr_444_to_420;

		connector->base.ycbcr_420_allowed = ycbcr_444_to_420;
	}
	connector->base.ycbcr_420_allowed = intel_dp_can_ycbcr420(intel_dp);

	drm_dbg_kms(&i915->drm,
		    "[CONNECTOR:%d:%s] RGB->YcbCr conversion? %s, YCbCr 4:2:0 allowed? %s, YCbCr 4:4:4->4:2:0 conversion? %s\n",