diff mbox

[09/11] drm/i915: handle csc for ycbcr HDMI output

Message ID 1491583168-20042-10-git-send-email-shashank.sharma@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sharma, Shashank April 7, 2017, 4:39 p.m. UTC
To support ycbcr HDMI output, we need a pipe CSC block to
do the RGB->YCBCR conversion, as the blender output is in RGB.

Current Intel platforms have only one pipe CSC unit, so
we can either do color correction using it, or we can perform
RGB->YCBCR conversion.

This function adds a csc handler, to perform RGB->YCBCR conversion
as per recommended spec values.

Cc: Ville Syrjala <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Ander Conselvan De Oliveira <ander.conselvan.de.oliveira@intel.com>
Signed-off-by: Shashank Sharma <shashank.sharma@intel.com>
---
 drivers/gpu/drm/i915/intel_color.c   | 49 +++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_display.c | 32 +++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_color.c b/drivers/gpu/drm/i915/intel_color.c
index 306c6b0..e677996 100644
--- a/drivers/gpu/drm/i915/intel_color.c
+++ b/drivers/gpu/drm/i915/intel_color.c
@@ -41,6 +41,21 @@ 
 
 #define LEGACY_LUT_LENGTH		(sizeof(struct drm_color_lut) * 256)
 
+/* Post offset values for RGB->YCBCR conversion */
+#define POSTOFF_RGB_TO_YUV_HI 0x800
+#define POSTOFF_RGB_TO_YUV_ME 0x100
+#define POSTOFF_RGB_TO_YUV_LO 0x800
+
+/* Direct spec values for RGB->YUV conversion matrix */
+#define CSC_RGB_TO_YUV_RU_GU 0x2D980B70
+#define CSC_RGB_TO_YUV_BU 0x39400000
+
+#define CSC_RGB_TO_YUV_RY_GY 0xBEA89C58
+#define CSC_RGB_TO_YUV_BY 0x08000000
+
+#define CSC_RGB_TO_YUV_RV_GV 0x08009E88
+#define CSC_RGB_TO_YUV_BV 0xB25E0000
+
 /*
  * Extract the CSC coefficient from a CTM coefficient (in U32.32 fixed point
  * format). This macro takes the coefficient we want transformed and the
@@ -91,6 +106,35 @@  static void ctm_mult_by_limited(uint64_t *result, int64_t *input)
 	}
 }
 
+void i9xx_load_ycbcr_conversion_matrix(struct intel_crtc *intel_crtc)
+{
+	int pipe = intel_crtc->pipe;
+	struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev);
+
+	/* We don't use high values for conversion */
+	I915_WRITE(PIPE_CSC_PREOFF_HI(pipe), 0);
+	I915_WRITE(PIPE_CSC_PREOFF_ME(pipe), 0);
+	I915_WRITE(PIPE_CSC_PREOFF_LO(pipe), 0);
+
+	/* Program direct spec values for RGB to YCBCR conversion matrix */
+	I915_WRITE(PIPE_CSC_COEFF_RU_GU(pipe), CSC_RGB_TO_YUV_RU_GU);
+	I915_WRITE(PIPE_CSC_COEFF_BU(pipe), CSC_RGB_TO_YUV_BU);
+
+	I915_WRITE(PIPE_CSC_COEFF_RY_GY(pipe), CSC_RGB_TO_YUV_RY_GY);
+	I915_WRITE(PIPE_CSC_COEFF_BY(pipe), CSC_RGB_TO_YUV_BY);
+
+	I915_WRITE(PIPE_CSC_COEFF_RV_GV(pipe), CSC_RGB_TO_YUV_RV_GV);
+	I915_WRITE(PIPE_CSC_COEFF_BV(pipe), CSC_RGB_TO_YUV_BV);
+
+	/* Spec postoffset values */
+	I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), POSTOFF_RGB_TO_YUV_HI);
+	I915_WRITE(PIPE_CSC_POSTOFF_ME(pipe), POSTOFF_RGB_TO_YUV_ME);
+	I915_WRITE(PIPE_CSC_POSTOFF_LO(pipe), POSTOFF_RGB_TO_YUV_LO);
+
+	/* CSC mode before gamma */
+	I915_WRITE(PIPE_CSC_MODE(pipe), 0);
+}
+
 /* Set up the pipe CSC unit. */
 static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
 {
@@ -101,7 +145,10 @@  static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
 	uint16_t coeffs[9] = { 0, };
 	struct intel_crtc_state *intel_crtc_state = to_intel_crtc_state(crtc_state);
 
-	if (crtc_state->ctm) {
+	if (intel_crtc_state->hdmi_output) {
+		i9xx_load_ycbcr_conversion_matrix(intel_crtc);
+		return;
+	} else if (crtc_state->ctm) {
 		struct drm_color_ctm *ctm =
 			(struct drm_color_ctm *)crtc_state->ctm->data;
 		uint64_t input[9] = { 0, };
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index fc43a28..b03e5f3 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6206,6 +6206,29 @@  static void intel_crtc_compute_pixel_rate(struct intel_crtc_state *crtc_state)
 			ilk_pipe_pixel_rate(crtc_state);
 }
 
+static int intel_crtc_ycbcr_config(struct intel_crtc_state *state)
+{
+	struct drm_crtc_state *drm_state = &state->base;
+	struct drm_i915_private *dev_priv = to_i915(drm_state->crtc->dev);
+
+	/* YCBCR420 is supported only in HDMI 2.0 controllers */
+	if ((state->hdmi_output == DRM_HDMI_OUTPUT_YCBCR420) &&
+		!IS_GEMINILAKE(dev_priv)) {
+		DRM_ERROR("YCBCR420 output is not supported\n");
+		return -EINVAL;
+	}
+
+	/* We need CSC for output conversion from RGB->YCBCR */
+	if (drm_state->ctm) {
+		DRM_ERROR("YCBCR output and CTM is not possible together\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG_DRIVER("Output %s can be supported\n",
+			 drm_get_hdmi_output_name(state->hdmi_output));
+	return 0;
+}
+
 static int intel_crtc_compute_config(struct intel_crtc *crtc,
 				     struct intel_crtc_state *pipe_config)
 {
@@ -6235,6 +6258,14 @@  static int intel_crtc_compute_config(struct intel_crtc *crtc,
 		return -EINVAL;
 	}
 
+	/* YCBCR output check */
+	if (pipe_config->hdmi_output) {
+		if (intel_crtc_ycbcr_config(pipe_config)) {
+			DRM_ERROR("Cant enable HDMI YCBCR output\n");
+			return -EINVAL;
+		}
+	}
+
 	/*
 	 * Pipe horizontal size must be even in:
 	 * - DVO ganged mode
@@ -11392,6 +11423,7 @@  intel_modeset_pipe_config(struct drm_crtc *crtc,
 			DRM_DEBUG_KMS("Encoder config failure\n");
 			goto fail;
 		}
+
 	}
 
 	/* Set default port clock if not overwritten by the encoder. Needs to be