[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#607529: linux-image-2.6.32-5-amd64: HDMI uses wrong signal for TVs with no HDMI sound support



Hi Paul,

Paul Sohier wrote:

> When a TV without sound support via HDMI is used with HDMI all
> output is green instead of blue. This happens with a LG Flatron
> MD2061D. You get a result like this:
> http://www.hosthuis.nl/IMAG0401.jpg
> 
> A patch can be found here (From freedesktop.org), however this patch
> does not cleanly apply to the Debian kernel:
> http://lists.freedesktop.org/archives/intel-gfx/2010-October/008321.html
> Using this change the color on the TV works correctly.

Do the attached patches work?

Alternatively, if you get a chance to test the kernel from sid or
experimental, that would be useful.

Thanks,
Jonathan
From: Adam Jackson <ajax@redhat.com>
Date: Mon, 29 Mar 2010 21:43:26 +0000
Subject: drm/edid: Add test for monitor reduced blanking support.

commit d1ff6409b1cd2cd2a65df415648fa38b9fdf4cd8 upstream.

The generic block walk callback looks like overkill, but we'll need it
for other detailed block walks in the future.

Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/drm_edid.c |   47 ++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 1097dece..424180c5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -32,6 +32,10 @@
 #include "drmP.h"
 #include "drm_edid.h"
 
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
 /*
  * TODO:
  *   - support EDID 1.4 (incl. CE blocks)
@@ -505,6 +509,45 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
 	return mode;
 }
 
+typedef void detailed_cb(struct detailed_timing *timing, void *closure);
+
+static void
+drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
+{
+	int i;
+	struct edid *edid = (struct edid *)raw_edid;
+
+	if (edid == NULL)
+		return;
+
+	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
+		cb(&(edid->detailed_timings[i]), closure);
+
+	/* XXX extension block walk */
+}
+
+static void
+is_rb(struct detailed_timing *t, void *data)
+{
+	u8 *r = (u8 *)t;
+	if (r[3] == EDID_DETAIL_MONITOR_RANGE)
+		if (r[15] & 0x10)
+			*(bool *)data = true;
+}
+
+/* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
+static bool
+drm_monitor_supports_rb(struct edid *edid)
+{
+	if (edid->revision >= 4) {
+		bool ret;
+		drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
+		return ret;
+	}
+
+	return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
+}
+
 /*
  * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
  * monitors fill with ascii space (0x20) instead.
@@ -748,10 +791,6 @@ static struct drm_display_mode edid_est_modes[] = {
 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
 };
 
-#define EDID_EST_TIMINGS 16
-#define EDID_STD_TIMINGS 8
-#define EDID_DETAILED_TIMINGS 4
-
 /**
  * add_established_modes - get est. modes from EDID and add them
  * @edid: EDID block to scan
-- 
1.7.6

From: Adam Jackson <ajax@redhat.com>
Date: Tue, 3 Aug 2010 14:38:17 -0400
Subject: drm/edid: Add detailed block walk for CEA extensions

commit 4d76a22134d5a5ad259fd667f3d2b215b7e71d10 upstream.

Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/drm_edid.c |   38 +++++++++++++++++++++++++++++++++++++-
 include/drm/drm_edid.h     |    6 ++++++
 2 files changed, 43 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 424180c5..e41ee948 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -512,6 +512,33 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
 typedef void detailed_cb(struct detailed_timing *timing, void *closure);
 
 static void
+cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+	int i, n = 0;
+	u8 rev = ext[0x01], d = ext[0x02];
+	u8 *det_base = ext + d;
+
+	switch (rev) {
+	case 0:
+		/* can't happen */
+		return;
+	case 1:
+		/* have to infer how many blocks we have, check pixel clock */
+		for (i = 0; i < 6; i++)
+			if (det_base[18*i] || det_base[18*i+1])
+				n++;
+		break;
+	default:
+		/* explicit count */
+		n = min(ext[0x03] & 0x0f, 6);
+		break;
+	}
+
+	for (i = 0; i < n; i++)
+		cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
 drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
 {
 	int i;
@@ -523,7 +550,16 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
 	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
 		cb(&(edid->detailed_timings[i]), closure);
 
-	/* XXX extension block walk */
+	for (i = 1; i <= raw_edid[0x7e]; i++) {
+		u8 *ext = raw_edid + (i * EDID_LENGTH);
+		switch (*ext) {
+		case CEA_EXT:
+			cea_for_each_detailed_block(ext, cb, closure);
+			break;
+		default:
+			break;
+		}
+	}
 }
 
 static void
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 7d6c9a2d..97336266 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -28,6 +28,12 @@
 #define EDID_LENGTH 128
 #define DDC_ADDR 0x50
 
