367 lines
8.3 KiB
C
367 lines
8.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include "ion_drv.h"
|
|
#ifdef CONFIG_MTK_IOMMU_V2
|
|
#include "mt_iommu.h"
|
|
#include "pseudo_m4u.h"
|
|
#endif
|
|
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <asm/mman.h>
|
|
|
|
#include "mdw_cmn.h"
|
|
#include "mdw_mem_cmn.h"
|
|
|
|
#define APUSYS_ION_PAGE_SIZE PAGE_SIZE
|
|
|
|
/* ion mem allocator */
|
|
struct mdw_mem_ion_ma {
|
|
struct ion_client *client;
|
|
struct mdw_mem_ops ops;
|
|
};
|
|
|
|
static struct mdw_mem_ion_ma ion_ma;
|
|
|
|
#ifdef CONFIG_MTK_IOMMU_V2
|
|
#define APUSYS_IOMMU_PORT M4U_PORT_L21_APU_FAKE_DATA
|
|
|
|
/* check argument */
|
|
static int mdw_mem_ion_check(struct apusys_kmem *mem)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (mem == NULL) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((mem->align != 0) &&
|
|
((mem->align > APUSYS_ION_PAGE_SIZE) ||
|
|
((APUSYS_ION_PAGE_SIZE % mem->align) != 0))) {
|
|
mdw_drv_err("align argument invalid (%d)\n", mem->align);
|
|
return -EINVAL;
|
|
}
|
|
if (mem->cache > 1) {
|
|
mdw_drv_err("Cache argument invalid (%d)\n", mem->cache);
|
|
return -EINVAL;
|
|
}
|
|
if ((mem->iova_size % APUSYS_ION_PAGE_SIZE) != 0) {
|
|
mdw_drv_err("iova_size argument invalid 0x%x\n",
|
|
mem->iova_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static uint32_t mdw_mem_ion_get_size(struct apusys_kmem *mem)
|
|
{
|
|
uint32_t size = 0;
|
|
struct dma_buf *db = NULL;
|
|
|
|
db = dma_buf_get(mem->fd);
|
|
if (IS_ERR(db)) {
|
|
mdw_drv_err("get dmabuf from ion handle fail\n");
|
|
return 0;
|
|
}
|
|
|
|
size = db->size;
|
|
dma_buf_put(db);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int mdw_mem_ion_map_kva(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
void *buffer = NULL;
|
|
struct ion_handle *ion_hnd = NULL;
|
|
uint32_t size = 0;
|
|
int ret = 0;
|
|
|
|
/* check argument */
|
|
if (mem == NULL) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* import fd */
|
|
ion_hnd = ion_import_dma_buf_fd(ion_ma.client, mem->fd);
|
|
if (IS_ERR_OR_NULL(ion_hnd))
|
|
return -EINVAL;
|
|
|
|
/* check size */
|
|
size = mdw_mem_ion_get_size(mem);
|
|
if (size < mem->size || !size) {
|
|
mdw_drv_err("buffer size invalid(%u/%u)\n", size, mem->size);
|
|
ret = -ENOMEM;
|
|
goto fail_map_kernel;
|
|
}
|
|
mdw_mem_debug("mem check size(%u/%u)\n", size, mem->size);
|
|
|
|
/* map kernel va*/
|
|
buffer = ion_map_kernel(ion_ma.client, ion_hnd);
|
|
if (IS_ERR_OR_NULL(buffer)) {
|
|
mdw_drv_err("map kernel va fail(%p/%p)\n",
|
|
ion_ma.client, ion_hnd);
|
|
ret = -ENOMEM;
|
|
goto fail_map_kernel;
|
|
}
|
|
|
|
if (!mem->khandle)
|
|
mem->khandle = (uint64_t)ion_hnd;
|
|
mem->kva = (uint64_t)buffer;
|
|
|
|
mdw_mem_debug("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
|
|
return 0;
|
|
|
|
fail_map_kernel:
|
|
mdw_drv_err("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
ion_free(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_mem_ion_map_iova(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
int ret = 0;
|
|
struct ion_handle *ion_hnd = NULL;
|
|
struct ion_mm_data mm_data;
|
|
|
|
/* check argument */
|
|
if (mdw_mem_ion_check(mem))
|
|
return -EINVAL;
|
|
|
|
/* import fd */
|
|
ion_hnd = ion_import_dma_buf_fd(ion_ma.client, mem->fd);
|
|
|
|
if (IS_ERR_OR_NULL(ion_hnd))
|
|
return -EINVAL;
|
|
|
|
/* use get_iova replace config_buffer & get_phys*/
|
|
memset((void *)&mm_data, 0, sizeof(struct ion_mm_data));
|
|
mm_data.mm_cmd = ION_MM_GET_IOVA;
|
|
mm_data.get_phys_param.kernel_handle = ion_hnd;
|
|
mm_data.get_phys_param.module_id = APUSYS_IOMMU_PORT;
|
|
mm_data.get_phys_param.coherent = 1;
|
|
mm_data.get_phys_param.phy_addr =
|
|
((unsigned long) APUSYS_IOMMU_PORT << 24);
|
|
|
|
if (ion_kernel_ioctl(ion_ma.client, ION_CMD_MULTIMEDIA,
|
|
(unsigned long)&mm_data)) {
|
|
mdw_drv_err("ion_config_buffer: ION_CMD_MULTIMEDIA failed\n");
|
|
ret = -ENOMEM;
|
|
goto free_import;
|
|
}
|
|
|
|
mem->iova = mm_data.get_phys_param.phy_addr;
|
|
mem->iova_size = mm_data.get_phys_param.len;
|
|
|
|
if (!mem->khandle)
|
|
mem->khandle = (uint64_t)ion_hnd;
|
|
|
|
mdw_mem_debug("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
|
|
return ret;
|
|
|
|
free_import:
|
|
mdw_drv_err("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
ion_free(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_mem_ion_unmap_iova(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
int ret = 0;
|
|
struct ion_handle *ion_hnd = NULL;
|
|
|
|
/* check argument */
|
|
if (mdw_mem_ion_check(mem))
|
|
return -EINVAL;
|
|
|
|
/* check argument */
|
|
if (mem->khandle == 0) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ion_hnd = (struct ion_handle *) mem->khandle;
|
|
|
|
mdw_mem_debug("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
|
|
ion_free(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_mem_ion_unmap_kva(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
struct ion_handle *ion_hnd = NULL;
|
|
int ret = 0;
|
|
|
|
/* check argument */
|
|
if (mdw_mem_ion_check(mem))
|
|
return -EINVAL;
|
|
|
|
/* check argument */
|
|
if (mem->khandle == 0) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mdw_mem_debug("mem(%d/0x%llx/0x%x/%d/0x%x/0x%llx/0x%llx)\n",
|
|
mem->fd, mem->uva, mem->iova, mem->size,
|
|
mem->iova_size, mem->khandle, mem->kva);
|
|
|
|
ion_hnd = (struct ion_handle *) mem->khandle;
|
|
|
|
ion_unmap_kernel(ion_ma.client, ion_hnd);
|
|
|
|
ion_free(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_mem_ion_alloc(struct apusys_kmem *mem)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int mdw_mem_ion_free(struct apusys_kmem *mem)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int mdw_mem_ion_flush(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
int ret = 0;
|
|
struct ion_sys_data sys_data;
|
|
void *va = NULL;
|
|
struct ion_handle *ion_hnd = NULL;
|
|
|
|
mdw_mem_debug("\n");
|
|
|
|
if (mem->khandle == 0) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
ion_hnd = (struct ion_handle *)mem->khandle;
|
|
|
|
va = ion_map_kernel(ion_ma.client, ion_hnd);
|
|
if (IS_ERR_OR_NULL(va))
|
|
return -ENOMEM;
|
|
sys_data.sys_cmd = ION_SYS_CACHE_SYNC;
|
|
sys_data.cache_sync_param.kernel_handle = ion_hnd;
|
|
sys_data.cache_sync_param.sync_type = ION_CACHE_FLUSH_BY_RANGE;
|
|
sys_data.cache_sync_param.va = va;
|
|
sys_data.cache_sync_param.size = mem->size;
|
|
if (ion_kernel_ioctl(ion_ma.client,
|
|
ION_CMD_SYSTEM, (unsigned long)&sys_data)) {
|
|
mdw_drv_err("ION_CACHE_FLUSH_BY_RANGE FAIL\n");
|
|
ret = -EINVAL;
|
|
}
|
|
ion_unmap_kernel(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_mem_ion_invalidate(struct apusys_kmem *mem)
|
|
{
|
|
#if !defined(CONFIG_MTK_IOMMU_V2)
|
|
int ret = -ENODEV;
|
|
#else
|
|
int ret = 0;
|
|
struct ion_sys_data sys_data;
|
|
void *va = NULL;
|
|
struct ion_handle *ion_hnd = NULL;
|
|
|
|
if (mem->khandle == 0) {
|
|
mdw_drv_err("invalid argument\n");
|
|
return -EINVAL;
|
|
}
|
|
ion_hnd = (struct ion_handle *)mem->khandle;
|
|
|
|
va = ion_map_kernel(ion_ma.client, ion_hnd);
|
|
if (IS_ERR_OR_NULL(va))
|
|
return -ENOMEM;
|
|
|
|
sys_data.sys_cmd = ION_SYS_CACHE_SYNC;
|
|
sys_data.cache_sync_param.kernel_handle = ion_hnd;
|
|
sys_data.cache_sync_param.sync_type = ION_CACHE_INVALID_BY_RANGE;
|
|
sys_data.cache_sync_param.va = va;
|
|
sys_data.cache_sync_param.size = mem->size;
|
|
if (ion_kernel_ioctl(ion_ma.client,
|
|
ION_CMD_SYSTEM, (unsigned long)&sys_data)) {
|
|
mdw_drv_err("ION_CACHE_INVALID_BY_RANGE FAIL\n");
|
|
ret = -EINVAL;
|
|
}
|
|
ion_unmap_kernel(ion_ma.client, ion_hnd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static void mdw_mem_ion_destroy(void)
|
|
{
|
|
#if defined(CONFIG_MTK_IOMMU_V2)
|
|
ion_client_destroy(ion_ma.client);
|
|
memset(&ion_ma, 0, sizeof(ion_ma));
|
|
#endif
|
|
}
|
|
|
|
struct mdw_mem_ops *mdw_mem_ion_init(void)
|
|
{
|
|
memset(&ion_ma, 0, sizeof(ion_ma));
|
|
|
|
#if defined(CONFIG_MTK_IOMMU_V2)
|
|
/* create ion client */
|
|
ion_ma.client = ion_client_create(g_ion_device, "apusys midware");
|
|
#endif
|
|
if (IS_ERR_OR_NULL(ion_ma.client))
|
|
return NULL;
|
|
|
|
ion_ma.ops.alloc = mdw_mem_ion_alloc;
|
|
ion_ma.ops.free = mdw_mem_ion_free;
|
|
ion_ma.ops.flush = mdw_mem_ion_flush;
|
|
ion_ma.ops.invalidate = mdw_mem_ion_invalidate;
|
|
ion_ma.ops.map_kva = mdw_mem_ion_map_kva;
|
|
ion_ma.ops.unmap_kva = mdw_mem_ion_unmap_kva;
|
|
ion_ma.ops.map_iova = mdw_mem_ion_map_iova;
|
|
ion_ma.ops.unmap_iova = mdw_mem_ion_unmap_iova;
|
|
ion_ma.ops.destroy = mdw_mem_ion_destroy;
|
|
|
|
return &ion_ma.ops;
|
|
}
|