xref: /dflybsd-src/sys/dev/drm/drm_bufs.c (revision 24edb8848e2499ece59b84a04f554a7a897feeab)
1df4baf3dSFrançois Tigeot /**
2df4baf3dSFrançois Tigeot  * \file drm_bufs.c
3df4baf3dSFrançois Tigeot  * Generic buffer template
4df4baf3dSFrançois Tigeot  *
5df4baf3dSFrançois Tigeot  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6df4baf3dSFrançois Tigeot  * \author Gareth Hughes <gareth@valinux.com>
7df4baf3dSFrançois Tigeot  */
8df4baf3dSFrançois Tigeot 
9df4baf3dSFrançois Tigeot /*
10df4baf3dSFrançois Tigeot  * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
11df4baf3dSFrançois Tigeot  *
127f3c3d6fSHasso Tepper  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
137f3c3d6fSHasso Tepper  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
147f3c3d6fSHasso Tepper  * All Rights Reserved.
157f3c3d6fSHasso Tepper  *
167f3c3d6fSHasso Tepper  * Permission is hereby granted, free of charge, to any person obtaining a
177f3c3d6fSHasso Tepper  * copy of this software and associated documentation files (the "Software"),
187f3c3d6fSHasso Tepper  * to deal in the Software without restriction, including without limitation
197f3c3d6fSHasso Tepper  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
207f3c3d6fSHasso Tepper  * and/or sell copies of the Software, and to permit persons to whom the
217f3c3d6fSHasso Tepper  * Software is furnished to do so, subject to the following conditions:
227f3c3d6fSHasso Tepper  *
237f3c3d6fSHasso Tepper  * The above copyright notice and this permission notice (including the next
247f3c3d6fSHasso Tepper  * paragraph) shall be included in all copies or substantial portions of the
257f3c3d6fSHasso Tepper  * Software.
267f3c3d6fSHasso Tepper  *
277f3c3d6fSHasso Tepper  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
287f3c3d6fSHasso Tepper  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
297f3c3d6fSHasso Tepper  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
307f3c3d6fSHasso Tepper  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
317f3c3d6fSHasso Tepper  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
327f3c3d6fSHasso Tepper  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
337f3c3d6fSHasso Tepper  * OTHER DEALINGS IN THE SOFTWARE.
347f3c3d6fSHasso Tepper  *
355718399fSFrançois Tigeot  * $FreeBSD: src/sys/dev/drm2/drm_bufs.c,v 1.1 2012/05/22 11:07:44 kib Exp $
367f3c3d6fSHasso Tepper  */
377f3c3d6fSHasso Tepper 
385718399fSFrançois Tigeot #include <sys/conf.h>
395718399fSFrançois Tigeot #include <bus/pci/pcireg.h>
40df4baf3dSFrançois Tigeot #include <linux/types.h>
41df4baf3dSFrançois Tigeot #include <linux/export.h>
4218e26a6dSFrançois Tigeot #include <drm/drmP.h>
437f3c3d6fSHasso Tepper 
447f3c3d6fSHasso Tepper /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
457f3c3d6fSHasso Tepper  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
467f3c3d6fSHasso Tepper  * address for accessing them.  Cleaned up at unload.
477f3c3d6fSHasso Tepper  */
48b3705d71SHasso Tepper static int drm_alloc_resource(struct drm_device *dev, int resource)
497f3c3d6fSHasso Tepper {
5099f70504SFrançois Tigeot 	struct resource *res;
5199f70504SFrançois Tigeot 	int rid;
5299f70504SFrançois Tigeot 
535718399fSFrançois Tigeot 	DRM_LOCK_ASSERT(dev);
5499f70504SFrançois Tigeot 
557f3c3d6fSHasso Tepper 	if (resource >= DRM_MAX_PCI_RESOURCE) {
567f3c3d6fSHasso Tepper 		DRM_ERROR("Resource %d too large\n", resource);
577f3c3d6fSHasso Tepper 		return 1;
587f3c3d6fSHasso Tepper 	}
597f3c3d6fSHasso Tepper 
607f3c3d6fSHasso Tepper 	if (dev->pcir[resource] != NULL) {
617f3c3d6fSHasso Tepper 		return 0;
627f3c3d6fSHasso Tepper 	}
637f3c3d6fSHasso Tepper 
645718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
6599f70504SFrançois Tigeot 	rid = PCIR_BAR(resource);
666df74fa7SFrançois Tigeot 	res = bus_alloc_resource_any(dev->dev, SYS_RES_MEMORY, &rid,
6799f70504SFrançois Tigeot 	    RF_SHAREABLE);
685718399fSFrançois Tigeot 	DRM_LOCK(dev);
6999f70504SFrançois Tigeot 	if (res == NULL) {
707f3c3d6fSHasso Tepper 		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
717f3c3d6fSHasso Tepper 		return 1;
727f3c3d6fSHasso Tepper 	}
737f3c3d6fSHasso Tepper 
7499f70504SFrançois Tigeot 	if (dev->pcir[resource] == NULL) {
7599f70504SFrançois Tigeot 		dev->pcirid[resource] = rid;
7699f70504SFrançois Tigeot 		dev->pcir[resource] = res;
7799f70504SFrançois Tigeot 	}
7899f70504SFrançois Tigeot 
797f3c3d6fSHasso Tepper 	return 0;
807f3c3d6fSHasso Tepper }
817f3c3d6fSHasso Tepper 
82b3705d71SHasso Tepper unsigned long drm_get_resource_start(struct drm_device *dev,
83b3705d71SHasso Tepper 				     unsigned int resource)
847f3c3d6fSHasso Tepper {
857f3c3d6fSHasso Tepper 	if (drm_alloc_resource(dev, resource) != 0)
867f3c3d6fSHasso Tepper 		return 0;
877f3c3d6fSHasso Tepper 
887f3c3d6fSHasso Tepper 	return rman_get_start(dev->pcir[resource]);
897f3c3d6fSHasso Tepper }
907f3c3d6fSHasso Tepper 
91b3705d71SHasso Tepper unsigned long drm_get_resource_len(struct drm_device *dev,
92b3705d71SHasso Tepper 				   unsigned int resource)
937f3c3d6fSHasso Tepper {
947f3c3d6fSHasso Tepper 	if (drm_alloc_resource(dev, resource) != 0)
957f3c3d6fSHasso Tepper 		return 0;
967f3c3d6fSHasso Tepper 
977f3c3d6fSHasso Tepper 	return rman_get_size(dev->pcir[resource]);
987f3c3d6fSHasso Tepper }
997f3c3d6fSHasso Tepper 
100df4baf3dSFrançois Tigeot int drm_addmap(struct drm_device * dev, resource_size_t offset,
101df4baf3dSFrançois Tigeot 	       unsigned int size, enum drm_map_type type,
102df4baf3dSFrançois Tigeot 	       enum drm_map_flags flags, struct drm_local_map ** map_ptr)
1037f3c3d6fSHasso Tepper {
104f599cd46SFrançois Tigeot 	struct drm_local_map *map;
10579d1f0c0SFrançois Tigeot 	struct drm_map_list *entry = NULL;
1065c002123SFrançois Tigeot 	drm_dma_handle_t *dmah;
10779d1f0c0SFrançois Tigeot 
10879d1f0c0SFrançois Tigeot 	/* Allocate a new map structure, fill it in, and do any type-specific
10979d1f0c0SFrançois Tigeot 	 * initialization necessary.
11079d1f0c0SFrançois Tigeot 	 */
111f8677ba6SMatthew Dillon 	map = kmalloc(sizeof(*map), M_DRM, M_ZERO | M_WAITOK | M_NULLOK);
11279d1f0c0SFrançois Tigeot 	if (!map) {
11379d1f0c0SFrançois Tigeot 		return ENOMEM;
11479d1f0c0SFrançois Tigeot 	}
11579d1f0c0SFrançois Tigeot 
11679d1f0c0SFrançois Tigeot 	map->offset = offset;
11779d1f0c0SFrançois Tigeot 	map->size = size;
11879d1f0c0SFrançois Tigeot 	map->type = type;
11979d1f0c0SFrançois Tigeot 	map->flags = flags;
1207f3c3d6fSHasso Tepper 
1217f3c3d6fSHasso Tepper 	/* Only allow shared memory to be removable since we only keep enough
1227f3c3d6fSHasso Tepper 	 * book keeping information about shared memory to allow for removal
1237f3c3d6fSHasso Tepper 	 * when processes fork.
1247f3c3d6fSHasso Tepper 	 */
1257f3c3d6fSHasso Tepper 	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
1267f3c3d6fSHasso Tepper 		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
1275a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
1287f3c3d6fSHasso Tepper 		return EINVAL;
1297f3c3d6fSHasso Tepper 	}
1307f3c3d6fSHasso Tepper 	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
1312ffc4fa7SSascha Wildner 		DRM_ERROR("offset/size not page aligned: 0x%jx/0x%04x\n",
1322ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
1335a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
1347f3c3d6fSHasso Tepper 		return EINVAL;
1357f3c3d6fSHasso Tepper 	}
1367f3c3d6fSHasso Tepper 	if (offset + size < offset) {
1372ffc4fa7SSascha Wildner 		DRM_ERROR("offset and size wrap around: 0x%jx/0x%04x\n",
1382ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
1395a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
1407f3c3d6fSHasso Tepper 		return EINVAL;
1417f3c3d6fSHasso Tepper 	}
1427f3c3d6fSHasso Tepper 
143df4baf3dSFrançois Tigeot 	DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
144df4baf3dSFrançois Tigeot 		  (unsigned long long)map->offset, map->size, map->type);
1457f3c3d6fSHasso Tepper 
1467f3c3d6fSHasso Tepper 	/* Check if this is just another version of a kernel-allocated map, and
1477f3c3d6fSHasso Tepper 	 * just hand that back if so.
1487f3c3d6fSHasso Tepper 	 */
1497f3c3d6fSHasso Tepper 	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
1507f3c3d6fSHasso Tepper 	    type == _DRM_SHM) {
151f599cd46SFrançois Tigeot 		list_for_each_entry(entry, &dev->maplist, head) {
152f599cd46SFrançois Tigeot 			if (entry->map->type == type && (entry->map->offset == offset ||
153f599cd46SFrançois Tigeot 			    (entry->map->type == _DRM_SHM &&
154f599cd46SFrançois Tigeot 			    entry->map->flags == _DRM_CONTAINS_LOCK))) {
155f599cd46SFrançois Tigeot 				entry->map->size = size;
1567f3c3d6fSHasso Tepper 				DRM_DEBUG("Found kernel map %d\n", type);
1577f3c3d6fSHasso Tepper 				goto done;
1587f3c3d6fSHasso Tepper 			}
1597f3c3d6fSHasso Tepper 		}
1607f3c3d6fSHasso Tepper 	}
1617f3c3d6fSHasso Tepper 
1627f3c3d6fSHasso Tepper 	switch (map->type) {
1637f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
1645c002123SFrançois Tigeot 		map->handle = 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:
173f8677ba6SMatthew Dillon 		map->handle = kmalloc(map->size, M_DRM, M_WAITOK | M_NULLOK);
1747f3c3d6fSHasso Tepper 		DRM_DEBUG("%lu %d %p\n",
1754cd92098Szrj 			  map->size, order_base_2(map->size), map->handle);
1765c002123SFrançois Tigeot 		if (!map->handle) {
1775a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
1787f3c3d6fSHasso Tepper 			return ENOMEM;
1797f3c3d6fSHasso Tepper 		}
1805c002123SFrançois Tigeot 		map->offset = (unsigned long)map->handle;
1817f3c3d6fSHasso Tepper 		if (map->flags & _DRM_CONTAINS_LOCK) {
1827f3c3d6fSHasso Tepper 			/* Prevent a 2nd X Server from creating a 2nd lock */
18379f713b0SFrançois Tigeot 			DRM_LOCK(dev);
18479f713b0SFrançois Tigeot 			if (dev->lock.hw_lock != NULL) {
18579f713b0SFrançois Tigeot 				DRM_UNLOCK(dev);
1865c002123SFrançois Tigeot 				drm_free(map->handle, M_DRM);
1875a3b77d5SFrançois Tigeot 				drm_free(map, M_DRM);
18879f713b0SFrançois Tigeot 				return EBUSY;
1897f3c3d6fSHasso Tepper 			}
19079f713b0SFrançois Tigeot 			dev->lock.hw_lock = map->handle; /* Pointer to lock */
19179f713b0SFrançois Tigeot 			DRM_UNLOCK(dev);
1927f3c3d6fSHasso Tepper 		}
1937f3c3d6fSHasso Tepper 		break;
1947f3c3d6fSHasso Tepper 	case _DRM_AGP:
1957f3c3d6fSHasso Tepper 		/*valid = 0;*/
1967f3c3d6fSHasso Tepper 		/* In some cases (i810 driver), user space may have already
1977f3c3d6fSHasso Tepper 		 * added the AGP base itself, because dev->agp->base previously
1987f3c3d6fSHasso Tepper 		 * only got set during AGP enable.  So, only add the base
1997f3c3d6fSHasso Tepper 		 * address if the map's offset isn't already within the
2007f3c3d6fSHasso Tepper 		 * aperture.
2017f3c3d6fSHasso Tepper 		 */
2027f3c3d6fSHasso Tepper 		if (map->offset < dev->agp->base ||
2037f3c3d6fSHasso Tepper 		    map->offset > dev->agp->base +
2049d567857SJean-Sébastien Pédron 		    dev->agp->agp_info.ai_aperture_size - 1) {
2057f3c3d6fSHasso Tepper 			map->offset += dev->agp->base;
2067f3c3d6fSHasso Tepper 		}
2079d567857SJean-Sébastien Pédron 		map->mtrr   = dev->agp->agp_mtrr; /* for getmap */
2087f3c3d6fSHasso Tepper 		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
2097f3c3d6fSHasso Tepper 			if ((map->offset >= entry->bound) &&
2107f3c3d6fSHasso Tepper 			    (map->offset + map->size <=
2117f3c3d6fSHasso Tepper 			    entry->bound + entry->pages * PAGE_SIZE)) {
2127f3c3d6fSHasso Tepper 				valid = 1;
2137f3c3d6fSHasso Tepper 				break;
2147f3c3d6fSHasso Tepper 			}
2157f3c3d6fSHasso Tepper 		}
2167f3c3d6fSHasso Tepper 		if (!valid) {
2175a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
2187f3c3d6fSHasso Tepper 			return EACCES;
2197f3c3d6fSHasso Tepper 		}*/
2207f3c3d6fSHasso Tepper 		break;
2217f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
2227f3c3d6fSHasso Tepper 		if (!dev->sg) {
2235a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
2247f3c3d6fSHasso Tepper 			return EINVAL;
2257f3c3d6fSHasso Tepper 		}
2265c002123SFrançois Tigeot 		map->handle = (void *)(uintptr_t)(dev->sg->vaddr + offset);
22799f70504SFrançois Tigeot 		map->offset = dev->sg->vaddr + offset;
2287f3c3d6fSHasso Tepper 		break;
2297f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
230b31e9d59SFrançois Tigeot 		/* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G,
231b31e9d59SFrançois Tigeot 		 * As we're limiting the address to 2^32-1 (or less),
232b31e9d59SFrançois Tigeot 		 * casting it down to 32 bits is no problem, but we
233b31e9d59SFrançois Tigeot 		 * need to point to a 64bit variable first. */
234b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, map->size, map->size);
2355c002123SFrançois Tigeot 		if (!dmah) {
236158486a6SFrançois Tigeot 			kfree(map);
237b31e9d59SFrançois Tigeot 			return -ENOMEM;
2387f3c3d6fSHasso Tepper 		}
2395c002123SFrançois Tigeot 		map->handle = dmah->vaddr;
240b31e9d59SFrançois Tigeot 		map->offset = dmah->busaddr;
2417f3c3d6fSHasso Tepper 		break;
2427f3c3d6fSHasso Tepper 	default:
2437f3c3d6fSHasso Tepper 		DRM_ERROR("Bad map type %d\n", map->type);
2445a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
2457f3c3d6fSHasso Tepper 		return EINVAL;
2467f3c3d6fSHasso Tepper 	}
2477f3c3d6fSHasso Tepper 
248f599cd46SFrançois Tigeot 	list_add(&entry->head, &dev->maplist);
2497f3c3d6fSHasso Tepper 
2507f3c3d6fSHasso Tepper done:
2517f3c3d6fSHasso Tepper 	/* Jumped to, with lock held, when a kernel map is found. */
2527f3c3d6fSHasso Tepper 
2537f3c3d6fSHasso Tepper 	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
2547f3c3d6fSHasso Tepper 	    map->size);
2557f3c3d6fSHasso Tepper 
2567f3c3d6fSHasso Tepper 	*map_ptr = map;
2577f3c3d6fSHasso Tepper 
2587f3c3d6fSHasso Tepper 	return 0;
2597f3c3d6fSHasso Tepper }
2607f3c3d6fSHasso Tepper 
261df4baf3dSFrançois Tigeot /**
262df4baf3dSFrançois Tigeot  * Ioctl to specify a range of memory that is available for mapping by a
263df4baf3dSFrançois Tigeot  * non-root process.
264df4baf3dSFrançois Tigeot  *
265df4baf3dSFrançois Tigeot  * \param inode device inode.
266df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
267df4baf3dSFrançois Tigeot  * \param cmd command.
268df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_map structure.
269df4baf3dSFrançois Tigeot  * \return zero on success or a negative value on error.
270df4baf3dSFrançois Tigeot  *
271df4baf3dSFrançois Tigeot  */
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 
282c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN) && request->type != _DRM_AGP)
2837f3c3d6fSHasso Tepper 		return EACCES;
2847f3c3d6fSHasso Tepper 
28579f713b0SFrançois Tigeot 	DRM_LOCK(dev);
2867f3c3d6fSHasso Tepper 	err = drm_addmap(dev, request->offset, request->size, request->type,
2877f3c3d6fSHasso Tepper 	    request->flags, &map);
28879f713b0SFrançois Tigeot 	DRM_UNLOCK(dev);
28979f713b0SFrançois Tigeot 	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 
30279f713b0SFrançois Tigeot void drm_rmmap(struct drm_device *dev, struct drm_local_map *map)
3037f3c3d6fSHasso Tepper {
304f599cd46SFrançois Tigeot 	struct drm_map_list *r_list = NULL, *list_t;
3055c002123SFrançois Tigeot 	drm_dma_handle_t dmah;
306f599cd46SFrançois Tigeot 	int found = 0;
30779f713b0SFrançois Tigeot 
30879f713b0SFrançois Tigeot 	DRM_LOCK_ASSERT(dev);
30979f713b0SFrançois Tigeot 
31079f713b0SFrançois Tigeot 	if (map == NULL)
31179f713b0SFrançois Tigeot 		return;
312c6bd1f0dSHasso Tepper 
313f599cd46SFrançois Tigeot 	/* Find the list entry for the map and remove it */
314f599cd46SFrançois Tigeot 	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
315f599cd46SFrançois Tigeot 		if (r_list->map == map) {
316f599cd46SFrançois Tigeot 			list_del(&r_list->head);
31779f713b0SFrançois Tigeot 			drm_free(r_list, M_DRM);
318f599cd46SFrançois Tigeot 			found = 1;
319f599cd46SFrançois Tigeot 			break;
320f599cd46SFrançois Tigeot 		}
321f599cd46SFrançois Tigeot 	}
322f599cd46SFrançois Tigeot 
323f599cd46SFrançois Tigeot 	if (!found)
32479f713b0SFrançois Tigeot 		return;
3257f3c3d6fSHasso Tepper 
3267f3c3d6fSHasso Tepper 	switch (map->type) {
3277f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
3287f3c3d6fSHasso Tepper 		drm_ioremapfree(map);
3297f3c3d6fSHasso Tepper 		/* FALLTHROUGH */
3307f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
3317f3c3d6fSHasso Tepper 		if (map->mtrr) {
33279f713b0SFrançois Tigeot 			int __unused retcode;
33379f713b0SFrançois Tigeot 
33479f713b0SFrançois Tigeot 			retcode = drm_mtrr_del(0, map->offset, map->size,
33579f713b0SFrançois Tigeot 			    DRM_MTRR_WC);
3367f3c3d6fSHasso Tepper 			DRM_DEBUG("mtrr_del = %d\n", retcode);
3377f3c3d6fSHasso Tepper 		}
3387f3c3d6fSHasso Tepper 		break;
3397f3c3d6fSHasso Tepper 	case _DRM_SHM:
3405c002123SFrançois Tigeot 		drm_free(map->handle, M_DRM);
3417f3c3d6fSHasso Tepper 		break;
3427f3c3d6fSHasso Tepper 	case _DRM_AGP:
3437f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
3447f3c3d6fSHasso Tepper 		break;
3457f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
3465c002123SFrançois Tigeot 		dmah.vaddr = map->handle;
3475c002123SFrançois Tigeot 		dmah.busaddr = map->offset;
3485c002123SFrançois Tigeot 		drm_pci_free(dev, &dmah);
3497f3c3d6fSHasso Tepper 		break;
35079f713b0SFrançois Tigeot 	default:
35179f713b0SFrançois Tigeot 		DRM_ERROR("Bad map type %d\n", map->type);
3527f3c3d6fSHasso Tepper 		break;
3537f3c3d6fSHasso Tepper 	}
35479f713b0SFrançois Tigeot 
3555a3b77d5SFrançois Tigeot 	drm_free(map, M_DRM);
3567f3c3d6fSHasso Tepper }
3577f3c3d6fSHasso Tepper 
358f599cd46SFrançois Tigeot /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
359f599cd46SFrançois Tigeot  * the last close of the device, and this is necessary for cleanup when things
360f599cd46SFrançois Tigeot  * exit uncleanly.  Therefore, having userland manually remove mappings seems
361f599cd46SFrançois Tigeot  * like a pointless exercise since they're going away anyway.
362f599cd46SFrançois Tigeot  *
363f599cd46SFrançois Tigeot  * One use case might be after addmap is allowed for normal users for SHM and
364f599cd46SFrançois Tigeot  * gets used by drivers that the server doesn't need to care about.  This seems
365f599cd46SFrançois Tigeot  * unlikely.
366f599cd46SFrançois Tigeot  *
367f599cd46SFrançois Tigeot  * \param inode device inode.
368f599cd46SFrançois Tigeot  * \param file_priv DRM file private.
369f599cd46SFrançois Tigeot  * \param cmd command.
370f599cd46SFrançois Tigeot  * \param arg pointer to a struct drm_map structure.
371f599cd46SFrançois Tigeot  * \return zero on success or a negative value on error.
3727f3c3d6fSHasso Tepper  */
373b3705d71SHasso Tepper int drm_rmmap_ioctl(struct drm_device *dev, void *data,
374b3705d71SHasso Tepper 		    struct drm_file *file_priv)
3757f3c3d6fSHasso Tepper {
376b3705d71SHasso Tepper 	struct drm_map *request = data;
377f599cd46SFrançois Tigeot 	struct drm_local_map *map = NULL;
378f599cd46SFrançois Tigeot 	struct drm_map_list *r_list;
3797f3c3d6fSHasso Tepper 
3805718399fSFrançois Tigeot 	DRM_LOCK(dev);
381f599cd46SFrançois Tigeot 	list_for_each_entry(r_list, &dev->maplist, head) {
382f599cd46SFrançois Tigeot 		if (r_list->map &&
383f599cd46SFrançois Tigeot 		    r_list->user_token == (unsigned long)request->handle &&
384f599cd46SFrançois Tigeot 		    r_list->map->flags & _DRM_REMOVABLE) {
385f599cd46SFrançois Tigeot 			map = r_list->map;
3867f3c3d6fSHasso Tepper 			break;
3877f3c3d6fSHasso Tepper 		}
388f599cd46SFrançois Tigeot 	}
3897f3c3d6fSHasso Tepper 
390f599cd46SFrançois Tigeot 	/* List has wrapped around to the head pointer, or its empty we didn't
391f599cd46SFrançois Tigeot 	 * find anything.
392f599cd46SFrançois Tigeot 	 */
393f599cd46SFrançois Tigeot 	if (list_empty(&dev->maplist) || !map) {
3945718399fSFrançois Tigeot 		DRM_UNLOCK(dev);
395f599cd46SFrançois Tigeot 		return -EINVAL;
396f599cd46SFrançois Tigeot 	}
397f599cd46SFrançois Tigeot 
398f599cd46SFrançois Tigeot 	/* Register and framebuffer maps are permanent */
399f599cd46SFrançois Tigeot 	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
400f599cd46SFrançois Tigeot 		DRM_UNLOCK(dev);
401f599cd46SFrançois Tigeot 		return 0;
4027f3c3d6fSHasso Tepper 	}
4037f3c3d6fSHasso Tepper 
4047f3c3d6fSHasso Tepper 	drm_rmmap(dev, map);
4057f3c3d6fSHasso Tepper 
4065718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
4077f3c3d6fSHasso Tepper 
4087f3c3d6fSHasso Tepper 	return 0;
4097f3c3d6fSHasso Tepper }
4107f3c3d6fSHasso Tepper 
411df4baf3dSFrançois Tigeot /**
412df4baf3dSFrançois Tigeot  * Cleanup after an error on one of the addbufs() functions.
413df4baf3dSFrançois Tigeot  *
414df4baf3dSFrançois Tigeot  * \param dev DRM device.
415df4baf3dSFrançois Tigeot  * \param entry buffer entry where the error occurred.
416df4baf3dSFrançois Tigeot  *
417df4baf3dSFrançois Tigeot  * Frees any pages and buffers associated with the given entry.
418df4baf3dSFrançois Tigeot  */
419b3705d71SHasso Tepper static void drm_cleanup_buf_error(struct drm_device * dev,
420df4baf3dSFrançois Tigeot 				  struct drm_buf_entry * entry)
4217f3c3d6fSHasso Tepper {
4227f3c3d6fSHasso Tepper 	int i;
4237f3c3d6fSHasso Tepper 
4247f3c3d6fSHasso Tepper 	if (entry->seg_count) {
4257f3c3d6fSHasso Tepper 		for (i = 0; i < entry->seg_count; i++) {
4267f3c3d6fSHasso Tepper 			drm_pci_free(dev, entry->seglist[i]);
4277f3c3d6fSHasso Tepper 		}
4285a3b77d5SFrançois Tigeot 		drm_free(entry->seglist, M_DRM);
4297f3c3d6fSHasso Tepper 
4307f3c3d6fSHasso Tepper 		entry->seg_count = 0;
4317f3c3d6fSHasso Tepper 	}
4327f3c3d6fSHasso Tepper 
4337f3c3d6fSHasso Tepper    	if (entry->buf_count) {
4347f3c3d6fSHasso Tepper 	   	for (i = 0; i < entry->buf_count; i++) {
4355a3b77d5SFrançois Tigeot 			drm_free(entry->buflist[i].dev_private, M_DRM);
4367f3c3d6fSHasso Tepper 		}
4375a3b77d5SFrançois Tigeot 		drm_free(entry->buflist, M_DRM);
4387f3c3d6fSHasso Tepper 
4397f3c3d6fSHasso Tepper 		entry->buf_count = 0;
4407f3c3d6fSHasso Tepper 	}
4417f3c3d6fSHasso Tepper }
4427f3c3d6fSHasso Tepper 
443b3705d71SHasso Tepper static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
4447f3c3d6fSHasso Tepper {
4457f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
4467f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
4477f3c3d6fSHasso Tepper 	/*drm_agp_mem_t *agp_entry;
4487f3c3d6fSHasso Tepper 	int valid*/
4497f3c3d6fSHasso Tepper 	drm_buf_t *buf;
4507f3c3d6fSHasso Tepper 	unsigned long offset;
4517f3c3d6fSHasso Tepper 	unsigned long agp_offset;
4527f3c3d6fSHasso Tepper 	int count;
4537f3c3d6fSHasso Tepper 	int order;
4547f3c3d6fSHasso Tepper 	int size;
4557f3c3d6fSHasso Tepper 	int alignment;
4567f3c3d6fSHasso Tepper 	int page_order;
4577f3c3d6fSHasso Tepper 	int total;
4587f3c3d6fSHasso Tepper 	int byte_count;
4597f3c3d6fSHasso Tepper 	int i;
4607f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
4617f3c3d6fSHasso Tepper 
4627f3c3d6fSHasso Tepper 	count = request->count;
4634cd92098Szrj 	order = order_base_2(request->size);
4647f3c3d6fSHasso Tepper 	size = 1 << order;
4657f3c3d6fSHasso Tepper 
4667f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
4677f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
4687f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
4697f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
4707f3c3d6fSHasso Tepper 
4717f3c3d6fSHasso Tepper 	byte_count = 0;
4727f3c3d6fSHasso Tepper 	agp_offset = dev->agp->base + request->agp_start;
4737f3c3d6fSHasso Tepper 
4747f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
4757f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
4767f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
4777f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
4787f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
4797f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
4807f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
4817f3c3d6fSHasso Tepper 
4827f3c3d6fSHasso Tepper 	/* Make sure buffers are located in AGP memory that we own */
4837f3c3d6fSHasso Tepper 	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
4847f3c3d6fSHasso Tepper 	 * memory.  Safe to ignore for now because these ioctls are still
4857f3c3d6fSHasso Tepper 	 * root-only.
4867f3c3d6fSHasso Tepper 	 */
4877f3c3d6fSHasso Tepper 	/*valid = 0;
4887f3c3d6fSHasso Tepper 	for (agp_entry = dev->agp->memory; agp_entry;
4897f3c3d6fSHasso Tepper 	    agp_entry = agp_entry->next) {
4907f3c3d6fSHasso Tepper 		if ((agp_offset >= agp_entry->bound) &&
4917f3c3d6fSHasso Tepper 		    (agp_offset + total * count <=
4927f3c3d6fSHasso Tepper 		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
4937f3c3d6fSHasso Tepper 			valid = 1;
4947f3c3d6fSHasso Tepper 			break;
4957f3c3d6fSHasso Tepper 		}
4967f3c3d6fSHasso Tepper 	}
4977f3c3d6fSHasso Tepper 	if (!valid) {
4987f3c3d6fSHasso Tepper 		DRM_DEBUG("zone invalid\n");
4997f3c3d6fSHasso Tepper 		return EINVAL;
5007f3c3d6fSHasso Tepper 	}*/
5017f3c3d6fSHasso Tepper 
5027f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
5037f3c3d6fSHasso Tepper 
5045a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
505f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
5067f3c3d6fSHasso Tepper 	if (!entry->buflist) {
5077f3c3d6fSHasso Tepper 		return ENOMEM;
5087f3c3d6fSHasso Tepper 	}
5097f3c3d6fSHasso Tepper 
5107f3c3d6fSHasso Tepper 	entry->buf_size = size;
5117f3c3d6fSHasso Tepper 	entry->page_order = page_order;
5127f3c3d6fSHasso Tepper 
5137f3c3d6fSHasso Tepper 	offset = 0;
5147f3c3d6fSHasso Tepper 
5157f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
5167f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
5177f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
5187f3c3d6fSHasso Tepper 		buf->total   = alignment;
5197f3c3d6fSHasso Tepper 		buf->order   = order;
5207f3c3d6fSHasso Tepper 		buf->used    = 0;
5217f3c3d6fSHasso Tepper 
5227f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
5237f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
5247f3c3d6fSHasso Tepper 		buf->address = (void *)(agp_offset + offset);
5257f3c3d6fSHasso Tepper 		buf->next    = NULL;
5267f3c3d6fSHasso Tepper 		buf->pending = 0;
5277f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
5287f3c3d6fSHasso Tepper 
529ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
5305a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
531f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
5327f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
5337f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
5347f3c3d6fSHasso Tepper 			entry->buf_count = count;
5357f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
5367f3c3d6fSHasso Tepper 			return ENOMEM;
5377f3c3d6fSHasso Tepper 		}
5387f3c3d6fSHasso Tepper 
5397f3c3d6fSHasso Tepper 		offset += alignment;
5407f3c3d6fSHasso Tepper 		entry->buf_count++;
5417f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
5427f3c3d6fSHasso Tepper 	}
5437f3c3d6fSHasso Tepper 
5447f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
5457f3c3d6fSHasso Tepper 
5465718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
547b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
548f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
5497f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
5507f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
5517f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
5527f3c3d6fSHasso Tepper 		return ENOMEM;
5537f3c3d6fSHasso Tepper 	}
5547f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
5557f3c3d6fSHasso Tepper 
5567f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
5577f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
5587f3c3d6fSHasso Tepper 	}
5597f3c3d6fSHasso Tepper 
5607f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
5617f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
5627f3c3d6fSHasso Tepper 
5637f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
5647f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
5657f3c3d6fSHasso Tepper 
5667f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
5677f3c3d6fSHasso Tepper 	request->size = size;
5687f3c3d6fSHasso Tepper 
5697f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_AGP;
5707f3c3d6fSHasso Tepper 
5717f3c3d6fSHasso Tepper 	return 0;
5727f3c3d6fSHasso Tepper }
5737f3c3d6fSHasso Tepper 
574b3705d71SHasso Tepper static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
5757f3c3d6fSHasso Tepper {
5767f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
5777f3c3d6fSHasso Tepper 	int count;
5787f3c3d6fSHasso Tepper 	int order;
5797f3c3d6fSHasso Tepper 	int size;
5807f3c3d6fSHasso Tepper 	int total;
5817f3c3d6fSHasso Tepper 	int page_order;
5827f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
583b31e9d59SFrançois Tigeot 	drm_dma_handle_t *dmah;
5847f3c3d6fSHasso Tepper 	drm_buf_t *buf;
5857f3c3d6fSHasso Tepper 	int alignment;
5867f3c3d6fSHasso Tepper 	unsigned long offset;
5877f3c3d6fSHasso Tepper 	int i;
5887f3c3d6fSHasso Tepper 	int byte_count;
5897f3c3d6fSHasso Tepper 	int page_count;
5907f3c3d6fSHasso Tepper 	unsigned long *temp_pagelist;
5917f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
5927f3c3d6fSHasso Tepper 
5937f3c3d6fSHasso Tepper 	count = request->count;
5944cd92098Szrj 	order = order_base_2(request->size);
5957f3c3d6fSHasso Tepper 	size = 1 << order;
5967f3c3d6fSHasso Tepper 
5977f3c3d6fSHasso Tepper 	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
5987f3c3d6fSHasso Tepper 	    request->count, request->size, size, order);
5997f3c3d6fSHasso Tepper 
6007f3c3d6fSHasso Tepper 	alignment = (request->flags & _DRM_PAGE_ALIGN)
6017f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
6027f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
6037f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
6047f3c3d6fSHasso Tepper 
6057f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
6067f3c3d6fSHasso Tepper 
6075a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
608f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
6095a3b77d5SFrançois Tigeot 	entry->seglist = kmalloc(count * sizeof(*entry->seglist), M_DRM,
610f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
6117f3c3d6fSHasso Tepper 
6127f3c3d6fSHasso Tepper 	/* Keep the original pagelist until we know all the allocations
6137f3c3d6fSHasso Tepper 	 * have succeeded
6147f3c3d6fSHasso Tepper 	 */
6155718399fSFrançois Tigeot 	temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
616f8677ba6SMatthew Dillon 				sizeof(*dma->pagelist),
617f8677ba6SMatthew Dillon 				M_DRM, M_WAITOK | M_NULLOK);
6187f3c3d6fSHasso Tepper 
6197f3c3d6fSHasso Tepper 	if (entry->buflist == NULL || entry->seglist == NULL ||
6207f3c3d6fSHasso Tepper 	    temp_pagelist == NULL) {
6215a3b77d5SFrançois Tigeot 		drm_free(temp_pagelist, M_DRM);
6225a3b77d5SFrançois Tigeot 		drm_free(entry->seglist, M_DRM);
6235a3b77d5SFrançois Tigeot 		drm_free(entry->buflist, M_DRM);
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);
640b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000);
6415718399fSFrançois Tigeot 		spin_lock(&dev->dma_lock);
642b31e9d59SFrançois Tigeot 
643b31e9d59SFrançois Tigeot 		if (!dmah) {
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);
6485a3b77d5SFrançois Tigeot 			drm_free(temp_pagelist, M_DRM);
649b31e9d59SFrançois Tigeot 			return -ENOMEM;
6507f3c3d6fSHasso Tepper 		}
6517f3c3d6fSHasso Tepper 
6527f3c3d6fSHasso Tepper 		entry->seglist[entry->seg_count++] = dmah;
6537f3c3d6fSHasso Tepper 		for (i = 0; i < (1 << page_order); i++) {
654b31e9d59SFrançois Tigeot 			DRM_DEBUG("page %d @ 0x%08lx\n",
6557f3c3d6fSHasso Tepper 				  dma->page_count + page_count,
656b31e9d59SFrançois Tigeot 				  (unsigned long)dmah->vaddr + PAGE_SIZE * i);
657b31e9d59SFrançois Tigeot 			temp_pagelist[dma->page_count + page_count++]
658b31e9d59SFrançois Tigeot 				= (unsigned 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 
675ba55f2f5SFrançois Tigeot 			buf->dev_priv_size = dev->driver->dev_priv_size;
6765718399fSFrançois Tigeot 			buf->dev_private = kmalloc(buf->dev_priv_size,
677f8677ba6SMatthew Dillon 						   M_DRM,
678f8677ba6SMatthew Dillon 						   M_WAITOK | M_NULLOK |
679f8677ba6SMatthew Dillon 						    M_ZERO);
6807f3c3d6fSHasso Tepper 			if (buf->dev_private == NULL) {
6817f3c3d6fSHasso Tepper 				/* Set count correctly so we free the proper amount. */
6827f3c3d6fSHasso Tepper 				entry->buf_count = count;
6837f3c3d6fSHasso Tepper 				entry->seg_count = count;
6847f3c3d6fSHasso Tepper 				drm_cleanup_buf_error(dev, entry);
6855a3b77d5SFrançois Tigeot 				drm_free(temp_pagelist, M_DRM);
6867f3c3d6fSHasso Tepper 				return ENOMEM;
6877f3c3d6fSHasso Tepper 			}
6887f3c3d6fSHasso Tepper 
6897f3c3d6fSHasso Tepper 			DRM_DEBUG("buffer %d @ %p\n",
6907f3c3d6fSHasso Tepper 			    entry->buf_count, buf->address);
6917f3c3d6fSHasso Tepper 		}
6927f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
6937f3c3d6fSHasso Tepper 	}
6947f3c3d6fSHasso Tepper 
6955718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
696b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
697f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
6987f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
6997f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
7007f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
7015a3b77d5SFrançois Tigeot 		drm_free(temp_pagelist, M_DRM);
7027f3c3d6fSHasso Tepper 		return ENOMEM;
7037f3c3d6fSHasso Tepper 	}
7047f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
7057f3c3d6fSHasso Tepper 
7067f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
7077f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
7087f3c3d6fSHasso Tepper 	}
7097f3c3d6fSHasso Tepper 
7107f3c3d6fSHasso Tepper 	/* No allocations failed, so now we can replace the orginal pagelist
7117f3c3d6fSHasso Tepper 	 * with the new one.
7127f3c3d6fSHasso Tepper 	 */
7135a3b77d5SFrançois Tigeot 	drm_free(dma->pagelist, M_DRM);
7147f3c3d6fSHasso Tepper 	dma->pagelist = temp_pagelist;
7157f3c3d6fSHasso Tepper 
7167f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
7177f3c3d6fSHasso Tepper 	dma->seg_count += entry->seg_count;
7187f3c3d6fSHasso Tepper 	dma->page_count += entry->seg_count << page_order;
7197f3c3d6fSHasso Tepper 	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
7207f3c3d6fSHasso Tepper 
7217f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
7227f3c3d6fSHasso Tepper 	request->size = size;
7237f3c3d6fSHasso Tepper 
7247f3c3d6fSHasso Tepper 	return 0;
7257f3c3d6fSHasso Tepper 
7267f3c3d6fSHasso Tepper }
7277f3c3d6fSHasso Tepper 
728b3705d71SHasso Tepper static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
7297f3c3d6fSHasso Tepper {
7307f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
7317f3c3d6fSHasso Tepper 	drm_buf_entry_t *entry;
7327f3c3d6fSHasso Tepper 	drm_buf_t *buf;
7337f3c3d6fSHasso Tepper 	unsigned long offset;
7347f3c3d6fSHasso Tepper 	unsigned long agp_offset;
7357f3c3d6fSHasso Tepper 	int count;
7367f3c3d6fSHasso Tepper 	int order;
7377f3c3d6fSHasso Tepper 	int size;
7387f3c3d6fSHasso Tepper 	int alignment;
7397f3c3d6fSHasso Tepper 	int page_order;
7407f3c3d6fSHasso Tepper 	int total;
7417f3c3d6fSHasso Tepper 	int byte_count;
7427f3c3d6fSHasso Tepper 	int i;
7437f3c3d6fSHasso Tepper 	drm_buf_t **temp_buflist;
7447f3c3d6fSHasso Tepper 
7457f3c3d6fSHasso Tepper 	count = request->count;
7464cd92098Szrj 	order = order_base_2(request->size);
7477f3c3d6fSHasso Tepper 	size = 1 << order;
7487f3c3d6fSHasso Tepper 
7497f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
7507f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
7517f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
7527f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
7537f3c3d6fSHasso Tepper 
7547f3c3d6fSHasso Tepper 	byte_count = 0;
7557f3c3d6fSHasso Tepper 	agp_offset = request->agp_start;
7567f3c3d6fSHasso Tepper 
7577f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
7587f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
7597f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
7607f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
7617f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
7627f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
7637f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
7647f3c3d6fSHasso Tepper 
7657f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
7667f3c3d6fSHasso Tepper 
7675a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
768f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
7697f3c3d6fSHasso Tepper 	if (entry->buflist == NULL)
7707f3c3d6fSHasso Tepper 		return ENOMEM;
7717f3c3d6fSHasso Tepper 
7727f3c3d6fSHasso Tepper 	entry->buf_size = size;
7737f3c3d6fSHasso Tepper 	entry->page_order = page_order;
7747f3c3d6fSHasso Tepper 
7757f3c3d6fSHasso Tepper 	offset = 0;
7767f3c3d6fSHasso Tepper 
7777f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
7787f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
7797f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
7807f3c3d6fSHasso Tepper 		buf->total   = alignment;
7817f3c3d6fSHasso Tepper 		buf->order   = order;
7827f3c3d6fSHasso Tepper 		buf->used    = 0;
7837f3c3d6fSHasso Tepper 
7847f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
7857f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
78699f70504SFrançois Tigeot 		buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
7877f3c3d6fSHasso Tepper 		buf->next    = NULL;
7887f3c3d6fSHasso Tepper 		buf->pending = 0;
7897f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
7907f3c3d6fSHasso Tepper 
791ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
7925a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
793f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
7947f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
7957f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
7967f3c3d6fSHasso Tepper 			entry->buf_count = count;
7977f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
7987f3c3d6fSHasso Tepper 			return ENOMEM;
7997f3c3d6fSHasso Tepper 		}
8007f3c3d6fSHasso Tepper 
8017f3c3d6fSHasso Tepper 		DRM_DEBUG("buffer %d @ %p\n",
8027f3c3d6fSHasso Tepper 		    entry->buf_count, buf->address);
8037f3c3d6fSHasso Tepper 
8047f3c3d6fSHasso Tepper 		offset += alignment;
8057f3c3d6fSHasso Tepper 		entry->buf_count++;
8067f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
8077f3c3d6fSHasso Tepper 	}
8087f3c3d6fSHasso Tepper 
8097f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
8107f3c3d6fSHasso Tepper 
8115718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
812b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
813f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
8147f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
8157f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
8167f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
8177f3c3d6fSHasso Tepper 		return ENOMEM;
8187f3c3d6fSHasso Tepper 	}
8197f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
8207f3c3d6fSHasso Tepper 
8217f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
8227f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
8237f3c3d6fSHasso Tepper 	}
8247f3c3d6fSHasso Tepper 
8257f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
8267f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
8277f3c3d6fSHasso Tepper 
8287f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
8297f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
8307f3c3d6fSHasso Tepper 
8317f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
8327f3c3d6fSHasso Tepper 	request->size = size;
8337f3c3d6fSHasso Tepper 
8347f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_SG;
8357f3c3d6fSHasso Tepper 
8367f3c3d6fSHasso Tepper 	return 0;
8377f3c3d6fSHasso Tepper }
8387f3c3d6fSHasso Tepper 
839df4baf3dSFrançois Tigeot /**
840df4baf3dSFrançois Tigeot  * Add AGP buffers for DMA transfers.
841df4baf3dSFrançois Tigeot  *
842df4baf3dSFrançois Tigeot  * \param dev struct drm_device to which the buffers are to be added.
843df4baf3dSFrançois Tigeot  * \param request pointer to a struct drm_buf_desc describing the request.
844df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
845df4baf3dSFrançois Tigeot  *
846df4baf3dSFrançois Tigeot  * After some sanity checks creates a drm_buf structure for each buffer and
847df4baf3dSFrançois Tigeot  * reallocates the buffer list of the same size order to accommodate the new
848df4baf3dSFrançois Tigeot  * buffers.
849df4baf3dSFrançois Tigeot  */
850b3705d71SHasso Tepper int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
8517f3c3d6fSHasso Tepper {
8527f3c3d6fSHasso Tepper 	int order, ret;
8537f3c3d6fSHasso Tepper 
8547f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
8557f3c3d6fSHasso Tepper 		return EINVAL;
8567f3c3d6fSHasso Tepper 
8574cd92098Szrj 	order = order_base_2(request->size);
8587f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
8597f3c3d6fSHasso Tepper 		return EINVAL;
8607f3c3d6fSHasso Tepper 
8615718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
862b3705d71SHasso Tepper 
8637f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8647f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8655718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8667f3c3d6fSHasso Tepper 		return EBUSY;
8677f3c3d6fSHasso Tepper 	}
8687f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
8697f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
8705718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
8717f3c3d6fSHasso Tepper 		return ENOMEM;
8727f3c3d6fSHasso Tepper 	}
8737f3c3d6fSHasso Tepper 
8747f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_agp(dev, request);
8757f3c3d6fSHasso Tepper 
8765718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
8777f3c3d6fSHasso Tepper 
8787f3c3d6fSHasso Tepper 	return ret;
8797f3c3d6fSHasso Tepper }
8807f3c3d6fSHasso Tepper 
881df4baf3dSFrançois Tigeot static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request)
8827f3c3d6fSHasso Tepper {
8837f3c3d6fSHasso Tepper 	int order, ret;
8847f3c3d6fSHasso Tepper 
885c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
8867f3c3d6fSHasso Tepper 		return EACCES;
8877f3c3d6fSHasso Tepper 
8887f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
8897f3c3d6fSHasso Tepper 		return EINVAL;
8907f3c3d6fSHasso Tepper 
8914cd92098Szrj 	order = order_base_2(request->size);
8927f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
8937f3c3d6fSHasso Tepper 		return EINVAL;
8947f3c3d6fSHasso Tepper 
8955718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
896b3705d71SHasso Tepper 
8977f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8987f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8995718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9007f3c3d6fSHasso Tepper 		return EBUSY;
9017f3c3d6fSHasso Tepper 	}
9027f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
9037f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
9045718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9057f3c3d6fSHasso Tepper 		return ENOMEM;
9067f3c3d6fSHasso Tepper 	}
9077f3c3d6fSHasso Tepper 
9087f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_sg(dev, request);
9097f3c3d6fSHasso Tepper 
9105718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
9117f3c3d6fSHasso Tepper 
9127f3c3d6fSHasso Tepper 	return ret;
9137f3c3d6fSHasso Tepper }
9147f3c3d6fSHasso Tepper 
915b3705d71SHasso Tepper int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
9167f3c3d6fSHasso Tepper {
9177f3c3d6fSHasso Tepper 	int order, ret;
9187f3c3d6fSHasso Tepper 
919c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
9207f3c3d6fSHasso Tepper 		return EACCES;
9217f3c3d6fSHasso Tepper 
9227f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
9237f3c3d6fSHasso Tepper 		return EINVAL;
9247f3c3d6fSHasso Tepper 
9254cd92098Szrj 	order = order_base_2(request->size);
9267f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
9277f3c3d6fSHasso Tepper 		return EINVAL;
9287f3c3d6fSHasso Tepper 
9295718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
930b3705d71SHasso Tepper 
9317f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
9327f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
9335718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9347f3c3d6fSHasso Tepper 		return EBUSY;
9357f3c3d6fSHasso Tepper 	}
9367f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
9377f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
9385718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
9397f3c3d6fSHasso Tepper 		return ENOMEM;
9407f3c3d6fSHasso Tepper 	}
9417f3c3d6fSHasso Tepper 
9427f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_pci(dev, request);
9437f3c3d6fSHasso Tepper 
9445718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
9457f3c3d6fSHasso Tepper 
9467f3c3d6fSHasso Tepper 	return ret;
9477f3c3d6fSHasso Tepper }
9487f3c3d6fSHasso Tepper 
949df4baf3dSFrançois Tigeot /**
950df4baf3dSFrançois Tigeot  * Add buffers for DMA transfers (ioctl).
951df4baf3dSFrançois Tigeot  *
952df4baf3dSFrançois Tigeot  * \param inode device inode.
953df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
954df4baf3dSFrançois Tigeot  * \param cmd command.
955df4baf3dSFrançois Tigeot  * \param arg pointer to a struct drm_buf_desc request.
956df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
957df4baf3dSFrançois Tigeot  *
958df4baf3dSFrançois Tigeot  * According with the memory type specified in drm_buf_desc::flags and the
959df4baf3dSFrançois Tigeot  * build options, it dispatches the call either to addbufs_agp(),
960df4baf3dSFrançois Tigeot  * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
961df4baf3dSFrançois Tigeot  * PCI memory respectively.
962df4baf3dSFrançois Tigeot  */
963df4baf3dSFrançois Tigeot int drm_addbufs(struct drm_device *dev, void *data,
964df4baf3dSFrançois Tigeot 		struct drm_file *file_priv)
9657f3c3d6fSHasso Tepper {
966b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
9677f3c3d6fSHasso Tepper 	int err;
9687f3c3d6fSHasso Tepper 
9697f3c3d6fSHasso Tepper 	if (request->flags & _DRM_AGP_BUFFER)
9707f3c3d6fSHasso Tepper 		err = drm_addbufs_agp(dev, request);
9717f3c3d6fSHasso Tepper 	else if (request->flags & _DRM_SG_BUFFER)
9727f3c3d6fSHasso Tepper 		err = drm_addbufs_sg(dev, request);
9737f3c3d6fSHasso Tepper 	else
9747f3c3d6fSHasso Tepper 		err = drm_addbufs_pci(dev, request);
9757f3c3d6fSHasso Tepper 
9767f3c3d6fSHasso Tepper 	return err;
9777f3c3d6fSHasso Tepper }
9787f3c3d6fSHasso Tepper 
979df4baf3dSFrançois Tigeot /**
980df4baf3dSFrançois Tigeot  * Get information about the buffer mappings.
981df4baf3dSFrançois Tigeot  *
982df4baf3dSFrançois Tigeot  * This was originally mean for debugging purposes, or by a sophisticated
983df4baf3dSFrançois Tigeot  * client library to determine how best to use the available buffers (e.g.,
984df4baf3dSFrançois Tigeot  * large buffers can be used for image transfer).
985df4baf3dSFrançois Tigeot  *
986df4baf3dSFrançois Tigeot  * \param inode device inode.
987df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
988df4baf3dSFrançois Tigeot  * \param cmd command.
989df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_info structure.
990df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
991df4baf3dSFrançois Tigeot  *
992*24edb884SFrançois Tigeot  * Increments drm_device::buf_use while holding the drm_device::buf_lock
993df4baf3dSFrançois Tigeot  * lock, preventing of allocating more buffers after this call. Information
994df4baf3dSFrançois Tigeot  * about each requested buffer is then copied into user space.
995df4baf3dSFrançois Tigeot  */
996df4baf3dSFrançois Tigeot int drm_infobufs(struct drm_device *dev, void *data,
997df4baf3dSFrançois Tigeot 		 struct drm_file *file_priv)
9987f3c3d6fSHasso Tepper {
999*24edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
1000b3705d71SHasso Tepper 	struct drm_buf_info *request = data;
10017f3c3d6fSHasso Tepper 	int i;
10027f3c3d6fSHasso Tepper 	int count;
10037f3c3d6fSHasso Tepper 
1004*24edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
1005*24edb884SFrançois Tigeot 		return -EINVAL;
1006*24edb884SFrançois Tigeot 
1007*24edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1008*24edb884SFrançois Tigeot 		return -EINVAL;
1009*24edb884SFrançois Tigeot 
1010*24edb884SFrançois Tigeot 	if (!dma)
1011*24edb884SFrançois Tigeot 		return -EINVAL;
1012*24edb884SFrançois Tigeot 
1013*24edb884SFrançois Tigeot 	spin_lock(&dev->buf_lock);
1014*24edb884SFrançois Tigeot 	if (atomic_read(&dev->buf_alloc)) {
1015*24edb884SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
1016*24edb884SFrançois Tigeot 		return -EBUSY;
1017*24edb884SFrançois Tigeot 	}
10187f3c3d6fSHasso Tepper 	++dev->buf_use;		/* Can't allocate more after this call */
1019*24edb884SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
10207f3c3d6fSHasso Tepper 
10217f3c3d6fSHasso Tepper 	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
1022b3705d71SHasso Tepper 		if (dma->bufs[i].buf_count)
1023b3705d71SHasso Tepper 			++count;
10247f3c3d6fSHasso Tepper 	}
10257f3c3d6fSHasso Tepper 
10267f3c3d6fSHasso Tepper 	DRM_DEBUG("count = %d\n", count);
10277f3c3d6fSHasso Tepper 
10287f3c3d6fSHasso Tepper 	if (request->count >= count) {
10297f3c3d6fSHasso Tepper 		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
10307f3c3d6fSHasso Tepper 			if (dma->bufs[i].buf_count) {
1031*24edb884SFrançois Tigeot 				struct drm_buf_desc __user *to =
1032*24edb884SFrançois Tigeot 				    &request->list[count];
1033*24edb884SFrançois Tigeot 				struct drm_buf_entry *from = &dma->bufs[i];
1034*24edb884SFrançois Tigeot 				if (copy_to_user(&to->count,
1035*24edb884SFrançois Tigeot 						 &from->buf_count,
1036*24edb884SFrançois Tigeot 						 sizeof(from->buf_count)) ||
1037*24edb884SFrançois Tigeot 				    copy_to_user(&to->size,
1038*24edb884SFrançois Tigeot 						 &from->buf_size,
1039*24edb884SFrançois Tigeot 						 sizeof(from->buf_size)) ||
1040*24edb884SFrançois Tigeot 				    copy_to_user(&to->low_mark,
1041*24edb884SFrançois Tigeot 						 &from->low_mark,
1042*24edb884SFrançois Tigeot 						 sizeof(from->low_mark)) ||
1043*24edb884SFrançois Tigeot 				    copy_to_user(&to->high_mark,
1044*24edb884SFrançois Tigeot 						 &from->high_mark,
1045*24edb884SFrançois Tigeot 						 sizeof(from->high_mark)))
1046*24edb884SFrançois Tigeot 					return -EFAULT;
10477f3c3d6fSHasso Tepper 
10487f3c3d6fSHasso Tepper 				DRM_DEBUG("%d %d %d %d %d\n",
1049*24edb884SFrançois Tigeot 					  i,
1050*24edb884SFrançois Tigeot 					  dma->bufs[i].buf_count,
10517f3c3d6fSHasso Tepper 					  dma->bufs[i].buf_size,
1052*24edb884SFrançois Tigeot 					  dma->bufs[i].low_mark,
1053*24edb884SFrançois Tigeot 					  dma->bufs[i].high_mark);
10547f3c3d6fSHasso Tepper 				++count;
10557f3c3d6fSHasso Tepper 			}
10567f3c3d6fSHasso Tepper 		}
10577f3c3d6fSHasso Tepper 	}
10587f3c3d6fSHasso Tepper 	request->count = count;
10597f3c3d6fSHasso Tepper 
1060*24edb884SFrançois Tigeot 	return 0;
10617f3c3d6fSHasso Tepper }
10627f3c3d6fSHasso Tepper 
1063df4baf3dSFrançois Tigeot /**
1064df4baf3dSFrançois Tigeot  * Specifies a low and high water mark for buffer allocation
1065df4baf3dSFrançois Tigeot  *
1066df4baf3dSFrançois Tigeot  * \param inode device inode.
1067df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1068df4baf3dSFrançois Tigeot  * \param cmd command.
1069df4baf3dSFrançois Tigeot  * \param arg a pointer to a drm_buf_desc structure.
1070df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1071df4baf3dSFrançois Tigeot  *
1072df4baf3dSFrançois Tigeot  * Verifies that the size order is bounded between the admissible orders and
1073df4baf3dSFrançois Tigeot  * updates the respective drm_device_dma::bufs entry low and high water mark.
1074df4baf3dSFrançois Tigeot  *
1075df4baf3dSFrançois Tigeot  * \note This ioctl is deprecated and mostly never used.
1076df4baf3dSFrançois Tigeot  */
1077df4baf3dSFrançois Tigeot int drm_markbufs(struct drm_device *dev, void *data,
1078df4baf3dSFrançois Tigeot 		 struct drm_file *file_priv)
10797f3c3d6fSHasso Tepper {
1080*24edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
1081b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
10827f3c3d6fSHasso Tepper 	int order;
1083*24edb884SFrançois Tigeot 	struct drm_buf_entry *entry;
1084*24edb884SFrançois Tigeot 
1085*24edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
1086*24edb884SFrançois Tigeot 		return -EINVAL;
1087*24edb884SFrançois Tigeot 
1088*24edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1089*24edb884SFrançois Tigeot 		return -EINVAL;
1090*24edb884SFrançois Tigeot 
1091*24edb884SFrançois Tigeot 	if (!dma)
1092*24edb884SFrançois Tigeot 		return -EINVAL;
10937f3c3d6fSHasso Tepper 
10947f3c3d6fSHasso Tepper 	DRM_DEBUG("%d, %d, %d\n",
10957f3c3d6fSHasso Tepper 		  request->size, request->low_mark, request->high_mark);
10964cd92098Szrj 	order = order_base_2(request->size);
1097*24edb884SFrançois Tigeot 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
1098*24edb884SFrançois Tigeot 		return -EINVAL;
1099*24edb884SFrançois Tigeot 	entry = &dma->bufs[order];
11007f3c3d6fSHasso Tepper 
1101*24edb884SFrançois Tigeot 	if (request->low_mark < 0 || request->low_mark > entry->buf_count)
1102*24edb884SFrançois Tigeot 		return -EINVAL;
1103*24edb884SFrançois Tigeot 	if (request->high_mark < 0 || request->high_mark > entry->buf_count)
1104*24edb884SFrançois Tigeot 		return -EINVAL;
11057f3c3d6fSHasso Tepper 
1106*24edb884SFrançois Tigeot 	entry->low_mark = request->low_mark;
1107*24edb884SFrançois Tigeot 	entry->high_mark = request->high_mark;
11087f3c3d6fSHasso Tepper 
11097f3c3d6fSHasso Tepper 	return 0;
11107f3c3d6fSHasso Tepper }
11117f3c3d6fSHasso Tepper 
1112df4baf3dSFrançois Tigeot /**
1113df4baf3dSFrançois Tigeot  * Unreserve the buffers in list, previously reserved using drmDMA.
1114df4baf3dSFrançois Tigeot  *
1115df4baf3dSFrançois Tigeot  * \param inode device inode.
1116df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1117df4baf3dSFrançois Tigeot  * \param cmd command.
1118df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_free structure.
1119df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1120df4baf3dSFrançois Tigeot  *
1121df4baf3dSFrançois Tigeot  * Calls free_buffer() for each used buffer.
1122df4baf3dSFrançois Tigeot  * This function is primarily used for debugging.
1123df4baf3dSFrançois Tigeot  */
1124df4baf3dSFrançois Tigeot int drm_freebufs(struct drm_device *dev, void *data,
1125df4baf3dSFrançois Tigeot 		 struct drm_file *file_priv)
11267f3c3d6fSHasso Tepper {
11277f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
1128b3705d71SHasso Tepper 	struct drm_buf_free *request = data;
11297f3c3d6fSHasso Tepper 	int i;
11307f3c3d6fSHasso Tepper 	int idx;
11317f3c3d6fSHasso Tepper 	drm_buf_t *buf;
11327f3c3d6fSHasso Tepper 	int retcode = 0;
11337f3c3d6fSHasso Tepper 
11347f3c3d6fSHasso Tepper 	DRM_DEBUG("%d\n", request->count);
11357f3c3d6fSHasso Tepper 
11365718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
11377f3c3d6fSHasso Tepper 	for (i = 0; i < request->count; i++) {
1138c6f73aabSFrançois Tigeot 		if (copy_from_user(&idx, &request->list[i], sizeof(idx))) {
11397f3c3d6fSHasso Tepper 			retcode = EFAULT;
11407f3c3d6fSHasso Tepper 			break;
11417f3c3d6fSHasso Tepper 		}
11427f3c3d6fSHasso Tepper 		if (idx < 0 || idx >= dma->buf_count) {
11437f3c3d6fSHasso Tepper 			DRM_ERROR("Index %d (of %d max)\n",
11447f3c3d6fSHasso Tepper 			    idx, dma->buf_count - 1);
11457f3c3d6fSHasso Tepper 			retcode = EINVAL;
11467f3c3d6fSHasso Tepper 			break;
11477f3c3d6fSHasso Tepper 		}
11487f3c3d6fSHasso Tepper 		buf = dma->buflist[idx];
11497f3c3d6fSHasso Tepper 		if (buf->file_priv != file_priv) {
11507f3c3d6fSHasso Tepper 			DRM_ERROR("Process %d freeing buffer not owned\n",
11517f3c3d6fSHasso Tepper 			    DRM_CURRENTPID);
11527f3c3d6fSHasso Tepper 			retcode = EINVAL;
11537f3c3d6fSHasso Tepper 			break;
11547f3c3d6fSHasso Tepper 		}
11557f3c3d6fSHasso Tepper 		drm_free_buffer(dev, buf);
11567f3c3d6fSHasso Tepper 	}
11575718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
11587f3c3d6fSHasso Tepper 
11597f3c3d6fSHasso Tepper 	return retcode;
11607f3c3d6fSHasso Tepper }
11617f3c3d6fSHasso Tepper 
1162df4baf3dSFrançois Tigeot /**
1163df4baf3dSFrançois Tigeot  * Maps all of the DMA buffers into client-virtual space (ioctl).
1164df4baf3dSFrançois Tigeot  *
1165df4baf3dSFrançois Tigeot  * \param inode device inode.
1166df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1167df4baf3dSFrançois Tigeot  * \param cmd command.
1168df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_map structure.
1169df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1170df4baf3dSFrançois Tigeot  *
1171df4baf3dSFrançois Tigeot  * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information
1172df4baf3dSFrançois Tigeot  * about each buffer into user space. For PCI buffers, it calls vm_mmap() with
1173df4baf3dSFrançois Tigeot  * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
1174df4baf3dSFrançois Tigeot  * drm_mmap_dma().
1175df4baf3dSFrançois Tigeot  */
1176df4baf3dSFrançois Tigeot int drm_mapbufs(struct drm_device *dev, void *data,
1177df4baf3dSFrançois Tigeot 	        struct drm_file *file_priv)
11787f3c3d6fSHasso Tepper {
11797f3c3d6fSHasso Tepper 	drm_device_dma_t *dma = dev->dma;
11807f3c3d6fSHasso Tepper 	int retcode = 0;
11817f3c3d6fSHasso Tepper 	const int zero = 0;
11827f3c3d6fSHasso Tepper 	vm_offset_t address;
11837f3c3d6fSHasso Tepper 	struct vmspace *vms;
11847f3c3d6fSHasso Tepper 	vm_ooffset_t foff;
11857f3c3d6fSHasso Tepper 	vm_size_t size;
11867f3c3d6fSHasso Tepper 	vm_offset_t vaddr;
1187b3705d71SHasso Tepper 	struct drm_buf_map *request = data;
11887f3c3d6fSHasso Tepper 	int i;
11897f3c3d6fSHasso Tepper 
11907f3c3d6fSHasso Tepper 	vms = DRM_CURPROC->td_proc->p_vmspace;
11917f3c3d6fSHasso Tepper 
11925718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
11937f3c3d6fSHasso Tepper 	dev->buf_use++;		/* Can't allocate more after this call */
11945718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
11957f3c3d6fSHasso Tepper 
11967f3c3d6fSHasso Tepper 	if (request->count < dma->buf_count)
11977f3c3d6fSHasso Tepper 		goto done;
11987f3c3d6fSHasso Tepper 
1199b3705d71SHasso Tepper 	if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
1200b3705d71SHasso Tepper 	    (drm_core_check_feature(dev, DRIVER_SG) &&
1201b3705d71SHasso Tepper 	    (dma->flags & _DRM_DMA_USE_SG))) {
12027f3c3d6fSHasso Tepper 		drm_local_map_t *map = dev->agp_buffer_map;
12037f3c3d6fSHasso Tepper 
12047f3c3d6fSHasso Tepper 		if (map == NULL) {
12057f3c3d6fSHasso Tepper 			retcode = EINVAL;
12067f3c3d6fSHasso Tepper 			goto done;
12077f3c3d6fSHasso Tepper 		}
12087f3c3d6fSHasso Tepper 		size = round_page(map->size);
120999f70504SFrançois Tigeot 		foff = (unsigned long)map->handle;
12107f3c3d6fSHasso Tepper 	} else {
12117f3c3d6fSHasso Tepper 		size = round_page(dma->byte_count),
12127f3c3d6fSHasso Tepper 		foff = 0;
12137f3c3d6fSHasso Tepper 	}
12147f3c3d6fSHasso Tepper 
12157f3c3d6fSHasso Tepper 	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
12167f3c3d6fSHasso Tepper 	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1217b3705d71SHasso Tepper 	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
1218b3705d71SHasso Tepper 	    SLIST_FIRST(&dev->devnode->si_hlist), foff);
12197f3c3d6fSHasso Tepper 	if (retcode)
12207f3c3d6fSHasso Tepper 		goto done;
12217f3c3d6fSHasso Tepper 
12227f3c3d6fSHasso Tepper 	request->virtual = (void *)vaddr;
12237f3c3d6fSHasso Tepper 
12247f3c3d6fSHasso Tepper 	for (i = 0; i < dma->buf_count; i++) {
1225c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].idx,
12267f3c3d6fSHasso Tepper 		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
12277f3c3d6fSHasso Tepper 			retcode = EFAULT;
12287f3c3d6fSHasso Tepper 			goto done;
12297f3c3d6fSHasso Tepper 		}
1230c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].total,
12317f3c3d6fSHasso Tepper 		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
12327f3c3d6fSHasso Tepper 			retcode = EFAULT;
12337f3c3d6fSHasso Tepper 			goto done;
12347f3c3d6fSHasso Tepper 		}
1235c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].used, &zero,
12367f3c3d6fSHasso Tepper 		    sizeof(zero))) {
12377f3c3d6fSHasso Tepper 			retcode = EFAULT;
12387f3c3d6fSHasso Tepper 			goto done;
12397f3c3d6fSHasso Tepper 		}
12407f3c3d6fSHasso Tepper 		address = vaddr + dma->buflist[i]->offset; /* *** */
1241c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].address, &address,
12427f3c3d6fSHasso Tepper 		    sizeof(address))) {
12437f3c3d6fSHasso Tepper 			retcode = EFAULT;
12447f3c3d6fSHasso Tepper 			goto done;
12457f3c3d6fSHasso Tepper 		}
12467f3c3d6fSHasso Tepper 	}
12477f3c3d6fSHasso Tepper       done:
12487f3c3d6fSHasso Tepper 	request->count = dma->buf_count;
12497f3c3d6fSHasso Tepper 	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
12507f3c3d6fSHasso Tepper 
12517f3c3d6fSHasso Tepper 	return retcode;
12527f3c3d6fSHasso Tepper }
1253