drm/i915: i8xx interrupt handler
authorChris Wilson <chris@chris-wilson.co.uk>
Sun, 22 Apr 2012 20:13:57 +0000 (21:13 +0100)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 3 May 2012 09:18:07 +0000 (11:18 +0200)
gen2 hardware has some significant differences from the other interrupt
routines that were glossed over and then forgotten about in the
transition to KMS. Such as

- 16bit IIR
- PendingFlip status bit

This patch reintroduces a handler specifically for gen2 for the purpose
of handling pageflips correctly, simplifying code in the process.

v2: Also fixup ring get/put irq to only access 16bit registers (Daniel)

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24202
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41793
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
[danvet: use posting_read16 in intel_ringbuffer.c and kill _driver
from the function names.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/intel_ringbuffer.c

index ab023ca73b45020623e26dcddb90ffb0567454d1..d45b43a35f15890e65e1406e20592ef316f60bfc 100644 (file)
@@ -2446,6 +2446,152 @@ static void i915_driver_irq_uninstall(struct drm_device * dev)
        I915_WRITE(IIR, I915_READ(IIR));
 }
 
+static void i8xx_irq_preinstall(struct drm_device * dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       int pipe;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       for_each_pipe(pipe)
+               I915_WRITE(PIPESTAT(pipe), 0);
+       I915_WRITE16(IMR, 0xffff);
+       I915_WRITE16(IER, 0x0);
+       POSTING_READ16(IER);
+}
+
+static int i8xx_irq_postinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
+
+       dev_priv->pipestat[0] = 0;
+       dev_priv->pipestat[1] = 0;
+
+       I915_WRITE16(EMR,
+                    ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
+
+       /* Unmask the interrupts that we always want on. */
+       dev_priv->irq_mask =
+               ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+                 I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+                 I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
+                 I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT |
+                 I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
+       I915_WRITE16(IMR, dev_priv->irq_mask);
+
+       I915_WRITE16(IER,
+                    I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+                    I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+                    I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT |
+                    I915_USER_INTERRUPT);
+       POSTING_READ16(IER);
+
+       return 0;
+}
+
+static irqreturn_t i8xx_irq_handler(DRM_IRQ_ARGS)
+{
+       struct drm_device *dev = (struct drm_device *) arg;
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_master_private *master_priv;
+       u16 iir, new_iir;
+       u32 pipe_stats[2];
+       unsigned long irqflags;
+       int irq_received;
+       int pipe;
+       u16 flip_mask =
+               I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
+               I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
+
+       atomic_inc(&dev_priv->irq_received);
+
+       iir = I915_READ16(IIR);
+       if (iir == 0)
+               return IRQ_NONE;
+
+       while (iir & ~flip_mask) {
+               /* Can't rely on pipestat interrupt bit in iir as it might
+                * have been cleared after the pipestat interrupt was received.
+                * It doesn't set the bit in iir again, but it still produces
+                * interrupts (for non-MSI).
+                */
+               spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+               if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+                       i915_handle_error(dev, false);
+
+               for_each_pipe(pipe) {
+                       int reg = PIPESTAT(pipe);
+                       pipe_stats[pipe] = I915_READ(reg);
+
+                       /*
+                        * Clear the PIPE*STAT regs before the IIR
+                        */
+                       if (pipe_stats[pipe] & 0x8000ffff) {
+                               if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
+                                       DRM_DEBUG_DRIVER("pipe %c underrun\n",
+                                                        pipe_name(pipe));
+                               I915_WRITE(reg, pipe_stats[pipe]);
+                               irq_received = 1;
+                       }
+               }
+               spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+               I915_WRITE16(IIR, iir & ~flip_mask);
+               new_iir = I915_READ16(IIR); /* Flush posted writes */
+
+               if (dev->primary->master) {
+                       master_priv = dev->primary->master->driver_priv;
+                       if (master_priv->sarea_priv)
+                               master_priv->sarea_priv->last_dispatch =
+                                       READ_BREADCRUMB(dev_priv);
+               }
+
+               if (iir & I915_USER_INTERRUPT)
+                       notify_ring(dev, &dev_priv->ring[RCS]);
+
+               if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS &&
+                   drm_handle_vblank(dev, 0)) {
+                       if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) {
+                               intel_prepare_page_flip(dev, 0);
+                               intel_finish_page_flip(dev, 0);
+                               flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT;
+                       }
+               }
+
+               if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS &&
+                   drm_handle_vblank(dev, 1)) {
+                       if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) {
+                               intel_prepare_page_flip(dev, 1);
+                               intel_finish_page_flip(dev, 1);
+                               flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
+                       }
+               }
+
+               iir = new_iir;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void i8xx_irq_uninstall(struct drm_device * dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       int pipe;
+
+       dev_priv->vblank_pipe = 0;
+
+       for_each_pipe(pipe) {
+               /* Clear enable bits; then clear status bits */
+               I915_WRITE(PIPESTAT(pipe), 0);
+               I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
+       }
+       I915_WRITE16(IMR, 0xffff);
+       I915_WRITE16(IER, 0x0);
+       I915_WRITE16(IIR, I915_READ16(IIR));
+}
+
 void intel_irq_init(struct drm_device *dev)
 {
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
@@ -2485,10 +2631,17 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->enable_vblank = ironlake_enable_vblank;
                dev->driver->disable_vblank = ironlake_disable_vblank;
        } else {
-               dev->driver->irq_preinstall = i915_driver_irq_preinstall;
-               dev->driver->irq_postinstall = i915_driver_irq_postinstall;
-               dev->driver->irq_uninstall = i915_driver_irq_uninstall;
-               dev->driver->irq_handler = i915_driver_irq_handler;
+               if (INTEL_INFO(dev)->gen == 2) {
+                       dev->driver->irq_preinstall = i8xx_irq_preinstall;
+                       dev->driver->irq_postinstall = i8xx_irq_postinstall;
+                       dev->driver->irq_handler = i8xx_irq_handler;
+                       dev->driver->irq_uninstall = i8xx_irq_uninstall;
+               } else {
+                       dev->driver->irq_preinstall = i915_driver_irq_preinstall;
+                       dev->driver->irq_postinstall = i915_driver_irq_postinstall;
+                       dev->driver->irq_uninstall = i915_driver_irq_uninstall;
+                       dev->driver->irq_handler = i915_driver_irq_handler;
+               }
                dev->driver->enable_vblank = i915_enable_vblank;
                dev->driver->disable_vblank = i915_disable_vblank;
        }
