Index: drmP.h
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm/dist/bsd-core/drmP.h,v
retrieving revision 1.5
diff -u -b -r1.5 drmP.h
--- drmP.h	19 Jul 2008 17:28:14 -0000	1.5
+++ drmP.h	19 Jul 2008 20:02:26 -0000
@@ -402,7 +402,11 @@
 					"lock; addl $0,0(%%rsp)" : : : "memory");
 #endif
 
-#ifdef __FreeBSD__
+/* XXX If we end up using these macros on memory which is not accessible
+ *     via volatile pointer dereferences, this code should be revisited.
+ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
 #define DRM_READ8(map, offset)						\
 	*(volatile u_int8_t *) (((unsigned long)(map)->handle) + (offset))
 #define DRM_READ16(map, offset)						\
@@ -416,13 +420,10 @@
 #define DRM_WRITE32(map, offset, val)					\
 	*(volatile u_int32_t *)(((unsigned long)(map)->handle) + (offset)) = val
 
-#define DRM_VERIFYAREA_READ( uaddr, size )		\
-	(!useracc(__DECONST(caddr_t, uaddr), size, VM_PROT_READ))
-
-#else /* __FreeBSD__ */
-
 typedef vaddr_t vm_offset_t;
 
+#else /* __FreeBSD__ || __NetBSD__ */
+
 #define DRM_READ8(map, offset)		\
 	bus_space_read_1( (map)->bst, (map)->bsh, (offset))
 #define DRM_READ16(map, offset)		\
@@ -436,11 +437,17 @@
 #define DRM_WRITE32(map, offset, val)	\
 	bus_space_write_4((map)->bst, (map)->bsh, (offset), (val))
 
+#endif /* !__FreeBSD__ */
+
+#ifdef __FreeBSD__
+#define DRM_VERIFYAREA_READ( uaddr, size )		\
+	(!useracc(__DECONST(caddr_t, uaddr), size, VM_PROT_READ))
+#else
 #define DRM_VERIFYAREA_READ( uaddr, size )		\
 	(!uvm_map_checkprot(&(curproc->p_vmspace->vm_map),              \
 		(vaddr_t)uaddr, (vaddr_t)uaddr+size, UVM_PROT_READ))
+#endif
 
-#endif /* !__FreeBSD__ */
 
 #define DRM_COPY_TO_USER(user, kern, size) \
 	copyout(kern, user, size)
@@ -881,6 +888,7 @@
 	DRM_SPINTYPE      dev_lock;	/* protects everything else */
 #endif
 	DRM_SPINTYPE	  drw_lock;
+	DRM_SPINTYPE	  tsk_lock;
 
 				/* Usage Counters */
 	int		  open_count;	/* Outstanding files open	   */
@@ -933,6 +941,7 @@
 
 	atomic_t	  context_flag;	/* Context swapping flag	   */
 	int		  last_context;	/* Last current context		   */
+	int               vblank_disable_allowed;
 	wait_queue_head_t *vbl_queue;	/* vblank wait queue */
 	atomic_t	  *_vblank_count;	/* number of VBLANK interrupts */
 						/* (driver must alloc the right number of counters) */
@@ -1195,8 +1204,9 @@
 		   struct drm_file *file_priv);
 int	drm_update_draw(struct drm_device *dev, void *data,
 			struct drm_file *file_priv);
+void	drm_drawable_free_all(struct drm_device *dev);
 struct drm_drawable_info *drm_get_drawable_info(struct drm_device *dev,
-						int handle);
+				    		 unsigned int handle);
 
 /* Authentication IOCTL support (drm_auth.c) */
 int	drm_getmagic(struct drm_device *dev, void *data,
Index: drm_drawable.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm/dist/bsd-core/drm_drawable.c,v
retrieving revision 1.3
diff -u -b -r1.3 drm_drawable.c
--- drm_drawable.c	19 Jul 2008 14:15:11 -0000	1.3
+++ drm_drawable.c	19 Jul 2008 20:02:26 -0000
@@ -35,13 +35,27 @@
 
 #include "drmP.h"
 
+struct bsd_drm_drawable_info;
+
+int	drm_drawable_compare(struct bsd_drm_drawable_info *,
+	    struct bsd_drm_drawable_info *);
+void	drm_drawable_free(struct drm_device *dev,
+	    struct bsd_drm_drawable_info *draw);
+struct bsd_drm_drawable_info *
+	drm_get_drawable(struct drm_device *, unsigned int);
+
+#if defined(__NetBSD__) || defined(__NetBSD__)
+RB_PROTOTYPE(drawable_tree, bsd_drm_drawable_info, tree,
+    drm_drawable_compare);
+#endif /* __NetBSD__ || __OpenBSD__ */
+
 struct bsd_drm_drawable_info {
 	struct drm_drawable_info info;
-	int handle;
+	unsigned int handle;
 	RB_ENTRY(bsd_drm_drawable_info) tree;
 };
 
-static int
+int
 drm_drawable_compare(struct bsd_drm_drawable_info *a,
     struct bsd_drm_drawable_info *b)
 {
@@ -50,23 +64,38 @@
 	if (a->handle < b->handle)
 		return -1;
 	return 0;
-}
+};
 
