[Bug 49838] [830M] fails to wake up from suspend on thinkpad X30

bugzilla-daemon at freedesktop.org bugzilla-daemon at freedesktop.org
Thu Apr 30 13:09:48 PDT 2015


https://bugs.freedesktop.org/show_bug.cgi?id=49838

thor at math.tu-berlin.de changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEEDINFO                    |RESOLVED
         Resolution|---                         |WORKSFORME

--- Comment #141 from thor at math.tu-berlin.de ---
>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

-- 
You are receiving this mail because:
You are the QA Contact for the bug.
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/intel-gfx-bugs/attachments/20150430/3c981cc3/attachment.html>


More information about the intel-gfx-bugs mailing list