aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-03-20 08:07:05 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-03-22 18:23:45 -0400
commitb03543857fd75876b96e10d4320b775e95041bb7 (patch)
tree3866fde96f20c80b2632ec2227ce618a7d7b0d57
parent1a8c55d37268d8b7ab7797e6d378caa697dbd029 (diff)
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@intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Reviewed-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h2
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c36
-rw-r--r--drivers/gpu/drm/i915/intel_display.c30
3 files changed, 62 insertions, 6 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b6098b05b02e..4cbed7f23e72 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 {
406 unsigned int lvds_use_ssc:1; 406 unsigned int lvds_use_ssc:1;
407 unsigned int display_clock_mode:1; 407 unsigned int display_clock_mode:1;
408 int lvds_ssc_freq; 408 int lvds_ssc_freq;
409 unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
410 unsigned int lvds_val; /* used for checking LVDS channel mode */
409 struct { 411 struct {
410 int rate; 412 int rate;
411 int lanes; 413 int lanes;
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 0ae76d6b6eab..e4317da848d5 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,
173 return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); 173 return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
174} 174}
175 175
176/* get lvds_fp_timing entry
177 * this function may return NULL if the corresponding entry is invalid
178 */
179static const struct lvds_fp_timing *
180get_lvds_fp_timing(const struct bdb_header *bdb,
181 const struct bdb_lvds_lfp_data *data,
182 const struct bdb_lvds_lfp_data_ptrs *ptrs,
183 int index)
184{
185 size_t data_ofs = (const u8 *)data - (const u8 *)bdb;
186 u16 data_size = ((const u16 *)data)[-1]; /* stored in header */
187 size_t ofs;
188
189 if (index >= ARRAY_SIZE(ptrs->ptr))
190 return NULL;
191 ofs = ptrs->ptr[index].fp_timing_offset;
192 if (ofs < data_ofs ||
193 ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size)
194 return NULL;
195 return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs);
196}
197
176/* Try to find integrated panel data */ 198/* Try to find integrated panel data */
177static void 199static void
178parse_lfp_panel_data(struct drm_i915_private *dev_priv, 200parse_lfp_panel_data(struct drm_i915_private *dev_priv,
@@ -182,6 +204,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
182 const struct bdb_lvds_lfp_data *lvds_lfp_data; 204 const struct bdb_lvds_lfp_data *lvds_lfp_data;
183 const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; 205 const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
184 const struct lvds_dvo_timing *panel_dvo_timing; 206 const struct lvds_dvo_timing *panel_dvo_timing;
207 const struct lvds_fp_timing *fp_timing;
185 struct drm_display_mode *panel_fixed_mode; 208 struct drm_display_mode *panel_fixed_mode;
186 int i, downclock; 209 int i, downclock;
187 210
@@ -243,6 +266,19 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
243 "Normal Clock %dKHz, downclock %dKHz\n", 266 "Normal Clock %dKHz, downclock %dKHz\n",
244 panel_fixed_mode->clock, 10*downclock); 267 panel_fixed_mode->clock, 10*downclock);
245 } 268 }
269
270 fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data,
271 lvds_lfp_data_ptrs,
272 lvds_options->panel_type);
273 if (fp_timing) {
274 /* check the resolution, just to be sure */
275 if (fp_timing->x_res == panel_fixed_mode->hdisplay &&
276 fp_timing->y_res == panel_fixed_mode->vdisplay) {
277 dev_priv->bios_lvds_val = fp_timing->lvds_reg_val;
278 DRM_DEBUG_KMS("VBT initial LVDS value %x\n",
279 dev_priv->bios_lvds_val);
280 }
281 }
246} 282}
247 283
248/* Try to find sdvo panel data */ 284/* 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 683002fb63d1..a76ac2eb9938 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 = {
360 .find_pll = intel_find_pll_ironlake_dp, 360 .find_pll = intel_find_pll_ironlake_dp,
361}; 361};
362 362
363static bool is_dual_link_lvds(struct drm_i915_private *dev_priv,
364 unsigned int reg)
365{
366 unsigned int val;
367
368 if (dev_priv->lvds_val)
369 val = dev_priv->lvds_val;
370 else {
371 /* BIOS should set the proper LVDS register value at boot, but
372 * in reality, it doesn't set the value when the lid is closed;
373 * we need to check "the value to be set" in VBT when LVDS
374 * register is uninitialized.
375 */
376 val = I915_READ(reg);
377 if (!(val & ~LVDS_DETECTED))
378 val = dev_priv->bios_lvds_val;
379 dev_priv->lvds_val = val;
380 }
381 return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
382}
383
363static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, 384static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
364 int refclk) 385 int refclk)
365{ 386{
@@ -368,8 +389,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
368 const intel_limit_t *limit; 389 const intel_limit_t *limit;
369 390
370 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { 391 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
371 if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == 392 if (is_dual_link_lvds(dev_priv, PCH_LVDS)) {
372 LVDS_CLKB_POWER_UP) {
373 /* LVDS dual channel */ 393 /* LVDS dual channel */
374 if (refclk == 100000) 394 if (refclk == 100000)
375 limit = &intel_limits_ironlake_dual_lvds_100m; 395 limit = &intel_limits_ironlake_dual_lvds_100m;
@@ -397,8 +417,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
397 const intel_limit_t *limit; 417 const intel_limit_t *limit;
398 418
399 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { 419 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
400 if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == 420 if (is_dual_link_lvds(dev_priv, LVDS))
401 LVDS_CLKB_POWER_UP)
402 /* LVDS with dual channel */ 421 /* LVDS with dual channel */
403 limit = &intel_limits_g4x_dual_channel_lvds; 422 limit = &intel_limits_g4x_dual_channel_lvds;
404 else 423 else
@@ -536,8 +555,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
536 * reliably set up different single/dual channel state, if we 555 * reliably set up different single/dual channel state, if we
537 * even can. 556 * even can.
538 */ 557 */
539 if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == 558 if (is_dual_link_lvds(dev_priv, LVDS))
540 LVDS_CLKB_POWER_UP)
541 clock.p2 = limit->p2.p2_fast; 559 clock.p2 = limit->p2.p2_fast;
542 else 560 else
543 clock.p2 = limit->p2.p2_slow; 561 clock.p2 = limit->p2.p2_slow;