[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