blob: beff5f88d1602563f402bee8a818764316234d52 [file] [log] [blame]
/*
* Copyright (C) 2012-2016 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 <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0)
#include <linux/uaccess.h>
#else
#include <asm/uaccess.h>
#endif
#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_memory.h"
#include "mali_memory_dma_buf.h"
#include "mali_memory_virtual.h"
#include "mali_pp_job.h"
/*
* Map DMA buf attachment \a mem into \a session at virtual address \a virt.
*/
static int mali_dma_buf_map(mali_mem_backend *mem_backend)
{
mali_mem_allocation *alloc;
struct mali_dma_buf_attachment *mem;
struct mali_session_data *session;
struct mali_page_directory *pagedir;
_mali_osk_errcode_t err;
struct scatterlist *sg;
u32 virt, flags, unmap_dma_size;
int i;
MALI_DEBUG_ASSERT_POINTER(mem_backend);
alloc = mem_backend->mali_allocation;
MALI_DEBUG_ASSERT_POINTER(alloc);
mem = mem_backend->dma_buf.attachment;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(mem->buf);
unmap_dma_size = mem->buf->size;
session = alloc->session;
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT(mem->session == session);
virt = alloc->mali_vma_node.vm_node.start;
flags = alloc->flags;
mali_session_memory_lock(session);
mem->map_ref++;
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: map attachment %p, new map_ref = %d\n", mem, mem->map_ref));
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: is_mapped %d, new map_ref = %d\n", mem->is_mapped, mem_backend->flags));
#if (!defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)) && (defined(CONFIG_MALI_DMA_BUF_LAZY_MAP))
if (MALI_FALSE == mem->is_mapped)
#else
if (1 == mem->map_ref || (MALI_FALSE == mem->is_mapped &&
mem_backend->flags & MALI_MEM_BACKEND_FLAG_VIDEO_LAZY_MAP))
#endif
{
/* First reference taken, so we need to map the dma buf */
MALI_DEBUG_ASSERT(!mem->is_mapped);
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"));
mem->map_ref--;
mali_session_memory_unlock(session);
return -EFAULT;
}
err = mali_mem_mali_map_prepare(alloc);
if (_MALI_OSK_ERR_OK != err) {
MALI_DEBUG_PRINT(1, ("Mapping of DMA memory failed\n"));
mem->map_ref--;
mali_session_memory_unlock(session);
return -ENOMEM;
}
pagedir = mali_session_get_page_directory(session);
MALI_DEBUG_ASSERT_POINTER(pagedir);
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);
unmap_dma_size -= size;
/* sg must be page aligned. */
MALI_DEBUG_ASSERT(0 == size % MALI_MMU_PAGE_SIZE);
MALI_DEBUG_ASSERT(0 == (phys & ~(uintptr_t)0xFFFFFFFF));
mali_mmu_pagedir_update(pagedir, virt, phys, size, MALI_MMU_FLAGS_DEFAULT);
virt += size;
}
if (flags & MALI_MEM_FLAG_MALI_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_MMU_FLAGS_DEFAULT);
}
mem->is_mapped = MALI_TRUE;
if (0 != unmap_dma_size) {
MALI_DEBUG_PRINT_ERROR(("The dma buf size isn't equal to the total scatterlists' dma length.\n"));
mali_session_memory_unlock(session);
return -EFAULT;
}
/* Wake up any thread waiting for buffer to become mapped */
wake_up_all(&mem->wait_queue);
mali_session_memory_unlock(session);
} else {
MALI_DEBUG_ASSERT(mem->is_mapped);
mali_session_memory_unlock(session);
}
return 0;
}
static void mali_dma_buf_unmap(mali_mem_allocation *alloc, struct mali_dma_buf_attachment *mem)
{
MALI_DEBUG_ASSERT_POINTER(alloc);
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(mem->attachment);
MALI_DEBUG_ASSERT_POINTER(mem->buf);
MALI_DEBUG_ASSERT_POINTER(alloc->session);
mali_session_memory_lock(alloc->session);
mem->map_ref--;
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: unmap attachment %p, new map_ref = %d\n", mem, mem->map_ref));
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: unmap is_mapped %d\n", mem->is_mapped));
if (0 == mem->map_ref) {
if (NULL != mem->sgt) {
dma_buf_unmap_attachment(mem->attachment, mem->sgt, DMA_BIDIRECTIONAL);
mem->sgt = NULL;
}
if (MALI_TRUE == mem->is_mapped) {
mali_mem_mali_map_free(alloc->session, alloc->psize, alloc->mali_vma_node.vm_node.start,
alloc->flags);
}
mem->is_mapped = MALI_FALSE;
}
/* Wake up any thread waiting for buffer to become unmapped */
wake_up_all(&mem->wait_queue);
mali_session_memory_unlock(alloc->session);
}
#if 1//!defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
int mali_dma_buf_map_job(struct mali_pp_job *job)
{
struct mali_dma_buf_attachment *mem;
_mali_osk_errcode_t err;
int i;
int ret = 0;
u32 num_memory_cookies;
struct mali_session_data *session;
struct mali_vma_node *mali_vma_node = NULL;
mali_mem_allocation *mali_alloc = NULL;
mali_mem_backend *mem_bkend = NULL;
MALI_DEBUG_ASSERT_POINTER(job);
num_memory_cookies = mali_pp_job_num_memory_cookies(job);
session = mali_pp_job_get_session(job);
MALI_DEBUG_ASSERT_POINTER(session);
for (i = 0; i < num_memory_cookies; i++) {
u32 mali_addr = mali_pp_job_get_memory_cookie(job, i);
mali_vma_node = mali_vma_offset_search(&session->allocation_mgr, mali_addr, 0);
MALI_DEBUG_ASSERT(NULL != mali_vma_node);
mali_alloc = container_of(mali_vma_node, struct mali_mem_allocation, mali_vma_node);
MALI_DEBUG_ASSERT(NULL != mali_alloc);
if (MALI_MEM_DMA_BUF != mali_alloc->type) {
continue;
}
/* Get backend memory & Map on CPU */
mutex_lock(&mali_idr_mutex);
mem_bkend = idr_find(&mali_backend_idr, mali_alloc->backend_handle);
mutex_unlock(&mali_idr_mutex);
MALI_DEBUG_ASSERT(NULL != mem_bkend);
mem = mem_bkend->dma_buf.attachment;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT(mem->session == mali_pp_job_get_session(job));
err = mali_dma_buf_map(mem_bkend);
if (0 != err) {
MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to map dma-buf for mali address %x\n", mali_addr));
ret = -EFAULT;
continue;
}
}
return ret;
}
void mali_dma_buf_unmap_job(struct mali_pp_job *job)
{
struct mali_dma_buf_attachment *mem;
int i;
u32 num_memory_cookies;
struct mali_session_data *session;
struct mali_vma_node *mali_vma_node = NULL;
mali_mem_allocation *mali_alloc = NULL;
mali_mem_backend *mem_bkend = NULL;
MALI_DEBUG_ASSERT_POINTER(job);
num_memory_cookies = mali_pp_job_num_memory_cookies(job);
session = mali_pp_job_get_session(job);
MALI_DEBUG_ASSERT_POINTER(session);
for (i = 0; i < num_memory_cookies; i++) {
u32 mali_addr = mali_pp_job_get_memory_cookie(job, i);
mali_vma_node = mali_vma_offset_search(&session->allocation_mgr, mali_addr, 0);
MALI_DEBUG_ASSERT(NULL != mali_vma_node);
mali_alloc = container_of(mali_vma_node, struct mali_mem_allocation, mali_vma_node);
MALI_DEBUG_ASSERT(NULL != mali_alloc);
if (MALI_MEM_DMA_BUF != mali_alloc->type) {
continue;
}
/* Get backend memory & Map on CPU */
mutex_lock(&mali_idr_mutex);
mem_bkend = idr_find(&mali_backend_idr, mali_alloc->backend_handle);
mutex_unlock(&mali_idr_mutex);
MALI_DEBUG_ASSERT(NULL != mem_bkend);
mem = mem_bkend->dma_buf.attachment;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT(mem->session == mali_pp_job_get_session(job));
mali_dma_buf_unmap(mem_bkend->mali_allocation, mem);
}
}
#endif /* !CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH */
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;
}
_mali_osk_errcode_t mali_mem_bind_dma_buf(mali_mem_allocation *alloc,
mali_mem_backend *mem_backend,
int fd, u32 flags)
{
struct dma_buf *buf;
struct mali_dma_buf_attachment *dma_mem;
struct mali_session_data *session = alloc->session;
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT_POINTER(mem_backend);
MALI_DEBUG_ASSERT_POINTER(alloc);
/* get dma buffer */
buf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(buf)) {
return _MALI_OSK_ERR_FAULT;
}
/* Currently, mapping of the full buffer are supported. */
if (alloc->psize != buf->size) {
goto failed_alloc_mem;
}
dma_mem = _mali_osk_calloc(1, sizeof(struct mali_dma_buf_attachment));
if (NULL == dma_mem) {
goto failed_alloc_mem;
}
dma_mem->buf = buf;
dma_mem->session = session;
#if (!defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)) && (defined(CONFIG_MALI_DMA_BUF_LAZY_MAP))
dma_mem->map_ref = 1;
#else
dma_mem->map_ref = 0;
#endif
if (flags & _MALI_MAP_VIDEO_LAYER)
dma_mem->map_ref = 1;
init_waitqueue_head(&dma_mem->wait_queue);
dma_mem->attachment = dma_buf_attach(dma_mem->buf, &mali_platform_device->dev);
if (NULL == dma_mem->attachment) {
goto failed_dma_attach;
}
mem_backend->dma_buf.attachment = dma_mem;
alloc->flags |= MALI_MEM_FLAG_DONT_CPU_MAP;
if (flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) {
alloc->flags |= MALI_MEM_FLAG_MALI_GUARD_PAGE;
}
#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
/* Map memory into session's Mali virtual address space. */
if (!(flags & _MALI_MAP_VIDEO_LAYER) &&
(0 != mali_dma_buf_map(mem_backend))) {
goto Failed_dma_map;
}
#endif
return _MALI_OSK_ERR_OK;
#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
Failed_dma_map:
mali_dma_buf_unmap(alloc, dma_mem);
#endif
/* Wait for buffer to become unmapped */
wait_event(dma_mem->wait_queue, !dma_mem->is_mapped);
MALI_DEBUG_ASSERT(!dma_mem->is_mapped);
dma_buf_detach(dma_mem->buf, dma_mem->attachment);
failed_dma_attach:
_mali_osk_free(dma_mem);
failed_alloc_mem:
dma_buf_put(buf);
return _MALI_OSK_ERR_FAULT;
}
void mali_mem_unbind_dma_buf(mali_mem_backend *mem_backend)
{
struct mali_dma_buf_attachment *mem;
struct mali_session_data *session;
MALI_DEBUG_ASSERT_POINTER(mem_backend);
MALI_DEBUG_ASSERT(MALI_MEM_DMA_BUF == mem_backend->type);
mem = mem_backend->dma_buf.attachment;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(mem->attachment);
MALI_DEBUG_ASSERT_POINTER(mem->buf);
MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release attachment %p\n", mem));
MALI_DEBUG_ASSERT_POINTER(mem_backend->mali_allocation);
session = mem_backend->mali_allocation->session;
#if (defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)) ||((!defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)) && (defined(CONFIG_MALI_DMA_BUF_LAZY_MAP)))
/* We mapped implicitly on attach, so we need to unmap on release */
mali_dma_buf_unmap(mem_backend->mali_allocation, 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_DEBUG_ASSERT_POINTER(session);
mali_session_memory_lock(session);
_mali_osk_free(mem);
mali_session_memory_unlock(session);
}
_mali_osk_errcode_t meson_update_video_texture(struct mali_session_data *session, int fd)
{
struct dma_buf *buf;
struct dma_buf_attachment *attachment;
struct sg_table *sgt;
_mali_osk_errcode_t ret = _MALI_OSK_ERR_OK;
/* get dma buffer */
buf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(buf)) {
return _MALI_OSK_ERR_FAULT;
}
attachment = dma_buf_attach(buf, &mali_platform_device->dev);
if (NULL == attachment) {
MALI_DEBUG_PRINT_ERROR(("Failed to attach dma-buf\n"));
ret = _MALI_OSK_ERR_FAULT;
goto failed_dma_attach;
}
sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(sgt)) {
MALI_DEBUG_PRINT_ERROR(("Failed to dma-buf-map attachment\n"));
ret = _MALI_OSK_ERR_FAULT;
goto failed_dma_map;
}
dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL);
sgt = NULL;
failed_dma_map:
dma_buf_detach(buf, attachment);
failed_dma_attach:
dma_buf_put(buf);
return ret;
}