svn commit: r190021 - head/sys/dev/drm

Robert Noland rnoland at FreeBSD.org
Thu Mar 19 01:28:37 PDT 2009


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;
 }


More information about the svn-src-head mailing list