[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