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@xxxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxxx> 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@xxxxxxxxxx> > + * Thomas Richter <thor@xxxxxxxxxxxxxxxxx> > * > */ > > @@ -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 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx