1df4baf3dSFrançois Tigeot /* 21b13d190SFrançois Tigeot * Legacy: Generic DRM Buffer Management 3df4baf3dSFrançois Tigeot * 47f3c3d6fSHasso Tepper * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. 57f3c3d6fSHasso Tepper * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 67f3c3d6fSHasso Tepper * All Rights Reserved. 77f3c3d6fSHasso Tepper * 81b13d190SFrançois Tigeot * Author: Rickard E. (Rik) Faith <faith@valinux.com> 91b13d190SFrançois Tigeot * Author: Gareth Hughes <gareth@valinux.com> 101b13d190SFrançois Tigeot * 117f3c3d6fSHasso Tepper * Permission is hereby granted, free of charge, to any person obtaining a 127f3c3d6fSHasso Tepper * copy of this software and associated documentation files (the "Software"), 137f3c3d6fSHasso Tepper * to deal in the Software without restriction, including without limitation 147f3c3d6fSHasso Tepper * the rights to use, copy, modify, merge, publish, distribute, sublicense, 157f3c3d6fSHasso Tepper * and/or sell copies of the Software, and to permit persons to whom the 167f3c3d6fSHasso Tepper * Software is furnished to do so, subject to the following conditions: 177f3c3d6fSHasso Tepper * 187f3c3d6fSHasso Tepper * The above copyright notice and this permission notice (including the next 197f3c3d6fSHasso Tepper * paragraph) shall be included in all copies or substantial portions of the 207f3c3d6fSHasso Tepper * Software. 217f3c3d6fSHasso Tepper * 227f3c3d6fSHasso Tepper * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 237f3c3d6fSHasso Tepper * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 247f3c3d6fSHasso Tepper * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 257f3c3d6fSHasso Tepper * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 267f3c3d6fSHasso Tepper * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 277f3c3d6fSHasso Tepper * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 287f3c3d6fSHasso Tepper * OTHER DEALINGS IN THE SOFTWARE. 297f3c3d6fSHasso Tepper */ 307f3c3d6fSHasso Tepper 315718399fSFrançois Tigeot #include <sys/conf.h> 325718399fSFrançois Tigeot #include <bus/pci/pcireg.h> 33df4baf3dSFrançois Tigeot #include <linux/types.h> 34df4baf3dSFrançois Tigeot #include <linux/export.h> 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 361b13d190SFrançois Tigeot #include "drm_legacy.h" 377f3c3d6fSHasso Tepper 381b13d190SFrançois Tigeot int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset, 39df4baf3dSFrançois Tigeot unsigned int size, enum drm_map_type type, 40df4baf3dSFrançois Tigeot enum drm_map_flags flags, struct drm_local_map **map_ptr) 417f3c3d6fSHasso Tepper { 42f599cd46SFrançois Tigeot struct drm_local_map *map; 4379d1f0c0SFrançois Tigeot struct drm_map_list *entry = NULL; 445c002123SFrançois Tigeot drm_dma_handle_t *dmah; 4579d1f0c0SFrançois Tigeot 4679d1f0c0SFrançois Tigeot /* Allocate a new map structure, fill it in, and do any type-specific 4779d1f0c0SFrançois Tigeot * initialization necessary. 4879d1f0c0SFrançois Tigeot */ 49f8677ba6SMatthew Dillon map = kmalloc(sizeof(*map), M_DRM, M_ZERO | M_WAITOK | M_NULLOK); 5079d1f0c0SFrançois Tigeot if (!map) { 51b922632fSImre Vadász return -ENOMEM; 5279d1f0c0SFrançois Tigeot } 5379d1f0c0SFrançois Tigeot 5479d1f0c0SFrançois Tigeot map->offset = offset; 5579d1f0c0SFrançois Tigeot map->size = size; 5679d1f0c0SFrançois Tigeot map->type = type; 5779d1f0c0SFrançois Tigeot map->flags = flags; 587f3c3d6fSHasso Tepper 597f3c3d6fSHasso Tepper /* Only allow shared memory to be removable since we only keep enough 607f3c3d6fSHasso Tepper * book keeping information about shared memory to allow for removal 617f3c3d6fSHasso Tepper * when processes fork. 627f3c3d6fSHasso Tepper */ 637f3c3d6fSHasso Tepper if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) { 647f3c3d6fSHasso Tepper DRM_ERROR("Requested removable map for non-DRM_SHM\n"); 65*175896dfSzrj kfree(map); 66b922632fSImre Vadász return -EINVAL; 677f3c3d6fSHasso Tepper } 687f3c3d6fSHasso Tepper if ((offset & PAGE_MASK) || (size & PAGE_MASK)) { 692ffc4fa7SSascha Wildner DRM_ERROR("offset/size not page aligned: 0x%jx/0x%04x\n", 702ffc4fa7SSascha Wildner (uintmax_t)offset, size); 71*175896dfSzrj kfree(map); 72b922632fSImre Vadász return -EINVAL; 737f3c3d6fSHasso Tepper } 747f3c3d6fSHasso Tepper if (offset + size < offset) { 752ffc4fa7SSascha Wildner DRM_ERROR("offset and size wrap around: 0x%jx/0x%04x\n", 762ffc4fa7SSascha Wildner (uintmax_t)offset, size); 77*175896dfSzrj kfree(map); 78b922632fSImre Vadász return -EINVAL; 797f3c3d6fSHasso Tepper } 807f3c3d6fSHasso Tepper 81df4baf3dSFrançois Tigeot DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n", 82df4baf3dSFrançois Tigeot (unsigned long long)map->offset, map->size, map->type); 837f3c3d6fSHasso Tepper 847f3c3d6fSHasso Tepper /* Check if this is just another version of a kernel-allocated map, and 857f3c3d6fSHasso Tepper * just hand that back if so. 867f3c3d6fSHasso Tepper */ 877f3c3d6fSHasso Tepper if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER || 887f3c3d6fSHasso Tepper type == _DRM_SHM) { 89f599cd46SFrançois Tigeot list_for_each_entry(entry, &dev->maplist, head) { 90f599cd46SFrançois Tigeot if (entry->map->type == type && (entry->map->offset == offset || 91f599cd46SFrançois Tigeot (entry->map->type == _DRM_SHM && 92f599cd46SFrançois Tigeot entry->map->flags == _DRM_CONTAINS_LOCK))) { 93f599cd46SFrançois Tigeot entry->map->size = size; 947f3c3d6fSHasso Tepper DRM_DEBUG("Found kernel map %d\n", type); 957f3c3d6fSHasso Tepper goto done; 967f3c3d6fSHasso Tepper } 977f3c3d6fSHasso Tepper } 987f3c3d6fSHasso Tepper } 997f3c3d6fSHasso Tepper 1007f3c3d6fSHasso Tepper switch (map->type) { 1017f3c3d6fSHasso Tepper case _DRM_REGISTERS: 1027f3c3d6fSHasso Tepper case _DRM_FRAME_BUFFER: 1036431cd91SFrançois Tigeot 1046431cd91SFrançois Tigeot if (map->type == _DRM_FRAME_BUFFER || 1056431cd91SFrançois Tigeot (map->flags & _DRM_WRITE_COMBINING)) { 1066431cd91SFrançois Tigeot map->mtrr = 1076431cd91SFrançois Tigeot arch_phys_wc_add(map->offset, map->size); 1086431cd91SFrançois Tigeot } 1096431cd91SFrançois Tigeot if (map->type == _DRM_REGISTERS) { 1106431cd91SFrançois Tigeot if (map->flags & _DRM_WRITE_COMBINING) 1116431cd91SFrançois Tigeot map->handle = ioremap_wc(map->offset, 1126431cd91SFrançois Tigeot map->size); 1136431cd91SFrançois Tigeot else 1146431cd91SFrançois Tigeot map->handle = ioremap(map->offset, map->size); 1156431cd91SFrançois Tigeot if (!map->handle) { 1166431cd91SFrançois Tigeot kfree(map); 1176431cd91SFrançois Tigeot return -ENOMEM; 1186431cd91SFrançois Tigeot } 1196431cd91SFrançois Tigeot } 1206431cd91SFrançois Tigeot 1217f3c3d6fSHasso Tepper break; 1227f3c3d6fSHasso Tepper case _DRM_SHM: 123f8677ba6SMatthew Dillon map->handle = kmalloc(map->size, M_DRM, M_WAITOK | M_NULLOK); 1247f3c3d6fSHasso Tepper DRM_DEBUG("%lu %d %p\n", 1254cd92098Szrj map->size, order_base_2(map->size), map->handle); 1265c002123SFrançois Tigeot if (!map->handle) { 127*175896dfSzrj kfree(map); 128b922632fSImre Vadász return -ENOMEM; 1297f3c3d6fSHasso Tepper } 1305c002123SFrançois Tigeot map->offset = (unsigned long)map->handle; 1317f3c3d6fSHasso Tepper if (map->flags & _DRM_CONTAINS_LOCK) { 1327f3c3d6fSHasso Tepper /* Prevent a 2nd X Server from creating a 2nd lock */ 13379f713b0SFrançois Tigeot DRM_LOCK(dev); 13479f713b0SFrançois Tigeot if (dev->lock.hw_lock != NULL) { 13579f713b0SFrançois Tigeot DRM_UNLOCK(dev); 136*175896dfSzrj kfree(map->handle); 137*175896dfSzrj kfree(map); 138b922632fSImre Vadász return -EBUSY; 1397f3c3d6fSHasso Tepper } 14079f713b0SFrançois Tigeot dev->lock.hw_lock = map->handle; /* Pointer to lock */ 14179f713b0SFrançois Tigeot DRM_UNLOCK(dev); 1427f3c3d6fSHasso Tepper } 1437f3c3d6fSHasso Tepper break; 1447f3c3d6fSHasso Tepper case _DRM_AGP: 14553e4e524Szrj 14653e4e524Szrj if (!dev->agp) { 14753e4e524Szrj kfree(map); 14853e4e524Szrj return -EINVAL; 14953e4e524Szrj } 1507f3c3d6fSHasso Tepper /*valid = 0;*/ 1517f3c3d6fSHasso Tepper /* In some cases (i810 driver), user space may have already 1527f3c3d6fSHasso Tepper * added the AGP base itself, because dev->agp->base previously 1537f3c3d6fSHasso Tepper * only got set during AGP enable. So, only add the base 1547f3c3d6fSHasso Tepper * address if the map's offset isn't already within the 1557f3c3d6fSHasso Tepper * aperture. 1567f3c3d6fSHasso Tepper */ 1577f3c3d6fSHasso Tepper if (map->offset < dev->agp->base || 1587f3c3d6fSHasso Tepper map->offset > dev->agp->base + 1599d567857SJean-Sébastien Pédron dev->agp->agp_info.ai_aperture_size - 1) { 1607f3c3d6fSHasso Tepper map->offset += dev->agp->base; 1617f3c3d6fSHasso Tepper } 1629d567857SJean-Sébastien Pédron map->mtrr = dev->agp->agp_mtrr; /* for getmap */ 1637f3c3d6fSHasso Tepper /*for (entry = dev->agp->memory; entry; entry = entry->next) { 1647f3c3d6fSHasso Tepper if ((map->offset >= entry->bound) && 1657f3c3d6fSHasso Tepper (map->offset + map->size <= 1667f3c3d6fSHasso Tepper entry->bound + entry->pages * PAGE_SIZE)) { 1677f3c3d6fSHasso Tepper valid = 1; 1687f3c3d6fSHasso Tepper break; 1697f3c3d6fSHasso Tepper } 1707f3c3d6fSHasso Tepper } 1717f3c3d6fSHasso Tepper if (!valid) { 172*175896dfSzrj kfree(map); 173b922632fSImre Vadász return -EACCES; 1747f3c3d6fSHasso Tepper }*/ 1757f3c3d6fSHasso Tepper break; 1767f3c3d6fSHasso Tepper case _DRM_SCATTER_GATHER: 1777f3c3d6fSHasso Tepper if (!dev->sg) { 178*175896dfSzrj kfree(map); 179b922632fSImre Vadász return -EINVAL; 1807f3c3d6fSHasso Tepper } 1815c002123SFrançois Tigeot map->handle = (void *)(uintptr_t)(dev->sg->vaddr + offset); 18299f70504SFrançois Tigeot map->offset = dev->sg->vaddr + offset; 1837f3c3d6fSHasso Tepper break; 1847f3c3d6fSHasso Tepper case _DRM_CONSISTENT: 185b31e9d59SFrançois Tigeot /* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G, 186b31e9d59SFrançois Tigeot * As we're limiting the address to 2^32-1 (or less), 187b31e9d59SFrançois Tigeot * casting it down to 32 bits is no problem, but we 188b31e9d59SFrançois Tigeot * need to point to a 64bit variable first. */ 189b31e9d59SFrançois Tigeot dmah = drm_pci_alloc(dev, map->size, map->size); 1905c002123SFrançois Tigeot if (!dmah) { 191158486a6SFrançois Tigeot kfree(map); 192b31e9d59SFrançois Tigeot return -ENOMEM; 1937f3c3d6fSHasso Tepper } 1945c002123SFrançois Tigeot map->handle = dmah->vaddr; 195b31e9d59SFrançois Tigeot map->offset = dmah->busaddr; 1967f3c3d6fSHasso Tepper break; 1977f3c3d6fSHasso Tepper default: 1987f3c3d6fSHasso Tepper DRM_ERROR("Bad map type %d\n", map->type); 199*175896dfSzrj kfree(map); 200b922632fSImre Vadász return -EINVAL; 2017f3c3d6fSHasso Tepper } 2027f3c3d6fSHasso Tepper 203f599cd46SFrançois Tigeot list_add(&entry->head, &dev->maplist); 2047f3c3d6fSHasso Tepper 2057f3c3d6fSHasso Tepper done: 2067f3c3d6fSHasso Tepper /* Jumped to, with lock held, when a kernel map is found. */ 2077f3c3d6fSHasso Tepper 2087f3c3d6fSHasso Tepper DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset, 2097f3c3d6fSHasso Tepper map->size); 2107f3c3d6fSHasso Tepper 2117f3c3d6fSHasso Tepper *map_ptr = map; 2127f3c3d6fSHasso Tepper 2137f3c3d6fSHasso Tepper return 0; 2147f3c3d6fSHasso Tepper } 2157f3c3d6fSHasso Tepper 216df4baf3dSFrançois Tigeot /** 217df4baf3dSFrançois Tigeot * Ioctl to specify a range of memory that is available for mapping by a 218df4baf3dSFrançois Tigeot * non-root process. 219df4baf3dSFrançois Tigeot * 220df4baf3dSFrançois Tigeot * \param inode device inode. 221df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 222df4baf3dSFrançois Tigeot * \param cmd command. 223df4baf3dSFrançois Tigeot * \param arg pointer to a drm_map structure. 224df4baf3dSFrançois Tigeot * \return zero on success or a negative value on error. 225df4baf3dSFrançois Tigeot * 226df4baf3dSFrançois Tigeot */ 2271b13d190SFrançois Tigeot int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data, 228b3705d71SHasso Tepper struct drm_file *file_priv) 2297f3c3d6fSHasso Tepper { 230b3705d71SHasso Tepper struct drm_map *request = data; 2317f3c3d6fSHasso Tepper drm_local_map_t *map; 2327f3c3d6fSHasso Tepper int err; 2337f3c3d6fSHasso Tepper 2347f3c3d6fSHasso Tepper if (!(dev->flags & (FREAD|FWRITE))) 235b922632fSImre Vadász return -EACCES; /* Require read/write */ 2367f3c3d6fSHasso Tepper 237c6f73aabSFrançois Tigeot if (!capable(CAP_SYS_ADMIN) && request->type != _DRM_AGP) 238b922632fSImre Vadász return -EACCES; 2397f3c3d6fSHasso Tepper 24079f713b0SFrançois Tigeot DRM_LOCK(dev); 2411b13d190SFrançois Tigeot err = drm_legacy_addmap(dev, request->offset, request->size, request->type, 2427f3c3d6fSHasso Tepper request->flags, &map); 24379f713b0SFrançois Tigeot DRM_UNLOCK(dev); 24479f713b0SFrançois Tigeot if (err != 0) 2457f3c3d6fSHasso Tepper return err; 2467f3c3d6fSHasso Tepper 2477f3c3d6fSHasso Tepper request->offset = map->offset; 2487f3c3d6fSHasso Tepper request->size = map->size; 2497f3c3d6fSHasso Tepper request->type = map->type; 2507f3c3d6fSHasso Tepper request->flags = map->flags; 2517f3c3d6fSHasso Tepper request->mtrr = map->mtrr; 25299f70504SFrançois Tigeot request->handle = (void *)map->handle; 2537f3c3d6fSHasso Tepper 2547f3c3d6fSHasso Tepper return 0; 2557f3c3d6fSHasso Tepper } 2567f3c3d6fSHasso Tepper 2571b13d190SFrançois Tigeot /** 2581b13d190SFrançois Tigeot * Remove a map private from list and deallocate resources if the mapping 2591b13d190SFrançois Tigeot * isn't in use. 2601b13d190SFrançois Tigeot * 2611b13d190SFrançois Tigeot * Searches the map on drm_device::maplist, removes it from the list, see if 2621b13d190SFrançois Tigeot * its being used, and free any associate resource (such as MTRR's) if it's not 2631b13d190SFrançois Tigeot * being on use. 2641b13d190SFrançois Tigeot * 2651b13d190SFrançois Tigeot * \sa drm_legacy_addmap 2661b13d190SFrançois Tigeot */ 2671b13d190SFrançois Tigeot int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) 2687f3c3d6fSHasso Tepper { 269f599cd46SFrançois Tigeot struct drm_map_list *r_list = NULL, *list_t; 2705c002123SFrançois Tigeot drm_dma_handle_t dmah; 271f599cd46SFrançois Tigeot int found = 0; 27279f713b0SFrançois Tigeot 273f599cd46SFrançois Tigeot /* Find the list entry for the map and remove it */ 274f599cd46SFrançois Tigeot list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { 275f599cd46SFrançois Tigeot if (r_list->map == map) { 276f599cd46SFrançois Tigeot list_del(&r_list->head); 2771b13d190SFrançois Tigeot kfree(r_list); 278f599cd46SFrançois Tigeot found = 1; 279f599cd46SFrançois Tigeot break; 280f599cd46SFrançois Tigeot } 281f599cd46SFrançois Tigeot } 282f599cd46SFrançois Tigeot 283f599cd46SFrançois Tigeot if (!found) 2841b13d190SFrançois Tigeot return -EINVAL; 2857f3c3d6fSHasso Tepper 2867f3c3d6fSHasso Tepper switch (map->type) { 2877f3c3d6fSHasso Tepper case _DRM_REGISTERS: 2886431cd91SFrançois Tigeot drm_legacy_ioremapfree(map, dev); 2897f3c3d6fSHasso Tepper /* FALLTHROUGH */ 2907f3c3d6fSHasso Tepper case _DRM_FRAME_BUFFER: 2916431cd91SFrançois Tigeot arch_phys_wc_del(map->mtrr); 2927f3c3d6fSHasso Tepper break; 2937f3c3d6fSHasso Tepper case _DRM_SHM: 294*175896dfSzrj kfree(map->handle); 2957f3c3d6fSHasso Tepper break; 2967f3c3d6fSHasso Tepper case _DRM_AGP: 2977f3c3d6fSHasso Tepper case _DRM_SCATTER_GATHER: 2987f3c3d6fSHasso Tepper break; 2997f3c3d6fSHasso Tepper case _DRM_CONSISTENT: 3005c002123SFrançois Tigeot dmah.vaddr = map->handle; 3015c002123SFrançois Tigeot dmah.busaddr = map->offset; 3021b13d190SFrançois Tigeot dmah.size = map->size; 3031b13d190SFrançois Tigeot __drm_legacy_pci_free(dev, &dmah); 3047f3c3d6fSHasso Tepper break; 3051b13d190SFrançois Tigeot } 3061b13d190SFrançois Tigeot kfree(map); 3071b13d190SFrançois Tigeot 3081b13d190SFrançois Tigeot return 0; 3097f3c3d6fSHasso Tepper } 31079f713b0SFrançois Tigeot 3111b13d190SFrançois Tigeot int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map) 3121b13d190SFrançois Tigeot { 3131b13d190SFrançois Tigeot int ret; 3141b13d190SFrançois Tigeot 3151b13d190SFrançois Tigeot mutex_lock(&dev->struct_mutex); 3161b13d190SFrançois Tigeot ret = drm_legacy_rmmap_locked(dev, map); 3171b13d190SFrançois Tigeot mutex_unlock(&dev->struct_mutex); 3181b13d190SFrançois Tigeot 3191b13d190SFrançois Tigeot return ret; 3207f3c3d6fSHasso Tepper } 3211b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_rmmap); 3227f3c3d6fSHasso Tepper 323f599cd46SFrançois Tigeot /* The rmmap ioctl appears to be unnecessary. All mappings are torn down on 324f599cd46SFrançois Tigeot * the last close of the device, and this is necessary for cleanup when things 325f599cd46SFrançois Tigeot * exit uncleanly. Therefore, having userland manually remove mappings seems 326f599cd46SFrançois Tigeot * like a pointless exercise since they're going away anyway. 327f599cd46SFrançois Tigeot * 328f599cd46SFrançois Tigeot * One use case might be after addmap is allowed for normal users for SHM and 329f599cd46SFrançois Tigeot * gets used by drivers that the server doesn't need to care about. This seems 330f599cd46SFrançois Tigeot * unlikely. 331f599cd46SFrançois Tigeot * 332f599cd46SFrançois Tigeot * \param inode device inode. 333f599cd46SFrançois Tigeot * \param file_priv DRM file private. 334f599cd46SFrançois Tigeot * \param cmd command. 335f599cd46SFrançois Tigeot * \param arg pointer to a struct drm_map structure. 336f599cd46SFrançois Tigeot * \return zero on success or a negative value on error. 3377f3c3d6fSHasso Tepper */ 3381b13d190SFrançois Tigeot int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data, 339b3705d71SHasso Tepper struct drm_file *file_priv) 3407f3c3d6fSHasso Tepper { 341b3705d71SHasso Tepper struct drm_map *request = data; 342f599cd46SFrançois Tigeot struct drm_local_map *map = NULL; 343f599cd46SFrançois Tigeot struct drm_map_list *r_list; 3447f3c3d6fSHasso Tepper 3455718399fSFrançois Tigeot DRM_LOCK(dev); 346f599cd46SFrançois Tigeot list_for_each_entry(r_list, &dev->maplist, head) { 347f599cd46SFrançois Tigeot if (r_list->map && 348f599cd46SFrançois Tigeot r_list->user_token == (unsigned long)request->handle && 349f599cd46SFrançois Tigeot r_list->map->flags & _DRM_REMOVABLE) { 350f599cd46SFrançois Tigeot map = r_list->map; 3517f3c3d6fSHasso Tepper break; 3527f3c3d6fSHasso Tepper } 353f599cd46SFrançois Tigeot } 3547f3c3d6fSHasso Tepper 355f599cd46SFrançois Tigeot /* List has wrapped around to the head pointer, or its empty we didn't 356f599cd46SFrançois Tigeot * find anything. 357f599cd46SFrançois Tigeot */ 358f599cd46SFrançois Tigeot if (list_empty(&dev->maplist) || !map) { 3595718399fSFrançois Tigeot DRM_UNLOCK(dev); 360f599cd46SFrançois Tigeot return -EINVAL; 361f599cd46SFrançois Tigeot } 362f599cd46SFrançois Tigeot 363f599cd46SFrançois Tigeot /* Register and framebuffer maps are permanent */ 364f599cd46SFrançois Tigeot if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { 365f599cd46SFrançois Tigeot DRM_UNLOCK(dev); 366f599cd46SFrançois Tigeot return 0; 3677f3c3d6fSHasso Tepper } 3687f3c3d6fSHasso Tepper 3691b13d190SFrançois Tigeot drm_legacy_rmmap(dev, map); 3707f3c3d6fSHasso Tepper 3715718399fSFrançois Tigeot DRM_UNLOCK(dev); 3727f3c3d6fSHasso Tepper 3737f3c3d6fSHasso Tepper return 0; 3747f3c3d6fSHasso Tepper } 3757f3c3d6fSHasso Tepper 376df4baf3dSFrançois Tigeot /** 377df4baf3dSFrançois Tigeot * Cleanup after an error on one of the addbufs() functions. 378df4baf3dSFrançois Tigeot * 379df4baf3dSFrançois Tigeot * \param dev DRM device. 380df4baf3dSFrançois Tigeot * \param entry buffer entry where the error occurred. 381df4baf3dSFrançois Tigeot * 382df4baf3dSFrançois Tigeot * Frees any pages and buffers associated with the given entry. 383df4baf3dSFrançois Tigeot */ 384b3705d71SHasso Tepper static void drm_cleanup_buf_error(struct drm_device * dev, 385df4baf3dSFrançois Tigeot struct drm_buf_entry * entry) 3867f3c3d6fSHasso Tepper { 3877f3c3d6fSHasso Tepper int i; 3887f3c3d6fSHasso Tepper 3897f3c3d6fSHasso Tepper if (entry->seg_count) { 3907f3c3d6fSHasso Tepper for (i = 0; i < entry->seg_count; i++) { 3917f3c3d6fSHasso Tepper drm_pci_free(dev, entry->seglist[i]); 3927f3c3d6fSHasso Tepper } 393*175896dfSzrj kfree(entry->seglist); 3947f3c3d6fSHasso Tepper 3957f3c3d6fSHasso Tepper entry->seg_count = 0; 3967f3c3d6fSHasso Tepper } 3977f3c3d6fSHasso Tepper 3987f3c3d6fSHasso Tepper if (entry->buf_count) { 3997f3c3d6fSHasso Tepper for (i = 0; i < entry->buf_count; i++) { 400*175896dfSzrj kfree(entry->buflist[i].dev_private); 4017f3c3d6fSHasso Tepper } 402*175896dfSzrj kfree(entry->buflist); 4037f3c3d6fSHasso Tepper 4047f3c3d6fSHasso Tepper entry->buf_count = 0; 4057f3c3d6fSHasso Tepper } 4067f3c3d6fSHasso Tepper } 4077f3c3d6fSHasso Tepper 408b3705d71SHasso Tepper static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request) 4097f3c3d6fSHasso Tepper { 4104250aa95Szrj struct drm_device_dma *dma = dev->dma; 4114250aa95Szrj struct drm_buf_entry *entry; 4124250aa95Szrj /* struct drm_agp_mem *agp_entry; */ 4134250aa95Szrj /* int valid */ 4144250aa95Szrj struct drm_buf *buf; 4157f3c3d6fSHasso Tepper unsigned long offset; 4167f3c3d6fSHasso Tepper unsigned long agp_offset; 4177f3c3d6fSHasso Tepper int count; 4187f3c3d6fSHasso Tepper int order; 4197f3c3d6fSHasso Tepper int size; 4207f3c3d6fSHasso Tepper int alignment; 4217f3c3d6fSHasso Tepper int page_order; 4227f3c3d6fSHasso Tepper int total; 4237f3c3d6fSHasso Tepper int byte_count; 4247f3c3d6fSHasso Tepper int i; 4254250aa95Szrj struct drm_buf **temp_buflist; 4267f3c3d6fSHasso Tepper 4277f3c3d6fSHasso Tepper count = request->count; 4284cd92098Szrj order = order_base_2(request->size); 4297f3c3d6fSHasso Tepper size = 1 << order; 4307f3c3d6fSHasso Tepper 4317f3c3d6fSHasso Tepper alignment = (request->flags & _DRM_PAGE_ALIGN) 4327f3c3d6fSHasso Tepper ? round_page(size) : size; 4337f3c3d6fSHasso Tepper page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 4347f3c3d6fSHasso Tepper total = PAGE_SIZE << page_order; 4357f3c3d6fSHasso Tepper 4367f3c3d6fSHasso Tepper byte_count = 0; 4377f3c3d6fSHasso Tepper agp_offset = dev->agp->base + request->agp_start; 4387f3c3d6fSHasso Tepper 4397f3c3d6fSHasso Tepper DRM_DEBUG("count: %d\n", count); 4407f3c3d6fSHasso Tepper DRM_DEBUG("order: %d\n", order); 4417f3c3d6fSHasso Tepper DRM_DEBUG("size: %d\n", size); 4427f3c3d6fSHasso Tepper DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset); 4437f3c3d6fSHasso Tepper DRM_DEBUG("alignment: %d\n", alignment); 4447f3c3d6fSHasso Tepper DRM_DEBUG("page_order: %d\n", page_order); 4457f3c3d6fSHasso Tepper DRM_DEBUG("total: %d\n", total); 4467f3c3d6fSHasso Tepper 4477f3c3d6fSHasso Tepper /* Make sure buffers are located in AGP memory that we own */ 4487f3c3d6fSHasso Tepper /* Breaks MGA due to drm_alloc_agp not setting up entries for the 4497f3c3d6fSHasso Tepper * memory. Safe to ignore for now because these ioctls are still 4507f3c3d6fSHasso Tepper * root-only. 4517f3c3d6fSHasso Tepper */ 4527f3c3d6fSHasso Tepper /*valid = 0; 4537f3c3d6fSHasso Tepper for (agp_entry = dev->agp->memory; agp_entry; 4547f3c3d6fSHasso Tepper agp_entry = agp_entry->next) { 4557f3c3d6fSHasso Tepper if ((agp_offset >= agp_entry->bound) && 4567f3c3d6fSHasso Tepper (agp_offset + total * count <= 4577f3c3d6fSHasso Tepper agp_entry->bound + agp_entry->pages * PAGE_SIZE)) { 4587f3c3d6fSHasso Tepper valid = 1; 4597f3c3d6fSHasso Tepper break; 4607f3c3d6fSHasso Tepper } 4617f3c3d6fSHasso Tepper } 4627f3c3d6fSHasso Tepper if (!valid) { 4637f3c3d6fSHasso Tepper DRM_DEBUG("zone invalid\n"); 464b922632fSImre Vadász return -EINVAL; 4657f3c3d6fSHasso Tepper }*/ 4667f3c3d6fSHasso Tepper 4677f3c3d6fSHasso Tepper entry = &dma->bufs[order]; 4687f3c3d6fSHasso Tepper 4695a3b77d5SFrançois Tigeot entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM, 470f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 4717f3c3d6fSHasso Tepper if (!entry->buflist) { 472b922632fSImre Vadász return -ENOMEM; 4737f3c3d6fSHasso Tepper } 4747f3c3d6fSHasso Tepper 4757f3c3d6fSHasso Tepper entry->buf_size = size; 4767f3c3d6fSHasso Tepper entry->page_order = page_order; 4777f3c3d6fSHasso Tepper 4787f3c3d6fSHasso Tepper offset = 0; 4797f3c3d6fSHasso Tepper 4807f3c3d6fSHasso Tepper while (entry->buf_count < count) { 4817f3c3d6fSHasso Tepper buf = &entry->buflist[entry->buf_count]; 4827f3c3d6fSHasso Tepper buf->idx = dma->buf_count + entry->buf_count; 4837f3c3d6fSHasso Tepper buf->total = alignment; 4847f3c3d6fSHasso Tepper buf->order = order; 4857f3c3d6fSHasso Tepper buf->used = 0; 4867f3c3d6fSHasso Tepper 4877f3c3d6fSHasso Tepper buf->offset = (dma->byte_count + offset); 4887f3c3d6fSHasso Tepper buf->bus_address = agp_offset + offset; 4897f3c3d6fSHasso Tepper buf->address = (void *)(agp_offset + offset); 4907f3c3d6fSHasso Tepper buf->next = NULL; 4917f3c3d6fSHasso Tepper buf->pending = 0; 4927f3c3d6fSHasso Tepper buf->file_priv = NULL; 4937f3c3d6fSHasso Tepper 494ba55f2f5SFrançois Tigeot buf->dev_priv_size = dev->driver->dev_priv_size; 4955a3b77d5SFrançois Tigeot buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM, 496f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 4977f3c3d6fSHasso Tepper if (buf->dev_private == NULL) { 4987f3c3d6fSHasso Tepper /* Set count correctly so we free the proper amount. */ 4997f3c3d6fSHasso Tepper entry->buf_count = count; 5007f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 501b922632fSImre Vadász return -ENOMEM; 5027f3c3d6fSHasso Tepper } 5037f3c3d6fSHasso Tepper 5047f3c3d6fSHasso Tepper offset += alignment; 5057f3c3d6fSHasso Tepper entry->buf_count++; 5067f3c3d6fSHasso Tepper byte_count += PAGE_SIZE << page_order; 5077f3c3d6fSHasso Tepper } 5087f3c3d6fSHasso Tepper 5097f3c3d6fSHasso Tepper DRM_DEBUG("byte_count: %d\n", byte_count); 5107f3c3d6fSHasso Tepper 5115718399fSFrançois Tigeot temp_buflist = krealloc(dma->buflist, 512b3705d71SHasso Tepper (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 513f8677ba6SMatthew Dillon M_DRM, M_WAITOK | M_NULLOK); 5147f3c3d6fSHasso Tepper if (temp_buflist == NULL) { 5157f3c3d6fSHasso Tepper /* Free the entry because it isn't valid */ 5167f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 517b922632fSImre Vadász return -ENOMEM; 5187f3c3d6fSHasso Tepper } 5197f3c3d6fSHasso Tepper dma->buflist = temp_buflist; 5207f3c3d6fSHasso Tepper 5217f3c3d6fSHasso Tepper for (i = 0; i < entry->buf_count; i++) { 5227f3c3d6fSHasso Tepper dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 5237f3c3d6fSHasso Tepper } 5247f3c3d6fSHasso Tepper 5257f3c3d6fSHasso Tepper dma->buf_count += entry->buf_count; 5267f3c3d6fSHasso Tepper dma->byte_count += byte_count; 5277f3c3d6fSHasso Tepper 5287f3c3d6fSHasso Tepper DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); 5297f3c3d6fSHasso Tepper DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); 5307f3c3d6fSHasso Tepper 5317f3c3d6fSHasso Tepper request->count = entry->buf_count; 5327f3c3d6fSHasso Tepper request->size = size; 5337f3c3d6fSHasso Tepper 5347f3c3d6fSHasso Tepper dma->flags = _DRM_DMA_USE_AGP; 5357f3c3d6fSHasso Tepper 5367f3c3d6fSHasso Tepper return 0; 5377f3c3d6fSHasso Tepper } 5387f3c3d6fSHasso Tepper 539b3705d71SHasso Tepper static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request) 5407f3c3d6fSHasso Tepper { 5414250aa95Szrj struct drm_device_dma *dma = dev->dma; 5427f3c3d6fSHasso Tepper int count; 5437f3c3d6fSHasso Tepper int order; 5447f3c3d6fSHasso Tepper int size; 5457f3c3d6fSHasso Tepper int total; 5467f3c3d6fSHasso Tepper int page_order; 5474250aa95Szrj struct drm_buf_entry *entry; 548b31e9d59SFrançois Tigeot drm_dma_handle_t *dmah; 5494250aa95Szrj struct drm_buf *buf; 5507f3c3d6fSHasso Tepper int alignment; 5517f3c3d6fSHasso Tepper unsigned long offset; 5527f3c3d6fSHasso Tepper int i; 5537f3c3d6fSHasso Tepper int byte_count; 5547f3c3d6fSHasso Tepper int page_count; 5557f3c3d6fSHasso Tepper unsigned long *temp_pagelist; 5564250aa95Szrj struct drm_buf **temp_buflist; 5577f3c3d6fSHasso Tepper 5587f3c3d6fSHasso Tepper count = request->count; 5594cd92098Szrj order = order_base_2(request->size); 5607f3c3d6fSHasso Tepper size = 1 << order; 5617f3c3d6fSHasso Tepper 5627f3c3d6fSHasso Tepper DRM_DEBUG("count=%d, size=%d (%d), order=%d\n", 5637f3c3d6fSHasso Tepper request->count, request->size, size, order); 5647f3c3d6fSHasso Tepper 5657f3c3d6fSHasso Tepper alignment = (request->flags & _DRM_PAGE_ALIGN) 5667f3c3d6fSHasso Tepper ? round_page(size) : size; 5677f3c3d6fSHasso Tepper page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 5687f3c3d6fSHasso Tepper total = PAGE_SIZE << page_order; 5697f3c3d6fSHasso Tepper 5707f3c3d6fSHasso Tepper entry = &dma->bufs[order]; 5717f3c3d6fSHasso Tepper 5725a3b77d5SFrançois Tigeot entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM, 573f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 5745a3b77d5SFrançois Tigeot entry->seglist = kmalloc(count * sizeof(*entry->seglist), M_DRM, 575f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 5767f3c3d6fSHasso Tepper 5777f3c3d6fSHasso Tepper /* Keep the original pagelist until we know all the allocations 5787f3c3d6fSHasso Tepper * have succeeded 5797f3c3d6fSHasso Tepper */ 5805718399fSFrançois Tigeot temp_pagelist = kmalloc((dma->page_count + (count << page_order)) * 581f8677ba6SMatthew Dillon sizeof(*dma->pagelist), 582f8677ba6SMatthew Dillon M_DRM, M_WAITOK | M_NULLOK); 5837f3c3d6fSHasso Tepper 5847f3c3d6fSHasso Tepper if (entry->buflist == NULL || entry->seglist == NULL || 5857f3c3d6fSHasso Tepper temp_pagelist == NULL) { 586*175896dfSzrj kfree(temp_pagelist); 587*175896dfSzrj kfree(entry->seglist); 588*175896dfSzrj kfree(entry->buflist); 589b922632fSImre Vadász return -ENOMEM; 5907f3c3d6fSHasso Tepper } 5917f3c3d6fSHasso Tepper 5927f3c3d6fSHasso Tepper memcpy(temp_pagelist, dma->pagelist, dma->page_count * 5937f3c3d6fSHasso Tepper sizeof(*dma->pagelist)); 5947f3c3d6fSHasso Tepper 5957f3c3d6fSHasso Tepper DRM_DEBUG("pagelist: %d entries\n", 5967f3c3d6fSHasso Tepper dma->page_count + (count << page_order)); 5977f3c3d6fSHasso Tepper 5987f3c3d6fSHasso Tepper entry->buf_size = size; 5997f3c3d6fSHasso Tepper entry->page_order = page_order; 6007f3c3d6fSHasso Tepper byte_count = 0; 6017f3c3d6fSHasso Tepper page_count = 0; 6027f3c3d6fSHasso Tepper 6037f3c3d6fSHasso Tepper while (entry->buf_count < count) { 6045718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 605b31e9d59SFrançois Tigeot dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000); 6065718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 607b31e9d59SFrançois Tigeot 608b31e9d59SFrançois Tigeot if (!dmah) { 6097f3c3d6fSHasso Tepper /* Set count correctly so we free the proper amount. */ 6107f3c3d6fSHasso Tepper entry->buf_count = count; 6117f3c3d6fSHasso Tepper entry->seg_count = count; 6127f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 613*175896dfSzrj kfree(temp_pagelist); 614b31e9d59SFrançois Tigeot return -ENOMEM; 6157f3c3d6fSHasso Tepper } 6167f3c3d6fSHasso Tepper entry->seglist[entry->seg_count++] = dmah; 6177f3c3d6fSHasso Tepper for (i = 0; i < (1 << page_order); i++) { 618b31e9d59SFrançois Tigeot DRM_DEBUG("page %d @ 0x%08lx\n", 6197f3c3d6fSHasso Tepper dma->page_count + page_count, 620b31e9d59SFrançois Tigeot (unsigned long)dmah->vaddr + PAGE_SIZE * i); 621b31e9d59SFrançois Tigeot temp_pagelist[dma->page_count + page_count++] 622b31e9d59SFrançois Tigeot = (unsigned long)dmah->vaddr + PAGE_SIZE * i; 6237f3c3d6fSHasso Tepper } 6247f3c3d6fSHasso Tepper for (offset = 0; 6257f3c3d6fSHasso Tepper offset + size <= total && entry->buf_count < count; 6267f3c3d6fSHasso Tepper offset += alignment, ++entry->buf_count) { 6277f3c3d6fSHasso Tepper buf = &entry->buflist[entry->buf_count]; 6287f3c3d6fSHasso Tepper buf->idx = dma->buf_count + entry->buf_count; 6297f3c3d6fSHasso Tepper buf->total = alignment; 6307f3c3d6fSHasso Tepper buf->order = order; 6317f3c3d6fSHasso Tepper buf->used = 0; 6327f3c3d6fSHasso Tepper buf->offset = (dma->byte_count + byte_count + offset); 6337f3c3d6fSHasso Tepper buf->address = ((char *)dmah->vaddr + offset); 6347f3c3d6fSHasso Tepper buf->bus_address = dmah->busaddr + offset; 6357f3c3d6fSHasso Tepper buf->next = NULL; 6367f3c3d6fSHasso Tepper buf->pending = 0; 6377f3c3d6fSHasso Tepper buf->file_priv = NULL; 6387f3c3d6fSHasso Tepper 639ba55f2f5SFrançois Tigeot buf->dev_priv_size = dev->driver->dev_priv_size; 6405718399fSFrançois Tigeot buf->dev_private = kmalloc(buf->dev_priv_size, 641f8677ba6SMatthew Dillon M_DRM, 642f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | 643f8677ba6SMatthew Dillon M_ZERO); 6447f3c3d6fSHasso Tepper if (buf->dev_private == NULL) { 6457f3c3d6fSHasso Tepper /* Set count correctly so we free the proper amount. */ 6467f3c3d6fSHasso Tepper entry->buf_count = count; 6477f3c3d6fSHasso Tepper entry->seg_count = count; 6487f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 649*175896dfSzrj kfree(temp_pagelist); 650b922632fSImre Vadász return -ENOMEM; 6517f3c3d6fSHasso Tepper } 6527f3c3d6fSHasso Tepper 6537f3c3d6fSHasso Tepper DRM_DEBUG("buffer %d @ %p\n", 6547f3c3d6fSHasso Tepper entry->buf_count, buf->address); 6557f3c3d6fSHasso Tepper } 6567f3c3d6fSHasso Tepper byte_count += PAGE_SIZE << page_order; 6577f3c3d6fSHasso Tepper } 6587f3c3d6fSHasso Tepper 6595718399fSFrançois Tigeot temp_buflist = krealloc(dma->buflist, 660b3705d71SHasso Tepper (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 661f8677ba6SMatthew Dillon M_DRM, M_WAITOK | M_NULLOK); 6627f3c3d6fSHasso Tepper if (temp_buflist == NULL) { 6637f3c3d6fSHasso Tepper /* Free the entry because it isn't valid */ 6647f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 665*175896dfSzrj kfree(temp_pagelist); 666b922632fSImre Vadász return -ENOMEM; 6677f3c3d6fSHasso Tepper } 6687f3c3d6fSHasso Tepper dma->buflist = temp_buflist; 6697f3c3d6fSHasso Tepper 6707f3c3d6fSHasso Tepper for (i = 0; i < entry->buf_count; i++) { 6717f3c3d6fSHasso Tepper dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 6727f3c3d6fSHasso Tepper } 6737f3c3d6fSHasso Tepper 674*175896dfSzrj /* No allocations failed, so now we can replace the original pagelist 6757f3c3d6fSHasso Tepper * with the new one. 6767f3c3d6fSHasso Tepper */ 677*175896dfSzrj kfree(dma->pagelist); 6787f3c3d6fSHasso Tepper dma->pagelist = temp_pagelist; 6797f3c3d6fSHasso Tepper 6807f3c3d6fSHasso Tepper dma->buf_count += entry->buf_count; 6817f3c3d6fSHasso Tepper dma->seg_count += entry->seg_count; 6827f3c3d6fSHasso Tepper dma->page_count += entry->seg_count << page_order; 6837f3c3d6fSHasso Tepper dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); 6847f3c3d6fSHasso Tepper 6857f3c3d6fSHasso Tepper request->count = entry->buf_count; 6867f3c3d6fSHasso Tepper request->size = size; 6877f3c3d6fSHasso Tepper 6887f3c3d6fSHasso Tepper return 0; 6897f3c3d6fSHasso Tepper 6907f3c3d6fSHasso Tepper } 6917f3c3d6fSHasso Tepper 692b3705d71SHasso Tepper static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request) 6937f3c3d6fSHasso Tepper { 6944250aa95Szrj struct drm_device_dma *dma = dev->dma; 6954250aa95Szrj struct drm_buf_entry *entry; 6964250aa95Szrj struct drm_buf *buf; 6977f3c3d6fSHasso Tepper unsigned long offset; 6987f3c3d6fSHasso Tepper unsigned long agp_offset; 6997f3c3d6fSHasso Tepper int count; 7007f3c3d6fSHasso Tepper int order; 7017f3c3d6fSHasso Tepper int size; 7027f3c3d6fSHasso Tepper int alignment; 7037f3c3d6fSHasso Tepper int page_order; 7047f3c3d6fSHasso Tepper int total; 7057f3c3d6fSHasso Tepper int byte_count; 7067f3c3d6fSHasso Tepper int i; 7074250aa95Szrj struct drm_buf **temp_buflist; 7087f3c3d6fSHasso Tepper 7097f3c3d6fSHasso Tepper count = request->count; 7104cd92098Szrj order = order_base_2(request->size); 7117f3c3d6fSHasso Tepper size = 1 << order; 7127f3c3d6fSHasso Tepper 7137f3c3d6fSHasso Tepper alignment = (request->flags & _DRM_PAGE_ALIGN) 7147f3c3d6fSHasso Tepper ? round_page(size) : size; 7157f3c3d6fSHasso Tepper page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 7167f3c3d6fSHasso Tepper total = PAGE_SIZE << page_order; 7177f3c3d6fSHasso Tepper 7187f3c3d6fSHasso Tepper byte_count = 0; 7197f3c3d6fSHasso Tepper agp_offset = request->agp_start; 7207f3c3d6fSHasso Tepper 7217f3c3d6fSHasso Tepper DRM_DEBUG("count: %d\n", count); 7227f3c3d6fSHasso Tepper DRM_DEBUG("order: %d\n", order); 7237f3c3d6fSHasso Tepper DRM_DEBUG("size: %d\n", size); 7247f3c3d6fSHasso Tepper DRM_DEBUG("agp_offset: %ld\n", agp_offset); 7257f3c3d6fSHasso Tepper DRM_DEBUG("alignment: %d\n", alignment); 7267f3c3d6fSHasso Tepper DRM_DEBUG("page_order: %d\n", page_order); 7277f3c3d6fSHasso Tepper DRM_DEBUG("total: %d\n", total); 7287f3c3d6fSHasso Tepper 7297f3c3d6fSHasso Tepper entry = &dma->bufs[order]; 7307f3c3d6fSHasso Tepper 7315a3b77d5SFrançois Tigeot entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM, 732f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 7337f3c3d6fSHasso Tepper if (entry->buflist == NULL) 734b922632fSImre Vadász return -ENOMEM; 7357f3c3d6fSHasso Tepper 7367f3c3d6fSHasso Tepper entry->buf_size = size; 7377f3c3d6fSHasso Tepper entry->page_order = page_order; 7387f3c3d6fSHasso Tepper 7397f3c3d6fSHasso Tepper offset = 0; 7407f3c3d6fSHasso Tepper 7417f3c3d6fSHasso Tepper while (entry->buf_count < count) { 7427f3c3d6fSHasso Tepper buf = &entry->buflist[entry->buf_count]; 7437f3c3d6fSHasso Tepper buf->idx = dma->buf_count + entry->buf_count; 7447f3c3d6fSHasso Tepper buf->total = alignment; 7457f3c3d6fSHasso Tepper buf->order = order; 7467f3c3d6fSHasso Tepper buf->used = 0; 7477f3c3d6fSHasso Tepper 7487f3c3d6fSHasso Tepper buf->offset = (dma->byte_count + offset); 7497f3c3d6fSHasso Tepper buf->bus_address = agp_offset + offset; 75099f70504SFrançois Tigeot buf->address = (void *)(agp_offset + offset + dev->sg->vaddr); 7517f3c3d6fSHasso Tepper buf->next = NULL; 7527f3c3d6fSHasso Tepper buf->pending = 0; 7537f3c3d6fSHasso Tepper buf->file_priv = NULL; 7547f3c3d6fSHasso Tepper 755ba55f2f5SFrançois Tigeot buf->dev_priv_size = dev->driver->dev_priv_size; 7565a3b77d5SFrançois Tigeot buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM, 757f8677ba6SMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO); 7587f3c3d6fSHasso Tepper if (buf->dev_private == NULL) { 7597f3c3d6fSHasso Tepper /* Set count correctly so we free the proper amount. */ 7607f3c3d6fSHasso Tepper entry->buf_count = count; 7617f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 762b922632fSImre Vadász return -ENOMEM; 7637f3c3d6fSHasso Tepper } 7647f3c3d6fSHasso Tepper 7657f3c3d6fSHasso Tepper DRM_DEBUG("buffer %d @ %p\n", 7667f3c3d6fSHasso Tepper entry->buf_count, buf->address); 7677f3c3d6fSHasso Tepper 7687f3c3d6fSHasso Tepper offset += alignment; 7697f3c3d6fSHasso Tepper entry->buf_count++; 7707f3c3d6fSHasso Tepper byte_count += PAGE_SIZE << page_order; 7717f3c3d6fSHasso Tepper } 7727f3c3d6fSHasso Tepper 7737f3c3d6fSHasso Tepper DRM_DEBUG("byte_count: %d\n", byte_count); 7747f3c3d6fSHasso Tepper 7755718399fSFrançois Tigeot temp_buflist = krealloc(dma->buflist, 776b3705d71SHasso Tepper (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 777f8677ba6SMatthew Dillon M_DRM, M_WAITOK | M_NULLOK); 7787f3c3d6fSHasso Tepper if (temp_buflist == NULL) { 7797f3c3d6fSHasso Tepper /* Free the entry because it isn't valid */ 7807f3c3d6fSHasso Tepper drm_cleanup_buf_error(dev, entry); 781b922632fSImre Vadász return -ENOMEM; 7827f3c3d6fSHasso Tepper } 7837f3c3d6fSHasso Tepper dma->buflist = temp_buflist; 7847f3c3d6fSHasso Tepper 7857f3c3d6fSHasso Tepper for (i = 0; i < entry->buf_count; i++) { 7867f3c3d6fSHasso Tepper dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 7877f3c3d6fSHasso Tepper } 7887f3c3d6fSHasso Tepper 7897f3c3d6fSHasso Tepper dma->buf_count += entry->buf_count; 7907f3c3d6fSHasso Tepper dma->byte_count += byte_count; 7917f3c3d6fSHasso Tepper 7927f3c3d6fSHasso Tepper DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); 7937f3c3d6fSHasso Tepper DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); 7947f3c3d6fSHasso Tepper 7957f3c3d6fSHasso Tepper request->count = entry->buf_count; 7967f3c3d6fSHasso Tepper request->size = size; 7977f3c3d6fSHasso Tepper 7987f3c3d6fSHasso Tepper dma->flags = _DRM_DMA_USE_SG; 7997f3c3d6fSHasso Tepper 8007f3c3d6fSHasso Tepper return 0; 8017f3c3d6fSHasso Tepper } 8027f3c3d6fSHasso Tepper 803df4baf3dSFrançois Tigeot /** 804df4baf3dSFrançois Tigeot * Add AGP buffers for DMA transfers. 805df4baf3dSFrançois Tigeot * 806df4baf3dSFrançois Tigeot * \param dev struct drm_device to which the buffers are to be added. 807df4baf3dSFrançois Tigeot * \param request pointer to a struct drm_buf_desc describing the request. 808df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 809df4baf3dSFrançois Tigeot * 810df4baf3dSFrançois Tigeot * After some sanity checks creates a drm_buf structure for each buffer and 811df4baf3dSFrançois Tigeot * reallocates the buffer list of the same size order to accommodate the new 812df4baf3dSFrançois Tigeot * buffers. 813df4baf3dSFrançois Tigeot */ 8141b13d190SFrançois Tigeot int drm_legacy_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) 8157f3c3d6fSHasso Tepper { 8167f3c3d6fSHasso Tepper int order, ret; 8177f3c3d6fSHasso Tepper 8187f3c3d6fSHasso Tepper if (request->count < 0 || request->count > 4096) 819b922632fSImre Vadász return -EINVAL; 8207f3c3d6fSHasso Tepper 8214cd92098Szrj order = order_base_2(request->size); 8227f3c3d6fSHasso Tepper if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 823b922632fSImre Vadász return -EINVAL; 8247f3c3d6fSHasso Tepper 8255718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 826b3705d71SHasso Tepper 8277f3c3d6fSHasso Tepper /* No more allocations after first buffer-using ioctl. */ 8287f3c3d6fSHasso Tepper if (dev->buf_use != 0) { 8295718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 830b922632fSImre Vadász return -EBUSY; 8317f3c3d6fSHasso Tepper } 8327f3c3d6fSHasso Tepper /* No more than one allocation per order */ 8337f3c3d6fSHasso Tepper if (dev->dma->bufs[order].buf_count != 0) { 8345718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 835b922632fSImre Vadász return -ENOMEM; 8367f3c3d6fSHasso Tepper } 8377f3c3d6fSHasso Tepper 8387f3c3d6fSHasso Tepper ret = drm_do_addbufs_agp(dev, request); 8397f3c3d6fSHasso Tepper 8405718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 8417f3c3d6fSHasso Tepper 8427f3c3d6fSHasso Tepper return ret; 8437f3c3d6fSHasso Tepper } 8447f3c3d6fSHasso Tepper 8451b13d190SFrançois Tigeot static int drm_legacy_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request) 8467f3c3d6fSHasso Tepper { 8477f3c3d6fSHasso Tepper int order, ret; 8487f3c3d6fSHasso Tepper 849c6f73aabSFrançois Tigeot if (!capable(CAP_SYS_ADMIN)) 850b922632fSImre Vadász return -EACCES; 8517f3c3d6fSHasso Tepper 8527f3c3d6fSHasso Tepper if (request->count < 0 || request->count > 4096) 853b922632fSImre Vadász return -EINVAL; 8547f3c3d6fSHasso Tepper 8554cd92098Szrj order = order_base_2(request->size); 8567f3c3d6fSHasso Tepper if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 857b922632fSImre Vadász return -EINVAL; 8587f3c3d6fSHasso Tepper 8595718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 860b3705d71SHasso Tepper 8617f3c3d6fSHasso Tepper /* No more allocations after first buffer-using ioctl. */ 8627f3c3d6fSHasso Tepper if (dev->buf_use != 0) { 8635718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 864b922632fSImre Vadász return -EBUSY; 8657f3c3d6fSHasso Tepper } 8667f3c3d6fSHasso Tepper /* No more than one allocation per order */ 8677f3c3d6fSHasso Tepper if (dev->dma->bufs[order].buf_count != 0) { 8685718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 869b922632fSImre Vadász return -ENOMEM; 8707f3c3d6fSHasso Tepper } 8717f3c3d6fSHasso Tepper 8727f3c3d6fSHasso Tepper ret = drm_do_addbufs_sg(dev, request); 8737f3c3d6fSHasso Tepper 8745718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 8757f3c3d6fSHasso Tepper 8767f3c3d6fSHasso Tepper return ret; 8777f3c3d6fSHasso Tepper } 8787f3c3d6fSHasso Tepper 8791b13d190SFrançois Tigeot int drm_legacy_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) 8807f3c3d6fSHasso Tepper { 8817f3c3d6fSHasso Tepper int order, ret; 8827f3c3d6fSHasso Tepper 883c6f73aabSFrançois Tigeot if (!capable(CAP_SYS_ADMIN)) 884b922632fSImre Vadász return -EACCES; 8857f3c3d6fSHasso Tepper 8867f3c3d6fSHasso Tepper if (request->count < 0 || request->count > 4096) 887b922632fSImre Vadász return -EINVAL; 8887f3c3d6fSHasso Tepper 8894cd92098Szrj order = order_base_2(request->size); 8907f3c3d6fSHasso Tepper if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 891b922632fSImre Vadász return -EINVAL; 8927f3c3d6fSHasso Tepper 8935718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 894b3705d71SHasso Tepper 8957f3c3d6fSHasso Tepper /* No more allocations after first buffer-using ioctl. */ 8967f3c3d6fSHasso Tepper if (dev->buf_use != 0) { 8975718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 898b922632fSImre Vadász return -EBUSY; 8997f3c3d6fSHasso Tepper } 9007f3c3d6fSHasso Tepper /* No more than one allocation per order */ 9017f3c3d6fSHasso Tepper if (dev->dma->bufs[order].buf_count != 0) { 9025718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 903b922632fSImre Vadász return -ENOMEM; 9047f3c3d6fSHasso Tepper } 9057f3c3d6fSHasso Tepper 9067f3c3d6fSHasso Tepper ret = drm_do_addbufs_pci(dev, request); 9077f3c3d6fSHasso Tepper 9085718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 9097f3c3d6fSHasso Tepper 9107f3c3d6fSHasso Tepper return ret; 9117f3c3d6fSHasso Tepper } 9127f3c3d6fSHasso Tepper 913df4baf3dSFrançois Tigeot /** 914df4baf3dSFrançois Tigeot * Add buffers for DMA transfers (ioctl). 915df4baf3dSFrançois Tigeot * 916df4baf3dSFrançois Tigeot * \param inode device inode. 917df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 918df4baf3dSFrançois Tigeot * \param cmd command. 919df4baf3dSFrançois Tigeot * \param arg pointer to a struct drm_buf_desc request. 920df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 921df4baf3dSFrançois Tigeot * 922df4baf3dSFrançois Tigeot * According with the memory type specified in drm_buf_desc::flags and the 923df4baf3dSFrançois Tigeot * build options, it dispatches the call either to addbufs_agp(), 924df4baf3dSFrançois Tigeot * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent 925df4baf3dSFrançois Tigeot * PCI memory respectively. 926df4baf3dSFrançois Tigeot */ 9271b13d190SFrançois Tigeot int drm_legacy_addbufs(struct drm_device *dev, void *data, 928df4baf3dSFrançois Tigeot struct drm_file *file_priv) 9297f3c3d6fSHasso Tepper { 930b3705d71SHasso Tepper struct drm_buf_desc *request = data; 9317f3c3d6fSHasso Tepper int err; 9327f3c3d6fSHasso Tepper 9337f3c3d6fSHasso Tepper if (request->flags & _DRM_AGP_BUFFER) 9341b13d190SFrançois Tigeot err = drm_legacy_addbufs_agp(dev, request); 9357f3c3d6fSHasso Tepper else if (request->flags & _DRM_SG_BUFFER) 9361b13d190SFrançois Tigeot err = drm_legacy_addbufs_sg(dev, request); 9377f3c3d6fSHasso Tepper else 9381b13d190SFrançois Tigeot err = drm_legacy_addbufs_pci(dev, request); 9397f3c3d6fSHasso Tepper 9407f3c3d6fSHasso Tepper return err; 9417f3c3d6fSHasso Tepper } 9427f3c3d6fSHasso Tepper 943df4baf3dSFrançois Tigeot /** 944df4baf3dSFrançois Tigeot * Get information about the buffer mappings. 945df4baf3dSFrançois Tigeot * 946df4baf3dSFrançois Tigeot * This was originally mean for debugging purposes, or by a sophisticated 947df4baf3dSFrançois Tigeot * client library to determine how best to use the available buffers (e.g., 948df4baf3dSFrançois Tigeot * large buffers can be used for image transfer). 949df4baf3dSFrançois Tigeot * 950df4baf3dSFrançois Tigeot * \param inode device inode. 951df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 952df4baf3dSFrançois Tigeot * \param cmd command. 953df4baf3dSFrançois Tigeot * \param arg pointer to a drm_buf_info structure. 954df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 955df4baf3dSFrançois Tigeot * 95624edb884SFrançois Tigeot * Increments drm_device::buf_use while holding the drm_device::buf_lock 957df4baf3dSFrançois Tigeot * lock, preventing of allocating more buffers after this call. Information 958df4baf3dSFrançois Tigeot * about each requested buffer is then copied into user space. 959df4baf3dSFrançois Tigeot */ 9601b13d190SFrançois Tigeot int drm_legacy_infobufs(struct drm_device *dev, void *data, 961df4baf3dSFrançois Tigeot struct drm_file *file_priv) 9627f3c3d6fSHasso Tepper { 96324edb884SFrançois Tigeot struct drm_device_dma *dma = dev->dma; 964b3705d71SHasso Tepper struct drm_buf_info *request = data; 9657f3c3d6fSHasso Tepper int i; 9667f3c3d6fSHasso Tepper int count; 9677f3c3d6fSHasso Tepper 96824edb884SFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_MODESET)) 96924edb884SFrançois Tigeot return -EINVAL; 97024edb884SFrançois Tigeot 97124edb884SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 97224edb884SFrançois Tigeot return -EINVAL; 97324edb884SFrançois Tigeot 97424edb884SFrançois Tigeot if (!dma) 97524edb884SFrançois Tigeot return -EINVAL; 97624edb884SFrançois Tigeot 97724edb884SFrançois Tigeot spin_lock(&dev->buf_lock); 97824edb884SFrançois Tigeot if (atomic_read(&dev->buf_alloc)) { 97924edb884SFrançois Tigeot spin_unlock(&dev->buf_lock); 98024edb884SFrançois Tigeot return -EBUSY; 98124edb884SFrançois Tigeot } 9827f3c3d6fSHasso Tepper ++dev->buf_use; /* Can't allocate more after this call */ 98324edb884SFrançois Tigeot spin_unlock(&dev->buf_lock); 9847f3c3d6fSHasso Tepper 9857f3c3d6fSHasso Tepper for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { 986b3705d71SHasso Tepper if (dma->bufs[i].buf_count) 987b3705d71SHasso Tepper ++count; 9887f3c3d6fSHasso Tepper } 9897f3c3d6fSHasso Tepper 9907f3c3d6fSHasso Tepper DRM_DEBUG("count = %d\n", count); 9917f3c3d6fSHasso Tepper 9927f3c3d6fSHasso Tepper if (request->count >= count) { 9937f3c3d6fSHasso Tepper for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { 9947f3c3d6fSHasso Tepper if (dma->bufs[i].buf_count) { 99524edb884SFrançois Tigeot struct drm_buf_desc __user *to = 99624edb884SFrançois Tigeot &request->list[count]; 99724edb884SFrançois Tigeot struct drm_buf_entry *from = &dma->bufs[i]; 99824edb884SFrançois Tigeot if (copy_to_user(&to->count, 99924edb884SFrançois Tigeot &from->buf_count, 100024edb884SFrançois Tigeot sizeof(from->buf_count)) || 100124edb884SFrançois Tigeot copy_to_user(&to->size, 100224edb884SFrançois Tigeot &from->buf_size, 100324edb884SFrançois Tigeot sizeof(from->buf_size)) || 100424edb884SFrançois Tigeot copy_to_user(&to->low_mark, 100524edb884SFrançois Tigeot &from->low_mark, 100624edb884SFrançois Tigeot sizeof(from->low_mark)) || 100724edb884SFrançois Tigeot copy_to_user(&to->high_mark, 100824edb884SFrançois Tigeot &from->high_mark, 100924edb884SFrançois Tigeot sizeof(from->high_mark))) 101024edb884SFrançois Tigeot return -EFAULT; 10117f3c3d6fSHasso Tepper 10127f3c3d6fSHasso Tepper DRM_DEBUG("%d %d %d %d %d\n", 101324edb884SFrançois Tigeot i, 101424edb884SFrançois Tigeot dma->bufs[i].buf_count, 10157f3c3d6fSHasso Tepper dma->bufs[i].buf_size, 101624edb884SFrançois Tigeot dma->bufs[i].low_mark, 101724edb884SFrançois Tigeot dma->bufs[i].high_mark); 10187f3c3d6fSHasso Tepper ++count; 10197f3c3d6fSHasso Tepper } 10207f3c3d6fSHasso Tepper } 10217f3c3d6fSHasso Tepper } 10227f3c3d6fSHasso Tepper request->count = count; 10237f3c3d6fSHasso Tepper 102424edb884SFrançois Tigeot return 0; 10257f3c3d6fSHasso Tepper } 10267f3c3d6fSHasso Tepper 1027df4baf3dSFrançois Tigeot /** 1028df4baf3dSFrançois Tigeot * Specifies a low and high water mark for buffer allocation 1029df4baf3dSFrançois Tigeot * 1030df4baf3dSFrançois Tigeot * \param inode device inode. 1031df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 1032df4baf3dSFrançois Tigeot * \param cmd command. 1033df4baf3dSFrançois Tigeot * \param arg a pointer to a drm_buf_desc structure. 1034df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 1035df4baf3dSFrançois Tigeot * 1036df4baf3dSFrançois Tigeot * Verifies that the size order is bounded between the admissible orders and 1037df4baf3dSFrançois Tigeot * updates the respective drm_device_dma::bufs entry low and high water mark. 1038df4baf3dSFrançois Tigeot * 1039df4baf3dSFrançois Tigeot * \note This ioctl is deprecated and mostly never used. 1040df4baf3dSFrançois Tigeot */ 10411b13d190SFrançois Tigeot int drm_legacy_markbufs(struct drm_device *dev, void *data, 1042df4baf3dSFrançois Tigeot struct drm_file *file_priv) 10437f3c3d6fSHasso Tepper { 104424edb884SFrançois Tigeot struct drm_device_dma *dma = dev->dma; 1045b3705d71SHasso Tepper struct drm_buf_desc *request = data; 10467f3c3d6fSHasso Tepper int order; 104724edb884SFrançois Tigeot struct drm_buf_entry *entry; 104824edb884SFrançois Tigeot 104924edb884SFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_MODESET)) 105024edb884SFrançois Tigeot return -EINVAL; 105124edb884SFrançois Tigeot 105224edb884SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 105324edb884SFrançois Tigeot return -EINVAL; 105424edb884SFrançois Tigeot 105524edb884SFrançois Tigeot if (!dma) 105624edb884SFrançois Tigeot return -EINVAL; 10577f3c3d6fSHasso Tepper 10587f3c3d6fSHasso Tepper DRM_DEBUG("%d, %d, %d\n", 10597f3c3d6fSHasso Tepper request->size, request->low_mark, request->high_mark); 10604cd92098Szrj order = order_base_2(request->size); 106124edb884SFrançois Tigeot if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 106224edb884SFrançois Tigeot return -EINVAL; 106324edb884SFrançois Tigeot entry = &dma->bufs[order]; 10647f3c3d6fSHasso Tepper 106524edb884SFrançois Tigeot if (request->low_mark < 0 || request->low_mark > entry->buf_count) 106624edb884SFrançois Tigeot return -EINVAL; 106724edb884SFrançois Tigeot if (request->high_mark < 0 || request->high_mark > entry->buf_count) 106824edb884SFrançois Tigeot return -EINVAL; 10697f3c3d6fSHasso Tepper 107024edb884SFrançois Tigeot entry->low_mark = request->low_mark; 107124edb884SFrançois Tigeot entry->high_mark = request->high_mark; 10727f3c3d6fSHasso Tepper 10737f3c3d6fSHasso Tepper return 0; 10747f3c3d6fSHasso Tepper } 10757f3c3d6fSHasso Tepper 1076df4baf3dSFrançois Tigeot /** 1077df4baf3dSFrançois Tigeot * Unreserve the buffers in list, previously reserved using drmDMA. 1078df4baf3dSFrançois Tigeot * 1079df4baf3dSFrançois Tigeot * \param inode device inode. 1080df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 1081df4baf3dSFrançois Tigeot * \param cmd command. 1082df4baf3dSFrançois Tigeot * \param arg pointer to a drm_buf_free structure. 1083df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 1084df4baf3dSFrançois Tigeot * 1085df4baf3dSFrançois Tigeot * Calls free_buffer() for each used buffer. 1086df4baf3dSFrançois Tigeot * This function is primarily used for debugging. 1087df4baf3dSFrançois Tigeot */ 10881b13d190SFrançois Tigeot int drm_legacy_freebufs(struct drm_device *dev, void *data, 1089df4baf3dSFrançois Tigeot struct drm_file *file_priv) 10907f3c3d6fSHasso Tepper { 10914250aa95Szrj struct drm_device_dma *dma = dev->dma; 1092b3705d71SHasso Tepper struct drm_buf_free *request = data; 10937f3c3d6fSHasso Tepper int i; 10947f3c3d6fSHasso Tepper int idx; 10954250aa95Szrj struct drm_buf *buf; 10967f3c3d6fSHasso Tepper int retcode = 0; 10977f3c3d6fSHasso Tepper 10987f3c3d6fSHasso Tepper DRM_DEBUG("%d\n", request->count); 10997f3c3d6fSHasso Tepper 11005718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 11017f3c3d6fSHasso Tepper for (i = 0; i < request->count; i++) { 1102c6f73aabSFrançois Tigeot if (copy_from_user(&idx, &request->list[i], sizeof(idx))) { 1103b922632fSImre Vadász retcode = -EFAULT; 11047f3c3d6fSHasso Tepper break; 11057f3c3d6fSHasso Tepper } 11067f3c3d6fSHasso Tepper if (idx < 0 || idx >= dma->buf_count) { 11077f3c3d6fSHasso Tepper DRM_ERROR("Index %d (of %d max)\n", 11087f3c3d6fSHasso Tepper idx, dma->buf_count - 1); 1109b922632fSImre Vadász retcode = -EINVAL; 11107f3c3d6fSHasso Tepper break; 11117f3c3d6fSHasso Tepper } 11127f3c3d6fSHasso Tepper buf = dma->buflist[idx]; 11137f3c3d6fSHasso Tepper if (buf->file_priv != file_priv) { 11147f3c3d6fSHasso Tepper DRM_ERROR("Process %d freeing buffer not owned\n", 11157f3c3d6fSHasso Tepper DRM_CURRENTPID); 1116b922632fSImre Vadász retcode = -EINVAL; 11177f3c3d6fSHasso Tepper break; 11187f3c3d6fSHasso Tepper } 11191b13d190SFrançois Tigeot drm_legacy_free_buffer(dev, buf); 11207f3c3d6fSHasso Tepper } 11215718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 11227f3c3d6fSHasso Tepper 11237f3c3d6fSHasso Tepper return retcode; 11247f3c3d6fSHasso Tepper } 11257f3c3d6fSHasso Tepper 1126df4baf3dSFrançois Tigeot /** 1127df4baf3dSFrançois Tigeot * Maps all of the DMA buffers into client-virtual space (ioctl). 1128df4baf3dSFrançois Tigeot * 1129df4baf3dSFrançois Tigeot * \param inode device inode. 1130df4baf3dSFrançois Tigeot * \param file_priv DRM file private. 1131df4baf3dSFrançois Tigeot * \param cmd command. 1132df4baf3dSFrançois Tigeot * \param arg pointer to a drm_buf_map structure. 1133df4baf3dSFrançois Tigeot * \return zero on success or a negative number on failure. 1134df4baf3dSFrançois Tigeot * 1135df4baf3dSFrançois Tigeot * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information 1136df4baf3dSFrançois Tigeot * about each buffer into user space. For PCI buffers, it calls vm_mmap() with 1137df4baf3dSFrançois Tigeot * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls 1138df4baf3dSFrançois Tigeot * drm_mmap_dma(). 1139df4baf3dSFrançois Tigeot */ 11401b13d190SFrançois Tigeot int drm_legacy_mapbufs(struct drm_device *dev, void *data, 1141df4baf3dSFrançois Tigeot struct drm_file *file_priv) 11427f3c3d6fSHasso Tepper { 11434250aa95Szrj struct drm_device_dma *dma = dev->dma; 11447f3c3d6fSHasso Tepper int retcode = 0; 11457f3c3d6fSHasso Tepper const int zero = 0; 11467f3c3d6fSHasso Tepper vm_offset_t address; 11477f3c3d6fSHasso Tepper struct vmspace *vms; 11487f3c3d6fSHasso Tepper vm_ooffset_t foff; 11497f3c3d6fSHasso Tepper vm_size_t size; 11507f3c3d6fSHasso Tepper vm_offset_t vaddr; 1151b3705d71SHasso Tepper struct drm_buf_map *request = data; 11527f3c3d6fSHasso Tepper int i; 11537f3c3d6fSHasso Tepper 11547f3c3d6fSHasso Tepper vms = DRM_CURPROC->td_proc->p_vmspace; 11557f3c3d6fSHasso Tepper 11565718399fSFrançois Tigeot spin_lock(&dev->dma_lock); 11577f3c3d6fSHasso Tepper dev->buf_use++; /* Can't allocate more after this call */ 11585718399fSFrançois Tigeot spin_unlock(&dev->dma_lock); 11597f3c3d6fSHasso Tepper 11607f3c3d6fSHasso Tepper if (request->count < dma->buf_count) 11617f3c3d6fSHasso Tepper goto done; 11627f3c3d6fSHasso Tepper 116353e4e524Szrj if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) || 1164b3705d71SHasso Tepper (drm_core_check_feature(dev, DRIVER_SG) && 1165b3705d71SHasso Tepper (dma->flags & _DRM_DMA_USE_SG))) { 11667f3c3d6fSHasso Tepper drm_local_map_t *map = dev->agp_buffer_map; 11677f3c3d6fSHasso Tepper 11687f3c3d6fSHasso Tepper if (map == NULL) { 1169b922632fSImre Vadász retcode = -EINVAL; 11707f3c3d6fSHasso Tepper goto done; 11717f3c3d6fSHasso Tepper } 11727f3c3d6fSHasso Tepper size = round_page(map->size); 117399f70504SFrançois Tigeot foff = (unsigned long)map->handle; 11747f3c3d6fSHasso Tepper } else { 11757f3c3d6fSHasso Tepper size = round_page(dma->byte_count), 11767f3c3d6fSHasso Tepper foff = 0; 11777f3c3d6fSHasso Tepper } 11787f3c3d6fSHasso Tepper 11797f3c3d6fSHasso Tepper vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ); 1180b922632fSImre Vadász retcode = -vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE, 1181b3705d71SHasso Tepper VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, 1182b3705d71SHasso Tepper SLIST_FIRST(&dev->devnode->si_hlist), foff); 11837f3c3d6fSHasso Tepper if (retcode) 11847f3c3d6fSHasso Tepper goto done; 11857f3c3d6fSHasso Tepper 11867f3c3d6fSHasso Tepper request->virtual = (void *)vaddr; 11877f3c3d6fSHasso Tepper 11887f3c3d6fSHasso Tepper for (i = 0; i < dma->buf_count; i++) { 1189c6f73aabSFrançois Tigeot if (copy_to_user(&request->list[i].idx, 11907f3c3d6fSHasso Tepper &dma->buflist[i]->idx, sizeof(request->list[0].idx))) { 1191b922632fSImre Vadász retcode = -EFAULT; 11927f3c3d6fSHasso Tepper goto done; 11937f3c3d6fSHasso Tepper } 1194c6f73aabSFrançois Tigeot if (copy_to_user(&request->list[i].total, 11957f3c3d6fSHasso Tepper &dma->buflist[i]->total, sizeof(request->list[0].total))) { 1196b922632fSImre Vadász retcode = -EFAULT; 11977f3c3d6fSHasso Tepper goto done; 11987f3c3d6fSHasso Tepper } 1199c6f73aabSFrançois Tigeot if (copy_to_user(&request->list[i].used, &zero, 12007f3c3d6fSHasso Tepper sizeof(zero))) { 1201b922632fSImre Vadász retcode = -EFAULT; 12027f3c3d6fSHasso Tepper goto done; 12037f3c3d6fSHasso Tepper } 12047f3c3d6fSHasso Tepper address = vaddr + dma->buflist[i]->offset; /* *** */ 1205c6f73aabSFrançois Tigeot if (copy_to_user(&request->list[i].address, &address, 12067f3c3d6fSHasso Tepper sizeof(address))) { 1207b922632fSImre Vadász retcode = -EFAULT; 12087f3c3d6fSHasso Tepper goto done; 12097f3c3d6fSHasso Tepper } 12107f3c3d6fSHasso Tepper } 12117f3c3d6fSHasso Tepper done: 12127f3c3d6fSHasso Tepper request->count = dma->buf_count; 12137f3c3d6fSHasso Tepper DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode); 12147f3c3d6fSHasso Tepper 12157f3c3d6fSHasso Tepper return retcode; 12167f3c3d6fSHasso Tepper } 12171b13d190SFrançois Tigeot 12181b13d190SFrançois Tigeot int drm_legacy_dma_ioctl(struct drm_device *dev, void *data, 12191b13d190SFrançois Tigeot struct drm_file *file_priv) 12201b13d190SFrançois Tigeot { 12211b13d190SFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_MODESET)) 12221b13d190SFrançois Tigeot return -EINVAL; 12231b13d190SFrançois Tigeot 12241b13d190SFrançois Tigeot if (dev->driver->dma_ioctl) 12251b13d190SFrançois Tigeot return dev->driver->dma_ioctl(dev, data, file_priv); 12261b13d190SFrançois Tigeot else 12271b13d190SFrançois Tigeot return -EINVAL; 12281b13d190SFrançois Tigeot } 12291b13d190SFrançois Tigeot 12301b13d190SFrançois Tigeot struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev) 12311b13d190SFrançois Tigeot { 12321b13d190SFrançois Tigeot struct drm_map_list *entry; 12331b13d190SFrançois Tigeot 12341b13d190SFrançois Tigeot list_for_each_entry(entry, &dev->maplist, head) { 12351b13d190SFrançois Tigeot if (entry->map && entry->map->type == _DRM_SHM && 12361b13d190SFrançois Tigeot (entry->map->flags & _DRM_CONTAINS_LOCK)) { 12371b13d190SFrançois Tigeot return entry->map; 12381b13d190SFrançois Tigeot } 12391b13d190SFrançois Tigeot } 12401b13d190SFrançois Tigeot return NULL; 12411b13d190SFrançois Tigeot } 12421b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_getsarea); 1243