Here's a patch that implements what we've been talking about:
  - uses the atomic counter while interrupts are on, which means the
    get_vblank_counter callback is only used when going from off->on to
    account for missed events
  - won't disable interrupts until the modeset ioctl is called, to preserve
    old behavior with old clients

I also found a bug in the i915 code while I was at it.  I was trying to figure 
out why when my test client exited I no longer saw interrupts, and then I saw 
the code in i915_driver_irq_handler that disabled pipe b's vblank events if 
the vblank_pipe bit for pipe b wasn't set.  Since the driver is managing 
interrupts now, I just made the pipe get/set routines into stubs and set the 
appropriate bits at irq install time instead.  This make the 2D driver 
impotent when it comes to enabling/disabling interrupts, but that should be 
ok (though it means more interrupts with an old 2D and new DRM).

How does it look?  I'm hoping it's ready for 2.6.27 so we don't have to revert 
it this time...

Thanks,
Jesse
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c
index abedbe7..55a2544 100644
--- a/linux-core/drm_irq.c
+++ b/linux-core/drm_irq.c
@@ -71,16 +71,29 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 	return 0;
 }
 
+/*
+ * At load time, disabling the vblank interrupt won't be allowed since
+ * old clients may not call the modeset ioctl and therefore misbehave.
+ * Once the modeset ioctl *has* been called though, we can safely
+ * disable them when unused.
+ */
+static int vblank_disable_allowed;
+
 static void vblank_disable_fn(unsigned long arg)
 {
 	struct drm_device *dev = (struct drm_device *)arg;
 	unsigned long irqflags;
 	int i;
 
+	if (!vblank_disable_allowed)
+		return;
+
 	for (i = 0; i < dev->num_crtcs; i++) {
 		spin_lock_irqsave(&dev->vbl_lock, irqflags);
 		if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
 		    dev->vblank_enabled[i]) {
+			DRM_DEBUG("disabling vblank interrupts for crtc %d\n",
+				  i);
 			dev->driver->disable_vblank(dev, i);
 			dev->vblank_enabled[i] = 0;
 		}
@@ -175,6 +188,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 		atomic_set(&dev->vblank_refcount[i], 0);
 	}
 
+	vblank_disable_allowed = 0;
+	
 	return 0;
 
 err:
@@ -344,10 +359,15 @@ EXPORT_SYMBOL(drm_vblank_count);
  * (specified by @crtc).  Deal with wraparound, if it occurred, and
  * update the last read value so we can deal with wraparound on the next
  * call if necessary.
+ *
+ * Only necessary when going from off->on, to account for frames we
+ * didn't get an interrupt for.
+ *
+ * Note: caller must hold dev->vbl_lock since this reads & writes
+ * device vblank fields.
  */
 void drm_update_vblank_count(struct drm_device *dev, int crtc)
 {
-	unsigned long irqflags;
 	u32 cur_vblank, diff;
 
 	if (dev->vblank_suspend[crtc])
@@ -361,7 +381,6 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc)
 	 * a long time.
 	 */
 	cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
 	if (cur_vblank < dev->last_vblank[crtc]) {
 		if (cur_vblank == dev->last_vblank[crtc] - 1) {
 			diff = 0;
@@ -377,11 +396,12 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc)
 		diff = cur_vblank - dev->last_vblank[crtc];
 	}
 	dev->last_vblank[crtc] = cur_vblank;
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+	DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
+		  crtc, diff);
 
 	atomic_add(diff, &dev->_vblank_count[crtc]);
 }
