Skip to content

Commit 28c70f1

Browse files
committed
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 <[email protected]> Reviewed-by: Imre Deak <[email protected]> Signed-off-by: Daniel Vetter <[email protected]>
1 parent 515ac2b commit 28c70f1

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

drivers/gpu/drm/i915/i915_drv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ typedef struct drm_i915_private {
641641

642642
struct intel_gmbus gmbus[GMBUS_NUM_PORTS];
643643

644+
644645
/** gmbus_mutex protects against concurrent usage of the single hw gmbus
645646
* controller on different i2c buses. */
646647
struct mutex gmbus_mutex;
@@ -650,6 +651,8 @@ typedef struct drm_i915_private {
650651
*/
651652
uint32_t gpio_mmio_base;
652653

654+
wait_queue_head_t gmbus_wait_queue;
655+
653656
struct pci_dev *bridge_dev;
654657
struct intel_ring_buffer ring[I915_NUM_RINGS];
655658
uint32_t next_seqno;

drivers/gpu/drm/i915/i915_irq.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
527527

528528
static void gmbus_irq_handler(struct drm_device *dev)
529529
{
530+
struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
531+
530532
DRM_DEBUG_DRIVER("GMBUS interrupt\n");
533+
534+
wake_up_all(&dev_priv->gmbus_wait_queue);
531535
}
532536

533537
static irqreturn_t valleyview_irq_handler(int irq, void *arg)

drivers/gpu/drm/i915/intel_i2c.c

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev)
6363
{
6464
struct drm_i915_private *dev_priv = dev->dev_private;
6565
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
66+
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
6667
}
6768

6869
static 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)
204205

205206
static int
206207
gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
207-
u32 gmbus2_status)
208+
u32 gmbus2_status,
209+
u32 gmbus4_irq_en)
208210
{
209-
int ret;
211+
int i;
210212
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);
212220

213-
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
214-
(GMBUS_SATOER | gmbus2_status),
215-
50);
221+
for (i = 0; i < msecs_to_jiffies(50) + 1; i++) {
222+
prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
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);
216234

217235
if (gmbus2 & GMBUS_SATOER)
218236
return -ENXIO;
219-
220-
return ret;
237+
if (gmbus2 & gmbus2_status)
238+
return 0;
239+
return -ETIMEDOUT;
221240
}
222241

223242
static int
@@ -238,7 +257,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
238257
int ret;
239258
u32 val, loop = 0;
240259

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);
242262
if (ret)
243263
return ret;
244264

@@ -282,7 +302,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
282302

283303
I915_WRITE(GMBUS3 + reg_offset, val);
284304

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);
286307
if (ret)
287308
return ret;
288309
}
@@ -367,7 +388,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
367388
if (ret == -ENXIO)
368389
goto clear_err;
369390

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);
371393
if (ret == -ENXIO)
372394
goto clear_err;
373395
if (ret)
@@ -473,6 +495,7 @@ int intel_setup_gmbus(struct drm_device *dev)
473495
dev_priv->gpio_mmio_base = 0;
474496

475497
mutex_init(&dev_priv->gmbus_mutex);
498+
init_waitqueue_head(&dev_priv->gmbus_wait_queue);
476499

477500
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
478501
struct intel_gmbus *bus = &dev_priv->gmbus[i];

0 commit comments

Comments
 (0)