summaryrefslogtreecommitdiff
path: root/drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c')
-rw-r--r--drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c b/drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c
new file mode 100644
index 00000000000..90962597ab5
--- /dev/null
+++ b/drivers/gpu/arm/mali400/r4p0_rel0/linux/mali_memory_block_alloc.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2010-2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "mali_kernel_common.h"
+#include "mali_memory.h"
+#include "mali_memory_block_alloc.h"
+#include "mali_osk.h"
+#include <linux/mutex.h>
+#define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */
+
+struct block_info {
+ struct block_info *next;
+};
+
+typedef struct block_info block_info;
+
+
+typedef struct block_allocator {
+ struct mutex mutex;
+ block_info *all_blocks;
+ block_info *first_free;
+ u32 base;
+ u32 cpu_usage_adjust;
+ u32 num_blocks;
+ u32 free_blocks;
+} block_allocator;
+
+static block_allocator *mali_mem_block_gobal_allocator = NULL;
+
+MALI_STATIC_INLINE u32 get_phys(block_allocator *info, block_info *block)
+{
+ return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE);
+}
+
+mali_mem_allocator *mali_mem_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size)
+{
+ block_allocator *info;
+ u32 usable_size;
+ u32 num_blocks;
+
+ usable_size = size & ~(MALI_BLOCK_SIZE - 1);
+ MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size));
+ MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size));
+ num_blocks = usable_size / MALI_BLOCK_SIZE;
+ MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks));
+
+ if (usable_size == 0) {
+ MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size));
+ return NULL;
+ }
+
+ info = _mali_osk_malloc(sizeof(block_allocator));
+ if (NULL != info) {
+ mutex_init(&info->mutex);
+ info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks);
+ if (NULL != info->all_blocks) {
+ u32 i;
+ info->first_free = NULL;
+ info->num_blocks = num_blocks;
+ info->free_blocks = num_blocks;
+
+ info->base = base_address;
+ info->cpu_usage_adjust = cpu_usage_adjust;
+
+ for ( i = 0; i < num_blocks; i++) {
+ info->all_blocks[i].next = info->first_free;
+ info->first_free = &info->all_blocks[i];
+ }
+
+ return (mali_mem_allocator *)info;
+ }
+ _mali_osk_free(info);
+ }
+
+ return NULL;
+}
+
+void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator)
+{
+ block_allocator *info = (block_allocator*)allocator;
+
+ info = mali_mem_block_gobal_allocator;
+ if (NULL == info) return;
+
+ MALI_DEBUG_ASSERT_POINTER(info);
+
+ _mali_osk_free(info->all_blocks);
+ _mali_osk_free(info);
+}
+
+static void mali_mem_block_mali_map(mali_mem_allocation *descriptor, u32 phys, u32 virt, u32 size)
+{
+ struct mali_page_directory *pagedir = descriptor->session->page_directory;
+ u32 prop = descriptor->mali_mapping.properties;
+ u32 offset = 0;
+
+ while (size) {
+ mali_mmu_pagedir_update(pagedir, virt + offset, phys + offset, MALI_MMU_PAGE_SIZE, prop);
+
+ size -= MALI_MMU_PAGE_SIZE;
+ offset += MALI_MMU_PAGE_SIZE;
+ }
+}
+
+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)
+{
+ u32 virt = vma->vm_start + mapping_offset;
+ u32 cpu_phys = mali_phys + cpu_usage_adjust;
+ u32 offset = 0;
+ int ret;
+
+ while (size) {
+ ret = vm_insert_pfn(vma, virt + offset, __phys_to_pfn(cpu_phys + offset));
+
+ if (unlikely(ret)) {
+ MALI_DEBUG_PRINT(1, ("Block allocator: Failed to insert pfn into vma\n"));
+ return 1;
+ }
+
+ size -= MALI_MMU_PAGE_SIZE;
+ offset += MALI_MMU_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session)
+{
+ _mali_osk_errcode_t err;
+ mali_mem_allocation *descriptor;
+ block_allocator *info;
+ u32 left;
+ block_info *last_allocated = NULL;
+ block_allocator_allocation *ret_allocation;
+ u32 offset = 0;
+
+ size = ALIGN(size, MALI_BLOCK_SIZE);
+
+ info = mali_mem_block_gobal_allocator;
+ if (NULL == info) return NULL;
+
+ left = size;
+ MALI_DEBUG_ASSERT(0 != left);
+
+ descriptor = mali_mem_descriptor_create(session, MALI_MEM_BLOCK);
+ if (NULL == descriptor) {
+ return NULL;
+ }
+
+ descriptor->mali_mapping.addr = mali_addr;
+ descriptor->size = size;
+ descriptor->cpu_mapping.addr = (void __user*)vma->vm_start;
+ descriptor->cpu_mapping.ref = 1;
+
+ if (VM_SHARED == (VM_SHARED & vma->vm_flags)) {
+ descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT;
+ } else {
+ /* Cached Mali memory mapping */
+ descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE;
+ vma->vm_flags |= VM_SHARED;
+ }
+
+ ret_allocation = &descriptor->block_mem.mem;
+
+ ret_allocation->mapping_length = 0;
+
+ _mali_osk_mutex_wait(session->memory_lock);
+ mutex_lock(&info->mutex);
+
+ if (left > (info->free_blocks * MALI_BLOCK_SIZE)) {
+ MALI_DEBUG_PRINT(2, ("Mali block allocator: not enough free blocks to service allocation (%u)\n", left));
+ mutex_unlock(&info->mutex);
+ _mali_osk_mutex_signal(session->memory_lock);
+ mali_mem_descriptor_destroy(descriptor);
+ return NULL;
+ }
+
+ err = mali_mem_mali_map_prepare(descriptor);
+ if (_MALI_OSK_ERR_OK != err) {
+ mutex_unlock(&info->mutex);
+ _mali_osk_mutex_signal(session->memory_lock);
+ mali_mem_descriptor_destroy(descriptor);
+ return NULL;
+ }
+
+ while ((left > 0) && (info->first_free)) {
+ block_info *block;
+ u32 phys_addr;
+ u32 current_mapping_size;
+
+ block = info->first_free;
+ info->first_free = info->first_free->next;
+ block->next = last_allocated;
+ last_allocated = block;
+
+ phys_addr = get_phys(info, block);
+
+ if (MALI_BLOCK_SIZE < left) {
+ current_mapping_size = MALI_BLOCK_SIZE;
+ } else {
+ current_mapping_size = left;
+ }
+
+ mali_mem_block_mali_map(descriptor, phys_addr, mali_addr + offset, current_mapping_size);
+ if (mali_mem_block_cpu_map(descriptor, vma, phys_addr, offset, current_mapping_size, info->cpu_usage_adjust)) {
+ /* release all memory back to the pool */
+ while (last_allocated) {
+ /* This relinks every block we've just allocated back into the free-list */
+ block = last_allocated->next;
+ last_allocated->next = info->first_free;
+ info->first_free = last_allocated;
+ last_allocated = block;
+ }
+
+ mutex_unlock(&info->mutex);
+ _mali_osk_mutex_signal(session->memory_lock);
+
+ mali_mem_mali_map_free(descriptor);
+ mali_mem_descriptor_destroy(descriptor);
+
+ return NULL;
+ }
+
+ left -= current_mapping_size;
+ offset += current_mapping_size;
+ ret_allocation->mapping_length += current_mapping_size;
+
+ --info->free_blocks;
+ }
+
+ mutex_unlock(&info->mutex);
+ _mali_osk_mutex_signal(session->memory_lock);
+
+ MALI_DEBUG_ASSERT(0 == left);
+
+ /* Record all the information about this allocation */
+ ret_allocation->last_allocated = last_allocated;
+ ret_allocation->info = info;
+
+ return descriptor;
+}
+
+void mali_mem_block_release(mali_mem_allocation *descriptor)
+{
+ block_allocator *info = descriptor->block_mem.mem.info;
+ block_info *block, *next;
+ block_allocator_allocation *allocation = &descriptor->block_mem.mem;
+
+ MALI_DEBUG_ASSERT(MALI_MEM_BLOCK == descriptor->type);
+
+ block = allocation->last_allocated;
+
+ MALI_DEBUG_ASSERT_POINTER(block);
+
+ /* unmap */
+ mali_mem_mali_map_free(descriptor);
+
+ mutex_lock(&info->mutex);
+
+ while (block) {
+ MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
+
+ next = block->next;
+
+ /* relink into free-list */
+ block->next = info->first_free;
+ info->first_free = block;
+
+ /* advance the loop */
+ block = next;
+
+ ++info->free_blocks;
+ }
+
+ mutex_unlock(&info->mutex);
+}
+
+u32 mali_mem_block_allocator_stat(void)
+{
+ block_allocator *info = (block_allocator *)mali_mem_block_gobal_allocator;
+
+ if (NULL == info) return 0;
+
+ MALI_DEBUG_ASSERT_POINTER(info);
+
+ return (info->num_blocks - info->free_blocks) * MALI_BLOCK_SIZE;
+}
+
+_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size)
+{
+ mali_mem_allocator *allocator;
+
+ /* Do the low level linux operation first */
+
+ /* Request ownership of the memory */
+ if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(start, size, "Dedicated Mali GPU memory")) {
+ MALI_DEBUG_PRINT(1, ("Failed to request memory region for frame buffer (0x%08X - 0x%08X)\n", start, start + size - 1));
+ return _MALI_OSK_ERR_FAULT;
+ }
+
+ /* Create generic block allocator object to handle it */
+ allocator = mali_mem_block_allocator_create(start, 0 /* cpu_usage_adjust */, size);
+
+ if (NULL == allocator) {
+ MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n"));
+ _mali_osk_mem_unreqregion(start, size);
+ MALI_ERROR(_MALI_OSK_ERR_FAULT);
+ }
+
+ mali_mem_block_gobal_allocator = (block_allocator*)allocator;
+
+ return _MALI_OSK_ERR_OK;
+}