[Intel-gfx] [PATCH 5/8] drm/i915: avoid waking up from PC8 on GMBUS operations

Paulo Zanoni przanoni at gmail.com
Mon Jul 29 22:48:24 CEST 2013


From: Paulo Zanoni <paulo.r.zanoni at intel.com>

If we're already allowing PC8, just don't use the IRQs, so we won't
need to wake from PC8. Waking up from PC8 is a slow thing, so avoid it
when we can.

Signed-off-by: Paulo Zanoni <paulo.r.zanoni at intel.com>
---
 drivers/gpu/drm/i915/intel_i2c.c | 75 ++++++++++++++++++++++++++--------------
 1 file changed, 50 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index d1c1e0f7..2f35c10 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -203,24 +203,18 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
 	algo->data = bus;
 }
 
-/*
- * gmbus on gen4 seems to be able to generate legacy interrupts even when in MSI
- * mode. This results in spurious interrupt warnings if the legacy irq no. is
- * shared with another device. The kernel then disables that interrupt source
- * and so prevents the other device from working properly.
- */
-#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
 static int
 gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
 		     u32 gmbus2_status,
-		     u32 gmbus4_irq_en)
+		     u32 gmbus4_irq_en,
+		     bool use_irq)
 {
 	int i;
 	int reg_offset = dev_priv->gpio_mmio_base;
 	u32 gmbus2 = 0;
 	DEFINE_WAIT(wait);
 
-	if (!HAS_GMBUS_IRQ(dev_priv->dev))
+	if (!use_irq)
 		gmbus4_irq_en = 0;
 
 	/* Important: The hw handles only the first bit, so set only one! Since
@@ -250,14 +244,14 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
 }
 
 static int
-gmbus_wait_idle(struct drm_i915_private *dev_priv)
+gmbus_wait_idle(struct drm_i915_private *dev_priv, bool use_irq)
 {
 	int ret;
 	int reg_offset = dev_priv->gpio_mmio_base;
 
 #define C ((I915_READ_NOTRACE(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0)
 
-	if (!HAS_GMBUS_IRQ(dev_priv->dev))
+	if (!use_irq)
 		return wait_for(C, 10);
 
 	/* Important: The hw handles only the first bit, so set only one! */
@@ -277,7 +271,7 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv)
 
 static int
 gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
-		u32 gmbus1_index)
+		u32 gmbus1_index, bool use_irq)
 {
 	int reg_offset = dev_priv->gpio_mmio_base;
 	u16 len = msg->len;
@@ -294,7 +288,7 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
 		u32 val, loop = 0;
 
 		ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
-					   GMBUS_HW_RDY_EN);
+					   GMBUS_HW_RDY_EN, use_irq);
 		if (ret)
 			return ret;
 
@@ -309,7 +303,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
 }
 
 static int
-gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
+gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
+		 bool use_irq)
 {
 	int reg_offset = dev_priv->gpio_mmio_base;
 	u16 len = msg->len;
@@ -339,7 +334,7 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 		I915_WRITE(GMBUS3 + reg_offset, val);
 
 		ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
-					   GMBUS_HW_RDY_EN);
+					   GMBUS_HW_RDY_EN, use_irq);
 		if (ret)
 			return ret;
 	}
@@ -359,7 +354,8 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
 }
 
 static int
-gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
+gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs,
+		      bool use_irq)
 {
 	int reg_offset = dev_priv->gpio_mmio_base;
 	u32 gmbus1_index = 0;
@@ -377,7 +373,7 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 	if (gmbus5)
 		I915_WRITE(GMBUS5 + reg_offset, gmbus5);
 
-	ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
+	ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index, use_irq);
 
 	/* Clear GMBUS5 after each index transfer */
 	if (gmbus5)
@@ -386,6 +382,31 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 	return ret;
 }
 
+static bool gmbus_use_irq(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	bool ret;
+
+	/*
+	 * gmbus on gen4 seems to be able to generate legacy interrupts even
+	 * when in MSI mode. This results in spurious interrupt warnings if the
+	 * legacy irq no. is shared with another device. The kernel then
+	 * disables that interrupt source and so prevents the other device from
+	 * working properly.
+	 */
+	if (INTEL_INFO(dev)->gen <= 4)
+		return false;
+
+	/* In case we're already allowing PC8, just don't use IRQs, so we won't
+	 * need to call intel_aux_display_runtime_get and then we won't wake up
+	 * from PC8. */
+	mutex_lock(&dev_priv->pc8.lock);
+	ret = (dev_priv->pc8.forbid_refcnt != 0);
+	mutex_unlock(&dev_priv->pc8.lock);
+
+	return ret;
+}
+
 static int
 gmbus_xfer(struct i2c_adapter *adapter,
 	   struct i2c_msg *msgs,
@@ -397,8 +418,10 @@ gmbus_xfer(struct i2c_adapter *adapter,
 	struct drm_i915_private *dev_priv = bus->dev_priv;
 	int i, reg_offset;
 	int ret = 0;
+	bool use_irq = gmbus_use_irq(dev_priv->dev);
 
-	intel_aux_display_runtime_get(dev_priv);
+	if (use_irq)
+		intel_aux_display_runtime_get(dev_priv);
 	mutex_lock(&dev_priv->gmbus_mutex);
 
 	if (bus->force_bit) {
@@ -412,12 +435,13 @@ gmbus_xfer(struct i2c_adapter *adapter,
 
 	for (i = 0; i < num; i++) {
 		if (gmbus_is_index_read(msgs, i, num)) {
-			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
+			ret = gmbus_xfer_index_read(dev_priv, &msgs[i],
+						    use_irq);
 			i += 1;  /* set i to the index of the read xfer */
 		} else if (msgs[i].flags & I2C_M_RD) {
-			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
+			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0, use_irq);
 		} else {
-			ret = gmbus_xfer_write(dev_priv, &msgs[i]);
+			ret = gmbus_xfer_write(dev_priv, &msgs[i], use_irq);
 		}
 
 		if (ret == -ETIMEDOUT)
@@ -426,7 +450,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
 			goto clear_err;
 
 		ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
-					   GMBUS_HW_WAIT_EN);
+					   GMBUS_HW_WAIT_EN, use_irq);
 		if (ret == -ENXIO)
 			goto clear_err;
 		if (ret)
@@ -443,7 +467,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
 	 * We will re-enable it at the start of the next xfer,
 	 * till then let it sleep.
 	 */
-	if (gmbus_wait_idle(dev_priv)) {
+	if (gmbus_wait_idle(dev_priv, use_irq)) {
 		DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
 			 adapter->name);
 		ret = -ETIMEDOUT;
@@ -467,7 +491,7 @@ clear_err:
 	 * it's slow responding and only answers on the 2nd retry.
 	 */
 	ret = -ENXIO;
-	if (gmbus_wait_idle(dev_priv)) {
+	if (gmbus_wait_idle(dev_priv, use_irq)) {
 		DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
 			      adapter->name);
 		ret = -ETIMEDOUT;
@@ -498,7 +522,8 @@ timeout:
 
 out:
 	mutex_unlock(&dev_priv->gmbus_mutex);
-	intel_aux_display_runtime_put(dev_priv);
+	if (use_irq)
+		intel_aux_display_runtime_put(dev_priv);
 	return ret;
 }
 
-- 
1.8.1.2




More information about the Intel-gfx mailing list