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