diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/drm/radeon_mem.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/drm/radeon_mem.c')
-rw-r--r-- | drivers/char/drm/radeon_mem.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/drivers/char/drm/radeon_mem.c b/drivers/char/drm/radeon_mem.c new file mode 100644 index 000000000000..134f894e6e4b --- /dev/null +++ b/drivers/char/drm/radeon_mem.c | |||
@@ -0,0 +1,322 @@ | |||
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 "drmP.h" | ||
33 | #include "drm.h" | ||
34 | #include "radeon_drm.h" | ||
35 | #include "radeon_drv.h" | ||
36 | |||
37 | /* Very simple allocator for GART memory, working on a static range | ||
38 | * already mapped into each client's address space. | ||
39 | */ | ||
40 | |||
41 | static struct mem_block *split_block(struct mem_block *p, int start, int size, | ||
42 | DRMFILE filp ) | ||
43 | { | ||
44 | /* Maybe cut off the start of an existing block */ | ||
45 | if (start > p->start) { | ||
46 | struct mem_block *newblock = drm_alloc(sizeof(*newblock), DRM_MEM_BUFS ); | ||
47 | if (!newblock) | ||
48 | goto out; | ||
49 | newblock->start = start; | ||
50 | newblock->size = p->size - (start - p->start); | ||
51 | newblock->filp = NULL; | ||
52 | newblock->next = p->next; | ||
53 | newblock->prev = p; | ||
54 | p->next->prev = newblock; | ||
55 | p->next = newblock; | ||
56 | p->size -= newblock->size; | ||
57 | p = newblock; | ||
58 | } | ||
59 | |||
60 | /* Maybe cut off the end of an existing block */ | ||
61 | if (size < p->size) { | ||
62 | struct mem_block *newblock = drm_alloc(sizeof(*newblock), DRM_MEM_BUFS ); | ||
63 | if (!newblock) | ||
64 | goto out; | ||
65 | newblock->start = start + size; | ||
66 | newblock->size = p->size - size; | ||
67 | newblock->filp = NULL; | ||
68 | newblock->next = p->next; | ||
69 | newblock->prev = p; | ||
70 | p->next->prev = newblock; | ||
71 | p->next = newblock; | ||
72 | p->size = size; | ||
73 | } | ||
74 | |||
75 | out: | ||
76 | /* Our block is in the middle */ | ||
77 | p->filp = filp; | ||
78 | return p; | ||
79 | } | ||
80 | |||
81 | static struct mem_block *alloc_block( struct mem_block *heap, int size, | ||
82 | int align2, DRMFILE filp ) | ||
83 | { | ||
84 | struct mem_block *p; | ||
85 | int mask = (1 << align2)-1; | ||
86 | |||
87 | list_for_each(p, heap) { | ||
88 | int start = (p->start + mask) & ~mask; | ||
89 | if (p->filp == 0 && start + size <= p->start + p->size) | ||
90 | return split_block( p, start, size, filp ); | ||
91 | } | ||
92 | |||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | static struct mem_block *find_block( struct mem_block *heap, int start ) | ||
97 | { | ||
98 | struct mem_block *p; | ||
99 | |||
100 | list_for_each(p, heap) | ||
101 | if (p->start == start) | ||
102 | return p; | ||
103 | |||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | |||
108 | static void free_block( struct mem_block *p ) | ||
109 | { | ||
110 | p->filp = NULL; | ||
111 | |||
112 | /* Assumes a single contiguous range. Needs a special filp in | ||
113 | * 'heap' to stop it being subsumed. | ||
114 | */ | ||
115 | if (p->next->filp == 0) { | ||
116 | struct mem_block *q = p->next; | ||
117 | p->size += q->size; | ||
118 | p->next = q->next; | ||
119 | p->next->prev = p; | ||
120 | drm_free(q, sizeof(*q), DRM_MEM_BUFS ); | ||
121 | } | ||
122 | |||
123 | if (p->prev->filp == 0) { | ||
124 | struct mem_block *q = p->prev; | ||
125 | q->size += p->size; | ||
126 | q->next = p->next; | ||
127 | q->next->prev = q; | ||
128 | drm_free(p, sizeof(*q), DRM_MEM_BUFS ); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /* Initialize. How to check for an uninitialized heap? | ||
133 | */ | ||
134 | static int init_heap(struct mem_block **heap, int start, int size) | ||
135 | { | ||
136 | struct mem_block *blocks = drm_alloc(sizeof(*blocks), DRM_MEM_BUFS ); | ||
137 | |||
138 | if (!blocks) | ||
139 | return DRM_ERR(ENOMEM); | ||
140 | |||
141 | *heap = drm_alloc(sizeof(**heap), DRM_MEM_BUFS ); | ||
142 | if (!*heap) { | ||
143 | drm_free( blocks, sizeof(*blocks), DRM_MEM_BUFS ); | ||
144 | return DRM_ERR(ENOMEM); | ||
145 | } | ||
146 | |||
147 | blocks->start = start; | ||
148 | blocks->size = size; | ||
149 | blocks->filp = NULL; | ||
150 | blocks->next = blocks->prev = *heap; | ||
151 | |||
152 | memset( *heap, 0, sizeof(**heap) ); | ||
153 | (*heap)->filp = (DRMFILE) -1; | ||
154 | (*heap)->next = (*heap)->prev = blocks; | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | |||
159 | /* Free all blocks associated with the releasing file. | ||
160 | */ | ||
161 | void radeon_mem_release( DRMFILE filp, struct mem_block *heap ) | ||
162 | { | ||
163 | struct mem_block *p; | ||
164 | |||
165 | if (!heap || !heap->next) | ||
166 | return; | ||
167 | |||
168 | list_for_each(p, heap) { | ||
169 | if (p->filp == filp) | ||
170 | p->filp = NULL; | ||
171 | } | ||
172 | |||
173 | /* Assumes a single contiguous range. Needs a special filp in | ||
174 | * 'heap' to stop it being subsumed. | ||
175 | */ | ||
176 | list_for_each(p, heap) { | ||
177 | while (p->filp == 0 && p->next->filp == 0) { | ||
178 | struct mem_block *q = p->next; | ||
179 | p->size += q->size; | ||
180 | p->next = q->next; | ||
181 | p->next->prev = p; | ||
182 | drm_free(q, sizeof(*q),DRM_MEM_DRIVER); | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* Shutdown. | ||
188 | */ | ||
189 | void radeon_mem_takedown( struct mem_block **heap ) | ||
190 | { | ||
191 | struct mem_block *p; | ||
192 | |||
193 | if (!*heap) | ||
194 | return; | ||
195 | |||
196 | for (p = (*heap)->next ; p != *heap ; ) { | ||
197 | struct mem_block *q = p; | ||
198 | p = p->next; | ||
199 | drm_free(q, sizeof(*q),DRM_MEM_DRIVER); | ||
200 | } | ||
201 | |||
202 | drm_free( *heap, sizeof(**heap),DRM_MEM_DRIVER ); | ||
203 | *heap = NULL; | ||
204 | } | ||
205 | |||
206 | |||
207 | |||
208 | /* IOCTL HANDLERS */ | ||
209 | |||
210 | static struct mem_block **get_heap( drm_radeon_private_t *dev_priv, | ||
211 | 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 | |||
223 | int radeon_mem_alloc( DRM_IOCTL_ARGS ) | ||
224 | { | ||
225 | DRM_DEVICE; | ||
226 | drm_radeon_private_t *dev_priv = dev->dev_private; | ||
227 | drm_radeon_mem_alloc_t alloc; | ||
228 | struct mem_block *block, **heap; | ||
229 | |||
230 | if ( !dev_priv ) { | ||
231 | DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); | ||
232 | return DRM_ERR(EINVAL); | ||
233 | } | ||
234 | |||
235 | DRM_COPY_FROM_USER_IOCTL( alloc, (drm_radeon_mem_alloc_t __user *)data, | ||
236 | sizeof(alloc) ); | ||
237 | |||
238 | heap = get_heap( dev_priv, alloc.region ); | ||
239 | if (!heap || !*heap) | ||
240 | return DRM_ERR(EFAULT); | ||
241 | |||
242 | /* Make things easier on ourselves: all allocations at least | ||
243 | * 4k aligned. | ||
244 | */ | ||
245 | if (alloc.alignment < 12) | ||
246 | alloc.alignment = 12; | ||
247 | |||
248 | block = alloc_block( *heap, alloc.size, alloc.alignment, | ||
249 | filp ); | ||
250 | |||
251 | if (!block) | ||
252 | return DRM_ERR(ENOMEM); | ||
253 | |||
254 | if ( DRM_COPY_TO_USER( alloc.region_offset, &block->start, | ||
255 | sizeof(int) ) ) { | ||
256 | DRM_ERROR( "copy_to_user\n" ); | ||
257 | return DRM_ERR(EFAULT); | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | |||
264 | |||
265 | int radeon_mem_free( DRM_IOCTL_ARGS ) | ||
266 | { | ||
267 | DRM_DEVICE; | ||
268 | drm_radeon_private_t *dev_priv = dev->dev_private; | ||
269 | drm_radeon_mem_free_t memfree; | ||
270 | struct mem_block *block, **heap; | ||
271 | |||
272 | if ( !dev_priv ) { | ||
273 | DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); | ||
274 | return DRM_ERR(EINVAL); | ||
275 | } | ||
276 | |||
277 | DRM_COPY_FROM_USER_IOCTL( memfree, (drm_radeon_mem_free_t __user *)data, | ||
278 | sizeof(memfree) ); | ||
279 | |||
280 | heap = get_heap( dev_priv, memfree.region ); | ||
281 | if (!heap || !*heap) | ||
282 | return DRM_ERR(EFAULT); | ||
283 | |||
284 | block = find_block( *heap, memfree.region_offset ); | ||
285 | if (!block) | ||
286 | return DRM_ERR(EFAULT); | ||
287 | |||
288 | if (block->filp != filp) | ||
289 | return DRM_ERR(EPERM); | ||
290 | |||
291 | free_block( block ); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | int radeon_mem_init_heap( DRM_IOCTL_ARGS ) | ||
296 | { | ||
297 | DRM_DEVICE; | ||
298 | drm_radeon_private_t *dev_priv = dev->dev_private; | ||
299 | drm_radeon_mem_init_heap_t initheap; | ||
300 | struct mem_block **heap; | ||
301 | |||
302 | if ( !dev_priv ) { | ||
303 | DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); | ||
304 | return DRM_ERR(EINVAL); | ||
305 | } | ||
306 | |||
307 | DRM_COPY_FROM_USER_IOCTL( initheap, (drm_radeon_mem_init_heap_t __user *)data, | ||
308 | sizeof(initheap) ); | ||
309 | |||
310 | heap = get_heap( dev_priv, initheap.region ); | ||
311 | if (!heap) | ||
312 | return DRM_ERR(EFAULT); | ||
313 | |||
314 | if (*heap) { | ||
315 | DRM_ERROR("heap already initialized?"); | ||
316 | return DRM_ERR(EFAULT); | ||
317 | } | ||
318 | |||
319 | return init_heap( heap, initheap.start, initheap.size ); | ||
320 | } | ||
321 | |||
322 | |||