a7698a403733ed10b00ad8f2d3c08e0e3b3b9ba6
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / mali400 / mali / linux / mali_memory_block_alloc.c
1 /*
2  * Copyright (C) 2010-2014 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_memory.h"
12 #include "mali_memory_block_alloc.h"
13 #include "mali_osk.h"
14 #include <linux/mutex.h>
15 #define MALI_BLOCK_SIZE (256UL * 1024UL)  /* 256 kB, remember to keep the ()s */
16
17 struct block_info {
18         struct block_info *next;
19 };
20
21 typedef struct block_info block_info;
22
23
24 typedef struct block_allocator {
25         struct mutex mutex;
26         block_info *all_blocks;
27         block_info *first_free;
28         u32 base;
29         u32 cpu_usage_adjust;
30         u32 num_blocks;
31         u32 free_blocks;
32 } block_allocator;
33
34 static block_allocator *mali_mem_block_gobal_allocator = NULL;
35
36 MALI_STATIC_INLINE u32 get_phys(block_allocator *info, block_info *block)
37 {
38         return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE);
39 }
40
41 static mali_mem_allocator *mali_mem_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size)
42 {
43         block_allocator *info;
44         u32 usable_size;
45         u32 num_blocks;
46
47         usable_size = size & ~(MALI_BLOCK_SIZE - 1);
48         MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size));
49         MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size));
50         num_blocks = usable_size / MALI_BLOCK_SIZE;
51         MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks));
52
53         if (usable_size == 0) {
54                 MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size));
55                 return NULL;
56         }
57
58         info = _mali_osk_malloc(sizeof(block_allocator));
59         if (NULL != info) {
60                 mutex_init(&info->mutex);
61                 info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks);
62                 if (NULL != info->all_blocks) {
63                         u32 i;
64                         info->first_free = NULL;
65                         info->num_blocks = num_blocks;
66                         info->free_blocks = num_blocks;
67
68                         info->base = base_address;
69                         info->cpu_usage_adjust = cpu_usage_adjust;
70
71                         for (i = 0; i < num_blocks; i++) {
72                                 info->all_blocks[i].next = info->first_free;
73                                 info->first_free = &info->all_blocks[i];
74                         }
75
76                         return (mali_mem_allocator *)info;
77                 }
78                 _mali_osk_free(info);
79         }
80
81         return NULL;
82 }
83
84 void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator)
85 {
86         block_allocator *info = (block_allocator *)allocator;
87
88         info = mali_mem_block_gobal_allocator;
89         if (NULL == info) return;
90
91         MALI_DEBUG_ASSERT_POINTER(info);
92
93         _mali_osk_free(info->all_blocks);
94         _mali_osk_free(info);
95 }
96
97 static void mali_mem_block_mali_map(mali_mem_allocation *descriptor, u32 phys, u32 virt, u32 size)
98 {
99         struct mali_page_directory *pagedir = descriptor->session->page_directory;
100         u32 prop = descriptor->mali_mapping.properties;
101         u32 offset = 0;
102
103         while (size) {
104                 mali_mmu_pagedir_update(pagedir, virt + offset, phys + offset, MALI_MMU_PAGE_SIZE, prop);
105
106                 size -= MALI_MMU_PAGE_SIZE;
107                 offset += MALI_MMU_PAGE_SIZE;
108         }
109 }
110
111 static int mali_mem_block_cpu_map(mali_mem_allocation *descriptor, struct vm_area_struct *vma, u32 mali_phys, u32 mapping_offset, u32 size, u32 cpu_usage_adjust)
112 {
113         u32 virt = vma->vm_start + mapping_offset;
114         u32 cpu_phys = mali_phys + cpu_usage_adjust;
115         u32 offset = 0;
116         int ret;
117
118         while (size) {
119                 ret = vm_insert_pfn(vma, virt + offset, __phys_to_pfn(cpu_phys + offset));
120
121                 if (unlikely(ret)) {
122                         MALI_DEBUG_PRINT(1, ("Block allocator: Failed to insert pfn into vma\n"));
123                         return 1;
124                 }
125
126                 size -= MALI_MMU_PAGE_SIZE;
127                 offset += MALI_MMU_PAGE_SIZE;
128         }
129
130         return 0;
131 }
132
133 mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session)
134 {
135         _mali_osk_errcode_t err;
136         mali_mem_allocation *descriptor;
137         block_allocator *info;
138         u32 left;
139         block_info *last_allocated = NULL;
140         block_allocator_allocation *ret_allocation;
141         u32 offset = 0;
142
143         size = ALIGN(size, MALI_BLOCK_SIZE);
144
145         info = mali_mem_block_gobal_allocator;
146         if (NULL == info) return NULL;
147
148         left = size;
149         MALI_DEBUG_ASSERT(0 != left);
150
151         descriptor = mali_mem_descriptor_create(session, MALI_MEM_BLOCK);
152         if (NULL == descriptor) {
153                 return NULL;
154         }
155
156         descriptor->mali_mapping.addr = mali_addr;
157         descriptor->size = size;
158         descriptor->cpu_mapping.addr = (void __user *)vma->vm_start;
159         descriptor->cpu_mapping.ref = 1;
160
161         if (VM_SHARED == (VM_SHARED & vma->vm_flags)) {
162                 descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT;
163         } else {
164                 /* Cached Mali memory mapping */
165                 descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE;
166                 vma->vm_flags |= VM_SHARED;
167         }
168
169         ret_allocation = &descriptor->block_mem.mem;
170
171         ret_allocation->mapping_length = 0;
172
173         _mali_osk_mutex_wait(session->memory_lock);
174         mutex_lock(&info->mutex);
175
176         if (left > (info->free_blocks * MALI_BLOCK_SIZE)) {
177                 MALI_DEBUG_PRINT(2, ("Mali block allocator: not enough free blocks to service allocation (%u)\n", left));
178                 mutex_unlock(&info->mutex);
179                 _mali_osk_mutex_signal(session->memory_lock);
180                 mali_mem_descriptor_destroy(descriptor);
181                 return NULL;
182         }
183
184         err = mali_mem_mali_map_prepare(descriptor);
185         if (_MALI_OSK_ERR_OK != err) {
186                 mutex_unlock(&info->mutex);
187                 _mali_osk_mutex_signal(session->memory_lock);
188                 mali_mem_descriptor_destroy(descriptor);
189                 return NULL;
190         }
191
192         while ((left > 0) && (info->first_free)) {
193                 block_info *block;
194                 u32 phys_addr;
195                 u32 current_mapping_size;
196
197                 block = info->first_free;
198                 info->first_free = info->first_free->next;
199                 block->next = last_allocated;
200                 last_allocated = block;
201
202                 phys_addr = get_phys(info, block);
203
204                 if (MALI_BLOCK_SIZE < left) {
205                         current_mapping_size = MALI_BLOCK_SIZE;
206                 } else {
207                         current_mapping_size = left;
208                 }
209
210                 mali_mem_block_mali_map(descriptor, phys_addr, mali_addr + offset, current_mapping_size);
211                 if (mali_mem_block_cpu_map(descriptor, vma, phys_addr, offset, current_mapping_size, info->cpu_usage_adjust)) {
212                         /* release all memory back to the pool */
213                         while (last_allocated) {
214                                 /* This relinks every block we've just allocated back into the free-list */
215                                 block = last_allocated->next;
216                                 last_allocated->next = info->first_free;
217                                 info->first_free = last_allocated;
218                                 last_allocated = block;
219                         }
220
221                         mutex_unlock(&info->mutex);
222                         _mali_osk_mutex_signal(session->memory_lock);
223
224                         mali_mem_mali_map_free(descriptor);
225                         mali_mem_descriptor_destroy(descriptor);
226
227                         return NULL;
228                 }
229
230                 left -= current_mapping_size;
231                 offset += current_mapping_size;
232                 ret_allocation->mapping_length += current_mapping_size;
233
234                 --info->free_blocks;
235         }
236
237         mutex_unlock(&info->mutex);
238         _mali_osk_mutex_signal(session->memory_lock);
239
240         MALI_DEBUG_ASSERT(0 == left);
241
242         /* Record all the information about this allocation */
243         ret_allocation->last_allocated = last_allocated;
244         ret_allocation->info = info;
245
246         return descriptor;
247 }
248
249 void mali_mem_block_release(mali_mem_allocation *descriptor)
250 {
251         block_allocator *info = descriptor->block_mem.mem.info;
252         block_info *block, *next;
253         block_allocator_allocation *allocation = &descriptor->block_mem.mem;
254
255         MALI_DEBUG_ASSERT(MALI_MEM_BLOCK == descriptor->type);
256
257         block = allocation->last_allocated;
258
259         MALI_DEBUG_ASSERT_POINTER(block);
260
261         /* unmap */
262         mali_mem_mali_map_free(descriptor);
263
264         mutex_lock(&info->mutex);
265
266         while (block) {
267                 MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
268
269                 next = block->next;
270
271                 /* relink into free-list */
272                 block->next = info->first_free;
273                 info->first_free = block;
274
275                 /* advance the loop */
276                 block = next;
277
278                 ++info->free_blocks;
279         }
280
281         mutex_unlock(&info->mutex);
282 }
283
284 u32 mali_mem_block_allocator_stat(void)
285 {
286         block_allocator *info = (block_allocator *)mali_mem_block_gobal_allocator;
287
288         if (NULL == info) return 0;
289
290         MALI_DEBUG_ASSERT_POINTER(info);
291
292         return (info->num_blocks - info->free_blocks) * MALI_BLOCK_SIZE;
293 }
294
295 _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size)
296 {
297         mali_mem_allocator *allocator;
298
299         /* Do the low level linux operation first */
300
301         /* Request ownership of the memory */
302         if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(start, size, "Dedicated Mali GPU memory")) {
303                 MALI_DEBUG_PRINT(1, ("Failed to request memory region for frame buffer (0x%08X - 0x%08X)\n", start, start + size - 1));
304                 return _MALI_OSK_ERR_FAULT;
305         }
306
307         /* Create generic block allocator object to handle it */
308         allocator = mali_mem_block_allocator_create(start, 0 /* cpu_usage_adjust */, size);
309
310         if (NULL == allocator) {
311                 MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n"));
312                 _mali_osk_mem_unreqregion(start, size);
313                 MALI_ERROR(_MALI_OSK_ERR_FAULT);
314         }
315
316         mali_mem_block_gobal_allocator = (block_allocator *)allocator;
317
318         return _MALI_OSK_ERR_OK;
319 }