1/* radeon_mem.c -- Simple GART/fb memory manager for radeon -*- linux-c -*- */ 2/*- 3 * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. 4 * 5 * The Weather Channel (TM) funded Tungsten Graphics to develop the 6 * initial release of the Radeon 8500 driver under the XFree86 license. 7 * This notice must be preserved. 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the next 17 * paragraph) shall be included in all copies or substantial portions of the 18 * Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 * DEALINGS IN THE SOFTWARE. 27 * 28 * Authors: 29 * Keith Whitwell <keith@tungstengraphics.com> 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include "dev/drm/drmP.h" 36#include "dev/drm/drm.h" 37#include "dev/drm/radeon_drm.h" 38#include "dev/drm/radeon_drv.h" 39 40/* Very simple allocator for GART memory, working on a static range 41 * already mapped into each client's address space. 42 */ 43 44static struct mem_block *split_block(struct mem_block *p, int start, int size, 45 struct drm_file *file_priv) 46{ 47 /* Maybe cut off the start of an existing block */ 48 if (start > p->start) { 49 struct mem_block *newblock = 50 drm_alloc(sizeof(*newblock), DRM_MEM_BUFS); 51 if (!newblock) 52 goto out; 53 newblock->start = start; 54 newblock->size = p->size - (start - p->start); 55 newblock->file_priv = NULL; 56 newblock->next = p->next; 57 newblock->prev = p; 58 p->next->prev = newblock; 59 p->next = newblock; 60 p->size -= newblock->size; 61 p = newblock; 62 } 63 64 /* Maybe cut off the end of an existing block */ 65 if (size < p->size) { 66 struct mem_block *newblock = 67 drm_alloc(sizeof(*newblock), DRM_MEM_BUFS); 68 if (!newblock) 69 goto out; 70 newblock->start = start + size; 71 newblock->size = p->size - size; 72 newblock->file_priv = NULL; 73 newblock->next = p->next; 74 newblock->prev = p; 75 p->next->prev = newblock; 76 p->next = newblock; 77 p->size = size; 78 } 79 80 out: 81 /* Our block is in the middle */ 82 p->file_priv = file_priv; 83 return p; 84} 85 86static struct mem_block *alloc_block(struct mem_block *heap, int size, 87 int align2, struct drm_file *file_priv) 88{ 89 struct mem_block *p; 90 int mask = (1 << align2) - 1; 91 92 list_for_each(p, heap) { 93 int start = (p->start + mask) & ~mask; 94 if (p->file_priv == NULL && start + size <= p->start + p->size) 95 return split_block(p, start, size, file_priv); 96 } 97 98 return NULL; 99} 100 101static struct mem_block *find_block(struct mem_block *heap, int start) 102{ 103 struct mem_block *p; 104 105 list_for_each(p, heap) 106 if (p->start == start) 107 return p; 108 109 return NULL; 110} 111 112static void free_block(struct mem_block *p) 113{ 114 p->file_priv = NULL; 115 116 /* Assumes a single contiguous range. Needs a special file_priv in 117 * 'heap' to stop it being subsumed. 118 */ 119 if (p->next->file_priv == NULL) { 120 struct mem_block *q = p->next; 121 p->size += q->size; 122 p->next = q->next; 123 p->next->prev = p; 124 drm_free(q, sizeof(*q), DRM_MEM_BUFS); 125 } 126 127 if (p->prev->file_priv == NULL) { 128 struct mem_block *q = p->prev; 129 q->size += p->size; 130 q->next = p->next; 131 q->next->prev = q; 132 drm_free(p, sizeof(*q), DRM_MEM_BUFS); 133 } 134} 135 136/* Initialize. How to check for an uninitialized heap? 137 */ 138static int init_heap(struct mem_block **heap, int start, int size) 139{ 140 struct mem_block *blocks = drm_alloc(sizeof(*blocks), DRM_MEM_BUFS); 141 142 if (!blocks) 143 return -ENOMEM; 144 145 *heap = drm_alloc(sizeof(**heap), DRM_MEM_BUFS); 146 if (!*heap) { 147 drm_free(blocks, sizeof(*blocks), DRM_MEM_BUFS); 148 return -ENOMEM; 149 } 150 151 blocks->start = start; 152 blocks->size = size; 153 blocks->file_priv = NULL; 154 blocks->next = blocks->prev = *heap; 155 156 memset(*heap, 0, sizeof(**heap)); 157 (*heap)->file_priv = (struct drm_file *) - 1; 158 (*heap)->next = (*heap)->prev = blocks; 159 return 0; 160} 161 162/* Free all blocks associated with the releasing file. 163 */ 164void radeon_mem_release(struct drm_file *file_priv, struct mem_block *heap) 165{ 166 struct mem_block *p; 167 168 if (!heap || !heap->next) 169 return; 170 171 list_for_each(p, heap) { 172 if (p->file_priv == file_priv) 173 p->file_priv = NULL; 174 } 175 176 /* Assumes a single contiguous range. Needs a special file_priv in 177 * 'heap' to stop it being subsumed. 178 */ 179 list_for_each(p, heap) { 180 while (p->file_priv == NULL && p->next->file_priv == NULL) { 181 struct mem_block *q = p->next; 182 p->size += q->size; 183 p->next = q->next; 184 p->next->prev = p; 185 drm_free(q, sizeof(*q), DRM_MEM_DRIVER); 186 } 187 } 188} 189 190/* Shutdown. 191 */ 192void radeon_mem_takedown(struct mem_block **heap) 193{ 194 struct mem_block *p; 195 196 if (!*heap) 197 return; 198 199 for (p = (*heap)->next; p != *heap;) { 200 struct mem_block *q = p; 201 p = p->next; 202 drm_free(q, sizeof(*q), DRM_MEM_DRIVER); 203 } 204 205 drm_free(*heap, sizeof(**heap), DRM_MEM_DRIVER); 206 *heap = NULL; 207} 208 209/* IOCTL HANDLERS */ 210 211static struct mem_block **get_heap(drm_radeon_private_t * dev_priv, int region) 212{ 213 switch (region) { 214 case RADEON_MEM_REGION_GART: 215 return &dev_priv->gart_heap; 216 case RADEON_MEM_REGION_FB: 217 return &dev_priv->fb_heap; 218 default: 219 return NULL; 220 } 221} 222 223int radeon_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv) 224{ 225 drm_radeon_private_t *dev_priv = dev->dev_private; 226 drm_radeon_mem_alloc_t *alloc = data; 227 struct mem_block *block, **heap; 228 229 if (!dev_priv) { 230 DRM_ERROR("called with no initialization\n"); 231 return -EINVAL; 232 } 233 234 heap = get_heap(dev_priv, alloc->region); 235 if (!heap || !*heap) 236 return -EFAULT; 237 238 /* Make things easier on ourselves: all allocations at least 239 * 4k aligned. 240 */ 241 if (alloc->alignment < 12) 242 alloc->alignment = 12; 243 244 block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv); 245 246 if (!block) 247 return -ENOMEM; 248 249 if (DRM_COPY_TO_USER(alloc->region_offset, &block->start, 250 sizeof(int))) { 251 DRM_ERROR("copy_to_user\n"); 252 return -EFAULT; 253 } 254 255 return 0; 256} 257 258int radeon_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 259{ 260 drm_radeon_private_t *dev_priv = dev->dev_private; 261 drm_radeon_mem_free_t *memfree = data; 262 struct mem_block *block, **heap; 263 264 if (!dev_priv) { 265 DRM_ERROR("called with no initialization\n"); 266 return -EINVAL; 267 } 268 269 heap = get_heap(dev_priv, memfree->region); 270 if (!heap || !*heap) 271 return -EFAULT; 272 273 block = find_block(*heap, memfree->region_offset); 274 if (!block) 275 return -EFAULT; 276 277 if (block->file_priv != file_priv) 278 return -EPERM; 279 280 free_block(block); 281 return 0; 282} 283 284int radeon_mem_init_heap(struct drm_device *dev, void *data, struct drm_file *file_priv) 285{ 286 drm_radeon_private_t *dev_priv = dev->dev_private; 287 drm_radeon_mem_init_heap_t *initheap = data; 288 struct mem_block **heap; 289 290 if (!dev_priv) { 291 DRM_ERROR("called with no initialization\n"); 292 return -EINVAL; 293 } 294 295 heap = get_heap(dev_priv, initheap->region); 296 if (!heap) 297 return -EFAULT; 298 299 if (*heap) { 300 DRM_ERROR("heap already initialized?"); 301 return -EFAULT; 302 } 303 304 return init_heap(heap, initheap->start, initheap->size); 305} 306