aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_i2c.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-01 07:53:45 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-06 07:14:37 -0500
commit28c70f162a315bdcfbe0bf940a740ef8bfb918d6 (patch)
treeddfa342c2b509942f5c526d9b5fdde6b09c2c7e0 /drivers/gpu/drm/i915/intel_i2c.c
parent515ac2bb95f609bc4a0d2ad5f7011b3264b2bb21 (diff)
drm/i915: use the gmbus irq for waits
We need two special things to properly wire this up: - Add another argument to gmbus_wait_hw_status to pass in the correct interrupt bit in gmbus4. - Since we can only get an irq for one of the two events we want, hand-roll the wait_event_timeout code so that we wake up every jiffie and can check for NAKs. This way we also subsume gmbus support for platforms without interrupts (or where those are not yet enabled). The important bit really is to only enable one gmbus interrupt source at the same time - with that piece of lore figured out, this seems to work flawlessly. Ben Widawsky rightfully complained the lack of measurements for the claimed benefits (especially since the first version was actually broken and fell back to bit-banging). Previously reading the 256 byte hdmi EDID takes about 72 ms here. With this patch it's down to 33 ms. Given that transfering the 256 bytes over i2c at wire speed takes 20.5ms alone, the reduction in additional overhead is rather nice. v2: Chris Wilson wondered whether GMBUS4 might contain some set bits when booting up an hence result in some spurious interrupts. Since we clear GMBUS4 after every wait and we do gmbus transfer really early in the setup sequence to detect displays the window is small, but still be paranoid and clear it properly. v3: Clarify the comment that gmbus irq generation can only support one kind of event, why it bothers us and how we work around that limit. Cc: Daniel Kurtz <djkurtz@chromium.org> Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_i2c.c')
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c45
1 files changed, 34 insertions, 11 deletions
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index a16eecdf1ed1..8b7189253cb4 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev)
63{ 63{
64 struct drm_i915_private *dev_priv = dev->dev_private; 64 struct drm_i915_private *dev_priv = dev->dev_private;
65 I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); 65 I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
66 I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
66} 67}
67 68
68static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) 69static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
@@ -204,20 +205,38 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
204 205
205static int 206static int
206gmbus_wait_hw_status(struct drm_i915_private *dev_priv, 207gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
207 u32 gmbus2_status) 208 u32 gmbus2_status,
209 u32 gmbus4_irq_en)
208{ 210{
209 int ret; 211 int i;
210 int reg_offset = dev_priv->gpio_mmio_base; 212 int reg_offset = dev_priv->gpio_mmio_base;
211 u32 gmbus2; 213 u32 gmbus2 = 0;
214 DEFINE_WAIT(wait);
215
216 /* Important: The hw handles only the first bit, so set only one! Since
217 * we also need to check for NAKs besides the hw ready/idle signal, we
218 * need to wake up periodically and check that ourselves. */
219 I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en);
212 220
213 ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & 221 for (i = 0; i < msecs_to_jiffies(50) + 1; i++) {
214 (GMBUS_SATOER | gmbus2_status), 222 prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
215 50); 223 TASK_UNINTERRUPTIBLE);
224
225 gmbus2 = I915_READ(GMBUS2 + reg_offset);
226 if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
227 break;
228
229 schedule_timeout(1);
230 }
231 finish_wait(&dev_priv->gmbus_wait_queue, &wait);
232
233 I915_WRITE(GMBUS4 + reg_offset, 0);
216 234
217 if (gmbus2 & GMBUS_SATOER) 235 if (gmbus2 & GMBUS_SATOER)
218 return -ENXIO; 236 return -ENXIO;
219 237 if (gmbus2 & gmbus2_status)
220 return ret; 238 return 0;
239 return -ETIMEDOUT;
221} 240}
222 241
223static int 242static int
@@ -238,7 +257,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
238 int ret; 257 int ret;
239 u32 val, loop = 0; 258 u32 val, loop = 0;
240 259
241 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); 260 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
261 GMBUS_HW_RDY_EN);
242 if (ret) 262 if (ret)
243 return ret; 263 return ret;
244 264
@@ -282,7 +302,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
282 302
283 I915_WRITE(GMBUS3 + reg_offset, val); 303 I915_WRITE(GMBUS3 + reg_offset, val);
284 304
285 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); 305 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
306 GMBUS_HW_RDY_EN);
286 if (ret) 307 if (ret)
287 return ret; 308 return ret;
288 } 309 }
@@ -367,7 +388,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
367 if (ret == -ENXIO) 388 if (ret == -ENXIO)
368 goto clear_err; 389 goto clear_err;
369 390
370 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); 391 ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
392 GMBUS_HW_WAIT_EN);
371 if (ret == -ENXIO) 393 if (ret == -ENXIO)
372 goto clear_err; 394 goto clear_err;
373 if (ret) 395 if (ret)
@@ -473,6 +495,7 @@ int intel_setup_gmbus(struct drm_device *dev)
473 dev_priv->gpio_mmio_base = 0; 495 dev_priv->gpio_mmio_base = 0;
474 496
475 mutex_init(&dev_priv->gmbus_mutex); 497 mutex_init(&dev_priv->gmbus_mutex);
498 init_waitqueue_head(&dev_priv->gmbus_wait_queue);
476 499
477 for (i = 0; i < GMBUS_NUM_PORTS; i++) { 500 for (i = 0; i < GMBUS_NUM_PORTS; i++) {
478 struct intel_gmbus *bus = &dev_priv->gmbus[i]; 501 struct intel_gmbus *bus = &dev_priv->gmbus[i];