M7350/kernel/drivers/media/platform/msm/vidc/msm_smem.c
2024-09-09 08:52:07 +00:00

529 lines
13 KiB
C

/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/slab.h>
#include <mach/iommu_domains.h>
#include "msm_smem.h"
#include "msm_vidc_debug.h"
struct smem_client {
int mem_type;
void *clnt;
struct msm_vidc_platform_resources *res;
};
static u32 get_tz_usage(struct smem_client *client, enum hal_buffer buffer_type)
{
int i;
struct buffer_usage_set *buffer_usage_set;
struct buffer_usage_table *buffer_usage_tbl;
buffer_usage_set = &client->res->buffer_usage_set;
if (!buffer_usage_set) {
dprintk(VIDC_DBG, "no buffer usage set present!\n");
return 0;
}
for (i = 0; i < buffer_usage_set->count; i++) {
buffer_usage_tbl = &buffer_usage_set->buffer_usage_tbl[i];
if (buffer_usage_tbl->buffer_type & buffer_type)
return buffer_usage_tbl->tz_usage;
}
dprintk(VIDC_DBG, "No tz usage found for buffer type: %x\n",
buffer_type);
return 0;
}
static int get_device_address(struct smem_client *smem_client,
struct ion_handle *hndl, unsigned long align,
unsigned long *iova, unsigned long *buffer_size,
u32 flags, enum hal_buffer buffer_type)
{
int rc = 0;
int domain, partition;
struct ion_client *clnt = NULL;
if (!iova || !buffer_size || !hndl || !smem_client) {
dprintk(VIDC_ERR, "Invalid params: %p, %p, %p, %p\n",
smem_client, hndl, iova, buffer_size);
return -EINVAL;
}
clnt = smem_client->clnt;
if (!clnt) {
dprintk(VIDC_ERR, "Invalid client");
return -EINVAL;
}
rc = msm_smem_get_domain_partition(smem_client, flags, buffer_type,
&domain, &partition);
if (rc) {
dprintk(VIDC_ERR, "Failed to get domain and partition: %d", rc);
goto mem_domain_get_failed;
}
if (flags & SMEM_SECURE) {
rc = msm_ion_secure_buffer(clnt, hndl,
get_tz_usage(smem_client, buffer_type), 0);
if (rc) {
dprintk(VIDC_ERR, "Failed to secure memory\n");
goto mem_domain_get_failed;
}
}
if (is_iommu_present(smem_client->res)) {
dprintk(VIDC_DBG,
"Calling ion_map_iommu - domain: %d, partition: %d",
domain, partition);
rc = ion_map_iommu(clnt, hndl, domain, partition, align,
0, iova, buffer_size, 0, 0);
} else {
dprintk(VIDC_DBG, "Using physical memory address");
rc = ion_phys(clnt, hndl, iova, (size_t *)buffer_size);
}
if (rc) {
dprintk(VIDC_ERR, "ion memory map failed - %d", rc);
goto mem_map_failed;
}
return 0;
mem_map_failed:
if (flags & SMEM_SECURE)
msm_ion_unsecure_buffer(clnt, hndl);
mem_domain_get_failed:
return rc;
}
static void put_device_address(struct smem_client *smem_client,
struct ion_handle *hndl, int domain_num, int partition_num, u32 flags)
{
struct ion_client *clnt = NULL;
if (!hndl || !smem_client) {
dprintk(VIDC_WARN, "Invalid params: %p, %p\n",
smem_client, hndl);
return;
}
clnt = smem_client->clnt;
if (!clnt) {
dprintk(VIDC_WARN, "Invalid client");
return;
}
if (is_iommu_present(smem_client->res)) {
dprintk(VIDC_DBG,
"Calling ion_unmap_iommu - domain: %d, parition: %d",
domain_num, partition_num);
ion_unmap_iommu(clnt, hndl, domain_num, partition_num);
}
if (flags & SMEM_SECURE) {
if (msm_ion_unsecure_buffer(clnt, hndl))
dprintk(VIDC_ERR, "Failed to unsecure memory\n");
}
}
static int ion_user_to_kernel(struct smem_client *client, int fd, u32 offset,
struct msm_smem *mem, enum hal_buffer buffer_type)
{
struct ion_handle *hndl;
unsigned long iova = 0;
unsigned long buffer_size = 0;
unsigned long ionflags = 0;
int rc = 0;
int align = SZ_4K;
hndl = ion_import_dma_buf(client->clnt, fd);
if (IS_ERR_OR_NULL(hndl)) {
dprintk(VIDC_ERR, "Failed to get handle: %p, %d, %d, %p\n",
client, fd, offset, hndl);
rc = -ENOMEM;
goto fail_import_fd;
}
mem->kvaddr = NULL;
rc = ion_handle_get_flags(client->clnt, hndl, &ionflags);
if (rc) {
dprintk(VIDC_ERR, "Failed to get ion flags: %d\n", rc);
goto fail_device_address;
}
mem->flags = ionflags;
mem->buffer_type = buffer_type;
if (mem->flags & SMEM_SECURE)
align = ALIGN(align, SZ_1M);
rc = get_device_address(client, hndl, align, &iova, &buffer_size,
mem->flags, buffer_type);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
goto fail_device_address;
}
mem->mem_type = client->mem_type;
mem->smem_priv = hndl;
mem->device_addr = iova;
mem->size = buffer_size;
dprintk(VIDC_DBG, "NOTE: Buffer device address: 0x%lx, size: %d\n",
mem->device_addr, mem->size);
return rc;
fail_device_address:
ion_free(client->clnt, hndl);
fail_import_fd:
return rc;
}
static int alloc_ion_mem(struct smem_client *client, size_t size, u32 align,
u32 flags, enum hal_buffer buffer_type, struct msm_smem *mem,
int map_kernel)
{
struct ion_handle *hndl;
unsigned long iova = 0;
unsigned long buffer_size = 0;
unsigned long heap_mask = 0;
int rc = 0;
align = ALIGN(align, SZ_4K);
size = ALIGN(size, SZ_4K);
if (flags & SMEM_SECURE) {
size = ALIGN(size, SZ_1M);
align = ALIGN(align, SZ_1M);
}
if (is_iommu_present(client->res)) {
heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID);
} else {
dprintk(VIDC_DBG,
"allocate shared memory from adsp heap size %d align %d\n",
size, align);
heap_mask = ION_HEAP(ION_ADSP_HEAP_ID);
}
if (flags & SMEM_SECURE)
heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
hndl = ion_alloc(client->clnt, size, align, heap_mask, flags);
if (IS_ERR_OR_NULL(hndl)) {
dprintk(VIDC_ERR,
"Failed to allocate shared memory = %p, %d, %d, 0x%x\n",
client, size, align, flags);
rc = -ENOMEM;
goto fail_shared_mem_alloc;
}
mem->mem_type = client->mem_type;
mem->smem_priv = hndl;
mem->flags = flags;
mem->buffer_type = buffer_type;
if (map_kernel) {
mem->kvaddr = ion_map_kernel(client->clnt, hndl);
if (!mem->kvaddr) {
dprintk(VIDC_ERR,
"Failed to map shared mem in kernel\n");
rc = -EIO;
goto fail_map;
}
} else
mem->kvaddr = NULL;
rc = get_device_address(client, hndl, align, &iova, &buffer_size,
flags, buffer_type);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n",
rc);
goto fail_device_address;
}
mem->device_addr = iova;
dprintk(VIDC_DBG,
"device_address = 0x%lx, kvaddr = 0x%p, size = %d\n",
mem->device_addr, mem->kvaddr, size);
mem->size = size;
return rc;
fail_device_address:
ion_unmap_kernel(client->clnt, hndl);
fail_map:
ion_free(client->clnt, hndl);
fail_shared_mem_alloc:
return rc;
}
static void free_ion_mem(struct smem_client *client, struct msm_smem *mem)
{
int domain, partition, rc;
rc = msm_smem_get_domain_partition((void *)client, mem->flags,
mem->buffer_type, &domain, &partition);
if (rc) {
dprintk(VIDC_ERR, "Failed to get domain, partition: %d", rc);
return;
}
if (mem->device_addr)
put_device_address(client,
mem->smem_priv, domain, partition, mem->flags);
if (mem->kvaddr)
ion_unmap_kernel(client->clnt, mem->smem_priv);
if (mem->smem_priv)
ion_free(client->clnt, mem->smem_priv);
}
static void *ion_new_client(void)
{
struct ion_client *client = NULL;
client = msm_ion_client_create(-1, "video_client");
if (!client)
dprintk(VIDC_ERR, "Failed to create smem client\n");
return client;
};
static void ion_delete_client(struct smem_client *client)
{
ion_client_destroy(client->clnt);
}
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
enum hal_buffer buffer_type)
{
struct smem_client *client = clt;
int rc = 0;
struct msm_smem *mem;
if (fd < 0) {
dprintk(VIDC_ERR, "Invalid fd: %d\n", fd);
return NULL;
}
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
if (!mem) {
dprintk(VIDC_ERR, "Failed to allocte shared mem\n");
return NULL;
}
switch (client->mem_type) {
case SMEM_ION:
rc = ion_user_to_kernel(clt, fd, offset, mem, buffer_type);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
rc = -EINVAL;
break;
}
if (rc) {
dprintk(VIDC_ERR, "Failed to allocate shared memory\n");
kfree(mem);
mem = NULL;
}
return mem;
}
static int ion_cache_operations(struct smem_client *client,
struct msm_smem *mem, enum smem_cache_ops cache_op)
{
unsigned long ionflag = 0;
int rc = 0;
int msm_cache_ops = 0;
if (!mem || !client) {
dprintk(VIDC_ERR, "Invalid params: %p, %p\n",
mem, client);
return -EINVAL;
}
rc = ion_handle_get_flags(client->clnt, mem->smem_priv,
&ionflag);
if (rc) {
dprintk(VIDC_ERR,
"ion_handle_get_flags failed: %d\n", rc);
goto cache_op_failed;
}
if (ION_IS_CACHED(ionflag)) {
switch (cache_op) {
case SMEM_CACHE_CLEAN:
msm_cache_ops = ION_IOC_CLEAN_CACHES;
break;
case SMEM_CACHE_INVALIDATE:
msm_cache_ops = ION_IOC_INV_CACHES;
break;
case SMEM_CACHE_CLEAN_INVALIDATE:
msm_cache_ops = ION_IOC_CLEAN_INV_CACHES;
break;
default:
dprintk(VIDC_ERR, "cache operation not supported\n");
rc = -EINVAL;
goto cache_op_failed;
}
rc = msm_ion_do_cache_op(client->clnt,
(struct ion_handle *)mem->smem_priv,
0, (unsigned long)mem->size,
msm_cache_ops);
if (rc) {
dprintk(VIDC_ERR,
"cache operation failed %d\n", rc);
goto cache_op_failed;
}
}
cache_op_failed:
return rc;
}
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
enum smem_cache_ops cache_op)
{
struct smem_client *client = clt;
int rc = 0;
if (!client) {
dprintk(VIDC_ERR, "Invalid params: %p\n",
client);
return -EINVAL;
}
switch (client->mem_type) {
case SMEM_ION:
rc = ion_cache_operations(client, mem, cache_op);
if (rc)
dprintk(VIDC_ERR,
"Failed cache operations: %d\n", rc);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
break;
}
return rc;
}
void *msm_smem_new_client(enum smem_type mtype,
struct msm_vidc_platform_resources *res)
{
struct smem_client *client = NULL;
void *clnt = NULL;
switch (mtype) {
case SMEM_ION:
clnt = ion_new_client();
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
break;
}
if (clnt) {
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (client) {
client->mem_type = mtype;
client->clnt = clnt;
client->res = res;
}
} else {
dprintk(VIDC_ERR, "Failed to create new client: mtype = %d\n",
mtype);
}
return client;
};
struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
enum hal_buffer buffer_type, int map_kernel)
{
struct smem_client *client;
int rc = 0;
struct msm_smem *mem;
client = clt;
if (!client) {
dprintk(VIDC_ERR, "Invalid client passed\n");
return NULL;
}
if (!size) {
dprintk(VIDC_ERR, "No need to allocate memory of size: %d\n",
size);
return NULL;
}
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
if (!mem) {
dprintk(VIDC_ERR, "Failed to allocate shared mem\n");
return NULL;
}
switch (client->mem_type) {
case SMEM_ION:
rc = alloc_ion_mem(client, size, align, flags, buffer_type,
mem, map_kernel);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
rc = -EINVAL;
break;
}
if (rc) {
dprintk(VIDC_ERR, "Failed to allocate shared memory\n");
kfree(mem);
mem = NULL;
}
return mem;
}
void msm_smem_free(void *clt, struct msm_smem *mem)
{
struct smem_client *client = clt;
if (!client || !mem) {
dprintk(VIDC_ERR, "Invalid client/handle passed\n");
return;
}
switch (client->mem_type) {
case SMEM_ION:
free_ion_mem(client, mem);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
break;
}
kfree(mem);
};
void msm_smem_delete_client(void *clt)
{
struct smem_client *client = clt;
if (!client) {
dprintk(VIDC_ERR, "Invalid client passed\n");
return;
}
switch (client->mem_type) {
case SMEM_ION:
ion_delete_client(client);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
break;
}
kfree(client);
}
int msm_smem_get_domain_partition(void *clt, u32 flags, enum hal_buffer
buffer_type, int *domain_num, int *partition_num)
{
struct smem_client *client = clt;
struct iommu_set *iommu_group_set = &client->res->iommu_group_set;
int i;
bool is_secure = (flags & SMEM_SECURE);
struct iommu_info *iommu_map;
*domain_num = -1;
*partition_num = -1;
if (!iommu_group_set) {
dprintk(VIDC_DBG, "no iommu group set present!\n");
return -ENOENT;
}
for (i = 0; i < iommu_group_set->count; i++) {
iommu_map = &iommu_group_set->iommu_maps[i];
if ((iommu_map->is_secure == is_secure) &&
(iommu_map->buffer_type & buffer_type)) {
*domain_num = iommu_map->domain;
*partition_num = 0;
if ((buffer_type & HAL_BUFFER_INTERNAL_CMD_QUEUE) &&
(iommu_map->npartitions == 2))
*partition_num = 1;
break;
}
}
dprintk(VIDC_DBG, "domain: %d, partition: %d found!\n",
*domain_num, *partition_num);
return 0;
}