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