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