| /* |
| * 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; |
| } |