[Intel-gfx] [PATCH] Fix X30 suspend from resume patch, closes bug #49838

Daniel Vetter daniel at ffwll.ch
Mon May 4 00:30:13 PDT 2015


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 <thor at 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 <thor at 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 <eric at anholt.net>
> - *
> - * Minor modifications (Dithering enable):
>   *    Thomas Richter <thor at 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


More information about the Intel-gfx mailing list