aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_display.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2012-10-27 09:58:40 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-11-11 17:50:59 -0500
commit01a415fd026c1a413a7016ee880fff7a113af6c8 (patch)
tree9aa9fc6f2b758b010715b96dc530e001bb067ff6 /drivers/gpu/drm/i915/intel_display.c
parent47fab7370b45c6708e57430831839261d0d63f53 (diff)
drm/i915: check fdi B/C lane sharing constraint
And properly toggle the chicken bit in the pch to enable/disable fdi C rx. If we don't set this bit correctly, the rx gets confused in link training, which can result in an fdi link that silently fails to train the link (since the corresponding register reports success). Note that both fdi link B and C can suffer when this bit is not set correctly. The code as-is has a few deficiencies: - We presume all pipes use the pch which is not the case for cpu edp. - We don't bother with disabling both pipes when we could make things work, e.g. when pipe B switched from 4 to 2 lanes due to a mode change, we don't bother updating the w/a bit. - It's ugly. All of these are because we compute ->fdi_lanes way too late, when we're already setting up individual pipes. We need to have this information in ->modeset_global_resources already, to set things up correctly. But that is a much larger reorg of the code. Note that we actually hit the 2 lanes limit in practice rather quickly: Even though the 1920x1200 mode native mode of my screen fits into 2 lanes, it needs 3 lanes for the 1920x1080 (since that somehow has much more blanking ...). Not obeying this restriction seems to results in cute-looking digital noise. v2: Only ever clear the chicken bit when both pipes are off. v3: Use the new ->modeset_global_resources callback. v4: Move the WARNs to the right place. Oh how I hate hacks. v5: Fix spelling, noticed by Paulo Zanoni. Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r--drivers/gpu/drm/i915/intel_display.c122
1 files changed, 118 insertions, 4 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 0b2224c592d8..4e7affd268eb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2343,6 +2343,29 @@ static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe)
2343 POSTING_READ(SOUTH_CHICKEN1); 2343 POSTING_READ(SOUTH_CHICKEN1);
2344} 2344}
2345 2345
2346static void ivb_modeset_global_resources(struct drm_device *dev)
2347{
2348 struct drm_i915_private *dev_priv = dev->dev_private;
2349 struct intel_crtc *pipe_B_crtc =
2350 to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]);
2351 struct intel_crtc *pipe_C_crtc =
2352 to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_C]);
2353 uint32_t temp;
2354
2355 /* When everything is off disable fdi C so that we could enable fdi B
2356 * with all lanes. XXX: This misses the case where a pipe is not using
2357 * any pch resources and so doesn't need any fdi lanes. */
2358 if (!pipe_B_crtc->base.enabled && !pipe_C_crtc->base.enabled) {
2359 WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
2360 WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
2361
2362 temp = I915_READ(SOUTH_CHICKEN1);
2363 temp &= ~FDI_BC_BIFURCATION_SELECT;
2364 DRM_DEBUG_KMS("disabling fdi C rx\n");
2365 I915_WRITE(SOUTH_CHICKEN1, temp);
2366 }
2367}
2368
2346/* The FDI link training functions for ILK/Ibexpeak. */ 2369/* The FDI link training functions for ILK/Ibexpeak. */
2347static void ironlake_fdi_link_train(struct drm_crtc *crtc) 2370static void ironlake_fdi_link_train(struct drm_crtc *crtc)
2348{ 2371{
@@ -2602,6 +2625,9 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
2602 POSTING_READ(reg); 2625 POSTING_READ(reg);
2603 udelay(150); 2626 udelay(150);
2604 2627
2628 DRM_DEBUG_KMS("FDI_RX_IIR before link train 0x%x\n",
2629 I915_READ(FDI_RX_IIR(pipe)));
2630
2605 /* enable CPU FDI TX and PCH FDI RX */ 2631 /* enable CPU FDI TX and PCH FDI RX */
2606 reg = FDI_TX_CTL(pipe); 2632 reg = FDI_TX_CTL(pipe);
2607 temp = I915_READ(reg); 2633 temp = I915_READ(reg);
@@ -2648,7 +2674,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
2648 if (temp & FDI_RX_BIT_LOCK || 2674 if (temp & FDI_RX_BIT_LOCK ||
2649 (I915_READ(reg) & FDI_RX_BIT_LOCK)) { 2675 (I915_READ(reg) & FDI_RX_BIT_LOCK)) {
2650 I915_WRITE(reg, temp | FDI_RX_BIT_LOCK); 2676 I915_WRITE(reg, temp | FDI_RX_BIT_LOCK);
2651 DRM_DEBUG_KMS("FDI train 1 done.\n"); 2677 DRM_DEBUG_KMS("FDI train 1 done, level %i.\n", i);
2652 break; 2678 break;
2653 } 2679 }
2654 } 2680 }
@@ -2689,7 +2715,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
2689 2715
2690 if (temp & FDI_RX_SYMBOL_LOCK) { 2716 if (temp & FDI_RX_SYMBOL_LOCK) {
2691 I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK); 2717 I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK);
2692 DRM_DEBUG_KMS("FDI train 2 done.\n"); 2718 DRM_DEBUG_KMS("FDI train 2 done, level %i.\n", i);
2693 break; 2719 break;
2694 } 2720 }
2695 } 2721 }
@@ -5013,6 +5039,88 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
5013 return true; 5039 return true;
5014} 5040}
5015 5041
5042static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
5043{
5044 struct drm_i915_private *dev_priv = dev->dev_private;
5045 uint32_t temp;
5046
5047 temp = I915_READ(SOUTH_CHICKEN1);
5048 if (temp & FDI_BC_BIFURCATION_SELECT)
5049 return;
5050
5051 WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
5052 WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
5053
5054 temp |= FDI_BC_BIFURCATION_SELECT;
5055 DRM_DEBUG_KMS("enabling fdi C rx\n");
5056 I915_WRITE(SOUTH_CHICKEN1, temp);
5057 POSTING_READ(SOUTH_CHICKEN1);
5058}
5059
5060static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc)
5061{
5062 struct drm_device *dev = intel_crtc->base.dev;
5063 struct drm_i915_private *dev_priv = dev->dev_private;
5064 struct intel_crtc *pipe_B_crtc =
5065 to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]);
5066
5067 DRM_DEBUG_KMS("checking fdi config on pipe %i, lanes %i\n",
5068 intel_crtc->pipe, intel_crtc->fdi_lanes);
5069 if (intel_crtc->fdi_lanes > 4) {
5070 DRM_DEBUG_KMS("invalid fdi lane config on pipe %i: %i lanes\n",
5071 intel_crtc->pipe, intel_crtc->fdi_lanes);
5072 /* Clamp lanes to avoid programming the hw with bogus values. */
5073 intel_crtc->fdi_lanes = 4;
5074
5075 return false;
5076 }
5077
5078 if (dev_priv->num_pipe == 2)
5079 return true;
5080
5081 switch (intel_crtc->pipe) {
5082 case PIPE_A:
5083 return true;
5084 case PIPE_B:
5085 if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled &&
5086 intel_crtc->fdi_lanes > 2) {
5087 DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n",
5088 intel_crtc->pipe, intel_crtc->fdi_lanes);
5089 /* Clamp lanes to avoid programming the hw with bogus values. */
5090 intel_crtc->fdi_lanes = 2;
5091
5092 return false;
5093 }
5094
5095 if (intel_crtc->fdi_lanes > 2)
5096 WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
5097 else
5098 cpt_enable_fdi_bc_bifurcation(dev);
5099
5100 return true;
5101 case PIPE_C:
5102 if (!pipe_B_crtc->base.enabled || pipe_B_crtc->fdi_lanes <= 2) {
5103 if (intel_crtc->fdi_lanes > 2) {
5104 DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n",
5105 intel_crtc->pipe, intel_crtc->fdi_lanes);
5106 /* Clamp lanes to avoid programming the hw with bogus values. */
5107 intel_crtc->fdi_lanes = 2;
5108
5109 return false;
5110 }
5111 } else {
5112 DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n");
5113 return false;
5114 }
5115
5116 cpt_enable_fdi_bc_bifurcation(dev);
5117
5118 return true;
5119 default:
5120 BUG();
5121 }
5122}
5123
5016static void ironlake_set_m_n(struct drm_crtc *crtc, 5124static void ironlake_set_m_n(struct drm_crtc *crtc,
5017 struct drm_display_mode *mode, 5125 struct drm_display_mode *mode,
5018 struct drm_display_mode *adjusted_mode) 5126 struct drm_display_mode *adjusted_mode)
@@ -5211,7 +5319,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
5211 struct intel_encoder *encoder; 5319 struct intel_encoder *encoder;
5212 u32 temp; 5320 u32 temp;
5213 int ret; 5321 int ret;
5214 bool dither; 5322 bool dither, fdi_config_ok;
5215 5323
5216 for_each_encoder_on_crtc(dev, crtc, encoder) { 5324 for_each_encoder_on_crtc(dev, crtc, encoder) {
5217 switch (encoder->type) { 5325 switch (encoder->type) {
@@ -5349,8 +5457,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
5349 5457
5350 intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); 5458 intel_set_pipe_timings(intel_crtc, mode, adjusted_mode);
5351 5459
5460 /* Note, this also computes intel_crtc->fdi_lanes which is used below in
5461 * ironlake_check_fdi_lanes. */
5352 ironlake_set_m_n(crtc, mode, adjusted_mode); 5462 ironlake_set_m_n(crtc, mode, adjusted_mode);
5353 5463
5464 fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc);
5465
5354 if (is_cpu_edp) 5466 if (is_cpu_edp)
5355 ironlake_set_pll_edp(crtc, adjusted_mode->clock); 5467 ironlake_set_pll_edp(crtc, adjusted_mode->clock);
5356 5468
@@ -5368,7 +5480,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
5368 5480
5369 intel_update_linetime_watermarks(dev, pipe, adjusted_mode); 5481 intel_update_linetime_watermarks(dev, pipe, adjusted_mode);
5370 5482
5371 return ret; 5483 return fdi_config_ok ? ret : -EINVAL;
5372} 5484}
5373 5485
5374static int haswell_crtc_mode_set(struct drm_crtc *crtc, 5486static int haswell_crtc_mode_set(struct drm_crtc *crtc,
@@ -8331,6 +8443,8 @@ static void intel_init_display(struct drm_device *dev)
8331 /* FIXME: detect B0+ stepping and use auto training */ 8443 /* FIXME: detect B0+ stepping and use auto training */
8332 dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; 8444 dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
8333 dev_priv->display.write_eld = ironlake_write_eld; 8445 dev_priv->display.write_eld = ironlake_write_eld;
8446 dev_priv->display.modeset_global_resources =
8447 ivb_modeset_global_resources;
8334 } else if (IS_HASWELL(dev)) { 8448 } else if (IS_HASWELL(dev)) {
8335 dev_priv->display.fdi_link_train = hsw_fdi_link_train; 8449 dev_priv->display.fdi_link_train = hsw_fdi_link_train;
8336 dev_priv->display.write_eld = haswell_write_eld; 8450 dev_priv->display.write_eld = haswell_write_eld;