summaryrefslogtreecommitdiff
path: root/drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c')
-rw-r--r--drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c b/drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c
new file mode 100644
index 000000000000..453ddda3080f
--- /dev/null
+++ b/drivers/gpu/arm/utgard/linux/mali_memory_block_alloc.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010-2015 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>
+
+
+static mali_block_allocator *mali_mem_block_gobal_allocator = NULL;
+
+unsigned long _mali_blk_item_get_phy_addr(mali_block_item *item)
+{
+ return (item->phy_addr & ~(MALI_BLOCK_REF_MASK));
+}
+
+
+unsigned long _mali_blk_item_get_pfn(mali_block_item *item)
+{
+ return (item->phy_addr / MALI_BLOCK_SIZE);
+}
+
+
+u32 mali_mem_block_get_ref_count(mali_page_node *node)
+{
+ MALI_DEBUG_ASSERT(node->type == MALI_PAGE_NODE_BLOCK);
+ return (node->blk_it->phy_addr & MALI_BLOCK_REF_MASK);
+}
+
+
+/* Increase the refence count
+* It not atomic, so it need to get sp_lock before call this function
+*/
+
+u32 mali_mem_block_add_ref(mali_page_node *node)
+{
+ MALI_DEBUG_ASSERT(node->type == MALI_PAGE_NODE_BLOCK);
+ MALI_DEBUG_ASSERT(mali_mem_block_get_ref_count(node) < MALI_BLOCK_MAX_REF_COUNT);
+ return (node->blk_it->phy_addr++ & MALI_BLOCK_REF_MASK);
+}
+
+/* Decase the refence count
+* It not atomic, so it need to get sp_lock before call this function
+*/
+u32 mali_mem_block_dec_ref(mali_page_node *node)
+{
+ MALI_DEBUG_ASSERT(node->type == MALI_PAGE_NODE_BLOCK);
+ MALI_DEBUG_ASSERT(mali_mem_block_get_ref_count(node) > 0);
+ return (node->blk_it->phy_addr-- & MALI_BLOCK_REF_MASK);
+}
+
+
+static mali_block_allocator *mali_mem_block_allocator_create(u32 base_address, u32 size)
+{
+ mali_block_allocator *info;
+ u32 usable_size;
+ u32 num_blocks;
+ mali_page_node *m_node;
+ mali_block_item *mali_blk_items = NULL;
+ int i = 0;
+
+ 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_calloc(1, sizeof(mali_block_allocator));
+ if (NULL != info) {
+ INIT_LIST_HEAD(&info->free);
+ spin_lock_init(&info->sp_lock);
+ info->total_num = num_blocks;
+ mali_blk_items = _mali_osk_calloc(1, sizeof(mali_block_item) * num_blocks);
+
+ if (mali_blk_items) {
+ info->items = mali_blk_items;
+ /* add blocks(4k size) to free list*/
+ for (i = 0 ; i < num_blocks ; i++) {
+ /* add block information*/
+ mali_blk_items[i].phy_addr = base_address + (i * MALI_BLOCK_SIZE);
+ /* add to free list */
+ m_node = _mali_page_node_allocate(MALI_PAGE_NODE_BLOCK);
+ if (m_node == NULL)
+ goto fail;
+ _mali_page_node_add_block_item(m_node, &(mali_blk_items[i]));
+ list_add_tail(&m_node->list, &info->free);
+ atomic_add(1, &info->free_num);
+ }
+ return info;
+ }
+ }
+fail:
+ mali_mem_block_allocator_destroy();
+ return NULL;
+}
+
+void mali_mem_block_allocator_destroy(void)
+{
+ struct mali_page_node *m_page, *m_tmp;
+ mali_block_allocator *info = mali_mem_block_gobal_allocator;
+ MALI_DEBUG_ASSERT_POINTER(info);
+ MALI_DEBUG_PRINT(4, ("Memory block destroy !\n"));
+
+ if (NULL == info)
+ return;
+
+ list_for_each_entry_safe(m_page, m_tmp , &info->free, list) {
+ MALI_DEBUG_ASSERT(m_page->type == MALI_PAGE_NODE_BLOCK);
+ list_del(&m_page->list);
+ kfree(m_page);
+ }
+
+ _mali_osk_free(info->items);
+ _mali_osk_free(info);
+}
+
+u32 mali_mem_block_release(mali_mem_backend *mem_bkend)
+{
+ mali_mem_allocation *alloc = mem_bkend->mali_allocation;
+ u32 free_pages_nr = 0;
+ MALI_DEBUG_ASSERT(mem_bkend->type == MALI_MEM_BLOCK);
+
+ /* Unmap the memory from the mali virtual address space. */
+ mali_mem_block_mali_unmap(alloc);
+ mutex_lock(&mem_bkend->mutex);
+ free_pages_nr = mali_mem_block_free(&mem_bkend->block_mem);
+ mutex_unlock(&mem_bkend->mutex);
+ return free_pages_nr;
+}
+
+
+int mali_mem_block_alloc(mali_mem_block_mem *block_mem, u32 size)
+{
+ struct mali_page_node *m_page, *m_tmp;
+ size_t page_count = PAGE_ALIGN(size) / _MALI_OSK_MALI_PAGE_SIZE;
+ mali_block_allocator *info = mali_mem_block_gobal_allocator;
+ MALI_DEBUG_ASSERT_POINTER(info);
+
+ MALI_DEBUG_PRINT(4, ("BLOCK Mem: Allocate size = 0x%x\n", size));
+ /*do some init */
+ INIT_LIST_HEAD(&block_mem->pfns);
+
+ spin_lock(&info->sp_lock);
+ /*check if have enough space*/
+ if (atomic_read(&info->free_num) > page_count) {
+ list_for_each_entry_safe(m_page, m_tmp , &info->free, list) {
+ if (page_count > 0) {
+ MALI_DEBUG_ASSERT(m_page->type == MALI_PAGE_NODE_BLOCK);
+ MALI_DEBUG_ASSERT(mali_mem_block_get_ref_count(m_page) == 0);
+ list_move(&m_page->list, &block_mem->pfns);
+ block_mem->count++;
+ atomic_dec(&info->free_num);
+ _mali_page_node_ref(m_page);
+ } else {
+ break;
+ }
+ page_count--;
+ }
+ } else {
+ /* can't allocate from BLOCK memory*/
+ spin_unlock(&info->sp_lock);
+ return -1;
+ }
+
+ spin_unlock(&info->sp_lock);
+ return 0;
+}
+
+u32 mali_mem_block_free(mali_mem_block_mem *block_mem)
+{
+ u32 free_pages_nr = 0;
+
+ free_pages_nr = mali_mem_block_free_list(&block_mem->pfns);
+ MALI_DEBUG_PRINT(4, ("BLOCK Mem free : allocated size = 0x%x, free size = 0x%x\n", block_mem->count * _MALI_OSK_MALI_PAGE_SIZE,
+ free_pages_nr * _MALI_OSK_MALI_PAGE_SIZE));
+ block_mem->count = 0;
+ MALI_DEBUG_ASSERT(list_empty(&block_mem->pfns));
+
+ return free_pages_nr;
+}
+
+
+u32 mali_mem_block_free_list(struct list_head *list)
+{
+ struct mali_page_node *m_page, *m_tmp;
+ mali_block_allocator *info = mali_mem_block_gobal_allocator;
+ u32 free_pages_nr = 0;
+
+ if (info) {
+ spin_lock(&info->sp_lock);
+ list_for_each_entry_safe(m_page, m_tmp , list, list) {
+ if (1 == _mali_page_node_get_ref_count(m_page)) {
+ free_pages_nr++;
+ }
+ mali_mem_block_free_node(m_page);
+ }
+ spin_unlock(&info->sp_lock);
+ }
+ return free_pages_nr;
+}
+
+/* free the node,*/
+void mali_mem_block_free_node(struct mali_page_node *node)
+{
+ mali_block_allocator *info = mali_mem_block_gobal_allocator;
+
+ /* only handle BLOCK node */
+ if (node->type == MALI_PAGE_NODE_BLOCK && info) {
+ /*Need to make this atomic?*/
+ if (1 == _mali_page_node_get_ref_count(node)) {
+ /*Move to free list*/
+ _mali_page_node_unref(node);
+ list_move_tail(&node->list, &info->free);
+ atomic_add(1, &info->free_num);
+ } else {
+ _mali_page_node_unref(node);
+ list_del(&node->list);
+ kfree(node);
+ }
+ }
+}
+
+/* unref the node, but not free it */
+_mali_osk_errcode_t mali_mem_block_unref_node(struct mali_page_node *node)
+{
+ mali_block_allocator *info = mali_mem_block_gobal_allocator;
+ mali_page_node *new_node;
+
+ /* only handle BLOCK node */
+ if (node->type == MALI_PAGE_NODE_BLOCK && info) {
+ /*Need to make this atomic?*/
+ if (1 == _mali_page_node_get_ref_count(node)) {
+ /* allocate a new node, Add to free list, keep the old node*/
+ _mali_page_node_unref(node);
+ new_node = _mali_page_node_allocate(MALI_PAGE_NODE_BLOCK);
+ if (new_node) {
+ memcpy(new_node, node, sizeof(mali_page_node));
+ list_add(&new_node->list, &info->free);
+ atomic_add(1, &info->free_num);
+ } else
+ return _MALI_OSK_ERR_FAULT;
+
+ } else {
+ _mali_page_node_unref(node);
+ }
+ }
+ return _MALI_OSK_ERR_OK;
+}
+
+
+int mali_mem_block_mali_map(mali_mem_block_mem *block_mem, struct mali_session_data *session, u32 vaddr, u32 props)
+{
+ struct mali_page_directory *pagedir = session->page_directory;
+ struct mali_page_node *m_page;
+ dma_addr_t phys;
+ u32 virt = vaddr;
+ u32 prop = props;
+
+ list_for_each_entry(m_page, &block_mem->pfns, list) {
+ MALI_DEBUG_ASSERT(m_page->type == MALI_PAGE_NODE_BLOCK);
+ phys = _mali_page_node_get_dma_addr(m_page);
+#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT)
+ /* Verify that the "physical" address is 32-bit and
+ * usable for Mali, when on a system with bus addresses
+ * wider than 32-bit. */
+ MALI_DEBUG_ASSERT(0 == (phys >> 32));
+#endif
+ mali_mmu_pagedir_update(pagedir, virt, (mali_dma_addr)phys, MALI_MMU_PAGE_SIZE, prop);
+ virt += MALI_MMU_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+void mali_mem_block_mali_unmap(mali_mem_allocation *alloc)
+{
+ struct mali_session_data *session;
+ MALI_DEBUG_ASSERT_POINTER(alloc);
+ session = alloc->session;
+ MALI_DEBUG_ASSERT_POINTER(session);
+
+ mali_session_memory_lock(session);
+ mali_mem_mali_map_free(session, alloc->psize, alloc->mali_vma_node.vm_node.start,
+ alloc->flags);
+ mali_session_memory_unlock(session);
+}
+
+
+int mali_mem_block_cpu_map(mali_mem_backend *mem_bkend, struct vm_area_struct *vma)
+{
+ int ret;
+ mali_mem_block_mem *block_mem = &mem_bkend->block_mem;
+ unsigned long addr = vma->vm_start;
+ struct mali_page_node *m_page;
+ MALI_DEBUG_ASSERT(mem_bkend->type == MALI_MEM_BLOCK);
+
+ list_for_each_entry(m_page, &block_mem->pfns, list) {
+ MALI_DEBUG_ASSERT(m_page->type == MALI_PAGE_NODE_BLOCK);
+ ret = vm_insert_pfn(vma, addr, _mali_page_node_get_pfn(m_page));
+
+ if (unlikely(0 != ret)) {
+ return -EFAULT;
+ }
+ addr += _MALI_OSK_MALI_PAGE_SIZE;
+
+ }
+
+ return 0;
+}
+
+
+_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size)
+{
+ mali_block_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, 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 = (mali_block_allocator *)allocator;
+
+ return _MALI_OSK_ERR_OK;
+}
+
+mali_bool mali_memory_have_dedicated_memory(void)
+{
+ return mali_mem_block_gobal_allocator ? MALI_TRUE : MALI_FALSE;
+}
+
+u32 mali_mem_block_allocator_stat(void)
+{
+ mali_block_allocator *allocator = mali_mem_block_gobal_allocator;
+ MALI_DEBUG_ASSERT_POINTER(allocator);
+
+ return (allocator->total_num - atomic_read(&allocator->free_num)) * _MALI_OSK_MALI_PAGE_SIZE;
+}