index 12d9bc789dfbe0ef3b269f548c66efc68a3140bf..6249a7fa9accc231212e77f53ff2fa4051ab22ba 100644 (file)
@@ -678,6 +678,41 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring)
        spin_unlock(&ring->irq_lock);
 }
 
+static bool
+i8xx_ring_get_irq(struct intel_ring_buffer *ring)
+{
+       struct drm_device *dev = ring->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (!dev->irq_enabled)
+               return false;
+
+       spin_lock(&ring->irq_lock);
+       if (ring->irq_refcount++ == 0) {
+               dev_priv->irq_mask &= ~ring->irq_enable_mask;
+               I915_WRITE16(IMR, dev_priv->irq_mask);
+               POSTING_READ16(IMR);
+       }
+       spin_unlock(&ring->irq_lock);
+
+       return true;
+}
+
+static void
+i8xx_ring_put_irq(struct intel_ring_buffer *ring)
+{
+       struct drm_device *dev = ring->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       spin_lock(&ring->irq_lock);
+       if (--ring->irq_refcount == 0) {
+               dev_priv->irq_mask |= ring->irq_enable_mask;
+               I915_WRITE16(IMR, dev_priv->irq_mask);
+               POSTING_READ16(IMR);
+       }
+       spin_unlock(&ring->irq_lock);
+}
+
 void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
 {
        struct drm_device *dev = ring->dev;
@@ -1310,8 +1345,13 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                else
                        ring->flush = gen4_render_ring_flush;
                ring->get_seqno = ring_get_seqno;
-               ring->irq_get = i9xx_ring_get_irq;
-               ring->irq_put = i9xx_ring_put_irq;
+               if (IS_GEN2(dev)) {
+                       ring->irq_get = i8xx_ring_get_irq;
+                       ring->irq_put = i8xx_ring_put_irq;
+               } else {
+                       ring->irq_get = i9xx_ring_get_irq;
+                       ring->irq_put = i9xx_ring_put_irq;
+               }
                ring->irq_enable_mask = I915_USER_INTERRUPT;
        }
        ring->write_tail = ring_write_tail;
@@ -1358,8 +1398,13 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
        else
                ring->flush = gen4_render_ring_flush;
        ring->get_seqno = ring_get_seqno;
-       ring->irq_get = i9xx_ring_get_irq;
-       ring->irq_put = i9xx_ring_put_irq;
+       if (IS_GEN2(dev)) {
+               ring->irq_get = i8xx_ring_get_irq;
+               ring->irq_put = i8xx_ring_put_irq;
+       } else {
+               ring->irq_get = i9xx_ring_get_irq;
+               ring->irq_put = i9xx_ring_put_irq;
+       }
        ring->irq_enable_mask = I915_USER_INTERRUPT;
        ring->write_tail = ring_write_tail;
        if (INTEL_INFO(dev)->gen >= 4)