Eliminate structure typedefs
[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 = 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 = 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 = 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 = 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         struct list_head *mempid_list;
100
101         if (offset < 0) {
102                 XGI_INFO("free onscreen frame buffer successfully !\n");
103         } else {
104                 xgi_down(info->fb_sem);
105                 block = xgi_mem_free(info, offset);
106                 xgi_up(info->fb_sem);
107
108                 if (block == NULL) {
109                         XGI_ERROR("xgi_mem_free() failed at base 0x%lx\n",
110                                   offset);
111                 }
112
113                 /* manage mempid */
114                 mempid_list = xgi_mempid_list.next;
115                 while (mempid_list != &xgi_mempid_list) {
116                         mempid_block =
117                             list_entry(mempid_list, struct xgi_mem_pid, list);
118                         if (mempid_block->location == LOCAL
119                             && mempid_block->bus_addr == bus_addr) {
120                                 mempid_freeblock = mempid_block;
121                                 break;
122                         }
123                         mempid_list = mempid_list->next;
124                 }
125                 if (mempid_freeblock) {
126                         list_del(&mempid_freeblock->list);
127                         XGI_INFO
128                             ("Memory ProcessID delete one fb block pid:%ld successfully! \n",
129                              mempid_freeblock->pid);
130                         kfree(mempid_freeblock);
131                 }
132         }
133 }
134
135 int xgi_fb_heap_init(struct xgi_info * info)
136 {
137         struct xgi_mem_block *block;
138
139         xgi_fb_heap = kmalloc(sizeof(struct xgi_mem_heap), GFP_KERNEL);
140         if (!xgi_fb_heap) {
141                 XGI_ERROR("xgi_fb_heap alloc failed\n");
142                 return 0;
143         }
144
145         INIT_LIST_HEAD(&xgi_fb_heap->free_list);
146         INIT_LIST_HEAD(&xgi_fb_heap->used_list);
147         INIT_LIST_HEAD(&xgi_fb_heap->sort_list);
148
149         xgi_fb_cache_block =
150             kmem_cache_create("xgi_fb_block", sizeof(struct xgi_mem_block), 0,
151                               SLAB_HWCACHE_ALIGN, NULL, NULL);
152
153         if (NULL == xgi_fb_cache_block) {
154                 XGI_ERROR("Fail to creat xgi_fb_block\n");
155                 goto fail1;
156         }
157
158         block =
159             (struct xgi_mem_block *) kmem_cache_alloc(xgi_fb_cache_block,
160                                                  GFP_KERNEL);
161         if (!block) {
162                 XGI_ERROR("kmem_cache_alloc failed\n");
163                 goto fail2;
164         }
165
166         block->offset = XGI_FB_HEAP_START;
167         block->size = info->fb.size - XGI_FB_HEAP_START;
168
169         list_add(&block->list, &xgi_fb_heap->free_list);
170
171         xgi_fb_heap->max_freesize = info->fb.size - XGI_FB_HEAP_START;
172
173         XGI_INFO("fb start offset: 0x%lx, memory size : 0x%lx\n", block->offset,
174                  block->size);
175         XGI_INFO("xgi_fb_heap->max_freesize: 0x%lx \n",
176                  xgi_fb_heap->max_freesize);
177
178         return 1;
179
180       fail2:
181         if (xgi_fb_cache_block) {
182                 kmem_cache_destroy(xgi_fb_cache_block);
183                 xgi_fb_cache_block = NULL;
184         }
185       fail1:
186         if (xgi_fb_heap) {
187                 kfree(xgi_fb_heap);
188                 xgi_fb_heap = NULL;
189         }
190         return 0;
191 }
192
193 void xgi_fb_heap_cleanup(struct xgi_info * info)
194 {
195         struct list_head *free_list, *temp;
196         struct xgi_mem_block *block;
197         int i;
198
199         if (xgi_fb_heap) {
200                 free_list = &xgi_fb_heap->free_list;
201                 for (i = 0; i < 3; i++, free_list++) {
202                         temp = free_list->next;
203                         while (temp != free_list) {
204                                 block =
205                                     list_entry(temp, struct xgi_mem_block,
206                                                list);
207                                 temp = temp->next;
208
209                                 XGI_INFO
210                                     ("No. %d block->offset: 0x%lx block->size: 0x%lx \n",
211                                      i, block->offset, block->size);
212                                 //XGI_INFO("No. %d free block: 0x%p \n", i, block);
213                                 kmem_cache_free(xgi_fb_cache_block, block);
214                                 block = NULL;
215                         }
216                 }
217                 XGI_INFO("xgi_fb_heap: 0x%p \n", xgi_fb_heap);
218                 kfree(xgi_fb_heap);
219                 xgi_fb_heap = NULL;
220         }
221
222         if (xgi_fb_cache_block) {
223                 kmem_cache_destroy(xgi_fb_cache_block);
224                 xgi_fb_cache_block = NULL;
225         }
226 }
227
228 static struct xgi_mem_block *xgi_mem_new_node(void)
229 {
230         struct xgi_mem_block *block;
231
232         block =
233             (struct xgi_mem_block *) kmem_cache_alloc(xgi_fb_cache_block,
234                                                  GFP_KERNEL);
235         if (!block) {
236                 XGI_ERROR("kmem_cache_alloc failed\n");
237                 return NULL;
238         }
239
240         return block;
241 }
242
243 #if 0
244 static void xgi_mem_insert_node_after(struct xgi_mem_list * list,
245                                       struct xgi_mem_block * current,
246                                       struct xgi_mem_block * block);
247 static void xgi_mem_insert_node_before(struct xgi_mem_list * list,
248                                        struct xgi_mem_block * current,
249                                        struct xgi_mem_block * block);
250 static void xgi_mem_insert_node_head(struct xgi_mem_list * list,
251                                      struct xgi_mem_block * block);
252 static void xgi_mem_insert_node_tail(struct xgi_mem_list * list,
253                                      struct xgi_mem_block * block);
254 static void xgi_mem_delete_node(struct xgi_mem_list * list, struct xgi_mem_block * block);
255 /*
256  *  insert node:block after node:current
257  */
258 static void xgi_mem_insert_node_after(struct xgi_mem_list * list,
259                                       struct xgi_mem_block * current,
260                                       struct xgi_mem_block * block)
261 {
262         block->prev = current;
263         block->next = current->next;
264         current->next = block;
265
266         if (current == list->tail) {
267                 list->tail = block;
268         } else {
269                 block->next->prev = block;
270         }
271 }
272
273 /*
274  *  insert node:block before node:current
275  */
276 static void xgi_mem_insert_node_before(struct xgi_mem_list * list,
277                                        struct xgi_mem_block * current,
278                                        struct xgi_mem_block * block)
279 {
280         block->prev = current->prev;
281         block->next = current;
282         current->prev = block;
283         if (current == list->head) {
284                 list->head = block;
285         } else {
286                 block->prev->next = block;
287         }
288 }
289 void xgi_mem_insert_node_head(struct xgi_mem_list * list, struct xgi_mem_block * block)
290 {
291         block->next = list->head;
292         block->prev = NULL;
293
294         if (NULL == list->head) {
295                 list->tail = block;
296         } else {
297                 list->head->prev = block;
298         }
299         list->head = block;
300 }
301
302 static void xgi_mem_insert_node_tail(struct xgi_mem_list * list,
303                                      struct xgi_mem_block * block)
304 {
305         block->next = NULL;
306         block->prev = list->tail;
307         if (NULL == list->tail) {
308                 list->head = block;
309         } else {
310                 list->tail->next = block;
311         }
312         list->tail = block;
313 }
314
315 static void xgi_mem_delete_node(struct xgi_mem_list * list, struct xgi_mem_block * block)
316 {
317         if (block == list->head) {
318                 list->head = block->next;
319         }
320         if (block == list->tail) {
321                 list->tail = block->prev;
322         }
323
324         if (block->prev) {
325                 block->prev->next = block->next;
326         }
327         if (block->next) {
328                 block->next->prev = block->prev;
329         }
330
331         block->next = block->prev = NULL;
332 }
333 #endif
334 static struct xgi_mem_block *xgi_mem_alloc(struct xgi_info * info,
335                                       unsigned long originalSize)
336 {
337         struct list_head *free_list;
338         struct xgi_mem_block *block, *free_block, *used_block;
339
340         unsigned long size = (originalSize + PAGE_SIZE - 1) & PAGE_MASK;
341
342         XGI_INFO("Original 0x%lx bytes requested, really 0x%lx allocated\n",
343                  originalSize, size);
344
345         if (size == 0) {
346                 XGI_ERROR("size == 0\n");
347                 return (NULL);
348         }
349         XGI_INFO("max_freesize: 0x%lx \n", xgi_fb_heap->max_freesize);
350         if (size > xgi_fb_heap->max_freesize) {
351                 XGI_ERROR
352                     ("size: 0x%lx is bigger than frame buffer total free size: 0x%lx !\n",
353                      size, xgi_fb_heap->max_freesize);
354                 return (NULL);
355         }
356
357         free_list = xgi_fb_heap->free_list.next;
358
359         while (free_list != &xgi_fb_heap->free_list) {
360                 XGI_INFO("free_list: 0x%px \n", free_list);
361                 block = list_entry(free_list, struct xgi_mem_block, list);
362                 if (size <= block->size) {
363                         break;
364                 }
365                 free_list = free_list->next;
366         }
367
368         if (free_list == &xgi_fb_heap->free_list) {
369                 XGI_ERROR
370                     ("Can't allocate %ldk size from frame buffer memory !\n",
371                      size / 1024);
372                 return (NULL);
373         }
374
375         free_block = block;
376         XGI_INFO("alloc size: 0x%lx from offset: 0x%lx size: 0x%lx \n",
377                  size, free_block->offset, free_block->size);
378
379         if (size == free_block->size) {
380                 used_block = free_block;
381                 XGI_INFO("size == free_block->size: free_block = 0x%p\n",
382                          free_block);
383                 list_del(&free_block->list);
384         } else {
385                 used_block = xgi_mem_new_node();
386
387                 if (used_block == NULL)
388                         return (NULL);
389
390                 if (used_block == free_block) {
391                         XGI_ERROR("used_block == free_block = 0x%p\n",
392                                   used_block);
393                 }
394
395                 used_block->offset = free_block->offset;
396                 used_block->size = size;
397
398                 free_block->offset += size;
399                 free_block->size -= size;
400         }
401
402         xgi_fb_heap->max_freesize -= size;
403
404         list_add(&used_block->list, &xgi_fb_heap->used_list);
405
406         return (used_block);
407 }
408
409 static struct xgi_mem_block *xgi_mem_free(struct xgi_info * info, unsigned long offset)
410 {
411         struct list_head *free_list, *used_list;
412         struct xgi_mem_block *used_block = NULL, *block = NULL;
413         struct xgi_mem_block *prev, *next;
414
415         unsigned long upper;
416         unsigned long lower;
417
418         used_list = xgi_fb_heap->used_list.next;
419         while (used_list != &xgi_fb_heap->used_list) {
420                 block = list_entry(used_list, struct xgi_mem_block, list);
421                 if (block->offset == offset) {
422                         break;
423                 }
424                 used_list = used_list->next;
425         }
426
427         if (used_list == &xgi_fb_heap->used_list) {
428                 XGI_ERROR("can't find block: 0x%lx to free!\n", offset);
429                 return (NULL);
430         }
431
432         used_block = block;
433         XGI_INFO("used_block: 0x%p, offset = 0x%lx, size = 0x%lx\n",
434                  used_block, used_block->offset, used_block->size);
435
436         xgi_fb_heap->max_freesize += used_block->size;
437
438         prev = next = NULL;
439         upper = used_block->offset + used_block->size;
440         lower = used_block->offset;
441
442         free_list = xgi_fb_heap->free_list.next;
443         while (free_list != &xgi_fb_heap->free_list) {
444                 block = list_entry(free_list, struct xgi_mem_block, list);
445
446                 if (block->offset == upper) {
447                         next = block;
448                 } else if ((block->offset + block->size) == lower) {
449                         prev = block;
450                 }
451                 free_list = free_list->next;
452         }
453
454         XGI_INFO("next = 0x%p, prev = 0x%p\n", next, prev);
455         list_del(&used_block->list);
456
457         if (prev && next) {
458                 prev->size += (used_block->size + next->size);
459                 list_del(&next->list);
460                 XGI_INFO("free node 0x%p\n", next);
461                 kmem_cache_free(xgi_fb_cache_block, next);
462                 kmem_cache_free(xgi_fb_cache_block, used_block);
463
464                 next = NULL;
465                 used_block = NULL;
466                 return (prev);
467         }
468
469         if (prev) {
470                 prev->size += used_block->size;
471                 XGI_INFO("free node 0x%p\n", used_block);
472                 kmem_cache_free(xgi_fb_cache_block, used_block);
473                 used_block = NULL;
474                 return (prev);
475         }
476
477         if (next) {
478                 next->size += used_block->size;
479                 next->offset = used_block->offset;
480                 XGI_INFO("free node 0x%p\n", used_block);
481                 kmem_cache_free(xgi_fb_cache_block, used_block);
482                 used_block = NULL;
483                 return (next);
484         }
485
486         list_add(&used_block->list, &xgi_fb_heap->free_list);
487         XGI_INFO("Recycled free node %p, offset = 0x%lx, size = 0x%lx\n",
488                  used_block, used_block->offset, used_block->size);
489
490         return (used_block);
491 }