[Intel-gfx] [PATCH 4/8] drm/i915: use the gmbus irq for waits
Daniel Kurtz
djkurtz at chromium.org
Mon Sep 10 04:53:10 CEST 2012
On Sun, Sep 9, 2012 at 5:00 PM, Daniel Vetter <daniel.vetter at ffwll.ch> wrote:
>
> 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).
>
Hi Daniel V,
>
> 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.
Great find!
Overall, this looks and sounds great.
See some comments inline...
> 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 at chromium.org>
> Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
> drivers/gpu/drm/i915/i915_drv.h | 2 ++
> drivers/gpu/drm/i915/i915_irq.c | 4 ++++
> drivers/gpu/drm/i915/intel_i2c.c | 45 ++++++++++++++++++++++++++++++----------
> 3 files changed, 40 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 26c6959..13b9e6a 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -419,6 +419,8 @@ typedef struct drm_i915_private {
> */
> uint32_t gpio_mmio_base;
>
> + wait_queue_head_t gmbus_wait_queue;
> +
> struct pci_dev *bridge_dev;
> struct intel_ring_buffer ring[I915_NUM_RINGS];
> uint32_t next_seqno;
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 86f1690..1741f2e 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -598,7 +598,11 @@ out:
>
> static void gmbus_irq_handler(struct drm_device *dev)
> {
> + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
> +
> DRM_DEBUG_DRIVER("GMBUS interrupt\n");
> +
> + wake_up_all(&dev_priv->gmbus_wait_queue);
> }
>
> static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
> diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
> index 57decac..7413595 100644
> --- a/drivers/gpu/drm/i915/intel_i2c.c
> +++ b/drivers/gpu/drm/i915/intel_i2c.c
> @@ -64,6 +64,7 @@ intel_i2c_reset(struct drm_device *dev)
> {
> struct drm_i915_private *dev_priv = dev->dev_private;
> I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
> + I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
> }
>
> static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
> @@ -205,20 +206,38 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
>
> static int
> gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
> - u32 gmbus2_status)
> + u32 gmbus2_status,
> + u32 gmbus4_irq_en)
> {
> - int ret;
> + int i;
> int reg_offset = dev_priv->gpio_mmio_base;
> - u32 gmbus2;
> + u32 gmbus2 = 0;
Technically, initializing gmbus2 here isn't necessary, since you
always assign it first before reading.
>
> + DEFINE_WAIT(wait);
> +
> + /* Important: The hw handles only the first bit, so set only one! Since
> + * we also need to check for NAKs besides the hw ready/idle signal, we
> + * need to wake up periodically and check that ourselves. */
-- It is unfortunate that we can't enable both HW_WAIT/HW_RDY & NAK irqs.
When I tried it before, it seemed to work... but something was always
unstable, and I never figured out what.
>
> + I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en);
>
> - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
> - (GMBUS_SATOER | gmbus2_status),
> - 50);
> + for (i = 0; i < msecs_to_jiffies(50) + 1; i++) {
Should there be an initial check of our condition before entering the wait?
>
> + prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
> + TASK_UNINTERRUPTIBLE);
Should this wait be interruptible?
> +
> + gmbus2 = I915_READ(GMBUS2 + reg_offset);
> + if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
> + break;
> +
> + schedule_timeout(1);
> + }
> + finish_wait(&dev_priv->gmbus_wait_queue, &wait);
Would it be more clear to just do 50 1-jiffy wait_event_timeout()s?
for (i = 0; i < msecs_to_jiffies(50) + 1; i++)
if (wait_event_timeout(&dev_priv->gmbus_wait_queue,
(gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | gmbus2_status),
1))
break;
>
> +
> + I915_WRITE(GMBUS4 + reg_offset, 0);
>
> if (gmbus2 & GMBUS_SATOER)
> return -ENXIO;
> -
> - return ret;
> + if (gmbus2 & gmbus2_status)
> + return 0;
> + return -ETIMEDOUT;
> }
>
> static int
> @@ -239,7 +258,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
> int ret;
> u32 val, loop = 0;
>
> - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY);
> + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
> + GMBUS_HW_RDY_EN);
> if (ret)
> return ret;
>
> @@ -283,7 +303,8 @@ 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);
> + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
> + GMBUS_HW_RDY_EN);
> if (ret)
> return ret;
> }
> @@ -368,7 +389,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
> if (ret == -ENXIO)
> goto clear_err;
>
> - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE);
> + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
> + GMBUS_HW_WAIT_EN);
> if (ret == -ENXIO)
> goto clear_err;
> if (ret)
> @@ -474,6 +496,7 @@ int intel_setup_gmbus(struct drm_device *dev)
> dev_priv->gpio_mmio_base = 0;
>
> mutex_init(&dev_priv->gmbus_mutex);
> + init_waitqueue_head(&dev_priv->gmbus_wait_queue);
>
> for (i = 0; i < GMBUS_NUM_PORTS; i++) {
> struct intel_gmbus *bus = &dev_priv->gmbus[i];
> --
> 1.7.11.2
>
More information about the Intel-gfx
mailing list