[PATCH 08/11] drm/i915: handle ycbcr outputs

Shashank Sharma shashank.sharma at intel.com
Fri Apr 7 16:39:25 UTC 2017


This patch adds support for HDMI ycbcr outputs in i915 layer.
HDMI output mode is a connector property, this patch checks if
source and sink can support the HDMI output type selected by user.
And if they both can, it commits the hdmi output type into crtc state,
for further staging in driver.

Cc: Ville Syrjala <ville.syrjala at linux.intel.com>
Cc: Daniel Vetter <daniel.vetter at intel.com>
Cc: Ander Conselvan De Oliveira <ander.conselvan.de.oliveira at intel.com>
Signed-off-by: Shashank Sharma <shashank.sharma at intel.com>
---
 drivers/gpu/drm/i915/intel_display.c |   1 +
 drivers/gpu/drm/i915/intel_drv.h     |   3 +
 drivers/gpu/drm/i915/intel_hdmi.c    | 161 ++++++++++++++++++++++++++++++++++-
 3 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b6b40cd..fc43a28 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11705,6 +11705,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv,
 	PIPE_CONF_CHECK_I(hdmi_scrambling);
 	PIPE_CONF_CHECK_I(hdmi_high_tmds_clock_ratio);
 	PIPE_CONF_CHECK_I(has_infoframe);
+	PIPE_CONF_CHECK_I(hdmi_output);
 
 	PIPE_CONF_CHECK_I(has_audio);
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 43ea748..98902d4 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -738,6 +738,9 @@ struct intel_crtc_state {
 
 	/* HDMI High TMDS char rate ratio */
 	bool hdmi_high_tmds_clock_ratio;
+
+	/* HDMI output type */
+	enum drm_hdmi_output_type hdmi_output;
 };
 
 struct intel_crtc {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 76d9e0d..40d3414 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1301,7 +1301,8 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
 	return status;
 }
 
-static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)
+static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state,
+				enum drm_hdmi_output_type hdmi_out)
 {
 	struct drm_i915_private *dev_priv =
 		to_i915(crtc_state->base.crtc->dev);
@@ -1313,6 +1314,16 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)
 	if (HAS_GMCH_DISPLAY(dev_priv))
 		return false;
 
