913 lines
21 KiB
C
Executable File
913 lines
21 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/delay.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include "vipx-log.h"
|
|
#include "vipx-device.h"
|
|
#include "vipx-binary.h"
|
|
#include "vipx-queue.h"
|
|
#include "vipx-graphmgr.h"
|
|
#include "vipx-memory.h"
|
|
#include "vipx-mailbox.h"
|
|
#include "vipx-message.h"
|
|
#include "vipx-kernel-binary.h"
|
|
#include "vipx-system.h"
|
|
#include "vipx-interface.h"
|
|
|
|
static inline void __vipx_interface_set_reply(struct vipx_interface *itf,
|
|
unsigned int gidx)
|
|
{
|
|
vipx_enter();
|
|
itf->reply[gidx].valid = 1;
|
|
wake_up(&itf->reply_wq);
|
|
vipx_leave();
|
|
}
|
|
|
|
static inline void __vipx_interface_clear_reply(struct vipx_interface *itf,
|
|
unsigned int gidx)
|
|
{
|
|
vipx_enter();
|
|
itf->reply[gidx].valid = 0;
|
|
vipx_leave();
|
|
}
|
|
|
|
static int __vipx_interface_send_interrupt(struct vipx_interface *itf)
|
|
{
|
|
int ret;
|
|
struct vipx_system *sys;
|
|
int try_count = 100;
|
|
unsigned int val;
|
|
|
|
vipx_enter();
|
|
sys = itf->system;
|
|
|
|
/* Check interrupt clear */
|
|
while (try_count) {
|
|
val = sys->ctrl_ops->get_irq(sys, IRQ1_TO_DEVICE);
|
|
if (!val)
|
|
break;
|
|
|
|
vipx_warn("irq1(0x%x) is not yet clear (%u)\n", val, try_count);
|
|
udelay(1);
|
|
try_count--;
|
|
}
|
|
|
|
if (val) {
|
|
ret = -EBUSY;
|
|
vipx_err("Failed to send interrupt(0x%x)\n", val);
|
|
goto p_err;
|
|
}
|
|
|
|
sys->ctrl_ops->set_irq(sys, IRQ1_TO_DEVICE, 0x100);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_hw_wait_bootup(struct vipx_interface *itf)
|
|
{
|
|
int ret;
|
|
int try_cnt = 1000;
|
|
|
|
vipx_enter();
|
|
while (!test_bit(VIPX_ITF_STATE_BOOTUP, &itf->state)) {
|
|
if (!try_cnt)
|
|
break;
|
|
|
|
udelay(100);
|
|
try_cnt--;
|
|
}
|
|
|
|
if (!test_bit(VIPX_ITF_STATE_BOOTUP, &itf->state)) {
|
|
vipx_debug_dump_debug_regs();
|
|
ret = -ETIMEDOUT;
|
|
vipx_err("Failed to boot CM7 (%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_hw_config(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_hw_process(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_hw_create(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_hw_destroy(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_hw_init(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_hw_deinit(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
vipx_enter();
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __vipx_interface_wait_mailbox_reply(struct vipx_interface *itf,
|
|
struct vipx_task *itask)
|
|
{
|
|
int ret;
|
|
long timeout;
|
|
|
|
vipx_enter();
|
|
timeout = wait_event_timeout(itf->reply_wq,
|
|
(itask->message == VIPX_TASK_DONE),
|
|
msecs_to_jiffies(itf->wait_time));
|
|
if (!timeout) {
|
|
ret = -ETIMEDOUT;
|
|
vipx_err("wait time(%u ms) is expired (%u)\n",
|
|
itf->wait_time, itask->id);
|
|
vipx_debug_dump_debug_regs();
|
|
goto p_err;
|
|
}
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vipx_interface_send_mailbox(struct vipx_interface *itf,
|
|
struct vipx_task *itask)
|
|
{
|
|
int ret;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_mailbox_ctrl *mctrl;
|
|
void *msg;
|
|
unsigned long size, type;
|
|
unsigned long flags, process_flags;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
mctrl = itf->mctrl;
|
|
|
|
msg = (void *)itask->param1;
|
|
size = itask->param2;
|
|
type = itask->param3;
|
|
|
|
vipx_pm_request_busy(&itf->system->pm);
|
|
|
|
spin_lock_irqsave(&itf->process_barrier, process_flags);
|
|
itf->process = itask;
|
|
|
|
ret = vipx_mailbox_check_full(mctrl, type, MAILBOX_WAIT);
|
|
if (ret) {
|
|
vipx_err("mailbox is full(%lu/%u)\n", type, itask->id);
|
|
vipx_mailbox_dump(mctrl);
|
|
goto p_err_process;
|
|
}
|
|
|
|
ret = vipx_mailbox_write(mctrl, type, msg, size);
|
|
if (ret) {
|
|
vipx_err("Failed to write to mailbox(%lu/%u)\n",
|
|
type, itask->id);
|
|
goto p_err_process;
|
|
}
|
|
|
|
ret = __vipx_interface_send_interrupt(itf);
|
|
if (ret) {
|
|
vipx_err("Failed to send interrupt(%lu/%u)\n",
|
|
type, itask->id);
|
|
goto p_err_process;
|
|
}
|
|
|
|
vipx_time_get_timestamp(&itask->time, TIMESTAMP_START);
|
|
spin_unlock_irqrestore(&itf->process_barrier, process_flags);
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_req_to_pro(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
ret = __vipx_interface_wait_mailbox_reply(itf, itask);
|
|
if (ret)
|
|
vipx_mailbox_dump(mctrl);
|
|
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_pro_to_com(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
spin_lock_irqsave(&itf->process_barrier, process_flags);
|
|
itf->process = NULL;
|
|
spin_unlock_irqrestore(&itf->process_barrier, process_flags);
|
|
|
|
vipx_pm_request_idle(&itf->system->pm);
|
|
vipx_leave();
|
|
return ret;
|
|
p_err_process:
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_req_to_com(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
itf->process = NULL;
|
|
spin_unlock_irqrestore(&itf->process_barrier, process_flags);
|
|
vipx_pm_request_idle(&itf->system->pm);
|
|
return ret;
|
|
}
|
|
|
|
int vipx_hw_load_graph(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
int ret;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_h2d_message msg;
|
|
struct vipx_h2d_load_graph_req *req;
|
|
struct vipx_common_graph_info *ginfo;
|
|
int idx;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
msg.head.mbox_version = MAILBOX_VERSION;
|
|
msg.head.msg_version = MESSAGE_VERSION;
|
|
msg.head.msg_type = NORMAL_MSG;
|
|
msg.head.msg_size = sizeof(*req);
|
|
msg.head.trans_id = itask->id;
|
|
msg.head.msg_id = H2D_LOAD_GRAPH_REQ;
|
|
|
|
req = &msg.body.load_graph_req;
|
|
ginfo = (struct vipx_common_graph_info *)itask->param0;
|
|
req->graph_info = *ginfo;
|
|
|
|
itask->param1 = (unsigned long)&msg;
|
|
itask->param2 = sizeof(msg);
|
|
itask->param3 = NORMAL_MSG;
|
|
|
|
vipx_dbg("[%s] load graph (firmware)\n", __func__);
|
|
for (idx = 0; idx < sizeof(*ginfo) >> 2; ++idx)
|
|
vipx_dbg("[%3d] %#10x\n", idx, ((int *)ginfo)[idx]);
|
|
|
|
ret = __vipx_interface_send_mailbox(itf, itask);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_hw_execute_graph(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
int ret;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_h2d_message msg;
|
|
struct vipx_h2d_execute_req *req;
|
|
struct vipx_common_execute_info *einfo;
|
|
struct vipx_graph_model *gmodel;
|
|
struct vipx_kernel_binary *kbin, *temp;
|
|
int idx = 0;
|
|
int *debug_addr;
|
|
int iter;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
msg.head.mbox_version = MAILBOX_VERSION;
|
|
msg.head.msg_version = MESSAGE_VERSION;
|
|
msg.head.msg_type = NORMAL_MSG;
|
|
msg.head.msg_size = sizeof(*req);
|
|
msg.head.trans_id = itask->id;
|
|
msg.head.msg_id = H2D_EXECUTE_REQ;
|
|
|
|
req = &msg.body.execute_req;
|
|
einfo = (struct vipx_common_execute_info *)itask->param0;
|
|
req->execute_info = *einfo;
|
|
|
|
gmodel = (struct vipx_graph_model *)itask->param1;
|
|
req->num_kernel_bin = gmodel->kbin_count;
|
|
//TODO check max count
|
|
list_for_each_entry_safe(kbin, temp, &gmodel->kbin_list, glist) {
|
|
req->kernel_bin[idx].addr = kbin->buffer.dvaddr;
|
|
req->kernel_bin[idx].size = kbin->buffer.size;
|
|
idx++;
|
|
}
|
|
|
|
itask->param1 = (unsigned long)&msg;
|
|
itask->param2 = sizeof(msg);
|
|
itask->param3 = NORMAL_MSG;
|
|
|
|
vipx_dbg("[%s] execute graph (firmware)\n", __func__);
|
|
vipx_dbg("num_kernel_bin : %d\n", req->num_kernel_bin);
|
|
for (idx = 0; idx < req->num_kernel_bin; ++idx) {
|
|
vipx_dbg("[%3d] kernel addr : %#10x\n",
|
|
idx, req->kernel_bin[idx].addr);
|
|
vipx_dbg("[%3d] kernel size : %#10x\n",
|
|
idx, req->kernel_bin[idx].size);
|
|
}
|
|
|
|
vipx_dbg("gid : %#x\n", einfo->gid);
|
|
vipx_dbg("macro_sc_offset : %u\n", einfo->macro_sg_offset);
|
|
vipx_dbg("num_input : %u\n", einfo->num_input);
|
|
for (idx = 0; idx < einfo->num_input; ++idx) {
|
|
debug_addr = (int *)&einfo->input[idx];
|
|
for (iter = 0; iter < sizeof(einfo->input[0]) >> 2; ++iter)
|
|
vipx_dbg("[%3d][%3d] %#10x\n",
|
|
idx, iter, debug_addr[iter]);
|
|
}
|
|
|
|
vipx_dbg("num_output : %u\n", einfo->num_output);
|
|
for (idx = 0; idx < einfo->num_output; ++idx) {
|
|
debug_addr = (int *)&einfo->output[idx];
|
|
for (iter = 0; iter < sizeof(einfo->output[0]) >> 2; ++iter)
|
|
vipx_dbg("[%3d][%3d] %#10x\n",
|
|
idx, iter, debug_addr[iter]);
|
|
}
|
|
|
|
vipx_dbg("user_para_size(not use) : %u\n", einfo->user_para_size);
|
|
debug_addr = (int *)&einfo->user_param_buffer;
|
|
for (idx = 0; idx < sizeof(einfo->user_param_buffer) >> 2; ++idx)
|
|
vipx_dbg("[%3d] %#10x\n", idx, debug_addr[idx]);
|
|
|
|
ret = __vipx_interface_send_mailbox(itf, itask);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vipx_hw_unload_graph(struct vipx_interface *itf, struct vipx_task *itask)
|
|
{
|
|
int ret;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_h2d_message msg;
|
|
struct vipx_h2d_unload_graph_req *req;
|
|
struct vipx_common_graph_info *ginfo;
|
|
|
|
vipx_enter();
|
|
itaskmgr = &itf->taskmgr;
|
|
|
|
msg.head.mbox_version = MAILBOX_VERSION;
|
|
msg.head.msg_version = MESSAGE_VERSION;
|
|
msg.head.msg_type = NORMAL_MSG;
|
|
msg.head.msg_size = sizeof(*req);
|
|
msg.head.trans_id = itask->id;
|
|
msg.head.msg_id = H2D_UNLOAD_GRAPH_REQ;
|
|
|
|
req = &msg.body.unload_graph_req;
|
|
ginfo = (struct vipx_common_graph_info *)itask->param0;
|
|
req->gid = ginfo->gid;
|
|
|
|
itask->param1 = (unsigned long)&msg;
|
|
itask->param2 = sizeof(msg);
|
|
itask->param3 = NORMAL_MSG;
|
|
|
|
vipx_dbg("[%s] unload graph (firmware)\n", __func__);
|
|
vipx_dbg("gid : %#x\n", req->gid);
|
|
|
|
ret = __vipx_interface_send_mailbox(itf, itask);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static void __vipx_interface_work_list_queue_free(
|
|
struct vipx_work_list *work_list, struct vipx_work *work)
|
|
{
|
|
unsigned long flags;
|
|
|
|
vipx_enter();
|
|
spin_lock_irqsave(&work_list->slock, flags);
|
|
list_add_tail(&work->list, &work_list->free_head);
|
|
work_list->free_cnt++;
|
|
spin_unlock_irqrestore(&work_list->slock, flags);
|
|
vipx_leave();
|
|
}
|
|
|
|
static struct vipx_work *__vipx_interface_work_list_dequeue_free(
|
|
struct vipx_work_list *work_list)
|
|
{
|
|
unsigned long flags;
|
|
struct vipx_work *work;
|
|
|
|
vipx_enter();
|
|
spin_lock_irqsave(&work_list->slock, flags);
|
|
if (work_list->free_cnt) {
|
|
work = list_first_entry(&work_list->free_head, struct vipx_work,
|
|
list);
|
|
list_del(&work->list);
|
|
work_list->free_cnt--;
|
|
} else {
|
|
work = NULL;
|
|
vipx_err("free work list empty\n");
|
|
}
|
|
spin_unlock_irqrestore(&work_list->slock, flags);
|
|
|
|
vipx_leave();
|
|
return work;
|
|
}
|
|
|
|
static void __vipx_interface_work_list_queue_reply(
|
|
struct vipx_work_list *work_list, struct vipx_work *work)
|
|
{
|
|
unsigned long flags;
|
|
|
|
vipx_enter();
|
|
spin_lock_irqsave(&work_list->slock, flags);
|
|
list_add_tail(&work->list, &work_list->reply_head);
|
|
work_list->reply_cnt++;
|
|
spin_unlock_irqrestore(&work_list->slock, flags);
|
|
vipx_leave();
|
|
}
|
|
|
|
static struct vipx_work *__vipx_interface_work_list_dequeue_reply(
|
|
struct vipx_work_list *work_list)
|
|
{
|
|
unsigned long flags;
|
|
struct vipx_work *work;
|
|
|
|
vipx_enter();
|
|
spin_lock_irqsave(&work_list->slock, flags);
|
|
if (work_list->reply_cnt) {
|
|
work = list_first_entry(&work_list->reply_head,
|
|
struct vipx_work, list);
|
|
list_del(&work->list);
|
|
work_list->reply_cnt--;
|
|
} else {
|
|
work = NULL;
|
|
}
|
|
spin_unlock_irqrestore(&work_list->slock, flags);
|
|
|
|
vipx_leave();
|
|
return work;
|
|
}
|
|
|
|
static void __vipx_interface_work_list_init(struct vipx_work_list *work_list,
|
|
unsigned int count)
|
|
{
|
|
unsigned int idx;
|
|
|
|
vipx_enter();
|
|
spin_lock_init(&work_list->slock);
|
|
INIT_LIST_HEAD(&work_list->free_head);
|
|
work_list->free_cnt = 0;
|
|
INIT_LIST_HEAD(&work_list->reply_head);
|
|
work_list->reply_cnt = 0;
|
|
|
|
for (idx = 0; idx < count; ++idx)
|
|
__vipx_interface_work_list_queue_free(work_list,
|
|
&work_list->work[idx]);
|
|
|
|
vipx_leave();
|
|
}
|
|
|
|
static void __vipx_interface_isr(void *data)
|
|
{
|
|
struct vipx_interface *itf;
|
|
struct vipx_mailbox_ctrl *mctrl;
|
|
struct vipx_work_list *work_list;
|
|
struct work_struct *work_queue;
|
|
struct vipx_work *work;
|
|
struct vipx_d2h_message rsp;
|
|
unsigned long flags;
|
|
|
|
vipx_enter();
|
|
itf = (struct vipx_interface *)data;
|
|
mctrl = itf->mctrl;
|
|
work_list = &itf->work_list;
|
|
work_queue = &itf->work_queue;
|
|
|
|
while (!vipx_mailbox_check_reply(mctrl, URGENT_MSG, 0)) {
|
|
vipx_mailbox_read(mctrl, URGENT_MSG, &rsp);
|
|
|
|
|
|
work = __vipx_interface_work_list_dequeue_free(work_list);
|
|
if (work) {
|
|
/* TODO */
|
|
work->id = rsp.head.trans_id;
|
|
work->message = 0;
|
|
work->param0 = 0;
|
|
work->param1 = 0;
|
|
work->param2 = 0;
|
|
work->param3 = 0;
|
|
|
|
__vipx_interface_work_list_queue_reply(work_list, work);
|
|
|
|
if (!work_pending(work_queue))
|
|
schedule_work(work_queue);
|
|
}
|
|
}
|
|
|
|
while (!vipx_mailbox_check_reply(mctrl, NORMAL_MSG, 0)) {
|
|
vipx_mailbox_read(mctrl, NORMAL_MSG, &rsp);
|
|
|
|
if (rsp.head.msg_id == D2H_BOOTUP_NTF) {
|
|
if (rsp.body.bootup_ntf.result == 0) {
|
|
vipx_info("CM7 bootup complete (%u)\n",
|
|
rsp.head.trans_id);
|
|
set_bit(VIPX_ITF_STATE_BOOTUP, &itf->state);
|
|
} else {
|
|
/* TODO process boot fail */
|
|
vipx_err("CM7 bootup failed (%u,%d)\n",
|
|
rsp.head.trans_id,
|
|
rsp.body.bootup_ntf.result);
|
|
}
|
|
break;
|
|
} else if (rsp.head.msg_id == D2H_LOAD_GRAPH_RSP) {
|
|
spin_lock_irqsave(&itf->process_barrier, flags);
|
|
if (!itf->process) {
|
|
vipx_err("process task is empty\n");
|
|
spin_unlock_irqrestore(&itf->process_barrier,
|
|
flags);
|
|
continue;
|
|
}
|
|
|
|
vipx_dbg("D2H_LOAD_GRAPH_RSP : %x\n",
|
|
rsp.body.load_graph_rsp.result);
|
|
vipx_dbg("D2H_LOAD_GRAPH_RSP(graph_id) : %d\n",
|
|
rsp.body.load_graph_rsp.gid);
|
|
vipx_time_get_timestamp(&itf->process->time,
|
|
TIMESTAMP_END);
|
|
itf->process->message = VIPX_TASK_DONE;
|
|
wake_up(&itf->reply_wq);
|
|
spin_unlock_irqrestore(&itf->process_barrier, flags);
|
|
continue;
|
|
} else if (rsp.head.msg_id == D2H_EXECUTE_RSP) {
|
|
spin_lock_irqsave(&itf->process_barrier, flags);
|
|
if (!itf->process) {
|
|
vipx_err("process task is empty\n");
|
|
spin_unlock_irqrestore(&itf->process_barrier,
|
|
flags);
|
|
continue;
|
|
}
|
|
|
|
vipx_dbg("D2H_EXECUTE_RSP : %x\n",
|
|
rsp.body.execute_rsp.result);
|
|
vipx_dbg("D2H_EXECUTE_RSP(graph_id) : %d\n",
|
|
rsp.body.execute_rsp.gid);
|
|
vipx_dbg("D2H_EXECUTE_RSP(macro) : %d\n",
|
|
rsp.body.execute_rsp.macro_sgid);
|
|
vipx_time_get_timestamp(&itf->process->time,
|
|
TIMESTAMP_END);
|
|
itf->process->message = VIPX_TASK_DONE;
|
|
wake_up(&itf->reply_wq);
|
|
spin_unlock_irqrestore(&itf->process_barrier, flags);
|
|
continue;
|
|
} else if (rsp.head.msg_id == D2H_UNLOAD_GRAPH_RSP) {
|
|
spin_lock_irqsave(&itf->process_barrier, flags);
|
|
if (!itf->process) {
|
|
vipx_err("process task is empty\n");
|
|
spin_unlock_irqrestore(&itf->process_barrier,
|
|
flags);
|
|
continue;
|
|
}
|
|
|
|
vipx_dbg("D2H_UNLOAD_GRAPH_RSP : %x\n",
|
|
rsp.body.unload_graph_rsp.result);
|
|
vipx_dbg("D2H_UNLOAD_GRAPH_RSP(graph_id) : %d\n",
|
|
rsp.body.unload_graph_rsp.gid);
|
|
vipx_time_get_timestamp(&itf->process->time,
|
|
TIMESTAMP_END);
|
|
itf->process->message = VIPX_TASK_DONE;
|
|
wake_up(&itf->reply_wq);
|
|
spin_unlock_irqrestore(&itf->process_barrier, flags);
|
|
continue;
|
|
}
|
|
work = __vipx_interface_work_list_dequeue_free(work_list);
|
|
if (work) {
|
|
/* TODO */
|
|
work->id = rsp.head.trans_id;
|
|
work->message = 0;
|
|
work->param0 = 0;
|
|
work->param1 = 0;
|
|
work->param2 = 0;
|
|
work->param3 = 0;
|
|
|
|
__vipx_interface_work_list_queue_reply(work_list, work);
|
|
|
|
if (!work_pending(work_queue))
|
|
schedule_work(work_queue);
|
|
}
|
|
}
|
|
|
|
vipx_leave();
|
|
}
|
|
|
|
static irqreturn_t vipx_interface_isr0(int irq, void *data)
|
|
{
|
|
struct vipx_interface *itf;
|
|
struct vipx_system *sys;
|
|
unsigned int val;
|
|
|
|
vipx_enter();
|
|
itf = (struct vipx_interface *)data;
|
|
sys = itf->system;
|
|
|
|
val = sys->ctrl_ops->get_irq(sys, IRQ_FROM_DEVICE);
|
|
if (val & 0x1) {
|
|
val &= ~(0x1);
|
|
sys->ctrl_ops->clear_irq(sys, IRQ_FROM_DEVICE, val);
|
|
|
|
__vipx_interface_isr(data);
|
|
}
|
|
|
|
vipx_leave();
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t vipx_interface_isr1(int irq, void *data)
|
|
{
|
|
struct vipx_interface *itf;
|
|
struct vipx_system *sys;
|
|
unsigned int val;
|
|
|
|
vipx_enter();
|
|
itf = (struct vipx_interface *)data;
|
|
sys = itf->system;
|
|
|
|
val = sys->ctrl_ops->get_irq(sys, IRQ_FROM_DEVICE);
|
|
if (val & (0x1 << 0x1)) {
|
|
val &= ~(0x1 << 0x1);
|
|
sys->ctrl_ops->clear_irq(sys, IRQ_FROM_DEVICE, val);
|
|
|
|
__vipx_interface_isr(data);
|
|
}
|
|
|
|
vipx_leave();
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int vipx_interface_start(struct vipx_interface *itf)
|
|
{
|
|
vipx_enter();
|
|
set_bit(VIPX_ITF_STATE_START, &itf->state);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static void __vipx_interface_cleanup(struct vipx_interface *itf)
|
|
{
|
|
struct vipx_taskmgr *taskmgr;
|
|
int task_count;
|
|
|
|
unsigned long flags;
|
|
unsigned int idx;
|
|
struct vipx_task *task;
|
|
|
|
vipx_enter();
|
|
taskmgr = &itf->taskmgr;
|
|
|
|
task_count = taskmgr->req_cnt + taskmgr->pro_cnt + taskmgr->com_cnt;
|
|
if (task_count) {
|
|
vipx_warn("count of task manager is not zero (%u/%u/%u)\n",
|
|
taskmgr->req_cnt, taskmgr->pro_cnt,
|
|
taskmgr->com_cnt);
|
|
/* TODO remove debug log */
|
|
vipx_task_print_all(&itf->taskmgr);
|
|
|
|
spin_lock_irqsave(&taskmgr->slock, flags);
|
|
|
|
for (idx = 1; idx < taskmgr->tot_cnt; ++idx) {
|
|
task = &taskmgr->task[idx];
|
|
if (task->state == VIPX_TASK_STATE_FREE)
|
|
continue;
|
|
|
|
vipx_task_trans_any_to_fre(taskmgr, task);
|
|
task_count--;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&taskmgr->slock, flags);
|
|
}
|
|
}
|
|
|
|
int vipx_interface_stop(struct vipx_interface *itf)
|
|
{
|
|
vipx_enter();
|
|
__vipx_interface_cleanup(itf);
|
|
clear_bit(VIPX_ITF_STATE_BOOTUP, &itf->state);
|
|
clear_bit(VIPX_ITF_STATE_START, &itf->state);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_interface_open(struct vipx_interface *itf, void *mbox)
|
|
{
|
|
int idx;
|
|
|
|
vipx_enter();
|
|
itf->mctrl = mbox;
|
|
vipx_mailbox_init(itf->mctrl);
|
|
|
|
itf->process = NULL;
|
|
for (idx = 0; idx < VIPX_MAX_GRAPH; ++idx) {
|
|
itf->request[idx] = NULL;
|
|
itf->reply[idx].valid = 0;
|
|
}
|
|
|
|
itf->done_cnt = 0;
|
|
itf->state = 0;
|
|
set_bit(VIPX_ITF_STATE_OPEN, &itf->state);
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
int vipx_interface_close(struct vipx_interface *itf)
|
|
{
|
|
vipx_enter();
|
|
clear_bit(VIPX_ITF_STATE_OPEN, &itf->state);
|
|
itf->mctrl = NULL;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
}
|
|
|
|
static void vipx_interface_work_reply_func(struct work_struct *data)
|
|
{
|
|
struct vipx_interface *itf;
|
|
struct vipx_taskmgr *itaskmgr;
|
|
struct vipx_work_list *work_list;
|
|
struct vipx_work *work;
|
|
struct vipx_task *itask;
|
|
unsigned int gidx;
|
|
unsigned long flags;
|
|
|
|
vipx_enter();
|
|
itf = container_of(data, struct vipx_interface, work_queue);
|
|
itaskmgr = &itf->taskmgr;
|
|
work_list = &itf->work_list;
|
|
|
|
while (true) {
|
|
work = __vipx_interface_work_list_dequeue_reply(work_list);
|
|
if (!work)
|
|
break;
|
|
|
|
if (work->id >= VIPX_MAX_TASK) {
|
|
vipx_err("work id is invalid (%d)\n", work->id);
|
|
goto p_end;
|
|
}
|
|
|
|
itask = &itaskmgr->task[work->id];
|
|
|
|
if (itask->state != VIPX_TASK_STATE_PROCESS) {
|
|
vipx_err("task(%u/%u/%lu) state(%u) is invalid\n",
|
|
itask->message, itask->index,
|
|
itask->param1, itask->state);
|
|
vipx_err("work(%u/%u/%u)\n", work->id,
|
|
work->param0, work->param1);
|
|
vipx_task_print_all(&itf->taskmgr);
|
|
goto p_end;
|
|
}
|
|
|
|
switch (itask->message) {
|
|
case VIPX_TASK_INIT:
|
|
case VIPX_TASK_DEINIT:
|
|
case VIPX_TASK_CREATE:
|
|
case VIPX_TASK_DESTROY:
|
|
case VIPX_TASK_ALLOCATE:
|
|
case VIPX_TASK_REQUEST:
|
|
gidx = itask->param1;
|
|
itf->reply[gidx] = *work;
|
|
__vipx_interface_set_reply(itf, gidx);
|
|
break;
|
|
case VIPX_TASK_PROCESS:
|
|
spin_lock_irqsave(&itaskmgr->slock, flags);
|
|
vipx_task_trans_pro_to_com(itaskmgr, itask);
|
|
spin_unlock_irqrestore(&itaskmgr->slock, flags);
|
|
|
|
itask->param2 = 0;
|
|
itask->param3 = 0;
|
|
vipx_graphmgr_queue(itf->cookie, itask);
|
|
itf->done_cnt++;
|
|
break;
|
|
default:
|
|
vipx_err("unresolved task message(%d) have arrived\n",
|
|
work->message);
|
|
break;
|
|
}
|
|
p_end:
|
|
__vipx_interface_work_list_queue_free(work_list, work);
|
|
};
|
|
vipx_leave();
|
|
}
|
|
|
|
int vipx_interface_probe(struct vipx_system *sys)
|
|
{
|
|
int ret;
|
|
struct platform_device *pdev;
|
|
struct vipx_interface *itf;
|
|
struct device *dev;
|
|
struct vipx_taskmgr *taskmgr;
|
|
|
|
vipx_enter();
|
|
pdev = to_platform_device(sys->dev);
|
|
itf = &sys->interface;
|
|
itf->system = sys;
|
|
dev = &pdev->dev;
|
|
|
|
ret = platform_get_irq(pdev, 0);
|
|
if (ret < 0) {
|
|
vipx_err("platform_get_irq(0) is fail (%d)\n", ret);
|
|
goto p_err_irq0;
|
|
}
|
|
|
|
itf->irq[REG_SS1] = ret;
|
|
|
|
ret = platform_get_irq(pdev, 1);
|
|
if (ret < 0) {
|
|
vipx_err("platform_get_irq(1) is fail (%d)\n", ret);
|
|
goto p_err_irq1;
|
|
}
|
|
|
|
itf->irq[REG_SS2] = ret;
|
|
|
|
ret = devm_request_irq(dev, itf->irq[REG_SS1], vipx_interface_isr0,
|
|
0, dev_name(dev), itf);
|
|
if (ret) {
|
|
vipx_err("devm_request_irq(0) is fail (%d)\n", ret);
|
|
goto p_err_req_irq0;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, itf->irq[REG_SS2], vipx_interface_isr1,
|
|
0, dev_name(dev), itf);
|
|
if (ret) {
|
|
vipx_err("devm_request_irq(1) is fail (%d)\n", ret);
|
|
goto p_err_req_irq1;
|
|
}
|
|
|
|
itf->regs = sys->reg_ss[REG_SS1];
|
|
|
|
taskmgr = &itf->taskmgr;
|
|
spin_lock_init(&taskmgr->slock);
|
|
ret = vipx_task_init(taskmgr, VIPX_MAX_GRAPH, itf);
|
|
if (ret)
|
|
goto p_err_taskmgr;
|
|
|
|
itf->cookie = &sys->graphmgr;
|
|
itf->state = 0;
|
|
|
|
init_waitqueue_head(&itf->reply_wq);
|
|
spin_lock_init(&itf->process_barrier);
|
|
|
|
INIT_WORK(&itf->work_queue, vipx_interface_work_reply_func);
|
|
__vipx_interface_work_list_init(&itf->work_list, VIPX_WORK_MAX_COUNT);
|
|
|
|
itf->wait_time = VIPX_RESPONSE_TIMEOUT;
|
|
|
|
vipx_leave();
|
|
return 0;
|
|
p_err_taskmgr:
|
|
devm_free_irq(dev, itf->irq[REG_SS2], itf);
|
|
p_err_req_irq1:
|
|
devm_free_irq(dev, itf->irq[REG_SS1], itf);
|
|
p_err_req_irq0:
|
|
p_err_irq1:
|
|
p_err_irq0:
|
|
return ret;
|
|
}
|
|
|
|
void vipx_interface_remove(struct vipx_interface *itf)
|
|
{
|
|
vipx_task_deinit(&itf->taskmgr);
|
|
devm_free_irq(itf->system->dev, itf->irq[REG_SS2], itf);
|
|
devm_free_irq(itf->system->dev, itf->irq[REG_SS1], itf);
|
|
}
|