On Fri, May 22, 2015 at 09:04:12PM +0200, Thomas Richter wrote:
> Hi folks,
> 
> this is re-submitting the intel DVO patch that closes bug #49838, namely
> that resuming from suspend-to-RAM on the IBM x30 leaves the laptop with
> a blank screen. See the attached patch for details.
> 
> Please let me know what else is missing to make this patch go into the
> kernel. It has been tested by Stefan Monnier, see
> https://bugs.freedesktop.org/show_bug.cgi?id=49838
> for details.
> 
> Thanks,
>       Thomas

> From 8ea307cc203d217cb6513ace045678d06c23ad61 Mon Sep 17 00:00:00 2001
> From: Thomas Richter <t...@math.tu-berlin.de>
> Date: Fri, 22 May 2015 20:55:54 +0200
> Subject: [PATCH 1/1] Fixes for IBM x30 suspend to ram.
> 
> This patch fixes the resume from suspend on the X30
> thinkpad. The bug is due to the X30 bios failing to
> restore the IVCH (DVO) registers, specifically the PLL
> registers.
> 
> This patch makes a backup of the internal DVO registers upon
> initialization - assuming that the BIOS sets up everything
> correctly. The values are then restored whenever the mode
> has to be restored.
> 
> Signed-off-by: Thomas Richter <t...@math.tu-berlin.de>

Ok patch format looks good now, but unfortunately they have pretty heavy
conflicts with drm-intel-nightly. Can you please rebase onto latest
upstream and resubmit? Applies to both patches.

One more comment below.

> ---
>  drivers/gpu/drm/i915/dvo_ivch.c |  119 
> +++++++++++++++++++++++++++++++--------
>  1 file changed, 95 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
> index baaf65b..d60edf8 100644
> --- a/drivers/gpu/drm/i915/dvo_ivch.c
> +++ b/drivers/gpu/drm/i915/dvo_ivch.c
> @@ -22,6 +22,7 @@
>   *
>   * Authors:
>   *    Eric Anholt <e...@anholt.net>
> + *    Thomas Richter <t...@math.tu-berlin.de>
>   *
>   */
>  
> @@ -59,6 +60,8 @@
>  # define VR01_DVO_BYPASS_ENABLE              (1 << 1)
>  /** Enables the DVO clock */
>  # define VR01_DVO_ENABLE             (1 << 0)
> +/** Enables dithering */
> +# define VR01_DITHER_ENABLE             (1 << 4)
>  
>  /*
>   * LCD Interface Format
> @@ -83,7 +86,7 @@
>  /*
>   * LCD Vertical Display Size
>   */
> -#define VR21 0x20
> +#define VR21 0x21
>  
>  /*
>   * Panel power down status
> @@ -148,16 +151,41 @@
>  # define VR8F_POWER_MASK             (0x3c)
>  # define VR8F_POWER_POS                      (2)
>  
> +/* Some Bios implementations do not restore the DVO state upon
> + * resume from standby. Thus, this driver has to handle it
> + * instead. The following list contains all registers that
> + * require saving.
> + */
> +static const uint16_t backup_addresses[] = {
> +     0x11, 0x12,
> +     0x18, 0x19, 0x1a, 0x1f,
> +     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
> +     0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
> +     0x8e, 0x8f,
> +     0x10            /* this must come last */
> +};
> +
>  
>  struct ivch_priv {
>       bool quiet;
>  
>       uint16_t width, height;
> +
> +     /* Register backup */
> +
> +     uint16_t reg_backup[ARRAY_SIZE(backup_addresses)];
>  };
>  
>  
>  static void ivch_dump_regs(struct intel_dvo_device *dvo);
>  
> +static inline struct intel_gmbus *
> +to_intel_gmbus(struct i2c_adapter *i2c)
> +{
> +     return container_of(i2c, struct intel_gmbus, adapter);
> +}
> +
> +
>  /**
>   * Reads a register on the ivch.
>   *
> @@ -167,6 +195,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int 
> addr, uint16_t *data)
>  {
>       struct ivch_priv *priv = dvo->dev_priv;
>       struct i2c_adapter *adapter = dvo->i2c_bus;
> +     struct intel_gmbus *bus = to_intel_gmbus(adapter);
>       u8 out_buf[1];
>       u8 in_buf[2];
>  
> @@ -191,17 +220,19 @@ static bool ivch_read(struct intel_dvo_device *dvo, int 
> addr, uint16_t *data)
>       };
>  
>       out_buf[0] = addr;
> -
> +     bus->force_bit++; /* the IVCH requires bit-banging */
>       if (i2c_transfer(adapter, msgs, 3) == 3) {
>               *data = (in_buf[1] << 8) | in_buf[0];
> +             bus->force_bit--;
>               return true;
> -     };
> +     }
>  
>       if (!priv->quiet) {
>               DRM_DEBUG_KMS("Unable to read register 0x%02x from "
>                               "%s:%02x.\n",
>                         addr, adapter->name, dvo->slave_addr);
>       }
> +     bus->force_bit--;

