blob: 553f650f9c78e7ca7a02df1b1a988d06872fad56 [file] [log] [blame]
/*
* 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();
}