Please submit patches to intel-gfx, not just to me in private. Adding the
list. I did test-apply the patch, and this applies cleanly. Thanks for
fixing up your setup.
-Daniel

On Thu, Apr 30, 2015 at 10:07:54PM +0200, Thomas Richter wrote:
> From af7f1b8da36808a7369c0e209fc3de7f567b46b5 Mon Sep 17 00:00:00 2001
> From: Thomas Richter <t...@math.tu-berlin.de>
> Date: Thu, 30 Apr 2015 21:59:00 +0200
> Subject: [PATCH 1/1] 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>
> ---
>  drivers/gpu/drm/i915/dvo_ivch.c |   94 
> +++++++++++++++++++++++++++++++--------
>  1 file changed, 75 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
> index 89b08a8..d60edf8 100644
> --- a/drivers/gpu/drm/i915/dvo_ivch.c
> +++ b/drivers/gpu/drm/i915/dvo_ivch.c
> @@ -22,8 +22,6 @@
>   *
>   * Authors:
>   *    Eric Anholt <e...@anholt.net>
> - *
> - * Minor modifications (Dithering enable):
>   *    Thomas Richter <t...@math.tu-berlin.de>
>   *
>   */
> @@ -62,7 +60,7 @@
>  # define VR01_DVO_BYPASS_ENABLE              (1 << 1)
>  /** Enables the DVO clock */
>  # define VR01_DVO_ENABLE             (1 << 0)
> -/** Enable dithering for 18bpp panels. Not documented. */
> +/** Enables dithering */
>  # define VR01_DITHER_ENABLE             (1 << 4)
>  
>  /*
> @@ -79,8 +77,6 @@
>  # define VR10_INTERFACE_2X18         (2 << 2)
>  /** Enables 2x24-bit LVDS output */
>  # define VR10_INTERFACE_2X24         (3 << 2)
> -/** Mask that defines the depth of the pipeline */
> -# define VR10_INTERFACE_DEPTH_MASK      (3 << 2)
>  
>  /*
>   * VR20 LCD Horizontal Display Size
> @@ -90,7 +86,7 @@
>  /*
>   * LCD Vertical Display Size
>   */
> -#define VR21 0x20
> +#define VR21 0x21
>  
>  /*
>   * Panel power down status
> @@ -155,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.
>   *
> @@ -174,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];
>  
> @@ -198,9 +220,10 @@ 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;
>       }
>  
> @@ -209,6 +232,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int 
> addr, uint16_t *data)
>                               "%s:%02x.\n",
>                         addr, adapter->name, dvo->slave_addr);
>       }
> +     bus->force_bit--;
>       return false;
>  }
>  
> @@ -217,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,
> @@ -228,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;
>  }
>  
> @@ -246,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)
> @@ -273,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:
> @@ -294,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;
> @@ -308,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)
> @@ -334,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;
> @@ -349,24 +409,20 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
>                         struct drm_display_mode *adjusted_mode)
>  {
>       uint16_t vr40 = 0;
> -     uint16_t vr01 = 0;
> -     uint16_t vr10;
> +     uint16_t vr01;
>  
> -     ivch_read(dvo, VR10, &vr10);
> -     /* Enable dithering for 18 bpp pipelines */
> -     vr10 &= VR10_INTERFACE_DEPTH_MASK;
> -     if (vr10 == VR10_INTERFACE_2X18 || vr10 == VR10_INTERFACE_1X18)
> -             vr01 = VR01_DITHER_ENABLE;
> +     ivch_reset(dvo);
>  
> -     vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
> -             VR40_HORIZONTAL_INTERP_ENABLE);
> +     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_ENHANCED_PANEL_FITTING;
> +             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) /
> @@ -382,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)
> -- 
> 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