drm: reorganise minor number handling using backported modesetting code.

rips out the head crap and replaces it with an idr and drm_minor structure

Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index 6540948..5446235 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -379,13 +379,12 @@
 struct drm_file {
 	int authenticated;
 	int master;
-	int minor;
 	pid_t pid;
 	uid_t uid;
 	drm_magic_t magic;
 	unsigned long ioctl_count;
 	struct list_head lhead;
-	struct drm_head *head;
+	struct drm_minor *minor;
 	int remove_auth_on_close;
 	unsigned long lock_count;
 	struct file *filp;
@@ -630,16 +629,19 @@
 	struct pci_driver pci_driver;
 };
 
+#define DRM_MINOR_UNASSIGNED 0
+#define DRM_MINOR_LEGACY 1
+
 /**
- * DRM head structure. This structure represent a video head on a card
- * that may contain multiple heads. Embed one per head of these in the
- * private drm_device structure.
+ * DRM minor structure. This structure represents a drm minor number.
  */
-struct drm_head {
-	int minor;			/**< Minor device number */
+struct drm_minor {
+	int index;			/**< Minor device number */
+	int type;                       /**< Control or render */
+	dev_t device;			/**< Device number for mknod */
+	struct device kdev;		/**< Linux device */
 	struct drm_device *dev;
 	struct proc_dir_entry *dev_root;  /**< proc directory entry */
-	dev_t device;			/**< Device number for mknod */
 };
 
 /**
@@ -647,7 +649,6 @@
  * may contain multiple heads.
  */
 struct drm_device {
-	struct device dev;		/**< Linux device */
 	char *unique;			/**< Unique identifier: e.g., busid */
 	int unique_len;			/**< Length of unique field */
 	char *devname;			/**< For /proc/interrupts */
@@ -763,7 +764,7 @@
 	struct drm_driver *driver;
 	drm_local_map_t *agp_buffer_map;
 	unsigned int agp_buffer_token;
-	struct drm_head primary;		/**< primary screen head */
+	struct drm_minor *primary;		/**< render type primary screen head */
 
 	/** \name Drawable information */
 	/*@{ */
@@ -1030,23 +1031,20 @@
 extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 		       struct drm_driver *driver);
 extern int drm_put_dev(struct drm_device *dev);
-extern int drm_put_head(struct drm_head *head);
+extern int drm_put_minor(struct drm_minor **minor);
 extern unsigned int drm_debug;
-extern unsigned int drm_cards_limit;
-extern struct drm_head **drm_heads;
+
 extern struct class *drm_class;
 extern struct proc_dir_entry *drm_proc_root;
 
+extern struct idr drm_minors_idr;
+
 extern drm_local_map_t *drm_getsarea(struct drm_device *dev);
 
 				/* Proc support (drm_proc.h) */
-extern int drm_proc_init(struct drm_device *dev,
-			 int minor,
-			 struct proc_dir_entry *root,
-			 struct proc_dir_entry **dev_root);
-extern int drm_proc_cleanup(int minor,
-			    struct proc_dir_entry *root,
-			    struct proc_dir_entry *dev_root);
+extern int drm_proc_init(struct drm_minor *minor, int minor_id,
+			 struct proc_dir_entry *root);
+extern int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root);
 
 				/* Scatter Gather Support (drm_scatter.h) */
 extern void drm_sg_cleanup(struct drm_sg_mem * entry);
@@ -1071,8 +1069,8 @@
 struct drm_sysfs_class;
 extern struct class *drm_sysfs_create(struct module *owner, char *name);
 extern void drm_sysfs_destroy(void);
