From patchwork Mon Oct 3 10:46:19 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jose Plans X-Patchwork-Id: 117433 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id A876CB6F7C for ; Mon, 3 Oct 2011 21:46:41 +1100 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1RAg2U-0001LX-Ka; Mon, 03 Oct 2011 10:46:26 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1RAg2O-0001LC-SZ for kernel-team@lists.ubuntu.com; Mon, 03 Oct 2011 10:46:20 +0000 Received: from cpc7-glfd6-2-0-cust157.6-2.cable.virginmedia.com ([86.27.227.158] helo=[192.168.0.3]) by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1RAg2O-0002B2-KR; Mon, 03 Oct 2011 10:46:20 +0000 Subject: [Natty-SRU] Lenovo x420s panel display corruption when mode-setting From: Jose Plans To: kernel-team@lists.ubuntu.com Organization: Canonical Ltd Date: Mon, 03 Oct 2011 11:46:19 +0100 Message-ID: <1317638779.1997.18.camel@jmp> Mime-Version: 1.0 X-Mailer: Evolution 2.32.2 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list Reply-To: jose.plans@canonical.com List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com SRU Justification: Impact: Every Lenovo x420s with Samsung panels will present a corrupted display when booting / mode setting. The systems are not usable. Fixes: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/812638 Fix: Backported patch commit from upstream: b24e71798871089da1a4ab049db2800afc1aac0c drm/i915: add pipe/plane enable/disable functions by Jesse Barnes. Test case: The reproducer is to simply boot the system with any kernels prior to Oneiric and using Lenovo x420 and it's Intel graphical chipset. Hardware tested was: - Lenovo x301 and on Lenovo x420s with Samsung panel. Note: This a very intrusive patch, I have spent some time studying it and I came to the conclusion it would be safer and easier to maintain if this patch was include as-is rather than providing a forked patch. I would like the list opinion on this. There are three major changes: (1) Addition of functions allowing to cleanly add or remove pipes / planes. (2) Addition of assertion functions to alert help prevent scenarios where pipes or planes are not present and they should be. For example a plane is being activated but there was no pipe active to contain it. (3) During crtc mode setting, now the pipe and planes are allocated later in the process conforming to intel specifications. Seen at the changes made in: intel_crtc_mode_set() only affecting ironlake +/sandybridge. (3) is the real fix for this panel issue, however (1) is necessary for it as well as (2) helps preventing further regressions. drivers/gpu/drm/i915/i915_reg.h | 5 +- drivers/gpu/drm/i915/intel_display.c | 308 +++++++++++++++++++++++---------- 2 files changed, 217 insertions(+), 96 deletions(-) Jose From: Jesse Barnes Date: Tue, 4 Jan 2011 15:09:30 -0800 Subject: [PATCH] drm/i915: add pipe/plane enable/disable functions Add plane enable/disable functions to prevent duplicated code and allow us to easily check for plane enable/disable requirements (such as pipe enable, plane status, pll status etc). Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson (Backport to 2.6.38.y) Signed-off-by: Robert Hooker Signed-off-by: Jose Plans --- drivers/gpu/drm/i915/i915_reg.h | 5 +- drivers/gpu/drm/i915/intel_display.c | 308 +++++++++++++++++++++++---------- 2 files changed, 217 insertions(+), 96 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 12c547a..d59df08 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2537,9 +2537,10 @@ #define DISPPLANE_32BPP_30BIT_NO_ALPHA (0xa<<26) #define DISPPLANE_STEREO_ENABLE (1<<25) #define DISPPLANE_STEREO_DISABLE 0 -#define DISPPLANE_SEL_PIPE_MASK (1<<24) +#define DISPPLANE_SEL_PIPE_SHIFT 24 +#define DISPPLANE_SEL_PIPE_MASK (3<> + DISPPLANE_SEL_PIPE_SHIFT; + WARN((val & DISPLAY_PLANE_ENABLE) && pipe == cur_pipe, + "plane %d assertion failure, should be off on pipe %c but is still active\n", + i, pipe ? 'B' : 'A'); + } +} + +/** + * intel_enable_pipe - enable a pipe, assertiing requirements + * @dev_priv: i915 private structure + * @pipe: pipe to enable + * + * Enable @pipe, making sure that various hardware specific requirements + * are met, if applicable, e.g. PLL enabled, LVDS pairs enabled, etc. + * + * @pipe should be %PIPE_A or %PIPE_B. + * + * Will wait until the pipe is actually running (i.e. first vblank) before + * returning. + */ +static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + int reg; + u32 val; + + /* + * A pipe without a PLL won't actually be able to drive bits from + * a plane. On ILK+ the pipe PLLs are integrated, so we don't + * need the check. + */ + if (!HAS_PCH_SPLIT(dev_priv->dev)) + assert_pll_enabled(dev_priv, pipe); + + reg = PIPECONF(pipe); + val = I915_READ(reg); + val |= PIPECONF_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + intel_wait_for_vblank(dev_priv->dev, pipe); +} + +/** + * intel_disable_pipe - disable a pipe, assertiing requirements + * @dev_priv: i915 private structure + * @pipe: pipe to disable + * + * Disable @pipe, making sure that various hardware specific requirements + * are met, if applicable, e.g. plane disabled, panel fitter off, etc. + * + * @pipe should be %PIPE_A or %PIPE_B. + * + * Will wait until the pipe has shut down before returning. + */ +static void intel_disable_pipe(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + int reg; + u32 val; + + /* + * Make sure planes won't keep trying to pump pixels to us, + * or we might hang the display. + */ + assert_planes_disabled(dev_priv, pipe); + + /* Don't disable pipe A or pipe A PLLs if needed */ + if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) + return; + + reg = PIPECONF(pipe); + val = I915_READ(reg); + val &= ~PIPECONF_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + intel_wait_for_pipe_off(dev_priv->dev, pipe); +} + +/** + * intel_enable_plane - enable a display plane on a given pipe + * @dev_priv: i915 private structure + * @plane: plane to enable + * @pipe: pipe being fed + * + * Enable @plane on @pipe, making sure that @pipe is running first. + */ +static void intel_enable_plane(struct drm_i915_private *dev_priv, + enum plane plane, enum pipe pipe) +{ + int reg; + u32 val; + + /* If the pipe isn't enabled, we can't pump pixels and may hang */ + assert_pipe_enabled(dev_priv, pipe); + + reg = DSPCNTR(plane); + val = I915_READ(reg); + val |= DISPLAY_PLANE_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + intel_wait_for_vblank(dev_priv->dev, pipe); +} + +/* + * Plane regs are double buffered, going from enabled->disabled needs a + * trigger in order to latch. The display address reg provides this. + */ +static void intel_flush_display_plane(struct drm_i915_private *dev_priv, + enum plane plane) +{ + u32 reg = DSPADDR(plane); + I915_WRITE(reg, I915_READ(reg)); +} + +/** + * intel_disable_plane - disable a display plane + * @dev_priv: i915 private structure + * @plane: plane to disable + * @pipe: pipe consuming the data + * + * Disable @plane; should be an independent operation. + */ +static void intel_disable_plane(struct drm_i915_private *dev_priv, + enum plane plane, enum pipe pipe) +{ + int reg; + u32 val; + + reg = DSPCNTR(plane); + val = I915_READ(reg); + val &= ~DISPLAY_PLANE_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + intel_flush_display_plane(dev_priv, plane); + intel_wait_for_vblank(dev_priv->dev, pipe); +} + static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; @@ -2003,14 +2200,6 @@ static void ironlake_fdi_enable(struct drm_crtc *crtc) } } -static void intel_flush_display_plane(struct drm_device *dev, - int plane) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg = DSPADDR(plane); - I915_WRITE(reg, I915_READ(reg)); -} - /* * When we disable a pipe, we need to clear any pending scanline wait events * to avoid hanging the ring, which we assume we are waiting on. @@ -2158,22 +2347,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) dev_priv->pch_pf_size); } - /* Enable CPU pipe */ - reg = PIPECONF(pipe); - temp = I915_READ(reg); - if ((temp & PIPECONF_ENABLE) == 0) { - I915_WRITE(reg, temp | PIPECONF_ENABLE); - POSTING_READ(reg); - intel_wait_for_vblank(dev, intel_crtc->pipe); - } - - /* configure and enable CPU plane */ - reg = DSPCNTR(plane); - temp = I915_READ(reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE); - intel_flush_display_plane(dev, plane); - } + intel_enable_pipe(dev_priv, pipe); + intel_enable_plane(dev_priv, plane, pipe); /* Skip the PCH stuff if possible */ if (!is_pch_port) @@ -2285,27 +2460,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false); - /* Disable display plane */ - reg = DSPCNTR(plane); - temp = I915_READ(reg); - if (temp & DISPLAY_PLANE_ENABLE) { - I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE); - intel_flush_display_plane(dev, plane); - } + intel_disable_plane(dev_priv, plane, pipe); if (dev_priv->cfb_plane == plane && dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); - /* disable cpu pipe, disable after all planes disabled */ - reg = PIPECONF(pipe); - temp = I915_READ(reg); - if (temp & PIPECONF_ENABLE) { - I915_WRITE(reg, temp & ~PIPECONF_ENABLE); - POSTING_READ(reg); - /* wait for cpu pipe off, pipe state */ - intel_wait_for_pipe_off(dev, intel_crtc->pipe); - } + intel_disable_pipe(dev_priv, pipe); /* Disable PF */ I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0); @@ -2500,19 +2661,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) udelay(150); } - /* Enable the pipe */ - reg = PIPECONF(pipe); - temp = I915_READ(reg); - if ((temp & PIPECONF_ENABLE) == 0) - I915_WRITE(reg, temp | PIPECONF_ENABLE); - - /* Enable the plane */ - reg = DSPCNTR(plane); - temp = I915_READ(reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE); - intel_flush_display_plane(dev, plane); - } + intel_enable_pipe(dev_priv, pipe); + intel_enable_plane(dev_priv, plane, pipe); intel_crtc_load_lut(crtc); intel_update_fbc(dev); @@ -2544,33 +2694,13 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); - /* Disable display plane */ - reg = DSPCNTR(plane); - temp = I915_READ(reg); - if (temp & DISPLAY_PLANE_ENABLE) { - I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - intel_flush_display_plane(dev, plane); - - /* Wait for vblank for the disable to take effect */ - if (IS_GEN2(dev)) - intel_wait_for_vblank(dev, pipe); - } + intel_disable_plane(dev_priv, plane, pipe); /* Don't disable pipe A or pipe A PLLs if needed */ if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) goto done; - /* Next, disable display pipes */ - reg = PIPECONF(pipe); - temp = I915_READ(reg); - if (temp & PIPECONF_ENABLE) { - I915_WRITE(reg, temp & ~PIPECONF_ENABLE); - - /* Wait for the pipe to turn off */ - POSTING_READ(reg); - intel_wait_for_pipe_off(dev, pipe); - } + intel_disable_pipe(dev_priv, pipe); reg = DPLL(pipe); temp = I915_READ(reg); @@ -4322,9 +4452,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, pipeconf &= ~PIPECONF_DOUBLE_WIDE; } - dspcntr |= DISPLAY_PLANE_ENABLE; - pipeconf |= PIPECONF_ENABLE; - dpll |= DPLL_VCO_ENABLE; + if (!HAS_PCH_SPLIT(dev)) + dpll |= DPLL_VCO_ENABLE; DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); @@ -4533,6 +4662,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(PIPECONF(pipe), pipeconf); POSTING_READ(PIPECONF(pipe)); + if (!HAS_PCH_SPLIT(dev)) + intel_enable_pipe(dev_priv, pipe); intel_wait_for_vblank(dev, pipe); @@ -4543,6 +4674,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } I915_WRITE(DSPCNTR(plane), dspcntr); + POSTING_READ(DSPCNTR(plane)); + if (!HAS_PCH_SPLIT(dev)) + intel_enable_plane(dev_priv, plane, pipe); ret = intel_pipe_set_base(crtc, x, y, old_fb); @@ -5662,22 +5796,8 @@ static void intel_sanitize_modesetting(struct drm_device *dev, pipe = !pipe; /* Disable the plane and wait for it to stop reading from the pipe. */ - I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE); - intel_flush_display_plane(dev, plane); - - if (IS_GEN2(dev)) - intel_wait_for_vblank(dev, pipe); - - if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) - return; - - /* Switch off the pipe. */ - reg = PIPECONF(pipe); - val = I915_READ(reg); - if (val & PIPECONF_ENABLE) { - I915_WRITE(reg, val & ~PIPECONF_ENABLE); - intel_wait_for_pipe_off(dev, pipe); - } + intel_disable_plane(dev_priv, plane, pipe); + intel_disable_pipe(dev_priv, pipe); } static void intel_crtc_reset(struct drm_crtc *crtc) -- 1.7.4.1