]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/drm/radeon_mem.c
This commit was generated by cvs2svn to compensate for changes in r138583,
[FreeBSD/FreeBSD.git] / sys / dev / drm / radeon_mem.c
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
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include "dev/drm/radeon.h"
37 #include "dev/drm/drmP.h"
38 #include "dev/drm/drm.h"
39 #include "dev/drm/radeon_drm.h"
40 #include "dev/drm/radeon_drv.h"
41
42 /* Very simple allocator for GART memory, working on a static range
43  * already mapped into each client's address space.  
44  */
45
46 static struct mem_block *split_block(struct mem_block *p, int start, int size,
47                                      DRMFILE filp )
48 {
49         /* Maybe cut off the start of an existing block */
50         if (start > p->start) {
51                 struct mem_block *newblock = DRM_MALLOC(sizeof(*newblock));
52                 if (!newblock) 
53                         goto out;
54                 newblock->start = start;
55                 newblock->size = p->size - (start - p->start);
56                 newblock->filp = 0;
57                 newblock->next = p->next;
58                 newblock->prev = p;
59                 p->next->prev = newblock;
60                 p->next = newblock;
61                 p->size -= newblock->size;
62                 p = newblock;
63         }
64    
65         /* Maybe cut off the end of an existing block */
66         if (size < p->size) {
67                 struct mem_block *newblock = DRM_MALLOC(sizeof(*newblock));
68                 if (!newblock)
69                         goto out;
70                 newblock->start = start + size;
71                 newblock->size = p->size - size;
72                 newblock->filp = 0;
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->filp = filp;
83         return p;
84 }
85
86 static struct mem_block *alloc_block( struct mem_block *heap, int size, 
87                                       int align2, DRMFILE filp )
88 {
89         struct mem_block *p;
90         int mask = (1 << align2)-1;
91
92         for (p = heap->next ; p != heap ; p = p->next) {
93                 int start = (p->start + mask) & ~mask;
94                 if (p->filp == 0 && start + size <= p->start + p->size)
95                         return split_block( p, start, size, filp );
96         }
97
98         return NULL;
99 }
100
101 static struct mem_block *find_block( struct mem_block *heap, int start )
102 {
103         struct mem_block *p;
104
105         for (p = heap->next ; p != heap ; p = p->next) 
106                 if (p->start == start)
107                         return p;
108
109         return NULL;
110 }
111
112
113 static void free_block( struct mem_block *p )
114 {
115         p->filp = 0;
116
117         /* Assumes a single contiguous range.  Needs a special filp in
118          * 'heap' to stop it being subsumed.
119          */
120         if (p->next->filp == 0) {
121                 struct mem_block *q = p->next;
122                 p->size += q->size;
123                 p->next = q->next;
124                 p->next->prev = p;
125                 DRM_FREE(q, sizeof(*q));
126         }
127
128         if (p->prev->filp == 0) {
129                 struct mem_block *q = p->prev;
130                 q->size += p->size;
131                 q->next = p->next;
132                 q->next->prev = q;
133                 DRM_FREE(p, sizeof(*q));
134         }
135 }
136
137 /* Initialize.  How to check for an uninitialized heap?
138  */
139 static int init_heap(struct mem_block **heap, int start, int size)
140 {
141         struct mem_block *blocks = DRM_MALLOC(sizeof(*blocks));
142
143         if (!blocks) 
144                 return DRM_ERR(ENOMEM);
145         
146         *heap = DRM_MALLOC(sizeof(**heap));
147         if (!*heap) {
148                 DRM_FREE( blocks, sizeof(*blocks) );
149                 return DRM_ERR(ENOMEM);
150         }
151
152         blocks->start = start;
153         blocks->size = size;
154         blocks->filp = 0;
155         blocks->next = blocks->prev = *heap;
156
157         memset( *heap, 0, sizeof(**heap) );
158         (*heap)->filp = (DRMFILE) -1;
159         (*heap)->next = (*heap)->prev = blocks;
160         return 0;
161 }
162
163
164 /* Free all blocks associated with the releasing file.
165  */
166 void radeon_mem_release( DRMFILE filp, struct mem_block *heap )
167 {
168         struct mem_block *p;
169
170         if (!heap || !heap->next)
171                 return;
172
173         for (p = heap->next ; p != heap ; p = p->next) {
174                 if (p->filp == filp) 
175                         p->filp = 0;
176         }
177
178         /* Assumes a single contiguous range.  Needs a special filp in
179          * 'heap' to stop it being subsumed.
180          */
181         for (p = heap->next ; p != heap ; p = p->next) {
182                 while (p->filp == 0 && p->next->filp == 0) {
183                         struct mem_block *q = p->next;
184                         p->size += q->size;
185                         p->next = q->next;
186                         p->next->prev = p;
187                         DRM_FREE(q, sizeof(*q));
188                 }
189         }
190 }
191
192 /* Shutdown.
193  */
194 void radeon_mem_takedown( struct mem_block **heap )
195 {
196         struct mem_block *p;
197         
198         if (!*heap)
199                 return;
200
201         for (p = (*heap)->next ; p != *heap ; ) {
202                 struct mem_block *q = p;
203                 p = p->next;
204                 DRM_FREE(q, sizeof(*q));
205         }
206
207         DRM_FREE( *heap, sizeof(**heap) );
208         *heap = 0;
209 }
210
211
212
213 /* IOCTL HANDLERS */
214
215 static struct mem_block **get_heap( drm_radeon_private_t *dev_priv,
216                                    int region )
217 {
218         switch( region ) {
219         case RADEON_MEM_REGION_GART:
220                 return &dev_priv->gart_heap; 
221         case RADEON_MEM_REGION_FB:
222                 return &dev_priv->fb_heap;
223         default:
224                 return 0;
225         }
226 }
227
228 int radeon_mem_alloc( DRM_IOCTL_ARGS )
229 {
230         DRM_DEVICE;
231         drm_radeon_private_t *dev_priv = dev->dev_private;
232         drm_radeon_mem_alloc_t alloc;
233         struct mem_block *block, **heap;
234
235         if ( !dev_priv ) {
236                 DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
237                 return DRM_ERR(EINVAL);
238         }
239
240         DRM_COPY_FROM_USER_IOCTL( alloc, (drm_radeon_mem_alloc_t *)data,
241                                   sizeof(alloc) );
242
243         heap = get_heap( dev_priv, alloc.region );
244         if (!heap || !*heap)
245                 return DRM_ERR(EFAULT);
246         
247         /* Make things easier on ourselves: all allocations at least
248          * 4k aligned.
249          */
250         if (alloc.alignment < 12)
251                 alloc.alignment = 12;
252
253         block = alloc_block( *heap, alloc.size, alloc.alignment,
254                              filp );
255
256         if (!block) 
257                 return DRM_ERR(ENOMEM);
258
259         if ( DRM_COPY_TO_USER( alloc.region_offset, &block->start, 
260                                sizeof(int) ) ) {
261                 DRM_ERROR( "copy_to_user\n" );
262                 return DRM_ERR(EFAULT);
263         }
264         
265         return 0;
266 }
267
268
269
270 int radeon_mem_free( DRM_IOCTL_ARGS )
271 {
272         DRM_DEVICE;
273         drm_radeon_private_t *dev_priv = dev->dev_private;
274         drm_radeon_mem_free_t memfree;
275         struct mem_block *block, **heap;
276
277         if ( !dev_priv ) {
278                 DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
279                 return DRM_ERR(EINVAL);
280         }
281
282         DRM_COPY_FROM_USER_IOCTL( memfree, (drm_radeon_mem_free_t *)data,
283                                   sizeof(memfree) );
284
285         heap = get_heap( dev_priv, memfree.region );
286         if (!heap || !*heap)
287                 return DRM_ERR(EFAULT);
288         
289         block = find_block( *heap, memfree.region_offset );
290         if (!block)
291                 return DRM_ERR(EFAULT);
292
293         if (block->filp != filp)
294                 return DRM_ERR(EPERM);
295
296         free_block( block );    
297         return 0;
298 }
299
300 int radeon_mem_init_heap( DRM_IOCTL_ARGS )
301 {
302         DRM_DEVICE;
303         drm_radeon_private_t *dev_priv = dev->dev_private;
304         drm_radeon_mem_init_heap_t initheap;
305         struct mem_block **heap;
306
307         if ( !dev_priv ) {
308                 DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
309                 return DRM_ERR(EINVAL);
310         }
311
312         DRM_COPY_FROM_USER_IOCTL( initheap, (drm_radeon_mem_init_heap_t *)data,
313                                   sizeof(initheap) );
314
315         heap = get_heap( dev_priv, initheap.region );
316         if (!heap) 
317                 return DRM_ERR(EFAULT);
318         
319         if (*heap) {
320                 DRM_ERROR("heap already initialized?");
321                 return DRM_ERR(EFAULT);
322         }
323                 
324         return init_heap( heap, initheap.start, initheap.size );
325 }
326
327