-extern int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head);
-extern void drm_sysfs_device_remove(struct drm_device *dev);
+extern int drm_sysfs_device_add(struct drm_minor *minor);
+extern void drm_sysfs_device_remove(struct drm_minor *minor);
 
 /*
  * Basic memory manager support (drm_mm.c)
diff --git a/drivers/char/drm/drm_agpsupport.c b/drivers/char/drm/drm_agpsupport.c
index 9468c78..aefa5ac 100644
--- a/drivers/char/drm/drm_agpsupport.c
+++ b/drivers/char/drm/drm_agpsupport.c
@@ -122,7 +122,7 @@
 int drm_agp_acquire_ioctl(struct drm_device *dev, void *data,
 			  struct drm_file *file_priv)
 {
-	return drm_agp_acquire((struct drm_device *) file_priv->head->dev);
+	return drm_agp_acquire((struct drm_device *) file_priv->minor->dev);
 }
 
 /**
diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c
index 0e7af53..fc54140 100644
--- a/drivers/char/drm/drm_drv.c
+++ b/drivers/char/drm/drm_drv.c
@@ -313,35 +313,36 @@
 	drm_ht_remove(&dev->map_hash);
 	drm_ctxbitmap_cleanup(dev);
 
-	drm_put_head(&dev->primary);
+	drm_put_minor(&dev->primary);
 	if (drm_put_dev(dev))
 		DRM_ERROR("Cannot unload module\n");
 }
 
+int drm_minors_cleanup(int id, void *ptr, void *data)
+{
+	struct drm_minor *minor = ptr;
+	struct drm_device *dev;
+	struct drm_driver *driver = data;
+
+	dev = minor->dev;
+	if (minor->dev->driver != driver)
+		return 0;
+
+	if (minor->type != DRM_MINOR_LEGACY)
+		return 0;
+
+	if (dev)
+		pci_dev_put(dev->pdev);
+	drm_cleanup(dev);
+	return 1;
+}
+
 void drm_exit(struct drm_driver *driver)
 {
-	int i;
-	struct drm_device *dev = NULL;
-	struct drm_head *head;
-
 	DRM_DEBUG("\n");
 
-	for (i = 0; i < drm_cards_limit; i++) {
-		head = drm_heads[i];
-		if (!head)
-			continue;
-		if (!head->dev)
-			continue;
-		if (head->dev->driver != driver)
-			continue;
-		dev = head->dev;
-		if (dev) {
-			/* release the pci driver */
-			if (dev->pdev)
-				pci_dev_put(dev->pdev);
-			drm_cleanup(dev);
-		}
-	}
+	idr_for_each(&drm_minors_idr, &drm_minors_cleanup, driver);
+
 	DRM_INFO("Module unloaded\n");
 }
 
@@ -357,13 +358,7 @@
 {
 	int ret = -ENOMEM;
 
-	drm_cards_limit =
-	    (drm_cards_limit <
-	     DRM_MAX_MINOR + 1 ? drm_cards_limit : DRM_MAX_MINOR + 1);
-	drm_heads =
-	    drm_calloc(drm_cards_limit, sizeof(*drm_heads), DRM_MEM_STUB);
-	if (!drm_heads)
-		goto err_p1;
+	idr_init(&drm_minors_idr);
 
 	if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
 		goto err_p1;
@@ -391,7 +386,8 @@
 	drm_sysfs_destroy();
 err_p2:
 	unregister_chrdev(DRM_MAJOR, "drm");
-	drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB);
+
+	idr_destroy(&drm_minors_idr);
 err_p1:
 	return ret;
 }
@@ -403,7 +399,7 @@
 
 	unregister_chrdev(DRM_MAJOR, "drm");
 
-	drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB);
+	idr_destroy(&drm_minors_idr);
 }
 
 module_init(drm_core_init);
