blob: 8847f6a7f72528150f1d9ec77fc2267aec313960 [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 <linux/delay.h>
#include "vipx-log.h"
#include "vipx-core.h"
#include "vipx-queue.h"
#include "vipx-context.h"
#include "vipx-graphmgr.h"
#include "vipx-graph.h"
#define VIPX_GRAPH_MAX_PRIORITY (20)
#define VIPX_GRAPH_TIMEOUT (3 * HZ)
static int __vipx_graph_start(struct vipx_graph *graph)
{
int ret;
vipx_enter();
if (test_bit(VIPX_GRAPH_STATE_START, &graph->state))
return 0;
ret = vipx_graphmgr_grp_start(graph->owner, graph);
if (ret)
goto p_err;
set_bit(VIPX_GRAPH_STATE_START, &graph->state);
vipx_leave();
return 0;
p_err:
return ret;
}
static int __vipx_graph_stop(struct vipx_graph *graph)
{
unsigned int timeout, retry;
struct vipx_taskmgr *tmgr;
struct vipx_task *control;
vipx_enter();
if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state))
return 0;
tmgr = &graph->taskmgr;
if (tmgr->req_cnt + tmgr->pre_cnt) {
control = &graph->control;
control->message = VIPX_CTRL_STOP;
vipx_graphmgr_queue(graph->owner, control);
timeout = wait_event_timeout(graph->control_wq,
control->message == VIPX_CTRL_STOP_DONE,
VIPX_GRAPH_TIMEOUT);
if (!timeout)
vipx_err("wait time(%u ms) is expired (%u)\n",
jiffies_to_msecs(VIPX_GRAPH_TIMEOUT),
graph->idx);
}
retry = VIPX_STOP_WAIT_COUNT;
while (tmgr->req_cnt) {
if (!retry)
break;
vipx_warn("Waiting request count(%u) to be zero (%u/%u)\n",
tmgr->req_cnt, retry, graph->idx);
udelay(10);
}
if (tmgr->req_cnt)
vipx_err("request count(%u) is remained (%u)\n",
tmgr->req_cnt, graph->idx);
retry = VIPX_STOP_WAIT_COUNT;
while (tmgr->pre_cnt) {
if (!retry)
break;
vipx_warn("Waiting prepare count(%u) to be zero (%u/%u)\n",
tmgr->pre_cnt, retry, graph->idx);
udelay(10);
}
if (tmgr->pre_cnt)
vipx_err("prepare count(%u) is remained (%u)\n",
tmgr->pre_cnt, graph->idx);
retry = VIPX_STOP_WAIT_COUNT;
while (tmgr->pro_cnt) {
if (!retry)
break;
vipx_warn("Waiting process count(%u) to be zero (%u/%u)\n",
tmgr->pro_cnt, retry, graph->idx);
udelay(10);
}
if (tmgr->pro_cnt)
vipx_err("process count(%u) is remained (%u)\n",
tmgr->pro_cnt, graph->idx);
vipx_graphmgr_grp_stop(graph->owner, graph);
vipx_task_flush(tmgr);
clear_bit(VIPX_GRAPH_STATE_START, &graph->state);
vipx_leave();
return 0;
}
static int vipx_graph_set_graph(struct vipx_graph *graph,
struct vs4l_graph *ginfo)
{
int ret;
vipx_enter();
if (test_bit(VIPX_GRAPH_STATE_CONFIG, &graph->state)) {
ret = -EINVAL;
vipx_err("graph(%u) is already configured (%lu)\n",
graph->idx, graph->state);
goto p_err;
}
if (ginfo->priority > VIPX_GRAPH_MAX_PRIORITY) {
vipx_warn("graph(%u) priority is over (%u/%u)\n",
graph->idx, ginfo->priority,
VIPX_GRAPH_MAX_PRIORITY);
ginfo->priority = VIPX_GRAPH_MAX_PRIORITY;
}
graph->uid = ginfo->id;
graph->flags = ginfo->flags;
graph->priority = ginfo->priority;
set_bit(VIPX_GRAPH_STATE_CONFIG, &graph->state);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_set_format(struct vipx_graph *graph,
struct vs4l_format_list *flist)
{
int ret;
struct vipx_format_list *in_flist;
struct vipx_format_list *ot_flist;
unsigned int cnt;
vipx_enter();
in_flist = &graph->inflist;
ot_flist = &graph->otflist;
if (flist->direction == VS4L_DIRECTION_IN) {
if (in_flist->count != flist->count) {
kfree(in_flist->formats);
in_flist->count = flist->count;
in_flist->formats = kcalloc(in_flist->count,
sizeof(*in_flist->formats), GFP_KERNEL);
if (!in_flist->formats) {
ret = -ENOMEM;
vipx_err("Failed to alloc in_flist formats\n");
goto p_err;
}
for (cnt = 0; cnt < in_flist->count; ++cnt) {
in_flist->formats[cnt].format =
flist->formats[cnt].format;
in_flist->formats[cnt].plane =
flist->formats[cnt].plane;
in_flist->formats[cnt].width =
flist->formats[cnt].width;
in_flist->formats[cnt].height =
flist->formats[cnt].height;
}
}
} else if (flist->direction == VS4L_DIRECTION_OT) {
if (ot_flist->count != flist->count) {
kfree(ot_flist->formats);
ot_flist->count = flist->count;
ot_flist->formats = kcalloc(ot_flist->count,
sizeof(*ot_flist->formats), GFP_KERNEL);
if (!ot_flist->formats) {
ret = -ENOMEM;
vipx_err("Failed to alloc ot_flist formats\n");
goto p_err;
}
for (cnt = 0; cnt < ot_flist->count; ++cnt) {
ot_flist->formats[cnt].format =
flist->formats[cnt].format;
ot_flist->formats[cnt].plane =
flist->formats[cnt].plane;
ot_flist->formats[cnt].width =
flist->formats[cnt].width;
ot_flist->formats[cnt].height =
flist->formats[cnt].height;
}
}
} else {
ret = -EINVAL;
vipx_err("invalid direction (%d)\n", flist->direction);
goto p_err;
}
vipx_leave();
return 0;
p_err:
kfree(in_flist->formats);
in_flist->formats = NULL;
in_flist->count = 0;
kfree(ot_flist->formats);
ot_flist->formats = NULL;
ot_flist->count = 0;
return ret;
}
static int vipx_graph_set_param(struct vipx_graph *graph,
struct vs4l_param_list *plist)
{
vipx_enter();
set_bit(VIPX_GRAPH_FLAG_UPDATE_PARAM, &graph->flags);
vipx_leave();
return 0;
}
static int vipx_graph_set_ctrl(struct vipx_graph *graph,
struct vs4l_ctrl *ctrl)
{
vipx_enter();
vipx_graph_print(graph);
vipx_leave();
return 0;
}
static int vipx_graph_queue(struct vipx_graph *graph,
struct vipx_container_list *incl,
struct vipx_container_list *otcl)
{
int ret;
struct vipx_taskmgr *tmgr;
struct vipx_task *task;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) {
ret = -EINVAL;
vipx_err("graph(%u) is not started (%lu)\n",
graph->idx, graph->state);
goto p_err;
}
if (incl->id != otcl->id) {
vipx_warn("buffer id is incoincidence (%u/%u)\n",
incl->id, otcl->id);
otcl->id = incl->id;
}
spin_lock_irqsave(&tmgr->slock, flags);
task = vipx_task_pick_fre_to_req(tmgr);
spin_unlock_irqrestore(&tmgr->slock, flags);
if (!task) {
ret = -ENOMEM;
vipx_err("free task is not remained (%u)\n", graph->idx);
vipx_task_print_all(tmgr);
goto p_err;
}
graph->inhash[incl->index] = task->index;
graph->othash[otcl->index] = task->index;
graph->input_cnt++;
task->id = incl->id;
task->incl = incl;
task->otcl = otcl;
task->message = VIPX_TASK_REQUEST;
task->param0 = 0;
task->param1 = 0;
task->param2 = 0;
task->param3 = 0;
vipx_graphmgr_queue(graph->owner, task);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_deque(struct vipx_graph *graph,
struct vipx_container_list *clist)
{
int ret;
struct vipx_taskmgr *tmgr;
unsigned int tidx;
struct vipx_task *task;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) {
ret = -EINVAL;
vipx_err("graph(%u) is not started (%lu)\n",
graph->idx, graph->state);
goto p_err;
}
if (clist->direction == VS4L_DIRECTION_IN)
tidx = graph->inhash[clist->index];
else
tidx = graph->othash[clist->index];
if (tidx >= VIPX_MAX_TASK) {
ret = -EINVAL;
vipx_err("task index(%u) invalid (%u)\n", tidx, graph->idx);
goto p_err;
}
task = &tmgr->task[tidx];
if (task->state != VIPX_TASK_STATE_COMPLETE) {
ret = -EINVAL;
vipx_err("task(%u) state(%d) is invalid (%u)\n",
tidx, task->state, graph->idx);
goto p_err;
}
if (clist->direction == VS4L_DIRECTION_IN) {
if (task->incl != clist) {
ret = -EINVAL;
vipx_err("incl ptr is invalid (%u)\n", graph->idx);
goto p_err;
}
graph->inhash[clist->index] = VIPX_MAX_TASK;
task->incl = NULL;
} else {
if (task->otcl != clist) {
ret = -EINVAL;
vipx_err("otcl ptr is invalid (%u)\n", graph->idx);
goto p_err;
}
graph->othash[clist->index] = VIPX_MAX_TASK;
task->otcl = NULL;
}
if (task->incl || task->otcl)
return 0;
spin_lock_irqsave(&tmgr->slock, flags);
vipx_task_trans_com_to_fre(tmgr, task);
spin_unlock_irqrestore(&tmgr->slock, flags);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_start(struct vipx_graph *graph)
{
int ret;
vipx_enter();
ret = __vipx_graph_start(graph);
if (ret)
goto p_err;
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_stop(struct vipx_graph *graph)
{
vipx_enter();
__vipx_graph_stop(graph);
vipx_leave();
return 0;
}
const struct vipx_queue_gops vipx_queue_gops = {
.set_graph = vipx_graph_set_graph,
.set_format = vipx_graph_set_format,
.set_param = vipx_graph_set_param,
.set_ctrl = vipx_graph_set_ctrl,
.queue = vipx_graph_queue,
.deque = vipx_graph_deque,
.start = vipx_graph_start,
.stop = vipx_graph_stop
};
static int vipx_graph_control(struct vipx_graph *graph, struct vipx_task *task)
{
int ret;
struct vipx_taskmgr *tmgr;
vipx_enter();
tmgr = &graph->taskmgr;
switch (task->message) {
case VIPX_CTRL_STOP:
graph->control.message = VIPX_CTRL_STOP_DONE;
wake_up(&graph->control_wq);
break;
default:
ret = -EINVAL;
vipx_err("invalid task message(%u) of graph(%u)\n",
task->message, graph->idx);
vipx_task_print_all(tmgr);
goto p_err;
}
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_request(struct vipx_graph *graph, struct vipx_task *task)
{
int ret;
struct vipx_taskmgr *tmgr;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
if (task->state != VIPX_TASK_STATE_REQUEST) {
ret = -EINVAL;
vipx_err("task state(%u) is not REQUEST (graph:%u)\n",
task->state, graph->idx);
goto p_err;
}
spin_lock_irqsave(&tmgr->slock, flags);
vipx_task_trans_req_to_pre(tmgr, task);
spin_unlock_irqrestore(&tmgr->slock, flags);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_process(struct vipx_graph *graph, struct vipx_task *task)
{
int ret;
struct vipx_taskmgr *tmgr;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
if (task->state != VIPX_TASK_STATE_PREPARE) {
ret = -EINVAL;
vipx_err("task state(%u) is not PREPARE (graph:%u)\n",
task->state, graph->idx);
goto p_err;
}
spin_lock_irqsave(&tmgr->slock, flags);
vipx_task_trans_pre_to_pro(tmgr, task);
spin_unlock_irqrestore(&tmgr->slock, flags);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_cancel(struct vipx_graph *graph, struct vipx_task *task)
{
int ret;
struct vipx_taskmgr *tmgr;
struct vipx_queue_list *qlist;
struct vipx_container_list *incl, *otcl;
unsigned long result;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
qlist = &graph->vctx->queue_list;
incl = task->incl;
otcl = task->otcl;
if (task->state != VIPX_TASK_STATE_PROCESS) {
ret = -EINVAL;
vipx_err("task state(%u) is not PROCESS (graph:%u)\n",
task->state, graph->idx);
goto p_err;
}
spin_lock_irqsave(&tmgr->slock, flags);
vipx_task_trans_pro_to_com(tmgr, task);
spin_unlock_irqrestore(&tmgr->slock, flags);
graph->recent = task->id;
graph->done_cnt++;
result = 0;
set_bit(VS4L_CL_FLAG_DONE, &result);
set_bit(VS4L_CL_FLAG_INVALID, &result);
vipx_queue_done(qlist, incl, otcl, result);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_done(struct vipx_graph *graph, struct vipx_task *task)
{
int ret;
struct vipx_taskmgr *tmgr;
struct vipx_queue_list *qlist;
struct vipx_container_list *incl, *otcl;
unsigned long result;
unsigned long flags;
vipx_enter();
tmgr = &graph->taskmgr;
qlist = &graph->vctx->queue_list;
incl = task->incl;
otcl = task->otcl;
if (task->state != VIPX_TASK_STATE_PROCESS) {
ret = -EINVAL;
vipx_err("task state(%u) is not PROCESS (graph:%u)\n",
task->state, graph->idx);
goto p_err;
}
spin_lock_irqsave(&tmgr->slock, flags);
vipx_task_trans_pro_to_com(tmgr, task);
spin_unlock_irqrestore(&tmgr->slock, flags);
graph->recent = task->id;
graph->done_cnt++;
result = 0;
if (task->param0) {
set_bit(VS4L_CL_FLAG_DONE, &result);
set_bit(VS4L_CL_FLAG_INVALID, &result);
} else {
set_bit(VS4L_CL_FLAG_DONE, &result);
}
vipx_queue_done(qlist, incl, otcl, result);
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_update_param(struct vipx_graph *graph,
struct vipx_task *task)
{
vipx_enter();
vipx_leave();
return 0;
}
static const struct vipx_graph_ops vipx_graph_ops = {
.control = vipx_graph_control,
.request = vipx_graph_request,
.process = vipx_graph_process,
.cancel = vipx_graph_cancel,
.done = vipx_graph_done,
.update_param = vipx_graph_update_param
};
static struct vipx_graph_model *vipx_graph_create_model(
struct vipx_graph *graph, struct vipx_common_graph_info *ginfo)
{
int ret;
struct vipx_graph_model *gmodel;
vipx_enter();
gmodel = kzalloc(sizeof(*gmodel), GFP_KERNEL);
if (!gmodel) {
ret = -ENOMEM;
vipx_err("Failed to alloc graph model\n");
goto p_err_alloc;
}
list_add_tail(&gmodel->list, &graph->gmodel_list);
graph->gmodel_count++;
gmodel->id = ginfo->gid;
INIT_LIST_HEAD(&gmodel->kbin_list);
memcpy(&gmodel->common_ginfo, ginfo, sizeof(gmodel->common_ginfo));
vipx_leave();
return gmodel;
p_err_alloc:
return ERR_PTR(ret);
}
static struct vipx_graph_model *vipx_graph_get_model(struct vipx_graph *graph,
unsigned int id)
{
unsigned int model_id = GET_COMMON_GRAPH_MODEL_ID(id);
struct vipx_graph_model *gmodel, *temp;
vipx_enter();
list_for_each_entry_safe(gmodel, temp, &graph->gmodel_list, list) {
unsigned int gmodel_id = GET_COMMON_GRAPH_MODEL_ID(gmodel->id);
if (gmodel_id == model_id) {
vipx_leave();
return gmodel;
}
}
vipx_err("Failed to get gmodel (%d/%u)\n", id, graph->gmodel_count);
return ERR_PTR(-EINVAL);
}
static int vipx_graph_destroy_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel)
{
vipx_enter();
graph->gmodel_count--;
list_del(&gmodel->list);
kfree(gmodel);
vipx_leave();
return 0;
}
static int vipx_graph_register_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel)
{
int ret;
vipx_enter();
ret = vipx_graphmgr_register_model(graph->owner, gmodel);
if (ret)
goto p_err;
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_unregister_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel)
{
int ret;
vipx_enter();
ret = vipx_graphmgr_unregister_model(graph->owner, gmodel);
if (ret)
goto p_err;
vipx_leave();
return 0;
p_err:
return ret;
}
static int vipx_graph_start_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel)
{
vipx_enter();
vipx_leave();
return 0;
}
static int vipx_graph_stop_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel)
{
vipx_enter();
vipx_leave();
return 0;
}
static int vipx_graph_execute_model(struct vipx_graph *graph,
struct vipx_graph_model *gmodel,
struct vipx_common_execute_info *einfo)
{
int ret;
vipx_enter();
ret = vipx_graphmgr_execute_model(graph->owner, gmodel, einfo);
if (ret)
goto p_err;
vipx_leave();
return 0;
p_err:
return ret;
}
static void __vipx_graph_cleanup_buffer(struct vipx_graph *graph,
struct vipx_buffer *buf)
{
struct vipx_memory *mem;
vipx_enter();
if (!buf)
return;
mem = &graph->vctx->core->system->memory;
mem->mops->sync_for_cpu(mem, buf);
mem->mops->unmap_dmabuf(mem, buf);
kfree(buf);
vipx_leave();
}
static void __vipx_graph_cleanup_model(struct vipx_graph *graph)
{
struct vipx_context *vctx;
struct vipx_graph_model *gmodel, *temp;
vipx_enter();
if (!graph->gmodel_count) {
vipx_leave();
return;
}
vctx = graph->vctx;
list_for_each_entry_safe(gmodel, temp, &graph->gmodel_list, list) {
vctx->graph_ops->unregister_model(graph, gmodel);
__vipx_graph_cleanup_buffer(graph, gmodel->user_param_buffer);
__vipx_graph_cleanup_buffer(graph, gmodel->bias);
__vipx_graph_cleanup_buffer(graph, gmodel->weight);
__vipx_graph_cleanup_buffer(graph, gmodel->temp_buf);
__vipx_graph_cleanup_buffer(graph, gmodel->graph);
vctx->graph_ops->destroy_model(graph, gmodel);
}
vipx_leave();
}
const struct vipx_context_gops vipx_context_gops = {
.create_model = vipx_graph_create_model,
.get_model = vipx_graph_get_model,
.destroy_model = vipx_graph_destroy_model,
.register_model = vipx_graph_register_model,
.unregister_model = vipx_graph_unregister_model,
.start_model = vipx_graph_start_model,
.stop_model = vipx_graph_stop_model,
.execute_model = vipx_graph_execute_model
};
void vipx_graph_print(struct vipx_graph *graph)
{
vipx_enter();
vipx_leave();
}
struct vipx_graph *vipx_graph_create(struct vipx_context *vctx,
void *graphmgr)
{
int ret;
struct vipx_graph *graph;
struct vipx_taskmgr *tmgr;
unsigned int idx;
vipx_enter();
graph = kzalloc(sizeof(*graph), GFP_KERNEL);
if (!graph) {
ret = -ENOMEM;
vipx_err("Failed to allocate graph\n");
goto p_err_kzalloc;
}
ret = vipx_graphmgr_grp_register(graphmgr, graph);
if (ret)
goto p_err_grp_register;
graph->owner = graphmgr;
graph->gops = &vipx_graph_ops;
mutex_init(&graph->local_lock);
graph->control.owner = graph;
graph->control.message = VIPX_CTRL_NONE;
init_waitqueue_head(&graph->control_wq);
tmgr = &graph->taskmgr;
spin_lock_init(&tmgr->slock);
ret = vipx_task_init(tmgr, graph->idx, graph);
if (ret)
goto p_err_task_init;
for (idx = 0; idx < VIPX_MAX_TASK; ++idx) {
graph->inhash[idx] = VIPX_MAX_TASK;
graph->othash[idx] = VIPX_MAX_TASK;
}
INIT_LIST_HEAD(&graph->gmodel_list);
graph->gmodel_count = 0;
vctx->graph_ops = &vipx_context_gops;
graph->vctx = vctx;
vipx_leave();
return graph;
p_err_task_init:
p_err_grp_register:
kfree(graph);
p_err_kzalloc:
return ERR_PTR(ret);
}
int vipx_graph_destroy(struct vipx_graph *graph)
{
vipx_enter();
__vipx_graph_stop(graph);
__vipx_graph_cleanup_model(graph);
vipx_graphmgr_grp_unregister(graph->owner, graph);
kfree(graph->inflist.formats);
graph->inflist.formats = NULL;
graph->inflist.count = 0;
kfree(graph->otflist.formats);
graph->otflist.formats = NULL;
graph->otflist.count = 0;
kfree(graph);
vipx_leave();
return 0;
}