+#define CEA_EXT	    0x02
+#define VTB_EXT	    0x10
+#define DI_EXT	    0x40
+#define LS_EXT	    0x50
+#define MI_EXT	    0x60
+
 struct est_timings {
 	u8 t1;
 	u8 t2;
-- 
1.7.6

From: Zhenyu Wang <zhenyuw@linux.intel.com>
Date: Sun, 19 Sep 2010 14:27:28 +0800
Subject: drm/edid: add helper function to detect monitor audio capability

commit 8fe9790d1652e7c306c862ea102a5e6126b412e1 upstream.

To help to determine if digital display port needs to enable
audio output or not. This one adds a helper to get monitor's
audio capability via EDID CEA extension block.

Tested-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/drm_edid.c |  102 +++++++++++++++++++++++++++++++++++--------
 include/drm/drm_crtc.h     |    1 +
 2 files changed, 84 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index e41ee948..9bcc31f2 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1171,7 +1171,39 @@ end:
 EXPORT_SYMBOL(drm_get_edid);
 
 #define HDMI_IDENTIFIER 0x000C03
+#define AUDIO_BLOCK	0x01
 #define VENDOR_BLOCK    0x03
+#define EDID_BASIC_AUDIO	(1 << 6)
+
+/**
+ * Search EDID for CEA extension block.
+ */
+static u8 *drm_find_cea_extension(struct edid *edid)
+{
+	u8 *edid_ext = NULL;
+	int i, edid_ext_num;
+
+	/* No EDID or EDID extensions */
+	if (edid == NULL || edid->extensions == 0)
+		return NULL;
+
+	/* Chose real EDID extension number */
+	edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
+		       MAX_EDID_EXT_NUM : edid->extensions;
+
+	/* Find CEA extension */
+	for (i = 0; i < edid_ext_num; i++) {
+		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+		if (edid_ext[0] == CEA_EXT)
+			break;
+	}
+
+	if (i == edid_ext_num)
+		return NULL;
+
+	return edid_ext;
+}
+
 /**
  * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
  * @edid: monitor EDID information
@@ -1181,28 +1213,13 @@ EXPORT_SYMBOL(drm_get_edid);
  */
 bool drm_detect_hdmi_monitor(struct edid *edid)
 {
-	char *edid_ext = NULL;
-	int i, hdmi_id, edid_ext_num;
+	u8 *edid_ext;
+	int i, hdmi_id;
 	int start_offset, end_offset;
 	bool is_hdmi = false;
 
-	/* No EDID or EDID extensions */
-	if (edid == NULL || edid->extensions == 0)
-		goto end;
-
-	/* Chose real EDID extension number */
-	edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
-		       MAX_EDID_EXT_NUM : edid->extensions;
-
-	/* Find CEA extension */
-	for (i = 0; i < edid_ext_num; i++) {
-		edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
-		/* This block is CEA extension */
-		if (edid_ext[0] == 0x02)
-			break;
-	}
-
-	if (i == edid_ext_num)
+	edid_ext = drm_find_cea_extension(edid);
+	if (!edid_ext)
 		goto end;
 
 	/* Data block offset in CEA extension block */
@@ -1233,6 +1250,53 @@ end:
 EXPORT_SYMBOL(drm_detect_hdmi_monitor);
 
 /**
+ * drm_detect_monitor_audio - check monitor audio capability
+ *
+ * Monitor should have CEA extension block.
+ * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
+ * audio' only. If there is any audio extension block and supported
+ * audio format, assume at least 'basic audio' support, even if 'basic
+ * audio' is not defined in EDID.
+ *
+ */
+bool drm_detect_monitor_audio(struct edid *edid)
+{
+	u8 *edid_ext;
+	int i, j;
+	bool has_audio = false;
+	int start_offset, end_offset;
+
+	edid_ext = drm_find_cea_extension(edid);
+	if (!edid_ext)
+		goto end;
+
+	has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+
+	if (has_audio) {
+		DRM_DEBUG_KMS("Monitor has basic audio support\n");
+		goto end;
+	}
+
+	/* Data block offset in CEA extension block */
+	start_offset = 4;
+	end_offset = edid_ext[2];
+
+	for (i = start_offset; i < end_offset;
+			i += ((edid_ext[i] & 0x1f) + 1)) {
+		if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
+			has_audio = true;
+			for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
+				DRM_DEBUG_KMS("CEA audio format %d\n",
+					      (edid_ext[i + j] >> 3) & 0xf);
+			goto end;
+		}
+	}
+end:
+	return has_audio;
+}
+EXPORT_SYMBOL(drm_detect_monitor_audio);
+
+/**
  * drm_add_edid_modes - add modes from EDID data, if available
  * @connector: connector we're probing
  * @edid: edid data
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b69347b8..a7a17907 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -756,6 +756,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 				    void *data, struct drm_file *file_priv);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
+extern bool drm_detect_monitor_audio(struct edid *edid);
 extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
 				int hdisplay, int vdisplay, int vrefresh,
 				bool reduced, bool interlaced, bool margins);
-- 
1.7.6

From: Zhenyu Wang <zhenyuw@linux.intel.com>
Date: Fri, 10 Sep 2010 10:39:40 +0800
Subject: drm/i915: Enable HDMI audio for monitor with audio support

commit 2e3d6006aca163db3eeb931cec631974aaa3c293 upstream

Rely on monitor's audio capability to turn on audio output for HDMI.

Tested-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/i915/intel_hdmi.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 85760bf7..41942c2a 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -40,6 +40,7 @@ struct intel_hdmi_priv {
 	u32 sdvox_reg;
 	u32 save_SDVOX;
 	bool has_hdmi_sink;
+	bool has_audio;
 };
 
 static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -59,7 +60,11 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
 		SDVO_VSYNC_ACTIVE_HIGH |
 		SDVO_HSYNC_ACTIVE_HIGH;
 
-	if (hdmi_priv->has_hdmi_sink)
+	/* Required on CPT */
+	if (hdmi_priv->has_hdmi_sink && HAS_PCH_CPT(dev))
+		sdvox |= HDMI_MODE_SELECT;
+
+	if (hdmi_priv->has_audio)
 		sdvox |= SDVO_AUDIO_ENABLE;
 
 	if (intel_crtc->pipe == 1)
@@ -156,6 +161,7 @@ intel_hdmi_detect(struct drm_connector *connector)
 	enum drm_connector_status status = connector_status_disconnected;
 
 	hdmi_priv->has_hdmi_sink = false;
+	hdmi_priv->has_audio = false;
 	edid = drm_get_edid(&intel_output->base,
 			    intel_output->ddc_bus);
 
@@ -163,6 +169,7 @@ intel_hdmi_detect(struct drm_connector *connector)
 		if (edid->input & DRM_EDID_INPUT_DIGITAL) {
 			status = connector_status_connected;
 			hdmi_priv->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+			hdmi_priv->has_audio = drm_detect_monitor_audio(edid);
 		}
 		intel_output->base.display_info.raw_edid = NULL;
 		kfree(edid);
-- 
1.7.6


Reply to: