Eliminate use of DRM_ERR.
[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->owner = PCIE_INVALID;
97         block->filp = (DRMFILE) -1;
98
99         return block;
100 }
101
102
103 struct xgi_mem_block *xgi_mem_alloc(struct xgi_mem_heap * heap,
104                                     unsigned long originalSize,
105                                     enum PcieOwner owner)
106 {
107         struct xgi_mem_block *block, *free_block, *used_block;
108         unsigned long size = (originalSize + PAGE_SIZE - 1) & PAGE_MASK;
109
110
111         DRM_INFO("Original 0x%lx bytes requested, really 0x%lx allocated\n",
112                  originalSize, size);
113
114         if (size == 0) {
115                 DRM_ERROR("size == 0\n");
116                 return (NULL);
117         }
118         DRM_INFO("max_freesize: 0x%lx \n", heap->max_freesize);
119         if (size > heap->max_freesize) {
120                 DRM_ERROR
121                     ("size: 0x%lx is bigger than frame buffer total free size: 0x%lx !\n",
122                      size, heap->max_freesize);
123                 return (NULL);
124         }
125
126         list_for_each_entry(block, &heap->free_list, list) {
127                 DRM_INFO("block: 0x%px \n", block);
128                 if (size <= block->size) {
129                         break;
130                 }
131         }
132
133         if (&block->list == &heap->free_list) {
134                 DRM_ERROR
135                     ("Can't allocate %ldk size from frame buffer memory !\n",
136                      size / 1024);
137                 return (NULL);
138         }
139
140         free_block = block;
141         DRM_INFO("alloc size: 0x%lx from offset: 0x%lx size: 0x%lx \n",
142                  size, free_block->offset, free_block->size);
143
144         if (size == free_block->size) {
145                 used_block = free_block;
146                 DRM_INFO("size == free_block->size: free_block = 0x%p\n",
147                          free_block);
148                 list_del(&free_block->list);
149         } else {
150                 used_block = xgi_mem_new_node();
151
152                 if (used_block == NULL)
153                         return (NULL);
154
155                 if (used_block == free_block) {
156                         DRM_ERROR("used_block == free_block = 0x%p\n",
157                                   used_block);
158                 }
159
160                 used_block->offset = free_block->offset;
161                 used_block->size = size;
162
163                 free_block->offset += size;
164                 free_block->size -= size;
165         }
166
167         heap->max_freesize -= size;
168
169         list_add(&used_block->list, &heap->used_list);
170         used_block->owner = owner;
171
172         return (used_block);
173 }
174
175 int xgi_mem_free(struct xgi_mem_heap * heap, unsigned long offset,
176                  DRMFILE filp)
177 {
178         struct xgi_mem_block *used_block = NULL, *block;
179         struct xgi_mem_block *prev, *next;
180
181         unsigned long upper;
182         unsigned long lower;
183
184         list_for_each_entry(block, &heap->used_list, list) {
185                 if (block->offset == offset) {
186                         break;
187                 }
188         }
189
190         if (&block->list == &heap->used_list) {
191                 DRM_ERROR("can't find block: 0x%lx to free!\n", offset);
192                 return -ENOENT;
193         }
194
195         if (block->filp != filp) {
196                 return -EPERM;
197         }
198
199         used_block = block;
200         DRM_INFO("used_block: 0x%p, offset = 0x%lx, size = 0x%lx\n",
201                  used_block, used_block->offset, used_block->size);
202
203         heap->max_freesize += used_block->size;
204
205         prev = next = NULL;
206         upper = used_block->offset + used_block->size;
207         lower = used_block->offset;
208
209         list_for_each_entry(block, &heap->free_list, list) {
210                 if (block->offset == upper) {
211                         next = block;
212                 } else if ((block->offset + block->size) == lower) {
213                         prev = block;
214                 }
215         }
216
217         DRM_INFO("next = 0x%p, prev = 0x%p\n", next, prev);
218         list_del(&used_block->list);
219
220         if (prev && next) {
221                 prev->size += (used_block->size + next->size);
222                 list_del(&next->list);
223                 DRM_INFO("free node 0x%p\n", next);
224                 kmem_cache_free(xgi_mem_block_cache, next);
225                 kmem_cache_free(xgi_mem_block_cache, used_block);
226         }
227         else if (prev) {
228                 prev->size += used_block->size;
229                 DRM_INFO("free node 0x%p\n", used_block);
230                 kmem_cache_free(xgi_mem_block_cache, used_block);
231         }
232         else if (next) {
233                 next->size += used_block->size;
234                 next->offset = used_block->offset;
235                 DRM_INFO("free node 0x%p\n", used_block);
236                 kmem_cache_free(xgi_mem_block_cache, used_block);
237         }
238         else {
239                 list_add(&used_block->list, &heap->free_list);
240                 DRM_INFO("Recycled free node %p, offset = 0x%lx, size = 0x%lx\n",
241                          used_block, used_block->offset, used_block->size);
242         }
243
244         return 0;
245 }
246
247
248 int xgi_fb_alloc(struct xgi_info * info, struct xgi_mem_alloc * alloc,
249                  DRMFILE filp)
250 {
251         struct xgi_mem_block *block;
252
253         if (alloc->is_front) {
254                 alloc->location = XGI_MEMLOC_LOCAL;
255                 alloc->offset = 0;
256                 alloc->hw_addr = 0;
257                 DRM_INFO
258                     ("Video RAM allocation on front buffer successfully! \n");
259         } else {
260                 down(&info->fb_sem);
261                 block = xgi_mem_alloc(&info->fb_heap, alloc->size, PCIE_2D);
262                 up(&info->fb_sem);
263
264                 if (block == NULL) {
265                         alloc->location = XGI_MEMLOC_LOCAL;
266                         alloc->size = 0;
267                         DRM_ERROR("Video RAM allocation failed\n");
268                         return -ENOMEM;
269                 } else {
270                         DRM_INFO("Video RAM allocation succeeded: 0x%p\n",
271                                  (char *)block->offset);
272                         alloc->location = XGI_MEMLOC_LOCAL;
273                         alloc->size = block->size;
274                         alloc->offset = block->offset;
275                         alloc->hw_addr = block->offset;
276
277                         block->filp = filp;
278                 }
279         }
280
281         return 0;
282 }
283
284
285 int xgi_fb_alloc_ioctl(DRM_IOCTL_ARGS)
286 {
287         DRM_DEVICE;
288         struct xgi_mem_alloc alloc;
289         struct xgi_info *info = dev->dev_private;
290         int err;
291
292         DRM_COPY_FROM_USER_IOCTL(alloc, (struct xgi_mem_alloc __user *) data,
293                                  sizeof(alloc));
294
295         err = xgi_fb_alloc(info, & alloc, filp);
296         if (err) {
297                 return err;
298         }
299
300         DRM_COPY_TO_USER_IOCTL((struct xgi_mem_alloc __user *) data,
301                                alloc, sizeof(alloc));
302
303         return 0;
304 }
305
306
307 int xgi_fb_free(struct xgi_info * info, unsigned long offset, DRMFILE filp)
308 {
309         int err = 0;
310
311         if (offset == 0) {
312                 DRM_INFO("free onscreen frame buffer successfully !\n");
313         } else {
314                 down(&info->fb_sem);
315                 err = xgi_mem_free(&info->fb_heap, offset, filp);
316                 up(&info->fb_sem);
317         }
318
319         return err;
320 }
321
322
323 int xgi_fb_free_ioctl(DRM_IOCTL_ARGS)
324 {
325         DRM_DEVICE;
326         struct xgi_info *info = dev->dev_private;
327         u32 offset;
328
329         DRM_COPY_FROM_USER_IOCTL(offset, (unsigned long __user *) data,
330                                  sizeof(offset));
331
332         return xgi_fb_free(info, offset, filp);
333 }
334
335
336 int xgi_fb_heap_init(struct xgi_info * info)
337 {
338         return xgi_mem_heap_init(&info->fb_heap, XGI_FB_HEAP_START,
339                                  info->fb.size);
340 }
341
342 /**
343  * Free all blocks associated with a particular file handle.
344  */
345 void xgi_fb_free_all(struct xgi_info * info, DRMFILE filp)
346 {
347         if (!info->fb_heap.initialized) {
348                 return;
349         }
350
351         down(&info->fb_sem);
352
353         do {
354                 struct xgi_mem_block *block;
355
356                 list_for_each_entry(block, &info->fb_heap.used_list, list) {
357                         if (block->filp == filp) {
358                                 break;
359                         }
360                 }
361
362                 if (&block->list == &info->fb_heap.used_list) {
363                         break;
364                 }
365
366                 (void) xgi_mem_free(&info->fb_heap, block->offset, filp);
367         } while(1);
368
369         up(&info->fb_sem);
370 }