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@xxxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxxx> > --- > 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@xxxxxxxxxx> > - * > - * Minor modifications (Dithering enable): > * Thomas Richter <thor@xxxxxxxxxxxxxxxxx> > * > */ > @@ -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@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx