diff --git a/sys/dev/drm/drm/Makefile b/sys/dev/drm/drm/Makefile
index c545962..8c0c4c7 100644
--- a/sys/dev/drm/drm/Makefile
+++ b/sys/dev/drm/drm/Makefile
@@ -13,12 +13,16 @@ SRCS    = \
 	drm_drawable.c \
 	drm_drv.c \
 	drm_fops.c \
+	drm_hashtab.c \
 	drm_ioctl.c \
 	drm_irq.c \
 	drm_lock.c \
 	drm_memory.c \
+	drm_mm.c \
 	drm_pci.c \
 	drm_scatter.c \
+	drm_sman.c \
+	drm_subr_hash.c \
 	drm_sysctl.c \
 	drm_vm.c
 
diff --git a/sys/dev/drm/drmP.h b/sys/dev/drm/drmP.h
index 3f98d20..3ca6fb2 100644
--- a/sys/dev/drm/drmP.h
+++ b/sys/dev/drm/drmP.h
@@ -63,6 +63,8 @@ struct drm_file;
 #include <vm/pmap.h>
 #include <vm/vm_extern.h>
 #include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
 #include <vm/vm_param.h>
 #include <machine/param.h>
 #include <machine/pmap.h>
@@ -141,6 +143,8 @@ MALLOC_DECLARE(DRM_MEM_AGPLISTS);
 MALLOC_DECLARE(DRM_MEM_CTXBITMAP);
 MALLOC_DECLARE(DRM_MEM_SGLISTS);
 MALLOC_DECLARE(DRM_MEM_DRAWABLE);
+MALLOC_DECLARE(DRM_MEM_MM);
+MALLOC_DECLARE(DRM_MEM_HASHTAB);
 
 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
 
@@ -185,6 +189,11 @@ typedef void			irqreturn_t;
 #define IRQ_HANDLED		/* nothing */
 #define IRQ_NONE		/* nothing */
 
+#define unlikely(x)            __builtin_expect(!!(x), 0)
+#define container_of(ptr, type, member) ({			\
+	__typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
 enum {
 	DRM_IS_NOT_AGP,
 	DRM_IS_AGP,
diff --git a/sys/dev/drm/drm_hashtab.c b/sys/dev/drm/drm_hashtab.c
new file mode 100644
index 0000000..e55c76e
--- /dev/null
+++ b/sys/dev/drm/drm_hashtab.c
@@ -0,0 +1,182 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_hashtab.c,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Simple open hash tab implementation.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/drm_hashtab.h"
+
+#if defined(__DragonFly__)
+#include "dev/drm/drm_priv_hash.h"
+#else
+#include <sys/hash.h>
+#endif
+
+int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
+{
+	ht->size = 1 << order;
+	ht->order = order;
+	ht->table = NULL;
+	ht->table = drm_hashinit(ht->size, DRM_MEM_HASHTAB, &ht->mask);
+	if (!ht->table) {
+		DRM_ERROR("Out of memory for hash table\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
+{
+	struct drm_hash_item *entry;
+	struct drm_hash_item_list *h_list;
+	unsigned int hashed_key;
+	int count = 0;
+
+	hashed_key = hash32_buf(&key, sizeof(key), ht->order);
+	DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
+	h_list = &ht->table[hashed_key & ht->mask];
+	LIST_FOREACH(entry, h_list, head)
+		DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
+}
+
+static struct drm_hash_item *
+drm_ht_find_key(struct drm_open_hash *ht, unsigned long key)
+{
+	struct drm_hash_item *entry;
+	struct drm_hash_item_list *h_list;
+	unsigned int hashed_key;
+
+	hashed_key = hash32_buf(&key, sizeof(key), ht->order);
+	h_list = &ht->table[hashed_key & ht->mask];
+	LIST_FOREACH(entry, h_list, head) {
+		if (entry->key == key)
+			return entry;
+		if (entry->key > key)
+			break;
+	}
+	return NULL;
+}
+
+
+int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
+{
+	struct drm_hash_item *entry, *parent;
+	struct drm_hash_item_list *h_list;
+	unsigned int hashed_key;
+	unsigned long key = item->key;
+
+	hashed_key = hash32_buf(&key, sizeof(key), ht->order);
+	h_list = &ht->table[hashed_key & ht->mask];
+	parent = NULL;
+	LIST_FOREACH(entry, h_list, head) {
+		if (entry->key == key)
+			return -EINVAL;
+		if (entry->key > key)
+			break;
+		parent = entry;
+	}
+	if (parent) {
+		LIST_INSERT_AFTER(parent, item, head);
+	} else {
+		LIST_INSERT_HEAD(h_list, item, head);
+	}
+	return 0;
+}
+
+/*
+ * Just insert an item and return any "bits" bit key that hasn't been
+ * used before.
+ */
+int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
+			      unsigned long seed, int bits, int shift,
+			      unsigned long add)
+{
+	int ret;
+	unsigned long mask = (1 << bits) - 1;
+	unsigned long first, unshifted_key = 0;
+
+	unshifted_key = hash32_buf(&seed, sizeof(seed), unshifted_key);
+	first = unshifted_key;
+	do {
+		item->key = (unshifted_key << shift) + add;
+		ret = drm_ht_insert_item(ht, item);
+		if (ret)
+			unshifted_key = (unshifted_key + 1) & mask;
+	} while(ret && (unshifted_key != first));
+
+	if (ret) {
+		DRM_ERROR("Available key bit space exhausted\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
+		     struct drm_hash_item **item)
+{
+	struct drm_hash_item *entry;
+
+	entry = drm_ht_find_key(ht, key);
+	if (!entry)
+		return -EINVAL;
+
+	*item = entry;
+	return 0;
+}
+
+int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
+{
+	struct drm_hash_item *entry;
+
+	entry = drm_ht_find_key(ht, key);
+	if (entry) {
+		LIST_REMOVE(entry, head);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
+{
+	LIST_REMOVE(item, head);
+	return 0;
+}
+
+void drm_ht_remove(struct drm_open_hash *ht)
+{
+	if (ht->table) {
+		drm_hashdestroy(ht->table, DRM_MEM_HASHTAB, ht->mask);
+		ht->table = NULL;
+	}
+}
diff --git a/sys/dev/drm/drm_hashtab.h b/sys/dev/drm/drm_hashtab.h
new file mode 100644
index 0000000..feb0e68
--- /dev/null
+++ b/sys/dev/drm/drm_hashtab.h
@@ -0,0 +1,66 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_hashtab.h,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Simple open hash tab implementation.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef DRM_HASHTAB_H
+#define DRM_HASHTAB_H
+
+#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member)
+
+struct drm_hash_item {
+	LIST_ENTRY(drm_hash_item) head;
+	unsigned long key;
+};
+
+struct drm_open_hash {
+	LIST_HEAD(drm_hash_item_list, drm_hash_item) *table;
+	unsigned int  size;
+	unsigned int order;
+	unsigned long mask;
+};
+
+extern int drm_ht_create(struct drm_open_hash *ht, unsigned int order);
+extern int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item);
+extern int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
+				     unsigned long seed, int bits, int shift,
+				     unsigned long add);
+extern int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, struct drm_hash_item **item);
+
+extern void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key);
+extern int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key);
+extern int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item);
+extern void drm_ht_remove(struct drm_open_hash *ht);
+
+#endif
diff --git a/sys/dev/drm/drm_linux_list.h b/sys/dev/drm/drm_linux_list.h
index 15c1b6e..e5a478a 100644
--- a/sys/dev/drm/drm_linux_list.h
+++ b/sys/dev/drm/drm_linux_list.h
@@ -29,12 +29,17 @@
  *
  */
 
+#include <sys/cdefs.h>
+
+#ifndef _DRM_LINUX_LIST_H_
+#define _DRM_LINUX_LIST_H_
+
 struct list_head {
 	struct list_head *next, *prev;
 };
 
-/* Cheat, assume the list_head is at the start of the struct */
-#define list_entry(entry, type, member)	(type *)(entry)
+#define list_entry(ptr, type, member) container_of(ptr,type,member)
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
 
 static __inline__ void
 INIT_LIST_HEAD(struct list_head *head) {
@@ -48,6 +53,14 @@ list_empty(struct list_head *head) {
 }
 
 static __inline__ void
+list_add(struct list_head *new, struct list_head *head) {
+        (head)->next->prev = new;
+        (new)->next = (head)->next;
+        (new)->prev = head;
+        (head)->next = new;
+}
+
+static __inline__ void
 list_add_tail(struct list_head *entry, struct list_head *head) {
 	(entry)->prev = (head)->prev;
 	(entry)->next = head;
@@ -61,6 +74,13 @@ list_del(struct list_head *entry) {
 	(entry)->prev->next = (entry)->next;
 }
 
+static __inline__ void
+list_del_init(struct list_head *entry) {
+	(entry)->next->prev = (entry)->prev;
+	(entry)->prev->next = (entry)->next;
+	INIT_LIST_HEAD(entry);
+}
+
 #define list_for_each(entry, head)				\
     for (entry = (head)->next; entry != head; entry = (entry)->next)
 
@@ -73,3 +93,17 @@ list_del(struct list_head *entry) {
 	entry != head; 						\
 	entry = temp, temp = entry->next)
 
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:        the type * to use as a loop cursor.
+ * @n:          another type * to use as temporary storage
+ * @head:       the head for your list.
+ * @member:     the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, __typeof(*pos), member),	\
+	    n = list_entry(pos->member.next, __typeof(*pos), member);	\
+	    &pos->member != (head);					\
+	    pos = n, n = list_entry(n->member.next, __typeof(*n), member))
+
+#endif /* _DRM_LINUX_LIST_H_ */
diff --git a/sys/dev/drm/drm_memory.c b/sys/dev/drm/drm_memory.c
index 85c0545..41a641c 100644
--- a/sys/dev/drm/drm_memory.c
+++ b/sys/dev/drm/drm_memory.c
@@ -57,6 +57,8 @@ MALLOC_DEFINE(DRM_MEM_CTXBITMAP, "drm_ctxbitmap",
     "DRM CTXBITMAP Data Structures");
 MALLOC_DEFINE(DRM_MEM_SGLISTS, "drm_sglists", "DRM SGLISTS Data Structures");
 MALLOC_DEFINE(DRM_MEM_DRAWABLE, "drm_drawable", "DRM DRAWABLE Data Structures");
+MALLOC_DEFINE(DRM_MEM_MM, "drm_sman", "DRM MEMORY MANAGER Data Structures");
+MALLOC_DEFINE(DRM_MEM_HASHTAB, "drm_hashtab", "DRM HASHTABLE Data Structures");
 
 void drm_mem_init(void)
 {
diff --git a/sys/dev/drm/drm_mm.c b/sys/dev/drm/drm_mm.c
new file mode 100644
index 0000000..98ce850
--- /dev/null
+++ b/sys/dev/drm/drm_mm.c
@@ -0,0 +1,366 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_mm.c,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Generic simple memory manager implementation. Intended to be used as a base
+ * class implementation for more advanced memory managers.
+ *
+ * Note that the algorithm used is quite simple and there might be substantial
+ * performance gains if a smarter free list is implemented. Currently it is just an
+ * unordered stack of free regions. This could easily be improved if an RB-tree
+ * is used instead. At least if we expect heavy fragmentation.
+ *
+ * Aligned allocations can also see improvement.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/drm_mm.h"
+
+#define MM_UNUSED_TARGET 4
+
+unsigned long drm_mm_tail_space(struct drm_mm *mm)
+{
+	struct list_head *tail_node;
+	struct drm_mm_node *entry;
+
+	tail_node = mm->ml_entry.prev;
+	entry = list_entry(tail_node, struct drm_mm_node, ml_entry);
+	if (!entry->free)
+		return 0;
+
+	return entry->size;
+}
+
+int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size)
+{
+	struct list_head *tail_node;
+	struct drm_mm_node *entry;
+
+	tail_node = mm->ml_entry.prev;
+	entry = list_entry(tail_node, struct drm_mm_node, ml_entry);
+	if (!entry->free)
+		return -ENOMEM;
+
+	if (entry->size <= size)
+		return -ENOMEM;
+
+	entry->size -= size;
+	return 0;
+}
+
+static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
+{
+	struct drm_mm_node *child;
+
+	if (atomic)
+		child = malloc(sizeof(*child), DRM_MEM_MM, M_NOWAIT);
+	else
+		child = malloc(sizeof(*child), DRM_MEM_MM, M_WAITOK);
+
+	if (unlikely(child == NULL)) {
+		DRM_SPINLOCK(&mm->unused_lock);
+		if (list_empty(&mm->unused_nodes))
+			child = NULL;
+		else {
+			child =
+			    list_entry(mm->unused_nodes.next,
+				       struct drm_mm_node, fl_entry);
+			list_del(&child->fl_entry);
+			--mm->num_unused;
+		}
+		DRM_SPINUNLOCK(&mm->unused_lock);
+	}
+	return child;
+}
+
+int drm_mm_pre_get(struct drm_mm *mm)
+{
+	struct drm_mm_node *node;
+
+	DRM_SPINLOCK(&mm->unused_lock);
+	while (mm->num_unused < MM_UNUSED_TARGET) {
+		DRM_SPINUNLOCK(&mm->unused_lock);
+		node = malloc(sizeof(*node), DRM_MEM_MM, M_WAITOK);
+		DRM_SPINLOCK(&mm->unused_lock);
+
+		if (unlikely(node == NULL)) {
+			int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
+			DRM_SPINUNLOCK(&mm->unused_lock);
+			return ret;
+		}
+		++mm->num_unused;
+		list_add_tail(&node->fl_entry, &mm->unused_nodes);
+	}
+	DRM_SPINUNLOCK(&mm->unused_lock);
+	return 0;
+}
+
+static int drm_mm_create_tail_node(struct drm_mm *mm,
+				   unsigned long start,
+				   unsigned long size, int atomic)
+{
+	struct drm_mm_node *child;
+
+	child = drm_mm_kmalloc(mm, atomic);
+	if (unlikely(child == NULL))
+		return -ENOMEM;
+
+	child->free = 1;
+	child->size = size;
+	child->start = start;
+	child->mm = mm;
+
+	list_add_tail(&child->ml_entry, &mm->ml_entry);
+	list_add_tail(&child->fl_entry, &mm->fl_entry);
+
+	return 0;
+}
+
+int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size, int atomic)
+{
+	struct list_head *tail_node;
+	struct drm_mm_node *entry;
+
+	tail_node = mm->ml_entry.prev;
+	entry = list_entry(tail_node, struct drm_mm_node, ml_entry);
+	if (!entry->free) {
+		return drm_mm_create_tail_node(mm, entry->start + entry->size,
+					       size, atomic);
+	}
+	entry->size += size;
+	return 0;
+}
+
+static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent,
+						 unsigned long size,
+						 int atomic)
+{
+	struct drm_mm_node *child;
+
+	child = drm_mm_kmalloc(parent->mm, atomic);
+	if (unlikely(child == NULL))
+		return NULL;
+
+	INIT_LIST_HEAD(&child->fl_entry);
+
+	child->free = 0;
+	child->size = size;
+	child->start = parent->start;
+	child->mm = parent->mm;
+
+	list_add_tail(&child->ml_entry, &parent->ml_entry);
+	INIT_LIST_HEAD(&child->fl_entry);
+
+	parent->size -= size;
+	parent->start += size;
+	return child;
+}
+
+
+struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
+					     unsigned long size,
+					     unsigned alignment,
+					     int atomic)
+{
+
+	struct drm_mm_node *align_splitoff = NULL;
+	unsigned tmp = 0;
+
+	if (alignment)
+		tmp = node->start % alignment;
+
+	if (tmp) {
+		align_splitoff =
+		    drm_mm_split_at_start(node, alignment - tmp, atomic);
+		if (unlikely(align_splitoff == NULL))
+			return NULL;
+	}
+
+	if (node->size == size) {
+		list_del_init(&node->fl_entry);
+		node->free = 0;
+	} else {
+		node = drm_mm_split_at_start(node, size, atomic);
+	}
+
+	if (align_splitoff)
+		drm_mm_put_block(align_splitoff);
+
+	return node;
+}
+
+/*
+ * Put a block. Merge with the previous and / or next block if they are free.
+ * Otherwise add to the free stack.
+ */
+
+void drm_mm_put_block(struct drm_mm_node *cur)
+{
+
+	struct drm_mm *mm = cur->mm;
+	struct list_head *cur_head = &cur->ml_entry;
+	struct list_head *root_head = &mm->ml_entry;
+	struct drm_mm_node *prev_node = NULL;
+	struct drm_mm_node *next_node;
+
+	int merged = 0;
+
+	if (cur_head->prev != root_head) {
+		prev_node =
+		    list_entry(cur_head->prev, struct drm_mm_node, ml_entry);
+		if (prev_node->free) {
+			prev_node->size += cur->size;
+			merged = 1;
+		}
+	}
+	if (cur_head->next != root_head) {
+		next_node =
+		    list_entry(cur_head->next, struct drm_mm_node, ml_entry);
+		if (next_node->free) {
+			if (merged) {
+				prev_node->size += next_node->size;
+				list_del(&next_node->ml_entry);
+				list_del(&next_node->fl_entry);
+				if (mm->num_unused < MM_UNUSED_TARGET) {
+					list_add(&next_node->fl_entry,
+						 &mm->unused_nodes);
+					++mm->num_unused;
+				} else
+					free(next_node, DRM_MEM_MM);
+			} else {
+				next_node->size += cur->size;
+				next_node->start = cur->start;
+				merged = 1;
+			}
+		}
+	}
+	if (!merged) {
+		cur->free = 1;
+		list_add(&cur->fl_entry, &mm->fl_entry);
+	} else {
+		list_del(&cur->ml_entry);
+		if (mm->num_unused < MM_UNUSED_TARGET) {
+			list_add(&cur->fl_entry, &mm->unused_nodes);
+			++mm->num_unused;
+		} else
+			free(cur, DRM_MEM_MM);
+	}
+}
+
+struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+				       unsigned long size,
+				       unsigned alignment, int best_match)
+{
+	struct list_head *list;
+	const struct list_head *free_stack = &mm->fl_entry;
+	struct drm_mm_node *entry;
+	struct drm_mm_node *best;
+	unsigned long best_size;
+	unsigned wasted;
+
+	best = NULL;
+	best_size = ~0UL;
+
+	list_for_each(list, free_stack) {
+		entry = list_entry(list, struct drm_mm_node, fl_entry);
+		wasted = 0;
+
+		if (entry->size < size)
+			continue;
+
+		if (alignment) {
+			register unsigned tmp = entry->start % alignment;
+			if (tmp)
+				wasted += alignment - tmp;
+		}
+
+		if (entry->size >= size + wasted) {
+			if (!best_match)
+				return entry;
+			if (size < best_size) {
+				best = entry;
+				best_size = entry->size;
+			}
+		}
+	}
+
+	return best;
+}
+
+int drm_mm_clean(struct drm_mm * mm)
+{
+	struct list_head *head = &mm->ml_entry;
+
+	return (head->next->next == head);
+}
+
+int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
+{
+	INIT_LIST_HEAD(&mm->ml_entry);
+	INIT_LIST_HEAD(&mm->fl_entry);
+	INIT_LIST_HEAD(&mm->unused_nodes);
+	mm->num_unused = 0;
+	DRM_SPININIT(&mm->unused_lock, "drm_unused");
+
+	return drm_mm_create_tail_node(mm, start, size, 0);
+}
+
+void drm_mm_takedown(struct drm_mm * mm)
+{
+	struct list_head *bnode = mm->fl_entry.next;
+	struct drm_mm_node *entry;
+	struct drm_mm_node *next;
+
+	entry = list_entry(bnode, struct drm_mm_node, fl_entry);
+
+	if (entry->ml_entry.next != &mm->ml_entry ||
+	    entry->fl_entry.next != &mm->fl_entry) {
+		DRM_ERROR("Memory manager not clean. Delaying takedown\n");
+		return;
+	}
+
+	list_del(&entry->fl_entry);
+	list_del(&entry->ml_entry);
+	free(entry, DRM_MEM_MM);
+
+	DRM_SPINLOCK(&mm->unused_lock);
+	list_for_each_entry_safe(entry, next, &mm->unused_nodes, fl_entry) {
+		list_del(&entry->fl_entry);
+		free(entry, DRM_MEM_MM);
+		--mm->num_unused;
+	}
+	DRM_SPINUNLOCK(&mm->unused_lock);
+
+	DRM_SPINUNINIT(&mm->unused_lock);
+
+	KASSERT(mm->num_unused == 0, ("num_unused != 0"));
+}
diff --git a/sys/dev/drm/drm_mm.h b/sys/dev/drm/drm_mm.h
new file mode 100644
index 0000000..f9a9e8f
--- /dev/null
+++ b/sys/dev/drm/drm_mm.h
@@ -0,0 +1,98 @@
+/**************************************************************************
+ *
+ * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_mm.h,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Authors:
+ * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef _DRM_MM_H_
+#define _DRM_MM_H_
+
+#include "dev/drm/drm_linux_list.h"
+
+struct drm_mm_node {
+	struct list_head fl_entry;
+	struct list_head ml_entry;
+	int free;
+	unsigned long start;
+	unsigned long size;
+	struct drm_mm *mm;
+	void *private;
+};
+
+struct drm_mm {
+	struct list_head fl_entry;
+	struct list_head ml_entry;
+	struct list_head unused_nodes;
+	int num_unused;
+	DRM_SPINTYPE unused_lock;
+};
+
+/*
+ * Basic range manager support (drm_mm.c)
+ */
+extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
+						    unsigned long size,
+						    unsigned alignment,
+						    int atomic);
+static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
+						   unsigned long size,
+						   unsigned alignment)
+{
+	return drm_mm_get_block_generic(parent, size, alignment, 0);
+}
+static inline struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
+							  unsigned long size,
+							  unsigned alignment)
+{
+	return drm_mm_get_block_generic(parent, size, alignment, 1);
+}
+extern void drm_mm_put_block(struct drm_mm_node *cur);
+extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+					      unsigned long size,
+					      unsigned alignment,
+					      int best_match);
+extern int drm_mm_init(struct drm_mm *mm, unsigned long start,
+		       unsigned long size);
+extern void drm_mm_takedown(struct drm_mm *mm);
+extern int drm_mm_clean(struct drm_mm *mm);
+extern unsigned long drm_mm_tail_space(struct drm_mm *mm);
+extern int drm_mm_remove_space_from_tail(struct drm_mm *mm,
+					 unsigned long size);
+extern int drm_mm_add_space_to_tail(struct drm_mm *mm,
+				    unsigned long size, int atomic);
+extern int drm_mm_pre_get(struct drm_mm *mm);
+
+static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block)
+{
+	return block->mm;
+}
+
+#endif
diff --git a/sys/dev/drm/drm_pciids.h b/sys/dev/drm/drm_pciids.h
index a2acc89..a6c6a58 100644
--- a/sys/dev/drm/drm_pciids.h
+++ b/sys/dev/drm/drm_pciids.h
@@ -1,5 +1,5 @@
 /*
- * $FreeBSD$
+ * $FreeBSD: src/sys/dev/drm/drm_pciids.h,v 1.21 2010/03/13 11:51:18 rnoland Exp $
  */
 /*
    This file is auto-generated from the drm_pciids.txt in the DRM CVS
@@ -338,6 +338,7 @@
 	{0x1002, 0x9440, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \
 	{0x1002, 0x9441, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4870 X2"}, \
 	{0x1002, 0x9442, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \
+	{0x1002, 0x9443, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4850 X2"}, \
 	{0x1002, 0x944C, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \
 	{0x1002, 0x9450, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9270"}, \
 	{0x1002, 0x9452, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9250"}, \
@@ -548,12 +549,12 @@
 	{0x8086, 0x29B2, CHIP_I9XX|CHIP_I915, "Intel Q35"}, \
 	{0x8086, 0x29D2, CHIP_I9XX|CHIP_I915, "Intel Q33"}, \
 	{0x8086, 0x2A42, CHIP_I9XX|CHIP_I965, "Mobile Intel® GM45 Express Chipset"}, \
-	{0x8086, 0x2E02, CHIP_I9XX|CHIP_I965, "Intel Integrated Graphics Device"}, \
+	{0x8086, 0x2E02, CHIP_I9XX|CHIP_I965, "Intel Eaglelake"}, \
+	{0x8086, 0xA001, CHIP_I9XX|CHIP_I965, "Intel Pineview"}, \
+	{0x8086, 0xA011, CHIP_I9XX|CHIP_I965, "Intel Pineview (M)"}, \
 	{0x8086, 0x2E12, CHIP_I9XX|CHIP_I965, "Intel Q45/Q43"}, \
 	{0x8086, 0x2E22, CHIP_I9XX|CHIP_I965, "Intel G45/G43"}, \
 	{0x8086, 0x2E32, CHIP_I9XX|CHIP_I965, "Intel G41"}, \
-	{0x8086, 0xA001, CHIP_I9XX|CHIP_I965, "Intel IGD"}, \
-	{0x8086, 0xA011, CHIP_I9XX|CHIP_I965, "Intel IGD"}, \
 	{0, 0, 0, NULL}
 
 #define imagine_PCI_IDS \
diff --git a/sys/dev/drm/drm_priv_hash.h b/sys/dev/drm/drm_priv_hash.h
new file mode 100644
index 0000000..bc4e874
--- /dev/null
+++ b/sys/dev/drm/drm_priv_hash.h
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2001 Tobias Weingartner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: hash.h,v 1.4 2004/05/25 18:37:23 jmc Exp $
+ * $FreeBSD: src/sys/sys/hash.h,v 1.3 2007/04/09 22:55:14 thompsa Exp $
+ */
+
+#ifndef _DRM_PRIV_HASH_H
+#define	_DRM_PRIV_HASH_H_
+#include <sys/types.h>
+
+/* Convenience */
+#ifndef	HASHINIT
+#define	HASHINIT	5381
+#define	HASHSTEP(x,c)	(((x << 5) + x) + (c))
+#endif
+
+/*
+ * Return a 32-bit hash of the given buffer.  The init
+ * value should be 0, or the previous hash value to extend
+ * the previous hash.
+ */
+static __inline uint32_t
+hash32_buf(const void *buf, size_t len, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (len--)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string.
+ */
+static __inline uint32_t
+hash32_str(const void *buf, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string, limited by N.
+ */
+static __inline uint32_t
+hash32_strn(const void *buf, size_t len, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && len--)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string terminated by C,
+ * (as well as 0).  This is mainly here as a helper for the
+ * namei() hashing of path name parts.
+ */
+static __inline uint32_t
+hash32_stre(const void *buf, int end, const char **ep, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && (*p != end))
+		hash = HASHSTEP(hash, *p++);
+
+	if (ep)
+		*ep = p;
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string, limited by N,
+ * and terminated by C (as well as 0).  This is mainly here
+ * as a helper for the namei() hashing of path name parts.
+ */
+static __inline uint32_t
+hash32_strne(const void *buf, size_t len, int end, const char **ep,
+    uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && (*p != end) && len--)
+		hash = HASHSTEP(hash, *p++);
+
+	if (ep)
+		*ep = p;
+
+	return hash;
+}
+
+/* Added to act as private header files for hash function */
+
+void *
+drm_hashinit(int elements, struct malloc_type *type, u_long *hashmask);
+
+void
+drm_hashdestroy(void *vhashtbl, struct malloc_type *type, u_long hashmask);
+
+#endif /* !_DRM_PRIV_HASH_H_ */
diff --git a/sys/dev/drm/drm_sman.c b/sys/dev/drm/drm_sman.c
new file mode 100644
index 0000000..620cdc5
--- /dev/null
+++ b/sys/dev/drm/drm_sman.c
@@ -0,0 +1,350 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_sman.c,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Simple memory manager interface that keeps track on allocate regions on a
+ * per "owner" basis. All regions associated with an "owner" can be released
+ * with a simple call. Typically if the "owner" exists. The owner is any
+ * "unsigned long" identifier. Can typically be a pointer to a file private
+ * struct or a context identifier.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/drm_sman.h"
+
+struct drm_owner_item {
+	struct drm_hash_item owner_hash;
+	struct list_head sman_list;
+	struct list_head mem_blocks;
+};
+
+void drm_sman_takedown(struct drm_sman * sman)
+{
+	drm_ht_remove(&sman->user_hash_tab);
+	drm_ht_remove(&sman->owner_hash_tab);
+	if (sman->mm)
+		drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm),
+		    DRM_MEM_MM);
+}
+
+int
+drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
+	      unsigned int user_order, unsigned int owner_order)
+{
+	int ret = 0;
+
+	sman->mm = (struct drm_sman_mm *) drm_calloc(num_managers,
+	    sizeof(*sman->mm), DRM_MEM_MM);
+	if (!sman->mm) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	sman->num_managers = num_managers;
+	INIT_LIST_HEAD(&sman->owner_items);
+	ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
+	if (ret)
+		goto out1;
+	ret = drm_ht_create(&sman->user_hash_tab, user_order);
+	if (!ret)
+		goto out;
+
+	drm_ht_remove(&sman->owner_hash_tab);
+out1:
+	drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM);
+out:
+	return ret;
+}
+
+static void *drm_sman_mm_allocate(void *private, unsigned long size,
+				  unsigned alignment)
+{
+	struct drm_mm *mm = (struct drm_mm *) private;
+	struct drm_mm_node *tmp;
+
+	tmp = drm_mm_search_free(mm, size, alignment, 1);
+	if (!tmp) {
+		return NULL;
+	}
+	tmp = drm_mm_get_block(tmp, size, alignment);
+	return tmp;
+}
+
+static void drm_sman_mm_free(void *private, void *ref)
+{
+	struct drm_mm_node *node = (struct drm_mm_node *) ref;
+
+	drm_mm_put_block(node);
+}
+
+static void drm_sman_mm_destroy(void *private)
+{
+	struct drm_mm *mm = (struct drm_mm *) private;
+	drm_mm_takedown(mm);
+	drm_free(mm, sizeof(*mm), DRM_MEM_MM);
+}
+
+static unsigned long drm_sman_mm_offset(void *private, void *ref)
+{
+	struct drm_mm_node *node = (struct drm_mm_node *) ref;
+	return node->start;
+}
+
+int
+drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
+		   unsigned long start, unsigned long size)
+{
+	struct drm_sman_mm *sman_mm;
+	struct drm_mm *mm;
+	int ret;
+
+	KASSERT(manager < sman->num_managers, ("Invalid manager"));
+
+	sman_mm = &sman->mm[manager];
+	mm = malloc(sizeof(*mm), DRM_MEM_MM, M_WAITOK | M_ZERO);
+	if (!mm) {
+		return -ENOMEM;
+	}
+	sman_mm->private = mm;
+	ret = drm_mm_init(mm, start, size);
+
+	if (ret) {
+		drm_free(mm, sizeof(*mm), DRM_MEM_MM);
+		return ret;
+	}
+
+	sman_mm->allocate = drm_sman_mm_allocate;
+	sman_mm->free = drm_sman_mm_free;
+	sman_mm->destroy = drm_sman_mm_destroy;
+	sman_mm->offset = drm_sman_mm_offset;
+
+	return 0;
+}
+
+int
+drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
+		     struct drm_sman_mm * allocator)
+{
+	KASSERT(manager < sman->num_managers, ("Invalid manager"));
+	sman->mm[manager] = *allocator;
+
+	return 0;
+}
+
+static struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman,
+						 unsigned long owner)
+{
+	int ret;
+	struct drm_hash_item *owner_hash_item;
+	struct drm_owner_item *owner_item;
+
+	ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
+	if (!ret) {
+		return drm_hash_entry(owner_hash_item, struct drm_owner_item,
+				      owner_hash);
+	}
+
+	owner_item = malloc(sizeof(*owner_item), DRM_MEM_MM, M_WAITOK | M_ZERO);
+	if (!owner_item)
+		goto out;
+
+	INIT_LIST_HEAD(&owner_item->mem_blocks);
+	owner_item->owner_hash.key = owner;
+	DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
+	if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
+		goto out1;
+
+	list_add_tail(&owner_item->sman_list, &sman->owner_items);
+	return owner_item;
+
+out1:
+	drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
+out:
+	return NULL;
+}
+
+struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
+				    unsigned long size, unsigned alignment,
+				    unsigned long owner)
+{
+	void *tmp;
+	struct drm_sman_mm *sman_mm;
+	struct drm_owner_item *owner_item;
+	struct drm_memblock_item *memblock;
+
+	KASSERT(manager < sman->num_managers, ("Invalid manager"));
+
+	sman_mm = &sman->mm[manager];
+	tmp = sman_mm->allocate(sman_mm->private, size, alignment);
+
+	if (!tmp) {
+		return NULL;
+	}
+
+	memblock = malloc(sizeof(*memblock), DRM_MEM_MM, M_WAITOK | M_ZERO);
+	DRM_DEBUG("allocated mem_block %p\n", memblock);
+	if (!memblock)
+		goto out;
+
+	memblock->mm_info = tmp;
+	memblock->mm = sman_mm;
+	memblock->sman = sman;
+	INIT_LIST_HEAD(&memblock->owner_list);
+
+	if (drm_ht_just_insert_please
+	    (&sman->user_hash_tab, &memblock->user_hash,
+	     (unsigned long)memblock, 32, 0, 0))
+		goto out1;
+
+	owner_item = drm_sman_get_owner_item(sman, owner);
+	if (!owner_item)
+		goto out2;
+
+	DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
+	DRM_DEBUG("owner_list.prev = %p, mem_blocks.prev = %p\n", memblock->owner_list.prev, owner_item->mem_blocks.prev);
+	DRM_DEBUG("owner_list.next = %p, mem_blocks.next = %p\n", memblock->owner_list.next, owner_item->mem_blocks.next);
+	list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
+
+	DRM_DEBUG("Complete\n");
+	return memblock;
+
+out2:
+	drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
+out1:
+	drm_free(memblock, sizeof(*memblock), DRM_MEM_MM);
+out:
+	sman_mm->free(sman_mm->private, tmp);
+
+	return NULL;
+}
+
+static void drm_sman_free(struct drm_memblock_item *item)
+{
+	struct drm_sman *sman = item->sman;
+
+	list_del(&item->owner_list);
+	drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
+	item->mm->free(item->mm->private, item->mm_info);
+	drm_free(item, sizeof(*item), DRM_MEM_MM);
+}
+
+int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
+{
+	struct drm_hash_item *hash_item;
+	struct drm_memblock_item *memblock_item;
+
+	if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
+		return -EINVAL;
+
+	memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
+				       user_hash);
+	drm_sman_free(memblock_item);
+	return 0;
+}
+
+static void drm_sman_remove_owner(struct drm_sman *sman,
+				  struct drm_owner_item *owner_item)
+{
+	list_del(&owner_item->sman_list);
+	drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
+	drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
+}
+
+int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
+{
+
+	struct drm_hash_item *hash_item;
+	struct drm_owner_item *owner_item;
+
+	if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
+		return -1;
+	}
+
+	owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
+	DRM_DEBUG("cleaning owner_item %p\n", owner_item);
+	if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
+		drm_sman_remove_owner(sman, owner_item);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void drm_sman_do_owner_cleanup(struct drm_sman *sman,
+				      struct drm_owner_item *owner_item)
+{
+	struct drm_memblock_item *entry, *next;
+
+	list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
+				 owner_list) {
+		DRM_DEBUG("freeing mem_block %p\n", entry);
+		drm_sman_free(entry);
+	}
+	drm_sman_remove_owner(sman, owner_item);
+}
+
+void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
+{
+
+	struct drm_hash_item *hash_item;
+	struct drm_owner_item *owner_item;
+
+	if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
+
+		return;
+	}
+
+	owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
+	drm_sman_do_owner_cleanup(sman, owner_item);
+}
+
+void drm_sman_cleanup(struct drm_sman *sman)
+{
+	struct drm_owner_item *entry, *next;
+	unsigned int i;
+	struct drm_sman_mm *sman_mm;
+
+	DRM_DEBUG("sman = %p, owner_items = %p\n",
+	    sman, &sman->owner_items);
+	list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
+		DRM_DEBUG("cleaning owner_item = %p\n", entry);
+		drm_sman_do_owner_cleanup(sman, entry);
+	}
+	if (sman->mm) {
+		for (i = 0; i < sman->num_managers; ++i) {
+			sman_mm = &sman->mm[i];
+			if (sman_mm->private) {
+				sman_mm->destroy(sman_mm->private);
+				sman_mm->private = NULL;
+			}
+		}
+	}
+}
diff --git a/sys/dev/drm/drm_sman.h b/sys/dev/drm/drm_sman.h
new file mode 100644
index 0000000..63df55e
--- /dev/null
+++ b/sys/dev/drm/drm_sman.h
@@ -0,0 +1,179 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/drm_sman.h,v 1.1 2010/01/31 14:25:29 rnoland Exp $");
+ **************************************************************************/
+
+/*
+ * Simple memory MANager interface that keeps track on allocate regions on a
+ * per "owner" basis. All regions associated with an "owner" can be released
+ * with a simple call. Typically if the "owner" exists. The owner is any
+ * "unsigned long" identifier. Can typically be a pointer to a file private
+ * struct or a context identifier.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef DRM_SMAN_H
+#define DRM_SMAN_H
+
+#include "dev/drm/drm_hashtab.h"
+#include "dev/drm/drm_linux_list.h"
+#include "dev/drm/drm_mm.h"
+
+/*
+ * A class that is an abstration of a simple memory allocator.
+ * The sman implementation provides a default such allocator
+ * using the drm_mm.c implementation. But the user can replace it.
+ * See the SiS implementation, which may use the SiS FB kernel module
+ * for memory management.
+ */
+
+struct drm_sman_mm {
+	/* private info. If allocated, needs to be destroyed by the destroy
+	   function */
+	void *private;
+
+	/* Allocate a memory block with given size and alignment.
+	   Return an opaque reference to the memory block */
+
+	void *(*allocate) (void *private, unsigned long size,
+			   unsigned alignment);
+
+	/* Free a memory block. "ref" is the opaque reference that we got from
+	   the "alloc" function */
+
+	void (*free) (void *private, void *ref);
+
+	/* Free all resources associated with this allocator */
+
+	void (*destroy) (void *private);
+
+	/* Return a memory offset from the opaque reference returned from the
+	   "alloc" function */
+
+	unsigned long (*offset) (void *private, void *ref);
+};
+
+struct drm_memblock_item {
+	struct list_head owner_list;
+	struct drm_hash_item user_hash;
+	void *mm_info;
+	struct drm_sman_mm *mm;
+	struct drm_sman *sman;
+};
+
+struct drm_sman {
+	struct drm_sman_mm *mm;
+	int num_managers;
+	struct drm_open_hash owner_hash_tab;
+	struct drm_open_hash user_hash_tab;
+	struct list_head owner_items;
+};
+
+/*
+ * Take down a memory manager. This function should only be called after a
+ * successful init and after a call to drm_sman_cleanup.
+ */
+
+extern void drm_sman_takedown(struct drm_sman * sman);
+
+/*
+ * Allocate structures for a manager.
+ * num_managers are the number of memory pools to manage. (VRAM, AGP, ....)
+ * user_order is the log2 of the number of buckets in the user hash table.
+ *	    set this to approximately log2 of the max number of memory regions
+ *	    that will be allocated for _all_ pools together.
+ * owner_order is the log2 of the number of buckets in the owner hash table.
+ *	    set this to approximately log2 of
+ *	    the number of client file connections that will
+ *	    be using the manager.
+ *
+ */
+
+extern int drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
+			 unsigned int user_order, unsigned int owner_order);
+
+/*
+ * Initialize a drm_mm.c allocator. Should be called only once for each
+ * manager unless a customized allogator is used.
+ */
+
+extern int drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
+			      unsigned long start, unsigned long size);
+
+/*
+ * Initialize a customized allocator for one of the managers.
+ * (See the SiS module). The object pointed to by "allocator" is copied,
+ * so it can be destroyed after this call.
+ */
+
+extern int drm_sman_set_manager(struct drm_sman * sman, unsigned int mananger,
+				struct drm_sman_mm * allocator);
+
+/*
+ * Allocate a memory block. Aligment is not implemented yet.
+ */
+
+extern struct drm_memblock_item *drm_sman_alloc(struct drm_sman * sman,
+						unsigned int manager,
+						unsigned long size,
+						unsigned alignment,
+						unsigned long owner);
+/*
+ * Free a memory block identified by its user hash key.
+ */
+
+extern int drm_sman_free_key(struct drm_sman * sman, unsigned int key);
+
+/*
+ * returns 1 iff there are no stale memory blocks associated with this owner.
+ * Typically called to determine if we need to idle the hardware and call
+ * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all
+ * resources associated with owner.
+ */
+
+extern int drm_sman_owner_clean(struct drm_sman * sman, unsigned long owner);
+
+/*
+ * Frees all stale memory blocks associated with this owner. Note that this
+ * requires that the hardware is finished with all blocks, so the graphics engine
+ * should be idled before this call is made. This function also frees
+ * any resources associated with "owner" and should be called when owner
+ * is not going to be referenced anymore.
+ */
+
+extern void drm_sman_owner_cleanup(struct drm_sman * sman, unsigned long owner);
+
+/*
+ * Frees all stale memory blocks associated with the memory manager.
+ * See idling above.
+ */
+
+extern void drm_sman_cleanup(struct drm_sman * sman);
+
+#endif
diff --git a/sys/dev/drm/drm_subr_hash.c b/sys/dev/drm/drm_subr_hash.c
new file mode 100644
index 0000000..5feb5f4
--- /dev/null
+++ b/sys/dev/drm/drm_subr_hash.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)kern_subr.c	8.3 (Berkeley) 1/21/94
+ * __FBSDID("$FreeBSD: src/sys/kern/subr_hash.c,v 1.1 2010/02/21 19:53:33 ed Exp $");
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/drm_priv_hash.h"
+
+/* From FreeBSD's sys/systm.h */
+#ifndef HASH_NOWAIT
+#define HASH_NOWAIT     0x00000001
+#endif
+#ifndef HASH_WAITOK
+#define HASH_WAITOK     0x00000002
+#endif
+
+/*
+ * General routine to allocate a hash table with control of memory flags.
+ */
+void *
+drm_hashinit_flags(int elements, struct malloc_type *type, u_long *hashmask,
+    int flags)
+{
+	long hashsize;
+	LIST_HEAD(generic, generic) *hashtbl;
+	int i;
+
+	if (elements <= 0)
+		panic("hashinit: bad elements");
+
+	/* Exactly one of HASH_WAITOK and HASH_NOWAIT must be set. */
+	KASSERT((flags & HASH_WAITOK) ^ (flags & HASH_NOWAIT),
+	    ("Bad flags (0x%x) passed to hashinit_flags", flags));
+
+	for (hashsize = 1; hashsize <= elements; hashsize <<= 1)
+		continue;
+	hashsize >>= 1;
+
+	if (flags & HASH_NOWAIT)
+		hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl),
+		    type, M_NOWAIT);
+	else
+		hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl),
+		    type, M_WAITOK);
+
+	if (hashtbl != NULL) {
+		for (i = 0; i < hashsize; i++)
+			LIST_INIT(&hashtbl[i]);
+		*hashmask = hashsize - 1;
+	}
+	return (hashtbl);
+}
+
+/*
+ * Allocate and initialize a hash table with default flag: may sleep.
+ */
+void *
+drm_hashinit(int elements, struct malloc_type *type, u_long *hashmask)
+{
+
+	return (drm_hashinit_flags(elements, type, hashmask, HASH_WAITOK));
+}
+
+void
+drm_hashdestroy(void *vhashtbl, struct malloc_type *type, u_long hashmask)
+{
+	LIST_HEAD(generic, generic) *hashtbl, *hp;
+
+	hashtbl = vhashtbl;
+	for (hp = hashtbl; hp <= &hashtbl[hashmask]; hp++)
+		if (!LIST_EMPTY(hp))
+			panic("hashdestroy: hash not empty");
+	free(hashtbl, type);
+}
+
+static const int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021, 1531,
+			2039, 2557, 3067, 3583, 4093, 4603, 5119, 5623, 6143,
+			6653, 7159, 7673, 8191, 12281, 16381, 24571, 32749 };
+#define NPRIMES (sizeof(primes) / sizeof(primes[0]))
+
+/*
+ * General routine to allocate a prime number sized hash table.
+ */
+void *
+drm_phashinit(int elements, struct malloc_type *type, u_long *nentries)
+{
+	long hashsize;
+	LIST_HEAD(generic, generic) *hashtbl;
+	int i;
+
+	if (elements <= 0)
+		panic("phashinit: bad elements");
+	for (i = 1, hashsize = primes[1]; hashsize <= elements;) {
+		i++;
+		if (i == NPRIMES)
+			break;
+		hashsize = primes[i];
+	}
+	hashsize = primes[i - 1];
+	hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK);
+	for (i = 0; i < hashsize; i++)
+		LIST_INIT(&hashtbl[i]);
+	*nentries = hashsize;
+	return (hashtbl);
+}
diff --git a/sys/dev/drm/i915_drv.c b/sys/dev/drm/i915_drv.c
index 60b37ed..89716c7 100644
--- a/sys/dev/drm/i915_drv.c
+++ b/sys/dev/drm/i915_drv.c
@@ -31,6 +31,7 @@
 
 #include "dev/drm/drmP.h"
 #include "dev/drm/drm.h"
+#include "dev/drm/drm_mm.h"
 #include "dev/drm/i915_drm.h"
 #include "dev/drm/i915_drv.h"
 #include "dev/drm/drm_pciids.h"
diff --git a/sys/dev/drm/i915_drv.h b/sys/dev/drm/i915_drv.h
index 8d907bf..bd75b1f 100644
--- a/sys/dev/drm/i915_drv.h
+++ b/sys/dev/drm/i915_drv.h
@@ -30,6 +30,7 @@
 #ifndef _I915_DRV_H_
 #define _I915_DRV_H_
 
+#include "dev/drm/drm_mm.h"
 #include "dev/drm/i915_reg.h"
 
 /* General customization:
diff --git a/sys/dev/drm/r600_blit.c b/sys/dev/drm/r600_blit.c
new file mode 100644
index 0000000..c34427a
--- /dev/null
+++ b/sys/dev/drm/r600_blit.c
@@ -0,0 +1,1998 @@
+/*-
+ * Copyright 2009 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     Alex Deucher <alexander.deucher@amd.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/r600_blit.c,v 1.5 2009/10/30 18:08:46 rnoland Exp $");
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/drm.h"
+#include "dev/drm/radeon_drm.h"
+#include "dev/drm/radeon_drv.h"
+
+static u32 r6xx_default_state[] =
+{
+	0xc0002400,
+	0x00000000,
+	0xc0012800,
+	0x80000000,
+	0x80000000,
+	0xc0004600,
+	0x00000016,
+	0xc0016800,
+	0x00000010,
+	0x00028000,
+	0xc0016800,
+	0x00000010,
+	0x00008000,
+	0xc0016800,
+	0x00000542,
+	0x07000003,
+	0xc0016800,
+	0x000005c5,
+	0x00000000,
+	0xc0016800,
+	0x00000363,
+	0x00000000,
+	0xc0016800,
+	0x0000060c,
+	0x82000000,
+	0xc0016800,
+	0x0000060e,
+	0x01020204,
+	0xc0016f00,
+	0x00000000,
+	0x00000000,
+	0xc0016f00,
+	0x00000001,
+	0x00000000,
+	0xc0096900,
+	0x0000022a,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x00000004,
+	0x00000000,
+	0xc0016900,
+	0x0000000a,
+	0x00000000,
+	0xc0016900,
+	0x0000000b,
+	0x00000000,
+	0xc0016900,
+	0x0000010c,
+	0x00000000,
+	0xc0016900,
+	0x0000010d,
+	0x00000000,
+	0xc0016900,
+	0x00000200,
+	0x00000000,
+	0xc0016900,
+	0x00000343,
+	0x00000060,
+	0xc0016900,
+	0x00000344,
+	0x00000040,
+	0xc0016900,
+	0x00000351,
+	0x0000aa00,
+	0xc0016900,
+	0x00000104,
+	0x00000000,
+	0xc0016900,
+	0x0000010e,
+	0x00000000,
+	0xc0046900,
+	0x00000105,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0036900,
+	0x00000109,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0046900,
+	0x0000030c,
+	0x01000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0046900,
+	0x00000048,
+	0x3f800000,
+	0x00000000,
+	0x3f800000,
+	0x3f800000,
+	0xc0016900,
+	0x0000008e,
+	0x0000000f,
+	0xc0016900,
+	0x00000080,
+	0x00000000,
+	0xc0016900,
+	0x00000083,
+	0x0000ffff,
+	0xc0016900,
+	0x00000084,
+	0x00000000,
+	0xc0016900,
+	0x00000085,
+	0x20002000,
+	0xc0016900,
+	0x00000086,
+	0x00000000,
+	0xc0016900,
+	0x00000087,
+	0x20002000,
+	0xc0016900,
+	0x00000088,
+	0x00000000,
+	0xc0016900,
+	0x00000089,
+	0x20002000,
+	0xc0016900,
+	0x0000008a,
+	0x00000000,
+	0xc0016900,
+	0x0000008b,
+	0x20002000,
+	0xc0016900,
+	0x0000008c,
+	0x00000000,
+	0xc0016900,
+	0x00000094,
+	0x80000000,
+	0xc0016900,
+	0x00000095,
+	0x20002000,
+	0xc0026900,
+	0x000000b4,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000096,
+	0x80000000,
+	0xc0016900,
+	0x00000097,
+	0x20002000,
+	0xc0026900,
+	0x000000b6,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000098,
+	0x80000000,
+	0xc0016900,
+	0x00000099,
+	0x20002000,
+	0xc0026900,
+	0x000000b8,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009a,
+	0x80000000,
+	0xc0016900,
+	0x0000009b,
+	0x20002000,
+	0xc0026900,
+	0x000000ba,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009c,
+	0x80000000,
+	0xc0016900,
+	0x0000009d,
+	0x20002000,
+	0xc0026900,
+	0x000000bc,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009e,
+	0x80000000,
+	0xc0016900,
+	0x0000009f,
+	0x20002000,
+	0xc0026900,
+	0x000000be,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a0,
+	0x80000000,
+	0xc0016900,
+	0x000000a1,
+	0x20002000,
+	0xc0026900,
+	0x000000c0,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a2,
+	0x80000000,
+	0xc0016900,
+	0x000000a3,
+	0x20002000,
+	0xc0026900,
+	0x000000c2,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a4,
+	0x80000000,
+	0xc0016900,
+	0x000000a5,
+	0x20002000,
+	0xc0026900,
+	0x000000c4,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a6,
+	0x80000000,
+	0xc0016900,
+	0x000000a7,
+	0x20002000,
+	0xc0026900,
+	0x000000c6,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a8,
+	0x80000000,
+	0xc0016900,
+	0x000000a9,
+	0x20002000,
+	0xc0026900,
+	0x000000c8,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000aa,
+	0x80000000,
+	0xc0016900,
+	0x000000ab,
+	0x20002000,
+	0xc0026900,
+	0x000000ca,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000ac,
+	0x80000000,
+	0xc0016900,
+	0x000000ad,
+	0x20002000,
+	0xc0026900,
+	0x000000cc,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000ae,
+	0x80000000,
+	0xc0016900,
+	0x000000af,
+	0x20002000,
+	0xc0026900,
+	0x000000ce,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000b0,
+	0x80000000,
+	0xc0016900,
+	0x000000b1,
+	0x20002000,
+	0xc0026900,
+	0x000000d0,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000b2,
+	0x80000000,
+	0xc0016900,
+	0x000000b3,
+	0x20002000,
+	0xc0026900,
+	0x000000d2,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000293,
+	0x00004010,
+	0xc0016900,
+	0x00000300,
+	0x00000000,
+	0xc0016900,
+	0x00000301,
+	0x00000000,
+	0xc0016900,
+	0x00000312,
+	0xffffffff,
+	0xc0016900,
+	0x00000307,
+	0x00000000,
+	0xc0016900,
+	0x00000308,
+	0x00000000,
+	0xc0016900,
+	0x00000283,
+	0x00000000,
+	0xc0016900,
+	0x00000292,
+	0x00000000,
+	0xc0066900,
+	0x0000010f,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x00000206,
+	0x00000000,
+	0xc0016900,
+	0x00000207,
+	0x00000000,
+	0xc0016900,
+	0x00000208,
+	0x00000000,
+	0xc0046900,
+	0x00000303,
+	0x3f800000,
+	0x3f800000,
+	0x3f800000,
+	0x3f800000,
+	0xc0016900,
+	0x00000205,
+	0x00000004,
+	0xc0016900,
+	0x00000280,
+	0x00000000,
+	0xc0016900,
+	0x00000281,
+	0x00000000,
+	0xc0016900,
+	0x0000037e,
+	0x00000000,
+	0xc0016900,
+	0x00000382,
+	0x00000000,
+	0xc0016900,
+	0x00000380,
+	0x00000000,
+	0xc0016900,
+	0x00000383,
+	0x00000000,
+	0xc0016900,
+	0x00000381,
+	0x00000000,
+	0xc0016900,
+	0x00000282,
+	0x00000008,
+	0xc0016900,
+	0x00000302,
+	0x0000002d,
+	0xc0016900,
+	0x0000037f,
+	0x00000000,
+	0xc0016900,
+	0x000001b2,
+	0x00000000,
+	0xc0016900,
+	0x000001b6,
+	0x00000000,
+	0xc0016900,
+	0x000001b7,
+	0x00000000,
+	0xc0016900,
+	0x000001b8,
+	0x00000000,
+	0xc0016900,
+	0x000001b9,
+	0x00000000,
+	0xc0016900,
+	0x00000225,
+	0x00000000,
+	0xc0016900,
+	0x00000229,
+	0x00000000,
+	0xc0016900,
+	0x00000237,
+	0x00000000,
+	0xc0016900,
+	0x00000100,
+	0x00000800,
+	0xc0016900,
+	0x00000101,
+	0x00000000,
+	0xc0016900,
+	0x00000102,
+	0x00000000,
+	0xc0016900,
+	0x000002a8,
+	0x00000000,
+	0xc0016900,
+	0x000002a9,
+	0x00000000,
+	0xc0016900,
+	0x00000103,
+	0x00000000,
+	0xc0016900,
+	0x00000284,
+	0x00000000,
+	0xc0016900,
+	0x00000290,
+	0x00000000,
+	0xc0016900,
+	0x00000285,
+	0x00000000,
+	0xc0016900,
+	0x00000286,
+	0x00000000,
+	0xc0016900,
+	0x00000287,
+	0x00000000,
+	0xc0016900,
+	0x00000288,
+	0x00000000,
+	0xc0016900,
+	0x00000289,
+	0x00000000,
+	0xc0016900,
+	0x0000028a,
+	0x00000000,
+	0xc0016900,
+	0x0000028b,
+	0x00000000,
+	0xc0016900,
+	0x0000028c,
+	0x00000000,
+	0xc0016900,
+	0x0000028d,
+	0x00000000,
+	0xc0016900,
+	0x0000028e,
+	0x00000000,
+	0xc0016900,
+	0x0000028f,
+	0x00000000,
+	0xc0016900,
+	0x000002a1,
+	0x00000000,
+	0xc0016900,
+	0x000002a5,
+	0x00000000,
+	0xc0016900,
+	0x000002ac,
+	0x00000000,
+	0xc0016900,
+	0x000002ad,
+	0x00000000,
+	0xc0016900,
+	0x000002ae,
+	0x00000000,
+	0xc0016900,
+	0x000002c8,
+	0x00000000,
+	0xc0016900,
+	0x00000206,
+	0x00000100,
+	0xc0016900,
+	0x00000204,
+	0x00010000,
+	0xc0036e00,
+	0x00000000,
+	0x00000012,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x0000008f,
+	0x0000000f,
+	0xc0016900,
+	0x000001e8,
+	0x00000001,
+	0xc0016900,
+	0x00000202,
+	0x00cc0000,
+	0xc0016900,
+	0x00000205,
+	0x00000244,
+	0xc0016900,
+	0x00000203,
+	0x00000210,
+	0xc0016900,
+	0x000001b1,
+	0x00000000,
+	0xc0016900,
+	0x00000185,
+	0x00000000,
+	0xc0016900,
+	0x000001b3,
+	0x00000001,
+	0xc0016900,
+	0x000001b4,
+	0x00000000,
+	0xc0016900,
+	0x00000191,
+	0x00000b00,
+	0xc0016900,
+	0x000001b5,
+	0x00000000,
+};
+
+static u32 r7xx_default_state[] =
+{
+	0xc0012800,
+	0x80000000,
+	0x80000000,
+	0xc0004600,
+	0x00000016,
+	0xc0016800,
+	0x00000010,
+	0x00028000,
+	0xc0016800,
+	0x00000010,
+	0x00008000,
+	0xc0016800,
+	0x00000542,
+	0x07000002,
+	0xc0016800,
+	0x000005c5,
+	0x00000000,
+	0xc0016800,
+	0x00000363,
+	0x00004000,
+	0xc0016800,
+	0x0000060c,
+	0x00000000,
+	0xc0016800,
+	0x0000060e,
+	0x00420204,
+	0xc0016f00,
+	0x00000000,
+	0x00000000,
+	0xc0016f00,
+	0x00000001,
+	0x00000000,
+	0xc0096900,
+	0x0000022a,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x00000004,
+	0x00000000,
+	0xc0016900,
+	0x0000000a,
+	0x00000000,
+	0xc0016900,
+	0x0000000b,
+	0x00000000,
+	0xc0016900,
+	0x0000010c,
+	0x00000000,
+	0xc0016900,
+	0x0000010d,
+	0x00000000,
+	0xc0016900,
+	0x00000200,
+	0x00000000,
+	0xc0016900,
+	0x00000343,
+	0x00000060,
+	0xc0016900,
+	0x00000344,
+	0x00000000,
+	0xc0016900,
+	0x00000351,
+	0x0000aa00,
+	0xc0016900,
+	0x00000104,
+	0x00000000,
+	0xc0016900,
+	0x0000010e,
+	0x00000000,
+	0xc0046900,
+	0x00000105,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0046900,
+	0x0000030c,
+	0x01000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x0000008e,
+	0x0000000f,
+	0xc0016900,
+	0x00000080,
+	0x00000000,
+	0xc0016900,
+	0x00000083,
+	0x0000ffff,
+	0xc0016900,
+	0x00000084,
+	0x00000000,
+	0xc0016900,
+	0x00000085,
+	0x20002000,
+	0xc0016900,
+	0x00000086,
+	0x00000000,
+	0xc0016900,
+	0x00000087,
+	0x20002000,
+	0xc0016900,
+	0x00000088,
+	0x00000000,
+	0xc0016900,
+	0x00000089,
+	0x20002000,
+	0xc0016900,
+	0x0000008a,
+	0x00000000,
+	0xc0016900,
+	0x0000008b,
+	0x20002000,
+	0xc0016900,
+	0x0000008c,
+	0xaaaaaaaa,
+	0xc0016900,
+	0x00000094,
+	0x80000000,
+	0xc0016900,
+	0x00000095,
+	0x20002000,
+	0xc0026900,
+	0x000000b4,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000096,
+	0x80000000,
+	0xc0016900,
+	0x00000097,
+	0x20002000,
+	0xc0026900,
+	0x000000b6,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000098,
+	0x80000000,
+	0xc0016900,
+	0x00000099,
+	0x20002000,
+	0xc0026900,
+	0x000000b8,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009a,
+	0x80000000,
+	0xc0016900,
+	0x0000009b,
+	0x20002000,
+	0xc0026900,
+	0x000000ba,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009c,
+	0x80000000,
+	0xc0016900,
+	0x0000009d,
+	0x20002000,
+	0xc0026900,
+	0x000000bc,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x0000009e,
+	0x80000000,
+	0xc0016900,
+	0x0000009f,
+	0x20002000,
+	0xc0026900,
+	0x000000be,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a0,
+	0x80000000,
+	0xc0016900,
+	0x000000a1,
+	0x20002000,
+	0xc0026900,
+	0x000000c0,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a2,
+	0x80000000,
+	0xc0016900,
+	0x000000a3,
+	0x20002000,
+	0xc0026900,
+	0x000000c2,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a4,
+	0x80000000,
+	0xc0016900,
+	0x000000a5,
+	0x20002000,
+	0xc0026900,
+	0x000000c4,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a6,
+	0x80000000,
+	0xc0016900,
+	0x000000a7,
+	0x20002000,
+	0xc0026900,
+	0x000000c6,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000a8,
+	0x80000000,
+	0xc0016900,
+	0x000000a9,
+	0x20002000,
+	0xc0026900,
+	0x000000c8,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000aa,
+	0x80000000,
+	0xc0016900,
+	0x000000ab,
+	0x20002000,
+	0xc0026900,
+	0x000000ca,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000ac,
+	0x80000000,
+	0xc0016900,
+	0x000000ad,
+	0x20002000,
+	0xc0026900,
+	0x000000cc,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000ae,
+	0x80000000,
+	0xc0016900,
+	0x000000af,
+	0x20002000,
+	0xc0026900,
+	0x000000ce,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000b0,
+	0x80000000,
+	0xc0016900,
+	0x000000b1,
+	0x20002000,
+	0xc0026900,
+	0x000000d0,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x000000b2,
+	0x80000000,
+	0xc0016900,
+	0x000000b3,
+	0x20002000,
+	0xc0026900,
+	0x000000d2,
+	0x00000000,
+	0x3f800000,
+	0xc0016900,
+	0x00000293,
+	0x00514000,
+	0xc0016900,
+	0x00000300,
+	0x00000000,
+	0xc0016900,
+	0x00000301,
+	0x00000000,
+	0xc0016900,
+	0x00000312,
+	0xffffffff,
+	0xc0016900,
+	0x00000307,
+	0x00000000,
+	0xc0016900,
+	0x00000308,
+	0x00000000,
+	0xc0016900,
+	0x00000283,
+	0x00000000,
+	0xc0016900,
+	0x00000292,
+	0x00000000,
+	0xc0066900,
+	0x0000010f,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x00000206,
+	0x00000000,
+	0xc0016900,
+	0x00000207,
+	0x00000000,
+	0xc0016900,
+	0x00000208,
+	0x00000000,
+	0xc0046900,
+	0x00000303,
+	0x3f800000,
+	0x3f800000,
+	0x3f800000,
+	0x3f800000,
+	0xc0016900,
+	0x00000205,
+	0x00000004,
+	0xc0016900,
+	0x00000280,
+	0x00000000,
+	0xc0016900,
+	0x00000281,
+	0x00000000,
+	0xc0016900,
+	0x0000037e,
+	0x00000000,
+	0xc0016900,
+	0x00000382,
+	0x00000000,
+	0xc0016900,
+	0x00000380,
+	0x00000000,
+	0xc0016900,
+	0x00000383,
+	0x00000000,
+	0xc0016900,
+	0x00000381,
+	0x00000000,
+	0xc0016900,
+	0x00000282,
+	0x00000008,
+	0xc0016900,
+	0x00000302,
+	0x0000002d,
+	0xc0016900,
+	0x0000037f,
+	0x00000000,
+	0xc0016900,
+	0x000001b2,
+	0x00000001,
+	0xc0016900,
+	0x000001b6,
+	0x00000000,
+	0xc0016900,
+	0x000001b7,
+	0x00000000,
+	0xc0016900,
+	0x000001b8,
+	0x00000000,
+	0xc0016900,
+	0x000001b9,
+	0x00000000,
+	0xc0016900,
+	0x00000225,
+	0x00000000,
+	0xc0016900,
+	0x00000229,
+	0x00000000,
+	0xc0016900,
+	0x00000237,
+	0x00000000,
+	0xc0016900,
+	0x00000100,
+	0x00000800,
+	0xc0016900,
+	0x00000101,
+	0x00000000,
+	0xc0016900,
+	0x00000102,
+	0x00000000,
+	0xc0016900,
+	0x000002a8,
+	0x00000000,
+	0xc0016900,
+	0x000002a9,
+	0x00000000,
+	0xc0016900,
+	0x00000103,
+	0x00000000,
+	0xc0016900,
+	0x00000284,
+	0x00000000,
+	0xc0016900,
+	0x00000290,
+	0x00000000,
+	0xc0016900,
+	0x00000285,
+	0x00000000,
+	0xc0016900,
+	0x00000286,
+	0x00000000,
+	0xc0016900,
+	0x00000287,
+	0x00000000,
+	0xc0016900,
+	0x00000288,
+	0x00000000,
+	0xc0016900,
+	0x00000289,
+	0x00000000,
+	0xc0016900,
+	0x0000028a,
+	0x00000000,
+	0xc0016900,
+	0x0000028b,
+	0x00000000,
+	0xc0016900,
+	0x0000028c,
+	0x00000000,
+	0xc0016900,
+	0x0000028d,
+	0x00000000,
+	0xc0016900,
+	0x0000028e,
+	0x00000000,
+	0xc0016900,
+	0x0000028f,
+	0x00000000,
+	0xc0016900,
+	0x000002a1,
+	0x00000000,
+	0xc0016900,
+	0x000002a5,
+	0x00000000,
+	0xc0016900,
+	0x000002ac,
+	0x00000000,
+	0xc0016900,
+	0x000002ad,
+	0x00000000,
+	0xc0016900,
+	0x000002ae,
+	0x00000000,
+	0xc0016900,
+	0x000002c8,
+	0x00000000,
+	0xc0016900,
+	0x00000206,
+	0x00000100,
+	0xc0016900,
+	0x00000204,
+	0x00010000,
+	0xc0036e00,
+	0x00000000,
+	0x00000012,
+	0x00000000,
+	0x00000000,
+	0xc0016900,
+	0x0000008f,
+	0x0000000f,
+	0xc0016900,
+	0x000001e8,
+	0x00000001,
+	0xc0016900,
+	0x00000202,
+	0x00cc0000,
+	0xc0016900,
+	0x00000205,
+	0x00000244,
+	0xc0016900,
+	0x00000203,
+	0x00000210,
+	0xc0016900,
+	0x000001b1,
+	0x00000000,
+	0xc0016900,
+	0x00000185,
+	0x00000000,
+	0xc0016900,
+	0x000001b3,
+	0x00000001,
+	0xc0016900,
+	0x000001b4,
+	0x00000000,
+	0xc0016900,
+	0x00000191,
+	0x00000b00,
+	0xc0016900,
+	0x000001b5,
+	0x00000000,
+};
+
+/* same for r6xx/r7xx */
+static u32 r6xx_vs[] =
+{
+	0x00000004,
+	0x81000000,
+	0x0000203c,
+	0x94000b08,
+	0x00004000,
+	0x14200b1a,
+	0x00000000,
+	0x00000000,
+	0x3c000000,
+	0x68cd1000,
+	0x00080000,
+	0x00000000,
+};
+
+static u32 r6xx_ps[] =
+{
+	0x00000002,
+	0x80800000,
+	0x00000000,
+	0x94200688,
+	0x00000010,
+	0x000d1000,
+	0xb0800000,
+	0x00000000,
+};
+
+#define DI_PT_RECTLIST 0x11
+#define DI_INDEX_SIZE_16_BIT 0x0
+#define DI_SRC_SEL_AUTO_INDEX 0x2
+
+#define FMT_8 1
+#define FMT_5_6_5 8
+#define FMT_8_8_8_8 0x1a
+#define COLOR_8 1
+#define COLOR_5_6_5 8
+#define COLOR_8_8_8_8 0x1a
+
+#define R600_CB0_DEST_BASE_ENA (1 << 6)
+#define R600_TC_ACTION_ENA (1 << 23)
+#define R600_VC_ACTION_ENA (1 << 24)
+#define R600_CB_ACTION_ENA (1 << 25)
+#define R600_DB_ACTION_ENA (1 << 26)
+#define R600_SH_ACTION_ENA (1 << 27)
+#define R600_SMX_ACTION_ENA (1 << 28)
+
+#define R600_CB_COLOR0_SIZE 0x28060
+#define R600_CB_COLOR0_VIEW 0x28080
+#define R600_CB_COLOR0_INFO 0x280a0
+#define R600_CB_COLOR0_TILE 0x280c0
+#define R600_CB_COLOR0_FRAG 0x280e0
+#define R600_CB_COLOR0_MASK 0x28100
+
+#define R600_SQ_PGM_START_VS                                   0x28858
+#define R600_SQ_PGM_RESOURCES_VS 0x28868
+#define R600_SQ_PGM_CF_OFFSET_VS 0x288d0
+#define R600_SQ_PGM_START_PS                                   0x28840
+#define R600_SQ_PGM_RESOURCES_PS 0x28850
+#define R600_SQ_PGM_EXPORTS_PS 0x28854
+#define R600_SQ_PGM_CF_OFFSET_PS 0x288cc
+
+#define R600_VGT_PRIMITIVE_TYPE 0x8958
+
+#define R600_PA_SC_SCREEN_SCISSOR_TL 0x28030
+#define R600_PA_SC_GENERIC_SCISSOR_TL 0x28240
+#define R600_PA_SC_WINDOW_SCISSOR_TL 0x28204
+
+#define R600_SQ_TEX_VTX_INVALID_TEXTURE                        0x0
+#define R600_SQ_TEX_VTX_INVALID_BUFFER                         0x1
+#define R600_SQ_TEX_VTX_VALID_TEXTURE                          0x2
+#define R600_SQ_TEX_VTX_VALID_BUFFER                           0x3
+
+/* packet 3 type offsets */
+#define R600_SET_CONFIG_REG_OFFSET                             0x00008000
+#define R600_SET_CONFIG_REG_END                                0x0000ac00
+#define R600_SET_CONTEXT_REG_OFFSET                            0x00028000
+#define R600_SET_CONTEXT_REG_END                               0x00029000
+#define R600_SET_ALU_CONST_OFFSET                              0x00030000
+#define R600_SET_ALU_CONST_END                                 0x00032000
+#define R600_SET_RESOURCE_OFFSET                               0x00038000
+#define R600_SET_RESOURCE_END                                  0x0003c000
+#define R600_SET_SAMPLER_OFFSET                                0x0003c000
+#define R600_SET_SAMPLER_END                                   0x0003cff0
+#define R600_SET_CTL_CONST_OFFSET                              0x0003cff0
+#define R600_SET_CTL_CONST_END                                 0x0003e200
+#define R600_SET_LOOP_CONST_OFFSET                             0x0003e200
+#define R600_SET_LOOP_CONST_END                                0x0003e380
+#define R600_SET_BOOL_CONST_OFFSET                             0x0003e380
+#define R600_SET_BOOL_CONST_END                                0x00040000
+
+/* Packet 3 types */
+#define R600_IT_INDIRECT_BUFFER_END               0x00001700
+#define R600_IT_SET_PREDICATION                   0x00002000
+#define R600_IT_REG_RMW                           0x00002100
+#define R600_IT_COND_EXEC                         0x00002200
+#define R600_IT_PRED_EXEC                         0x00002300
+#define R600_IT_START_3D_CMDBUF                   0x00002400
+#define R600_IT_DRAW_INDEX_2                      0x00002700
+#define R600_IT_CONTEXT_CONTROL                   0x00002800
+#define R600_IT_DRAW_INDEX_IMMD_BE                0x00002900
+#define R600_IT_INDEX_TYPE                        0x00002A00
+#define R600_IT_DRAW_INDEX                        0x00002B00
+#define R600_IT_DRAW_INDEX_AUTO                   0x00002D00
+#define R600_IT_DRAW_INDEX_IMMD                   0x00002E00
+#define R600_IT_NUM_INSTANCES                     0x00002F00
+#define R600_IT_STRMOUT_BUFFER_UPDATE             0x00003400
+#define R600_IT_INDIRECT_BUFFER_MP                0x00003800
+#define R600_IT_MEM_SEMAPHORE                     0x00003900
+#define R600_IT_MPEG_INDEX                        0x00003A00
+#define R600_IT_WAIT_REG_MEM                      0x00003C00
+#define R600_IT_MEM_WRITE                         0x00003D00
+#define R600_IT_INDIRECT_BUFFER                   0x00003200
+#define R600_IT_CP_INTERRUPT                      0x00004000
+#define R600_IT_SURFACE_SYNC                      0x00004300
+#define R600_IT_ME_INITIALIZE                     0x00004400
+#define R600_IT_COND_WRITE                        0x00004500
+#define R600_IT_EVENT_WRITE                       0x00004600
+#define R600_IT_EVENT_WRITE_EOP                   0x00004700
+#define R600_IT_ONE_REG_WRITE                     0x00005700
+#define R600_IT_SET_CONFIG_REG                    0x00006800
+#define R600_IT_SET_CONTEXT_REG                   0x00006900
+#define R600_IT_SET_ALU_CONST                     0x00006A00
+#define R600_IT_SET_BOOL_CONST                    0x00006B00
+#define R600_IT_SET_LOOP_CONST                    0x00006C00
+#define R600_IT_SET_RESOURCE                      0x00006D00
+#define R600_IT_SET_SAMPLER                       0x00006E00
+#define R600_IT_SET_CTL_CONST                     0x00006F00
+#define R600_IT_SURFACE_BASE_UPDATE               0x00007300
+
+static inline void
+set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64 gpu_addr)
+{
+	u32 cb_color_info;
+	int pitch, slice;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	h = (h + 7) & ~7;
+	if (h < 8)
+		h = 8;
+
+	cb_color_info = ((format << 2) | (1 << 27));
+	pitch = (w / 8) - 1;
+	slice = ((w * h) / 64) - 1;
+
+	if (((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_R600) &&
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV770)) {
+		BEGIN_RING(21 + 2);
+		OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+		OUT_RING((R600_CB_COLOR0_BASE - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+		OUT_RING(gpu_addr >> 8);
+		OUT_RING(CP_PACKET3(R600_IT_SURFACE_BASE_UPDATE, 0));
+		OUT_RING(2 << 0);
+	} else {
+		BEGIN_RING(21);
+		OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+		OUT_RING((R600_CB_COLOR0_BASE - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+		OUT_RING(gpu_addr >> 8);
+	}
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_SIZE - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING((pitch << 0) | (slice << 10));
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_VIEW - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_INFO - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(cb_color_info);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_TILE - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_FRAG - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_CB_COLOR0_MASK - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+
+	ADVANCE_RING();
+}
+
+static inline void
+cp_set_surface_sync(drm_radeon_private_t *dev_priv,
+		    u32 sync_type, u32 size, u64 mc_addr)
+{
+	u32 cp_coher_size;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	if (size == 0xffffffff)
+		cp_coher_size = 0xffffffff;
+	else
+		cp_coher_size = ((size + 255) >> 8);
+
+	BEGIN_RING(5);
+	OUT_RING(CP_PACKET3(R600_IT_SURFACE_SYNC, 3));
+	OUT_RING(sync_type);
+	OUT_RING(cp_coher_size);
+	OUT_RING((mc_addr >> 8));
+	OUT_RING(10); /* poll interval */
+	ADVANCE_RING();
+}
+
+static inline void
+set_shaders(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	u64 gpu_addr;
+	int shader_size, i;
+	u32 *vs, *ps;
+	uint32_t sq_pgm_resources;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	/* load shaders */
+	vs = (u32 *) ((char *)dev->agp_buffer_map->handle + dev_priv->blit_vb->offset);
+	ps = (u32 *) ((char *)dev->agp_buffer_map->handle + dev_priv->blit_vb->offset + 256);
+
+	shader_size = sizeof(r6xx_vs) / 4;
+	for (i= 0; i < shader_size; i++)
+		vs[i] = r6xx_vs[i];
+	shader_size = sizeof(r6xx_ps) / 4;
+	for (i= 0; i < shader_size; i++)
+		ps[i] = r6xx_ps[i];
+
+	dev_priv->blit_vb->used = 512;
+
+	gpu_addr = dev_priv->gart_buffers_offset + dev_priv->blit_vb->offset;
+
+	/* setup shader regs */
+	sq_pgm_resources = (1 << 0);
+
+	BEGIN_RING(9 + 12);
+	/* VS */
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_START_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(gpu_addr >> 8);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_RESOURCES_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(sq_pgm_resources);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_CF_OFFSET_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+
+	/* PS */
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_START_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING((gpu_addr + 256) >> 8);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_RESOURCES_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(sq_pgm_resources | (1 << 28));
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_EXPORTS_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(2);
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1));
+        OUT_RING((R600_SQ_PGM_CF_OFFSET_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING(0);
+	ADVANCE_RING();
+
+	cp_set_surface_sync(dev_priv,
+			    R600_SH_ACTION_ENA, 512, gpu_addr);
+}
+
+static inline void
+set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr)
+{
+	uint32_t sq_vtx_constant_word2;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	sq_vtx_constant_word2 = (((gpu_addr >> 32) & 0xff) | (16 << 8));
+
+	BEGIN_RING(9);
+	OUT_RING(CP_PACKET3(R600_IT_SET_RESOURCE, 7));
+	OUT_RING(0x460);
+	OUT_RING(gpu_addr & 0xffffffff);
+	OUT_RING(48 - 1);
+	OUT_RING(sq_vtx_constant_word2);
+	OUT_RING(1 << 0);
+	OUT_RING(0);
+	OUT_RING(0);
+	OUT_RING(R600_SQ_TEX_VTX_VALID_BUFFER << 30);
+	ADVANCE_RING();
+
+	if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710))
+		cp_set_surface_sync(dev_priv,
+				    R600_TC_ACTION_ENA, 48, gpu_addr);
+	else
+		cp_set_surface_sync(dev_priv,
+				    R600_VC_ACTION_ENA, 48, gpu_addr);
+}
+
+static inline void
+set_tex_resource(drm_radeon_private_t *dev_priv,
+		 int format, int w, int h, int pitch, u64 gpu_addr)
+{
+	uint32_t sq_tex_resource_word0, sq_tex_resource_word1, sq_tex_resource_word4;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	if (h < 1)
+		h = 1;
+
+	sq_tex_resource_word0 = (1 << 0);
+	sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 8) |
+				  ((w - 1) << 19));
+
+	sq_tex_resource_word1 = (format << 26);
+	sq_tex_resource_word1 |= ((h - 1) << 0);
+
+	sq_tex_resource_word4 = ((1 << 14) |
+				 (0 << 16) |
+				 (1 << 19) |
+				 (2 << 22) |
+				 (3 << 25));
+
+	BEGIN_RING(9);
+	OUT_RING(CP_PACKET3(R600_IT_SET_RESOURCE, 7));
+	OUT_RING(0);
+	OUT_RING(sq_tex_resource_word0);
+	OUT_RING(sq_tex_resource_word1);
+	OUT_RING(gpu_addr >> 8);
+	OUT_RING(gpu_addr >> 8);
+	OUT_RING(sq_tex_resource_word4);
+	OUT_RING(0);
+	OUT_RING(R600_SQ_TEX_VTX_VALID_TEXTURE << 30);
+	ADVANCE_RING();
+
+}
+
+static inline void
+set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2)
+{
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_RING(12);
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2));
+        OUT_RING((R600_PA_SC_SCREEN_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING((x1 << 0) | (y1 << 16));
+	OUT_RING((x2 << 0) | (y2 << 16));
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2));
+        OUT_RING((R600_PA_SC_GENERIC_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING((x1 << 0) | (y1 << 16) | (1 << 31));
+	OUT_RING((x2 << 0) | (y2 << 16));
+
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2));
+        OUT_RING((R600_PA_SC_WINDOW_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2);
+	OUT_RING((x1 << 0) | (y1 << 16) | (1 << 31));
+	OUT_RING((x2 << 0) | (y2 << 16));
+	ADVANCE_RING();
+}
+
+static inline void
+draw_auto(drm_radeon_private_t *dev_priv)
+{
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_RING(10);
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1));
+        OUT_RING((R600_VGT_PRIMITIVE_TYPE - R600_SET_CONFIG_REG_OFFSET) >> 2);
+	OUT_RING(DI_PT_RECTLIST);
+
+	OUT_RING(CP_PACKET3(R600_IT_INDEX_TYPE, 0));
+	OUT_RING(DI_INDEX_SIZE_16_BIT);
+
+	OUT_RING(CP_PACKET3(R600_IT_NUM_INSTANCES, 0));
+	OUT_RING(1);
+
+	OUT_RING(CP_PACKET3(R600_IT_DRAW_INDEX_AUTO, 1));
+	OUT_RING(3);
+	OUT_RING(DI_SRC_SEL_AUTO_INDEX);
+
+	ADVANCE_RING();
+	COMMIT_RING();
+}
+
+static inline void
+set_default_state(drm_radeon_private_t *dev_priv)
+{
+	int default_state_dw, i;
+	u32 sq_config, sq_gpr_resource_mgmt_1, sq_gpr_resource_mgmt_2;
+	u32 sq_thread_resource_mgmt, sq_stack_resource_mgmt_1, sq_stack_resource_mgmt_2;
+	int num_ps_gprs, num_vs_gprs, num_temp_gprs, num_gs_gprs, num_es_gprs;
+	int num_ps_threads, num_vs_threads, num_gs_threads, num_es_threads;
+	int num_ps_stack_entries, num_vs_stack_entries, num_gs_stack_entries, num_es_stack_entries;
+	RING_LOCALS;
+
+	switch ((dev_priv->flags & RADEON_FAMILY_MASK)) {
+	case CHIP_R600:
+		num_ps_gprs = 192;
+		num_vs_gprs = 56;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 136;
+		num_vs_threads = 48;
+		num_gs_threads = 4;
+		num_es_threads = 4;
+		num_ps_stack_entries = 128;
+		num_vs_stack_entries = 128;
+		num_gs_stack_entries = 0;
+		num_es_stack_entries = 0;
+		break;
+	case CHIP_RV630:
+	case CHIP_RV635:
+		num_ps_gprs = 84;
+		num_vs_gprs = 36;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 144;
+		num_vs_threads = 40;
+		num_gs_threads = 4;
+		num_es_threads = 4;
+		num_ps_stack_entries = 40;
+		num_vs_stack_entries = 40;
+		num_gs_stack_entries = 32;
+		num_es_stack_entries = 16;
+		break;
+	case CHIP_RV610:
+	case CHIP_RV620:
+	case CHIP_RS780:
+	case CHIP_RS880:
+	default:
+		num_ps_gprs = 84;
+		num_vs_gprs = 36;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 136;
+		num_vs_threads = 48;
+		num_gs_threads = 4;
+		num_es_threads = 4;
+		num_ps_stack_entries = 40;
+		num_vs_stack_entries = 40;
+		num_gs_stack_entries = 32;
+		num_es_stack_entries = 16;
+		break;
+	case CHIP_RV670:
+		num_ps_gprs = 144;
+		num_vs_gprs = 40;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 136;
+		num_vs_threads = 48;
+		num_gs_threads = 4;
+		num_es_threads = 4;
+		num_ps_stack_entries = 40;
+		num_vs_stack_entries = 40;
+		num_gs_stack_entries = 32;
+		num_es_stack_entries = 16;
+		break;
+	case CHIP_RV770:
+		num_ps_gprs = 192;
+		num_vs_gprs = 56;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 188;
+		num_vs_threads = 60;
+		num_gs_threads = 0;
+		num_es_threads = 0;
+		num_ps_stack_entries = 256;
+		num_vs_stack_entries = 256;
+		num_gs_stack_entries = 0;
+		num_es_stack_entries = 0;
+		break;
+	case CHIP_RV730:
+	case CHIP_RV740:
+		num_ps_gprs = 84;
+		num_vs_gprs = 36;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 188;
+		num_vs_threads = 60;
+		num_gs_threads = 0;
+		num_es_threads = 0;
+		num_ps_stack_entries = 128;
+		num_vs_stack_entries = 128;
+		num_gs_stack_entries = 0;
+		num_es_stack_entries = 0;
+		break;
+	case CHIP_RV710:
+		num_ps_gprs = 192;
+		num_vs_gprs = 56;
+		num_temp_gprs = 4;
+		num_gs_gprs = 0;
+		num_es_gprs = 0;
+		num_ps_threads = 144;
+		num_vs_threads = 48;
+		num_gs_threads = 0;
+		num_es_threads = 0;
+		num_ps_stack_entries = 128;
+		num_vs_stack_entries = 128;
+		num_gs_stack_entries = 0;
+		num_es_stack_entries = 0;
+		break;
+	}
+
+	if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880) ||
+	    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710))
+		sq_config = 0;
+	else
+		sq_config = R600_VC_ENABLE;
+
+	sq_config |= (R600_DX9_CONSTS |
+		      R600_ALU_INST_PREFER_VECTOR |
+		      R600_PS_PRIO(0) |
+		      R600_VS_PRIO(1) |
+		      R600_GS_PRIO(2) |
+		      R600_ES_PRIO(3));
+
+	sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(num_ps_gprs) |
+				  R600_NUM_VS_GPRS(num_vs_gprs) |
+				  R600_NUM_CLAUSE_TEMP_GPRS(num_temp_gprs));
+	sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(num_gs_gprs) |
+				  R600_NUM_ES_GPRS(num_es_gprs));
+	sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(num_ps_threads) |
+				   R600_NUM_VS_THREADS(num_vs_threads) |
+				   R600_NUM_GS_THREADS(num_gs_threads) |
+				   R600_NUM_ES_THREADS(num_es_threads));
+	sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(num_ps_stack_entries) |
+				    R600_NUM_VS_STACK_ENTRIES(num_vs_stack_entries));
+	sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(num_gs_stack_entries) |
+				    R600_NUM_ES_STACK_ENTRIES(num_es_stack_entries));
+
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) {
+		default_state_dw = sizeof(r7xx_default_state) / 4;
+		BEGIN_RING(default_state_dw + 10);
+		for (i = 0; i < default_state_dw; i++)
+			OUT_RING(r7xx_default_state[i]);
+	} else {
+		default_state_dw = sizeof(r6xx_default_state) / 4;
+		BEGIN_RING(default_state_dw + 10);
+		for (i = 0; i < default_state_dw; i++)
+			OUT_RING(r6xx_default_state[i]);
+	}
+	OUT_RING(CP_PACKET3(R600_IT_EVENT_WRITE, 0));
+	OUT_RING(R600_CACHE_FLUSH_AND_INV_EVENT);
+	/* SQ config */
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 6));
+        OUT_RING((R600_SQ_CONFIG - R600_SET_CONFIG_REG_OFFSET) >> 2);
+	OUT_RING(sq_config);
+	OUT_RING(sq_gpr_resource_mgmt_1);
+	OUT_RING(sq_gpr_resource_mgmt_2);
+	OUT_RING(sq_thread_resource_mgmt);
+	OUT_RING(sq_stack_resource_mgmt_1);
+	OUT_RING(sq_stack_resource_mgmt_2);
+	ADVANCE_RING();
+}
+
+static inline uint32_t i2f(uint32_t input)
+{
+	u32 result, i, exponent, fraction;
+
+	if ((input & 0x3fff) == 0)
+		result = 0; /* 0 is a special case */
+	else {
+		exponent = 140; /* exponent biased by 127; */
+		fraction = (input & 0x3fff) << 10; /* cheat and only
+						      handle numbers below 2^^15 */
+		for (i = 0; i < 14; i++) {
+			if (fraction & 0x800000)
+				break;
+			else {
+				fraction = fraction << 1; /* keep
+							     shifting left until top bit = 1 */
+				exponent = exponent -1;
+			}
+		}
+		result = exponent << 23 | (fraction & 0x7fffff); /* mask
+								    off top bit; assumed 1 */
+	}
+	return result;
+}
+
+int
+r600_prepare_blit_copy(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG("\n");
+
+	dev_priv->blit_vb = radeon_freelist_get(dev);
+	if (!dev_priv->blit_vb) {
+		DRM_ERROR("Unable to allocate vertex buffer for blit\n");
+		return -EAGAIN;
+	}
+
+	set_default_state(dev_priv);
+	set_shaders(dev);
+
+	return 0;
+}
+
+void
+r600_done_blit_copy(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_RING(5);
+	OUT_RING(CP_PACKET3(R600_IT_EVENT_WRITE, 0));
+	OUT_RING(R600_CACHE_FLUSH_AND_INV_EVENT);
+	/* wait for 3D idle clean */
+	OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1));
+	OUT_RING((R600_WAIT_UNTIL - R600_SET_CONFIG_REG_OFFSET) >> 2);
+	OUT_RING(RADEON_WAIT_3D_IDLE | RADEON_WAIT_3D_IDLECLEAN);
+
+	ADVANCE_RING();
+	COMMIT_RING();
+
+	dev_priv->blit_vb->used = 0;
+	radeon_cp_discard_buffer(dev, dev_priv->blit_vb);
+}
+
+void
+r600_blit_copy(struct drm_device *dev,
+	       uint64_t src_gpu_addr, uint64_t dst_gpu_addr,
+	       int size_bytes)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	int max_bytes;
+	u64 vb_addr;
+	u32 *vb;
+
+	vb = (u32 *) ((char *)dev->agp_buffer_map->handle +
+	    dev_priv->blit_vb->offset + dev_priv->blit_vb->used);
+	DRM_DEBUG("src=0x%016llx, dst=0x%016llx, size=%d\n",
+	    (unsigned long long)src_gpu_addr,
+	    (unsigned long long)dst_gpu_addr, size_bytes);
+
+	if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) {
+		max_bytes = 8192;
+
+		while (size_bytes) {
+			int cur_size = size_bytes;
+			int src_x = src_gpu_addr & 255;
+			int dst_x = dst_gpu_addr & 255;
+			int h = 1;
+			src_gpu_addr = src_gpu_addr & ~255;
+			dst_gpu_addr = dst_gpu_addr & ~255;
+
+			if (!src_x && !dst_x) {
+				h = (cur_size / max_bytes);
+				if (h > 8192)
+					h = 8192;
+				if (h == 0)
+					h = 1;
+				else
+					cur_size = max_bytes;
+			} else {
+				if (cur_size > max_bytes)
+					cur_size = max_bytes;
+				if (cur_size > (max_bytes - dst_x))
+					cur_size = (max_bytes - dst_x);
+				if (cur_size > (max_bytes - src_x))
+					cur_size = (max_bytes - src_x);
+			}
+
+			if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) {
+				dev_priv->blit_vb->used = 0;
+				radeon_cp_discard_buffer(dev, dev_priv->blit_vb);
+				dev_priv->blit_vb = radeon_freelist_get(dev);
+				if (!dev_priv->blit_vb)
+					return;
+				set_shaders(dev);
+				vb = (u32 *) ((char *)dev->agp_buffer_map->handle +
+				    dev_priv->blit_vb->offset + dev_priv->blit_vb->used);
+			}
+
+			vb[0] = i2f(dst_x);
+			vb[1] = 0;
+			vb[2] = i2f(src_x);
+			vb[3] = 0;
+
+			vb[4] = i2f(dst_x);
+			vb[5] = i2f(h);
+			vb[6] = i2f(src_x);
+			vb[7] = i2f(h);
+
+			vb[8] = i2f(dst_x + cur_size);
+			vb[9] = i2f(h);
+			vb[10] = i2f(src_x + cur_size);
+			vb[11] = i2f(h);
+
+			/* src */
+			set_tex_resource(dev_priv, FMT_8,
+					 src_x + cur_size, h, src_x + cur_size,
+					 src_gpu_addr);
+
+			cp_set_surface_sync(dev_priv,
+					    R600_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
+
+			/* dst */
+			set_render_target(dev_priv, COLOR_8,
+					  dst_x + cur_size, h,
+					  dst_gpu_addr);
+
+			/* scissors */
+			set_scissors(dev_priv, dst_x, 0, dst_x + cur_size, h);
+
+			/* Vertex buffer setup */
+			vb_addr = dev_priv->gart_buffers_offset +
+                                dev_priv->blit_vb->offset +
+				dev_priv->blit_vb->used;
+			set_vtx_resource(dev_priv, vb_addr);
+
+			/* draw */
+			draw_auto(dev_priv);
+
+			cp_set_surface_sync(dev_priv,
+					    R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA,
+					    cur_size * h, dst_gpu_addr);
+
+			vb += 12;
+			dev_priv->blit_vb->used += 12 * 4;
+
+			src_gpu_addr += cur_size * h;
+			dst_gpu_addr += cur_size * h;
+			size_bytes -= cur_size * h;
+		}
+	} else {
+		max_bytes = 8192 * 4;
+
+		while (size_bytes) {
+			int cur_size = size_bytes;
+			int src_x = (src_gpu_addr & 255);
+			int dst_x = (dst_gpu_addr & 255);
+			int h = 1;
+			src_gpu_addr = src_gpu_addr & ~255;
+			dst_gpu_addr = dst_gpu_addr & ~255;
+
+			if (!src_x && !dst_x) {
+				h = (cur_size / max_bytes);
+				if (h > 8192)
+					h = 8192;
+				if (h == 0)
+					h = 1;
+				else
+					cur_size = max_bytes;
+			} else {
+				if (cur_size > max_bytes)
+				    cur_size = max_bytes;
+				if (cur_size > (max_bytes - dst_x))
+					cur_size = (max_bytes - dst_x);
+				if (cur_size > (max_bytes - src_x))
+					cur_size = (max_bytes - src_x);
+			}
+
+			if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) {
+				dev_priv->blit_vb->used = 0;
+				radeon_cp_discard_buffer(dev, dev_priv->blit_vb);
+				dev_priv->blit_vb = radeon_freelist_get(dev);
+				if (!dev_priv->blit_vb)
+					return;
+				set_shaders(dev);
+				vb = (u32 *) ((char *)dev->agp_buffer_map->handle +
+				    dev_priv->blit_vb->offset + dev_priv->blit_vb->used);
+			}
+
+			vb[0] = i2f(dst_x / 4);
+			vb[1] = 0;
+			vb[2] = i2f(src_x / 4);
+			vb[3] = 0;
+
+			vb[4] = i2f(dst_x / 4);
+			vb[5] = i2f(h);
+			vb[6] = i2f(src_x / 4);
+			vb[7] = i2f(h);
+
+			vb[8] = i2f((dst_x + cur_size) / 4);
+			vb[9] = i2f(h);
+			vb[10] = i2f((src_x + cur_size) / 4);
+			vb[11] = i2f(h);
+
+			/* src */
+			set_tex_resource(dev_priv, FMT_8_8_8_8,
+					 (src_x + cur_size) / 4,
+					 h, (src_x + cur_size) / 4,
+					 src_gpu_addr);
+
+			cp_set_surface_sync(dev_priv,
+					    R600_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
+
+			/* dst */
+			set_render_target(dev_priv, COLOR_8_8_8_8,
+					  (dst_x + cur_size) / 4, h,
+					  dst_gpu_addr);
+
+			/* scissors */
+			set_scissors(dev_priv, (dst_x / 4), 0, (dst_x + cur_size / 4), h);
+
+			/* Vertex buffer setup */
+			vb_addr = dev_priv->gart_buffers_offset +
+                                dev_priv->blit_vb->offset +
+				dev_priv->blit_vb->used;
+			set_vtx_resource(dev_priv, vb_addr);
+
+			/* draw */
+			draw_auto(dev_priv);
+
+			cp_set_surface_sync(dev_priv,
+					    R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA,
+					    cur_size * h, dst_gpu_addr);
+
+			vb += 12;
+			dev_priv->blit_vb->used += 12 * 4;
+
+			src_gpu_addr += cur_size * h;
+			dst_gpu_addr += cur_size * h;
+			size_bytes -= cur_size * h;
+		}
+	}
+}
+
+void
+r600_blit_swap(struct drm_device *dev,
+	       uint64_t src_gpu_addr, uint64_t dst_gpu_addr,
+	       int sx, int sy, int dx, int dy,
+	       int w, int h, int src_pitch, int dst_pitch, int cpp)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	int cb_format, tex_format;
+	int sx2, sy2, dx2, dy2;
+	u64 vb_addr;
+	u32 *vb;
+
+	if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) {
+		dev_priv->blit_vb->used = 0;
+		radeon_cp_discard_buffer(dev, dev_priv->blit_vb);
+		dev_priv->blit_vb = radeon_freelist_get(dev);
+		if (!dev_priv->blit_vb)
+			return;
+		set_shaders(dev);
+	}
+	vb = (u32 *) ((char *)dev->agp_buffer_map->handle +
+	    dev_priv->blit_vb->offset + dev_priv->blit_vb->used);
+
+	sx2 = sx + w;
+	sy2 = sy + h;
+	dx2 = dx + w;
+	dy2 = dy + h;
+
+	vb[0] = i2f(dx);
+	vb[1] = i2f(dy);
+	vb[2] = i2f(sx);
+	vb[3] = i2f(sy);
+
+	vb[4] = i2f(dx);
+	vb[5] = i2f(dy2);
+	vb[6] = i2f(sx);
+	vb[7] = i2f(sy2);
+
+	vb[8] = i2f(dx2);
+	vb[9] = i2f(dy2);
+	vb[10] = i2f(sx2);
+	vb[11] = i2f(sy2);
+
+	switch(cpp) {
+	case 4:
+		cb_format = COLOR_8_8_8_8;
+		tex_format = FMT_8_8_8_8;
+		break;
+	case 2:
+		cb_format = COLOR_5_6_5;
+		tex_format = FMT_5_6_5;
+		break;
+	default:
+		cb_format = COLOR_8;
+		tex_format = FMT_8;
+		break;
+	}
+
+	/* src */
+	set_tex_resource(dev_priv, tex_format,
+			 src_pitch / cpp,
+			 sy2, src_pitch / cpp,
+			 src_gpu_addr);
+
+	cp_set_surface_sync(dev_priv,
+			    R600_TC_ACTION_ENA, src_pitch * sy2, src_gpu_addr);
+
+	/* dst */
+	set_render_target(dev_priv, cb_format,
+			  dst_pitch / cpp, dy2,
+			  dst_gpu_addr);
+
+	/* scissors */
+	set_scissors(dev_priv, dx, dy, dx2, dy2);
+
+	/* Vertex buffer setup */
+	vb_addr = dev_priv->gart_buffers_offset +
+		dev_priv->blit_vb->offset +
+		dev_priv->blit_vb->used;
+	set_vtx_resource(dev_priv, vb_addr);
+
+	/* draw */
+	draw_auto(dev_priv);
+
+	cp_set_surface_sync(dev_priv,
+			    R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA,
+			    dst_pitch * dy2, dst_gpu_addr);
+
+	dev_priv->blit_vb->used += 12 * 4;
+}
diff --git a/sys/dev/drm/r600_cp.c b/sys/dev/drm/r600_cp.c
index 9af4b71..19d44f5 100644
--- a/sys/dev/drm/r600_cp.c
+++ b/sys/dev/drm/r600_cp.c
@@ -24,6 +24,7 @@
  * Authors:
  *     Dave Airlie <airlied@redhat.com>
  *     Alex Deucher <alexander.deucher@amd.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/r600_cp.c,v 1.10 2009/08/23 14:55:57 rnoland Exp $");
  */
 
 #include "dev/drm/drmP.h"
@@ -1840,6 +1841,7 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
 	 */
 	dev_priv->vblank_crtc = DRM_RADEON_VBLANK_CRTC1;
 
+	dev_priv->do_boxes = 0;
 	dev_priv->cp_mode = init->cp_mode;
 
 	/* We don't support anything other than bus-mastering ring mode,
@@ -2097,6 +2099,8 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
 	r600_do_engine_reset(dev);
 	r600_test_writeback(dev_priv);
 
+	r600_cs_init(dev);
+
 	return 0;
 }
 
@@ -2229,3 +2233,135 @@ int r600_cp_dispatch_indirect(struct drm_device *dev,
 
 	return 0;
 }
+
+void r600_cp_dispatch_swap(struct drm_device * dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	struct drm_clip_rect *pbox = sarea_priv->boxes;
+	int i, cpp, src_pitch, dst_pitch;
+	uint64_t src, dst;
+	RING_LOCALS;
+	DRM_DEBUG("\n");
+
+	if (dev_priv->color_fmt == RADEON_COLOR_FORMAT_ARGB8888)
+		cpp = 4;
+	else
+		cpp = 2;
+
+	if (dev_priv->sarea_priv->pfCurrentPage == 0) {
+		src_pitch = dev_priv->back_pitch;
+		dst_pitch = dev_priv->front_pitch;
+		src = dev_priv->back_offset + dev_priv->fb_location;
+		dst = dev_priv->front_offset + dev_priv->fb_location;
+	} else {
+		src_pitch = dev_priv->front_pitch;
+		dst_pitch = dev_priv->back_pitch;
+		src = dev_priv->front_offset + dev_priv->fb_location;
+		dst = dev_priv->back_offset + dev_priv->fb_location;
+	}
+
+	if (r600_prepare_blit_copy(dev)) {
+		DRM_ERROR("unable to allocate vertex buffer for swap buffer\n");
+		return;
+	}
+	for (i = 0; i < nbox; i++) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG("%d,%d-%d,%d\n", x, y, w, h);
+
+		r600_blit_swap(dev,
+			       src, dst,
+			       x, y, x, y, w, h,
+			       src_pitch, dst_pitch, cpp);
+	}
+	r600_done_blit_copy(dev);
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING(3);
+	R600_FRAME_AGE(dev_priv->sarea_priv->last_frame);
+	ADVANCE_RING();
+}
+
+int r600_cp_dispatch_texture(struct drm_device * dev,
+			     struct drm_file *file_priv,
+			     drm_radeon_texture_t * tex,
+			     drm_radeon_tex_image_t * image)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct drm_buf *buf;
+	u32 *buffer;
+	const u8 __user *data;
+	int size, pass_size;
+	u64 src_offset, dst_offset;
+
+	if (!radeon_check_offset(dev_priv, tex->offset)) {
+		DRM_ERROR("Invalid destination offset\n");
+		return -EINVAL;
+	}
+
+	/* this might fail for zero-sized uploads - are those illegal? */
+	if (!radeon_check_offset(dev_priv, tex->offset + tex->height * tex->pitch - 1)) {
+		DRM_ERROR("Invalid final destination offset\n");
+		return -EINVAL;
+	}
+
+	size = tex->height * tex->pitch;
+
+	if (size == 0)
+		return 0;
+
+	dst_offset = tex->offset;
+
+	r600_prepare_blit_copy(dev);
+	do {
+		data = (const u8 __user *)image->data;
+		pass_size = size;
+
+		buf = radeon_freelist_get(dev);
+		if (!buf) {
+			DRM_DEBUG("EAGAIN\n");
+			if (DRM_COPY_TO_USER(tex->image, image, sizeof(*image)))
+				return -EFAULT;
+			return -EAGAIN;
+		}
+
+		if (pass_size > buf->total)
+			pass_size = buf->total;
+
+		/* Dispatch the indirect buffer.
+		 */
+		buffer =
+		    (u32 *) ((char *)dev->agp_buffer_map->handle + buf->offset);
+
+		if (DRM_COPY_FROM_USER(buffer, data, pass_size)) {
+			DRM_ERROR("EFAULT on pad, %d bytes\n", pass_size);
+			return -EFAULT;
+		}
+
+		buf->file_priv = file_priv;
+		buf->used = pass_size;
+		src_offset = dev_priv->gart_buffers_offset + buf->offset;
+
+		r600_blit_copy(dev, src_offset, dst_offset, pass_size);
+
+		radeon_cp_discard_buffer(dev, buf);
+
+		/* Update the input parameters for next time */
+		image->data = (const u8 __user *)image->data + pass_size;
+		dst_offset += pass_size;
+		size -= pass_size;
+	} while (size > 0);
+	r600_done_blit_copy(dev);
+
+	return 0;
+}
diff --git a/sys/dev/drm/radeon/Makefile b/sys/dev/drm/radeon/Makefile
index 31c8f6c..e51c639 100644
--- a/sys/dev/drm/radeon/Makefile
+++ b/sys/dev/drm/radeon/Makefile
@@ -4,7 +4,7 @@
 KMOD	= radeon
 NO_MAN	= YES
 SRCS    = r300_cmdbuf.c r600_cp.c radeon_cp.c radeon_drv.c radeon_state.c \
-	  radeon_irq.c radeon_mem.c
+	  radeon_irq.c radeon_mem.c radeon_cs.c r600_blit.c
 SRCS   += device_if.h bus_if.h pci_if.h opt_drm.h
 CFLAGS += ${DEBUG_FLAGS} -I. -I..
 
diff --git a/sys/dev/drm/radeon_cp.c b/sys/dev/drm/radeon_cp.c
index db5d1fa..ef5cfc8 100644
--- a/sys/dev/drm/radeon_cp.c
+++ b/sys/dev/drm/radeon_cp.c
@@ -26,6 +26,7 @@
  * Authors:
  *    Kevin E. Martin <martin@valinux.com>
  *    Gareth Hughes <gareth@valinux.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_cp.c,v 1.36 2009/10/30 18:07:22 rnoland Exp $");
  */
 
 #include "dev/drm/drmP.h"
@@ -405,6 +406,15 @@ static void radeon_init_pipes(drm_radeon_private_t *dev_priv)
 {
 	uint32_t gb_tile_config, gb_pipe_sel = 0;
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) {
+		uint32_t z_pipe_sel = RADEON_READ(RV530_GB_PIPE_SELECT2);
+		if ((z_pipe_sel & 3) == 3)
+			dev_priv->num_z_pipes = 2;
+		else
+			dev_priv->num_z_pipes = 1;
+	} else
+		dev_priv->num_z_pipes = 1;
+
 	/* RS4xx/RS6xx/R4xx/R5xx */
 	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R420) {
 		gb_pipe_sel = RADEON_READ(R400_GB_PIPE_SELECT);
@@ -1695,6 +1705,10 @@ void radeon_do_release(struct drm_device * dev)
 						    PCATCH | PINTERLOCKED,
 						    "rdnrel", 0);
                 			DRM_LOCK();
+/* DragonFly equivalent of
+ *					mtx_sleep(&ret, &dev->dev_lock, 0,
+ *					    "rdnrel", 1);
+ */
 				}
 			} else {
 				while ((ret = radeon_do_cp_idle(dev_priv)) != 0) {
@@ -1858,8 +1872,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
 	for (t = 0; t < dev_priv->usec_timeout; t++) {
 		u32 done_age = GET_SCRATCH(dev_priv, 1);
 		DRM_DEBUG("done_age = %d\n", done_age);
-		for (i = start; i < dma->buf_count; i++) {
-			buf = dma->buflist[i];
+		for (i = 0; i < dma->buf_count; i++) {
+			buf = dma->buflist[start];
 			buf_priv = buf->dev_private;
 			if (buf->file_priv == NULL || (buf->pending &&
 						       buf_priv->age <=
@@ -1868,7 +1882,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
 				buf->pending = 0;
 				return buf;
 			}
-			start = 0;
+			if (++start >= dma->buf_count)
+				start = 0;
 		}
 
 		if (t) {
@@ -1881,43 +1896,6 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
 	return NULL;
 }
 
-#if 0
-struct drm_buf *radeon_freelist_get(struct drm_device * dev)
-{
-	struct drm_device_dma *dma = dev->dma;
-	drm_radeon_private_t *dev_priv = dev->dev_private;
-	drm_radeon_buf_priv_t *buf_priv;
-	struct drm_buf *buf;
-	int i, t;
-	int start;
-	u32 done_age;
-
-	done_age = radeon_read_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1));
-	if (++dev_priv->last_buf >= dma->buf_count)
-		dev_priv->last_buf = 0;
-
-	start = dev_priv->last_buf;
-	dev_priv->stats.freelist_loops++;
-
-	for (t = 0; t < 2; t++) {
-		for (i = start; i < dma->buf_count; i++) {
-			buf = dma->buflist[i];
-			buf_priv = buf->dev_private;
-			if (buf->file_priv == 0 || (buf->pending &&
-						    buf_priv->age <=
-						    done_age)) {
-				dev_priv->stats.requested_bufs++;
-				buf->pending = 0;
-				return buf;
-			}
-		}
-		start = 0;
-	}
-
-	return NULL;
-}
-#endif
-
 void radeon_freelist_reset(struct drm_device * dev)
 {
 	struct drm_device_dma *dma = dev->dma;
@@ -2067,6 +2045,8 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
 	else
 		dev_priv->flags |= RADEON_IS_PCI;
 
+	DRM_SPININIT(&dev_priv->cs.cs_mutex, "cs_mtx");
+
 	ret = drm_addmap(dev, drm_get_resource_start(dev, 2),
 			 drm_get_resource_len(dev, 2), _DRM_REGISTERS,
 			 _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio);
@@ -2119,6 +2099,8 @@ int radeon_driver_unload(struct drm_device *dev)
 
 	drm_rmmap(dev, dev_priv->mmio);
 
+	DRM_SPINUNINIT(&dev_priv->cs.cs_mutex);
+
 	drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
 
 	dev->dev_private = NULL;
@@ -2133,9 +2115,9 @@ void radeon_commit_ring(drm_radeon_private_t *dev_priv)
 
 	/* check if the ring is padded out to 16-dword alignment */
 
-	tail_aligned = dev_priv->ring.tail & 0xf;
+	tail_aligned = dev_priv->ring.tail & (RADEON_RING_ALIGN - 1);
 	if (tail_aligned) {
-		int num_p2 = 16 - tail_aligned;
+		int num_p2 = RADEON_RING_ALIGN - tail_aligned;
 
 		ring = dev_priv->ring.start;
 		/* pad with some CP_PACKET2 */
diff --git a/sys/dev/drm/radeon_cs.c b/sys/dev/drm/radeon_cs.c
new file mode 100644
index 0000000..e49830d
--- /dev/null
+++ b/sys/dev/drm/radeon_cs.c
@@ -0,0 +1,855 @@
+/*-
+ * Copyright 2008 Jerome Glisse.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Jerome Glisse <glisse@freedesktop.org>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_cs.c,v 1.2 2009/09/28 22:41:28 rnoland Exp $");
+ */
+
+#include "dev/drm/drmP.h"
+#include "dev/drm/radeon_drm.h"
+#include "dev/drm/radeon_drv.h"
+
+/* regs */
+#define AVIVO_D1MODE_VLINE_START_END                           0x6538
+#define AVIVO_D2MODE_VLINE_START_END                           0x6d38
+#define R600_CP_COHER_BASE                                     0x85f8
+#define R600_DB_DEPTH_BASE                                     0x2800c
+#define R600_CB_COLOR0_BASE                                    0x28040
+#define R600_CB_COLOR1_BASE                                    0x28044
+#define R600_CB_COLOR2_BASE                                    0x28048
+#define R600_CB_COLOR3_BASE                                    0x2804c
+#define R600_CB_COLOR4_BASE                                    0x28050
+#define R600_CB_COLOR5_BASE                                    0x28054
+#define R600_CB_COLOR6_BASE                                    0x28058
+#define R600_CB_COLOR7_BASE                                    0x2805c
+#define R600_SQ_PGM_START_FS                                   0x28894
+#define R600_SQ_PGM_START_ES                                   0x28880
+#define R600_SQ_PGM_START_VS                                   0x28858
+#define R600_SQ_PGM_START_GS                                   0x2886c
+#define R600_SQ_PGM_START_PS                                   0x28840
+#define R600_VGT_DMA_BASE                                      0x287e8
+#define R600_VGT_DMA_BASE_HI                                   0x287e4
+#define R600_VGT_STRMOUT_BASE_OFFSET_0                         0x28b10
+#define R600_VGT_STRMOUT_BASE_OFFSET_1                         0x28b14
+#define R600_VGT_STRMOUT_BASE_OFFSET_2                         0x28b18
+#define R600_VGT_STRMOUT_BASE_OFFSET_3                         0x28b1c
+#define R600_VGT_STRMOUT_BASE_OFFSET_HI_0                      0x28b44
+#define R600_VGT_STRMOUT_BASE_OFFSET_HI_1                      0x28b48
+#define R600_VGT_STRMOUT_BASE_OFFSET_HI_2                      0x28b4c
+#define R600_VGT_STRMOUT_BASE_OFFSET_HI_3                      0x28b50
+#define R600_VGT_STRMOUT_BUFFER_BASE_0                         0x28ad8
+#define R600_VGT_STRMOUT_BUFFER_BASE_1                         0x28ae8
+#define R600_VGT_STRMOUT_BUFFER_BASE_2                         0x28af8
+#define R600_VGT_STRMOUT_BUFFER_BASE_3                         0x28b08
+#define R600_VGT_STRMOUT_BUFFER_OFFSET_0                       0x28adc
+#define R600_VGT_STRMOUT_BUFFER_OFFSET_1                       0x28aec
+#define R600_VGT_STRMOUT_BUFFER_OFFSET_2                       0x28afc
+#define R600_VGT_STRMOUT_BUFFER_OFFSET_3                       0x28b0c
+
+/* resource type */
+#define R600_SQ_TEX_VTX_INVALID_TEXTURE                        0x0
+#define R600_SQ_TEX_VTX_INVALID_BUFFER                         0x1
+#define R600_SQ_TEX_VTX_VALID_TEXTURE                          0x2
+#define R600_SQ_TEX_VTX_VALID_BUFFER                           0x3
+
+/* packet 3 type offsets */
+#define R600_SET_CONFIG_REG_OFFSET                             0x00008000
+#define R600_SET_CONFIG_REG_END                                0x0000ac00
+#define R600_SET_CONTEXT_REG_OFFSET                            0x00028000
+#define R600_SET_CONTEXT_REG_END                               0x00029000
+#define R600_SET_ALU_CONST_OFFSET                              0x00030000
+#define R600_SET_ALU_CONST_END                                 0x00032000
+#define R600_SET_RESOURCE_OFFSET                               0x00038000
+#define R600_SET_RESOURCE_END                                  0x0003c000
+#define R600_SET_SAMPLER_OFFSET                                0x0003c000
+#define R600_SET_SAMPLER_END                                   0x0003cff0
+#define R600_SET_CTL_CONST_OFFSET                              0x0003cff0
+#define R600_SET_CTL_CONST_END                                 0x0003e200
+#define R600_SET_LOOP_CONST_OFFSET                             0x0003e200
+#define R600_SET_LOOP_CONST_END                                0x0003e380
+#define R600_SET_BOOL_CONST_OFFSET                             0x0003e380
+#define R600_SET_BOOL_CONST_END                                0x00040000
+
+/* Packet 3 types */
+#define R600_IT_INDIRECT_BUFFER_END               0x00001700
+#define R600_IT_SET_PREDICATION                   0x00002000
+#define R600_IT_REG_RMW                           0x00002100
+#define R600_IT_COND_EXEC                         0x00002200
+#define R600_IT_PRED_EXEC                         0x00002300
+#define R600_IT_START_3D_CMDBUF                   0x00002400
+#define R600_IT_DRAW_INDEX_2                      0x00002700
+#define R600_IT_CONTEXT_CONTROL                   0x00002800
+#define R600_IT_DRAW_INDEX_IMMD_BE                0x00002900
+#define R600_IT_INDEX_TYPE                        0x00002A00
+#define R600_IT_DRAW_INDEX                        0x00002B00
+#define R600_IT_DRAW_INDEX_AUTO                   0x00002D00
+#define R600_IT_DRAW_INDEX_IMMD                   0x00002E00
+#define R600_IT_NUM_INSTANCES                     0x00002F00
+#define R600_IT_STRMOUT_BUFFER_UPDATE             0x00003400
+#define R600_IT_INDIRECT_BUFFER_MP                0x00003800
+#define R600_IT_MEM_SEMAPHORE                     0x00003900
+#define R600_IT_MPEG_INDEX                        0x00003A00
+#define R600_IT_WAIT_REG_MEM                      0x00003C00
+#define R600_IT_MEM_WRITE                         0x00003D00
+#define R600_IT_INDIRECT_BUFFER                   0x00003200
+#define R600_IT_CP_INTERRUPT                      0x00004000
+#define R600_IT_SURFACE_SYNC                      0x00004300
+#define R600_IT_ME_INITIALIZE                     0x00004400
+#define R600_IT_COND_WRITE                        0x00004500
+#define R600_IT_EVENT_WRITE                       0x00004600
+#define R600_IT_EVENT_WRITE_EOP                   0x00004700
+#define R600_IT_ONE_REG_WRITE                     0x00005700
+#define R600_IT_SET_CONFIG_REG                    0x00006800
+#define R600_IT_SET_CONTEXT_REG                   0x00006900
+#define R600_IT_SET_ALU_CONST                     0x00006A00
+#define R600_IT_SET_BOOL_CONST                    0x00006B00
+#define R600_IT_SET_LOOP_CONST                    0x00006C00
+#define R600_IT_SET_RESOURCE                      0x00006D00
+#define R600_IT_SET_SAMPLER                       0x00006E00
+#define R600_IT_SET_CTL_CONST                     0x00006F00
+#define R600_IT_SURFACE_BASE_UPDATE               0x00007300
+
+int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv)
+{
+	struct drm_radeon_cs_parser parser;
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+	struct drm_radeon_cs *cs = data;
+	uint32_t cs_id;
+	struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
+	uint64_t *chunk_array;
+	uint64_t *chunk_array_ptr;
+	long size;
+	int r, i;
+
+	DRM_SPINLOCK(&dev_priv->cs.cs_mutex);
+	/* set command stream id to 0 which is fake id */
+	cs_id = 0;
+	cs->cs_id = cs_id;
+
+	if (dev_priv == NULL) {
+		DRM_ERROR("called with no initialization\n");
+		DRM_SPINUNLOCK(&dev_priv->cs.cs_mutex);
+		return -EINVAL;
+	}
+	if (!cs->num_chunks) {
+		DRM_SPINUNLOCK(&dev_priv->cs.cs_mutex);
+		return 0;
+	}
+
+
+	chunk_array = drm_calloc(cs->num_chunks, sizeof(uint64_t), DRM_MEM_DRIVER);
+	if (!chunk_array) {
+		DRM_SPINUNLOCK(&dev_priv->cs.cs_mutex);
+		return -ENOMEM;
+	}
+
+	chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks);
+
+	if (DRM_COPY_FROM_USER(chunk_array, chunk_array_ptr, sizeof(uint64_t)*cs->num_chunks)) {
+		r = -EFAULT;
+		goto out;
+	}
+
+	parser.dev = dev;
+	parser.file_priv = fpriv;
+	parser.reloc_index = -1;
+	parser.ib_index = -1;
+	parser.num_chunks = cs->num_chunks;
+	/* copy out the chunk headers */
+	parser.chunks = drm_calloc(parser.num_chunks, sizeof(struct drm_radeon_kernel_chunk), DRM_MEM_DRIVER);
+	if (!parser.chunks) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < parser.num_chunks; i++) {
+		struct drm_radeon_cs_chunk user_chunk;
+
+		chunk_ptr = (void __user *)(unsigned long)chunk_array[i];
+
+		if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr, sizeof(struct drm_radeon_cs_chunk))){
+			r = -EFAULT;
+			goto out;
+		}
+		parser.chunks[i].chunk_id = user_chunk.chunk_id;
+
+		if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS)
+			parser.reloc_index = i;
+
+		if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_IB)
+			parser.ib_index = i;
+
+		if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_OLD) {
+			parser.ib_index = i;
+			parser.reloc_index = -1;
+		}
+
+		parser.chunks[i].length_dw = user_chunk.length_dw;
+		parser.chunks[i].chunk_data = (uint32_t *)(unsigned long)user_chunk.chunk_data;
+
+		parser.chunks[i].kdata = NULL;
+		size = parser.chunks[i].length_dw * sizeof(uint32_t);
+
+		switch(parser.chunks[i].chunk_id) {
+		case RADEON_CHUNK_ID_IB:
+		case RADEON_CHUNK_ID_OLD:
+			if (size == 0) {
+				r = -EINVAL;
+				goto out;
+			}
+		case RADEON_CHUNK_ID_RELOCS:
+			if (size) {
+				parser.chunks[i].kdata = drm_alloc(size, DRM_MEM_DRIVER);
+				if (!parser.chunks[i].kdata) {
+					r = -ENOMEM;
+					goto out;
+				}
+
+				if (DRM_COPY_FROM_USER(parser.chunks[i].kdata, parser.chunks[i].chunk_data, size)) {
+					r = -EFAULT;
+					goto out;
+				}
+			} else
+				parser.chunks[i].kdata = NULL;
+			break;
+		default:
+			break;
+		}
+		DRM_DEBUG("chunk %d %d %d %p\n", i, parser.chunks[i].chunk_id, parser.chunks[i].length_dw,
+			  parser.chunks[i].chunk_data);
+	}
+
+	if (parser.chunks[parser.ib_index].length_dw > (16 * 1024)) {
+		DRM_ERROR("cs->dwords too big: %d\n", parser.chunks[parser.ib_index].length_dw);
+		r = -EINVAL;
+		goto out;
+	}
+
+	/* get ib */
+	r = dev_priv->cs.ib_get(&parser);
+	if (r) {
+		DRM_ERROR("ib_get failed\n");
+		goto out;
+	}
+
+	/* now parse command stream */
+	r = dev_priv->cs.parse(&parser);
+	if (r) {
+		goto out;
+	}
+
+out:
+	dev_priv->cs.ib_free(&parser, r);
+
+	/* emit cs id sequence */
+	dev_priv->cs.id_emit(&parser, &cs_id);
+
+	cs->cs_id = cs_id;
+
+	DRM_SPINUNLOCK(&dev_priv->cs.cs_mutex);
+
+	for (i = 0; i < parser.num_chunks; i++) {
+		if (parser.chunks[i].kdata)
+			drm_free(parser.chunks[i].kdata, parser.chunks[i].length_dw * sizeof(uint32_t), DRM_MEM_DRIVER);
+	}
+
+	drm_free(parser.chunks, sizeof(struct drm_radeon_kernel_chunk)*parser.num_chunks, DRM_MEM_DRIVER);
+	drm_free(chunk_array, sizeof(uint64_t)*parser.num_chunks, DRM_MEM_DRIVER);
+
+	return r;
+}
+
+/* for non-mm */
+static int r600_nomm_relocate(struct drm_radeon_cs_parser *parser, uint32_t *reloc, uint64_t *offset)
+{
+	struct drm_device *dev = parser->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct drm_radeon_kernel_chunk *reloc_chunk = &parser->chunks[parser->reloc_index];
+	uint32_t offset_dw = reloc[1];
+
+	//DRM_INFO("reloc: 0x%08x 0x%08x\n", reloc[0], reloc[1]);
+	//DRM_INFO("length: %d\n", reloc_chunk->length_dw);
+
+	if (!reloc_chunk->kdata)
+		return -EINVAL;
+
+	if (offset_dw > reloc_chunk->length_dw) {
+		DRM_ERROR("Offset larger than chunk 0x%x %d\n", offset_dw, reloc_chunk->length_dw);
+		return -EINVAL;
+	}
+
+	/* 40 bit addr */
+	*offset = reloc_chunk->kdata[offset_dw + 3];
+	*offset <<= 32;
+	*offset |= reloc_chunk->kdata[offset_dw + 0];
+
+	//DRM_INFO("offset 0x%lx\n", *offset);
+
+	if (!radeon_check_offset(dev_priv, *offset)) {
+		DRM_ERROR("bad offset! 0x%lx\n", (unsigned long)*offset);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int r600_cs_packet0(struct drm_radeon_cs_parser *parser, uint32_t *offset_dw_p)
+{
+	uint32_t hdr, num_dw, reg;
+	int count_dw = 1;
+	int ret = 0;
+	uint32_t offset_dw = *offset_dw_p;
+	int incr = 2;
+
+	hdr = parser->chunks[parser->ib_index].kdata[offset_dw];
+	num_dw = ((hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16) + 2;
+	reg = (hdr & 0xffff) << 2;
+
+	while (count_dw < num_dw) {
+		switch (reg) {
+		case AVIVO_D1MODE_VLINE_START_END:
+		case AVIVO_D2MODE_VLINE_START_END:
+			break;
+		default:
+			ret = -EINVAL;
+			DRM_ERROR("bad packet 0 reg: 0x%08x\n", reg);
+			break;
+		}
+		if (ret)
+			break;
+		count_dw++;
+		reg += 4;
+	}
+	*offset_dw_p += incr;
+	return ret;
+}
+
+static inline int r600_cs_packet3(struct drm_radeon_cs_parser *parser, uint32_t *offset_dw_p)
+{
+	struct drm_device *dev = parser->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	uint32_t hdr, num_dw, start_reg, end_reg, reg;
+	uint32_t *reloc;
+	uint64_t offset;
+	int ret = 0;
+	uint32_t offset_dw = *offset_dw_p;
+	int incr = 2;
+	int i;
+	struct drm_radeon_kernel_chunk *ib_chunk;
+
+	ib_chunk = &parser->chunks[parser->ib_index];
+
+	hdr = ib_chunk->kdata[offset_dw];
+	num_dw = ((hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16) + 2;
+
+	/* just the ones we use for now, add more later */
+	switch (hdr & 0xff00) {
+	case R600_IT_START_3D_CMDBUF:
+		//DRM_INFO("R600_IT_START_3D_CMDBUF\n");
+		if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770)
+			ret = -EINVAL;
+		if (num_dw != 2)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad START_3D\n");
+		break;
+	case R600_IT_CONTEXT_CONTROL:
+		//DRM_INFO("R600_IT_CONTEXT_CONTROL\n");
+		if (num_dw != 3)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad CONTEXT_CONTROL\n");
+		break;
+	case R600_IT_INDEX_TYPE:
+	case R600_IT_NUM_INSTANCES:
+		//DRM_INFO("R600_IT_INDEX_TYPE/R600_IT_NUM_INSTANCES\n");
+		if (num_dw != 2)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES\n");
+		break;
+	case R600_IT_DRAW_INDEX:
+		//DRM_INFO("R600_IT_DRAW_INDEX\n");
+		if (num_dw != 5) {
+			ret = -EINVAL;
+			DRM_ERROR("bad DRAW_INDEX\n");
+			break;
+		}
+		reloc = ib_chunk->kdata + offset_dw + num_dw;
+		ret = dev_priv->cs.relocate(parser, reloc, &offset);
+		if (ret) {
+			DRM_ERROR("bad DRAW_INDEX\n");
+			break;
+		}
+		ib_chunk->kdata[offset_dw + 1] += (offset & 0xffffffff);
+		ib_chunk->kdata[offset_dw + 2] += (upper_32_bits(offset) & 0xff);
+		break;
+	case R600_IT_DRAW_INDEX_AUTO:
+		//DRM_INFO("R600_IT_DRAW_INDEX_AUTO\n");
+		if (num_dw != 3)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad DRAW_INDEX_AUTO\n");
+		break;
+	case R600_IT_DRAW_INDEX_IMMD_BE:
+	case R600_IT_DRAW_INDEX_IMMD:
+		//DRM_INFO("R600_IT_DRAW_INDEX_IMMD\n");
+		if (num_dw < 4)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad DRAW_INDEX_IMMD\n");
+		break;
+	case R600_IT_WAIT_REG_MEM:
+		//DRM_INFO("R600_IT_WAIT_REG_MEM\n");
+		if (num_dw != 7)
+			ret = -EINVAL;
+		/* bit 4 is reg (0) or mem (1) */
+		if (ib_chunk->kdata[offset_dw + 1] & 0x10) {
+			reloc = ib_chunk->kdata + offset_dw + num_dw;
+			ret = dev_priv->cs.relocate(parser, reloc, &offset);
+			if (ret) {
+				DRM_ERROR("bad WAIT_REG_MEM\n");
+				break;
+			}
+			ib_chunk->kdata[offset_dw + 2] += (offset & 0xffffffff);
+			ib_chunk->kdata[offset_dw + 3] += (upper_32_bits(offset) & 0xff);
+		}
+		if (ret)
+			DRM_ERROR("bad WAIT_REG_MEM\n");
+		break;
+	case R600_IT_SURFACE_SYNC:
+		//DRM_INFO("R600_IT_SURFACE_SYNC\n");
+		if (num_dw != 5)
+			ret = -EINVAL;
+		/* 0xffffffff/0x0 is flush all cache flag */
+		else if ((ib_chunk->kdata[offset_dw + 2] == 0xffffffff) &&
+			 (ib_chunk->kdata[offset_dw + 3] == 0))
+			ret = 0;
+		else {
+			reloc = ib_chunk->kdata + offset_dw + num_dw;
+			ret = dev_priv->cs.relocate(parser, reloc, &offset);
+			if (ret) {
+				DRM_ERROR("bad SURFACE_SYNC\n");
+				break;
+			}
+			ib_chunk->kdata[offset_dw + 3] += ((offset >> 8) & 0xffffffff);
+		}
+		break;
+	case R600_IT_EVENT_WRITE:
+		//DRM_INFO("R600_IT_EVENT_WRITE\n");
+		if ((num_dw != 4) && (num_dw != 2))
+			ret = -EINVAL;
+		if (num_dw > 2) {
+			reloc = ib_chunk->kdata + offset_dw + num_dw;
+			ret = dev_priv->cs.relocate(parser, reloc, &offset);
+			if (ret) {
+				DRM_ERROR("bad EVENT_WRITE\n");
+				break;
+			}
+			ib_chunk->kdata[offset_dw + 2] += (offset & 0xffffffff);
+			ib_chunk->kdata[offset_dw + 3] += (upper_32_bits(offset) & 0xff);
+		}
+		if (ret)
+			DRM_ERROR("bad EVENT_WRITE\n");
+		break;
+	case R600_IT_EVENT_WRITE_EOP:
+		//DRM_INFO("R600_IT_EVENT_WRITE_EOP\n");
+		if (num_dw != 6) {
+			ret = -EINVAL;
+			DRM_ERROR("bad EVENT_WRITE_EOP\n");
+			break;
+		}
+		reloc = ib_chunk->kdata + offset_dw + num_dw;
+		ret = dev_priv->cs.relocate(parser, reloc, &offset);
+		if (ret) {
+			DRM_ERROR("bad EVENT_WRITE_EOP\n");
+			break;
+		}
+		ib_chunk->kdata[offset_dw + 2] += (offset & 0xffffffff);
+		ib_chunk->kdata[offset_dw + 3] += (upper_32_bits(offset) & 0xff);
+		break;
+	case R600_IT_SET_CONFIG_REG:
+		//DRM_INFO("R600_IT_SET_CONFIG_REG\n");
+		start_reg = (ib_chunk->kdata[offset_dw + 1] << 2) + R600_SET_CONFIG_REG_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_CONFIG_REG_OFFSET) ||
+		    (start_reg >= R600_SET_CONFIG_REG_END) ||
+		    (end_reg >= R600_SET_CONFIG_REG_END))
+			ret = -EINVAL;
+		else {
+			for (i = 0; i < (num_dw - 2); i++) {
+				reg = start_reg + (4 * i);
+				switch (reg) {
+				case R600_CP_COHER_BASE:
+					/* use R600_IT_SURFACE_SYNC */
+					ret = -EINVAL;
+					break;
+				default:
+					break;
+				}
+				if (ret)
+					break;
+			}
+		}
+		if (ret)
+			DRM_ERROR("bad SET_CONFIG_REG\n");
+		break;
+	case R600_IT_SET_CONTEXT_REG:
+		//DRM_INFO("R600_IT_SET_CONTEXT_REG\n");
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_CONTEXT_REG_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_CONTEXT_REG_OFFSET) ||
+		    (start_reg >= R600_SET_CONTEXT_REG_END) ||
+		    (end_reg >= R600_SET_CONTEXT_REG_END))
+			ret = -EINVAL;
+		else {
+			for (i = 0; i < (num_dw - 2); i++) {
+				reg = start_reg + (4 * i);
+				switch (reg) {
+				case R600_DB_DEPTH_BASE:
+				case R600_CB_COLOR0_BASE:
+				case R600_CB_COLOR1_BASE:
+				case R600_CB_COLOR2_BASE:
+				case R600_CB_COLOR3_BASE:
+				case R600_CB_COLOR4_BASE:
+				case R600_CB_COLOR5_BASE:
+				case R600_CB_COLOR6_BASE:
+				case R600_CB_COLOR7_BASE:
+				case R600_SQ_PGM_START_FS:
+				case R600_SQ_PGM_START_ES:
+				case R600_SQ_PGM_START_VS:
+				case R600_SQ_PGM_START_GS:
+				case R600_SQ_PGM_START_PS:
+					//DRM_INFO("reg: 0x%08x\n", reg);
+					reloc = ib_chunk->kdata + offset_dw + num_dw + (i * 2);
+					ret = dev_priv->cs.relocate(parser, reloc, &offset);
+					if (ret) {
+						DRM_ERROR("bad SET_CONTEXT_REG\n");
+						break;
+					}
+					ib_chunk->kdata[offset_dw + 2 + i] +=
+						((offset >> 8) & 0xffffffff);
+					break;
+				case R600_VGT_DMA_BASE:
+				case R600_VGT_DMA_BASE_HI:
+					/* These should be handled by DRAW_INDEX packet 3 */
+				case R600_VGT_STRMOUT_BASE_OFFSET_0:
+				case R600_VGT_STRMOUT_BASE_OFFSET_1:
+				case R600_VGT_STRMOUT_BASE_OFFSET_2:
+				case R600_VGT_STRMOUT_BASE_OFFSET_3:
+				case R600_VGT_STRMOUT_BASE_OFFSET_HI_0:
+				case R600_VGT_STRMOUT_BASE_OFFSET_HI_1:
+				case R600_VGT_STRMOUT_BASE_OFFSET_HI_2:
+				case R600_VGT_STRMOUT_BASE_OFFSET_HI_3:
+				case R600_VGT_STRMOUT_BUFFER_BASE_0:
+				case R600_VGT_STRMOUT_BUFFER_BASE_1:
+				case R600_VGT_STRMOUT_BUFFER_BASE_2:
+				case R600_VGT_STRMOUT_BUFFER_BASE_3:
+				case R600_VGT_STRMOUT_BUFFER_OFFSET_0:
+				case R600_VGT_STRMOUT_BUFFER_OFFSET_1:
+				case R600_VGT_STRMOUT_BUFFER_OFFSET_2:
+				case R600_VGT_STRMOUT_BUFFER_OFFSET_3:
+					/* These should be handled by STRMOUT_BUFFER packet 3 */
+					DRM_ERROR("bad context reg: 0x%08x\n", reg);
+					ret = -EINVAL;
+					break;
+				default:
+					break;
+				}
+				if (ret)
+					break;
+			}
+		}
+		if (ret)
+			DRM_ERROR("bad SET_CONTEXT_REG\n");
+		break;
+	case R600_IT_SET_RESOURCE:
+		//DRM_INFO("R600_IT_SET_RESOURCE\n");
+		if ((num_dw - 2) % 7)
+			ret = -EINVAL;
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_RESOURCE_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_RESOURCE_OFFSET) ||
+		    (start_reg >= R600_SET_RESOURCE_END) ||
+		    (end_reg >= R600_SET_RESOURCE_END))
+			ret = -EINVAL;
+		else {
+			for (i = 0; i < ((num_dw - 2) / 7); i++) {
+				switch ((ib_chunk->kdata[offset_dw + (i * 7) + 6 + 2] & 0xc0000000) >> 30) {
+				case R600_SQ_TEX_VTX_INVALID_TEXTURE:
+				case R600_SQ_TEX_VTX_INVALID_BUFFER:
+				default:
+					ret = -EINVAL;
+					break;
+				case R600_SQ_TEX_VTX_VALID_TEXTURE:
+					/* tex base */
+					reloc = ib_chunk->kdata + offset_dw + num_dw + (i * 4);
+					ret = dev_priv->cs.relocate(parser, reloc, &offset);
+					if (ret)
+						break;
+					ib_chunk->kdata[offset_dw + (i * 7) + 2 + 2] +=
+						((offset >> 8) & 0xffffffff);
+					/* tex mip base */
+					reloc = ib_chunk->kdata + offset_dw + num_dw + (i * 4) + 2;
+					ret = dev_priv->cs.relocate(parser, reloc, &offset);
+					if (ret)
+						break;
+					ib_chunk->kdata[offset_dw + (i * 7) + 3 + 2] +=
+						((offset >> 8) & 0xffffffff);
+					break;
+				case R600_SQ_TEX_VTX_VALID_BUFFER:
+					/* vtx base */
+					reloc = ib_chunk->kdata + offset_dw + num_dw + (i * 2);
+					ret = dev_priv->cs.relocate(parser, reloc, &offset);
+					if (ret)
+						break;
+					ib_chunk->kdata[offset_dw + (i * 7) + 0 + 2] += (offset & 0xffffffff);
+					ib_chunk->kdata[offset_dw + (i * 7) + 2 + 2] += (upper_32_bits(offset) & 0xff);
+					break;
+				}
+				if (ret)
+					break;
+			}
+		}
+		if (ret)
+			DRM_ERROR("bad SET_RESOURCE\n");
+		break;
+	case R600_IT_SET_ALU_CONST:
+		//DRM_INFO("R600_IT_SET_ALU_CONST\n");
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_ALU_CONST_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_ALU_CONST_OFFSET) ||
+		    (start_reg >= R600_SET_ALU_CONST_END) ||
+		    (end_reg >= R600_SET_ALU_CONST_END))
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SET_ALU_CONST\n");
+		break;
+	case R600_IT_SET_BOOL_CONST:
+		//DRM_INFO("R600_IT_SET_BOOL_CONST\n");
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_BOOL_CONST_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_BOOL_CONST_OFFSET) ||
+		    (start_reg >= R600_SET_BOOL_CONST_END) ||
+		    (end_reg >= R600_SET_BOOL_CONST_END))
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SET_BOOL_CONST\n");
+		break;
+	case R600_IT_SET_LOOP_CONST:
+		//DRM_INFO("R600_IT_SET_LOOP_CONST\n");
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_LOOP_CONST_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_LOOP_CONST_OFFSET) ||
+		    (start_reg >= R600_SET_LOOP_CONST_END) ||
+		    (end_reg >= R600_SET_LOOP_CONST_END))
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SET_LOOP_CONST\n");
+		break;
+	case R600_IT_SET_CTL_CONST:
+		//DRM_INFO("R600_IT_SET_CTL_CONST\n");
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_CTL_CONST_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_CTL_CONST_OFFSET) ||
+		    (start_reg >= R600_SET_CTL_CONST_END) ||
+		    (end_reg >= R600_SET_CTL_CONST_END))
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SET_CTL_CONST\n");
+		break;
+	case R600_IT_SET_SAMPLER:
+		//DRM_INFO("R600_IT_SET_SAMPLER\n");
+		if ((num_dw - 2) % 3)
+			ret = -EINVAL;
+		start_reg = ib_chunk->kdata[offset_dw + 1] << 2;
+		start_reg += R600_SET_SAMPLER_OFFSET;
+		end_reg = 4 * (num_dw - 2) + start_reg - 4;
+		if ((start_reg < R600_SET_SAMPLER_OFFSET) ||
+		    (start_reg >= R600_SET_SAMPLER_END) ||
+		    (end_reg >= R600_SET_SAMPLER_END))
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SET_SAMPLER\n");
+		break;
+	case R600_IT_SURFACE_BASE_UPDATE:
+		//DRM_INFO("R600_IT_SURFACE_BASE_UPDATE\n");
+		if (((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) ||
+		    ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R600))
+			ret = -EINVAL;
+		if (num_dw != 2)
+			ret = -EINVAL;
+		if (ret)
+			DRM_ERROR("bad SURFACE_BASE_UPDATE\n");
+		break;
+	case RADEON_CP_NOP:
+		//DRM_INFO("NOP: %d\n", ib_chunk->kdata[offset_dw + 1]);
+		break;
+	default:
+		DRM_ERROR("invalid packet 3 0x%08x\n", 0xff00);
+		ret = -EINVAL;
+		break;
+	}
+
+	*offset_dw_p += incr;
+	return ret;
+}
+
+static int r600_cs_parse(struct drm_radeon_cs_parser *parser)
+{
+	volatile int rb;
+	struct drm_radeon_kernel_chunk *ib_chunk;
+	/* scan the packet for various things */
+	int count_dw = 0, size_dw;
+	int ret = 0;
+
+	ib_chunk = &parser->chunks[parser->ib_index];
+	size_dw = ib_chunk->length_dw;
+
+	while (count_dw < size_dw && ret == 0) {
+		int hdr = ib_chunk->kdata[count_dw];
+		int num_dw = (hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16;
+
+		switch (hdr & RADEON_CP_PACKET_MASK) {
+		case RADEON_CP_PACKET0:
+			ret = r600_cs_packet0(parser, &count_dw);
+			break;
+		case RADEON_CP_PACKET1:
+			ret = -EINVAL;
+			break;
+		case RADEON_CP_PACKET2:
+			DRM_DEBUG("Packet 2\n");
+			num_dw += 1;
+			break;
+		case RADEON_CP_PACKET3:
+			ret = r600_cs_packet3(parser, &count_dw);
+			break;
+		}
+
+		count_dw += num_dw;
+	}
+
+	if (ret)
+		return ret;
+
+
+	/* copy the packet into the IB */
+	memcpy(parser->ib, ib_chunk->kdata, ib_chunk->length_dw * sizeof(uint32_t));
+
+	/* read back last byte to flush WC buffers */
+	rb = readl(((vm_offset_t)parser->ib + (ib_chunk->length_dw-1) * sizeof(uint32_t)));
+
+	return 0;
+}
+
+static uint32_t radeon_cs_id_get(struct drm_radeon_private *radeon)
+{
+	/* FIXME: protect with a spinlock */
+	/* FIXME: check if wrap affect last reported wrap & sequence */
+	radeon->cs.id_scnt = (radeon->cs.id_scnt + 1) & 0x00FFFFFF;
+	if (!radeon->cs.id_scnt) {
+		/* increment wrap counter */
+		radeon->cs.id_wcnt += 0x01000000;
+		/* valid sequence counter start at 1 */
+		radeon->cs.id_scnt = 1;
+	}
+	return (radeon->cs.id_scnt | radeon->cs.id_wcnt);
+}
+
+static void r600_cs_id_emit(struct drm_radeon_cs_parser *parser, uint32_t *id)
+{
+	drm_radeon_private_t *dev_priv = parser->dev->dev_private;
+	RING_LOCALS;
+
+	//dev_priv->irq_emitted = radeon_update_breadcrumb(parser->dev);
+
+	*id = radeon_cs_id_get(dev_priv);
+
+	/* SCRATCH 2 */
+	BEGIN_RING(3);
+	R600_CLEAR_AGE(*id);
+	ADVANCE_RING();
+	COMMIT_RING();
+}
+
+static uint32_t r600_cs_id_last_get(struct drm_device *dev)
+{
+	//drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	//return GET_R600_SCRATCH(dev_priv, 2);
+	return 0;
+}
+
+static int r600_ib_get(struct drm_radeon_cs_parser *parser)
+{
+	struct drm_device *dev = parser->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct drm_buf *buf;
+
+	buf = radeon_freelist_get(dev);
+	if (!buf) {
+		dev_priv->cs_buf = NULL;
+		return -EBUSY;
+	}
+	buf->file_priv = parser->file_priv;
+	dev_priv->cs_buf = buf;
+	parser->ib = (void *)((vm_offset_t)dev->agp_buffer_map->handle +
+	    buf->offset);
+
+	return 0;
+}
+
+static void r600_ib_free(struct drm_radeon_cs_parser *parser, int error)
+{
+	struct drm_device *dev = parser->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct drm_buf *buf = dev_priv->cs_buf;
+
+	if (buf) {
+		if (!error)
+			r600_cp_dispatch_indirect(dev, buf, 0,
+						  parser->chunks[parser->ib_index].length_dw * sizeof(uint32_t));
+		radeon_cp_discard_buffer(dev, buf);
+		COMMIT_RING();
+	}
+}
+
+int r600_cs_init(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	dev_priv->cs.ib_get = r600_ib_get;
+	dev_priv->cs.ib_free = r600_ib_free;
+	dev_priv->cs.id_emit = r600_cs_id_emit;
+	dev_priv->cs.id_last_get = r600_cs_id_last_get;
+	dev_priv->cs.parse = r600_cs_parse;
+	dev_priv->cs.relocate = r600_nomm_relocate;
+	return 0;
+}
diff --git a/sys/dev/drm/radeon_drm.h b/sys/dev/drm/radeon_drm.h
index c0d566c..ba6100a 100644
--- a/sys/dev/drm/radeon_drm.h
+++ b/sys/dev/drm/radeon_drm.h
@@ -28,6 +28,7 @@
  *    Kevin E. Martin <martin@valinux.com>
  *    Gareth Hughes <gareth@valinux.com>
  *    Keith Whitwell <keith@tungstengraphics.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_drm.h,v 1.18 2009/08/23 15:02:58 rnoland Exp $");
  */
 
 #ifndef __RADEON_DRM_H__
@@ -494,6 +495,8 @@ typedef struct {
 #define DRM_RADEON_SURF_ALLOC 0x1a
 #define DRM_RADEON_SURF_FREE  0x1b
 
+#define DRM_RADEON_CS         0x26
+
 #define DRM_IOCTL_RADEON_CP_INIT    DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t)
 #define DRM_IOCTL_RADEON_CP_START   DRM_IO(  DRM_COMMAND_BASE + DRM_RADEON_CP_START)
 #define DRM_IOCTL_RADEON_CP_STOP    DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t)
@@ -521,6 +524,7 @@ typedef struct {
 #define DRM_IOCTL_RADEON_SETPARAM   DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t)
 #define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t)
 #define DRM_IOCTL_RADEON_SURF_FREE  DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t)
+#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs)
 
 typedef struct drm_radeon_init {
 	enum {
@@ -682,6 +686,8 @@ typedef struct drm_radeon_indirect {
 #define RADEON_PARAM_VBLANK_CRTC           13   /* VBLANK CRTC */
 #define RADEON_PARAM_FB_LOCATION           14   /* FB location */
 #define RADEON_PARAM_NUM_GB_PIPES          15   /* num GB pipes */
+#define RADEON_PARAM_DEVICE_ID             16
+#define RADEON_PARAM_NUM_Z_PIPES           17   /* num Z pipes */
 
 typedef struct drm_radeon_getparam {
 	int param;
@@ -752,4 +758,23 @@ typedef struct drm_radeon_surface_free {
 #define	DRM_RADEON_VBLANK_CRTC1		1
 #define	DRM_RADEON_VBLANK_CRTC2		2
 
+/* New interface which obsolete all previous interface.
+ */
+#define RADEON_CHUNK_ID_RELOCS 0x01
+#define RADEON_CHUNK_ID_IB     0x02
+#define RADEON_CHUNK_ID_OLD 0xff
+
+struct drm_radeon_cs_chunk {
+	uint32_t chunk_id;
+	uint32_t length_dw;
+	uint64_t chunk_data;
+};
+
+struct drm_radeon_cs {
+	uint32_t        num_chunks;
+	uint32_t        cs_id;
+	uint64_t        chunks; /* this points to uint64_t * which point to
+				   cs chunks */
+};
+
 #endif
diff --git a/sys/dev/drm/radeon_drv.h b/sys/dev/drm/radeon_drv.h
index 511e3eb..33001a2 100644
--- a/sys/dev/drm/radeon_drv.h
+++ b/sys/dev/drm/radeon_drv.h
@@ -26,6 +26,7 @@
  * Authors:
  *    Kevin E. Martin <martin@valinux.com>
  *    Gareth Hughes <gareth@valinux.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_drv.h,v 1.27 2009/09/28 22:40:29 rnoland Exp $");
  */
 
 #ifndef __RADEON_DRV_H__
@@ -38,7 +39,7 @@
 
 #define DRIVER_NAME		"radeon"
 #define DRIVER_DESC		"ATI Radeon"
-#define DRIVER_DATE		"20080528"
+#define DRIVER_DATE		"20080613"
 
 /* Interface history:
  *
@@ -99,9 +100,11 @@
  * 1.27- Add support for IGP GART
  * 1.28- Add support for VBL on CRTC2
  * 1.29- R500 3D cmd buffer support
+ * 1.30- Add support for occlusion queries
+ * 1.31- Add support for num Z pipes from GET_PARAM
  */
 #define DRIVER_MAJOR		1
-#define DRIVER_MINOR		29
+#define DRIVER_MINOR		31
 #define DRIVER_PATCHLEVEL	0
 
 /*
@@ -138,15 +141,15 @@ enum radeon_family {
 	CHIP_R600,
 	CHIP_RV610,
 	CHIP_RV630,
+	CHIP_RV670,
 	CHIP_RV620,
 	CHIP_RV635,
-	CHIP_RV670,
 	CHIP_RS780,
 	CHIP_RS880,
 	CHIP_RV770,
-	CHIP_RV740,
 	CHIP_RV730,
 	CHIP_RV710,
+	CHIP_RV740,
 	CHIP_LAST,
 };
 
@@ -233,6 +236,46 @@ struct radeon_virt_surface {
 #define PCIGART_FILE_PRIV	((void *) -1L)
 };
 
+struct drm_radeon_kernel_chunk {
+	uint32_t chunk_id;
+	uint32_t length_dw;
+	uint32_t __user *chunk_data;
+	uint32_t *kdata;
+};
+
+struct drm_radeon_cs_parser {
+	struct drm_device *dev;
+	struct drm_file *file_priv;
+	uint32_t num_chunks;
+	struct drm_radeon_kernel_chunk *chunks;
+	int ib_index;
+	int reloc_index;
+	uint32_t card_offset;
+	void *ib;
+};
+
+/* command submission struct */
+struct drm_radeon_cs_priv {
+	DRM_SPINTYPE cs_mutex;
+	uint32_t id_wcnt;
+	uint32_t id_scnt;
+	uint32_t id_last_wcnt;
+	uint32_t id_last_scnt;
+
+	int (*parse)(struct drm_radeon_cs_parser *parser);
+	void (*id_emit)(struct drm_radeon_cs_parser *parser, uint32_t *id);
+	uint32_t (*id_last_get)(struct drm_device *dev);
+	/* this ib handling callback are for hidding memory manager drm
+	 * from memory manager less drm, free have to emit ib discard
+	 * sequence into the ring */
+	int (*ib_get)(struct drm_radeon_cs_parser *parser);
+	uint32_t (*ib_get_ptr)(struct drm_device *dev, void *ib);
+	void (*ib_free)(struct drm_radeon_cs_parser *parser, int error);
+	/* do a relocation either MM or non-MM */
+	int (*relocate)(struct drm_radeon_cs_parser *parser,
+			uint32_t *reloc, uint64_t *offset);
+};
+
 #define RADEON_FLUSH_EMITED	(1 << 0)
 #define RADEON_PURGE_EMITED	(1 << 1)
 
@@ -325,6 +368,7 @@ typedef struct drm_radeon_private {
 	unsigned long fb_aper_offset;
 
 	int num_gb_pipes;
+	int num_z_pipes;
 	int track_flush;
 	drm_local_map_t *mmio;
 
@@ -346,6 +390,12 @@ typedef struct drm_radeon_private {
 	int r700_sc_prim_fifo_size;
 	int r700_sc_hiz_tile_fifo_size;
 	int r700_sc_earlyz_tile_fifo_fize;
+	/* r6xx/r7xx drm blit vertex buffer */
+	struct drm_buf *blit_vb;
+
+	/* CS */
+	struct drm_radeon_cs_priv cs;
+	struct drm_buf *cs_buf;
 
 } drm_radeon_private_t;
 
@@ -376,10 +426,10 @@ extern void radeon_set_ring_head(drm_radeon_private_t *dev_priv, u32 val);
 static __inline__ int radeon_check_offset(drm_radeon_private_t *dev_priv,
 					  u64 off)
 {
-	u32 fb_start = dev_priv->fb_location;
-	u32 fb_end = fb_start + dev_priv->fb_size - 1;
-	u32 gart_start = dev_priv->gart_vm_start;
-	u32 gart_end = gart_start + dev_priv->gart_size - 1;
+	u64 fb_start = dev_priv->fb_location;
+	u64 fb_end = fb_start + dev_priv->fb_size - 1;
+	u64 gart_start = dev_priv->gart_vm_start;
+	u64 gart_end = gart_start + dev_priv->gart_size - 1;
 
 	return ((off >= fb_start && off <= fb_end) ||
 		(off >= gart_start && off <= gart_end));
@@ -473,6 +523,33 @@ extern int r600_cp_dispatch_indirect(struct drm_device *dev,
 				     struct drm_buf *buf, int start, int end);
 extern int r600_page_table_init(struct drm_device *dev);
 extern void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info);
+extern void r600_cp_dispatch_swap(struct drm_device * dev);
+extern int r600_cp_dispatch_texture(struct drm_device * dev,
+				    struct drm_file *file_priv,
+				    drm_radeon_texture_t * tex,
+				    drm_radeon_tex_image_t * image);
+
+/* r600_blit.c */
+extern int
+r600_prepare_blit_copy(struct drm_device *dev);
+extern void
+r600_done_blit_copy(struct drm_device *dev);
+extern void
+r600_blit_copy(struct drm_device *dev,
+	       uint64_t src_gpu_addr, uint64_t dst_gpu_addr,
+	       int size_bytes);
+extern void
+r600_blit_swap(struct drm_device *dev,
+	       uint64_t src_gpu_addr, uint64_t dst_gpu_addr,
+	       int sx, int sy, int dx, int dy,
+	       int w, int h, int src_pitch, int dst_pitch, int cpp);
+
+/* radeon_state.c */
+extern void radeon_cp_discard_buffer(struct drm_device * dev, struct drm_buf * buf);
+
+/* radeon_cs.c */
+extern int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv);
+extern int r600_cs_init(struct drm_device *dev);
 
 /* Flags for stats.boxes
  */
@@ -682,6 +759,7 @@ extern void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pciga
 
 /* pipe config regs */
 #define R400_GB_PIPE_SELECT             0x402c
+#define RV530_GB_PIPE_SELECT2           0x4124
 #define R500_DYN_SCLK_PWMEM_PIPE        0x000d /* PLL */
 #define R300_GB_TILE_CONFIG             0x4018
 #       define R300_ENABLE_TILING       (1 << 0)
@@ -1824,26 +1902,38 @@ do {									\
  */
 
 #define RADEON_WAIT_UNTIL_2D_IDLE() do {				\
-	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)        \
+		OUT_RING( CP_PACKET0( R600_WAIT_UNTIL, 0 ) );           \
+	else                                                            \
+		OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );         \
 	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
 		   RADEON_WAIT_HOST_IDLECLEAN) );			\
 } while (0)
 
 #define RADEON_WAIT_UNTIL_3D_IDLE() do {				\
-	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)        \
+		OUT_RING( CP_PACKET0( R600_WAIT_UNTIL, 0 ) );           \
+	else                                                            \
+		OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );         \
 	OUT_RING( (RADEON_WAIT_3D_IDLECLEAN |				\
 		   RADEON_WAIT_HOST_IDLECLEAN) );			\
 } while (0)
 
 #define RADEON_WAIT_UNTIL_IDLE() do {					\
-	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)        \
+		OUT_RING( CP_PACKET0( R600_WAIT_UNTIL, 0 ) );           \
+	else                                                            \
+		OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );         \
 	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
 		   RADEON_WAIT_3D_IDLECLEAN |				\
 		   RADEON_WAIT_HOST_IDLECLEAN) );			\
 } while (0)
 
 #define RADEON_WAIT_UNTIL_PAGE_FLIPPED() do {				\
-	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)        \
+		OUT_RING( CP_PACKET0( R600_WAIT_UNTIL, 0 ) );           \
+	else                                                            \
+		OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );         \
 	OUT_RING( RADEON_WAIT_CRTC_PFLIP );				\
 } while (0)
 
@@ -1958,14 +2048,17 @@ do {								\
 
 #define RING_LOCALS	int write, _nr, _align_nr; unsigned int mask; u32 *ring;
 
+#define RADEON_RING_ALIGN 16
+
 #define BEGIN_RING( n ) do {						\
 	if ( RADEON_VERBOSE ) {						\
 		DRM_INFO( "BEGIN_RING( %d )\n", (n));			\
 	}								\
-	_align_nr = (n + 0xf) & ~0xf;					\
-	if (dev_priv->ring.space <= (_align_nr * sizeof(u32))) {	\
-                COMMIT_RING();						\
-		radeon_wait_ring( dev_priv, _align_nr * sizeof(u32));	\
+	_align_nr = RADEON_RING_ALIGN - ((dev_priv->ring.tail + n) & (RADEON_RING_ALIGN - 1)); \
+	_align_nr += n;							\
+	if ( dev_priv->ring.space <= (_align_nr) * sizeof(u32) ) {	\
+		COMMIT_RING();						\
+		radeon_wait_ring( dev_priv, (_align_nr) * sizeof(u32) ); \
 	}								\
 	_nr = n; dev_priv->ring.space -= (n) * sizeof(u32);		\
 	ring = dev_priv->ring.start;					\
diff --git a/sys/dev/drm/radeon_irq.c b/sys/dev/drm/radeon_irq.c
index 9ff3bfd..8165025 100644
--- a/sys/dev/drm/radeon_irq.c
+++ b/sys/dev/drm/radeon_irq.c
@@ -28,6 +28,7 @@
  * Authors:
  *    Keith Whitwell <keith@tungstengraphics.com>
  *    Michel D�zer <michel@daenzer.net>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_irq.c,v 1.16 2009/09/28 22:37:07 rnoland Exp $");
  */
 
 #include "dev/drm/drmP.h"
@@ -191,6 +192,9 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS)
 	u32 r500_disp_int;
 	u32 tmp;
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		return IRQ_NONE;
+
 	/* Only consider the bits we're interested in - others could be used
 	 * outside the DRM
 	 */
@@ -320,6 +324,9 @@ int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_pr
 	drm_radeon_irq_emit_t *emit = data;
 	int result;
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		return -EINVAL;
+
 	LOCK_TEST_WITH_RETURN(dev, file_priv);
 
 	if (!dev_priv) {
@@ -360,6 +367,9 @@ void radeon_driver_irq_preinstall(struct drm_device * dev)
 	    (drm_radeon_private_t *) dev->dev_private;
 	u32 dummy;
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		return;
+
 	/* Disable *all* interrupts */
 	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600)
 		RADEON_WRITE(R500_DxMODE_INT_MASK, 0);
@@ -377,6 +387,9 @@ int radeon_driver_irq_postinstall(struct drm_device * dev)
 	atomic_set(&dev_priv->swi_emitted, 0);
 	DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		return 0;
+
 	radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
 
 	return 0;
@@ -391,6 +404,9 @@ void radeon_driver_irq_uninstall(struct drm_device * dev)
 
 	dev_priv->irq_enabled = 0;
 
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		return;
+
 	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600)
 		RADEON_WRITE(R500_DxMODE_INT_MASK, 0);
 	/* Disable *all* interrupts */
