Add Interrupt mitigation for the i9XX user_irq, should save a bit of power;
authoroga <oga@openbsd.org>
Mon, 1 Sep 2008 17:37:05 +0000 (17:37 +0000)
committeroga <oga@openbsd.org>
Mon, 1 Sep 2008 17:37:05 +0000 (17:37 +0000)
from drm git.

sys/dev/pci/drm/i915_dma.c
sys/dev/pci/drm/i915_drv.h
sys/dev/pci/drm/i915_irq.c

index e0232ab..d35007a 100644 (file)
@@ -80,7 +80,7 @@ static int i915_dma_cleanup(struct drm_device * dev)
         * may not have been called from userspace and after dev_private
         * is freed, it's too late.
         */
-       if (dev->irq)
+       if (dev->irq_enabled)
                drm_irq_uninstall(dev);
 
        if (dev_priv->ring.virtual_start) {
@@ -745,7 +745,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
 
        switch (param->param) {
        case I915_PARAM_IRQ_ACTIVE:
-               value = dev->irq ? 1 : 0;
+               value = dev->irq_enabled;
                break;
        case I915_PARAM_ALLOW_BATCHBUFFER:
                value = dev_priv->allow_batchbuffer ? 1 : 0;
@@ -921,6 +921,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        ret = drm_addmap(dev, base, size, _DRM_REGISTERS,
                _DRM_KERNEL | _DRM_DRIVER, &dev_priv->mmio_map);
 
+       mtx_init(&dev_priv->swaps_lock, IPL_BIO);
+       DRM_SPININIT(&dev_priv->user_irq_lock, "I915 irq lock");
+
 #ifdef __linux__
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        intel_init_chipset_flush_compat(dev);
@@ -938,6 +941,9 @@ int i915_driver_unload(struct drm_device *dev)
        if (dev_priv->mmio_map)
                drm_rmmap(dev, dev_priv->mmio_map);
 
+       DRM_SPINUNINIT(&dev_priv->swaps_lock);
+       DRM_SPINUNINIT(&dev_priv->user_irq_lock);
+
 #ifdef __linux__
        intel_opregion_free(dev);
 #endif
index dd19e90..cf9d112 100644 (file)
@@ -126,17 +126,19 @@ typedef struct drm_i915_private {
        wait_queue_head_t irq_queue;
        atomic_t irq_received;
        atomic_t irq_emitted;
+       /* Protects user_irq_refcount and irq_mask reg */
+       DRM_SPINTYPE user_irq_lock;
+       /* Refcount for user irq, only enabled when needed */
+       int user_irq_refcount;
+       /* Cached value of IMR to avoid reads in updating the bitfield */
+       u_int32_t irq_mask_reg;
+       int irq_enabled;
 
        int tex_lru_log_granularity;
        int allow_batchbuffer;
        struct mem_block *agp_heap;
        unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
        int vblank_pipe;
-       DRM_SPINTYPE user_irq_lock;
-       int user_irq_refcount;
-       int fence_irq_on;
-       uint32_t irq_enable_reg;
-       int irq_enabled;
 
        DRM_SPINTYPE swaps_lock;
        drm_i915_vbl_swap_t vbl_swaps;
index 6c74b6a..bb26e4b 100644 (file)
 #include "i915_drv.h"
 
 #define MAX_NOPID ((u32)~0)
+void   i915_enable_irq(drm_i915_private_t *, u_int32_t);
+void   i915_disable_irq(drm_i915_private_t *, u_int32_t);
+int    i915_wait_irq(struct drm_device *, int);
+
+/** These are the interrupts used by the driver */
+#define I915_INTERRUPT_ENABLE_MASK     (I915_USER_INTERRUPT |          \
+                                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
+                                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
+
+inline void
+i915_enable_irq(drm_i915_private_t *dev_priv, u_int32_t mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != 0) {
+               dev_priv->irq_mask_reg &= ~mask;
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void)I915_READ(IMR);
+       }
+}
+
+inline void
+i915_disable_irq(drm_i915_private_t *dev_priv, u_int32_t mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != mask) {
+               dev_priv->irq_mask_reg |= mask;
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void)I915_READ(IMR);
+       }
+}
 
 /**
  * i915_get_pipe - return the the pipe associated with a given plane
@@ -375,7 +403,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
        low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-           DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+           DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
            return 0;
        }
 
@@ -403,10 +431,11 @@ irqreturn_t i915_driver_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;
        u32 iir;
-       u32 pipea_stats, pipeb_stats;
+       u32 pipea_stats = 0, pipeb_stats = 0;
        int vblank = 0;
 
        iir = I915_READ(IIR);
+       atomic_inc(&dev_priv->irq_received);
        if (iir == 0)
                return IRQ_NONE;
 
@@ -458,7 +487,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
            dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
 
        I915_WRITE(IIR, iir);
-       (void) I915_READ(IIR);
+       (void) I915_READ(IIR); /* Flush posted writes */
 
        if (iir & I915_USER_INTERRUPT) {
                DRM_WAKEUP(&dev_priv->irq_queue);
@@ -494,10 +523,8 @@ int i915_emit_irq(struct drm_device *dev)
 void i915_user_irq_on(drm_i915_private_t *dev_priv)
 {
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
-       if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
-               dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
-               I915_WRITE(IER, dev_priv->irq_enable_reg);
-       }
+       if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1))
+               i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
 }