+#ifdef __FreeBSD__
 RB_GENERATE_STATIC(drawable_tree, bsd_drm_drawable_info, tree,
     drm_drawable_compare);
+#else
+RB_GENERATE(drawable_tree, bsd_drm_drawable_info, tree,
+    drm_drawable_compare);
+#endif
 
-struct drm_drawable_info *
-drm_get_drawable_info(struct drm_device *dev, int handle)
+struct bsd_drm_drawable_info *
+drm_get_drawable(struct drm_device *dev, unsigned int handle)
 {
-	struct bsd_drm_drawable_info find, *result;
+	struct bsd_drm_drawable_info find;
 
 	find.handle = handle;
-	result = RB_FIND(drawable_tree, &dev->drw_head, &find);
+	return (RB_FIND(drawable_tree, &dev->drw_head, &find));
+}
+
+struct drm_drawable_info *
+drm_get_drawable_info(struct drm_device *dev, unsigned int handle)
+{
+	struct bsd_drm_drawable_info *result = NULL;
 
+	if ((result = drm_get_drawable(dev, handle)))
 	return &result->info;
+
+	return (NULL);
 }
 
-int drm_adddraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int
+drm_adddraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
 	drm_draw_t *draw = data;
 	struct bsd_drm_drawable_info *info;
@@ -79,7 +108,6 @@
 #ifdef __FreeBSD__
 	info->handle = alloc_unr(dev->drw_unrhdr);
 #else
-	/* XXXJDM */
 	info->handle = ++dev->drw_no;
 #endif
 	DRM_SPINLOCK(&dev->drw_lock);
@@ -92,22 +120,17 @@
 	return 0;
 }
 
-int drm_rmdraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int
+drm_rmdraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
 	drm_draw_t *draw = (drm_draw_t *)data;
-	struct drm_drawable_info *info;
+	struct bsd_drm_drawable_info *info;
 
 	DRM_SPINLOCK(&dev->drw_lock);
-	info = drm_get_drawable_info(dev, draw->handle);
+	info = drm_get_drawable(dev, draw->handle);
 	if (info != NULL) {
-		RB_REMOVE(drawable_tree, &dev->drw_head,
-		    (struct bsd_drm_drawable_info *)info);
+		drm_drawable_free(dev, info);
 		DRM_SPINUNLOCK(&dev->drw_lock);
-#ifdef __FreeBSD__
-		free_unr(dev->drw_unrhdr, draw->handle);
-#endif
-		drm_free(info, sizeof(struct bsd_drm_drawable_info),
-		    DRM_MEM_DRAWABLE);
 		return 0;
 	} else {
 		DRM_SPINUNLOCK(&dev->drw_lock);
@@ -115,8 +138,8 @@
 	}
 }
 
-int drm_update_draw(struct drm_device *dev, void *data,
-		    struct drm_file *file_priv)
+int
+drm_update_draw(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
 	struct drm_drawable_info *info;
 	struct drm_update_draw *update = (struct drm_update_draw *)data;
@@ -130,6 +153,7 @@
 	case DRM_DRAWABLE_CLIPRECTS:
 		DRM_SPINLOCK(&dev->drw_lock);
 		if (update->num != info->num_rects) {
+			if (info->rects)
 			drm_free(info->rects,
 			    sizeof(*info->rects) * info->num_rects,
 			    DRM_MEM_DRAWABLE);
@@ -158,3 +182,37 @@
 		return EINVAL;
 	}
 }