diff --git a/sys/dev/drm/radeon_state.c b/sys/dev/drm/radeon_state.c
index e7c5c5e..a77f475 100644
--- a/sys/dev/drm/radeon_state.c
+++ b/sys/dev/drm/radeon_state.c
@@ -24,8 +24,11 @@
  * Authors:
  *    Gareth Hughes <gareth@valinux.com>
  *    Kevin E. Martin <martin@valinux.com>
+ * __FBSDID("$FreeBSD: src/sys/dev/drm/radeon_state.c,v 1.27 2009/09/28 22:37:07 rnoland Exp $");
  */
 
+#include <sys/cdefs.h>
+
 #include "dev/drm/drmP.h"
 #include "dev/drm/drm.h"
 #include "dev/drm/drm_sarea.h"
@@ -1538,7 +1541,7 @@ static void radeon_cp_dispatch_vertex(struct drm_device * dev,
 	} while (i < nbox);
 }
 
-static void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_buf *buf)
+void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_buf *buf)
 {
 	drm_radeon_private_t *dev_priv = dev->dev_private;
 	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
@@ -2199,7 +2202,10 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f
 	if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS)
 		sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
 
-	radeon_cp_dispatch_swap(dev);
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		r600_cp_dispatch_swap(dev);
+	else
+		radeon_cp_dispatch_swap(dev);
 	sarea_priv->ctx_owner = 0;
 
 	COMMIT_RING();
@@ -2396,7 +2402,10 @@ static int radeon_cp_texture(struct drm_device *dev, void *data, struct drm_file
 	RING_SPACE_TEST_WITH_RETURN(dev_priv);
 	VB_AGE_TEST_WITH_RETURN(dev_priv);
 
-	ret = radeon_cp_dispatch_texture(dev, file_priv, tex, &image);
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+		ret = r600_cp_dispatch_texture(dev, file_priv, tex, &image);
+	else
+		ret = radeon_cp_dispatch_texture(dev, file_priv, tex, &image);
 
 	return ret;
 }
@@ -3015,7 +3024,10 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
 		value = GET_SCRATCH(dev_priv, 2);
 		break;
 	case RADEON_PARAM_IRQ_NR:
-		value = dev->irq;
+		if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+			value = 0;
+		else
+			value = dev->irq;
 		break;
 	case RADEON_PARAM_GART_BASE:
 		value = dev_priv->gart_vm_start;
@@ -3069,6 +3081,9 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
 	case RADEON_PARAM_NUM_GB_PIPES:
 		value = dev_priv->num_gb_pipes;
 		break;
+	case RADEON_PARAM_NUM_Z_PIPES:
+		value = dev_priv->num_z_pipes;
+		break;
 	default:
 		DRM_DEBUG("Invalid parameter %d\n", param->param);
 		return -EINVAL;
@@ -3153,6 +3168,14 @@ void radeon_driver_preclose(struct drm_device *dev, struct drm_file *file_priv)
 void radeon_driver_lastclose(struct drm_device *dev)
 {
 	radeon_surfaces_release(PCIGART_FILE_PRIV, dev->dev_private);
+	if (dev->dev_private) {
+		drm_radeon_private_t *dev_priv = dev->dev_private;
+
+		if (dev_priv->sarea_priv &&
+		    dev_priv->sarea_priv->pfCurrentPage != 0)
+			radeon_cp_dispatch_flip(dev);
+	}
+
 	radeon_do_release(dev);
 }
 
@@ -3213,7 +3236,8 @@ struct drm_ioctl_desc radeon_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_RADEON_IRQ_WAIT, radeon_irq_wait, DRM_AUTH),
 	DRM_IOCTL_DEF(DRM_RADEON_SETPARAM, radeon_cp_setparam, DRM_AUTH),
 	DRM_IOCTL_DEF(DRM_RADEON_SURF_ALLOC, radeon_surface_alloc, DRM_AUTH),
-	DRM_IOCTL_DEF(DRM_RADEON_SURF_FREE, radeon_surface_free, DRM_AUTH)
+	DRM_IOCTL_DEF(DRM_RADEON_SURF_FREE, radeon_surface_free, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_RADEON_CS, radeon_cs_ioctl, DRM_AUTH)
 };
 
 int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls);
