blob: 8dfe0149e95fe5a65a640e95a28a2a7abfbf0918 [file] [log] [blame]
/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
#include <linux/list.h>
#include <linux/spinlock.h>
#include "aml_vcodec_drv.h"
#include "aml_task_chain.h"
#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
#include <trace/events/meson_atrace.h>
struct task_item_name_s {
enum task_type_e type;
const u8 *name;
};
static const struct task_item_name_s iname[] = {
{TASK_TYPE_DEC, "dec"},
{TASK_TYPE_VPP, "vpp"},
{TASK_TYPE_V4L_SINK, "v4l-sink"},
{TASK_TYPE_GE2D, "ge2d"},
{TASK_TYPE_MAX, "unknown"},
};
static const u8 *type_to_name(enum task_type_e type)
{
const u8 *name = "unknown";
int i, size = ARRAY_SIZE(iname);
for (i = 0; i < size; i++) {
if (type == iname[i].type)
name = iname[i].name;
}
return name;
}
static struct task_item_s *find_task_item(struct task_chain_s *task,
enum task_type_e type)
{
struct task_item_s *item = NULL;
ulong flags;
spin_lock_irqsave(&task->slock, flags);
if (!list_empty(&task->list_item)) {
struct task_item_s *p;
list_for_each_entry(p, &task->list_item, node) {
if (p->ops->type == type) {
item = p;
break;
}
}
}
if (item)
kref_get(&item->ref);
spin_unlock_irqrestore(&task->slock, flags);
return item;
}
static void task_item_release(struct kref *kref);
static void task_item_vframe_push(struct task_item_s *item, struct vframe_s *vframe)
{
int i = 0;
for (i = 0 ; i < 3; i++) {
if (item->vframe[i] == NULL) {
item->vframe[i] = vframe;
break;
}
}
}
static struct vframe_s *task_item_vframe_pop(struct task_item_s *item)
{
struct vframe_s *vframe = NULL;
int i = 0;
for (i = 0 ; i < 3; i++) {
if (item->vframe[i] != NULL) {
vframe = item->vframe[i];
item->vframe[i] = NULL;
break;
}
}
return vframe;
}
static struct task_item_s *task_item_get(struct task_chain_s *task,
enum task_type_e type)
{
struct task_item_s *item = NULL;
item = find_task_item(task, type);
if (!item) {
v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
"TSK(%px):%d get item:%d fail.\n", task, task->id, type);
}
return item;
}
static int task_item_put(struct task_item_s *item)
{
return kref_put(&item->ref, task_item_release);
}
static void task_buffer_submit(struct task_chain_s *task,
enum task_type_e type)
{
struct vdec_v4l2_buffer *fb =
(struct vdec_v4l2_buffer *)task->obj;
struct task_item_s *item = NULL;
struct task_item_s *item2 = NULL;
struct vframe_s *vf = NULL;
item = task_item_get(task, type);
if (item) {
item->ops->get_vframe(item->caller, &vf);
fb->vframe = (void *)vf;
task_item_vframe_push(item, vf);
item->is_active = false;
item2 = task_item_get(task, task->map[0][type]);
if (item2) {
item2->is_active = true;
item2->ops->fill_buffer(task->ctx, fb);
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d, vf:%px, phy:%lx, submit %d => %d.\n",
task, task->id, vf, fb->m.mem[0].addr,
type, task->map[0][type]);
task->direction = TASK_DIR_SUBMIT;
task_item_put(item2);
}
task_item_put(item);
}
}
static void task_buffer_recycle(struct task_chain_s *task,
enum task_type_e type)
{
struct vdec_v4l2_buffer *fb =
(struct vdec_v4l2_buffer *)task->obj;
struct task_item_s *item = NULL;
struct task_item_s *item2 = NULL;
item = task_item_get(task, type);
if (item) {
item->is_active = false;
item2 = task_item_get(task, task->map[1][type]);
if (item2) {
struct vframe_s *vf = NULL;
item2->is_active = true;
vf = task_item_vframe_pop(item2);
item2->ops->put_vframe(item2->caller, vf);
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d, vf:%px, phy:%lx, recycle %d => %d.\n",
task, task->id, vf, fb->m.mem[0].addr,
type, task->map[1][type]);
task->direction = TASK_DIR_RECYCLE;
task_item_put(item2);
}
task_item_put(item);
}
}
void task_chain_show(struct task_chain_s *task)
{
struct task_item_s *item = NULL;
char buf[128] = {0};
char *pbuf = buf;
ulong flags;
if (!task || !task->ctx)
return;
spin_lock_irqsave(&task->slock, flags);
if (!list_empty(&task->list_item)) {
struct vdec_v4l2_buffer *fb =
(struct vdec_v4l2_buffer *)task->obj;
list_for_each_entry(item, &task->list_item, node) {
pbuf += sprintf(pbuf, "%s(%d)",
item->name, item->is_active);
if (item->node.next != &task->list_item) {
if (task->direction == TASK_DIR_SUBMIT)
pbuf += sprintf(pbuf, " ==> ");
else
pbuf += sprintf(pbuf, " <== ");
}
}
v4l_dbg(task->ctx, V4L_DEBUG_CODEC_PRINFO,
"vb:%2d, phy:%lx %s\n",
task->id, fb->m.mem[0].addr, buf);
}
spin_unlock_irqrestore(&task->slock, flags);
}
EXPORT_SYMBOL(task_chain_show);
static void task_chain_destroy(struct kref *kref)
{
struct task_chain_s *task;
task = container_of(kref, struct task_chain_s, ref);
task->cur_type = TASK_TYPE_MAX;
memset(task->map, 0, sizeof(task->map));
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d task chain destroyed.\n", task, task->id);
kfree(task);
}
static void task_item_release(struct kref *kref)
{
struct task_item_s *item;
item = container_of(kref, struct task_item_s, ref);
list_del(&item->node);
v4l_dbg(item->task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d task item:(%px,%d) released.\n",
item->task, item->task->id, item, item->ops->type);
kref_put(&item->task->ref, task_chain_destroy);
kfree(item);
}
void task_chain_clean(struct task_chain_s *task)
{
struct task_item_s *item, *tmp;
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d task chain clean.\n", task, task->id);
if (!list_empty(&task->list_item)) {
list_for_each_entry_safe(item, tmp, &task->list_item, node)
kref_put(&item->ref, task_item_release);
}
}
EXPORT_SYMBOL(task_chain_clean);
void task_chain_release(struct task_chain_s *task)
{
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d task chain release.\n", task, task->id);
kref_put(&task->ref, task_chain_destroy);
}
EXPORT_SYMBOL(task_chain_release);
void task_order_attach(struct task_chain_s *task,
struct task_ops_s *ops,
void *caller)
{
struct task_item_s *item;
item = kzalloc(sizeof(struct task_item_s), GFP_ATOMIC);
if (!item) {
v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
"TSK(%px):%d alloc item fail.\n", task, task->id);
return;
}
item->task = task;
item->ops = ops;
item->caller = caller;
item->name = type_to_name(ops->type);
kref_init(&item->ref);
task->map[0][ops->type] = task->cur_type;
task->map[1][task->cur_type] = ops->type;
task->cur_type = ops->type;
kref_get(&task->ref);
list_add(&item->node, &task->list_item);
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d attach item:(%px,%d).\n",
task, task->id, item, ops->type);
}
EXPORT_SYMBOL(task_order_attach);
void task_chain_update_object(struct task_chain_s *task, void *obj)
{
/*
* Note: have to invoke this funtion
* if the task object has been changed.
*/
task->obj = obj;
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d update task obj:%px.\n",
task, task->id, obj);
}
EXPORT_SYMBOL(task_chain_update_object);
int task_chain_init(struct task_chain_s **task_out,
void *v4l_ctx,
void *obj,
int vb_idx)
{
struct task_chain_s *task;
task = kzalloc(sizeof(struct task_chain_s), GFP_ATOMIC);
if (!task) {
v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
"TSK(%px):%d alloc task fail.\n", task, task->id);
return -ENOMEM;
}
task->id = vb_idx;
task->obj = obj;
task->ctx = v4l_ctx;
kref_init(&task->ref);
spin_lock_init(&task->slock);
INIT_LIST_HEAD(&task->list_item);
task->attach = task_order_attach;
task->submit = task_buffer_submit;
task->recycle = task_buffer_recycle;
*task_out = task;
v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
"TSK(%px):%d task chain creat success.\n", task, task->id);
return 0;
}
EXPORT_SYMBOL(task_chain_init);