+	if (hdmi_out == DRM_HDMI_OUTPUT_YCBCR422) {
+		/*
+		 * HDMI spec says YCBCR422 is 12bpc, but its not a deep
+		 * color format. So respect the spec, and not allow this
+		 * to be deep color
+		 */
+		DRM_DEBUG_KMS("Not allowing deep color for YCBCR422 output\n");
+		return false;
+	}
+
 	/*
 	 * HDMI 12bpc affects the clocks, so it's only possible
 	 * when not cloning with other encoder types.
@@ -1333,6 +1344,136 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)
 	return true;
 }
 
+static inline
+bool _check_ycbcr_mode_support(struct drm_display_mode *mode)
+{
+	return mode->flags & DRM_MODE_FLAG_420_MASK;
+}
+
+static inline
+bool _check_ycbcr_sink_support(struct drm_display_info *display,
+			       enum drm_hdmi_output_type type)
+{
+	return display->color_formats & type;
+}
+
+static inline enum drm_hdmi_output_type
+_get_highest_quality_ycbcr_supported(struct drm_display_info *display,
+			   struct drm_display_mode *mode)
+{
+	/*
+	 * Get the ycbcr output with the highest possible subsampling rate.
+	 * Preference should go as:
+	 * ycbcr 444
+	 * ycbcr 422
+	 * ycbcr 420
+	 */
+	if (display->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+		return DRM_HDMI_OUTPUT_YCBCR444;
+	else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+		return DRM_HDMI_OUTPUT_YCBCR422;
+	else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB420 &&
+			_check_ycbcr_mode_support(mode))
+		return DRM_HDMI_OUTPUT_YCBCR420;
+	else {
+		DRM_ERROR("Sink does't support any YCBCR output\n");
+		return DRM_HDMI_OUTPUT_INVALID;
+	}
+}
+
+static inline enum drm_hdmi_output_type
+_get_lowest_quality_ycbcr_supported(struct drm_display_info *display,
+			  struct drm_display_mode *mode)
+{
+	/*
+	 * Get the ycbcr output with the lowest possible subsampling rate.
+	 * Preference should go as:
+	 * ycbcr 420
+	 * ycbcr 422
+	 * ycbcr 444
+	 */
+	if (display->color_formats & DRM_COLOR_FORMAT_YCRCB420 &&
+		_check_ycbcr_mode_support(mode))
+		return DRM_HDMI_OUTPUT_YCBCR420;
+	else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+		return DRM_HDMI_OUTPUT_YCBCR422;
+	else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+		return DRM_HDMI_OUTPUT_YCBCR420;
+	else {
+		DRM_ERROR("Sink does't support any YCBCR output\n");
+		return DRM_HDMI_OUTPUT_INVALID;
+	}
+}
+
+static inline enum drm_hdmi_output_type
+intel_hdmi_compute_ycbcr_config(struct drm_connector_state *conn_state,
+			       struct intel_crtc_state *config,
+			       int *clock_12bpc, int *clock_8bpc)
+{
+	struct drm_connector *connector = conn_state->connector;
+	struct drm_display_info *info = &connector->display_info;
+	struct drm_display_mode *mode = &config->base.adjusted_mode;
+	enum drm_hdmi_output_type type = conn_state->hdmi_output;
+
+	if (type == DRM_HDMI_OUTPUT_YCBCR_HQ) {
+		type = _get_highest_quality_ycbcr_supported(info, mode);
+		if (type == DRM_HDMI_OUTPUT_INVALID) {
+			DRM_ERROR("Can't support mode %s in YCBCR format\n",
+				  mode->name);
+			return DRM_HDMI_OUTPUT_INVALID;
+		}
+	} else if (type == DRM_HDMI_OUTPUT_YCBCR_LQ) {
+		type = _get_lowest_quality_ycbcr_supported(info, mode);
+		if (type == DRM_HDMI_OUTPUT_INVALID) {
+			DRM_ERROR("Can't support mode %s in YCBCR format\n",
+				  mode->name);
+			return DRM_HDMI_OUTPUT_INVALID;
+		}
+	} else if (!_check_ycbcr_sink_support(info, type)) {
+		DRM_ERROR("Sink doesn't support output mode %s\n",
+			  drm_get_hdmi_output_name(type));
+		return DRM_HDMI_OUTPUT_INVALID;
+	}
+
+	/* Now deal with the preferred and supported output */
+	switch (type) {
+	case DRM_HDMI_OUTPUT_YCBCR420:
+		/*
+		 * Sinks can support ycbcr420 output on a mode-by-mode
+		 * basis, so check if this mode is supported as 420 output
+		 * mode.
+		 */
+		if (!_check_ycbcr_mode_support(mode)) {
+			DRM_ERROR("Sink cant support %s output with %s mode\n",
+				  drm_get_hdmi_output_name(type),
+				  mode->name);
+			return DRM_HDMI_OUTPUT_INVALID;
+		}
+
+		/* ycbcr 420 TMDS rate requirement is half the pixel clock */
+		config->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+		config->port_clock /= 2;
+		*clock_12bpc /= 2;
+		*clock_8bpc /= 2;
+		break;
+
+	case DRM_HDMI_OUTPUT_YCBCR422:
+	case DRM_HDMI_OUTPUT_YCBCR444:
+		break;
+
+	case DRM_HDMI_OUTPUT_INVALID:
+	default:
+		DRM_ERROR("Invalid YCBCR output\n");
+		return DRM_HDMI_OUTPUT_INVALID;
+	}
+
+	/* Encoder is capable of this output, lets commit to CRTC */
+	config->hdmi_output = type;
+	DRM_DEBUG_KMS("HDMI output type is %s\n",
+		      drm_get_hdmi_output_name(type));
+	return type;
+}
+
 bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 			       struct intel_crtc_state *pipe_config,
 			       struct drm_connector_state *conn_state)
@@ -1340,7 +1481,10 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 	struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
-	struct drm_scdc *scdc = &conn_state->connector->display_info.hdmi.scdc;
+	struct drm_connector *connector = conn_state->connector;
+	struct drm_display_info *info = &connector->display_info;
+	struct drm_scdc *scdc = &info->hdmi.scdc;
+	enum drm_hdmi_output_type hdmi_out = conn_state->hdmi_output;
 	int clock_8bpc = pipe_config->base.adjusted_mode.crtc_clock;
 	int clock_12bpc = clock_8bpc * 3 / 2;
 	int desired_bpp;
@@ -1367,6 +1511,17 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 		clock_12bpc *= 2;
 	}
 
+	/* YCBCR HDMI output */
+	if (hdmi_out > DRM_HDMI_OUTPUT_DEFAULT_RGB) {
+		hdmi_out = intel_hdmi_compute_ycbcr_config(conn_state,
+						   pipe_config, &clock_12bpc,
+						   &clock_8bpc);
+		if (hdmi_out == DRM_HDMI_OUTPUT_INVALID) {
+			DRM_ERROR("Can't support desired HDMI output\n");
+			return false;
+		}
+	}
+
 	if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv))
 		pipe_config->has_pch_encoder = true;
 
@@ -1381,7 +1536,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 	 */
 	if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
 	    hdmi_port_clock_valid(intel_hdmi, clock_12bpc, true) == MODE_OK &&
-	    hdmi_12bpc_possible(pipe_config)) {
+	    hdmi_12bpc_possible(pipe_config, hdmi_out)) {
 		DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
 		desired_bpp = 12*3;
 
-- 
2.7.4



More information about the dri-devel mailing list