tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / mali / common / mali_block_allocator.c
1 /*
2  * Copyright (C) 2011-2012 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10 #include "mali_kernel_common.h"
11 #include "mali_kernel_memory_engine.h"
12 #include "mali_block_allocator.h"
13 #include "mali_osk.h"
14
15 #define MALI_BLOCK_SIZE (256UL * 1024UL)  /* 256 kB, remember to keep the ()s */
16
17 typedef struct block_info
18 {
19         struct block_info * next;
20 } block_info;
21
22 /* The structure used as the handle produced by block_allocator_allocate,
23  * and removed by block_allocator_release */
24 typedef struct block_allocator_allocation
25 {
26         /* The list will be released in reverse order */
27         block_info *last_allocated;
28         mali_allocation_engine * engine;
29         mali_memory_allocation * descriptor;
30         u32 start_offset;
31         u32 mapping_length;
32 } block_allocator_allocation;
33
34
35 typedef struct block_allocator
36 {
37     _mali_osk_lock_t *mutex;
38         block_info * all_blocks;
39         block_info * first_free;
40         u32 base;
41         u32 cpu_usage_adjust;
42         u32 num_blocks;
43 } block_allocator;
44
45 MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block);
46 static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine,  mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info);
47 static void block_allocator_release(void * ctx, void * handle);
48 static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block);
49 static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block );
50 static void block_allocator_destroy(mali_physical_memory_allocator * allocator);
51 static u32 block_allocator_stat(mali_physical_memory_allocator * allocator);
52
53 mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name)
54 {
55         mali_physical_memory_allocator * allocator;
56         block_allocator * info;
57         u32 usable_size;
58         u32 num_blocks;
59
60         usable_size = size & ~(MALI_BLOCK_SIZE - 1);
61         MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size));
62         MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size));
63         num_blocks = usable_size / MALI_BLOCK_SIZE;
64         MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks));
65
66         if (usable_size == 0)
67         {
68                 MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size));
69                 return NULL;
70         }
71
72         allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator));
73         if (NULL != allocator)
74         {
75                 info = _mali_osk_malloc(sizeof(block_allocator));
76                 if (NULL != info)
77                 {
78             info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED, 0, _MALI_OSK_LOCK_ORDER_MEM_INFO);
79             if (NULL != info->mutex)
80             {
81                         info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks);
82                             if (NULL != info->all_blocks)
83                             {
84                                     u32 i;
85                                     info->first_free = NULL;
86                                     info->num_blocks = num_blocks;
87
88                                     info->base = base_address;
89                                     info->cpu_usage_adjust = cpu_usage_adjust;
90
91                                     for ( i = 0; i < num_blocks; i++)
92                                     {
93                                             info->all_blocks[i].next = info->first_free;
94                                             info->first_free = &info->all_blocks[i];
95                                     }
96
97                                     allocator->allocate = block_allocator_allocate;
98                                     allocator->allocate_page_table_block = block_allocator_allocate_page_table_block;
99                                     allocator->destroy = block_allocator_destroy;
100                                     allocator->stat = block_allocator_stat;
101                                     allocator->ctx = info;
102                                         allocator->name = name;
103
104                                     return allocator;
105                             }
106                 _mali_osk_lock_term(info->mutex);
107             }
108                         _mali_osk_free(info);
109                 }
110                 _mali_osk_free(allocator);
111         }
112
113         return NULL;
114 }
115
116 static void block_allocator_destroy(mali_physical_memory_allocator * allocator)
117 {
118         block_allocator * info;
119         MALI_DEBUG_ASSERT_POINTER(allocator);
120         MALI_DEBUG_ASSERT_POINTER(allocator->ctx);
121         info = (block_allocator*)allocator->ctx;
122
123         _mali_osk_free(info->all_blocks);
124     _mali_osk_lock_term(info->mutex);
125         _mali_osk_free(info);
126         _mali_osk_free(allocator);
127 }
128
129 MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block)
130 {
131         return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE);
132 }
133
134 static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info)
135 {
136         block_allocator * info;
137         u32 left;
138         block_info * last_allocated = NULL;
139         mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE;
140         block_allocator_allocation *ret_allocation;
141
142         MALI_DEBUG_ASSERT_POINTER(ctx);
143         MALI_DEBUG_ASSERT_POINTER(descriptor);
144         MALI_DEBUG_ASSERT_POINTER(offset);
145         MALI_DEBUG_ASSERT_POINTER(alloc_info);
146
147         info = (block_allocator*)ctx;
148         left = descriptor->size - *offset;
149         MALI_DEBUG_ASSERT(0 != left);
150
151         if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
152
153         ret_allocation = _mali_osk_malloc( sizeof(block_allocator_allocation) );
154
155         if ( NULL == ret_allocation )
156         {
157                 /* Failure; try another allocator by returning MALI_MEM_ALLOC_NONE */
158                 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
159                 return result;
160         }
161
162         ret_allocation->start_offset = *offset;
163         ret_allocation->mapping_length = 0;
164
165         while ((left > 0) && (info->first_free))
166         {
167                 block_info * block;
168                 u32 phys_addr;
169                 u32 padding;
170                 u32 current_mapping_size;
171
172                 block = info->first_free;
173                 info->first_free = info->first_free->next;
174                 block->next = last_allocated;
175                 last_allocated = block;
176
177                 phys_addr = get_phys(info, block);
178
179                 padding = *offset & (MALI_BLOCK_SIZE-1);
180
181                 if (MALI_BLOCK_SIZE - padding < left)
182                 {
183                         current_mapping_size = MALI_BLOCK_SIZE - padding;
184                 }
185                 else
186                 {
187                         current_mapping_size = left;
188                 }
189
190                 if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, phys_addr + padding, info->cpu_usage_adjust, current_mapping_size))
191                 {
192                         MALI_DEBUG_PRINT(1, ("Mapping of physical memory  failed\n"));
193                         result = MALI_MEM_ALLOC_INTERNAL_FAILURE;
194                         mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->start_offset, ret_allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0);
195
196                         /* release all memory back to the pool */
197                         while (last_allocated)
198                         {
199                                 /* This relinks every block we've just allocated back into the free-list */
200                                 block = last_allocated->next;
201                                 last_allocated->next = info->first_free;
202                                 info->first_free = last_allocated;
203                                 last_allocated = block;
204                         }
205
206                         break;
207                 }
208
209                 *offset += current_mapping_size;
210                 left -= current_mapping_size;
211                 ret_allocation->mapping_length += current_mapping_size;
212         }
213
214         _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
215
216         if (last_allocated)
217         {
218                 if (left) result = MALI_MEM_ALLOC_PARTIAL;
219                 else result = MALI_MEM_ALLOC_FINISHED;
220
221                 /* Record all the information about this allocation */
222                 ret_allocation->last_allocated = last_allocated;
223                 ret_allocation->engine = engine;
224                 ret_allocation->descriptor = descriptor;
225
226                 alloc_info->ctx = info;
227                 alloc_info->handle = ret_allocation;
228                 alloc_info->release = block_allocator_release;
229         }
230         else
231         {
232                 /* Free the allocation information - nothing to be passed back */
233                 _mali_osk_free( ret_allocation );
234         }
235
236         return result;
237 }
238
239 static void block_allocator_release(void * ctx, void * handle)
240 {
241         block_allocator * info;
242         block_info * block, * next;
243         block_allocator_allocation *allocation;
244
245         MALI_DEBUG_ASSERT_POINTER(ctx);
246         MALI_DEBUG_ASSERT_POINTER(handle);
247
248         info = (block_allocator*)ctx;
249         allocation = (block_allocator_allocation*)handle;
250         block = allocation->last_allocated;
251
252         MALI_DEBUG_ASSERT_POINTER(block);
253
254         if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
255         {
256                 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
257                 return;
258         }
259
260         /* unmap */
261         mali_allocation_engine_unmap_physical(allocation->engine, allocation->descriptor, allocation->start_offset, allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0);
262
263         while (block)
264         {
265                 MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
266
267                 next = block->next;
268
269                 /* relink into free-list */
270                 block->next = info->first_free;
271                 info->first_free = block;
272
273                 /* advance the loop */
274                 block = next;
275         }
276
277         _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
278
279         _mali_osk_free( allocation );
280 }
281
282
283 static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block)
284 {
285         block_allocator * info;
286         mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_INTERNAL_FAILURE;
287
288         MALI_DEBUG_ASSERT_POINTER(ctx);
289         MALI_DEBUG_ASSERT_POINTER(block);
290         info = (block_allocator*)ctx;
291
292         if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
293
294         if (NULL != info->first_free)
295         {
296                 void * virt;
297                 u32 phys;
298                 u32 size;
299                 block_info * alloc;
300                 alloc = info->first_free;
301
302                 phys = get_phys(info, alloc); /* Does not modify info or alloc */
303                 size = MALI_BLOCK_SIZE; /* Must be multiple of MALI_MMU_PAGE_SIZE */
304                 virt = _mali_osk_mem_mapioregion( phys, size, "Mali block allocator page tables" );
305
306                 /* Failure of _mali_osk_mem_mapioregion will result in MALI_MEM_ALLOC_INTERNAL_FAILURE,
307                  * because it's unlikely another allocator will be able to map in. */
308
309                 if ( NULL != virt )
310                 {
311                         block->ctx = info; /* same as incoming ctx */
312                         block->handle = alloc;
313                         block->phys_base = phys;
314                         block->size = size;
315                         block->release = block_allocator_release_page_table_block;
316                         block->mapping = virt;
317
318                         info->first_free = alloc->next;
319
320                         alloc->next = NULL; /* Could potentially link many blocks together instead */
321
322                         result = MALI_MEM_ALLOC_FINISHED;
323                 }
324         }
325         else result = MALI_MEM_ALLOC_NONE;
326
327         _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
328
329         return result;
330 }
331
332
333 static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block )
334 {
335         block_allocator * info;
336         block_info * block, * next;
337
338         MALI_DEBUG_ASSERT_POINTER( page_table_block );
339
340         info = (block_allocator*)page_table_block->ctx;
341         block = (block_info*)page_table_block->handle;
342
343         MALI_DEBUG_ASSERT_POINTER(info);
344         MALI_DEBUG_ASSERT_POINTER(block);
345
346
347         if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
348         {
349                 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
350                 return;
351         }
352
353         /* Unmap all the physical memory at once */
354         _mali_osk_mem_unmapioregion( page_table_block->phys_base, page_table_block->size, page_table_block->mapping );
355
356         /** @note This loop handles the case where more than one block_info was linked.
357          * Probably unnecessary for page table block releasing. */
358         while (block)
359         {
360                 next = block->next;
361
362                 MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
363
364                 block->next = info->first_free;
365                 info->first_free = block;
366
367                 block = next;
368         }
369
370         _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
371 }
372
373 static u32 block_allocator_stat(mali_physical_memory_allocator * allocator)
374 {
375         block_allocator * info;
376         block_info *block;
377         u32 free_blocks = 0;
378
379         MALI_DEBUG_ASSERT_POINTER(allocator);
380
381         info = (block_allocator*)allocator->ctx;
382         block = info->first_free;
383
384         while(block)
385         {
386                 free_blocks++;
387                 block = block->next;
388         }
389         return (info->num_blocks - free_blocks) * MALI_BLOCK_SIZE;
390 }