drm_stub.c 8.34 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/**
 * \file drm_stub.h
 * Stub support
 *
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 */

/*
 * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
 *
 * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
 * 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.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include "drmP.h"
#include "drm_core.h"

Dave Airlie's avatar
Dave Airlie committed
39
unsigned int drm_debug = 0;	/* 1 to enable debug output */
Linus Torvalds's avatar
Linus Torvalds committed
40 41
EXPORT_SYMBOL(drm_debug);

Dave Airlie's avatar
Dave Airlie committed
42 43
MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46
MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output");

47
module_param_named(debug, drm_debug, int, 0600);
Linus Torvalds's avatar
Linus Torvalds committed
48

49 50
struct idr drm_minors_idr;

51
struct class *drm_class;
Linus Torvalds's avatar
Linus Torvalds committed
52 53
struct proc_dir_entry *drm_proc_root;

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
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;
}

82
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
Dave Airlie's avatar
Dave Airlie committed
83 84
			   const struct pci_device_id *ent,
			   struct drm_driver *driver)
Linus Torvalds's avatar
Linus Torvalds committed
85 86 87
{
	int retcode;

88
	INIT_LIST_HEAD(&dev->filelist);
Dave Airlie's avatar
Dave Airlie committed
89 90 91 92
	INIT_LIST_HEAD(&dev->ctxlist);
	INIT_LIST_HEAD(&dev->vmalist);
	INIT_LIST_HEAD(&dev->maplist);

Linus Torvalds's avatar
Linus Torvalds committed
93
	spin_lock_init(&dev->count_lock);
94
	spin_lock_init(&dev->drw_lock);
95
	spin_lock_init(&dev->lock.spinlock);
Dave Airlie's avatar
Dave Airlie committed
96
	init_timer(&dev->timer);
Dave Airlie's avatar
Dave Airlie committed
97 98
	mutex_init(&dev->struct_mutex);
	mutex_init(&dev->ctxlist_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
99

Dave Airlie's avatar
Dave Airlie committed
100 101
	idr_init(&dev->drw_idr);

Dave Airlie's avatar
Dave Airlie committed
102
	dev->pdev = pdev;
103 104
	dev->pci_device = pdev->device;
	dev->pci_vendor = pdev->vendor;
Linus Torvalds's avatar
Linus Torvalds committed
105 106

#ifdef __alpha__
Dave Airlie's avatar
Dave Airlie committed
107
	dev->hose = pdev->sysdata;
Linus Torvalds's avatar
Linus Torvalds committed
108 109
#endif

110 111 112
	if (drm_ht_create(&dev->map_hash, 12)) {
		return -ENOMEM;
	}
Dave Airlie's avatar
Dave Airlie committed
113

Linus Torvalds's avatar
Linus Torvalds committed
114 115
	/* the DRM has 6 basic counters */
	dev->counters = 6;
Dave Airlie's avatar
Dave Airlie committed
116 117 118 119 120 121
	dev->types[0] = _DRM_STAT_LOCK;
	dev->types[1] = _DRM_STAT_OPENS;
	dev->types[2] = _DRM_STAT_CLOSES;
	dev->types[3] = _DRM_STAT_IOCTLS;
	dev->types[4] = _DRM_STAT_LOCKS;
	dev->types[5] = _DRM_STAT_UNLOCKS;
Linus Torvalds's avatar
Linus Torvalds committed
122 123

	dev->driver = driver;
Dave Airlie's avatar
Dave Airlie committed
124

Linus Torvalds's avatar
Linus Torvalds committed
125
	if (drm_core_has_AGP(dev)) {
126 127
		if (drm_device_is_agp(dev))
			dev->agp = drm_agp_init(dev);
Dave Airlie's avatar
Dave Airlie committed
128 129 130
		if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP)
		    && (dev->agp == NULL)) {
			DRM_ERROR("Cannot initialize the agpgart module.\n");
Linus Torvalds's avatar
Linus Torvalds committed
131 132 133 134 135
			retcode = -EINVAL;
			goto error_out_unreg;
		}
		if (drm_core_has_MTRR(dev)) {
			if (dev->agp)
Dave Airlie's avatar
Dave Airlie committed
136 137 138 139
				dev->agp->agp_mtrr =
				    mtrr_add(dev->agp->agp_info.aper_base,
					     dev->agp->agp_info.aper_size *
					     1024 * 1024, MTRR_TYPE_WRCOMB, 1);
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142
		}
	}

143 144 145 146
	if (dev->driver->load)
		if ((retcode = dev->driver->load(dev, ent->driver_data)))
			goto error_out_unreg;

Dave Airlie's avatar
Dave Airlie committed
147 148 149
	retcode = drm_ctxbitmap_init(dev);
	if (retcode) {
		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152
		goto error_out_unreg;
	}

153 154 155 156 157 158 159 160 161
	if (driver->driver_features & DRIVER_GEM) {
		retcode = drm_gem_init(dev);
		if (retcode) {
			DRM_ERROR("Cannot initialize graphics execution "
				  "manager (GEM)\n");
			goto error_out_unreg;
		}
	}

Linus Torvalds's avatar
Linus Torvalds committed
162
	return 0;