+
+void
+drm_drawable_free(struct drm_device *dev, struct bsd_drm_drawable_info *draw)
+{
+	if (draw) {
+		RB_REMOVE(drawable_tree, &dev->drw_head, draw);
+		if (draw->info.rects)
+			drm_free(draw->info.rects,
+			    sizeof(*draw->info.rects) * draw->info.num_rects,
+			    DRM_MEM_DRAWABLE);
+#ifdef __FreeBSD__
+		free_unr(dev->drw_unrhdr, draw->info->handle);
+#endif
+		drm_free(draw, sizeof(*draw),
+		    DRM_MEM_DRAWABLE);
+	}
+}
+
+void
+drm_drawable_free_all(struct drm_device *dev)
+{
+	struct bsd_drm_drawable_info *draw, *nxt;
+
+	DRM_SPINLOCK(&dev->drw_lock);
+
+	for (draw = RB_MIN(drawable_tree, &dev->drw_head); draw != NULL;
+	    draw = nxt) {
+		nxt = RB_NEXT(drawable_tree, &dev->drw_head, draw);
+		drm_drawable_free(dev, draw);
+
+	}
+	DRM_SPINUNLOCK(&dev->drw_lock);
+
+}
Index: drm_drv.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm/dist/bsd-core/drm_drv.c,v
retrieving revision 1.5
diff -u -b -r1.5 drm_drv.c
--- drm_drv.c	19 Jul 2008 17:44:14 -0000	1.5
+++ drm_drv.c	19 Jul 2008 20:02:26 -0000
@@ -427,6 +427,7 @@
 	mutex_init(&dev->irq_lock, MUTEX_DEFAULT, IPL_VM);
 	mutex_init(&dev->vbl_lock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&dev->drw_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&dev->tsk_lock, MUTEX_DEFAULT, IPL_NONE);
 
 	drm_attach_common(dev, pa, idlist);
 }
@@ -733,6 +734,7 @@
 	DRM_SPINUNINIT(&dev->irq_lock);
 	DRM_SPINUNINIT(&dev->vbl_lock);
 	DRM_SPINUNINIT(&dev->drw_lock);
+	DRM_SPINUNINIT(&dev->tsk_lock);
 #endif
 	return retcode;
 }
@@ -792,6 +794,8 @@
 
 #ifdef __FreeBSD__
 	delete_unrhdr(dev->drw_unrhdr);
+#elif defined(__NetBSD__)
+        drm_drawable_free_all(dev);
 #endif
 
 	drm_mem_uninit();
Index: drm_irq.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm/dist/bsd-core/drm_irq.c,v
retrieving revision 1.3
diff -u -b -r1.3 drm_irq.c
--- drm_irq.c	19 Jul 2008 14:15:11 -0000	1.3
+++ drm_irq.c	19 Jul 2008 20:02:26 -0000
@@ -89,9 +89,6 @@
 	struct drm_device *dev = (struct drm_device *)arg;
 	int i;
 
-#ifndef __FreeBSD__	/* XXXMRG */
-	mutex_enter(&dev->vbl_lock);
-#endif
 	if (callout_pending(&dev->vblank_disable_timer)) {
 		/* callout was reset */
 		return;
@@ -102,16 +99,19 @@
 	}
 	callout_deactivate(&dev->vblank_disable_timer);
 
+	if (!dev->vblank_disable_allowed)
+		return;
+
 	for (i = 0; i < dev->num_crtcs; i++) {
 		if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
 		    dev->vblank_enabled[i]) {
+			DRM_DEBUG("disabling vblank on crtc %d\n", i);
+			dev->last_vblank[i] =
+			    dev->driver.get_vblank_counter(dev, i);
 			dev->driver.disable_vblank(dev, i);
 			dev->vblank_enabled[i] = 0;
 		}
 	}
-#ifndef __FreeBSD__	/* XXXMRG */
-	mutex_exit(&dev->vbl_lock);
-#endif
 }
 
 static void drm_vblank_cleanup(struct drm_device *dev)
@@ -209,6 +209,8 @@
 		atomic_set(&dev->vblank_refcount[i], 0);
 	}
 
+	dev->vblank_disable_allowed = 0;
+
 	return 0;
 
 err:
@@ -384,7 +386,6 @@
 
 void drm_update_vblank_count(struct drm_device *dev, int crtc)
 {
-	unsigned long irqflags;
 	u32 cur_vblank, diff;
 
 	if (dev->vblank_suspend[crtc])
@@ -398,7 +399,6 @@
 	 * a long time.
 	 */
 	cur_vblank = dev->driver.get_vblank_counter(dev, crtc);
-	DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags);
 	if (cur_vblank < dev->last_vblank[crtc]) {
 		if (cur_vblank == dev->last_vblank[crtc] - 1) {
 			diff = 0;
@@ -414,7 +414,9 @@
 		diff = cur_vblank - dev->last_vblank[crtc];
 	}
 	dev->last_vblank[crtc] = cur_vblank;
-	DRM_SPINUNLOCK_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]);
 }
@@ -432,8 +434,10 @@
 		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);
+		}
 	}
 	DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags);
 
