From 324a23e9a26e65b30f64199db593e60e7d5a0374 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Thu, 19 Mar 2009 08:28:36 +0000 Subject: [PATCH] Sync up the rest of the code that we use with what Intel is shipping -Some irq/vblank related changes that hopefully will help. -A little more cleanup while I'm here. MFC after: 3 days --- sys/dev/drm/i915_dma.c | 19 +++-- sys/dev/drm/i915_irq.c | 173 +++++++++++++++++++++++------------------ 2 files changed, 110 insertions(+), 82 deletions(-) diff --git a/sys/dev/drm/i915_dma.c b/sys/dev/drm/i915_dma.c index 199683130463..db43a885a5d3 100644 --- a/sys/dev/drm/i915_dma.c +++ b/sys/dev/drm/i915_dma.c @@ -193,7 +193,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) dev_priv->ring.map.flags = 0; dev_priv->ring.map.mtrr = 0; - drm_core_ioremap(&dev_priv->ring.map, dev); + drm_core_ioremap_wc(&dev_priv->ring.map, dev); if (dev_priv->ring.map.handle == NULL) { i915_dma_cleanup(dev); @@ -209,7 +209,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) dev_priv->back_offset = init->back_offset; dev_priv->front_offset = init->front_offset; dev_priv->current_page = 0; - dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; + dev_priv->sarea_priv->pf_current_page = 0; /* Allow hardware batchbuffers unless told otherwise. */ @@ -721,7 +721,7 @@ static int i915_flip_bufs(struct drm_device *dev, void *data, DRM_DEBUG("%s\n", __func__); - LOCK_TEST_WITH_RETURN(dev, file_priv); + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); ret = i915_dispatch_flip(dev); @@ -758,7 +758,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = 0; break; default: - DRM_ERROR("Unknown parameter %d\n", param->param); + DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; } @@ -791,7 +791,7 @@ static int i915_setparam(struct drm_device *dev, void *data, dev_priv->allow_batchbuffer = param->value; break; default: - DRM_ERROR("unknown parameter %d\n", param->param); + DRM_DEBUG("unknown parameter %d\n", param->param); return -EINVAL; } @@ -822,7 +822,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, dev_priv->hws_map.flags = 0; dev_priv->hws_map.mtrr = 0; - drm_core_ioremap(&dev_priv->hws_map, dev); + drm_core_ioremap_wc(&dev_priv->hws_map, dev); if (dev_priv->hws_map.handle == NULL) { i915_dma_cleanup(dev); dev_priv->status_gfx_addr = 0; @@ -880,8 +880,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) /* Init HWS */ if (!I915_NEED_GFX_HWS(dev)) { ret = i915_init_phys_hws(dev); - if (ret != 0) + if (ret != 0) { + drm_rmmap(dev, dev_priv->mmio_map); + drm_free(dev_priv, sizeof(struct drm_i915_private), + DRM_MEM_DRIVER); return ret; + } } #ifdef __linux__ /* On the 945G/GM, the chipset reports the MSI capability on the @@ -901,6 +905,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_opregion_init(dev); #endif DRM_SPININIT(&dev_priv->user_irq_lock, "userirq"); + dev_priv->user_irq_refcount = 0; ret = drm_vblank_init(dev, I915_NUM_PIPE); diff --git a/sys/dev/drm/i915_irq.c b/sys/dev/drm/i915_irq.c index 1117f62f24db..18400b20e236 100644 --- a/sys/dev/drm/i915_irq.c +++ b/sys/dev/drm/i915_irq.c @@ -43,21 +43,28 @@ __FBSDID("$FreeBSD$"); * we leave them always unmasked in IMR and then control enabling them through * PIPESTAT alone. */ -#define I915_INTERRUPT_ENABLE_FIX (I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +#define I915_INTERRUPT_ENABLE_FIX (I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) /** Interrupts that we mask and unmask at runtime. */ -#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) /** These are all of the interrupts used by the driver */ -#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ - I915_INTERRUPT_ENABLE_VAR) +#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ + I915_INTERRUPT_ENABLE_VAR) + +#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\ + PIPE_VBLANK_INTERRUPT_STATUS) + +#define I915_PIPE_VBLANK_ENABLE (PIPE_START_VBLANK_INTERRUPT_ENABLE |\ + PIPE_VBLANK_INTERRUPT_ENABLE) + +#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \ + DRM_I915_VBLANK_PIPE_B) static inline void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) { - DRM_DEBUG("irq_enable_reg = 0x%08x, mask = 0x%08x\n", - dev_priv->irq_mask_reg, mask); mask &= I915_INTERRUPT_ENABLE_VAR; if ((dev_priv->irq_mask_reg & mask) != 0) { dev_priv->irq_mask_reg &= ~mask; @@ -189,59 +196,84 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 iir, new_iir; u32 pipea_stats, pipeb_stats; + u32 vblank_status; + u32 vblank_enable; + int irq_received; atomic_inc(&dev_priv->irq_received); - for (iir = I915_READ(IIR) ; iir != 0 ; iir = new_iir) { + iir = I915_READ(IIR); - pipea_stats = pipeb_stats = 0; + if (IS_I965G(dev)) { + vblank_status = I915_START_VBLANK_INTERRUPT_STATUS; + vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE; + } else { + vblank_status = I915_VBLANK_INTERRUPT_STATUS; + vblank_enable = I915_VBLANK_INTERRUPT_ENABLE; + } + + for (;;) { + irq_received = iir != 0; + + /* 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). + */ + DRM_SPINLOCK(&dev_priv->user_irq_lock); + pipea_stats = I915_READ(PIPEASTAT); + pipeb_stats = I915_READ(PIPEBSTAT); /* * Clear the PIPE(A|B)STAT regs before the IIR */ - if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { - DRM_SPINLOCK(&dev_priv->user_irq_lock); - pipea_stats = I915_READ(PIPEASTAT); + if (pipea_stats & 0x8000ffff) { I915_WRITE(PIPEASTAT, pipea_stats); - DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + irq_received = 1; } - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { - DRM_SPINLOCK(&dev_priv->user_irq_lock); - pipeb_stats = I915_READ(PIPEBSTAT); + if (pipeb_stats & 0x8000ffff) { I915_WRITE(PIPEBSTAT, pipeb_stats); - DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + irq_received = 1; } + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + + if (!irq_received) + break; I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); - - DRM_DEBUG("iir = 0x%08x, pipestats a = 0x%08x, b = 0x%08x\n", - iir, pipea_stats, pipeb_stats); + new_iir = I915_READ(IIR); /* Flush posted writes */ if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); if (iir & I915_USER_INTERRUPT) { -#ifdef I915_HAVE_GEM - dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); -#endif DRM_WAKEUP(&dev_priv->irq_queue); } - if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS)) + if (pipea_stats & vblank_status) drm_handle_vblank(dev, 0); - if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS)) + if (pipeb_stats & vblank_status) drm_handle_vblank(dev, 1); -#ifdef __linux__ - if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || - (iir & I915_ASLE_INTERRUPT)) - opregion_asle_intr(dev); -#endif + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; } } @@ -273,27 +305,25 @@ static int i915_emit_irq(struct drm_device * dev) void i915_user_irq_get(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long irqflags; DRM_DEBUG("\n"); - DRM_SPINLOCK_IRQSAVE(&dev_priv->user_irq_lock, irqflags); + DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) i915_enable_irq(dev_priv, I915_USER_INTERRUPT); - DRM_SPINUNLOCK_IRQRESTORE(&dev_priv->user_irq_lock, irqflags); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } void i915_user_irq_put(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long irqflags; - DRM_SPINLOCK_IRQSAVE(&dev_priv->user_irq_lock, irqflags); -#ifdef __linux__ - BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); -#endif - if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) - i915_disable_irq(dev_priv, I915_USER_INTERRUPT); - DRM_SPINUNLOCK_IRQRESTORE(&dev_priv->user_irq_lock, irqflags); + DRM_SPINLOCK(&dev_priv->user_irq_lock); + if (dev->irq_enabled) { + KASSERT(dev_priv->user_irq_refcount > 0, ("invalid refcount")); + if (--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) @@ -320,15 +350,14 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) READ_BREADCRUMB(dev_priv) >= irq_nr); i915_user_irq_put(dev); + if (ret == -ERESTART) + DRM_DEBUG("restarting syscall\n"); + if (ret == -EBUSY) { DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); } - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - return ret; } @@ -341,13 +370,13 @@ int i915_irq_emit(struct drm_device *dev, void *data, drm_i915_irq_emit_t *emit = data; int result; - RING_LOCK_TEST_WITH_RETURN(dev, file_priv); - if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); + result = i915_emit_irq(dev); if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { @@ -380,21 +409,21 @@ int i915_irq_wait(struct drm_device *dev, void *data, int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long irqflags; - u32 pipestat; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 pipeconf; - /* - * Older chips didn't have the start vblank interrupt, - * but - */ - if (IS_I965G (dev)) - pipestat = PIPE_START_VBLANK_INTERRUPT_ENABLE; + pipeconf = I915_READ(pipeconf_reg); + if (!(pipeconf & PIPEACONF_ENABLE)) + return -EINVAL; + + DRM_SPINLOCK(&dev_priv->user_irq_lock); + if (IS_I965G(dev)) + i915_enable_pipestat(dev_priv, pipe, + PIPE_START_VBLANK_INTERRUPT_ENABLE); else - pipestat = PIPE_VBLANK_INTERRUPT_ENABLE; - - DRM_SPINLOCK_IRQSAVE(&dev_priv->user_irq_lock, irqflags); - i915_enable_pipestat(dev_priv, pipe, pipestat); - DRM_SPINUNLOCK_IRQRESTORE(&dev_priv->user_irq_lock, irqflags); + i915_enable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); return 0; } @@ -404,12 +433,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) void i915_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long irqflags; - DRM_SPINLOCK_IRQSAVE(&dev_priv->user_irq_lock, irqflags); + DRM_SPINLOCK(&dev_priv->user_irq_lock); i915_disable_pipestat(dev_priv, pipe, - PIPE_START_VBLANK_INTERRUPT_ENABLE | PIPE_VBLANK_INTERRUPT_ENABLE); - DRM_SPINUNLOCK_IRQRESTORE(&dev_priv->user_irq_lock, irqflags); + PIPE_VBLANK_INTERRUPT_ENABLE | + PIPE_START_VBLANK_INTERRUPT_ENABLE); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } /* Set the vblank monitor pipe @@ -463,7 +492,6 @@ int i915_vblank_swap(struct drm_device *dev, void *data, * Context switching to userland and back is plenty fast enough for * meeting the requirements of vblank swapping. */ - return -EINVAL; } @@ -473,6 +501,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + atomic_set_int(&dev_priv->irq_received, 0); + I915_WRITE(HWSTAM, 0xeffe); I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEBSTAT, 0); @@ -505,13 +535,6 @@ int i915_driver_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IER); -#ifdef __linux__ - opregion_enable_asle(dev); -#endif - DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); - - i915_enable_vblank(dev, 0); - i915_enable_vblank(dev, 1); return 0; }