aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/i915_mem.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2008-05-28 20:09:59 -0400
committerDave Airlie <airlied@redhat.com>2008-07-13 20:45:01 -0400
commitc0e09200dc0813972442e550a5905a132768e56c (patch)
treed38e635a30ff8b0a2b98b9d7f97cab1501f8209e /drivers/gpu/drm/i915/i915_mem.c
parentbce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff)
drm: reorganise drm tree to be more future proof.
With the coming of kernel based modesetting and the memory manager stuff, the everything in one directory approach was getting very ugly and starting to be unmanageable. This restructures the drm along the lines of other kernel components. It creates a drivers/gpu/drm directory and moves the hw drivers into subdirectores. It moves the includes into an include/drm, and sets up the unifdef for the userspace headers we should be exporting. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_mem.c')
-rw-r--r--drivers/gpu/drm/i915/i915_mem.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
new file mode 100644
index 00000000000..6126a60dc9c
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_mem.c
@@ -0,0 +1,386 @@
1/* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*-
2 */
3/*
4 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 */
28
29#include "drmP.h"
30#include "drm.h"
31#include "i915_drm.h"
32#include "i915_drv.h"
33
34/* This memory manager is integrated into the global/local lru
35 * mechanisms used by the clients. Specifically, it operates by
36 * setting the 'in_use' fields of the global LRU to indicate whether
37 * this region is privately allocated to a client.
38 *
39 * This does require the client to actually respect that field.
40 *
41 * Currently no effort is made to allocate 'private' memory in any
42 * clever way - the LRU information isn't used to determine which
43 * block to allocate, and the ring is drained prior to allocations --
44 * in other words allocation is expensive.
45 */
46static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)
47{
48 drm_i915_private_t *dev_priv = dev->dev_private;
49 drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
50 struct drm_tex_region *list;
51 unsigned shift, nr;
52 unsigned start;
53 unsigned end;
54 unsigned i;
55 int age;
56
57 shift = dev_priv->tex_lru_log_granularity;
58 nr = I915_NR_TEX_REGIONS;
59
60 start = p->start >> shift;
61 end = (p->start + p->size - 1) >> shift;
62
63 age = ++sarea_priv->texAge;
64 list = sarea_priv->texList;
65
66 /* Mark the regions with the new flag and update their age. Move
67 * them to head of list to preserve LRU semantics.
68 */
69 for (i = start; i <= end; i++) {
70 list[i].in_use = in_use;
71 list[i].age = age;
72
73 /* remove_from_list(i)
74 */
75 list[(unsigned)list[i].next].prev = list[i].prev;
76 list[(unsigned)list[i].prev].next = list[i].next;
77
78 /* insert_at_head(list, i)
79 */
80 list[i].prev = nr;
81 list[i].next = list[nr].next;
82 list[(unsigned)list[nr].next].prev = i;
83 list[nr].next = i;
84 }
85}
86
87/* Very simple allocator for agp memory, working on a static range
88 * already mapped into each client's address space.
89 */
90
91static struct mem_block *split_block(struct mem_block *p, int start, int size,
92 struct drm_file *file_priv)
93{
94 /* Maybe cut off the start of an existing block */
95 if (start > p->start) {
96 struct mem_block *newblock =
97 drm_alloc(sizeof(*newblock), DRM_MEM_BUFLISTS);
98 if (!newblock)
99 goto out;
100 newblock->start = start;
101 newblock->size = p->size - (start - p->start);
102 newblock->file_priv = NULL;
103 newblock->next = p->next;
104 newblock->prev = p;
105 p->next->prev = newblock;
106 p->next = newblock;
107 p->size -= newblock->size;
108 p = newblock;
109 }
110
111 /* Maybe cut off the end of an existing block */
112 if (size < p->size) {
113 struct mem_block *newblock =
114 drm_alloc(sizeof(*newblock), DRM_MEM_BUFLISTS);
115 if (!newblock)
116 goto out;
117 newblock->start = start + size;
118 newblock->size = p->size - size;
119 newblock->file_priv = NULL;
120 newblock->next = p->next;
121 newblock->prev = p;
122 p->next->prev = newblock;
123 p->next = newblock;
124 p->size = size;
125 }
126
127 out:
128 /* Our block is in the middle */
129 p->file_priv = file_priv;
130 return p;
131}
132
133static struct mem_block *alloc_block(struct mem_block *heap, int size,
134 int align2, struct drm_file *file_priv)
135{
136 struct mem_block *p;
137 int mask = (1 << align2) - 1;
138
139 for (p = heap->next; p != heap; p = p->next) {
140 int start = (p->start + mask) & ~mask;
141 if (p->file_priv == NULL && start + size <= p->start + p->size)
142 return split_block(p, start, size, file_priv);
143 }
144
145 return NULL;
146}
147
148static struct mem_block *find_block(struct mem_block *heap, int start)
149{
150 struct mem_block *p;
151
152 for (p = heap->next; p != heap; p = p->next)
153 if (p->start == start)
154 return p;
155
156 return NULL;
157}
158
159static void free_block(struct mem_block *p)
160{
161 p->file_priv = NULL;
162
163 /* Assumes a single contiguous range. Needs a special file_priv in
164 * 'heap' to stop it being subsumed.
165 */
166 if (p->next->file_priv == NULL) {
167 struct mem_block *q = p->next;
168 p->size += q->size;
169 p->next = q->next;
170 p->next->prev = p;
171 drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
172 }
173
174 if (p->prev->file_priv == NULL) {
175 struct mem_block *q = p->prev;
176 q->size += p->size;
177 q->next = p->next;
178 q->next->prev = q;
179 drm_free(p, sizeof(*q), DRM_MEM_BUFLISTS);
180 }
181}
182
183/* Initialize. How to check for an uninitialized heap?
184 */
185static int init_heap(struct mem_block **heap, int start, int size)
186{
187 struct mem_block *blocks = drm_alloc(sizeof(*blocks), DRM_MEM_BUFLISTS);
188
189 if (!blocks)
190 return -ENOMEM;
191
192 *heap = drm_alloc(sizeof(**heap), DRM_MEM_BUFLISTS);
193 if (!*heap) {
194 drm_free(blocks, sizeof(*blocks), DRM_MEM_BUFLISTS);
195 return -ENOMEM;
196 }
197
198 blocks->start = start;
199 blocks->size = size;
200 blocks->file_priv = NULL;
201 blocks->next = blocks->prev = *heap;
202
203 memset(*heap, 0, sizeof(**heap));
204 (*heap)->file_priv = (struct drm_file *) - 1;
205 (*heap)->next = (*heap)->prev = blocks;
206 return 0;
207}
208
209/* Free all blocks associated with the releasing file.
210 */
211void i915_mem_release(struct drm_device * dev, struct drm_file *file_priv,
212 struct mem_block *heap)
213{
214 struct mem_block *p;
215
216 if (!heap || !heap->next)
217 return;
218
219 for (p = heap->next; p != heap; p = p->next) {
220 if (p->file_priv == file_priv) {
221 p->file_priv = NULL;
222 mark_block(dev, p, 0);
223 }
224 }
225
226 /* Assumes a single contiguous range. Needs a special file_priv in
227 * 'heap' to stop it being subsumed.
228 */
229 for (p = heap->next; p != heap; p = p->next) {
230 while (p->file_priv == NULL && p->next->file_priv == NULL) {
231 struct mem_block *q = p->next;
232 p->size += q->size;
233 p->next = q->next;
234 p->next->prev = p;
235 drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
236 }
237 }
238}
239
240/* Shutdown.
241 */
242void i915_mem_takedown(struct mem_block **heap)
243{
244 struct mem_block *p;
245
246 if (!*heap)
247 return;
248
249 for (p = (*heap)->next; p != *heap;) {
250 struct mem_block *q = p;
251 p = p->next;
252 drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
253 }
254
255 drm_free(*heap, sizeof(**heap), DRM_MEM_BUFLISTS);
256 *heap = NULL;
257}
258
259static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region)
260{
261 switch (region) {
262 case I915_MEM_REGION_AGP:
263 return &dev_priv->agp_heap;
264 default:
265 return NULL;
266 }
267}
268
269/* IOCTL HANDLERS */
270
271int i915_mem_alloc(struct drm_device *dev, void *data,
272 struct drm_file *file_priv)
273{
274 drm_i915_private_t *dev_priv = dev->dev_private;
275 drm_i915_mem_alloc_t *alloc = data;
276 struct mem_block *block, **heap;
277
278 if (!dev_priv) {
279 DRM_ERROR("called with no initialization\n");
280 return -EINVAL;
281 }
282
283 heap = get_heap(dev_priv, alloc->region);
284 if (!heap || !*heap)
285 return -EFAULT;
286
287 /* Make things easier on ourselves: all allocations at least
288 * 4k aligned.
289 */
290 if (alloc->alignment < 12)
291 alloc->alignment = 12;
292
293 block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv);
294
295 if (!block)
296 return -ENOMEM;
297
298 mark_block(dev, block, 1);
299
300 if (DRM_COPY_TO_USER(alloc->region_offset, &block->start,
301 sizeof(int))) {
302 DRM_ERROR("copy_to_user\n");
303 return -EFAULT;
304 }
305
306 return 0;
307}
308
309int i915_mem_free(struct drm_device *dev, void *data,
310 struct drm_file *file_priv)
311{
312 drm_i915_private_t *dev_priv = dev->dev_private;
313 drm_i915_mem_free_t *memfree = data;
314 struct mem_block *block, **heap;
315
316 if (!dev_priv) {
317 DRM_ERROR("called with no initialization\n");
318 return -EINVAL;
319 }
320
321 heap = get_heap(dev_priv, memfree->region);
322 if (!heap || !*heap)
323 return -EFAULT;
324
325 block = find_block(*heap, memfree->region_offset);
326 if (!block)
327 return -EFAULT;
328
329 if (block->file_priv != file_priv)
330 return -EPERM;
331
332 mark_block(dev, block, 0);
333 free_block(block);
334 return 0;
335}
336
337int i915_mem_init_heap(struct drm_device *dev, void *data,
338 struct drm_file *file_priv)
339{
340 drm_i915_private_t *dev_priv = dev->dev_private;
341 drm_i915_mem_init_heap_t *initheap = data;
342 struct mem_block **heap;
343
344 if (!dev_priv) {
345 DRM_ERROR("called with no initialization\n");
346 return -EINVAL;
347 }
348
349 heap = get_heap(dev_priv, initheap->region);
350 if (!heap)
351 return -EFAULT;
352
353 if (*heap) {
354 DRM_ERROR("heap already initialized?");
355 return -EFAULT;
356 }
357
358 return init_heap(heap, initheap->start, initheap->size);
359}
360
361int i915_mem_destroy_heap( struct drm_device *dev, void *data,
362 struct drm_file *file_priv )
363{
364 drm_i915_private_t *dev_priv = dev->dev_private;
365 drm_i915_mem_destroy_heap_t *destroyheap = data;
366 struct mem_block **heap;
367
368 if ( !dev_priv ) {
369 DRM_ERROR( "called with no initialization\n" );
370 return -EINVAL;
371 }
372
373 heap = get_heap( dev_priv, destroyheap->region );
374 if (!heap) {
375 DRM_ERROR("get_heap failed");
376 return -EFAULT;
377 }
378
379 if (!*heap) {
380 DRM_ERROR("heap not initialized?");
381 return -EFAULT;
382 }
383
384 i915_mem_takedown( heap );
385 return 0;
386}