Refactor xgi_(fb|pcie)_free_all into xgi_free_all.
[platform/upstream/libdrm.git] / linux-core / xgi_fb.c
1 /****************************************************************************
2  * Copyright (C) 2003-2006 by XGI Technology, Taiwan.
3  *
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation on the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial
16  * portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
21  * XGI AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  ***************************************************************************/
26
27 #include "xgi_drv.h"
28
29 #define XGI_FB_HEAP_START 0x1000000
30
31 struct kmem_cache *xgi_mem_block_cache = NULL;
32
33 static struct xgi_mem_block *xgi_mem_new_node(void);
34
35
36 int xgi_mem_heap_init(struct xgi_mem_heap *heap, unsigned int start,
37                       unsigned int end)
38 {
39         struct xgi_mem_block *block;
40
41         INIT_LIST_HEAD(&heap->free_list);
42         INIT_LIST_HEAD(&heap->used_list);
43         INIT_LIST_HEAD(&heap->sort_list);
44         heap->initialized = TRUE;
45
46         block = kmem_cache_alloc(xgi_mem_block_cache, GFP_KERNEL);
47         if (!block) {
48                 return -ENOMEM;
49         }
50
51         block->offset = start;
52         block->size = end - start;
53
54         list_add(&block->list, &heap->free_list);
55
56         heap->max_freesize = end - start;
57
58         return 0;
59 }
60
61
62 void xgi_mem_heap_cleanup(struct xgi_mem_heap * heap)
63 {
64         struct list_head *free_list;
65         struct xgi_mem_block *block;
66         struct xgi_mem_block *next;
67         int i;
68
69         free_list = &heap->free_list;
70         for (i = 0; i < 3; i++, free_list++) {
71                 list_for_each_entry_safe(block, next, free_list, list) {
72                         DRM_INFO
73                                 ("No. %d block->offset: 0x%lx block->size: 0x%lx \n",
74                                  i, block->offset, block->size);
75                         kmem_cache_free(xgi_mem_block_cache, block);
76                         block = NULL;
77                 }
78         }
79         
80         heap->initialized = 0;
81 }
82
83
84 struct xgi_mem_block *xgi_mem_new_node(void)
85 {
86         struct xgi_mem_block *block =
87                 kmem_cache_alloc(xgi_mem_block_cache, GFP_KERNEL);
88
89         if (!block) {
90                 DRM_ERROR("kmem_cache_alloc failed\n");
91                 return NULL;
92         }
93
94         block->offset = 0;
95         block->size = 0;
96         block->filp = (struct drm_file *) -1;
97
98         return block;
99 }
100
101
102 static struct xgi_mem_block *xgi_mem_alloc(struct xgi_mem_heap * heap,
103                                            unsigned long originalSize)
104 {
105         struct xgi_mem_block *block, *free_block, *used_block;
106         unsigned long size = (originalSize + PAGE_SIZE - 1) & PAGE_MASK;
107
108
109         DRM_INFO("Original 0x%lx bytes requested, really 0x%lx allocated\n",
110                  originalSize, size);
111
112         if (size == 0) {
113                 DRM_ERROR("size == 0\n");
114                 return (NULL);
115         }
116         DRM_INFO("max_freesize: 0x%lx \n", heap->max_freesize);
117         if (size > heap->max_freesize) {
118                 DRM_ERROR
119                     ("size: 0x%lx is bigger than frame buffer total free size: 0x%lx !\n",
120                      size, heap->max_freesize);
121                 return (NULL);
122         }
123
124         list_for_each_entry(block, &heap->free_list, list) {
125                 DRM_INFO("block: 0x%px \n", block);
126                 if (size <= block->size) {
127                         break;
128                 }
129         }
130
131         if (&block->list == &heap->free_list) {
132                 DRM_ERROR
133                     ("Can't allocate %ldk size from frame buffer memory !\n",
134                      size / 1024);
135                 return (NULL);
136         }
137
138         free_block = block;
139         DRM_INFO("alloc size: 0x%lx from offset: 0x%lx size: 0x%lx \n",
140                  size, free_block->offset, free_block->size);
141
142         if (size == free_block->size) {
143                 used_block = free_block;
144                 DRM_INFO("size == free_block->size: free_block = 0x%p\n",
145                          free_block);
146                 list_del(&free_block->list);
147         } else {
148                 used_block = xgi_mem_new_node();
149
150                 if (used_block == NULL)
151                         return (NULL);
152
153                 if (used_block == free_block) {
154                         DRM_ERROR("used_block == free_block = 0x%p\n",
155                                   used_block);
156                 }
157
158                 used_block->offset = free_block->offset;
159                 used_block->size = size;
160
161                 free_block->offset += size;
162                 free_block->size -= size;
163         }
164
165         heap->max_freesize -= size;
166
167         list_add(&used_block->list, &heap->used_list);
168
169         return (used_block);
170 }
171
172 int xgi_mem_free(struct xgi_mem_heap * heap, unsigned long offset,
173                  struct drm_file * filp)
174 {
175         struct xgi_mem_block *used_block = NULL, *block;
176         struct xgi_mem_block *prev, *next;
177
178         unsigned long upper;
179         unsigned long lower;
180
181         list_for_each_entry(block, &heap->used_list, list) {
182                 if (block->offset == offset) {
183                         break;
184                 }
185         }
186
187         if (&block->list == &heap->used_list) {
188                 DRM_ERROR("can't find block: 0x%lx to free!\n", offset);
189                 return -ENOENT;
190         }
191
192         if (block->filp != filp) {
193                 return -EPERM;
194         }
195
196         used_block = block;
197         DRM_INFO("used_block: 0x%p, offset = 0x%lx, size = 0x%lx\n",
198                  used_block, used_block->offset, used_block->size);
199
200         heap->max_freesize += used_block->size;
201
202         prev = next = NULL;
203         upper = used_block->offset + used_block->size;
204         lower = used_block->offset;
205
206         list_for_each_entry(block, &heap->free_list, list) {
207                 if (block->offset == upper) {
208                         next = block;
209                 } else if ((block->offset + block->size) == lower) {
210                         prev = block;
211                 }
212         }
213
214         DRM_INFO("next = 0x%p, prev = 0x%p\n", next, prev);
215         list_del(&used_block->list);
216
217         if (prev && next) {
218                 prev->size += (used_block->size + next->size);
219                 list_del(&next->list);
220                 DRM_INFO("free node 0x%p\n", next);
221                 kmem_cache_free(xgi_mem_block_cache, next);
222                 kmem_cache_free(xgi_mem_block_cache, used_block);
223         }
224         else if (prev) {
225                 prev->size += used_block->size;
226                 DRM_INFO("free node 0x%p\n", used_block);
227                 kmem_cache_free(xgi_mem_block_cache, used_block);
228         }
229         else if (next) {
230                 next->size += used_block->size;
231                 next->offset = used_block->offset;
232                 DRM_INFO("free node 0x%p\n", used_block);
233                 kmem_cache_free(xgi_mem_block_cache, used_block);
234         }
235         else {
236                 list_add(&used_block->list, &heap->free_list);
237                 DRM_INFO("Recycled free node %p, offset = 0x%lx, size = 0x%lx\n",
238                          used_block, used_block->offset, used_block->size);
239         }
240
241         return 0;
242 }
243
244
245 int xgi_alloc(struct xgi_info * info, struct xgi_mem_alloc * alloc,
246                  struct drm_file * filp)
247 {
248         struct xgi_mem_block *block;
249
250         mutex_lock(&info->dev->struct_mutex);
251         block = xgi_mem_alloc((alloc->location == XGI_MEMLOC_LOCAL)
252                               ? &info->fb_heap : &info->pcie_heap,
253                               alloc->size);
254         mutex_unlock(&info->dev->struct_mutex);
255
256         if (block == NULL) {
257                 alloc->size = 0;
258                 DRM_ERROR("Video RAM allocation failed\n");
259                 return -ENOMEM;
260         } else {
261                 DRM_INFO("Video RAM allocation succeeded: 0x%p\n",
262                          (char *)block->offset);
263                 alloc->size = block->size;
264                 alloc->offset = block->offset;
265                 alloc->hw_addr = block->offset;
266
267                 if (alloc->location == XGI_MEMLOC_NON_LOCAL) {
268                         alloc->hw_addr += info->pcie.base;
269                 }
270
271                 block->filp = filp;
272         }
273
274         return 0;
275 }
276
277
278 int xgi_fb_alloc_ioctl(struct drm_device * dev, void * data,
279                        struct drm_file * filp)
280 {
281         struct xgi_mem_alloc *alloc = 
282                 (struct xgi_mem_alloc *) data;
283         struct xgi_info *info = dev->dev_private;
284
285         alloc->location = XGI_MEMLOC_LOCAL;
286         return xgi_alloc(info, alloc, filp);
287 }
288
289
290 int xgi_fb_free(struct xgi_info * info, unsigned long offset,
291                 struct drm_file * filp)
292 {
293         int err = 0;
294
295         mutex_lock(&info->dev->struct_mutex);
296         err = xgi_mem_free(&info->fb_heap, offset, filp);
297         mutex_unlock(&info->dev->struct_mutex);
298
299         return err;
300 }
301
302
303 int xgi_fb_free_ioctl(struct drm_device * dev, void * data,
304                       struct drm_file * filp)
305 {
306         struct xgi_info *info = dev->dev_private;
307
308         return xgi_fb_free(info, *(u32 *) data, filp);
309 }
310
311
312 int xgi_fb_heap_init(struct xgi_info * info)
313 {
314         return xgi_mem_heap_init(&info->fb_heap, XGI_FB_HEAP_START,
315                                  info->fb.size);
316 }
317
318 /**
319  * Free all blocks associated with a particular file handle.
320  */
321 void xgi_free_all(struct xgi_info * info, struct xgi_mem_heap * heap,
322                   struct drm_file * filp)
323 {
324         if (!heap->initialized) {
325                 return;
326         }
327
328
329         do {
330                 struct xgi_mem_block *block;
331
332                 list_for_each_entry(block, &heap->used_list, list) {
333                         if (block->filp == filp) {
334                                 break;
335                         }
336                 }
337
338                 if (&block->list == &heap->used_list) {
339                         break;
340                 }
341
342                 (void) xgi_mem_free(heap, block->offset, filp);
343         } while(1);
344 }