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