xref: /dflybsd-src/sys/dev/drm/drm_bufs.c (revision f599cd4658a7dc4d6a9794840d71f21b15c787a9)
17f3c3d6fSHasso Tepper /*-
27f3c3d6fSHasso Tepper  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
37f3c3d6fSHasso Tepper  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
47f3c3d6fSHasso Tepper  * All Rights Reserved.
57f3c3d6fSHasso Tepper  *
67f3c3d6fSHasso Tepper  * Permission is hereby granted, free of charge, to any person obtaining a
77f3c3d6fSHasso Tepper  * copy of this software and associated documentation files (the "Software"),
87f3c3d6fSHasso Tepper  * to deal in the Software without restriction, including without limitation
97f3c3d6fSHasso Tepper  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107f3c3d6fSHasso Tepper  * and/or sell copies of the Software, and to permit persons to whom the
117f3c3d6fSHasso Tepper  * Software is furnished to do so, subject to the following conditions:
127f3c3d6fSHasso Tepper  *
137f3c3d6fSHasso Tepper  * The above copyright notice and this permission notice (including the next
147f3c3d6fSHasso Tepper  * paragraph) shall be included in all copies or substantial portions of the
157f3c3d6fSHasso Tepper  * Software.
167f3c3d6fSHasso Tepper  *
177f3c3d6fSHasso Tepper  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187f3c3d6fSHasso Tepper  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
197f3c3d6fSHasso Tepper  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
207f3c3d6fSHasso Tepper  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
217f3c3d6fSHasso Tepper  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
227f3c3d6fSHasso Tepper  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
237f3c3d6fSHasso Tepper  * OTHER DEALINGS IN THE SOFTWARE.
247f3c3d6fSHasso Tepper  *
257f3c3d6fSHasso Tepper  * Authors:
267f3c3d6fSHasso Tepper  *    Rickard E. (Rik) Faith <faith@valinux.com>
277f3c3d6fSHasso Tepper  *    Gareth Hughes <gareth@valinux.com>
287f3c3d6fSHasso Tepper  *
295718399fSFrançois Tigeot  * $FreeBSD: src/sys/dev/drm2/drm_bufs.c,v 1.1 2012/05/22 11:07:44 kib Exp $
307f3c3d6fSHasso Tepper  */
317f3c3d6fSHasso Tepper 
327f3c3d6fSHasso Tepper /** @file drm_bufs.c
337f3c3d6fSHasso Tepper  * Implementation of the ioctls for setup of DRM mappings and DMA buffers.
347f3c3d6fSHasso Tepper  */
357f3c3d6fSHasso Tepper 
365718399fSFrançois Tigeot #include <sys/conf.h>
375718399fSFrançois Tigeot #include <bus/pci/pcireg.h>
387f3c3d6fSHasso Tepper 
3918e26a6dSFrançois Tigeot #include <drm/drmP.h>
407f3c3d6fSHasso Tepper 
417f3c3d6fSHasso Tepper /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
427f3c3d6fSHasso Tepper  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
437f3c3d6fSHasso Tepper  * address for accessing them.  Cleaned up at unload.
447f3c3d6fSHasso Tepper  */
45b3705d71SHasso Tepper static int drm_alloc_resource(struct drm_device *dev, int resource)
467f3c3d6fSHasso Tepper {
4799f70504SFrançois Tigeot 	struct resource *res;
4899f70504SFrançois Tigeot 	int rid;
4999f70504SFrançois Tigeot 
505718399fSFrançois Tigeot 	DRM_LOCK_ASSERT(dev);
5199f70504SFrançois Tigeot 
527f3c3d6fSHasso Tepper 	if (resource >= DRM_MAX_PCI_RESOURCE) {
537f3c3d6fSHasso Tepper 		DRM_ERROR("Resource %d too large\n", resource);
547f3c3d6fSHasso Tepper 		return 1;
557f3c3d6fSHasso Tepper 	}
567f3c3d6fSHasso Tepper 
577f3c3d6fSHasso Tepper 	if (dev->pcir[resource] != NULL) {
587f3c3d6fSHasso Tepper 		return 0;
597f3c3d6fSHasso Tepper 	}
607f3c3d6fSHasso Tepper 
615718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
6299f70504SFrançois Tigeot 	rid = PCIR_BAR(resource);
6399f70504SFrançois Tigeot 	res = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &rid,
6499f70504SFrançois Tigeot 	    RF_SHAREABLE);
655718399fSFrançois Tigeot 	DRM_LOCK(dev);
6699f70504SFrançois Tigeot 	if (res == NULL) {
677f3c3d6fSHasso Tepper 		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
687f3c3d6fSHasso Tepper 		return 1;
697f3c3d6fSHasso Tepper 	}
707f3c3d6fSHasso Tepper 
7199f70504SFrançois Tigeot 	if (dev->pcir[resource] == NULL) {
7299f70504SFrançois Tigeot 		dev->pcirid[resource] = rid;
7399f70504SFrançois Tigeot 		dev->pcir[resource] = res;
7499f70504SFrançois Tigeot 	}
7599f70504SFrançois Tigeot 
767f3c3d6fSHasso Tepper 	return 0;
777f3c3d6fSHasso Tepper }
787f3c3d6fSHasso Tepper 
79b3705d71SHasso Tepper unsigned long drm_get_resource_start(struct drm_device *dev,
80b3705d71SHasso Tepper 				     unsigned int resource)
817f3c3d6fSHasso Tepper {
827f3c3d6fSHasso Tepper 	if (drm_alloc_resource(dev, resource) != 0)
837f3c3d6fSHasso Tepper 		return 0;
847f3c3d6fSHasso Tepper 
857f3c3d6fSHasso Tepper 	return rman_get_start(dev->pcir[resource]);
867f3c3d6fSHasso Tepper }
877f3c3d6fSHasso Tepper 
88b3705d71SHasso Tepper unsigned long drm_get_resource_len(struct drm_device *dev,
89b3705d71SHasso Tepper 				   unsigned int resource)
907f3c3d6fSHasso Tepper {
917f3c3d6fSHasso Tepper 	if (drm_alloc_resource(dev, resource) != 0)
927f3c3d6fSHasso Tepper 		return 0;
937f3c3d6fSHasso Tepper 
947f3c3d6fSHasso Tepper 	return rman_get_size(dev->pcir[resource]);
957f3c3d6fSHasso Tepper }
967f3c3d6fSHasso Tepper 
97b3705d71SHasso Tepper int drm_addmap(struct drm_device * dev, unsigned long offset,
98b3705d71SHasso Tepper 	       unsigned long size,
99b3705d71SHasso Tepper     enum drm_map_type type, enum drm_map_flags flags, drm_local_map_t **map_ptr)
1007f3c3d6fSHasso Tepper {
101*f599cd46SFrançois Tigeot 	struct drm_local_map *map;
102*f599cd46SFrançois Tigeot 	struct drm_map_list *entry;
1037f3c3d6fSHasso Tepper 	int align;
1047f3c3d6fSHasso Tepper 	/*drm_agp_mem_t *entry;
1057f3c3d6fSHasso Tepper 	int valid;*/
1067f3c3d6fSHasso Tepper 
1077f3c3d6fSHasso Tepper 	/* Only allow shared memory to be removable since we only keep enough
1087f3c3d6fSHasso Tepper 	 * book keeping information about shared memory to allow for removal
1097f3c3d6fSHasso Tepper 	 * when processes fork.
1107f3c3d6fSHasso Tepper 	 */
1117f3c3d6fSHasso Tepper 	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
1127f3c3d6fSHasso Tepper 		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
1137f3c3d6fSHasso Tepper 		return EINVAL;
1147f3c3d6fSHasso Tepper 	}
1157f3c3d6fSHasso Tepper 	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
1167f3c3d6fSHasso Tepper 		DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
1177f3c3d6fSHasso Tepper 		    offset, size);
1187f3c3d6fSHasso Tepper 		return EINVAL;
1197f3c3d6fSHasso Tepper 	}
1207f3c3d6fSHasso Tepper 	if (offset + size < offset) {
1217f3c3d6fSHasso Tepper 		DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
1227f3c3d6fSHasso Tepper 		    offset, size);
1237f3c3d6fSHasso Tepper 		return EINVAL;
1247f3c3d6fSHasso Tepper 	}
1257f3c3d6fSHasso Tepper 
1267f3c3d6fSHasso Tepper 	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
1277f3c3d6fSHasso Tepper 	    size, type);
1287f3c3d6fSHasso Tepper 
1297f3c3d6fSHasso Tepper 	/* Check if this is just another version of a kernel-allocated map, and
1307f3c3d6fSHasso Tepper 	 * just hand that back if so.
1317f3c3d6fSHasso Tepper 	 */
1327f3c3d6fSHasso Tepper 	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
1337f3c3d6fSHasso Tepper 	    type == _DRM_SHM) {
134*f599cd46SFrançois Tigeot 		list_for_each_entry(entry, &dev->maplist, head) {
135*f599cd46SFrançois Tigeot 			if (entry->map->type == type && (entry->map->offset == offset ||
136*f599cd46SFrançois Tigeot 			    (entry->map->type == _DRM_SHM &&
137*f599cd46SFrançois Tigeot 			    entry->map->flags == _DRM_CONTAINS_LOCK))) {
138*f599cd46SFrançois Tigeot 				entry->map->size = size;
1397f3c3d6fSHasso Tepper 				DRM_DEBUG("Found kernel map %d\n", type);
1407f3c3d6fSHasso Tepper 				goto done;
1417f3c3d6fSHasso Tepper 			}
1427f3c3d6fSHasso Tepper 		}
1437f3c3d6fSHasso Tepper 	}
1445718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
1457f3c3d6fSHasso Tepper 
1467f3c3d6fSHasso Tepper 	/* Allocate a new map structure, fill it in, and do any type-specific
1477f3c3d6fSHasso Tepper 	 * initialization necessary.
1487f3c3d6fSHasso Tepper 	 */
1495718399fSFrançois Tigeot 	map = kmalloc(sizeof(*map), DRM_MEM_MAPS, M_ZERO | M_NOWAIT);
150b3705d71SHasso Tepper 	if (!map) {
1515718399fSFrançois Tigeot 		DRM_LOCK(dev);
1527f3c3d6fSHasso Tepper 		return ENOMEM;
153b3705d71SHasso Tepper 	}
1547f3c3d6fSHasso Tepper 
1557f3c3d6fSHasso Tepper 	map->offset = offset;
1567f3c3d6fSHasso Tepper 	map->size = size;
1577f3c3d6fSHasso Tepper 	map->type = type;
1587f3c3d6fSHasso Tepper 	map->flags = flags;
15999f70504SFrançois Tigeot 	map->handle = (void *)((unsigned long)alloc_unr(dev->map_unrhdr) <<
16099f70504SFrançois Tigeot 	    DRM_MAP_HANDLE_SHIFT);
1617f3c3d6fSHasso Tepper 
1627f3c3d6fSHasso Tepper 	switch (map->type) {
1637f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
16499f70504SFrançois Tigeot 		map->virtual = drm_ioremap(dev, map);
1657f3c3d6fSHasso Tepper 		if (!(map->flags & _DRM_WRITE_COMBINING))
1667f3c3d6fSHasso Tepper 			break;
1677f3c3d6fSHasso Tepper 		/* FALLTHROUGH */
1687f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
1697f3c3d6fSHasso Tepper 		if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
1707f3c3d6fSHasso Tepper 			map->mtrr = 1;
1717f3c3d6fSHasso Tepper 		break;
1727f3c3d6fSHasso Tepper 	case _DRM_SHM:
1735718399fSFrançois Tigeot 		map->virtual = kmalloc(map->size, DRM_MEM_MAPS, M_NOWAIT);
1747f3c3d6fSHasso Tepper 		DRM_DEBUG("%lu %d %p\n",
17599f70504SFrançois Tigeot 		    map->size, drm_order(map->size), map->virtual);
17699f70504SFrançois Tigeot 		if (!map->virtual) {
1775718399fSFrançois Tigeot 			drm_free(map, DRM_MEM_MAPS);
1785718399fSFrançois Tigeot 			DRM_LOCK(dev);
1797f3c3d6fSHasso Tepper 			return ENOMEM;
1807f3c3d6fSHasso Tepper 		}
18199f70504SFrançois Tigeot 		map->offset = (unsigned long)map->virtual;
1827f3c3d6fSHasso Tepper 		if (map->flags & _DRM_CONTAINS_LOCK) {
1837f3c3d6fSHasso Tepper 			/* Prevent a 2nd X Server from creating a 2nd lock */
1845718399fSFrançois Tigeot 			DRM_LOCK(dev);
1857f3c3d6fSHasso Tepper 			if (dev->lock.hw_lock != NULL) {
1865718399fSFrançois Tigeot 				DRM_UNLOCK(dev);
1875718399fSFrançois Tigeot 				drm_free(map->virtual, DRM_MEM_MAPS);
1885718399fSFrançois Tigeot 				drm_free(map, DRM_MEM_MAPS);
1897f3c3d6fSHasso Tepper 				return EBUSY;
1907f3c3d6fSHasso Tepper 			}
19199f70504SFrançois Tigeot 			dev->lock.hw_lock = map->virtual; /* Pointer to lock */
1925718399fSFrançois Tigeot 			DRM_UNLOCK(dev);
1937f3c3d6fSHasso Tepper 		}
1947f3c3d6fSHasso Tepper 		break;
1957f3c3d6fSHasso Tepper 	case _DRM_AGP:
1967f3c3d6fSHasso Tepper 		/*valid = 0;*/
1977f3c3d6fSHasso Tepper 		/* In some cases (i810 driver), user space may have already
1987f3c3d6fSHasso Tepper 		 * added the AGP base itself, because dev->agp->base previously
1997f3c3d6fSHasso Tepper 		 * only got set during AGP enable.  So, only add the base
2007f3c3d6fSHasso Tepper 		 * address if the map's offset isn't already within the
2017f3c3d6fSHasso Tepper 		 * aperture.
2027f3c3d6fSHasso Tepper 		 */
2037f3c3d6fSHasso Tepper 		if (map->offset < dev->agp->base ||
2047f3c3d6fSHasso Tepper 		    map->offset > dev->agp->base +
2057f3c3d6fSHasso Tepper 		    dev->agp->info.ai_aperture_size - 1) {
2067f3c3d6fSHasso Tepper 			map->offset += dev->agp->base;
2077f3c3d6fSHasso Tepper 		}
2087f3c3d6fSHasso Tepper 		map->mtrr   = dev->agp->mtrr; /* for getmap */
2097f3c3d6fSHasso Tepper 		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
2107f3c3d6fSHasso Tepper 			if ((map->offset >= entry->bound) &&
2117f3c3d6fSHasso Tepper 			    (map->offset + map->size <=
2127f3c3d6fSHasso Tepper 			    entry->bound + entry->pages * PAGE_SIZE)) {
2137f3c3d6fSHasso Tepper 				valid = 1;
2147f3c3d6fSHasso Tepper 				break;
2157f3c3d6fSHasso Tepper 			}
2167f3c3d6fSHasso Tepper 		}
2177f3c3d6fSHasso Tepper 		if (!valid) {
2185718399fSFrançois Tigeot 			drm_free(map, DRM_MEM_MAPS);
2195718399fSFrançois Tigeot 			DRM_LOCK(dev);
2207f3c3d6fSHasso Tepper 			return EACCES;
2217f3c3d6fSHasso Tepper 		}*/
2227f3c3d6fSHasso Tepper 		break;
2237f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
2247f3c3d6fSHasso Tepper 		if (!dev->sg) {
2255718399fSFrançois Tigeot 			drm_free(map, DRM_MEM_MAPS);
2265718399fSFrançois Tigeot 			DRM_LOCK(dev);
2277f3c3d6fSHasso Tepper 			return EINVAL;
2287f3c3d6fSHasso Tepper 		}
22999f70504SFrançois Tigeot 		map->virtual = (void *)(dev->sg->vaddr + offset);
23099f70504SFrançois Tigeot 		map->offset = dev->sg->vaddr + offset;
2317f3c3d6fSHasso Tepper 		break;
2327f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
2337f3c3d6fSHasso Tepper 		/* Unfortunately, we don't get any alignment specification from
2347f3c3d6fSHasso Tepper 		 * the caller, so we have to guess.  drm_pci_alloc requires
2357f3c3d6fSHasso Tepper 		 * a power-of-two alignment, so try to align the bus address of
2367f3c3d6fSHasso Tepper 		 * the map to it size if possible, otherwise just assume
2377f3c3d6fSHasso Tepper 		 * PAGE_SIZE alignment.
2387f3c3d6fSHasso Tepper 		 */
2397f3c3d6fSHasso Tepper 		align = map->size;
2407f3c3d6fSHasso Tepper 		if ((align & (align - 1)) != 0)
2417f3c3d6fSHasso Tepper 			align = PAGE_SIZE;
2427f3c3d6fSHasso Tepper 		map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
2437f3c3d6fSHasso Tepper 		if (map->dmah == NULL) {
2445718399fSFrançois Tigeot 			drm_free(map, DRM_MEM_MAPS);
2455718399fSFrançois Tigeot 			DRM_LOCK(dev);
2467f3c3d6fSHasso Tepper 			return ENOMEM;
2477f3c3d6fSHasso Tepper 		}
24899f70504SFrançois Tigeot 		map->virtual = map->dmah->vaddr;
2497f3c3d6fSHasso Tepper 		map->offset = map->dmah->busaddr;
2507f3c3d6fSHasso Tepper 		break;
2517f3c3d6fSHasso Tepper 	default:
2527f3c3d6fSHasso Tepper 		DRM_ERROR("Bad map type %d\n", map->type);
2535718399fSFrançois Tigeot 		drm_free(map, DRM_MEM_MAPS);
2545718399fSFrançois Tigeot 		DRM_LOCK(dev);
2557f3c3d6fSHasso Tepper 		return EINVAL;
2567f3c3d6fSHasso Tepper 	}
2577f3c3d6fSHasso Tepper 
2585718399fSFrançois Tigeot 	DRM_LOCK(dev);
259*f599cd46SFrançois Tigeot 	list_add(&entry->head, &dev->maplist);
2607f3c3d6fSHasso Tepper 
2617f3c3d6fSHasso Tepper done:
2627f3c3d6fSHasso Tepper 	/* Jumped to, with lock held, when a kernel map is found. */
2637f3c3d6fSHasso Tepper 
2647f3c3d6fSHasso Tepper 	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
2657f3c3d6fSHasso Tepper 	    map->size);
2667f3c3d6fSHasso Tepper 
2677f3c3d6fSHasso Tepper 	*map_ptr = map;
2687f3c3d6fSHasso Tepper 
2697f3c3d6fSHasso Tepper 	return 0;
2707f3c3d6fSHasso Tepper }
2717f3c3d6fSHasso Tepper 
272b3705d71SHasso Tepper int drm_addmap_ioctl(struct drm_device *dev, void *data,
273b3705d71SHasso Tepper 		     struct drm_file *file_priv)
2747f3c3d6fSHasso Tepper {
275b3705d71SHasso Tepper 	struct drm_map *request = data;
2767f3c3d6fSHasso Tepper 	drm_local_map_t *map;
2777f3c3d6fSHasso Tepper 	int err;
2787f3c3d6fSHasso Tepper 
2797f3c3d6fSHasso Tepper 	if (!(dev->flags & (FREAD|FWRITE)))
2807f3c3d6fSHasso Tepper 		return EACCES; /* Require read/write */
2817f3c3d6fSHasso Tepper 
2827f3c3d6fSHasso Tepper 	if (!DRM_SUSER(DRM_CURPROC) && request->type != _DRM_AGP)
2837f3c3d6fSHasso Tepper 		return EACCES;
2847f3c3d6fSHasso Tepper 
2855718399fSFrançois Tigeot 	DRM_LOCK(dev);
2867f3c3d6fSHasso Tepper 	err = drm_addmap(dev, request->offset, request->size, request->type,
2877f3c3d6fSHasso Tepper 	    request->flags, &map);
2885718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
2897f3c3d6fSHasso Tepper 	if (err != 0)
2907f3c3d6fSHasso Tepper 		return err;
2917f3c3d6fSHasso Tepper 
2927f3c3d6fSHasso Tepper 	request->offset = map->offset;
2937f3c3d6fSHasso Tepper 	request->size = map->size;
2947f3c3d6fSHasso Tepper 	request->type = map->type;
2957f3c3d6fSHasso Tepper 	request->flags = map->flags;
2967f3c3d6fSHasso Tepper 	request->mtrr   = map->mtrr;
29799f70504SFrançois Tigeot 	request->handle = (void *)map->handle;
2987f3c3d6fSHasso Tepper 
2997f3c3d6fSHasso Tepper 	return 0;
3007f3c3d6fSHasso Tepper }
3017f3c3d6fSHasso Tepper 
302*f599cd46SFrançois Tigeot void drm_rmmap(struct drm_device *dev, struct drm_local_map *map)
3037f3c3d6fSHasso Tepper {
304*f599cd46SFrançois Tigeot 	struct drm_map_list *r_list = NULL, *list_t;
305*f599cd46SFrançois Tigeot 	int found = 0;
306*f599cd46SFrançois Tigeot 
3075718399fSFrançois Tigeot 	DRM_LOCK_ASSERT(dev);
3087f3c3d6fSHasso Tepper 
309c6bd1f0dSHasso Tepper 	if (map == NULL)
310c6bd1f0dSHasso Tepper 		return;
311c6bd1f0dSHasso Tepper 
312*f599cd46SFrançois Tigeot 	/* Find the list entry for the map and remove it */
313*f599cd46SFrançois Tigeot 	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
314*f599cd46SFrançois Tigeot 		if (r_list->map == map) {
315*f599cd46SFrançois Tigeot 			list_del(&r_list->head);
316*f599cd46SFrançois Tigeot 			drm_free(r_list, DRM_MEM_DRIVER);
317*f599cd46SFrançois Tigeot 			found = 1;
318*f599cd46SFrançois Tigeot 			break;
319*f599cd46SFrançois Tigeot 		}
320*f599cd46SFrançois Tigeot 	}
321*f599cd46SFrançois Tigeot 
322*f599cd46SFrançois Tigeot 	if (!found)
323*f599cd46SFrançois Tigeot 		return;
3247f3c3d6fSHasso Tepper 
3257f3c3d6fSHasso Tepper 	switch (map->type) {
3267f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
3277f3c3d6fSHasso Tepper 		if (map->bsr == NULL)
3287f3c3d6fSHasso Tepper 			drm_ioremapfree(map);
3297f3c3d6fSHasso Tepper 		/* FALLTHROUGH */
3307f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
3317f3c3d6fSHasso Tepper 		if (map->mtrr) {
3325718399fSFrançois Tigeot 			int __unused retcode;
3337f3c3d6fSHasso Tepper 
3347f3c3d6fSHasso Tepper 			retcode = drm_mtrr_del(0, map->offset, map->size,
3357f3c3d6fSHasso Tepper 			    DRM_MTRR_WC);
3367f3c3d6fSHasso Tepper 			DRM_DEBUG("mtrr_del = %d\n", retcode);
3377f3c3d6fSHasso Tepper 		}
3387f3c3d6fSHasso Tepper 		break;
3397f3c3d6fSHasso Tepper 	case _DRM_SHM:
3405718399fSFrançois Tigeot 		drm_free(map->virtual, DRM_MEM_MAPS);
3417f3c3d6fSHasso Tepper 		break;
3427f3c3d6fSHasso Tepper 	case _DRM_AGP:
3437f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
3447f3c3d6fSHasso Tepper 		break;
3457f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
3467f3c3d6fSHasso Tepper 		drm_pci_free(dev, map->dmah);
3477f3c3d6fSHasso Tepper 		break;
3487f3c3d6fSHasso Tepper 	default:
3497f3c3d6fSHasso Tepper 		DRM_ERROR("Bad map type %d\n", map->type);
3507f3c3d6fSHasso Tepper 		break;
3517f3c3d6fSHasso Tepper 	}
3527f3c3d6fSHasso Tepper 
3537f3c3d6fSHasso Tepper 	if (map->bsr != NULL) {
3547f3c3d6fSHasso Tepper 		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
3557f3c3d6fSHasso Tepper 		    map->bsr);
3567f3c3d6fSHasso Tepper 	}
3577f3c3d6fSHasso Tepper 
3585718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
35999f70504SFrançois Tigeot 	if (map->handle)
36099f70504SFrançois Tigeot 		free_unr(dev->map_unrhdr, (unsigned long)map->handle >>
36199f70504SFrançois Tigeot 		    DRM_MAP_HANDLE_SHIFT);
3625718399fSFrançois Tigeot 	DRM_LOCK(dev);
36399f70504SFrançois Tigeot 
3645718399fSFrançois Tigeot 	drm_free(map, DRM_MEM_MAPS);
3657f3c3d6fSHasso Tepper }
3667f3c3d6fSHasso Tepper 
367*f599cd46SFrançois Tigeot /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
368*f599cd46SFrançois Tigeot  * the last close of the device, and this is necessary for cleanup when things
369*f599cd46SFrançois Tigeot  * exit uncleanly.  Therefore, having userland manually remove mappings seems
370*f599cd46SFrançois Tigeot  * like a pointless exercise since they're going away anyway.
371*f599cd46SFrançois Tigeot  *
372*f599cd46SFrançois Tigeot  * One use case might be after addmap is allowed for normal users for SHM and
373*f599cd46SFrançois Tigeot  * gets used by drivers that the server doesn't need to care about.  This seems
374*f599cd46SFrançois Tigeot  * unlikely.
375*f599cd46SFrançois Tigeot  *
376*f599cd46SFrançois Tigeot  * \param inode device inode.
377*f599cd46SFrançois Tigeot  * \param file_priv DRM file private.
378*f599cd46SFrançois Tigeot  * \param cmd command.
379*f599cd46SFrançois Tigeot  * \param arg pointer to a struct drm_map structure.
380*f599cd46SFrançois Tigeot  * \return zero on success or a negative value on error.
3817f3c3d6fSHasso Tepper  */
382b3705d71SHasso Tepper int drm_rmmap_ioctl(struct drm_device *dev, void *data,
383b3705d71SHasso Tepper 		    struct drm_file *file_priv)
3847f3c3d6fSHasso Tepper {
385b3705d71SHasso Tepper 	struct drm_map *request = data;
386*f599cd46SFrançois Tigeot 	struct drm_local_map *map = NULL;
387*f599cd46SFrançois Tigeot 	struct drm_map_list *r_list;
3887f3c3d6fSHasso Tepper 
3895718399fSFrançois Tigeot 	DRM_LOCK(dev);
390*f599cd46SFrançois Tigeot 	list_for_each_entry(r_list, &dev->maplist, head) {
391*f599cd46SFrançois Tigeot 		if (r_list->map &&
392*f599cd46SFrançois Tigeot 		    r_list->user_token == (unsigned long)request->handle &&
393*f599cd46SFrançois Tigeot 		    r_list->map->flags & _DRM_REMOVABLE) {
394*f599cd46SFrançois Tigeot 			map = r_list->map;
3957f3c3d6fSHasso Tepper 			break;
3967f3c3d6fSHasso Tepper 		}
397*f599cd46SFrançois Tigeot 	}
3987f3c3d6fSHasso Tepper 
399*f599cd46SFrançois Tigeot 	/* List has wrapped around to the head pointer, or its empty we didn't
400*f599cd46SFrançois Tigeot 	 * find anything.
401*f599cd46SFrançois Tigeot 	 */
402*f599cd46SFrançois Tigeot 	if (list_empty(&dev->maplist) || !map) {
4035718399fSFrançois Tigeot 		DRM_UNLOCK(dev);
404*f599cd46SFrançois Tigeot 		return -EINVAL;
405*f599cd46SFrançois Tigeot 	}
406*f599cd46SFrançois Tigeot 
407*f599cd46SFrançois Tigeot 	/* Register and framebuffer maps are permanent */
408*f599cd46SFrançois Tigeot 	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
409*f599cd46SFrançois Tigeot 		DRM_UNLOCK(dev);
410*f599cd46SFrançois Tigeot 		return 0;
4117f3c3d6fSHasso Tepper 	}
4127f3c3d6fSHasso Tepper 
4137f3c3d6fSHasso Tepper 	drm_rmmap(dev, map);
4147f3c3d6fSHasso Tepper 
4155718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
4167f3c3d6fSHasso Tepper 
4177f3c3d6fSHasso Tepper 	return 0;
4187f3c3d6fSHasso Tepper }
4197f3c3d6fSHasso Tepper 
4207f3c3d6fSHasso Tepper 
421b3705d71SHasso Tepper static void drm_cleanup_buf_error(struct drm_device *dev,
422b3705d71SHasso Tepper 				  drm_buf_entry_t *entry)
4237f3c3d6fSHasso Tepper {
4247f3c3d6fSHasso Tepper 	int i;
4257f3c3d6fSHasso Tepper 
4267f3c3d6fSHasso Tepper 	if (entry->seg_count) {
4277f3c3d6fSHasso Tepper 		for (i = 0; i < entry->seg_count; i++) {
4287f3c3d6fSHasso Tepper 			drm_pci_free(dev, entry->seglist[i]);
4297f3c3d6fSHasso Tepper 		}
4305718399fSFrançois Tigeot 		drm_free(entry->seglist, DRM_MEM_SEGS);
4317f3c3d6fSHasso Tepper 
4327f3c3d6fSHasso Tepper 		entry->seg_count = 0;
4337f3c3d6fSHasso Tepper 	}
4347f3c3d6fSHasso Tepper 
4357f3c3d6fSHasso Tepper    	if (entry->buf_count) {
4367f3c3d6fSHasso Tepper 	   	for (i = 0; i < entry->buf_count; i++) {
4375718399fSFrançois Tigeot 			drm_free(entry->buflist[i].dev_private, DRM_MEM_BUFS);
4387f3c3d6fSHasso Tepper 		}
4395718399fSFrançois Tigeot 		drm_free(entry->buflist, DRM_MEM_BUFS);
4407f3c3d6fSHasso Tepper 
4417f3c3d6fSHasso Tepper 		entry->buf_count = 0;
4427f3c3d6fSHasso Tepper 	}
4437f3c3d6fSHasso Tepper }
4447f3c3d6fSHasso Tepper 
445b3705d71SHasso Tepper static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
4467f3c3d6fSHasso Tepper {
4477f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
4487f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
4497f3c3d6fSHasso Tepper 	/*drm_agp_mem_t *agp_entry;
4507f3c3d6fSHasso Tepper 	int valid*/
4517f3c3d6fSHasso Tepper 	drm_buf_t *buf;
4527f3c3d6fSHasso Tepper 	unsigned long offset;
4537f3c3d6fSHasso Tepper 	unsigned long agp_offset;
4547f3c3d6fSHasso Tepper 	int count;
4557f3c3d6fSHasso Tepper 	int order;
4567f3c3d6fSHasso Tepper 	int size;
4577f3c3d6fSHasso Tepper 	int alignment;
4587f3c3d6fSHasso Tepper 	int page_order;
4597f3c3d6fSHasso Tepper 	int total;
4607f3c3d6fSHasso Tepper 	int byte_count;
4617f3c3d6fSHasso Tepper 	int i;
4627f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
4637f3c3d6fSHasso Tepper 
4647f3c3d6fSHasso Tepper 	count = request->count;
4657f3c3d6fSHasso Tepper 	order = drm_order(request->size);
4667f3c3d6fSHasso Tepper 	size = 1 << order;
4677f3c3d6fSHasso Tepper 
4687f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
4697f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
4707f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
4717f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
4727f3c3d6fSHasso Tepper 
4737f3c3d6fSHasso Tepper 	byte_count = 0;
4747f3c3d6fSHasso Tepper 	agp_offset = dev->agp->base + request->agp_start;
4757f3c3d6fSHasso Tepper 
4767f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
4777f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
4787f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
4797f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
4807f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
4817f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
4827f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
4837f3c3d6fSHasso Tepper 
4847f3c3d6fSHasso Tepper 	/* Make sure buffers are located in AGP memory that we own */
4857f3c3d6fSHasso Tepper 	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
4867f3c3d6fSHasso Tepper 	 * memory.  Safe to ignore for now because these ioctls are still
4877f3c3d6fSHasso Tepper 	 * root-only.
4887f3c3d6fSHasso Tepper 	 */
4897f3c3d6fSHasso Tepper 	/*valid = 0;
4907f3c3d6fSHasso Tepper 	for (agp_entry = dev->agp->memory; agp_entry;
4917f3c3d6fSHasso Tepper 	    agp_entry = agp_entry->next) {
4927f3c3d6fSHasso Tepper 		if ((agp_offset >= agp_entry->bound) &&
4937f3c3d6fSHasso Tepper 		    (agp_offset + total * count <=
4947f3c3d6fSHasso Tepper 		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
4957f3c3d6fSHasso Tepper 			valid = 1;
4967f3c3d6fSHasso Tepper 			break;
4977f3c3d6fSHasso Tepper 		}
4987f3c3d6fSHasso Tepper 	}
4997f3c3d6fSHasso Tepper 	if (!valid) {
5007f3c3d6fSHasso Tepper 		DRM_DEBUG("zone invalid\n");
5017f3c3d6fSHasso Tepper 		return EINVAL;
5027f3c3d6fSHasso Tepper 	}*/
5037f3c3d6fSHasso Tepper 
5047f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
5057f3c3d6fSHasso Tepper 
5065718399fSFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
5077f3c3d6fSHasso Tepper 	    M_NOWAIT | M_ZERO);
5087f3c3d6fSHasso Tepper 	if (!entry->buflist) {
5097f3c3d6fSHasso Tepper 		return ENOMEM;
5107f3c3d6fSHasso Tepper 	}
5117f3c3d6fSHasso Tepper 
5127f3c3d6fSHasso Tepper 	entry->buf_size = size;
5137f3c3d6fSHasso Tepper 	entry->page_order = page_order;
5147f3c3d6fSHasso Tepper 
5157f3c3d6fSHasso Tepper 	offset = 0;
5167f3c3d6fSHasso Tepper 
5177f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
5187f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
5197f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
5207f3c3d6fSHasso Tepper 		buf->total   = alignment;
5217f3c3d6fSHasso Tepper 		buf->order   = order;
5227f3c3d6fSHasso Tepper 		buf->used    = 0;
5237f3c3d6fSHasso Tepper 
5247f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
5257f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
5267f3c3d6fSHasso Tepper 		buf->address = (void *)(agp_offset + offset);
5277f3c3d6fSHasso Tepper 		buf->next    = NULL;
5287f3c3d6fSHasso Tepper 		buf->pending = 0;
5297f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
5307f3c3d6fSHasso Tepper 
531b3705d71SHasso Tepper 		buf->dev_priv_size = dev->driver->buf_priv_size;
5325718399fSFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, DRM_MEM_BUFS,
5337f3c3d6fSHasso Tepper 		    M_NOWAIT | M_ZERO);
5347f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
5357f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
5367f3c3d6fSHasso Tepper 			entry->buf_count = count;
5377f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
5387f3c3d6fSHasso Tepper 			return ENOMEM;
5397f3c3d6fSHasso Tepper 		}
5407f3c3d6fSHasso Tepper 
5417f3c3d6fSHasso Tepper 		offset += alignment;
5427f3c3d6fSHasso Tepper 		entry->buf_count++;
5437f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
5447f3c3d6fSHasso Tepper 	}
5457f3c3d6fSHasso Tepper 
5467f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
5477f3c3d6fSHasso Tepper 
5485718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
549b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
550b3705d71SHasso Tepper 	    DRM_MEM_BUFS, M_NOWAIT);
5517f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
5527f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
5537f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
5547f3c3d6fSHasso Tepper 		return ENOMEM;
5557f3c3d6fSHasso Tepper 	}
5567f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
5577f3c3d6fSHasso Tepper 
5587f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
5597f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
5607f3c3d6fSHasso Tepper 	}
5617f3c3d6fSHasso Tepper 
5627f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
5637f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
5647f3c3d6fSHasso Tepper 
5657f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
5667f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
5677f3c3d6fSHasso Tepper 
5687f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
5697f3c3d6fSHasso Tepper 	request->size = size;
5707f3c3d6fSHasso Tepper 
5717f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_AGP;
5727f3c3d6fSHasso Tepper 
5737f3c3d6fSHasso Tepper 	return 0;
5747f3c3d6fSHasso Tepper }
5757f3c3d6fSHasso Tepper 
576b3705d71SHasso Tepper static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
5777f3c3d6fSHasso Tepper {
5787f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
5797f3c3d6fSHasso Tepper 	int count;
5807f3c3d6fSHasso Tepper 	int order;
5817f3c3d6fSHasso Tepper 	int size;
5827f3c3d6fSHasso Tepper 	int total;
5837f3c3d6fSHasso Tepper 	int page_order;
5847f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
5857f3c3d6fSHasso Tepper 	drm_buf_t *buf;
5867f3c3d6fSHasso Tepper 	int alignment;
5877f3c3d6fSHasso Tepper 	unsigned long offset;
5887f3c3d6fSHasso Tepper 	int i;
5897f3c3d6fSHasso Tepper 	int byte_count;
5907f3c3d6fSHasso Tepper 	int page_count;
5917f3c3d6fSHasso Tepper 	unsigned long *temp_pagelist;
5927f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
5937f3c3d6fSHasso Tepper 
5947f3c3d6fSHasso Tepper 	count = request->count;
5957f3c3d6fSHasso Tepper 	order = drm_order(request->size);
5967f3c3d6fSHasso Tepper 	size = 1 << order;
5977f3c3d6fSHasso Tepper 
5987f3c3d6fSHasso Tepper 	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
5997f3c3d6fSHasso Tepper 	    request->count, request->size, size, order);
6007f3c3d6fSHasso Tepper 
6017f3c3d6fSHasso Tepper 	alignment = (request->flags & _DRM_PAGE_ALIGN)
6027f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
6037f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
6047f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
6057f3c3d6fSHasso Tepper 
6067f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
6077f3c3d6fSHasso Tepper 
6085718399fSFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
6097f3c3d6fSHasso Tepper 	    M_NOWAIT | M_ZERO);
6105718399fSFrançois Tigeot 	entry->seglist = kmalloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS,
6117f3c3d6fSHasso Tepper 	    M_NOWAIT | M_ZERO);
6127f3c3d6fSHasso Tepper 
6137f3c3d6fSHasso Tepper 	/* Keep the original pagelist until we know all the allocations
6147f3c3d6fSHasso Tepper 	 * have succeeded
6157f3c3d6fSHasso Tepper 	 */
6165718399fSFrançois Tigeot 	temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
617b3705d71SHasso Tepper 	    sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT);
6187f3c3d6fSHasso Tepper 
6197f3c3d6fSHasso Tepper 	if (entry->buflist == NULL || entry->seglist == NULL ||
6207f3c3d6fSHasso Tepper 	    temp_pagelist == NULL) {
6215718399fSFrançois Tigeot 		drm_free(temp_pagelist, DRM_MEM_PAGES);
6225718399fSFrançois Tigeot 		drm_free(entry->seglist, DRM_MEM_SEGS);
6235718399fSFrançois Tigeot 		drm_free(entry->buflist, DRM_MEM_BUFS);
6247f3c3d6fSHasso Tepper 		return ENOMEM;
6257f3c3d6fSHasso Tepper 	}
6267f3c3d6fSHasso Tepper 
6277f3c3d6fSHasso Tepper 	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
6287f3c3d6fSHasso Tepper 	    sizeof(*dma->pagelist));
6297f3c3d6fSHasso Tepper 
6307f3c3d6fSHasso Tepper 	DRM_DEBUG("pagelist: %d entries\n",
6317f3c3d6fSHasso Tepper 	    dma->page_count + (count << page_order));
6327f3c3d6fSHasso Tepper 
6337f3c3d6fSHasso Tepper 	entry->buf_size	= size;
6347f3c3d6fSHasso Tepper 	entry->page_order = page_order;
6357f3c3d6fSHasso Tepper 	byte_count = 0;
6367f3c3d6fSHasso Tepper 	page_count = 0;
6377f3c3d6fSHasso Tepper 
6387f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
6395718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
6407f3c3d6fSHasso Tepper 		drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
6417f3c3d6fSHasso Tepper 		    0xfffffffful);
6425718399fSFrançois Tigeot 		spin_lock(&dev->dma_lock);
6437f3c3d6fSHasso Tepper 		if (dmah == NULL) {
6447f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
6457f3c3d6fSHasso Tepper 			entry->buf_count = count;
6467f3c3d6fSHasso Tepper 			entry->seg_count = count;
6477f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
6485718399fSFrançois Tigeot 			drm_free(temp_pagelist, DRM_MEM_PAGES);
6497f3c3d6fSHasso Tepper 			return ENOMEM;
6507f3c3d6fSHasso Tepper 		}
6517f3c3d6fSHasso Tepper 
6527f3c3d6fSHasso Tepper 		entry->seglist[entry->seg_count++] = dmah;
6537f3c3d6fSHasso Tepper 		for (i = 0; i < (1 << page_order); i++) {
6547f3c3d6fSHasso Tepper 			DRM_DEBUG("page %d @ %p\n",
6557f3c3d6fSHasso Tepper 			    dma->page_count + page_count,
6567f3c3d6fSHasso Tepper 			    (char *)dmah->vaddr + PAGE_SIZE * i);
6577f3c3d6fSHasso Tepper 			temp_pagelist[dma->page_count + page_count++] =
6587f3c3d6fSHasso Tepper 			    (long)dmah->vaddr + PAGE_SIZE * i;
6597f3c3d6fSHasso Tepper 		}
6607f3c3d6fSHasso Tepper 		for (offset = 0;
6617f3c3d6fSHasso Tepper 		    offset + size <= total && entry->buf_count < count;
6627f3c3d6fSHasso Tepper 		    offset += alignment, ++entry->buf_count) {
6637f3c3d6fSHasso Tepper 			buf	     = &entry->buflist[entry->buf_count];
6647f3c3d6fSHasso Tepper 			buf->idx     = dma->buf_count + entry->buf_count;
6657f3c3d6fSHasso Tepper 			buf->total   = alignment;
6667f3c3d6fSHasso Tepper 			buf->order   = order;
6677f3c3d6fSHasso Tepper 			buf->used    = 0;
6687f3c3d6fSHasso Tepper 			buf->offset  = (dma->byte_count + byte_count + offset);
6697f3c3d6fSHasso Tepper 			buf->address = ((char *)dmah->vaddr + offset);
6707f3c3d6fSHasso Tepper 			buf->bus_address = dmah->busaddr + offset;
6717f3c3d6fSHasso Tepper 			buf->next    = NULL;
6727f3c3d6fSHasso Tepper 			buf->pending = 0;
6737f3c3d6fSHasso Tepper 			buf->file_priv = NULL;
6747f3c3d6fSHasso Tepper 
675b3705d71SHasso Tepper 			buf->dev_priv_size = dev->driver->buf_priv_size;
6765718399fSFrançois Tigeot 			buf->dev_private = kmalloc(buf->dev_priv_size,
677b3705d71SHasso Tepper 			    DRM_MEM_BUFS, M_NOWAIT | M_ZERO);
6787f3c3d6fSHasso Tepper 			if (buf->dev_private == NULL) {
6797f3c3d6fSHasso Tepper 				/* Set count correctly so we free the proper amount. */
6807f3c3d6fSHasso Tepper 				entry->buf_count = count;
6817f3c3d6fSHasso Tepper 				entry->seg_count = count;
6827f3c3d6fSHasso Tepper 				drm_cleanup_buf_error(dev, entry);
6835718399fSFrançois Tigeot 				drm_free(temp_pagelist, DRM_MEM_PAGES);
6847f3c3d6fSHasso Tepper 				return ENOMEM;
6857f3c3d6fSHasso Tepper 			}
6867f3c3d6fSHasso Tepper 
6877f3c3d6fSHasso Tepper 			DRM_DEBUG("buffer %d @ %p\n",
6887f3c3d6fSHasso Tepper 			    entry->buf_count, buf->address);
6897f3c3d6fSHasso Tepper 		}
6907f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
6917f3c3d6fSHasso Tepper 	}
6927f3c3d6fSHasso Tepper 
6935718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
694b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
695b3705d71SHasso Tepper 	    DRM_MEM_BUFS, M_NOWAIT);
6967f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
6977f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
6987f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
6995718399fSFrançois Tigeot 		drm_free(temp_pagelist, DRM_MEM_PAGES);
7007f3c3d6fSHasso Tepper 		return ENOMEM;
7017f3c3d6fSHasso Tepper 	}
7027f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
7037f3c3d6fSHasso Tepper 
7047f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
7057f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
7067f3c3d6fSHasso Tepper 	}
7077f3c3d6fSHasso Tepper 
7087f3c3d6fSHasso Tepper 	/* No allocations failed, so now we can replace the orginal pagelist
7097f3c3d6fSHasso Tepper 	 * with the new one.
7107f3c3d6fSHasso Tepper 	 */
7115718399fSFrançois Tigeot 	drm_free(dma->pagelist, DRM_MEM_PAGES);
7127f3c3d6fSHasso Tepper 	dma->pagelist = temp_pagelist;
7137f3c3d6fSHasso Tepper 
7147f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
7157f3c3d6fSHasso Tepper 	dma->seg_count += entry->seg_count;
7167f3c3d6fSHasso Tepper 	dma->page_count += entry->seg_count << page_order;
7177f3c3d6fSHasso Tepper 	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
7187f3c3d6fSHasso Tepper 
7197f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
7207f3c3d6fSHasso Tepper 	request->size = size;
7217f3c3d6fSHasso Tepper 
7227f3c3d6fSHasso Tepper 	return 0;
7237f3c3d6fSHasso Tepper 
7247f3c3d6fSHasso Tepper }
7257f3c3d6fSHasso Tepper 
726b3705d71SHasso Tepper static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
7277f3c3d6fSHasso Tepper {
7287f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
7297f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
7307f3c3d6fSHasso Tepper 	drm_buf_t *buf;
7317f3c3d6fSHasso Tepper 	unsigned long offset;
7327f3c3d6fSHasso Tepper 	unsigned long agp_offset;
7337f3c3d6fSHasso Tepper 	int count;
7347f3c3d6fSHasso Tepper 	int order;
7357f3c3d6fSHasso Tepper 	int size;
7367f3c3d6fSHasso Tepper 	int alignment;
7377f3c3d6fSHasso Tepper 	int page_order;
7387f3c3d6fSHasso Tepper 	int total;
7397f3c3d6fSHasso Tepper 	int byte_count;
7407f3c3d6fSHasso Tepper 	int i;
7417f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
7427f3c3d6fSHasso Tepper 
7437f3c3d6fSHasso Tepper 	count = request->count;
7447f3c3d6fSHasso Tepper 	order = drm_order(request->size);
7457f3c3d6fSHasso Tepper 	size = 1 << order;
7467f3c3d6fSHasso Tepper 
7477f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
7487f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
7497f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
7507f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
7517f3c3d6fSHasso Tepper 
7527f3c3d6fSHasso Tepper 	byte_count = 0;
7537f3c3d6fSHasso Tepper 	agp_offset = request->agp_start;
7547f3c3d6fSHasso Tepper 
7557f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
7567f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
7577f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
7587f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
7597f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
7607f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
7617f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
7627f3c3d6fSHasso Tepper 
7637f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
7647f3c3d6fSHasso Tepper 
7655718399fSFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
7667f3c3d6fSHasso Tepper 	    M_NOWAIT | M_ZERO);
7677f3c3d6fSHasso Tepper 	if (entry->buflist == NULL)
7687f3c3d6fSHasso Tepper 		return ENOMEM;
7697f3c3d6fSHasso Tepper 
7707f3c3d6fSHasso Tepper 	entry->buf_size = size;
7717f3c3d6fSHasso Tepper 	entry->page_order = page_order;
7727f3c3d6fSHasso Tepper 
7737f3c3d6fSHasso Tepper 	offset = 0;
7747f3c3d6fSHasso Tepper 
7757f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
7767f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
7777f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
7787f3c3d6fSHasso Tepper 		buf->total   = alignment;
7797f3c3d6fSHasso Tepper 		buf->order   = order;
7807f3c3d6fSHasso Tepper 		buf->used    = 0;
7817f3c3d6fSHasso Tepper 
7827f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
7837f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
78499f70504SFrançois Tigeot 		buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
7857f3c3d6fSHasso Tepper 		buf->next    = NULL;
7867f3c3d6fSHasso Tepper 		buf->pending = 0;
7877f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
7887f3c3d6fSHasso Tepper 
789b3705d71SHasso Tepper 		buf->dev_priv_size = dev->driver->buf_priv_size;
7905718399fSFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, DRM_MEM_BUFS,
7917f3c3d6fSHasso Tepper 		    M_NOWAIT | M_ZERO);
7927f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
7937f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
7947f3c3d6fSHasso Tepper 			entry->buf_count = count;
7957f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
7967f3c3d6fSHasso Tepper 			return ENOMEM;
7977f3c3d6fSHasso Tepper 		}
7987f3c3d6fSHasso Tepper 
7997f3c3d6fSHasso Tepper 		DRM_DEBUG("buffer %d @ %p\n",
8007f3c3d6fSHasso Tepper 		    entry->buf_count, buf->address);
8017f3c3d6fSHasso Tepper 
8027f3c3d6fSHasso Tepper 		offset += alignment;
8037f3c3d6fSHasso Tepper 		entry->buf_count++;
8047f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
8057f3c3d6fSHasso Tepper 	}
8067f3c3d6fSHasso Tepper 
8077f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
8087f3c3d6fSHasso Tepper 
8095718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
810b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
811b3705d71SHasso Tepper 	    DRM_MEM_BUFS, M_NOWAIT);
8127f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
8137f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
8147f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
8157f3c3d6fSHasso Tepper 		return ENOMEM;
8167f3c3d6fSHasso Tepper 	}
8177f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
8187f3c3d6fSHasso Tepper 
8197f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
8207f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
8217f3c3d6fSHasso Tepper 	}
8227f3c3d6fSHasso Tepper 
8237f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
8247f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
8257f3c3d6fSHasso Tepper 
8267f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
8277f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
8287f3c3d6fSHasso Tepper 
8297f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
8307f3c3d6fSHasso Tepper 	request->size = size;
8317f3c3d6fSHasso Tepper 
8327f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_SG;
8337f3c3d6fSHasso Tepper 
8347f3c3d6fSHasso Tepper 	return 0;
8357f3c3d6fSHasso Tepper }
8367f3c3d6fSHasso Tepper 
837b3705d71SHasso Tepper int drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
8387f3c3d6fSHasso Tepper {
8397f3c3d6fSHasso Tepper 	int order, ret;
8407f3c3d6fSHasso Tepper 
8417f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
8427f3c3d6fSHasso Tepper 		return EINVAL;
8437f3c3d6fSHasso Tepper 
8447f3c3d6fSHasso Tepper 	order = drm_order(request->size);
8457f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
8467f3c3d6fSHasso Tepper 		return EINVAL;
8477f3c3d6fSHasso Tepper 
8485718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
849b3705d71SHasso Tepper 
8507f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8517f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8525718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8537f3c3d6fSHasso Tepper 		return EBUSY;
8547f3c3d6fSHasso Tepper 	}
8557f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
8567f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
8575718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8587f3c3d6fSHasso Tepper 		return ENOMEM;
8597f3c3d6fSHasso Tepper 	}
8607f3c3d6fSHasso Tepper 
8617f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_agp(dev, request);
8627f3c3d6fSHasso Tepper 
8635718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
8647f3c3d6fSHasso Tepper 
8657f3c3d6fSHasso Tepper 	return ret;
8667f3c3d6fSHasso Tepper }
8677f3c3d6fSHasso Tepper 
868b3705d71SHasso Tepper int drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
8697f3c3d6fSHasso Tepper {
8707f3c3d6fSHasso Tepper 	int order, ret;
8717f3c3d6fSHasso Tepper 
8727f3c3d6fSHasso Tepper 	if (!DRM_SUSER(DRM_CURPROC))
8737f3c3d6fSHasso Tepper 		return EACCES;
8747f3c3d6fSHasso Tepper 
8757f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
8767f3c3d6fSHasso Tepper 		return EINVAL;
8777f3c3d6fSHasso Tepper 
8787f3c3d6fSHasso Tepper 	order = drm_order(request->size);
8797f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
8807f3c3d6fSHasso Tepper 		return EINVAL;
8817f3c3d6fSHasso Tepper 
8825718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
883b3705d71SHasso Tepper 
8847f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8857f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8865718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8877f3c3d6fSHasso Tepper 		return EBUSY;
8887f3c3d6fSHasso Tepper 	}
8897f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
8907f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
8915718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8927f3c3d6fSHasso Tepper 		return ENOMEM;
8937f3c3d6fSHasso Tepper 	}
8947f3c3d6fSHasso Tepper 
8957f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_sg(dev, request);
8967f3c3d6fSHasso Tepper 
8975718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
8987f3c3d6fSHasso Tepper 
8997f3c3d6fSHasso Tepper 	return ret;
9007f3c3d6fSHasso Tepper }
9017f3c3d6fSHasso Tepper 
902b3705d71SHasso Tepper int drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
9037f3c3d6fSHasso Tepper {
9047f3c3d6fSHasso Tepper 	int order, ret;
9057f3c3d6fSHasso Tepper 
9067f3c3d6fSHasso Tepper 	if (!DRM_SUSER(DRM_CURPROC))
9077f3c3d6fSHasso Tepper 		return EACCES;
9087f3c3d6fSHasso Tepper 
9097f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
9107f3c3d6fSHasso Tepper 		return EINVAL;
9117f3c3d6fSHasso Tepper 
9127f3c3d6fSHasso Tepper 	order = drm_order(request->size);
9137f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
9147f3c3d6fSHasso Tepper 		return EINVAL;
9157f3c3d6fSHasso Tepper 
9165718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
917b3705d71SHasso Tepper 
9187f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
9197f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
9205718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9217f3c3d6fSHasso Tepper 		return EBUSY;
9227f3c3d6fSHasso Tepper 	}
9237f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
9247f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
9255718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9267f3c3d6fSHasso Tepper 		return ENOMEM;
9277f3c3d6fSHasso Tepper 	}
9287f3c3d6fSHasso Tepper 
9297f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_pci(dev, request);
9307f3c3d6fSHasso Tepper 
9315718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
9327f3c3d6fSHasso Tepper 
9337f3c3d6fSHasso Tepper 	return ret;
9347f3c3d6fSHasso Tepper }
9357f3c3d6fSHasso Tepper 
936b3705d71SHasso Tepper int drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
9377f3c3d6fSHasso Tepper {
938b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
9397f3c3d6fSHasso Tepper 	int err;
9407f3c3d6fSHasso Tepper 
9417f3c3d6fSHasso Tepper 	if (request->flags & _DRM_AGP_BUFFER)
9427f3c3d6fSHasso Tepper 		err = drm_addbufs_agp(dev, request);
9437f3c3d6fSHasso Tepper 	else if (request->flags & _DRM_SG_BUFFER)
9447f3c3d6fSHasso Tepper 		err = drm_addbufs_sg(dev, request);
9457f3c3d6fSHasso Tepper 	else
9467f3c3d6fSHasso Tepper 		err = drm_addbufs_pci(dev, request);
9477f3c3d6fSHasso Tepper 
9487f3c3d6fSHasso Tepper 	return err;
9497f3c3d6fSHasso Tepper }
9507f3c3d6fSHasso Tepper 
951b3705d71SHasso Tepper int drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
9527f3c3d6fSHasso Tepper {
9537f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
954b3705d71SHasso Tepper 	struct drm_buf_info *request = data;
9557f3c3d6fSHasso Tepper 	int i;
9567f3c3d6fSHasso Tepper 	int count;
9577f3c3d6fSHasso Tepper 	int retcode = 0;
9587f3c3d6fSHasso Tepper 
9595718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
9607f3c3d6fSHasso Tepper 	++dev->buf_use;		/* Can't allocate more after this call */
9615718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
9627f3c3d6fSHasso Tepper 
9637f3c3d6fSHasso Tepper 	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
964b3705d71SHasso Tepper 		if (dma->bufs[i].buf_count)
965b3705d71SHasso Tepper 			++count;
9667f3c3d6fSHasso Tepper 	}
9677f3c3d6fSHasso Tepper 
9687f3c3d6fSHasso Tepper 	DRM_DEBUG("count = %d\n", count);
9697f3c3d6fSHasso Tepper 
9707f3c3d6fSHasso Tepper 	if (request->count >= count) {
9717f3c3d6fSHasso Tepper 		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
9727f3c3d6fSHasso Tepper 			if (dma->bufs[i].buf_count) {
973b3705d71SHasso Tepper 				struct drm_buf_desc from;
9747f3c3d6fSHasso Tepper 
9757f3c3d6fSHasso Tepper 				from.count = dma->bufs[i].buf_count;
9767f3c3d6fSHasso Tepper 				from.size = dma->bufs[i].buf_size;
9777f3c3d6fSHasso Tepper 				from.low_mark = dma->bufs[i].freelist.low_mark;
9787f3c3d6fSHasso Tepper 				from.high_mark = dma->bufs[i].freelist.high_mark;
9797f3c3d6fSHasso Tepper 
9807f3c3d6fSHasso Tepper 				if (DRM_COPY_TO_USER(&request->list[count], &from,
981b3705d71SHasso Tepper 				    sizeof(struct drm_buf_desc)) != 0) {
9827f3c3d6fSHasso Tepper 					retcode = EFAULT;
9837f3c3d6fSHasso Tepper 					break;
9847f3c3d6fSHasso Tepper 				}
9857f3c3d6fSHasso Tepper 
9867f3c3d6fSHasso Tepper 				DRM_DEBUG("%d %d %d %d %d\n",
987b3705d71SHasso Tepper 				    i, dma->bufs[i].buf_count,
9887f3c3d6fSHasso Tepper 				    dma->bufs[i].buf_size,
9897f3c3d6fSHasso Tepper 				    dma->bufs[i].freelist.low_mark,
9907f3c3d6fSHasso Tepper 				    dma->bufs[i].freelist.high_mark);
9917f3c3d6fSHasso Tepper 				++count;
9927f3c3d6fSHasso Tepper 			}
9937f3c3d6fSHasso Tepper 		}
9947f3c3d6fSHasso Tepper 	}
9957f3c3d6fSHasso Tepper 	request->count = count;
9967f3c3d6fSHasso Tepper 
9977f3c3d6fSHasso Tepper 	return retcode;
9987f3c3d6fSHasso Tepper }
9997f3c3d6fSHasso Tepper 
1000b3705d71SHasso Tepper int drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
10017f3c3d6fSHasso Tepper {
10027f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
1003b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
10047f3c3d6fSHasso Tepper 	int order;
10057f3c3d6fSHasso Tepper 
10067f3c3d6fSHasso Tepper 	DRM_DEBUG("%d, %d, %d\n",
10077f3c3d6fSHasso Tepper 		  request->size, request->low_mark, request->high_mark);
10087f3c3d6fSHasso Tepper 
10097f3c3d6fSHasso Tepper 
10107f3c3d6fSHasso Tepper 	order = drm_order(request->size);
10117f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
10127f3c3d6fSHasso Tepper 	    request->low_mark < 0 || request->high_mark < 0) {
10137f3c3d6fSHasso Tepper 		return EINVAL;
10147f3c3d6fSHasso Tepper 	}
10157f3c3d6fSHasso Tepper 
10165718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
10177f3c3d6fSHasso Tepper 	if (request->low_mark > dma->bufs[order].buf_count ||
10187f3c3d6fSHasso Tepper 	    request->high_mark > dma->bufs[order].buf_count) {
10195718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
10207f3c3d6fSHasso Tepper 		return EINVAL;
10217f3c3d6fSHasso Tepper 	}
10227f3c3d6fSHasso Tepper 
10237f3c3d6fSHasso Tepper 	dma->bufs[order].freelist.low_mark  = request->low_mark;
10247f3c3d6fSHasso Tepper 	dma->bufs[order].freelist.high_mark = request->high_mark;
10255718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
10267f3c3d6fSHasso Tepper 
10277f3c3d6fSHasso Tepper 	return 0;
10287f3c3d6fSHasso Tepper }
10297f3c3d6fSHasso Tepper 
1030b3705d71SHasso Tepper int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
10317f3c3d6fSHasso Tepper {
10327f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
1033b3705d71SHasso Tepper 	struct drm_buf_free *request = data;
10347f3c3d6fSHasso Tepper 	int i;
10357f3c3d6fSHasso Tepper 	int idx;
10367f3c3d6fSHasso Tepper 	drm_buf_t *buf;
10377f3c3d6fSHasso Tepper 	int retcode = 0;
10387f3c3d6fSHasso Tepper 
10397f3c3d6fSHasso Tepper 	DRM_DEBUG("%d\n", request->count);
10407f3c3d6fSHasso Tepper 
10415718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
10427f3c3d6fSHasso Tepper 	for (i = 0; i < request->count; i++) {
10437f3c3d6fSHasso Tepper 		if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) {
10447f3c3d6fSHasso Tepper 			retcode = EFAULT;
10457f3c3d6fSHasso Tepper 			break;
10467f3c3d6fSHasso Tepper 		}
10477f3c3d6fSHasso Tepper 		if (idx < 0 || idx >= dma->buf_count) {
10487f3c3d6fSHasso Tepper 			DRM_ERROR("Index %d (of %d max)\n",
10497f3c3d6fSHasso Tepper 			    idx, dma->buf_count - 1);
10507f3c3d6fSHasso Tepper 			retcode = EINVAL;
10517f3c3d6fSHasso Tepper 			break;
10527f3c3d6fSHasso Tepper 		}
10537f3c3d6fSHasso Tepper 		buf = dma->buflist[idx];
10547f3c3d6fSHasso Tepper 		if (buf->file_priv != file_priv) {
10557f3c3d6fSHasso Tepper 			DRM_ERROR("Process %d freeing buffer not owned\n",
10567f3c3d6fSHasso Tepper 			    DRM_CURRENTPID);
10577f3c3d6fSHasso Tepper 			retcode = EINVAL;
10587f3c3d6fSHasso Tepper 			break;
10597f3c3d6fSHasso Tepper 		}
10607f3c3d6fSHasso Tepper 		drm_free_buffer(dev, buf);
10617f3c3d6fSHasso Tepper 	}
10625718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
10637f3c3d6fSHasso Tepper 
10647f3c3d6fSHasso Tepper 	return retcode;
10657f3c3d6fSHasso Tepper }
10667f3c3d6fSHasso Tepper 
1067b3705d71SHasso Tepper int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
10687f3c3d6fSHasso Tepper {
10697f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
10707f3c3d6fSHasso Tepper 	int retcode = 0;
10717f3c3d6fSHasso Tepper 	const int zero = 0;
10727f3c3d6fSHasso Tepper 	vm_offset_t address;
10737f3c3d6fSHasso Tepper 	struct vmspace *vms;
10747f3c3d6fSHasso Tepper 	vm_ooffset_t foff;
10757f3c3d6fSHasso Tepper 	vm_size_t size;
10767f3c3d6fSHasso Tepper 	vm_offset_t vaddr;
1077b3705d71SHasso Tepper 	struct drm_buf_map *request = data;
10787f3c3d6fSHasso Tepper 	int i;
10797f3c3d6fSHasso Tepper 
10807f3c3d6fSHasso Tepper 	vms = DRM_CURPROC->td_proc->p_vmspace;
10817f3c3d6fSHasso Tepper 
10825718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
10837f3c3d6fSHasso Tepper 	dev->buf_use++;		/* Can't allocate more after this call */
10845718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
10857f3c3d6fSHasso Tepper 
10867f3c3d6fSHasso Tepper 	if (request->count < dma->buf_count)
10877f3c3d6fSHasso Tepper 		goto done;
10887f3c3d6fSHasso Tepper 
1089b3705d71SHasso Tepper 	if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
1090b3705d71SHasso Tepper 	    (drm_core_check_feature(dev, DRIVER_SG) &&
1091b3705d71SHasso Tepper 	    (dma->flags & _DRM_DMA_USE_SG))) {
10927f3c3d6fSHasso Tepper 		drm_local_map_t *map = dev->agp_buffer_map;
10937f3c3d6fSHasso Tepper 
10947f3c3d6fSHasso Tepper 		if (map == NULL) {
10957f3c3d6fSHasso Tepper 			retcode = EINVAL;
10967f3c3d6fSHasso Tepper 			goto done;
10977f3c3d6fSHasso Tepper 		}
10987f3c3d6fSHasso Tepper 		size = round_page(map->size);
109999f70504SFrançois Tigeot 		foff = (unsigned long)map->handle;
11007f3c3d6fSHasso Tepper 	} else {
11017f3c3d6fSHasso Tepper 		size = round_page(dma->byte_count),
11027f3c3d6fSHasso Tepper 		foff = 0;
11037f3c3d6fSHasso Tepper 	}
11047f3c3d6fSHasso Tepper 
11057f3c3d6fSHasso Tepper 	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
11067f3c3d6fSHasso Tepper 	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1107b3705d71SHasso Tepper 	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
1108b3705d71SHasso Tepper 	    SLIST_FIRST(&dev->devnode->si_hlist), foff);
11097f3c3d6fSHasso Tepper 	if (retcode)
11107f3c3d6fSHasso Tepper 		goto done;
11117f3c3d6fSHasso Tepper 
11127f3c3d6fSHasso Tepper 	request->virtual = (void *)vaddr;
11137f3c3d6fSHasso Tepper 
11147f3c3d6fSHasso Tepper 	for (i = 0; i < dma->buf_count; i++) {
11157f3c3d6fSHasso Tepper 		if (DRM_COPY_TO_USER(&request->list[i].idx,
11167f3c3d6fSHasso Tepper 		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
11177f3c3d6fSHasso Tepper 			retcode = EFAULT;
11187f3c3d6fSHasso Tepper 			goto done;
11197f3c3d6fSHasso Tepper 		}
11207f3c3d6fSHasso Tepper 		if (DRM_COPY_TO_USER(&request->list[i].total,
11217f3c3d6fSHasso Tepper 		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
11227f3c3d6fSHasso Tepper 			retcode = EFAULT;
11237f3c3d6fSHasso Tepper 			goto done;
11247f3c3d6fSHasso Tepper 		}
11257f3c3d6fSHasso Tepper 		if (DRM_COPY_TO_USER(&request->list[i].used, &zero,
11267f3c3d6fSHasso Tepper 		    sizeof(zero))) {
11277f3c3d6fSHasso Tepper 			retcode = EFAULT;
11287f3c3d6fSHasso Tepper 			goto done;
11297f3c3d6fSHasso Tepper 		}
11307f3c3d6fSHasso Tepper 		address = vaddr + dma->buflist[i]->offset; /* *** */
11317f3c3d6fSHasso Tepper 		if (DRM_COPY_TO_USER(&request->list[i].address, &address,
11327f3c3d6fSHasso Tepper 		    sizeof(address))) {
11337f3c3d6fSHasso Tepper 			retcode = EFAULT;
11347f3c3d6fSHasso Tepper 			goto done;
11357f3c3d6fSHasso Tepper 		}
11367f3c3d6fSHasso Tepper 	}
11377f3c3d6fSHasso Tepper 
11387f3c3d6fSHasso Tepper  done:
11397f3c3d6fSHasso Tepper 	request->count = dma->buf_count;
11407f3c3d6fSHasso Tepper 
11417f3c3d6fSHasso Tepper 	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
11427f3c3d6fSHasso Tepper 
11437f3c3d6fSHasso Tepper 	return retcode;
11447f3c3d6fSHasso Tepper }
1145b3705d71SHasso Tepper 
1146b3705d71SHasso Tepper /*
1147b3705d71SHasso Tepper  * Compute order.  Can be made faster.
1148b3705d71SHasso Tepper  */
1149b3705d71SHasso Tepper int drm_order(unsigned long size)
1150b3705d71SHasso Tepper {
1151b3705d71SHasso Tepper 	int order;
1152b3705d71SHasso Tepper 
1153b3705d71SHasso Tepper 	if (size == 0)
1154b3705d71SHasso Tepper 		return 0;
1155b3705d71SHasso Tepper 
1156b3705d71SHasso Tepper 	order = flsl(size) - 1;
1157b3705d71SHasso Tepper 	if (size & ~(1ul << order))
1158b3705d71SHasso Tepper 		++order;
1159b3705d71SHasso Tepper 
1160b3705d71SHasso Tepper 	return order;
1161b3705d71SHasso Tepper }
1162