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