Convert xgi_mem_location enum values to less generic names.
[platform/upstream/libdrm.git] / linux-core / xgi_fb.c
1
2 /****************************************************************************
3  * Copyright (C) 2003-2006 by XGI Technology, Taiwan.                   
4  *                                                                                                                                                      *
5  * All Rights Reserved.                                                                                                         *
6  *                                                                                                                                                      *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the      
9  * "Software"), to deal in the Software without restriction, including  
10  * without limitation on the rights to use, copy, modify, merge,        
11  * publish, distribute, sublicense, and/or sell copies of the Software, 
12  * and to permit persons to whom the Software is furnished to do so,    
13  * subject to 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       
17  * portions of the Software.                                            
18  *                                                                                                                                                      *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                
22  * NON-INFRINGEMENT.  IN NO EVENT SHALL XGI AND/OR                      
23  * ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,           
24  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,           
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER                        
26  * DEALINGS IN THE SOFTWARE.                                                                                            
27  ***************************************************************************/
28
29 #include "xgi_types.h"
30 #include "xgi_linux.h"
31 #include "xgi_drv.h"
32 #include "xgi_fb.h"
33
34 #define XGI_FB_HEAP_START 0x1000000
35
36 static struct xgi_mem_heap *xgi_fb_heap;
37 static struct kmem_cache *xgi_fb_cache_block = NULL;
38 extern struct list_head xgi_mempid_list;
39
40 static struct xgi_mem_block *xgi_mem_new_node(void);
41 static struct xgi_mem_block *xgi_mem_alloc(struct xgi_info * info, unsigned long size);
42 static struct xgi_mem_block *xgi_mem_free(struct xgi_info * info, unsigned long offset);
43
44 void xgi_fb_alloc(struct xgi_info * info,
45                   struct xgi_mem_req * req, struct xgi_mem_alloc * alloc)
46 {
47         struct xgi_mem_block *block;
48         struct xgi_mem_pid *mempid_block;
49
50         if (req->is_front) {
51                 alloc->location = XGI_MEMLOC_LOCAL;
52                 alloc->bus_addr = info->fb.base;
53                 alloc->hw_addr = 0;
54                 XGI_INFO
55                     ("Video RAM allocation on front buffer successfully! \n");
56         } else {
57                 xgi_down(info->fb_sem);
58                 block = xgi_mem_alloc(info, req->size);
59                 xgi_up(info->fb_sem);
60
61                 if (block == NULL) {
62                         alloc->location = XGI_MEMLOC_LOCAL;
63                         alloc->size = 0;
64                         alloc->bus_addr = 0;
65                         alloc->hw_addr = 0;
66                         XGI_ERROR("Video RAM allocation failed\n");
67                 } else {
68                         XGI_INFO("Video RAM allocation succeeded: 0x%p\n",
69                                  (char *)block->offset);
70                         alloc->location = XGI_MEMLOC_LOCAL;
71                         alloc->size = block->size;
72                         alloc->bus_addr = info->fb.base + block->offset;
73                         alloc->hw_addr = block->offset;
74
75                         /* manage mempid */
76                         mempid_block =
77                             kmalloc(sizeof(struct xgi_mem_pid), GFP_KERNEL);
78                         mempid_block->location = XGI_MEMLOC_LOCAL;
79                         mempid_block->bus_addr = alloc->bus_addr;
80                         mempid_block->pid = alloc->pid;
81
82                         if (!mempid_block)
83                                 XGI_ERROR("mempid_block alloc failed\n");
84
85                         XGI_INFO
86                             ("Memory ProcessID add one fb block pid:%ld successfully! \n",
87                              mempid_block->pid);
88                         list_add(&mempid_block->list, &xgi_mempid_list);
89                 }
90         }
91 }
92
93 void xgi_fb_free(struct xgi_info * info, unsigned long bus_addr)
94 {
95         struct xgi_mem_block *block;
96         unsigned long offset = bus_addr - info->fb.base;
97         struct xgi_mem_pid *mempid_block;
98         struct xgi_mem_pid *mempid_freeblock = NULL;
99
100         if (offset < 0) {
101                 XGI_INFO("free onscreen frame buffer successfully !\n");
102         } else {
103                 xgi_down(info->fb_sem);
104                 block = xgi_mem_free(info, offset);
105                 xgi_up(info->fb_sem);
106
107                 if (block == NULL) {
108                         XGI_ERROR("xgi_mem_free() failed at base 0x%lx\n",
109                                   offset);
110                 }
111
112                 /* manage mempid */
113                 list_for_each_entry(mempid_block, &xgi_mempid_list, list) {
114                         if (mempid_block->location == XGI_MEMLOC_LOCAL
115                             && mempid_block->bus_addr == bus_addr) {
116                                 mempid_freeblock = mempid_block;
117                                 break;
118                         }
119                 }
120                 if (mempid_freeblock) {
121                         list_del(&mempid_freeblock->list);
122                         XGI_INFO
123                             ("Memory ProcessID delete one fb block pid:%ld successfully! \n",
124                              mempid_freeblock->pid);
125                         kfree(mempid_freeblock);
126                 }
127         }
128 }
129
130 int xgi_fb_heap_init(struct xgi_info * info)
131 {
132         struct xgi_mem_block *block;
133
134         xgi_fb_heap = kmalloc(sizeof(struct xgi_mem_heap), GFP_KERNEL);
135         if (!xgi_fb_heap) {
136                 XGI_ERROR("xgi_fb_heap alloc failed\n");
137                 return 0;
138         }
139
140         INIT_LIST_HEAD(&xgi_fb_heap->free_list);
141         INIT_LIST_HEAD(&xgi_fb_heap->used_list);
142         INIT_LIST_HEAD(&xgi_fb_heap->sort_list);
143
144         xgi_fb_cache_block =
145             kmem_cache_create("xgi_fb_block", sizeof(struct xgi_mem_block), 0,
146                               SLAB_HWCACHE_ALIGN, NULL, NULL);
147
148         if (NULL == xgi_fb_cache_block) {
149                 XGI_ERROR("Fail to creat xgi_fb_block\n");
150                 goto fail1;
151         }
152
153         block =
154             (struct xgi_mem_block *) kmem_cache_alloc(xgi_fb_cache_block,
155                                                  GFP_KERNEL);
156         if (!block) {
157                 XGI_ERROR("kmem_cache_alloc failed\n");
158                 goto fail2;
159         }
160
161         block->offset = XGI_FB_HEAP_START;
162         block->size = info->fb.size - XGI_FB_HEAP_START;
163
164         list_add(&block->list, &xgi_fb_heap->free_list);
165
166         xgi_fb_heap->max_freesize = info->fb.size - XGI_FB_HEAP_START;
167
168         XGI_INFO("fb start offset: 0x%lx, memory size : 0x%lx\n", block->offset,
169                  block->size);
170         XGI_INFO("xgi_fb_heap->max_freesize: 0x%lx \n",
171                  xgi_fb_heap->max_freesize);
172
173         return 1;
174
175       fail2:
176         if (xgi_fb_cache_block) {
177                 kmem_cache_destroy(xgi_fb_cache_block);
178                 xgi_fb_cache_block = NULL;
179         }
180       fail1:
181         if (xgi_fb_heap) {
182                 kfree(xgi_fb_heap);
183                 xgi_fb_heap = NULL;
184         }
185         return 0;
186 }
187
188 void xgi_fb_heap_cleanup(struct xgi_info * info)
189 {
190         struct list_head *free_list;
191         struct xgi_mem_block *block;
192         struct xgi_mem_block *next;
193         int i;
194
195         if (xgi_fb_heap) {
196                 free_list = &xgi_fb_heap->free_list;
197                 for (i = 0; i < 3; i++, free_list++) {
198                         list_for_each_entry_safe(block, next, free_list, list) {
199                                 XGI_INFO
200                                     ("No. %d block->offset: 0x%lx block->size: 0x%lx \n",
201                                      i, block->offset, block->size);
202                                 //XGI_INFO("No. %d free block: 0x%p \n", i, block);
203                                 kmem_cache_free(xgi_fb_cache_block, block);
204                                 block = NULL;
205                         }
206                 }
207                 XGI_INFO("xgi_fb_heap: 0x%p \n", xgi_fb_heap);
208                 kfree(xgi_fb_heap);
209                 xgi_fb_heap = NULL;
210         }
211
212         if (xgi_fb_cache_block) {
213                 kmem_cache_destroy(xgi_fb_cache_block);
214                 xgi_fb_cache_block = NULL;
215         }
216 }
217
218 static struct xgi_mem_block *xgi_mem_new_node(void)
219 {
220         struct xgi_mem_block *block;
221
222         block =
223             (struct xgi_mem_block *) kmem_cache_alloc(xgi_fb_cache_block,
224                                                  GFP_KERNEL);
225         if (!block) {
226                 XGI_ERROR("kmem_cache_alloc failed\n");
227                 return NULL;
228         }
229
230         return block;
231 }
232
233 #if 0
234 static void xgi_mem_insert_node_after(struct xgi_mem_list * list,
235                                       struct xgi_mem_block * current,
236                                       struct xgi_mem_block * block);
237 static void xgi_mem_insert_node_before(struct xgi_mem_list * list,
238                                        struct xgi_mem_block * current,
239                                        struct xgi_mem_block * block);
240 static void xgi_mem_insert_node_head(struct xgi_mem_list * list,
241                                      struct xgi_mem_block * block);
242 static void xgi_mem_insert_node_tail(struct xgi_mem_list * list,
243                                      struct xgi_mem_block * block);
244 static void xgi_mem_delete_node(struct xgi_mem_list * list, struct xgi_mem_block * block);
245 /*
246  *  insert node:block after node:current
247  */
248 static void xgi_mem_insert_node_after(struct xgi_mem_list * list,
249                                       struct xgi_mem_block * current,
250                                       struct xgi_mem_block * block)
251 {
252         block->prev = current;
253         block->next = current->next;
254         current->next = block;
255
256         if (current == list->tail) {
257                 list->tail = block;
258         } else {
259                 block->next->prev = block;
260         }
261 }
262
263 /*
264  *  insert node:block before node:current
265  */
266 static void xgi_mem_insert_node_before(struct xgi_mem_list * list,
267                                        struct xgi_mem_block * current,
268                                        struct xgi_mem_block * block)
269 {
270         block->prev = current->prev;
271         block->next = current;
272         current->prev = block;
273         if (current == list->head) {
274                 list->head = block;
275         } else {
276                 block->prev->next = block;
277         }
278 }
279 void xgi_mem_insert_node_head(struct xgi_mem_list * list, struct xgi_mem_block * block)
280 {
281         block->next = list->head;
282         block->prev = NULL;
283
284         if (NULL == list->head) {
285                 list->tail = block;
286         } else {
287                 list->head->prev = block;
288         }
289         list->head = block;
290 }
291
292 static void xgi_mem_insert_node_tail(struct xgi_mem_list * list,
293                                      struct xgi_mem_block * block)
294 {
295         block->next = NULL;
296         block->prev = list->tail;
297         if (NULL == list->tail) {
298                 list->head = block;
299         } else {
300                 list->tail->next = block;
301         }
302         list->tail = block;
303 }
304
305 static void xgi_mem_delete_node(struct xgi_mem_list * list, struct xgi_mem_block * block)
306 {
307         if (block == list->head) {
308                 list->head = block->next;
309         }
310         if (block == list->tail) {
311                 list->tail = block->prev;
312         }
313
314         if (block->prev) {
315                 block->prev->next = block->next;
316         }
317         if (block->next) {
318                 block->next->prev = block->prev;
319         }
320
321         block->next = block->prev = NULL;
322 }
323 #endif
324 static struct xgi_mem_block *xgi_mem_alloc(struct xgi_info * info,
325                                       unsigned long originalSize)
326 {
327         struct xgi_mem_block *block, *free_block, *used_block;
328
329         unsigned long size = (originalSize + PAGE_SIZE - 1) & PAGE_MASK;
330
331         XGI_INFO("Original 0x%lx bytes requested, really 0x%lx allocated\n",
332                  originalSize, size);
333
334         if (size == 0) {
335                 XGI_ERROR("size == 0\n");
336                 return (NULL);
337         }
338         XGI_INFO("max_freesize: 0x%lx \n", xgi_fb_heap->max_freesize);
339         if (size > xgi_fb_heap->max_freesize) {
340                 XGI_ERROR
341                     ("size: 0x%lx is bigger than frame buffer total free size: 0x%lx !\n",
342                      size, xgi_fb_heap->max_freesize);
343                 return (NULL);
344         }
345
346         list_for_each_entry(block, &xgi_fb_heap->free_list, list) {
347                 XGI_INFO("free_list: 0x%px \n", free_list);
348                 if (size <= block->size) {
349                         break;
350                 }
351         }
352
353         if (&block->list == &xgi_fb_heap->free_list) {
354                 XGI_ERROR
355                     ("Can't allocate %ldk size from frame buffer memory !\n",
356                      size / 1024);
357                 return (NULL);
358         }
359
360         free_block = block;
361         XGI_INFO("alloc size: 0x%lx from offset: 0x%lx size: 0x%lx \n",
362                  size, free_block->offset, free_block->size);
363
364         if (size == free_block->size) {
365                 used_block = free_block;
366                 XGI_INFO("size == free_block->size: free_block = 0x%p\n",
367                          free_block);
368                 list_del(&free_block->list);
369         } else {
370                 used_block = xgi_mem_new_node();
371
372                 if (used_block == NULL)
373                         return (NULL);
374
375                 if (used_block == free_block) {
376                         XGI_ERROR("used_block == free_block = 0x%p\n",
377                                   used_block);
378                 }
379
380                 used_block->offset = free_block->offset;
381                 used_block->size = size;
382
383                 free_block->offset += size;
384                 free_block->size -= size;
385         }
386
387         xgi_fb_heap->max_freesize -= size;
388
389         list_add(&used_block->list, &xgi_fb_heap->used_list);
390
391         return (used_block);
392 }
393
394 static struct xgi_mem_block *xgi_mem_free(struct xgi_info * info, unsigned long offset)
395 {
396         struct xgi_mem_block *used_block = NULL, *block;
397         struct xgi_mem_block *prev, *next;
398
399         unsigned long upper;
400         unsigned long lower;
401
402         list_for_each_entry(block, &xgi_fb_heap->used_list, list) {
403                 if (block->offset == offset) {
404                         break;
405                 }
406         }
407
408         if (&block->list == &xgi_fb_heap->used_list) {
409                 XGI_ERROR("can't find block: 0x%lx to free!\n", offset);
410                 return (NULL);
411         }
412
413         used_block = block;
414         XGI_INFO("used_block: 0x%p, offset = 0x%lx, size = 0x%lx\n",
415                  used_block, used_block->offset, used_block->size);
416
417         xgi_fb_heap->max_freesize += used_block->size;
418
419         prev = next = NULL;
420         upper = used_block->offset + used_block->size;
421         lower = used_block->offset;
422
423         list_for_each_entry(block, &xgi_fb_heap->free_list, list) {
424                 if (block->offset == upper) {
425                         next = block;
426                 } else if ((block->offset + block->size) == lower) {
427                         prev = block;
428                 }
429         }
430
431         XGI_INFO("next = 0x%p, prev = 0x%p\n", next, prev);
432         list_del(&used_block->list);
433
434         if (prev && next) {
435                 prev->size += (used_block->size + next->size);
436                 list_del(&next->list);
437                 XGI_INFO("free node 0x%p\n", next);
438                 kmem_cache_free(xgi_fb_cache_block, next);
439                 kmem_cache_free(xgi_fb_cache_block, used_block);
440
441                 next = NULL;
442                 used_block = NULL;
443                 return (prev);
444         }
445
446         if (prev) {
447                 prev->size += used_block->size;
448                 XGI_INFO("free node 0x%p\n", used_block);
449                 kmem_cache_free(xgi_fb_cache_block, used_block);
450                 used_block = NULL;
451                 return (prev);
452         }
453
454         if (next) {
455                 next->size += used_block->size;
456                 next->offset = used_block->offset;
457                 XGI_INFO("free node 0x%p\n", used_block);
458                 kmem_cache_free(xgi_fb_cache_block, used_block);
459                 used_block = NULL;
460                 return (next);
461         }
462
463         list_add(&used_block->list, &xgi_fb_heap->free_list);
464         XGI_INFO("Recycled free node %p, offset = 0x%lx, size = 0x%lx\n",
465                  used_block, used_block->offset, used_block->size);
466
467         return (used_block);
468 }