diff options
author | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-12-06 17:07:15 +0100 |
---|---|---|
committer | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-12-06 17:07:15 +0100 |
commit | 68ee93f3d058ff58f994d08be00d3aad9ec20970 (patch) | |
tree | 5fb2d14340332062e416246c5759adcaa64a2340 /drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c | |
parent | 17021fd920fb5bc97a8c81a20b208c5b38f1b0e8 (diff) | |
parent | b99374450c03bf5081b88995d91d34fb9b2fd040 (diff) | |
download | kernel_samsung_smdk4412-68ee93f3d058ff58f994d08be00d3aad9ec20970.zip kernel_samsung_smdk4412-68ee93f3d058ff58f994d08be00d3aad9ec20970.tar.gz kernel_samsung_smdk4412-68ee93f3d058ff58f994d08be00d3aad9ec20970.tar.bz2 |
Merge branch 'master' of fossencdi.org:kernel_samsung_smdk4412
Diffstat (limited to 'drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c')
-rw-r--r-- | drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c b/drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c new file mode 100644 index 0000000..c34b2c6 --- /dev/null +++ b/drivers/gpu/mali400/r3p2/mali/linux/mali_dma_buf.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 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 <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ +#include <linux/dma-buf.h> +#include <linux/scatterlist.h> +#include <linux/rbtree.h> +#include <linux/platform_device.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/mutex.h> + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_kernel_linux.h" + +#include "mali_kernel_memory_engine.h" +#include "mali_memory.h" +#include "mali_dma_buf.h" + + +struct mali_dma_buf_attachment { + struct dma_buf *buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + struct mali_session_data *session; + int map_ref; + struct mutex map_lock; + mali_bool is_mapped; + wait_queue_head_t wait_queue; +}; + +void mali_dma_buf_release(void *ctx, void *handle) +{ + struct mali_dma_buf_attachment *mem; + + mem = (struct mali_dma_buf_attachment *)handle; + + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release attachment %p\n", mem)); + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(mem->attachment); + MALI_DEBUG_ASSERT_POINTER(mem->buf); + +#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + /* We mapped implicitly on attach, so we need to unmap on release */ + mali_dma_buf_unmap(mem); +#endif + + /* Wait for buffer to become unmapped */ + wait_event(mem->wait_queue, !mem->is_mapped); + MALI_DEBUG_ASSERT(!mem->is_mapped); + + dma_buf_detach(mem->buf, mem->attachment); + dma_buf_put(mem->buf); + + _mali_osk_free(mem); +} + +/* + * Map DMA buf attachment \a mem into \a session at virtual address \a virt. + */ +int mali_dma_buf_map(struct mali_dma_buf_attachment *mem, struct mali_session_data *session, u32 virt, u32 *offset, u32 flags) +{ + struct mali_page_directory *pagedir; + struct scatterlist *sg; + int i; + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT(mem->session == session); + + mutex_lock(&mem->map_lock); + + mem->map_ref++; + + MALI_DEBUG_PRINT(5, ("Mali DMA-buf: map attachment %p, new map_ref = %d\n", mem, mem->map_ref)); + + if (1 == mem->map_ref) + { + /* First reference taken, so we need to map the dma buf */ + MALI_DEBUG_ASSERT(!mem->is_mapped); + + pagedir = mali_session_get_page_directory(session); + MALI_DEBUG_ASSERT_POINTER(pagedir); + + mem->sgt = dma_buf_map_attachment(mem->attachment, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(mem->sgt)) + { + MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf attachment\n")); + return -EFAULT; + } + + for_each_sg(mem->sgt->sgl, sg, mem->sgt->nents, i) + { + u32 size = sg_dma_len(sg); + dma_addr_t phys = sg_dma_address(sg); + + /* sg must be page aligned. */ + MALI_DEBUG_ASSERT(0 == size % MALI_MMU_PAGE_SIZE); + + mali_mmu_pagedir_update(pagedir, virt, phys, size, MALI_CACHE_STANDARD); + + virt += size; + *offset += size; + } + + if (flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + u32 guard_phys; + MALI_DEBUG_PRINT(7, ("Mapping in extra guard page\n")); + + guard_phys = sg_dma_address(mem->sgt->sgl); + mali_mmu_pagedir_update(pagedir, virt, guard_phys, MALI_MMU_PAGE_SIZE, MALI_CACHE_STANDARD); + } + + mem->is_mapped = MALI_TRUE; + mutex_unlock(&mem->map_lock); + + /* Wake up any thread waiting for buffer to become mapped */ + wake_up_all(&mem->wait_queue); + } + else + { + MALI_DEBUG_ASSERT(mem->is_mapped); + mutex_unlock(&mem->map_lock); + } + + return 0; +} + +void mali_dma_buf_unmap(struct mali_dma_buf_attachment *mem) +{ + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(mem->attachment); + MALI_DEBUG_ASSERT_POINTER(mem->buf); + + mutex_lock(&mem->map_lock); + + mem->map_ref--; + + MALI_DEBUG_PRINT(5, ("Mali DMA-buf: unmap attachment %p, new map_ref = %d\n", mem, mem->map_ref)); + + if (0 == mem->map_ref) + { + dma_buf_unmap_attachment(mem->attachment, mem->sgt, DMA_BIDIRECTIONAL); + + mem->is_mapped = MALI_FALSE; + } + + mutex_unlock(&mem->map_lock); + + /* Wake up any thread waiting for buffer to become unmapped */ + wake_up_all(&mem->wait_queue); +} + +#if !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +int mali_dma_buf_map_job(struct mali_pp_job *job) +{ + mali_memory_allocation *descriptor; + struct mali_dma_buf_attachment *mem; + _mali_osk_errcode_t err; + int i; + u32 offset = 0; + int ret = 0; + + _mali_osk_lock_wait( job->session->memory_lock, _MALI_OSK_LOCKMODE_RW ); + + for (i = 0; i < job->num_memory_cookies; i++) + { + int cookie = job->memory_cookies[i]; + + if (0 == cookie) + { + /* 0 is not a valid cookie */ + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + MALI_DEBUG_ASSERT(0 < cookie); + + err = mali_descriptor_mapping_get(job->session->descriptor_mapping, + cookie, (void**)&descriptor); + + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to get descriptor for cookie %d\n", cookie)); + ret = -EFAULT; + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + if (mali_dma_buf_release != descriptor->physical_allocation.release) + { + /* Not a DMA-buf */ + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + mem = (struct mali_dma_buf_attachment *)descriptor->physical_allocation.handle; + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT(mem->session == job->session); + + err = mali_dma_buf_map(mem, mem->session, descriptor->mali_address, &offset, descriptor->flags); + if (0 != err) + { + MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to map dma-buf for cookie %d at mali address %x\b", + cookie, descriptor->mali_address)); + ret = -EFAULT; + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + /* Add mem to list of DMA-bufs mapped for this job */ + job->dma_bufs[i] = mem; + } + + _mali_osk_lock_signal( job->session->memory_lock, _MALI_OSK_LOCKMODE_RW ); + + return ret; +} + +void mali_dma_buf_unmap_job(struct mali_pp_job *job) +{ + int i; + for (i = 0; i < job->num_dma_bufs; i++) + { + if (NULL == job->dma_bufs[i]) continue; + + mali_dma_buf_unmap(job->dma_bufs[i]); + job->dma_bufs[i] = NULL; + } +} +#endif /* !CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH */ + +/* Callback from memory engine which will map into Mali virtual address space */ +static mali_physical_memory_allocation_result mali_dma_buf_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + struct mali_session_data *session; + struct mali_dma_buf_attachment *mem; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(offset); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + /* Mapping dma-buf with an offset is not supported. */ + MALI_DEBUG_ASSERT(0 == *offset); + + session = (struct mali_session_data *)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session); + + mem = (struct mali_dma_buf_attachment *)ctx; + + MALI_DEBUG_ASSERT(mem->session == session); + +#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + if (0 == mali_dma_buf_map(mem, session, descriptor->mali_address, offset, descriptor->flags)) + { + MALI_DEBUG_ASSERT(*offset == descriptor->size); +#else + { +#endif + alloc_info->ctx = NULL; + alloc_info->handle = mem; + alloc_info->next = NULL; + alloc_info->release = mali_dma_buf_release; + + return MALI_MEM_ALLOC_FINISHED; + } + + return MALI_MEM_ALLOC_INTERNAL_FAILURE; +} + +int mali_attach_dma_buf(struct mali_session_data *session, _mali_uk_attach_dma_buf_s __user *user_arg) +{ + mali_physical_memory_allocator external_memory_allocator; + struct dma_buf *buf; + struct mali_dma_buf_attachment *mem; + _mali_uk_attach_dma_buf_s args; + mali_memory_allocation *descriptor; + int md; + int fd; + + /* Get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if (0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_attach_dma_buf_s))) + { + return -EFAULT; + } + + fd = args.mem_fd; + + buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(buf)) + { + MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd)); + return PTR_RET(buf); + } + + /* Currently, mapping of the full buffer are supported. */ + if (args.size != buf->size) + { + MALI_DEBUG_PRINT_ERROR(("dma-buf size doesn't match mapping size.\n")); + dma_buf_put(buf); + return -EINVAL; + } + + mem = _mali_osk_calloc(1, sizeof(struct mali_dma_buf_attachment)); + if (NULL == mem) + { + MALI_DEBUG_PRINT_ERROR(("Failed to allocate dma-buf tracing struct\n")); + dma_buf_put(buf); + return -ENOMEM; + } + + mem->buf = buf; + mem->session = session; + mem->map_ref = 0; + mutex_init(&mem->map_lock); + init_waitqueue_head(&mem->wait_queue); + + mem->attachment = dma_buf_attach(mem->buf, &mali_platform_device->dev); + if (NULL == mem->attachment) + { + MALI_DEBUG_PRINT_ERROR(("Failed to attach to dma-buf %d\n", fd)); + dma_buf_put(mem->buf); + _mali_osk_free(mem); + return -EFAULT; + } + + /* Map dma-buf into this session's page tables */ + + /* Set up Mali memory descriptor */ + descriptor = _mali_osk_calloc(1, sizeof(mali_memory_allocation)); + if (NULL == descriptor) + { + MALI_DEBUG_PRINT_ERROR(("Failed to allocate descriptor dma-buf %d\n", fd)); + mali_dma_buf_release(NULL, mem); + return -ENOMEM; + } + + descriptor->size = args.size; + descriptor->mapping = NULL; + descriptor->mali_address = args.mali_address; + descriptor->mali_addr_mapping_info = (void*)session; + descriptor->process_addr_mapping_info = NULL; /* do not map to process address space */ + descriptor->lock = session->memory_lock; + + if (args.flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) + { + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE; + } + _mali_osk_list_init( &descriptor->list ); + + /* Get descriptor mapping for memory. */ + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) + { + MALI_DEBUG_PRINT_ERROR(("Failed to create descriptor mapping for dma-buf %d\n", fd)); + _mali_osk_free(descriptor); + mali_dma_buf_release(NULL, mem); + return -EFAULT; + } + + MALI_DEBUG_ASSERT(0 < md); + + external_memory_allocator.allocate = mali_dma_buf_commit; + external_memory_allocator.allocate_page_table_block = NULL; + external_memory_allocator.ctx = mem; + external_memory_allocator.name = "DMA-BUF Memory"; + external_memory_allocator.next = NULL; + + /* Map memory into session's Mali virtual address space. */ + _mali_osk_lock_wait(session->memory_lock, _MALI_OSK_LOCKMODE_RW); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_memory(mali_mem_get_memory_engine(), descriptor, &external_memory_allocator, NULL)) + { + _mali_osk_lock_signal(session->memory_lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf %d into Mali address space\n", fd)); + mali_descriptor_mapping_free(session->descriptor_mapping, md); + mali_dma_buf_release(NULL, mem); + return -ENOMEM; + } + _mali_osk_lock_signal(session->memory_lock, _MALI_OSK_LOCKMODE_RW); + + /* Return stuff to user space */ + if (0 != put_user(md, &user_arg->cookie)) + { + /* Roll back */ + MALI_DEBUG_PRINT_ERROR(("Failed to return descriptor to user space for dma-buf %d\n", fd)); + mali_descriptor_mapping_free(session->descriptor_mapping, md); + mali_dma_buf_release(NULL, mem); + return -EFAULT; + } + + return 0; +} + +int mali_release_dma_buf(struct mali_session_data *session, _mali_uk_release_dma_buf_s __user *user_arg) +{ + int ret = 0; + _mali_uk_release_dma_buf_s args; + mali_memory_allocation *descriptor; + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_release_dma_buf_s)) ) + { + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release descriptor cookie %d\n", args.cookie)); + + _mali_osk_lock_wait( session->memory_lock, _MALI_OSK_LOCKMODE_RW ); + + descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args.cookie); + + if (NULL != descriptor) + { + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: Releasing dma-buf at mali address %x\n", descriptor->mali_address)); + + /* Will call back to mali_dma_buf_release() which will release the dma-buf attachment. */ + mali_allocation_engine_release_memory(mali_mem_get_memory_engine(), descriptor); + + _mali_osk_free(descriptor); + } + else + { + MALI_DEBUG_PRINT_ERROR(("Invalid memory descriptor %d used to release dma-buf\n", args.cookie)); + ret = -EINVAL; + } + + _mali_osk_lock_signal( session->memory_lock, _MALI_OSK_LOCKMODE_RW ); + + /* Return the error that _mali_ukk_map_external_ump_mem produced */ + return ret; +} + +int mali_dma_buf_get_size(struct mali_session_data *session, _mali_uk_dma_buf_get_size_s __user *user_arg) +{ + _mali_uk_dma_buf_get_size_s args; + int fd; + struct dma_buf *buf; + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_dma_buf_get_size_s)) ) + { + return -EFAULT; + } + + /* Do DMA-BUF stuff */ + fd = args.mem_fd; + + buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(buf)) + { + MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd)); + return PTR_RET(buf); + } + + if (0 != put_user(buf->size, &user_arg->size)) + { + dma_buf_put(buf); + return -EFAULT; + } + + dma_buf_put(buf); + + return 0; +} |