@@ -505,24 +532,31 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv)
 void i915_user_irq_off(drm_i915_private_t *dev_priv)
 {
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
-       if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
-               //              dev_priv->irq_enable_reg &= ~I915_USER_INTERRUPT;
-               //              I915_WRITE(IER, dev_priv->irq_enable_reg);
-       }
+       if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0))
+               i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 }
 
 
-static int i915_wait_irq(struct drm_device * dev, int irq_nr)
+int i915_wait_irq(struct drm_device * dev, int irq_nr)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int ret = 0;
 
+       if (!dev_priv) {
+               DRM_ERROR("called with no initialization\n");
+               return -EINVAL;
+       }
+
        DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
 
-       if (READ_BREADCRUMB(dev_priv) >= irq_nr)
+       if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
+               if (dev_priv->sarea_priv)
+                       dev_priv->sarea_priv->last_dispatch =
+                           READ_BREADCRUMB(dev_priv);
                return 0;
+       }
 
        i915_user_irq_on(dev_priv);
        DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
@@ -587,16 +621,17 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int pipe = i915_get_pipe(dev, plane);
        u32     pipestat_reg = 0;
+       u32     mask_reg = 0;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
                pipestat_reg = PIPEASTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
                pipestat_reg = PIPEBSTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
@@ -622,7 +657,9 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
                             PIPE_VBLANK_INTERRUPT_STATUS);
                I915_WRITE(pipestat_reg, pipestat);
        }
-       I915_WRITE(IER, dev_priv->irq_enable_reg);
+       DRM_SPINLOCK(&dev_priv->user_irq_lock);
+       i915_enable_irq(dev_priv, mask_reg);
+       DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
        return 0;
 }
@@ -632,16 +669,17 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int pipe = i915_get_pipe(dev, plane);
        u32     pipestat_reg = 0;
+       u32     mask_reg = 0;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
                pipestat_reg = PIPEASTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
                pipestat_reg = PIPEBSTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
@@ -649,7 +687,9 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                break;
        }
 
-       I915_WRITE(IER, dev_priv->irq_enable_reg);
+       DRM_SPINLOCK(&dev_priv->user_irq_lock);
+       i915_disable_irq(dev_priv, mask_reg);
+       DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
        if (pipestat_reg)
        {
@@ -662,20 +702,23 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
                             PIPE_VBLANK_INTERRUPT_STATUS);
                I915_WRITE(pipestat_reg, pipestat);
+               (void)I915_READ(pipestat_reg);
        }
 }
 
 void i915_enable_interrupt (struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       
-       dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
+
+       dev_priv->irq_mask_reg = ~0;
+       I915_WRITE(IMR, dev_priv->irq_mask_reg);
+       I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
+       (void)I915_READ(IER);
 
 #ifdef __linux__
        opregion_enable_asle(dev);
 #endif
 
-       I915_WRITE(IER, dev_priv->irq_enable_reg);
        dev_priv->irq_enabled = 1;
 }
 
@@ -875,9 +918,9 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       I915_WRITE16(HWSTAM, 0xeffe);
-       I915_WRITE16(IMR, 0x0);
-       I915_WRITE16(IER, 0x0);
+       I915_WRITE(HWSTAM, 0xeffe);
+       I915_WRITE(IMR, 0x0);
+       I915_WRITE(IER, 0x0);
 }
 
 int i915_driver_irq_postinstall(struct drm_device * dev)
@@ -885,13 +928,11 @@ int i915_driver_irq_postinstall(struct drm_device * dev)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int ret, num_pipes = 2;
 
-       DRM_SPININIT(&dev_priv->swaps_lock, "swap");
        INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
        dev_priv->swaps_pending = 0;
 
-       DRM_SPININIT(&dev_priv->user_irq_lock, "userirq");
        dev_priv->user_irq_refcount = 0;
-       dev_priv->irq_enable_reg = 0;
+       dev_priv->irq_mask_reg = ~0;
 
        ret = drm_vblank_init(dev, num_pipes);
        if (ret)
@@ -919,6 +960,8 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
        if (!dev_priv)
                return;
 
+       dev_priv->vblank_pipe = 0;
+
        dev_priv->irq_enabled = 0;
        I915_WRITE(HWSTAM, 0xffffffff);
        I915_WRITE(IMR, 0xffffffff);