[Intel-gfx] [PATCH] Fix resume from suspend to RAM on IBM X30

Daniel Vetter daniel at ffwll.ch
Tue May 26 00:27:10 PDT 2015


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 <thor at 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 <thor at 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 <eric at anholt.net>
> + *    Thomas Richter <thor at 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


More information about the Intel-gfx mailing list