-EXPORT_SYMBOL(drm_update_vblank_count);
 
 /**
  * drm_vblank_get - get a reference count on vblank events
@@ -389,9 +409,7 @@ EXPORT_SYMBOL(drm_update_vblank_count);
  * @crtc: which CRTC to own
  *
  * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.  Note callers will probably want to update the master counter
- * using drm_update_vblank_count() above before calling this routine so that
- * wakeups occur on the right vblank event.
+ * while in use.
  *
  * RETURNS
  * Zero on success, nonzero on failure.
@@ -408,8 +426,10 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 		ret = dev->driver->enable_vblank(dev, crtc);
 		if (ret)
 			atomic_dec(&dev->vblank_refcount[crtc]);
-		else
+		else {
 			dev->vblank_enabled[crtc] = 1;
+			drm_update_vblank_count(dev, crtc);
+		}
 	}
 	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
@@ -457,6 +477,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
 		dev->vblank_premodeset[crtc] =
 			dev->driver->get_vblank_counter(dev, crtc);
 		dev->vblank_suspend[crtc] = 1;
+		vblank_disable_allowed = 1;
 		break;
 	case _DRM_POST_MODESET:
 		if (dev->vblank_suspend[crtc]) {
@@ -475,6 +496,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
 			}
 		}
 		dev->vblank_suspend[crtc] = 0;
+		vblank_disable_allowed = 1;
 		break;
 	default:
 		ret = -EINVAL;
@@ -528,7 +550,6 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
 	if (crtc >= dev->num_crtcs)
 		return -EINVAL;
 
-	drm_update_vblank_count(dev, crtc);
 	seq = drm_vblank_count(dev, crtc);
 
 	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
@@ -680,7 +701,7 @@ static void drm_vbl_send_signals(struct drm_device * dev, int crtc)
  */
 void drm_handle_vblank(struct drm_device *dev, int crtc)
 {
-	drm_update_vblank_count(dev, crtc);
+	atomic_inc(&dev->_vblank_count[crtc]);
 	DRM_WAKEUP(&dev->vbl_queue[crtc]);
 	drm_vbl_send_signals(dev, crtc);
 }
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c
index d7fa47d..d0e3be2 100644
--- a/shared-core/i915_irq.c
+++ b/shared-core/i915_irq.c
@@ -361,28 +361,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
 		drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
 	}
 }
-#if 0
-static int i915_in_vblank(struct drm_device *dev, int pipe)
-{
-	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-	unsigned long pipedsl, vblank, vtotal;
-	unsigned long vbl_start, vbl_end, cur_line;
-
-	pipedsl = pipe ? PIPEBDSL : PIPEADSL;
-	vblank = pipe ? VBLANK_B : VBLANK_A;
-	vtotal = pipe ? VTOTAL_B : VTOTAL_A;
 
-	vbl_start = I915_READ(vblank) & VBLANK_START_MASK;
-	vbl_end = (I915_READ(vblank) >> VBLANK_END_SHIFT) & VBLANK_END_MASK;
-
-	cur_line = I915_READ(pipedsl);
-
-	if (cur_line >= vbl_start)
-		return 1;
-
-	return 0;
-}
-#endif
 u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
 {
 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -416,22 +395,6 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
 
 	count = (high1 << 8) | low;
 
-	/*
-	 * If we're in the middle of the vblank period, the
-	 * above regs won't have been updated yet, so return
-	 * an incremented count to stay accurate
-	 */
-#if 0
-	if (i915_in_vblank(dev, pipe))
-		count++;
-#endif
-	/* count may be reset by other driver(e.g. 2D driver), 
-	   we have no way to know if it is wrapped or resetted 
-	   when count is zero. do a rough guess.
-	*/
-	if (count == 0 && dev->last_vblank[pipe] < dev->max_vblank_count/2)
-		dev->last_vblank[pipe] = 0; 
-	
 	return count;
 }
 
@@ -444,18 +407,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 	int vblank = 0;
 
 	iir = I915_READ(IIR);
-#if 0
-	DRM_DEBUG("flag=%08x\n", iir);
-#endif
-	if (iir == 0) {
-		DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
-			   iir,
-			   I915_READ(IMR),
-			   I915_READ(IER),
-			   I915_READ(PIPEASTAT),
-			   I915_READ(PIPEBSTAT));
+	if (iir == 0)
 		return IRQ_NONE;
-	}
 
 	/*
 	 * Clear the PIPE(A|B)STAT regs before the IIR otherwise
@@ -742,13 +695,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
 		return -EINVAL;
 	}
 
-	if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) {
-		DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe);
-		return -EINVAL;
-	}
-
-	dev_priv->vblank_pipe = pipe->pipe;
-
 	return 0;
 }
 
@@ -764,13 +710,7 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
 		return -EINVAL;
 	}
 
-	flag = I915_READ(IER);
-
-	pipe->pipe = 0;
-	if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)
-		pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
-	if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
-		pipe->pipe |= DRM_I915_VBLANK_PIPE_B;
+	pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 
 	return 0;
 }
@@ -831,7 +771,6 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
 	DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags);
 
-	drm_update_vblank_count(dev, pipe);
 	curseq = drm_vblank_count(dev, pipe);
 
 	if (seqtype == _DRM_VBLANK_RELATIVE)
@@ -957,6 +896,7 @@ int i915_driver_irq_postinstall(struct drm_device * dev)
 	if (ret)
 		return ret;
 
+	dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 	dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
 
 	i915_enable_interrupt(dev);
@@ -978,6 +918,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);
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to