@@ -460,6 +464,8 @@
 		    struct drm_file *file_priv)
 {
 	struct drm_modeset_ctl *modeset = data;
+	unsigned long irqflags;
+	u32 new, diff;
 	int crtc, ret = 0;
 
 	crtc = modeset->crtc;
@@ -470,27 +476,28 @@
 
 	switch (modeset->cmd) {
 	case _DRM_PRE_MODESET:
+		DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags);
+		dev->vblank_disable_allowed = 1;
+		if (!dev->vblank_enabled[crtc]) {
 		dev->vblank_premodeset[crtc] =
 		    dev->driver.get_vblank_counter(dev, crtc);
 		dev->vblank_suspend[crtc] = 1;
+		}
+		DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags);
 		break;
 	case _DRM_POST_MODESET:
-		if (dev->vblank_suspend[crtc]) {
-			u32 new = dev->driver.get_vblank_counter(dev, crtc);
-
-			/* Compensate for spurious wraparound */
-			if (new < dev->vblank_premodeset[crtc]) {
-				atomic_sub(dev->max_vblank_count + new -
-				    dev->vblank_premodeset[crtc],
-				    &dev->_vblank_count[crtc]);
-				DRM_DEBUG("vblank_premodeset[%d]=0x%x, new=0x%x"
-				    " => _vblank_count[%d]-=0x%x\n", crtc,
-				    dev->vblank_premodeset[crtc], new,
-				    crtc, dev->max_vblank_count + new -
-				    dev->vblank_premodeset[crtc]);
-			}
+		DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags);
+		dev->vblank_disable_allowed = 1;
+		new = dev->driver.get_vblank_counter(dev, crtc);
+		if (dev->vblank_suspend[crtc] && !dev->vblank_enabled[crtc]) {
+			if (new > dev->vblank_premodeset[crtc])
+			    diff = dev->vblank_premodeset[crtc] - new;
+			else
+			    diff = new;
+			atomic_add(diff, &dev->_vblank_count[crtc]);
 		}
 		dev->vblank_suspend[crtc] = 0;
+		DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags);
 		break;
 	default:
 		ret = EINVAL;
@@ -524,7 +531,6 @@
 	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) {
@@ -622,7 +628,7 @@
 
 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);
 }
@@ -636,35 +642,29 @@
 {
 	struct drm_device *dev = context;
 
-	DRM_LOCK();
-	for (;;) {
-		int ret;
+	DRM_SPINLOCK(&dev->tsk_lock);
+
+	DRM_LOCK(); /* XXX drm_lock_take() should do it's own locking */
+	if (dev->locked_task_call == NULL ||
+	drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT) == 0) {
+		DRM_UNLOCK();
+		DRM_SPINUNLOCK(&dev->tsk_lock);
+		return;
+        }
 
-		if (drm_lock_take(&dev->lock.hw_lock->lock,
-		    DRM_KERNEL_CONTEXT))
-		{
 			dev->lock.file_priv = NULL; /* kernel owned */
 			dev->lock.lock_time = jiffies;
 			atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
-			break;  /* Got lock */
-		}
 
-		/* Contention */
-#if defined(__FreeBSD__) && __FreeBSD_version > 500000
-		ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock,
-		    PZERO | PCATCH, "drmlk2", 0);
-#else
-		ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH,
-		    "drmlk2", 0);
-#endif
-		if (ret != 0)
-			return;
-	}
 	DRM_UNLOCK();
 
 	dev->locked_task_call(dev);
 
 	drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+
+	dev->locked_task_call = NULL;
+
+	DRM_SPINUNLOCK(&dev->tsk_lock);
 }
 
 void
@@ -674,8 +674,14 @@
 #if defined(__NetBSD__)
 	static struct work drm_tasklet_wk;
 #endif
+	DRM_SPINLOCK(&dev->tsk_lock);
+	if (dev->locked_task_call != NULL) {
+	  DRM_SPINUNLOCK(&dev->tsk_lock);
+	return;
+	}
 
 	dev->locked_task_call = tasklet;
+	DRM_SPINUNLOCK(&dev->tsk_lock);
 #if defined(__FreeBSD__)
 	taskqueue_enqueue(taskqueue_swi, &dev->locked_task);
 #elif defined(__NetBSD__)
Index: drm_lock.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm/dist/bsd-core/drm_lock.c,v
retrieving revision 1.2
diff -u -b -r1.2 drm_lock.c
--- drm_lock.c	19 Jul 2008 06:18:23 -0000	1.2
+++ drm_lock.c	19 Jul 2008 20:02:26 -0000
@@ -183,6 +183,13 @@
 	    _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) != lock->context)
 		return EINVAL;
 
+	DRM_SPINLOCK(&dev->tsk_lock);
+	if (dev->locked_task_call != NULL) {
+		dev->locked_task_call(dev);
+		dev->locked_task_call = NULL;
+	}
+	DRM_SPINUNLOCK(&dev->tsk_lock);
+
 	atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
 
 	DRM_LOCK();
