diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-09-13 01:16:10 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2010-09-13 10:25:26 +0100 |
commit | e9e331a8abeece1565d383510ed985945132ffe3 (patch) | |
tree | f35a5f938e1eda21f99d1fbdb3d85b7923417791 /drivers/gpu/drm/i915/intel_lvds.c | |
parent | 6edc3242e35f03990e362e7c115e722717f0f7a7 (diff) | |
download | blackbird-op-linux-e9e331a8abeece1565d383510ed985945132ffe3.tar.gz blackbird-op-linux-e9e331a8abeece1565d383510ed985945132ffe3.zip |
drm/i915/lvds: Ensure panel is unlocked for Ironlake or the panel fitter
Commit 77d07fd9d73ef28689737c0952dbd5d6a5017743 introduced a regression
where by not waiting for the panel to be turned off, left the panel and
PLL registers locked across the modeset. Thus the panel remaining blank.
As pointed out by Daniel Vetter, when testing LVDS it helps to open the
laptop and look at the actual panel you are purporting to test.
A second issue with the patch was that in order to modify the panel
fitter before gen5, the pipe and the panel must have be completely
powered down. So we wait.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lvds.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_lvds.c | 103 |
1 files changed, 75 insertions, 28 deletions
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 9089604011f9..bfc1bb443b05 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -46,6 +46,7 @@ struct intel_lvds { int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; + bool pfit_dirty; }; static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) @@ -53,31 +54,20 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) return container_of(encoder, struct intel_lvds, base.base); } -static void intel_lvds_lock_panel(struct drm_device *dev, bool lock) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (lock) - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); - else - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); -} - /** * Sets the power state for the panel. */ -static void intel_lvds_set_power(struct drm_device *dev, bool on) +static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on) { + struct drm_device *dev = intel_lvds->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, status_reg, lvds_reg; + u32 ctl_reg, lvds_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - status_reg = PCH_PP_STATUS; lvds_reg = PCH_LVDS; } else { ctl_reg = PP_CONTROL; - status_reg = PP_STATUS; lvds_reg = LVDS; } @@ -86,8 +76,18 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { + dev_priv->backlight_level = intel_panel_get_backlight(dev); + intel_panel_set_backlight(dev, 0); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); + + if (intel_lvds->pfit_control) { + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_CONTROL, 0); + intel_lvds->pfit_control = 0; + } + I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); } POSTING_READ(lvds_reg); @@ -95,12 +95,12 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) { - struct drm_device *dev = encoder->dev; + struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); if (mode == DRM_MODE_DPMS_ON) - intel_lvds_set_power(dev, true); + intel_lvds_set_power(intel_lvds, true); else - intel_lvds_set_power(dev, false); + intel_lvds_set_power(intel_lvds, false); /* XXX: We never power down the LVDS pairs. */ } @@ -331,8 +331,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } out: - intel_lvds->pfit_control = pfit_control; - intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + if (pfit_control != intel_lvds->pfit_control || + pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) { + intel_lvds->pfit_control = pfit_control; + intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + intel_lvds->pfit_dirty = true; + } dev_priv->lvds_border_bits = border; /* @@ -352,24 +356,56 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) dev_priv->backlight_level = intel_panel_get_backlight(dev); - if (intel_lvds->pfit_control == I915_READ(PFIT_CONTROL)) - intel_lvds_lock_panel(dev, false); - else - intel_lvds_set_power(dev, false); + /* We try to do the minimum that is necessary in order to unlock + * the registers for mode setting. + * + * On Ironlake, this is quite simple as we just set the unlock key + * and ignore all subtleties. (This may cause some issues...) + * + * Prior to Ironlake, we must disable the pipe if we want to adjust + * the panel fitter. However at all other times we can just reset + * the registers regardless. + */ + + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else if (intel_lvds->pfit_dirty) { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) & ~POWER_TARGET_ON); + I915_WRITE(LVDS, I915_READ(LVDS) & ~LVDS_PORT_EN); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } } -static void intel_lvds_commit( struct drm_encoder *encoder) +static void intel_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - if ((I915_READ(PP_CONTROL) & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) - intel_lvds_lock_panel(dev, true); - else - intel_lvds_set_power(dev, true); + /* Undo any unlocking done in prepare to prevent accidental + * adjustment of the registers. + */ + if (HAS_PCH_SPLIT(dev)) { + u32 val = I915_READ(PCH_PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PCH_PP_CONTROL, val & 0x3); + } else { + u32 val = I915_READ(PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PP_CONTROL, val & 0x3); + } + + /* Always do a full power on as we do not know what state + * we were left in. + */ + intel_lvds_set_power(intel_lvds, true); } static void intel_lvds_mode_set(struct drm_encoder *encoder, @@ -389,13 +425,20 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, if (HAS_PCH_SPLIT(dev)) return; + if (!intel_lvds->pfit_dirty) + return; + /* * Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to * register description and PRM. */ + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); + intel_lvds->pfit_dirty = false; } /** @@ -824,6 +867,10 @@ void intel_lvds_init(struct drm_device *dev) return; } + if (!HAS_PCH_SPLIT(dev)) { + intel_lvds->pfit_control = I915_READ(PFIT_CONTROL); + } + intel_encoder = &intel_lvds->base; encoder = &intel_encoder->base; connector = &intel_connector->base; |