Dave Airlie's avatar
Dave Airlie committed
163 164

      error_out_unreg:
165
	drm_lastclose(dev);
Linus Torvalds's avatar
Linus Torvalds committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	return retcode;
}


/**
 * Get a secondary minor number.
 *
 * \param dev device data structure
 * \param sec-minor structure to hold the assigned minor
 * \return negative number on failure.
 *
 * Search an empty entry and initialize it to the given parameters, and
 * create the proc init entry via proc_init(). This routines assigns
 * minor numbers to secondary heads of multi-headed cards
 */
181
static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
Linus Torvalds's avatar
Linus Torvalds committed
182
{
183
	struct drm_minor *new_minor;
Linus Torvalds's avatar
Linus Torvalds committed
184
	int ret;
185
	int minor_id;
Linus Torvalds's avatar
Linus Torvalds committed
186 187 188

	DRM_DEBUG("\n");

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	minor_id = drm_minor_get_id(dev, type);
	if (minor_id < 0)
		return minor_id;

	new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
	if (!new_minor) {
		ret = -ENOMEM;
		goto err_idr;
	}

	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;
Linus Torvalds's avatar
Linus Torvalds committed
211
		}
212 213 214 215 216 217 218 219
	} 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;
Linus Torvalds's avatar
Linus Torvalds committed
220
	}
221 222 223 224 225 226 227 228 229 230 231 232 233 234
	*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;
Linus Torvalds's avatar
Linus Torvalds committed
235 236
	return ret;
}
Dave Airlie's avatar
Dave Airlie committed
237

Dave Airlie's avatar
Dave Airlie committed
238 239 240 241 242 243 244 245 246 247 248 249
/**
 * Register.
 *
 * \param pdev - PCI device structure
 * \param ent entry from the PCI ID table with device type flags
 * \return zero on success or a negative number on failure.
 *
 * Attempt to gets inter module "drm" information. If we are first
 * then register the character device and inter module information.
 * Try and register, if we fail to register, backout previous work.
 */
int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
Dave Airlie's avatar
Dave Airlie committed
250
		struct drm_driver *driver)
Dave Airlie's avatar
Dave Airlie committed
251
{
252
	struct drm_device *dev;
Dave Airlie's avatar
Dave Airlie committed
253 254 255 256 257 258 259 260
	int ret;

	DRM_DEBUG("\n");

	dev = drm_calloc(1, sizeof(*dev), DRM_MEM_STUB);
	if (!dev)
		return -ENOMEM;

261 262 263
	ret = pci_enable_device(pdev);
	if (ret)
		goto err_g1;
Dave Airlie's avatar
Dave Airlie committed
264

265
	pci_set_master(pdev);
Dave Airlie's avatar
Dave Airlie committed
266 267
	if ((ret = drm_fill_in_dev(dev, pdev, ent, driver))) {
		printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
268
		goto err_g2;
Dave Airlie's avatar
Dave Airlie committed
269
	}
270
	if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
271
		goto err_g2;
272

273 274
	list_add_tail(&dev->driver_item, &driver->device_list);

275 276
	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
		 driver->name, driver->major, driver->minor, driver->patchlevel,
277
		 driver->date, dev->primary->index);
Dave Airlie's avatar
Dave Airlie committed
278 279 280

	return 0;

281 282 283
err_g2:
	pci_disable_device(pdev);
err_g1:
Dave Airlie's avatar
Dave Airlie committed
284 285 286
	drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
	return ret;
}
Dave Airlie's avatar
Dave Airlie committed
287

Linus Torvalds's avatar
Linus Torvalds committed
288 289 290 291 292 293 294 295 296 297
/**
 * Put a device minor number.
 *
 * \param dev device data structure
 * \return always zero
 *
 * Cleans up the proc resources. If it is the last minor then release the foreign
 * "drm" data, otherwise unregisters the "drm" data, frees the dev list and
 * unregisters the character device.
 */
298
int drm_put_dev(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
{
	DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name);

	if (dev->unique) {
		drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
		dev->unique = NULL;
		dev->unique_len = 0;
	}
	if (dev->devname) {
		drm_free(dev->devname, strlen(dev->devname) + 1,
			 DRM_MEM_DRIVER);
		dev->devname = NULL;
	}
	drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
	return 0;
}

/**
 * Put a secondary minor number.
 *
 * \param sec_minor - structure to be released
 * \return always zero
 *
 * Cleans up the proc resources. Not legal for this to be the
 * last minor released.
 *
 */
326
int drm_put_minor(struct drm_minor **minor_p)
Linus Torvalds's avatar
Linus Torvalds committed
327
{
328
	struct drm_minor *minor = *minor_p;
329

330
	DRM_DEBUG("release secondary minor %d\n", minor->index);
Dave Airlie's avatar
Dave Airlie committed
331

332 333 334
	if (minor->type == DRM_MINOR_LEGACY)
		drm_proc_cleanup(minor, drm_proc_root);
	drm_sysfs_device_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
335

336
	idr_remove(&drm_minors_idr, minor->index);
Dave Airlie's avatar
Dave Airlie committed
337

338 339
	kfree(minor);
	*minor_p = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
340 341
	return 0;
}