1080 lines
24 KiB
C
Executable File
1080 lines
24 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/sched/types.h>
|
|
|
|
#include "vipx-log.h"
|
|
#include "vipx-common-type.h"
|
|
#include "vipx-device.h"
|
|
#include "vipx-graphmgr.h"
|
|
|
|
static void __vipx_taskdesc_set_free(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_FREE;
|
|
list_add_tail(&taskdesc->list, &gmgr->tdfre_list);
|
|
gmgr->tdfre_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static struct vipx_taskdesc *__vipx_taskdesc_get_first_free(
|
|
struct vipx_graphmgr *gmgr)
|
|
{
|
|
struct vipx_taskdesc *taskdesc = NULL;
|
|
|
|
vipx_enter();
|
|
if (gmgr->tdfre_cnt)
|
|
taskdesc = list_first_entry(&gmgr->tdfre_list,
|
|
struct vipx_taskdesc, list);
|
|
vipx_leave();
|
|
return taskdesc;
|
|
}
|
|
|
|
static void __vipx_taskdesc_set_ready(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_READY;
|
|
list_add_tail(&taskdesc->list, &gmgr->tdrdy_list);
|
|
gmgr->tdrdy_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static struct vipx_taskdesc *__vipx_taskdesc_pop_ready(
|
|
struct vipx_graphmgr *gmgr)
|
|
{
|
|
struct vipx_taskdesc *taskdesc = NULL;
|
|
|
|
vipx_enter();
|
|
if (gmgr->tdrdy_cnt) {
|
|
taskdesc = container_of(gmgr->tdrdy_list.next,
|
|
struct vipx_taskdesc, list);
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdrdy_cnt--;
|
|
}
|
|
vipx_leave();
|
|
return taskdesc;
|
|
}
|
|
|
|
static void __vipx_taskdesc_set_request(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_REQUEST;
|
|
list_add_tail(&taskdesc->list, &gmgr->tdreq_list);
|
|
gmgr->tdreq_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_set_request_sched(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc, struct vipx_taskdesc *next)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_REQUEST;
|
|
list_add_tail(&taskdesc->list, &next->list);
|
|
gmgr->tdreq_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_set_process(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_PROCESS;
|
|
list_add_tail(&taskdesc->list, &gmgr->tdpro_list);
|
|
gmgr->tdpro_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_set_complete(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
taskdesc->state = VIPX_TASKDESC_STATE_COMPLETE;
|
|
list_add_tail(&taskdesc->list, &gmgr->tdcom_list);
|
|
gmgr->tdcom_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_fre_to_rdy(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdfre_cnt) {
|
|
vipx_warn("tdfre_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_FREE) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdfre_cnt--;
|
|
__vipx_taskdesc_set_ready(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_rdy_to_fre(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdrdy_cnt) {
|
|
vipx_warn("tdrdy_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_READY) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdrdy_cnt--;
|
|
__vipx_taskdesc_set_free(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_req_to_pro(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdreq_cnt) {
|
|
vipx_warn("tdreq_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_REQUEST) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdreq_cnt--;
|
|
__vipx_taskdesc_set_process(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_req_to_fre(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdreq_cnt) {
|
|
vipx_warn("tdreq_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_REQUEST) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdreq_cnt--;
|
|
__vipx_taskdesc_set_free(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_alc_to_pro(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdalc_cnt) {
|
|
vipx_warn("tdalc_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_ALLOC) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdalc_cnt--;
|
|
__vipx_taskdesc_set_process(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_alc_to_fre(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdalc_cnt) {
|
|
vipx_warn("tdalc_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_ALLOC) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdalc_cnt--;
|
|
__vipx_taskdesc_set_free(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_pro_to_com(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdpro_cnt) {
|
|
vipx_warn("tdpro_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_PROCESS) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdpro_cnt--;
|
|
__vipx_taskdesc_set_complete(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_pro_to_fre(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdpro_cnt) {
|
|
vipx_warn("tdpro_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_PROCESS) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdpro_cnt--;
|
|
__vipx_taskdesc_set_free(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_taskdesc_trans_com_to_fre(struct vipx_graphmgr *gmgr,
|
|
struct vipx_taskdesc *taskdesc)
|
|
{
|
|
vipx_enter();
|
|
if (!gmgr->tdcom_cnt) {
|
|
vipx_warn("tdcom_cnt is zero\n");
|
|
return;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_COMPLETE) {
|
|
vipx_warn("state(%x) is invlid\n", taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
list_del(&taskdesc->list);
|
|
gmgr->tdcom_cnt--;
|
|
__vipx_taskdesc_set_free(gmgr, taskdesc);
|
|
vipx_leave();
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_init(struct vipx_interface *itf,
|
|
struct vipx_graph *graph)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_deinit(struct vipx_interface *itf,
|
|
struct vipx_graph *graph)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_create(struct vipx_interface *itf,
|
|
struct vipx_graph *graph)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_destroy(struct vipx_interface *itf,
|
|
struct vipx_graph *graph)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_config(struct vipx_interface *itf,
|
|
struct vipx_graph *graph)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_process(struct vipx_interface *itf,
|
|
struct vipx_graph *graph, struct vipx_task *task)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static void __vipx_graphmgr_sched(struct vipx_graphmgr *gmgr)
|
|
{
|
|
int ret;
|
|
struct vipx_taskdesc *ready_td, *list_td, *temp;
|
|
struct vipx_graph *graph;
|
|
struct vipx_task *task;
|
|
|
|
vipx_enter();
|
|
|
|
mutex_lock(&gmgr->tdlock);
|
|
|
|
while (1) {
|
|
ready_td = __vipx_taskdesc_pop_ready(gmgr);
|
|
if (!ready_td)
|
|
break;
|
|
|
|
if (!gmgr->tdreq_cnt) {
|
|
__vipx_taskdesc_set_request(gmgr, ready_td);
|
|
continue;
|
|
}
|
|
|
|
list_for_each_entry_safe(list_td, temp, &gmgr->tdreq_list,
|
|
list) {
|
|
if (ready_td->priority > list_td->priority) {
|
|
__vipx_taskdesc_set_request_sched(gmgr,
|
|
ready_td, list_td);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ready_td->state == VIPX_TASKDESC_STATE_READY)
|
|
__vipx_taskdesc_set_request(gmgr, ready_td);
|
|
}
|
|
|
|
list_for_each_entry_safe(list_td, temp, &gmgr->tdreq_list, list) {
|
|
__vipx_taskdesc_trans_req_to_pro(gmgr, list_td);
|
|
}
|
|
|
|
mutex_unlock(&gmgr->tdlock);
|
|
|
|
list_for_each_entry_safe(list_td, temp, &gmgr->tdpro_list, list) {
|
|
graph = list_td->graph;
|
|
task = list_td->task;
|
|
|
|
ret = __vipx_graphmgr_itf_process(gmgr->interface, graph, task);
|
|
if (ret) {
|
|
ret = graph->gops->cancel(graph, task);
|
|
if (ret)
|
|
return;
|
|
|
|
list_td->graph = NULL;
|
|
list_td->task = NULL;
|
|
__vipx_taskdesc_trans_pro_to_fre(gmgr, list_td);
|
|
continue;
|
|
}
|
|
|
|
__vipx_taskdesc_trans_pro_to_com(gmgr, list_td);
|
|
}
|
|
|
|
gmgr->sched_cnt++;
|
|
vipx_leave();
|
|
}
|
|
|
|
static void vipx_graph_thread(struct kthread_work *work)
|
|
{
|
|
int ret;
|
|
struct vipx_task *task;
|
|
struct vipx_graph *graph;
|
|
struct vipx_graphmgr *gmgr;
|
|
struct vipx_taskdesc *taskdesc, *temp;
|
|
|
|
vipx_enter();
|
|
task = container_of(work, struct vipx_task, work);
|
|
graph = task->owner;
|
|
gmgr = graph->owner;
|
|
|
|
switch (task->message) {
|
|
case VIPX_TASK_REQUEST:
|
|
ret = graph->gops->request(graph, task);
|
|
if (ret)
|
|
return;
|
|
|
|
taskdesc = __vipx_taskdesc_get_first_free(gmgr);
|
|
if (!taskdesc) {
|
|
vipx_err("taskdesc is NULL\n");
|
|
return;
|
|
}
|
|
|
|
task->tdindex = taskdesc->index;
|
|
taskdesc->graph = graph;
|
|
taskdesc->task = task;
|
|
taskdesc->priority = graph->priority;
|
|
__vipx_taskdesc_trans_fre_to_rdy(gmgr, taskdesc);
|
|
break;
|
|
case VIPX_CTRL_STOP:
|
|
list_for_each_entry_safe(taskdesc, temp,
|
|
&gmgr->tdrdy_list, list) {
|
|
if (taskdesc->graph->idx != graph->idx)
|
|
continue;
|
|
|
|
ret = graph->gops->cancel(graph, taskdesc->task);
|
|
if (ret)
|
|
return;
|
|
|
|
taskdesc->graph = NULL;
|
|
taskdesc->task = NULL;
|
|
__vipx_taskdesc_trans_rdy_to_fre(gmgr, taskdesc);
|
|
}
|
|
|
|
mutex_lock(&gmgr->tdlock);
|
|
|
|
list_for_each_entry_safe(taskdesc, temp,
|
|
&gmgr->tdreq_list, list) {
|
|
if (taskdesc->graph->idx != graph->idx)
|
|
continue;
|
|
|
|
ret = graph->gops->cancel(graph, taskdesc->task);
|
|
if (ret)
|
|
return;
|
|
|
|
taskdesc->graph = NULL;
|
|
taskdesc->task = NULL;
|
|
__vipx_taskdesc_trans_req_to_fre(gmgr, taskdesc);
|
|
}
|
|
|
|
mutex_unlock(&gmgr->tdlock);
|
|
|
|
ret = graph->gops->control(graph, task);
|
|
if (ret)
|
|
return;
|
|
/* TODO check return/break */
|
|
return;
|
|
default:
|
|
vipx_err("message of task is invalid (%d)\n", task->message);
|
|
return;
|
|
}
|
|
|
|
__vipx_graphmgr_sched(gmgr);
|
|
vipx_leave();
|
|
}
|
|
|
|
static void vipx_interface_thread(struct kthread_work *work)
|
|
{
|
|
int ret;
|
|
struct vipx_task *task, *itask;
|
|
struct vipx_interface *itf;
|
|
struct vipx_graphmgr *gmgr;
|
|
unsigned int taskdesc_index;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_graph *graph;
|
|
struct vipx_taskdesc *taskdesc;
|
|
unsigned long flags;
|
|
|
|
vipx_enter();
|
|
itask = container_of(work, struct vipx_task, work);
|
|
itf = itask->owner;
|
|
itaskmgr = &itf->taskmgr;
|
|
gmgr = itf->cookie;
|
|
|
|
if (itask->findex >= VIPX_MAX_TASK) {
|
|
vipx_err("task index(%u) is invalid (%u)\n",
|
|
itask->findex, VIPX_MAX_TASK);
|
|
return;
|
|
}
|
|
|
|
switch (itask->message) {
|
|
case VIPX_TASK_ALLOCATE:
|
|
taskdesc_index = itask->tdindex;
|
|
if (taskdesc_index >= VIPX_MAX_TASKDESC) {
|
|
vipx_err("taskdesc index(%u) is invalid (%u)\n",
|
|
taskdesc_index, VIPX_MAX_TASKDESC);
|
|
return;
|
|
}
|
|
|
|
taskdesc = &gmgr->taskdesc[taskdesc_index];
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_ALLOC) {
|
|
vipx_err("taskdesc state(%u) is not ALLOC (%u)\n",
|
|
taskdesc->state, taskdesc_index);
|
|
return;
|
|
}
|
|
|
|
graph = taskdesc->graph;
|
|
if (!graph) {
|
|
vipx_err("graph is NULL (%u)\n", taskdesc_index);
|
|
return;
|
|
}
|
|
|
|
task = taskdesc->task;
|
|
if (!task) {
|
|
vipx_err("task is NULL (%u)\n", taskdesc_index);
|
|
return;
|
|
}
|
|
|
|
task->message = itask->message;
|
|
task->param0 = itask->param2;
|
|
task->param1 = itask->param3;
|
|
|
|
/* return status check */
|
|
if (task->param0) {
|
|
vipx_err("task allocation failed (%lu, %lu)\n",
|
|
task->param0, task->param1);
|
|
|
|
ret = graph->gops->cancel(graph, task);
|
|
if (ret)
|
|
return;
|
|
|
|
/* taskdesc cleanup */
|
|
mutex_lock(&gmgr->tdlock);
|
|
taskdesc->graph = NULL;
|
|
taskdesc->task = NULL;
|
|
__vipx_taskdesc_trans_alc_to_fre(gmgr, taskdesc);
|
|
mutex_unlock(&gmgr->tdlock);
|
|
} else {
|
|
/* taskdesc transition */
|
|
mutex_lock(&gmgr->tdlock);
|
|
__vipx_taskdesc_trans_alc_to_pro(gmgr, taskdesc);
|
|
mutex_unlock(&gmgr->tdlock);
|
|
}
|
|
|
|
/* itask cleanup */
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
break;
|
|
case VIPX_TASK_PROCESS:
|
|
taskdesc_index = itask->tdindex;
|
|
if (taskdesc_index >= VIPX_MAX_TASKDESC) {
|
|
vipx_err("taskdesc index(%u) is invalid (%u)\n",
|
|
taskdesc_index, VIPX_MAX_TASKDESC);
|
|
return;
|
|
}
|
|
|
|
taskdesc = &gmgr->taskdesc[taskdesc_index];
|
|
if (taskdesc->state == VIPX_TASKDESC_STATE_FREE) {
|
|
vipx_err("taskdesc(%u) state is FREE\n",
|
|
taskdesc_index);
|
|
|
|
/* itask cleanup */
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
break;
|
|
}
|
|
|
|
if (taskdesc->state != VIPX_TASKDESC_STATE_COMPLETE) {
|
|
vipx_err("taskdesc state is not COMPLETE (%u)\n",
|
|
taskdesc->state);
|
|
return;
|
|
}
|
|
|
|
graph = taskdesc->graph;
|
|
if (!graph) {
|
|
vipx_err("graph is NULL (%u)\n", taskdesc_index);
|
|
return;
|
|
}
|
|
|
|
task = taskdesc->task;
|
|
if (!task) {
|
|
vipx_err("task is NULL (%u)\n", taskdesc_index);
|
|
return;
|
|
}
|
|
|
|
task->message = itask->message;
|
|
task->tdindex = VIPX_MAX_TASKDESC;
|
|
task->param0 = itask->param2;
|
|
task->param1 = itask->param3;
|
|
|
|
ret = graph->gops->done(graph, task);
|
|
if (ret)
|
|
return;
|
|
|
|
/* taskdesc cleanup */
|
|
taskdesc->graph = NULL;
|
|
taskdesc->task = NULL;
|
|
__vipx_taskdesc_trans_com_to_fre(gmgr, taskdesc);
|
|
|
|
/* itask cleanup */
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
break;
|
|
default:
|
|
vipx_err("message of task is invalid (%d)\n", itask->message);
|
|
return;
|
|
}
|
|
|
|
__vipx_graphmgr_sched(gmgr);
|
|
vipx_leave();
|
|
}
|
|
|
|
void vipx_taskdesc_print(struct vipx_graphmgr *gmgr)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
}
|
|
|
|
int vipx_graphmgr_grp_register(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph *graph)
|
|
{
|
|
int ret;
|
|
unsigned int index;
|
|
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
for (index = 0; index < VIPX_MAX_GRAPH; ++index) {
|
|
if (!gmgr->graph[index]) {
|
|
gmgr->graph[index] = graph;
|
|
graph->idx = index;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
if (index >= VIPX_MAX_GRAPH) {
|
|
ret = -EINVAL;
|
|
vipx_err("graph slot is lack\n");
|
|
goto p_err;
|
|
}
|
|
|
|
kthread_init_work(&graph->control.work, vipx_graph_thread);
|
|
for (index = 0; index < VIPX_MAX_TASK; ++index)
|
|
kthread_init_work(&graph->taskmgr.task[index].work,
|
|
vipx_graph_thread);
|
|
|
|
graph->global_lock = &gmgr->mlock;
|
|
atomic_inc(&gmgr->active_cnt);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_grp_unregister(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph *graph)
|
|
{
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
gmgr->graph[graph->idx] = NULL;
|
|
mutex_unlock(&gmgr->mlock);
|
|
atomic_dec(&gmgr->active_cnt);
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_graphmgr_grp_start(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph *graph)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
if (!test_bit(VIPX_GRAPHMGR_ENUM, &gmgr->state)) {
|
|
ret = __vipx_graphmgr_itf_init(gmgr->interface, graph);
|
|
if (ret) {
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err_init;
|
|
}
|
|
set_bit(VIPX_GRAPHMGR_ENUM, &gmgr->state);
|
|
}
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
ret = __vipx_graphmgr_itf_create(gmgr->interface, graph);
|
|
if (ret)
|
|
goto p_err_create;
|
|
|
|
ret = __vipx_graphmgr_itf_config(gmgr->interface, graph);
|
|
if (ret)
|
|
goto p_err_config;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_config:
|
|
p_err_create:
|
|
p_err_init:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_grp_stop(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph *graph)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
ret = __vipx_graphmgr_itf_destroy(gmgr->interface, graph);
|
|
if (ret)
|
|
goto p_err_destroy;
|
|
|
|
mutex_lock(&gmgr->mlock);
|
|
if (test_bit(VIPX_GRAPHMGR_ENUM, &gmgr->state) &&
|
|
atomic_read(&gmgr->active_cnt) == 1) {
|
|
ret = __vipx_graphmgr_itf_deinit(gmgr->interface, graph);
|
|
if (ret) {
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err_deinit;
|
|
}
|
|
clear_bit(VIPX_GRAPHMGR_ENUM, &gmgr->state);
|
|
}
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_deinit:
|
|
p_err_destroy:
|
|
return ret;
|
|
}
|
|
|
|
void vipx_graphmgr_queue(struct vipx_graphmgr *gmgr, struct vipx_task *task)
|
|
{
|
|
vipx_enter();
|
|
kthread_queue_work(&gmgr->worker, &task->work);
|
|
vipx_leave();
|
|
}
|
|
|
|
static bool __vipx_graphmgr_check_model_id(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph_model *gmodel)
|
|
{
|
|
unsigned int current_id, id;
|
|
bool same;
|
|
|
|
vipx_enter();
|
|
current_id = GET_COMMON_GRAPH_MODEL_ID(gmgr->current_model->id);
|
|
id = GET_COMMON_GRAPH_MODEL_ID(gmodel->id);
|
|
|
|
if (current_id == id)
|
|
same = true;
|
|
else
|
|
same = false;
|
|
|
|
vipx_leave();
|
|
return same;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_load_graph(struct vipx_interface *itf,
|
|
struct vipx_graph_model *gmodel)
|
|
{
|
|
int ret;
|
|
unsigned long flags;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_task *itask;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
itask = vipx_task_pick_fre_to_req(itaskmgr);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
if (!itask) {
|
|
ret = -ENOMEM;
|
|
vipx_err("itask is NULL\n");
|
|
goto p_err_pick;
|
|
}
|
|
|
|
itask->id = GET_COMMON_GRAPH_MODEL_ID(gmodel->id);
|
|
itask->message = VIPX_TASK_PROCESS;
|
|
itask->param0 = (unsigned long)&gmodel->common_ginfo;
|
|
|
|
ret = vipx_hw_load_graph(itf, itask);
|
|
|
|
gmodel->time[TIME_LOAD_GRAPH] = itask->time;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
if (ret)
|
|
goto p_err_hw_load_graph;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_hw_load_graph:
|
|
p_err_pick:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_register_model(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph_model *gmodel)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
if (gmgr->current_model) {
|
|
if (!__vipx_graphmgr_check_model_id(gmgr, gmodel)) {
|
|
ret = -EBUSY;
|
|
vipx_err("model is already registered(%#x/%#x)\n",
|
|
gmgr->current_model->id, gmodel->id);
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
gmgr->current_model = gmodel;
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
ret = __vipx_graphmgr_itf_load_graph(gmgr->interface, gmodel);
|
|
if (ret)
|
|
goto p_err_register;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_register:
|
|
mutex_lock(&gmgr->mlock);
|
|
gmgr->current_model = NULL;
|
|
mutex_unlock(&gmgr->mlock);
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_unload_graph(struct vipx_interface *itf,
|
|
struct vipx_graph_model *gmodel)
|
|
{
|
|
int ret;
|
|
unsigned long flags;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_task *itask;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
itask = vipx_task_pick_fre_to_req(itaskmgr);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
if (!itask) {
|
|
ret = -ENOMEM;
|
|
vipx_err("itask is NULL\n");
|
|
goto p_err_pick;
|
|
}
|
|
|
|
itask->id = GET_COMMON_GRAPH_MODEL_ID(gmodel->id);
|
|
itask->message = VIPX_TASK_PROCESS;
|
|
itask->param0 = (unsigned long)&gmodel->common_ginfo;
|
|
|
|
ret = vipx_hw_unload_graph(itf, itask);
|
|
|
|
gmodel->time[TIME_UNLOAD_GRAPH] = itask->time;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
if (ret)
|
|
goto p_err_hw_unload_graph;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_hw_unload_graph:
|
|
p_err_pick:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_unregister_model(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph_model *gmodel)
|
|
{
|
|
int ret;
|
|
struct vipx_graph_model *current_gmodel;
|
|
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
if (!gmgr->current_model) {
|
|
ret = -EFAULT;
|
|
vipx_err("model of manager is empty (%#x)\n", gmodel->id);
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err;
|
|
}
|
|
|
|
if (!__vipx_graphmgr_check_model_id(gmgr, gmodel)) {
|
|
ret = -EINVAL;
|
|
vipx_err("model of manager is different (%#x/%#x)\n",
|
|
gmgr->current_model->id, gmodel->id);
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err;
|
|
}
|
|
|
|
current_gmodel = gmgr->current_model;
|
|
gmgr->current_model = NULL;
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
ret = __vipx_graphmgr_itf_unload_graph(gmgr->interface, current_gmodel);
|
|
if (ret)
|
|
goto p_err_unregister;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_unregister:
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vipx_graphmgr_itf_execute_graph(struct vipx_interface *itf,
|
|
struct vipx_graph_model *gmodel,
|
|
struct vipx_common_execute_info *einfo)
|
|
{
|
|
int ret;
|
|
unsigned long flags;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_task *itask;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
itask = vipx_task_pick_fre_to_req(itaskmgr);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
if (!itask) {
|
|
ret = -ENOMEM;
|
|
vipx_err("itask is NULL\n");
|
|
goto p_err_pick;
|
|
}
|
|
|
|
itask->id = GET_COMMON_GRAPH_MODEL_ID(gmodel->id);
|
|
itask->message = VIPX_TASK_PROCESS;
|
|
itask->param0 = (unsigned long)einfo;
|
|
itask->param1 = (unsigned long)gmodel;
|
|
|
|
ret = vipx_hw_execute_graph(itf, itask);
|
|
|
|
gmodel->time[TIME_EXECUTE_GRAPH] = itask->time;
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_com_to_fre(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
if (ret)
|
|
goto p_err_hw_load_graph;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_hw_load_graph:
|
|
p_err_pick:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_execute_model(struct vipx_graphmgr *gmgr,
|
|
struct vipx_graph_model *gmodel,
|
|
struct vipx_common_execute_info *einfo)
|
|
{
|
|
int ret;
|
|
|
|
vipx_enter();
|
|
mutex_lock(&gmgr->mlock);
|
|
if (gmgr->current_model) {
|
|
if (!__vipx_graphmgr_check_model_id(gmgr, gmodel)) {
|
|
ret = -EBUSY;
|
|
vipx_err("other model is registered(%#x/%#x)\n",
|
|
gmgr->current_model->id, gmodel->id);
|
|
mutex_unlock(&gmgr->mlock);
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
gmgr->current_model = gmodel;
|
|
mutex_unlock(&gmgr->mlock);
|
|
|
|
ret = __vipx_graphmgr_itf_execute_graph(gmgr->interface, gmodel, einfo);
|
|
if (ret)
|
|
goto p_err_execute;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_execute:
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_open(struct vipx_graphmgr *gmgr)
|
|
{
|
|
int ret;
|
|
char name[30];
|
|
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
|
|
|
|
vipx_enter();
|
|
kthread_init_worker(&gmgr->worker);
|
|
snprintf(name, sizeof(name), "vipx_graph");
|
|
gmgr->graph_task = kthread_run(kthread_worker_fn,
|
|
&gmgr->worker, name);
|
|
if (IS_ERR(gmgr->graph_task)) {
|
|
ret = PTR_ERR(gmgr->graph_task);
|
|
vipx_err("kthread_run is fail\n");
|
|
goto p_err_run;
|
|
}
|
|
|
|
ret = sched_setscheduler_nocheck(gmgr->graph_task,
|
|
SCHED_FIFO, ¶m);
|
|
if (ret) {
|
|
vipx_err("sched_setscheduler_nocheck is fail(%d)\n", ret);
|
|
goto p_err_sched;
|
|
}
|
|
|
|
gmgr->current_model = NULL;
|
|
set_bit(VIPX_GRAPHMGR_OPEN, &gmgr->state);
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_sched:
|
|
kthread_stop(gmgr->graph_task);
|
|
p_err_run:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_graphmgr_close(struct vipx_graphmgr *gmgr)
|
|
{
|
|
vipx_enter();
|
|
kthread_stop(gmgr->graph_task);
|
|
clear_bit(VIPX_GRAPHMGR_OPEN, &gmgr->state);
|
|
clear_bit(VIPX_GRAPHMGR_ENUM, &gmgr->state);
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_graphmgr_probe(struct vipx_system *sys)
|
|
{
|
|
struct vipx_graphmgr *gmgr;
|
|
int index;
|
|
|
|
vipx_enter();
|
|
gmgr = &sys->graphmgr;
|
|
gmgr->interface = &sys->interface;
|
|
|
|
atomic_set(&gmgr->active_cnt, 0);
|
|
mutex_init(&gmgr->mlock);
|
|
mutex_init(&gmgr->tdlock);
|
|
|
|
INIT_LIST_HEAD(&gmgr->tdfre_list);
|
|
INIT_LIST_HEAD(&gmgr->tdrdy_list);
|
|
INIT_LIST_HEAD(&gmgr->tdreq_list);
|
|
INIT_LIST_HEAD(&gmgr->tdalc_list);
|
|
INIT_LIST_HEAD(&gmgr->tdpro_list);
|
|
INIT_LIST_HEAD(&gmgr->tdcom_list);
|
|
|
|
for (index = 0; index < VIPX_MAX_TASKDESC; ++index) {
|
|
gmgr->taskdesc[index].index = index;
|
|
__vipx_taskdesc_set_free(gmgr, &gmgr->taskdesc[index]);
|
|
}
|
|
|
|
for (index = 0; index < VIPX_MAX_TASK; ++index)
|
|
kthread_init_work(&gmgr->interface->taskmgr.task[index].work,
|
|
vipx_interface_thread);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vipx_graphmgr_remove(struct vipx_graphmgr *gmgr)
|
|
{
|
|
mutex_destroy(&gmgr->tdlock);
|
|
mutex_destroy(&gmgr->mlock);
|
|
}
|