1 /*- 2 * Copyright (c) 2009 Robert C. Noland III <rnoland@FreeBSD.org> 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <sys/cdefs.h> 26 /** @file drm_scatter.c 27 * Allocation of memory for scatter-gather mappings by the graphics chip. 28 * The memory allocated here is then made into an aperture in the card 29 * by mapping the pages into the GART. 30 */ 31 32 #include <dev/drm2/drmP.h> 33 34 #define DEBUG_SCATTER 0 35 36 static inline void *drm_vmalloc_dma(vm_size_t size) 37 { 38 return kmem_alloc_attr(size, M_NOWAIT | M_ZERO, 0, 39 BUS_SPACE_MAXADDR_32BIT, VM_MEMATTR_WRITE_COMBINING); 40 } 41 42 void drm_sg_cleanup(struct drm_sg_mem * entry) 43 { 44 if (entry == NULL) 45 return; 46 47 if (entry->vaddr != NULL) 48 kmem_free(entry->vaddr, IDX_TO_OFF(entry->pages)); 49 50 free(entry->busaddr, DRM_MEM_SGLISTS); 51 free(entry, DRM_MEM_DRIVER); 52 } 53 54 int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request) 55 { 56 struct drm_sg_mem *entry; 57 vm_size_t size; 58 vm_pindex_t pindex; 59 60 DRM_DEBUG("\n"); 61 62 if (!drm_core_check_feature(dev, DRIVER_SG)) 63 return -EINVAL; 64 65 if (dev->sg) 66 return -EINVAL; 67 68 entry = malloc(sizeof(*entry), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO); 69 if (!entry) 70 return -ENOMEM; 71 72 DRM_DEBUG("request size=%ld\n", request->size); 73 74 size = round_page(request->size); 75 entry->pages = atop(size); 76 entry->busaddr = malloc(entry->pages * sizeof(*entry->busaddr), 77 DRM_MEM_SGLISTS, M_NOWAIT | M_ZERO); 78 if (!entry->busaddr) { 79 free(entry, DRM_MEM_DRIVER); 80 return -ENOMEM; 81 } 82 83 entry->vaddr = drm_vmalloc_dma(size); 84 if (entry->vaddr == NULL) { 85 free(entry->busaddr, DRM_MEM_DRIVER); 86 free(entry, DRM_MEM_DRIVER); 87 return -ENOMEM; 88 } 89 90 for (pindex = 0; pindex < entry->pages; pindex++) { 91 entry->busaddr[pindex] = 92 vtophys((uintptr_t)entry->vaddr + IDX_TO_OFF(pindex)); 93 } 94 95 request->handle = (uintptr_t)entry->vaddr; 96 97 dev->sg = entry; 98 99 DRM_DEBUG("allocated %ju pages @ %p, contents=%08lx\n", 100 entry->pages, entry->vaddr, *(unsigned long *)entry->vaddr); 101 102 return 0; 103 } 104 105 int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, 106 struct drm_file *file_priv) 107 { 108 struct drm_scatter_gather *request = data; 109 110 return drm_sg_alloc(dev, request); 111 112 } 113 114 int drm_sg_free(struct drm_device *dev, void *data, 115 struct drm_file *file_priv) 116 { 117 struct drm_scatter_gather *request = data; 118 struct drm_sg_mem *entry; 119 120 if (!drm_core_check_feature(dev, DRIVER_SG)) 121 return -EINVAL; 122 123 entry = dev->sg; 124 dev->sg = NULL; 125 126 if (!entry || (uintptr_t)entry->vaddr != request->handle) 127 return -EINVAL; 128 129 DRM_DEBUG("free %p\n", entry->vaddr); 130 131 drm_sg_cleanup(entry); 132 133 return 0; 134 } 135