490 lines
12 KiB
C
Executable File
490 lines
12 KiB
C
Executable File
/*
|
|
* Samsung Exynos SoC series VIPx driver
|
|
*
|
|
* Copyright (c) 2018 Samsung Electronics Co., Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include "vipx-log.h"
|
|
#include "vipx-util.h"
|
|
#include "vipx-kernel-binary.h"
|
|
#include "vipx-memory.h"
|
|
#include "vipx-context.h"
|
|
|
|
static struct vipx_buffer *__vipx_context_create_buffer(
|
|
struct vipx_context *vctx,
|
|
struct vipx_common_mem *common_mem,
|
|
enum dma_data_direction dir)
|
|
{
|
|
int ret;
|
|
struct vipx_buffer *buf;
|
|
struct vipx_memory *mem;
|
|
|
|
vipx_enter();
|
|
|
|
if (unlikely(common_mem->size == 0)) {
|
|
ret = -EINVAL;
|
|
vipx_err("memory must not be zero\n");
|
|
goto p_err_size;
|
|
}
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
|
if (!buf) {
|
|
ret = -ENOMEM;
|
|
vipx_err("Failed to alloc buffer\n");
|
|
goto p_err_alloc;
|
|
}
|
|
|
|
mem = &vctx->core->system->memory;
|
|
|
|
buf->m.fd = common_mem->fd;
|
|
buf->size = common_mem->size;
|
|
buf->addr_type = common_mem->addr_type;
|
|
buf->mem_attr = common_mem->mem_attr;
|
|
buf->mem_type = common_mem->mem_type;
|
|
buf->offset = common_mem->offset;
|
|
buf->direction = dir;
|
|
|
|
ret = mem->mops->map_dmabuf(mem, buf);
|
|
if (unlikely(ret))
|
|
goto p_err_map;
|
|
|
|
ret = mem->mops->sync_for_device(mem, buf);
|
|
if (ret)
|
|
goto p_err_sync;
|
|
|
|
common_mem->iova = (unsigned int)buf->dvaddr + buf->offset;
|
|
|
|
vipx_leave();
|
|
return buf;
|
|
p_err_sync:
|
|
mem->mops->unmap_dmabuf(mem, buf);
|
|
p_err_map:
|
|
kfree(buf);
|
|
p_err_alloc:
|
|
p_err_size:
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
static void __vipx_context_destroy_buffer(struct vipx_context *vctx,
|
|
struct vipx_buffer *buf)
|
|
{
|
|
struct vipx_memory *mem;
|
|
|
|
vipx_enter();
|
|
if (!buf)
|
|
return;
|
|
|
|
mem = &vctx->core->system->memory;
|
|
|
|
mem->mops->sync_for_cpu(mem, buf);
|
|
mem->mops->unmap_dmabuf(mem, buf);
|
|
kfree(buf);
|
|
vipx_leave();
|
|
}
|
|
|
|
static int vipx_context_load_kernel_binary(struct vipx_context *vctx,
|
|
struct vipx_ioc_load_kernel_binary *kernel_bin)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
vipx_dbg("[%s] load kernel binary (framework)\n", __func__);
|
|
vipx_dbg("global_id : %#x\n", kernel_bin->global_id);
|
|
vipx_dbg("kernel_fd : %#x\n", kernel_bin->kernel_fd);
|
|
vipx_dbg("kernel_size : %#x\n", kernel_bin->kernel_size);
|
|
|
|
ret = vipx_kernel_binary_add(vctx, kernel_bin->global_id,
|
|
kernel_bin->kernel_fd, kernel_bin->kernel_size);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int vipx_context_unload_kernel_binary(struct vipx_context *vctx,
|
|
struct vipx_ioc_unload_kernel_binary *unload_kbin)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
vipx_dbg("[%s] unload kernel binary (framework)\n", __func__);
|
|
vipx_dbg("model_id : %#x\n", unload_kbin->global_id);
|
|
|
|
ret = vipx_kernel_binary_unload(vctx, unload_kbin->global_id,
|
|
unload_kbin->kernel_fd, unload_kbin->kernel_size);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int vipx_context_load_graph_info(struct vipx_context *vctx,
|
|
struct vipx_ioc_load_graph_info *ginfo)
|
|
{
|
|
int ret;
|
|
struct vipx_common_graph_info *common_ginfo;
|
|
struct vipx_graph_model *gmodel;
|
|
struct vipx_common_mem *common_mem;
|
|
struct vipx_buffer *buffer;
|
|
int idx;
|
|
int *debug_addr;
|
|
|
|
vipx_enter();
|
|
common_ginfo = &ginfo->graph_info;
|
|
|
|
vipx_dbg("[%s] load graph (framework)\n", __func__);
|
|
debug_addr = (int *)common_ginfo;
|
|
for (idx = 0; idx < sizeof(*common_ginfo) >> 2; ++idx)
|
|
vipx_dbg("[%3d] %#10x\n", idx, debug_addr[idx]);
|
|
|
|
gmodel = vctx->graph_ops->create_model(vctx->graph, common_ginfo);
|
|
if (IS_ERR(gmodel)) {
|
|
ret = PTR_ERR(gmodel);
|
|
goto p_err_gmodel;
|
|
}
|
|
|
|
common_mem = &gmodel->common_ginfo.graph;
|
|
buffer = __vipx_context_create_buffer(vctx, common_mem, DMA_TO_DEVICE);
|
|
if (IS_ERR(buffer)) {
|
|
ret = PTR_ERR(buffer);
|
|
vipx_err("Failed to create buffer for graph (%d)\n", ret);
|
|
goto p_err_graph;
|
|
}
|
|
gmodel->graph = buffer;
|
|
|
|
common_mem = &gmodel->common_ginfo.temp_buf;
|
|
if (common_mem->size) {
|
|
buffer = __vipx_context_create_buffer(vctx, common_mem,
|
|
DMA_FROM_DEVICE);
|
|
if (IS_ERR(buffer)) {
|
|
ret = PTR_ERR(buffer);
|
|
vipx_err("Failed to create buffer for temp_buf (%d)\n",
|
|
ret);
|
|
goto p_err_temp_buf;
|
|
}
|
|
gmodel->temp_buf = buffer;
|
|
} else {
|
|
gmodel->temp_buf = NULL;
|
|
}
|
|
|
|
common_mem = &gmodel->common_ginfo.weight;
|
|
if (common_mem->size) {
|
|
buffer = __vipx_context_create_buffer(vctx, common_mem,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR(buffer)) {
|
|
ret = PTR_ERR(buffer);
|
|
vipx_err("Failed to create buffer for weight (%d)\n",
|
|
ret);
|
|
goto p_err_weight;
|
|
}
|
|
gmodel->weight = buffer;
|
|
} else {
|
|
gmodel->weight = NULL;
|
|
}
|
|
|
|
common_mem = &gmodel->common_ginfo.bias;
|
|
if (common_mem->size) {
|
|
buffer = __vipx_context_create_buffer(vctx, common_mem,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR(buffer)) {
|
|
ret = PTR_ERR(buffer);
|
|
vipx_err("Failed to create buffer for bias (%d)\n",
|
|
ret);
|
|
goto p_err_bias;
|
|
}
|
|
gmodel->bias = buffer;
|
|
} else {
|
|
gmodel->bias = NULL;
|
|
}
|
|
|
|
common_mem = &gmodel->common_ginfo.user_param_buffer;
|
|
if (common_mem->size) {
|
|
buffer = __vipx_context_create_buffer(vctx, common_mem,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR(buffer)) {
|
|
ret = PTR_ERR(buffer);
|
|
vipx_err("Failed to create user_param_buffer (%d)\n",
|
|
ret);
|
|
goto p_err_user_param_buffer;
|
|
}
|
|
gmodel->user_param_buffer = buffer;
|
|
} else {
|
|
gmodel->user_param_buffer = NULL;
|
|
}
|
|
|
|
ret = vctx->graph_ops->register_model(vctx->graph, gmodel);
|
|
if (ret)
|
|
goto p_err_register_model;
|
|
|
|
ginfo->timestamp[0] = gmodel->time[TIME_LOAD_GRAPH].start;
|
|
ginfo->timestamp[1] = gmodel->time[TIME_LOAD_GRAPH].end;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_register_model:
|
|
__vipx_context_destroy_buffer(vctx, gmodel->user_param_buffer);
|
|
p_err_user_param_buffer:
|
|
__vipx_context_destroy_buffer(vctx, gmodel->bias);
|
|
p_err_bias:
|
|
__vipx_context_destroy_buffer(vctx, gmodel->weight);
|
|
p_err_weight:
|
|
__vipx_context_destroy_buffer(vctx, gmodel->temp_buf);
|
|
p_err_temp_buf:
|
|
__vipx_context_destroy_buffer(vctx, gmodel->graph);
|
|
p_err_graph:
|
|
vctx->graph_ops->destroy_model(vctx->graph, gmodel);
|
|
p_err_gmodel:
|
|
return ret;
|
|
}
|
|
|
|
static int vipx_context_unload_graph_info(struct vipx_context *vctx,
|
|
struct vipx_ioc_unload_graph_info *ginfo)
|
|
{
|
|
int ret;
|
|
struct vipx_graph_model *gmodel;
|
|
|
|
vipx_enter();
|
|
vipx_dbg("[%s] unload graph (framework)\n", __func__);
|
|
vipx_dbg("graph_id : %#x\n", ginfo->graph_id);
|
|
|
|
gmodel = vctx->graph_ops->get_model(vctx->graph, ginfo->graph_id);
|
|
if (IS_ERR(gmodel)) {
|
|
ret = PTR_ERR(gmodel);
|
|
goto p_err_get_model;
|
|
}
|
|
|
|
vctx->graph_ops->unregister_model(vctx->graph, gmodel);
|
|
|
|
ginfo->timestamp[0] = gmodel->time[TIME_UNLOAD_GRAPH].start;
|
|
ginfo->timestamp[1] = gmodel->time[TIME_UNLOAD_GRAPH].end;
|
|
|
|
__vipx_context_destroy_buffer(vctx, gmodel->user_param_buffer);
|
|
__vipx_context_destroy_buffer(vctx, gmodel->bias);
|
|
__vipx_context_destroy_buffer(vctx, gmodel->weight);
|
|
__vipx_context_destroy_buffer(vctx, gmodel->temp_buf);
|
|
__vipx_context_destroy_buffer(vctx, gmodel->graph);
|
|
|
|
vctx->graph_ops->destroy_model(vctx->graph, gmodel);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_get_model:
|
|
return ret;
|
|
}
|
|
|
|
static int vipx_context_execute_submodel(struct vipx_context *vctx,
|
|
struct vipx_ioc_execute_submodel *execute)
|
|
{
|
|
int ret;
|
|
int num_input, num_output;
|
|
struct vipx_common_execute_info *execute_info;
|
|
struct vipx_graph_model *gmodel;
|
|
struct vipx_common_mem *common_mem;
|
|
struct vipx_buffer **buffer;
|
|
int buffer_count = 0, translate_count = 0;
|
|
int idx;
|
|
int *debug_addr;
|
|
|
|
vipx_enter();
|
|
execute_info = &execute->execute_info;
|
|
|
|
vipx_dbg("[%s] execute (framework)\n", __func__);
|
|
debug_addr = (int *)execute_info;
|
|
for (idx = 0; idx < sizeof(*execute_info) >> 2; ++idx)
|
|
vipx_dbg("[%3d] %#10x\n", idx, debug_addr[idx]);
|
|
|
|
num_input = execute_info->num_input;
|
|
if (unlikely(num_input > MAX_INPUT_NUM)) {
|
|
ret = -EINVAL;
|
|
vipx_err("num_input[%d] is more than MAX[%d]\n", num_input,
|
|
MAX_INPUT_NUM);
|
|
goto p_err_num;
|
|
}
|
|
//TODO check plane count of buffer
|
|
buffer_count += num_input;
|
|
|
|
num_output = execute_info->num_output;
|
|
if (unlikely(num_output > MAX_OUTPUT_NUM)) {
|
|
ret = -EINVAL;
|
|
vipx_err("num_output[%d] is more than MAX[%d]\n", num_output,
|
|
MAX_INPUT_NUM);
|
|
goto p_err_num;
|
|
}
|
|
//TODO check plane count of buffer
|
|
buffer_count += num_output;
|
|
|
|
if (execute_info->user_param_buffer.size)
|
|
buffer_count++;
|
|
|
|
gmodel = vctx->graph_ops->get_model(vctx->graph, execute_info->gid);
|
|
if (unlikely(IS_ERR(gmodel))) {
|
|
ret = PTR_ERR(gmodel);
|
|
goto p_err_get_model;
|
|
}
|
|
|
|
vipx_kernel_binary_set_gmodel(vctx, gmodel);
|
|
if (!gmodel->kbin_count) {
|
|
ret = -ENODATA;
|
|
vipx_err("No kernel binary to set at graph model(%#x)\n",
|
|
gmodel->id);
|
|
goto p_err_set_model;
|
|
}
|
|
|
|
buffer = kmalloc(sizeof(*buffer) * buffer_count, GFP_KERNEL);
|
|
if (!buffer) {
|
|
ret = -ENOMEM;
|
|
vipx_err("Failed to alloc buffer for in/out (%d)\n",
|
|
buffer_count);
|
|
goto p_err_alloc;
|
|
}
|
|
|
|
//TODO check plane count of buffer
|
|
for (idx = 0; idx < num_input; ++idx) {
|
|
common_mem = &execute_info->input[idx];
|
|
buffer[translate_count] = __vipx_context_create_buffer(vctx,
|
|
common_mem, DMA_TO_DEVICE);
|
|
if (unlikely(IS_ERR(buffer[translate_count]))) {
|
|
ret = PTR_ERR(buffer[translate_count]);
|
|
vipx_err("Failed to translate input(%d/%d,%d/%d,%d)\n",
|
|
idx, num_input,
|
|
translate_count, buffer_count, ret);
|
|
goto p_err_buffer;
|
|
}
|
|
translate_count++;
|
|
}
|
|
|
|
//TODO check plane count of buffer
|
|
for (idx = 0; idx < num_output; ++idx) {
|
|
common_mem = &execute_info->output[idx];
|
|
buffer[translate_count] = __vipx_context_create_buffer(vctx,
|
|
common_mem, DMA_FROM_DEVICE);
|
|
if (unlikely(IS_ERR(buffer[translate_count]))) {
|
|
ret = PTR_ERR(buffer[translate_count]);
|
|
vipx_err("Failed to translate output(%d/%d,%d/%d,%d)\n",
|
|
idx, num_output,
|
|
translate_count, buffer_count, ret);
|
|
goto p_err_buffer;
|
|
}
|
|
translate_count++;
|
|
}
|
|
|
|
if (execute_info->user_param_buffer.size) {
|
|
common_mem = &execute_info->user_param_buffer;
|
|
buffer[translate_count] = __vipx_context_create_buffer(vctx,
|
|
common_mem, DMA_TO_DEVICE);
|
|
if (unlikely(IS_ERR(buffer[translate_count]))) {
|
|
ret = PTR_ERR(buffer[translate_count]);
|
|
vipx_err("Failed to translate user_param(%d/%d,%d)\n",
|
|
translate_count, buffer_count, ret);
|
|
goto p_err_buffer;
|
|
}
|
|
translate_count++;
|
|
}
|
|
|
|
ret = vctx->graph_ops->execute_model(vctx->graph, gmodel, execute_info);
|
|
if (ret)
|
|
goto p_err_execute;
|
|
|
|
execute->timestamp[0] = gmodel->time[TIME_EXECUTE_GRAPH].start;
|
|
execute->timestamp[1] = gmodel->time[TIME_EXECUTE_GRAPH].end;
|
|
|
|
for (idx = 0; idx < translate_count; ++idx)
|
|
__vipx_context_destroy_buffer(vctx, buffer[idx]);
|
|
|
|
kfree(buffer);
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_execute:
|
|
p_err_buffer:
|
|
for (idx = 0; idx < translate_count; ++idx)
|
|
__vipx_context_destroy_buffer(vctx, buffer[idx]);
|
|
|
|
kfree(buffer);
|
|
p_err_alloc:
|
|
p_err_set_model:
|
|
p_err_get_model:
|
|
p_err_num:
|
|
return ret;
|
|
}
|
|
|
|
static const struct vipx_context_ops vipx_context_ops = {
|
|
.load_kernel_binary = vipx_context_load_kernel_binary,
|
|
.unload_kernel_binary = vipx_context_unload_kernel_binary,
|
|
.load_graph_info = vipx_context_load_graph_info,
|
|
.unload_graph_info = vipx_context_unload_graph_info,
|
|
.execute_submodel = vipx_context_execute_submodel
|
|
};
|
|
|
|
struct vipx_context *vipx_context_create(struct vipx_core *core)
|
|
{
|
|
int ret;
|
|
struct vipx_context *vctx;
|
|
|
|
vipx_enter();
|
|
vctx = kzalloc(sizeof(*vctx), GFP_KERNEL);
|
|
if (!vctx) {
|
|
ret = -ENOMEM;
|
|
vipx_err("Failed to alloc context\n");
|
|
goto p_err;
|
|
}
|
|
vctx->core = core;
|
|
|
|
vctx->idx = vipx_util_bitmap_get_zero_bit(core->vctx_map,
|
|
VIPX_MAX_CONTEXT);
|
|
if (vctx->idx == VIPX_MAX_CONTEXT) {
|
|
ret = -ENOMEM;
|
|
vipx_err("Failed to get idx of context\n");
|
|
goto p_err_bitmap;
|
|
}
|
|
|
|
core->vctx_count++;
|
|
list_add_tail(&vctx->list, &core->vctx_list);
|
|
|
|
mutex_init(&vctx->lock);
|
|
|
|
vctx->vops = &vipx_context_ops;
|
|
vctx->binary_count = 0;
|
|
INIT_LIST_HEAD(&vctx->binary_list);
|
|
spin_lock_init(&vctx->binary_slock);
|
|
|
|
vipx_queue_init(vctx);
|
|
|
|
vctx->state = BIT(VIPX_CONTEXT_OPEN);
|
|
vipx_leave();
|
|
return vctx;
|
|
p_err_bitmap:
|
|
kfree(vctx);
|
|
p_err:
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void vipx_context_destroy(struct vipx_context *vctx)
|
|
{
|
|
struct vipx_core *core;
|
|
|
|
vipx_enter();
|
|
core = vctx->core;
|
|
|
|
if (vctx->binary_count)
|
|
vipx_kernel_binary_all_remove(vctx);
|
|
|
|
mutex_destroy(&vctx->lock);
|
|
list_del(&vctx->list);
|
|
core->vctx_count--;
|
|
vipx_util_bitmap_clear_bit(core->vctx_map, vctx->idx);
|
|
kfree(vctx);
|
|
vipx_leave();
|
|
}
|