There's no gmbus on i830M, this should be a no-op. Are you sure this is
required? Also the correct way to do this is by calling
intel_gmbus_force_bit.
-Daniel

>       return false;
>  }
>  
> @@ -210,6 +241,7 @@ static bool ivch_write(struct intel_dvo_device *dvo, int 
> addr, uint16_t data)
>  {
>       struct ivch_priv *priv = dvo->dev_priv;
>       struct i2c_adapter *adapter = dvo->i2c_bus;
> +     struct intel_gmbus *bus = to_intel_gmbus(adapter);
>       u8 out_buf[3];
>       struct i2c_msg msg = {
>               .addr = dvo->slave_addr,
> @@ -221,15 +253,19 @@ static bool ivch_write(struct intel_dvo_device *dvo, 
> int addr, uint16_t data)
>       out_buf[0] = addr;
>       out_buf[1] = data & 0xff;
>       out_buf[2] = data >> 8;
> +     bus->force_bit++; /* bit-banging required for the IVCH */
>  
> -     if (i2c_transfer(adapter, &msg, 1) == 1)
> +     if (i2c_transfer(adapter, &msg, 1) == 1) {
> +             bus->force_bit--;
>               return true;
> +     }
>  
>       if (!priv->quiet) {
>               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
>                         addr, adapter->name, dvo->slave_addr);
>       }
>  
> +     bus->force_bit--;
>       return false;
>  }
>  
> @@ -239,6 +275,7 @@ static bool ivch_init(struct intel_dvo_device *dvo,
>  {
>       struct ivch_priv *priv;
>       uint16_t temp;
> +     int i;
>  
>       priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
>       if (priv == NULL)
> @@ -266,6 +303,14 @@ static bool ivch_init(struct intel_dvo_device *dvo,
>       ivch_read(dvo, VR20, &priv->width);
>       ivch_read(dvo, VR21, &priv->height);
>  
> +     /* Make a backup of the registers to be able to restore them
> +      * upon suspend.
> +      */
> +     for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
> +             ivch_read(dvo, backup_addresses[i], priv->reg_backup + i);
> +
> +     ivch_dump_regs(dvo);
> +
>       return true;
>  
>  out:
> @@ -287,12 +332,31 @@ static enum drm_mode_status ivch_mode_valid(struct 
> intel_dvo_device *dvo,
>       return MODE_OK;
>  }
>  
> +/* Restore the DVO registers after a resume
> + * from RAM. Registers have been saved during
> + * the initialization.
> + */
> +static void ivch_reset(struct intel_dvo_device *dvo)
> +{
> +     struct ivch_priv *priv = dvo->dev_priv;
> +     int i;
> +
> +     DRM_DEBUG_KMS("Resetting the IVCH registers\n");
> +
> +     ivch_write(dvo, VR10, 0x0000);
> +
> +     for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
> +             ivch_write(dvo, backup_addresses[i], priv->reg_backup[i]);
> +}
> +
>  /** Sets the power state of the panel connected to the ivch */
>  static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
>  {
>       int i;
>       uint16_t vr01, vr30, backlight;
>  
> +     ivch_reset(dvo);
> +
>       /* Set the new power state of the panel. */
>       if (!ivch_read(dvo, VR01, &vr01))
>               return;
> @@ -301,6 +365,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, bool 
> enable)
>               backlight = 1;
>       else
>               backlight = 0;
> +
>       ivch_write(dvo, VR80, backlight);
>  
>       if (enable)
> @@ -327,6 +392,8 @@ static bool ivch_get_hw_state(struct intel_dvo_device 
> *dvo)
>  {
>       uint16_t vr01;
>  
> +     ivch_reset(dvo);
> +
>       /* Set the new power state of the panel. */
>       if (!ivch_read(dvo, VR01, &vr01))
>               return false;
> @@ -344,16 +411,18 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
>       uint16_t vr40 = 0;
>       uint16_t vr01;
>  
> -     vr01 = 0;
> -     vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
> -             VR40_HORIZONTAL_INTERP_ENABLE);
> +     ivch_reset(dvo);
> +
> +     vr01 = VR01_DITHER_ENABLE;
> +     vr40 = VR40_STALL_ENABLE;
>  
>       if (mode->hdisplay != adjusted_mode->hdisplay ||
>           mode->vdisplay != adjusted_mode->vdisplay) {
>               uint16_t x_ratio, y_ratio;
>  
>               vr01 |= VR01_PANEL_FIT_ENABLE;
> -             vr40 |= VR40_CLOCK_GATING_ENABLE;
> +             vr40 |= VR40_CLOCK_GATING_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
> +               VR40_HORIZONTAL_INTERP_ENABLE;
>               x_ratio = (((mode->hdisplay - 1) << 16) /
>                          (adjusted_mode->hdisplay - 1)) >> 2;
>               y_ratio = (((mode->vdisplay - 1) << 16) /
> @@ -369,7 +438,7 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
>       ivch_write(dvo, VR01, vr01);
>       ivch_write(dvo, VR40, vr40);
>  
> -     ivch_dump_regs(dvo);
> +     /* ivch_dump_regs(dvo); */
>  }
>  
>  static void ivch_dump_regs(struct intel_dvo_device *dvo)
> @@ -377,41 +446,43 @@ static void ivch_dump_regs(struct intel_dvo_device *dvo)
>       uint16_t val;
>  
>       ivch_read(dvo, VR00, &val);
> -     DRM_LOG_KMS("VR00: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR00: 0x%04x\n", val);
>       ivch_read(dvo, VR01, &val);
> -     DRM_LOG_KMS("VR01: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR01: 0x%04x\n", val);
> +     ivch_read(dvo, VR10, &val);
> +     DRM_DEBUG_KMS("VR10: 0x%04x\n", val);
>       ivch_read(dvo, VR30, &val);
> -     DRM_LOG_KMS("VR30: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR30: 0x%04x\n", val);
>       ivch_read(dvo, VR40, &val);
> -     DRM_LOG_KMS("VR40: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR40: 0x%04x\n", val);
>  
>       /* GPIO registers */
>       ivch_read(dvo, VR80, &val);
> -     DRM_LOG_KMS("VR80: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR80: 0x%04x\n", val);
>       ivch_read(dvo, VR81, &val);
> -     DRM_LOG_KMS("VR81: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR81: 0x%04x\n", val);
>       ivch_read(dvo, VR82, &val);
> -     DRM_LOG_KMS("VR82: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR82: 0x%04x\n", val);
>       ivch_read(dvo, VR83, &val);
> -     DRM_LOG_KMS("VR83: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR83: 0x%04x\n", val);
>       ivch_read(dvo, VR84, &val);
> -     DRM_LOG_KMS("VR84: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR84: 0x%04x\n", val);
>       ivch_read(dvo, VR85, &val);
> -     DRM_LOG_KMS("VR85: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR85: 0x%04x\n", val);
>       ivch_read(dvo, VR86, &val);
> -     DRM_LOG_KMS("VR86: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR86: 0x%04x\n", val);
>       ivch_read(dvo, VR87, &val);
> -     DRM_LOG_KMS("VR87: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR87: 0x%04x\n", val);
>       ivch_read(dvo, VR88, &val);
> -     DRM_LOG_KMS("VR88: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR88: 0x%04x\n", val);
>  
>       /* Scratch register 0 - AIM Panel type */
>       ivch_read(dvo, VR8E, &val);
> -     DRM_LOG_KMS("VR8E: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR8E: 0x%04x\n", val);
>  
>       /* Scratch register 1 - Status register */
>       ivch_read(dvo, VR8F, &val);
> -     DRM_LOG_KMS("VR8F: 0x%04x\n", val);
> +     DRM_DEBUG_KMS("VR8F: 0x%04x\n", val);
>  }
>  
>  static void ivch_destroy(struct intel_dvo_device *dvo)
> -- 
> 1.7.10.4
> 


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to