@@ -452,7 +448,7 @@
 	      unsigned int cmd, unsigned long arg)
 {
 	struct drm_file *file_priv = filp->private_data;
-	struct drm_device *dev = file_priv->head->dev;
+	struct drm_device *dev = file_priv->minor->dev;
 	struct drm_ioctl_desc *ioctl;
 	drm_ioctl_t *func;
 	unsigned int nr = DRM_IOCTL_NR(cmd);
@@ -465,7 +461,7 @@
 
 	DRM_DEBUG("pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
 		  task_pid_nr(current), cmd, nr,
-		  (long)old_encode_dev(file_priv->head->device),
+		  (long)old_encode_dev(file_priv->minor->device),
 		  file_priv->authenticated);
 
 	if ((nr >= DRM_CORE_IOCTL_COUNT) &&
diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c
index f09d4b5..df7bf48 100644
--- a/drivers/char/drm/drm_fops.c
+++ b/drivers/char/drm/drm_fops.c
@@ -129,16 +129,15 @@
 int drm_open(struct inode *inode, struct file *filp)
 {
 	struct drm_device *dev = NULL;
-	int minor = iminor(inode);
+	int minor_id = iminor(inode);
+	struct drm_minor *minor;
 	int retcode = 0;
 
-	if (!((minor >= 0) && (minor < drm_cards_limit)))
+	minor = idr_find(&drm_minors_idr, minor_id);
+	if (!minor)
 		return -ENODEV;
 
-	if (!drm_heads[minor])
-		return -ENODEV;
-
-	if (!(dev = drm_heads[minor]->dev))
+	if (!(dev = minor->dev))
 		return -ENODEV;
 
 	retcode = drm_open_helper(inode, filp, dev);
@@ -168,19 +167,18 @@
 int drm_stub_open(struct inode *inode, struct file *filp)
 {
 	struct drm_device *dev = NULL;
-	int minor = iminor(inode);
+	struct drm_minor *minor;
+	int minor_id = iminor(inode);
 	int err = -ENODEV;
 	const struct file_operations *old_fops;
 
 	DRM_DEBUG("\n");
 
-	if (!((minor >= 0) && (minor < drm_cards_limit)))
+	minor = idr_find(&drm_minors_idr, minor_id);
+	if (!minor)
 		return -ENODEV;
 
-	if (!drm_heads[minor])
-		return -ENODEV;
-
-	if (!(dev = drm_heads[minor]->dev))
+	if (!(dev = minor->dev))
 		return -ENODEV;
 
 	old_fops = filp->f_op;
@@ -225,7 +223,7 @@
 static int drm_open_helper(struct inode *inode, struct file *filp,
 			   struct drm_device * dev)
 {
-	int minor = iminor(inode);
+	int minor_id = iminor(inode);
 	struct drm_file *priv;
 	int ret;
 
@@ -234,7 +232,7 @@
 	if (!drm_cpu_valid())
 		return -EINVAL;
 
-	DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor);
+	DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id);
 
 	priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
 	if (!priv)
@@ -245,8 +243,7 @@
 	priv->filp = filp;
 	priv->uid = current->euid;
 	priv->pid = task_pid_nr(current);
-	priv->minor = minor;
-	priv->head = drm_heads[minor];
+	priv->minor = idr_find(&drm_minors_idr, minor_id);
 	priv->ioctl_count = 0;
 	/* for compatibility root is always authenticated */
 	priv->authenticated = capable(CAP_SYS_ADMIN);
@@ -297,11 +294,11 @@
 int drm_fasync(int fd, struct file *filp, int on)
 {
 	struct drm_file *priv = filp->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	int retcode;
 
 	DRM_DEBUG("fd = %d, device = 0x%lx\n", fd,
-		  (long)old_encode_dev(priv->head->device));
+		  (long)old_encode_dev(priv->minor->device));
 	retcode = fasync_helper(fd, filp, on, &dev->buf_async);
 	if (retcode < 0)
 		return retcode;
@@ -324,7 +321,7 @@
 int drm_release(struct inode *inode, struct file *filp)
 {
 	struct drm_file *file_priv = filp->private_data;
-	struct drm_device *dev = file_priv->head->dev;
+	struct drm_device *dev = file_priv->minor->dev;
 	int retcode = 0;
 	unsigned long irqflags;
 
@@ -341,7 +338,7 @@
 
 	DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
 		  task_pid_nr(current),
-		  (long)old_encode_dev(file_priv->head->device),
+		  (long)old_encode_dev(file_priv->minor->device),
 		  dev->open_count);
 
 	if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
diff --git a/drivers/char/drm/drm_proc.c b/drivers/char/drm/drm_proc.c
index d9b560f..93b1e04 100644
--- a/drivers/char/drm/drm_proc.c
+++ b/drivers/char/drm/drm_proc.c
@@ -87,34 +87,35 @@
  * "/proc/dri/%minor%/", and each entry in proc_list as
  * "/proc/dri/%minor%/%name%".
  */
-int drm_proc_init(struct drm_device * dev, int minor,
-		  struct proc_dir_entry *root, struct proc_dir_entry **dev_root)
+int drm_proc_init(struct drm_minor *minor, int minor_id,
+		  struct proc_dir_entry *root)
 {
 	struct proc_dir_entry *ent;
 	int i, j;
 	char name[64];
 
-	sprintf(name, "%d", minor);
-	*dev_root = proc_mkdir(name, root);
-	if (!*dev_root) {
+	sprintf(name, "%d", minor_id);
+	minor->dev_root = proc_mkdir(name, root);
+	if (!minor->dev_root) {
 		DRM_ERROR("Cannot create /proc/dri/%s\n", name);
 		return -1;
 	}
 
 	for (i = 0; i < DRM_PROC_ENTRIES; i++) {
 		ent = create_proc_entry(drm_proc_list[i].name,
-					S_IFREG | S_IRUGO, *dev_root);
+					S_IFREG | S_IRUGO, minor->dev_root);
 		if (!ent) {
 			DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
 				  name, drm_proc_list[i].name);
 			for (j = 0; j < i; j++)
 				remove_proc_entry(drm_proc_list[i].name,
-						  *dev_root);
+						  minor->dev_root);
 			remove_proc_entry(name, root);
+			minor->dev_root = NULL;
 			return -1;
 		}
 		ent->read_proc = drm_proc_list[i].f;
-		ent->data = dev;
+		ent->data = minor;
 	}
 
 	return 0;
@@ -130,18 +131,17 @@
  *
  * Remove all proc entries created by proc_init().
  */
-int drm_proc_cleanup(int minor, struct proc_dir_entry *root,
-		     struct proc_dir_entry *dev_root)
+int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root)
 {
 	int i;
 	char name[64];
 
-	if (!root || !dev_root)
+	if (!root || !minor->dev_root)
 		return 0;
 
 	for (i = 0; i < DRM_PROC_ENTRIES; i++)
-		remove_proc_entry(drm_proc_list[i].name, dev_root);
-	sprintf(name, "%d", minor);
+		remove_proc_entry(drm_proc_list[i].name, minor->dev_root);
+	sprintf(name, "%d", minor->index);
 	remove_proc_entry(name, root);
 
 	return 0;
@@ -163,7 +163,8 @@
 static int drm_name_info(char *buf, char **start, off_t offset, int request,
 			 int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 
 	if (offset > DRM_PROC_LIMIT) {
@@ -205,7 +206,8 @@
 static int drm__vm_info(char *buf, char **start, off_t offset, int request,
 			int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 	struct drm_map *map;
 	struct drm_map_list *r_list;
@@ -261,7 +263,8 @@
 static int drm_vm_info(char *buf, char **start, off_t offset, int request,
 		       int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
@@ -284,7 +287,8 @@
 static int drm__queues_info(char *buf, char **start, off_t offset,
 			    int request, int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 	int i;
 	struct drm_queue *q;
@@ -334,7 +338,8 @@
 static int drm_queues_info(char *buf, char **start, off_t offset, int request,
 			   int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
@@ -357,7 +362,8 @@
 static int drm__bufs_info(char *buf, char **start, off_t offset, int request,
 			  int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 	struct drm_device_dma *dma = dev->dma;
 	int i;
@@ -406,7 +412,8 @@
 static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
 			 int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
@@ -429,7 +436,8 @@
 static int drm__clients_info(char *buf, char **start, off_t offset,
 			     int request, int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 	struct drm_file *priv;
 
@@ -445,7 +453,7 @@
 	list_for_each_entry(priv, &dev->filelist, lhead) {
 		DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
 			       priv->authenticated ? 'y' : 'n',
-			       priv->minor,
+			       priv->minor->index,
 			       priv->pid,
 			       priv->uid, priv->magic, priv->ioctl_count);
 	}
@@ -462,7 +470,8 @@
 static int drm_clients_info(char *buf, char **start, off_t offset,
 			    int request, int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
@@ -476,7 +485,8 @@
 static int drm__vma_info(char *buf, char **start, off_t offset, int request,
 			 int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int len = 0;
 	struct drm_vma_entry *pt;
 	struct vm_area_struct *vma;
@@ -535,7 +545,8 @@
 static int drm_vma_info(char *buf, char **start, off_t offset, int request,
 			int *eof, void *data)
 {
-	struct drm_device *dev = (struct drm_device *) data;
+	struct drm_minor *minor = (struct drm_minor *) data;
+	struct drm_device *dev = minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c
index d93a217..c2f584f 100644
--- a/drivers/char/drm/drm_stub.c
+++ b/drivers/char/drm/drm_stub.c
@@ -36,23 +36,49 @@
 #include "drmP.h"
 #include "drm_core.h"
 
-unsigned int drm_cards_limit = 16;	/* Enough for one machine */
 unsigned int drm_debug = 0;	/* 1 to enable debug output */
 EXPORT_SYMBOL(drm_debug);
 
 MODULE_AUTHOR(CORE_AUTHOR);
 MODULE_DESCRIPTION(CORE_DESC);
 MODULE_LICENSE("GPL and additional rights");
-MODULE_PARM_DESC(cards_limit, "Maximum number of graphics cards");
 MODULE_PARM_DESC(debug, "Enable debug output");
 
-module_param_named(cards_limit, drm_cards_limit, int, 0444);
 module_param_named(debug, drm_debug, int, 0600);
 
-struct drm_head **drm_heads;
+struct idr drm_minors_idr;
+
 struct class *drm_class;
 struct proc_dir_entry *drm_proc_root;
 
+static int drm_minor_get_id(struct drm_device *dev, int type)
+{
+	int new_id;
+	int ret;
+	int base = 0, limit = 63;
+
+again:
+	if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
+		DRM_ERROR("Out of memory expanding drawable idr\n");
+		return -ENOMEM;
+	}
+	mutex_lock(&dev->struct_mutex);
+	ret = idr_get_new_above(&drm_minors_idr, NULL,
+				base, &new_id);
+	mutex_unlock(&dev->struct_mutex);
+	if (ret == -EAGAIN) {
+		goto again;
+	} else if (ret) {
+		return ret;
+	}
+
+	if (new_id >= limit) {
+		idr_remove(&drm_minors_idr, new_id);
+		return -EINVAL;
+	}
+	return new_id;
+}
+
 static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
 			   const struct pci_device_id *ent,
 			   struct drm_driver *driver)
@@ -145,48 +171,60 @@
  * create the proc init entry via proc_init(). This routines assigns
  * minor numbers to secondary heads of multi-headed cards
  */
-static int drm_get_head(struct drm_device * dev, struct drm_head * head)
+static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
 {
-	struct drm_head **heads = drm_heads;
+	struct drm_minor *new_minor;
 	int ret;
-	int minor;
+	int minor_id;
 
 	DRM_DEBUG("\n");
 
-	for (minor = 0; minor < drm_cards_limit; minor++, heads++) {
-		if (!*heads) {
+	minor_id = drm_minor_get_id(dev, type);
+	if (minor_id < 0)
+		return minor_id;
 
-			*head = (struct drm_head) {
-			.dev = dev,.device =
-				    MKDEV(DRM_MAJOR, minor),.minor = minor,};
-
-			if ((ret =
-			     drm_proc_init(dev, minor, drm_proc_root,
-					   &head->dev_root))) {
-				printk(KERN_ERR
-				       "DRM: Failed to initialize /proc/dri.\n");
-				goto err_g1;
-			}
-
-			ret = drm_sysfs_device_add(dev, head);
-			if (ret) {
-				printk(KERN_ERR
-				       "DRM: Error sysfs_device_add.\n");
-				goto err_g2;
-			}
-			*heads = head;
-
-			DRM_DEBUG("new minor assigned %d\n", minor);
-			return 0;
-		}
+	new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
+	if (!new_minor) {
+		ret = -ENOMEM;
+		goto err_idr;
 	}
-	DRM_ERROR("out of minors\n");
-	return -ENOMEM;
-      err_g2:
-	drm_proc_cleanup(minor, drm_proc_root, head->dev_root);
-      err_g1:
-	*head = (struct drm_head) {
-	.dev = NULL};
+
+	new_minor->type = type;
+	new_minor->device = MKDEV(DRM_MAJOR, minor_id);
+	new_minor->dev = dev;
+	new_minor->index = minor_id;
+
+	idr_replace(&drm_minors_idr, new_minor, minor_id);
+
+	if (type == DRM_MINOR_LEGACY) {
+		ret = drm_proc_init(new_minor, minor_id, drm_proc_root);
+		if (ret) {
+			DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
+			goto err_mem;
+		}
+	} else
+		new_minor->dev_root = NULL;
+
+	ret = drm_sysfs_device_add(new_minor);
+	if (ret) {
+		printk(KERN_ERR
+		       "DRM: Error sysfs_device_add.\n");
+		goto err_g2;
+	}
+	*minor = new_minor;
+
+	DRM_DEBUG("new minor assigned %d\n", minor_id);
+	return 0;
+
+
+err_g2:
+	if (new_minor->type == DRM_MINOR_LEGACY)
+		drm_proc_cleanup(new_minor, drm_proc_root);
+err_mem:
+	kfree(new_minor);
+err_idr:
+	idr_remove(&drm_minors_idr, minor_id);
+	*minor = NULL;
 	return ret;
 }
 
@@ -222,12 +260,12 @@
 		printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
 		goto err_g2;
 	}
-	if ((ret = drm_get_head(dev, &dev->primary)))
+	if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
 		goto err_g2;
 
 	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
 		 driver->name, driver->major, driver->minor, driver->patchlevel,
-		 driver->date, dev->primary.minor);
+		 driver->date, dev->primary->index);
 
 	return 0;
 
@@ -276,18 +314,18 @@
  * last minor released.
  *
  */
-int drm_put_head(struct drm_head * head)
+int drm_put_minor(struct drm_minor **minor_p)
 {
-	int minor = head->minor;
+	struct drm_minor *minor = *minor_p;
+	DRM_DEBUG("release secondary minor %d\n", minor->index);
 
-	DRM_DEBUG("release secondary minor %d\n", minor);
+	if (minor->type == DRM_MINOR_LEGACY)
+		drm_proc_cleanup(minor, drm_proc_root);
+	drm_sysfs_device_remove(minor);
 
-	drm_proc_cleanup(minor, drm_proc_root, head->dev_root);
-	drm_sysfs_device_remove(head->dev);
+	idr_remove(&drm_minors_idr, minor->index);
 
-	*head = (struct drm_head) {.dev = NULL};
-
-	drm_heads[minor] = NULL;
-
+	kfree(minor);
+	*minor_p = NULL;
 	return 0;
 }
diff --git a/drivers/char/drm/drm_sysfs.c b/drivers/char/drm/drm_sysfs.c
index 05ed504..7a1d9a7 100644
--- a/drivers/char/drm/drm_sysfs.c
+++ b/drivers/char/drm/drm_sysfs.c
@@ -19,7 +19,7 @@
 #include "drm_core.h"
 #include "drmP.h"
 
-#define to_drm_device(d) container_of(d, struct drm_device, dev)
+#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
 
 /**
  * drm_sysfs_suspend - DRM class suspend hook
@@ -31,7 +31,8 @@
  */
 static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
 {
-	struct drm_device *drm_dev = to_drm_device(dev);
+	struct drm_minor *drm_minor = to_drm_minor(dev);
+	struct drm_device *drm_dev = drm_minor->dev;
 
 	printk(KERN_ERR "%s\n", __FUNCTION__);
 
@@ -50,7 +51,8 @@
  */
 static int drm_sysfs_resume(struct device *dev)
 {
-	struct drm_device *drm_dev = to_drm_device(dev);
+	struct drm_minor *drm_minor = to_drm_minor(dev);
+	struct drm_device *drm_dev = drm_minor->dev;
 
 	if (drm_dev->driver->resume)
 		return drm_dev->driver->resume(drm_dev);
@@ -120,10 +122,11 @@
 static ssize_t show_dri(struct device *device, struct device_attribute *attr,
 			char *buf)
 {
-	struct drm_device *dev = to_drm_device(device);
-	if (dev->driver->dri_library_name)
-		return dev->driver->dri_library_name(dev, buf);
-	return snprintf(buf, PAGE_SIZE, "%s\n", dev->driver->pci_driver.name);
+	struct drm_minor *drm_minor = to_drm_minor(device);
+	struct drm_device *drm_dev = drm_minor->dev;
+	if (drm_dev->driver->dri_library_name)
+		return drm_dev->driver->dri_library_name(drm_dev, buf);
+	return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);
 }
 
 static struct device_attribute device_attrs[] = {
@@ -152,25 +155,28 @@
  * as the parent for the Linux device, and make sure it has a file containing
  * the driver we're using (for userspace compatibility).
  */
-int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head)
+int drm_sysfs_device_add(struct drm_minor *minor)
 {
 	int err;
 	int i, j;
+	char *minor_str;
 
-	dev->dev.parent = &dev->pdev->dev;
-	dev->dev.class = drm_class;
-	dev->dev.release = drm_sysfs_device_release;
-	dev->dev.devt = head->device;
-	snprintf(dev->dev.bus_id, BUS_ID_SIZE, "card%d", head->minor);
+	minor->kdev.parent = &minor->dev->pdev->dev;
+	minor->kdev.class = drm_class;
+	minor->kdev.release = drm_sysfs_device_release;
+	minor->kdev.devt = minor->device;
+	minor_str = "card%d";
 
-	err = device_register(&dev->dev);
+	snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
+
+	err = device_register(&minor->kdev);
 	if (err) {
 		DRM_ERROR("device add failed: %d\n", err);
 		goto err_out;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
-		err = device_create_file(&dev->dev, &device_attrs[i]);
+		err = device_create_file(&minor->kdev, &device_attrs[i]);
 		if (err)
 			goto err_out_files;
 	}
@@ -180,8 +186,8 @@
 err_out_files:
 	if (i > 0)
 		for (j = 0; j < i; j++)
-			device_remove_file(&dev->dev, &device_attrs[i]);
-	device_unregister(&dev->dev);
+			device_remove_file(&minor->kdev, &device_attrs[i]);
+	device_unregister(&minor->kdev);
 err_out:
 
 	return err;
@@ -194,11 +200,11 @@
  * This call unregisters and cleans up a class device that was created with a
  * call to drm_sysfs_device_add()
  */
-void drm_sysfs_device_remove(struct drm_device *dev)
+void drm_sysfs_device_remove(struct drm_minor *minor)
 {
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
-		device_remove_file(&dev->dev, &device_attrs[i]);
-	device_unregister(&dev->dev);
+		device_remove_file(&minor->kdev, &device_attrs[i]);
+	device_unregister(&minor->kdev);
 }
diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c
index b20c1e9..c234c6f 100644
--- a/drivers/char/drm/drm_vm.c
+++ b/drivers/char/drm/drm_vm.c
@@ -90,7 +90,7 @@
 static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_map *map = NULL;
 	struct drm_map_list *r_list;
 	struct drm_hash_item *hash;
@@ -207,7 +207,7 @@
 static void drm_vm_shm_close(struct vm_area_struct *vma)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_vma_entry *pt, *temp;
 	struct drm_map *map;
 	struct drm_map_list *r_list;
@@ -286,7 +286,7 @@
 static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_device_dma *dma = dev->dma;
 	unsigned long offset;
 	unsigned long page_nr;
@@ -321,7 +321,7 @@
 {
 	struct drm_map *map = (struct drm_map *) vma->vm_private_data;
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_sg_mem *entry = dev->sg;
 	unsigned long offset;
 	unsigned long map_offset;
@@ -402,7 +402,7 @@
 static void drm_vm_open_locked(struct vm_area_struct *vma)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_vma_entry *vma_entry;
 
 	DRM_DEBUG("0x%08lx,0x%08lx\n",
@@ -420,7 +420,7 @@
 static void drm_vm_open(struct vm_area_struct *vma)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 
 	mutex_lock(&dev->struct_mutex);
 	drm_vm_open_locked(vma);
@@ -438,7 +438,7 @@
 static void drm_vm_close(struct vm_area_struct *vma)
 {
 	struct drm_file *priv = vma->vm_file->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_vma_entry *pt, *temp;
 
 	DRM_DEBUG("0x%08lx,0x%08lx\n",
@@ -473,7 +473,7 @@
 	struct drm_device_dma *dma;
 	unsigned long length = vma->vm_end - vma->vm_start;
 
-	dev = priv->head->dev;
+	dev = priv->minor->dev;
 	dma = dev->dma;
 	DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n",
 		  vma->vm_start, vma->vm_end, vma->vm_pgoff);
@@ -543,7 +543,7 @@
 static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
 {
 	struct drm_file *priv = filp->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	struct drm_map *map = NULL;
 	unsigned long offset = 0;
 	struct drm_hash_item *hash;
@@ -661,7 +661,7 @@
 int drm_mmap(struct file *filp, struct vm_area_struct *vma)
 {
 	struct drm_file *priv = filp->private_data;
-	struct drm_device *dev = priv->head->dev;
+	struct drm_device *dev = priv->minor->dev;
 	int ret;
 
 	mutex_lock(&dev->struct_mutex);
diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c
index 8d7ea81..e5de8ea 100644
--- a/drivers/char/drm/i810_dma.c
+++ b/drivers/char/drm/i810_dma.c
@@ -94,7 +94,7 @@
 	drm_i810_buf_priv_t *buf_priv;
 
 	lock_kernel();
-	dev = priv->head->dev;
+	dev = priv->minor->dev;
 	dev_priv = dev->dev_private;
 	buf = dev_priv->mmap_buffer;
 	buf_priv = buf->dev_private;
@@ -122,7 +122,7 @@
 
 static int i810_map_buffer(struct drm_buf * buf, struct drm_file *file_priv)
 {
-	struct drm_device *dev = file_priv->head->dev;
+	struct drm_device *dev = file_priv->minor->dev;
 	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
 	drm_i810_private_t *dev_priv = dev->dev_private;
 	const struct file_operations *old_fops;
diff --git a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c
index 9df0810..60c9376 100644
--- a/drivers/char/drm/i830_dma.c
+++ b/drivers/char/drm/i830_dma.c
@@ -96,7 +96,7 @@
 	drm_i830_buf_priv_t *buf_priv;
 
 	lock_kernel();
-	dev = priv->head->dev;
+	dev = priv->minor->dev;
 	dev_priv = dev->dev_private;
 	buf = dev_priv->mmap_buffer;
 	buf_priv = buf->dev_private;
@@ -124,7 +124,7 @@
 
 static int i830_map_buffer(struct drm_buf * buf, struct drm_file *file_priv)
 {
-	struct drm_device *dev = file_priv->head->dev;
+	struct drm_device *dev = file_priv->minor->dev;
 	drm_i830_buf_priv_t *buf_priv = buf->dev_private;
 	drm_i830_private_t *dev_priv = dev->dev_private;
 	const struct file_operations *old_fops;