From owner-svn-src-all@FreeBSD.ORG Thu Mar 19 08:28:36 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B1A901065670; Thu, 19 Mar 2009 08:28:36 +0000 (UTC) (envelope-from rnoland@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 9F3D98FC0C; Thu, 19 Mar 2009 08:28:36 +0000 (UTC) (envelope-from rnoland@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n2J8SaBL053404; Thu, 19 Mar 2009 08:28:36 GMT (envelope-from rnoland@svn.freebsd.org) Received: (from rnoland@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n2J8Sanh053402; Thu, 19 Mar 2009 08:28:36 GMT (envelope-from rnoland@svn.freebsd.org) Message-Id: <200903190828.n2J8Sanh053402@svn.freebsd.org> From: Robert Noland Date: Thu, 19 Mar 2009 08:28:36 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r190021 - head/sys/dev/drm X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 19 Mar 2009 08:28:37 -0000 Author: rnoland Date: Thu Mar 19 08:28:36 2009 New Revision: 190021 URL: http://svn.freebsd.org/changeset/base/190021 Log: 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 Modified: head/sys/dev/drm/i915_dma.c head/sys/dev/drm/i915_irq.c Modified: head/sys/dev/drm/i915_dma.c ============================================================================== --- head/sys/dev/drm/i915_dma.c Thu Mar 19 08:22:56 2009 (r190020) +++ head/sys/dev/drm/i915_dma.c Thu Mar 19 08:28:36 2009 (r190021) @@ -193,7 +193,7 @@ static int i915_initialize(struct drm_de 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_de 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_dev 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_devi 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_devi 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 d 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 * /* 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 * 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); Modified: head/sys/dev/drm/i915_irq.c ============================================================================== --- head/sys/dev/drm/i915_irq.c Thu Mar 19 08:22:56 2009 (r190020) +++ head/sys/dev/drm/i915_irq.c Thu Mar 19 08:28:36 2009 (r190021) @@ -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_ 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); - I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); + if (!irq_received) + break; - DRM_DEBUG("iir = 0x%08x, pipestats a = 0x%08x, b = 0x%08x\n", - iir, pipea_stats, pipeb_stats); + I915_WRITE(IIR, iir); + 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_devi 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_devi 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 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 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; - else - pipestat = PIPE_VBLANK_INTERRUPT_ENABLE; + pipeconf = I915_READ(pipeconf_reg); + if (!(pipeconf & PIPEACONF_ENABLE)) + return -EINVAL; - 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); + DRM_SPINLOCK(&dev_priv->user_irq_lock); + if (IS_I965G(dev)) + i915_enable_pipestat(dev_priv, pipe, + PIPE_START_VBLANK_INTERRUPT_ENABLE); + else + 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 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 * * 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 d { 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 d 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; }