529 lines
13 KiB
C
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;
|
|
}
|