[kernel/f17] drm-i915-lvds-dual-channel.patch: Scrape dual-channel-ness for LVDS out

Adam Jackson ajax at fedoraproject.org
Wed May 23 19:42:27 UTC 2012


commit 2ee75d45c72d760a275dd5a949af88db1e7e5e81
Author: Adam Jackson <ajax at redhat.com>
Date:   Wed May 23 15:42:25 2012 -0400

    drm-i915-lvds-dual-channel.patch: Scrape dual-channel-ness for LVDS out
    
      of the VBT, rather than just making things up. (#819343)

 drm-i915-lvds-dual-channel.patch |  161 ++++++++++++++++++++++++++++++++++++++
 kernel.spec                      |    6 ++
 2 files changed, 167 insertions(+), 0 deletions(-)
---
diff --git a/drm-i915-lvds-dual-channel.patch b/drm-i915-lvds-dual-channel.patch
new file mode 100644
index 0000000..b2ddff0
--- /dev/null
+++ b/drm-i915-lvds-dual-channel.patch
@@ -0,0 +1,161 @@
+From b03543857fd75876b96e10d4320b775e95041bb7 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai at suse.de>
+Date: Tue, 20 Mar 2012 12:07:05 +0000
+Subject: drm/i915: Check VBIOS value for determining LVDS dual channel mode, too
+
+Currently i915 driver checks [PCH_]LVDS register bits to decide
+whether to set up the dual-link or the single-link mode.  This relies
+implicitly on that BIOS initializes the register properly at boot.
+However, BIOS doesn't initialize it always.  When the machine is
+booted with the closed lid, BIOS skips the LVDS reg initialization.
+This ends up in blank output on a machine with a dual-link LVDS when
+you open the lid after the boot.
+
+This patch adds a workaround for that problem by checking the initial
+LVDS register value in VBT.
+
+Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=37742
+Tested-By: Paulo Zanoni <paulo.r.zanoni at intel.com>
+Reviewed-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
+Reviewed-by: Adam Jackson <ajax at redhat.com>
+Signed-off-by: Takashi Iwai <tiwai at suse.de>
+Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch>
+---
+diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
+index b6098b0..4cbed7f 100644
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -406,6 +406,8 @@ typedef struct drm_i915_private {
+ 	unsigned int lvds_use_ssc:1;
+ 	unsigned int display_clock_mode:1;
+ 	int lvds_ssc_freq;
++	unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
++	unsigned int lvds_val; /* used for checking LVDS channel mode */
+ 	struct {
+ 		int rate;
+ 		int lanes;
+diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
+index 0ae76d6..e4317da 100644
+--- a/drivers/gpu/drm/i915/intel_bios.c
++++ b/drivers/gpu/drm/i915/intel_bios.c
+@@ -173,6 +173,28 @@ get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
+ 	return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
+ }
+ 
++/* get lvds_fp_timing entry
++ * this function may return NULL if the corresponding entry is invalid
++ */
++static const struct lvds_fp_timing *
++get_lvds_fp_timing(const struct bdb_header *bdb,
++		   const struct bdb_lvds_lfp_data *data,
++		   const struct bdb_lvds_lfp_data_ptrs *ptrs,
++		   int index)
++{
++	size_t data_ofs = (const u8 *)data - (const u8 *)bdb;
++	u16 data_size = ((const u16 *)data)[-1]; /* stored in header */
++	size_t ofs;
++
++	if (index >= ARRAY_SIZE(ptrs->ptr))
++		return NULL;
++	ofs = ptrs->ptr[index].fp_timing_offset;
++	if (ofs < data_ofs ||
++	    ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size)
++		return NULL;
++	return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs);
++}
++
+ /* Try to find integrated panel data */
+ static void
+ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+@@ -182,6 +204,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+ 	const struct bdb_lvds_lfp_data *lvds_lfp_data;
+ 	const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
+ 	const struct lvds_dvo_timing *panel_dvo_timing;
++	const struct lvds_fp_timing *fp_timing;
+ 	struct drm_display_mode *panel_fixed_mode;
+ 	int i, downclock;
+ 
+@@ -243,6 +266,19 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+ 			      "Normal Clock %dKHz, downclock %dKHz\n",
+ 			      panel_fixed_mode->clock, 10*downclock);
+ 	}
++
++	fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data,
++				       lvds_lfp_data_ptrs,
++				       lvds_options->panel_type);
++	if (fp_timing) {
++		/* check the resolution, just to be sure */
++		if (fp_timing->x_res == panel_fixed_mode->hdisplay &&
++		    fp_timing->y_res == panel_fixed_mode->vdisplay) {
++			dev_priv->bios_lvds_val = fp_timing->lvds_reg_val;
++			DRM_DEBUG_KMS("VBT initial LVDS value %x\n",
++				      dev_priv->bios_lvds_val);
++		}
++	}
+ }
+ 
+ /* Try to find sdvo panel data */
+diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
+index 683002fb..a76ac2e 100644
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -360,6 +360,27 @@ static const intel_limit_t intel_limits_ironlake_display_port = {
+ 	.find_pll = intel_find_pll_ironlake_dp,
+ };
+ 
++static bool is_dual_link_lvds(struct drm_i915_private *dev_priv,
++			      unsigned int reg)
++{
++	unsigned int val;
++
++	if (dev_priv->lvds_val)
++		val = dev_priv->lvds_val;
++	else {
++		/* BIOS should set the proper LVDS register value at boot, but
++		 * in reality, it doesn't set the value when the lid is closed;
++		 * we need to check "the value to be set" in VBT when LVDS
++		 * register is uninitialized.
++		 */
++		val = I915_READ(reg);
++		if (!(val & ~LVDS_DETECTED))
++			val = dev_priv->bios_lvds_val;
++		dev_priv->lvds_val = val;
++	}
++	return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
++}
++
+ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
+ 						int refclk)
+ {
+@@ -368,8 +389,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
+ 	const intel_limit_t *limit;
+ 
+ 	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+-		if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
+-		    LVDS_CLKB_POWER_UP) {
++		if (is_dual_link_lvds(dev_priv, PCH_LVDS)) {
+ 			/* LVDS dual channel */
+ 			if (refclk == 100000)
+ 				limit = &intel_limits_ironlake_dual_lvds_100m;
+@@ -397,8 +417,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
+ 	const intel_limit_t *limit;
+ 
+ 	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+-		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+-		    LVDS_CLKB_POWER_UP)
++		if (is_dual_link_lvds(dev_priv, LVDS))
+ 			/* LVDS with dual channel */
+ 			limit = &intel_limits_g4x_dual_channel_lvds;
+ 		else
+@@ -536,8 +555,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ 		 * reliably set up different single/dual channel state, if we
+ 		 * even can.
+ 		 */
+-		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+-		    LVDS_CLKB_POWER_UP)
++		if (is_dual_link_lvds(dev_priv, LVDS))
+ 			clock.p2 = limit->p2.p2_fast;
+ 		else
+ 			clock.p2 = limit->p2.p2_slow;
+--
+cgit v0.9.0.2-2-gbebe
diff --git a/kernel.spec b/kernel.spec
index fd1a987..a8a0167 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -690,6 +690,7 @@ Patch1900: linux-2.6-intel-iommu-igfx.patch
 
 Patch1950: drm-i915-allow-to-select-rc6-modes-via-kernel-parame.patch
 Patch1951: drm-i915-enable-plain-RC6-on-Sandy-Bridge-by-default.patch
+Patch1952: drm-i915-lvds-dual-channel.patch
 
 # Quiet boot fixes
 # silence the ACPI blacklist code
@@ -1472,6 +1473,7 @@ ApplyPatch linux-2.6-intel-iommu-igfx.patch
 
 ApplyPatch drm-i915-allow-to-select-rc6-modes-via-kernel-parame.patch
 ApplyPatch drm-i915-enable-plain-RC6-on-Sandy-Bridge-by-default.patch
+ApplyPatch drm-i915-lvds-dual-channel.patch
 
 # silence the ACPI blacklist code
 ApplyPatch linux-2.6-silence-acpi-blacklist.patch
@@ -2421,6 +2423,10 @@ fi
 #    '-'      |  |
 #              '-'
 %changelog
+* Wed May 23 2012 Adam Jackson <ajax at redhat.com>
+- drm-i915-lvds-dual-channel.patch: Scrape dual-channel-ness for LVDS out
+  of the VBT, rather than just making things up. (#819343)
+
 * Mon May 21 2012 Justin M. Forbes <jforbes at redhat.com> 3.3.7-1
 - Linux 3.3.7
 - Disable CONFIG_RELOCATABLE for 32bit builds. Turn back on for 3.4


More information about the scm-commits mailing list