blob: 768f661274f533f7f9a62572e42b76c3e6c53bf2 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/dma-buf.h>
#include <linux/sync_file.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <dev_ion.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/video_sink/v4lvideo_ext.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/utils/am_com.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/amlogic/media/ge2d/ge2d_func.h>
#include <linux/amlogic/media/video_processor/video_pp_common.h>
#include "meson_uvm_aipq_processor.h"
struct dma_buf *dmabuf_last;
static int uvm_aipq_debug;
module_param(uvm_aipq_debug, int, 0644);
static int uvm_open_aipq;
module_param(uvm_open_aipq, int, 0644);
static int uvm_aipq_dump;
module_param(uvm_aipq_dump, int, 0644);
static int uvm_aipq_skip_height = 1088;
module_param(uvm_aipq_skip_height, int, 0644);
#define PRINT_ERROR 0X0
#define PRINT_OTHER 0X0001
#define PRINT_NN_DUMP 0X0002
int aipq_print(int debug_flag, const char *fmt, ...)
{
if ((uvm_aipq_debug & debug_flag) ||
debug_flag == PRINT_ERROR) {
unsigned char buf[256];
int len = 0;
va_list args;
va_start(args, fmt);
len = sprintf(buf, "uvm_aipq:[%d]", 0);
vsnprintf(buf + len, 256 - len, fmt, args);
pr_info("%s", buf);
va_end(args);
}
return 0;
}
int aipq_vf_set_value(struct uvm_aipq_info *aipq_info, bool enable_aipq)
{
struct uvm_hook_mod *uhmod = NULL;
struct dma_buf *dmabuf = NULL;
bool is_dec_vf = false, is_v4l_vf = false;
struct vframe_s *vf = NULL;
struct file_private_data *file_private_data = NULL;
int shared_fd = aipq_info->shared_fd;
int i = 0;
dmabuf = dma_buf_get(shared_fd);
if (IS_ERR_OR_NULL(dmabuf)) {
aipq_print(PRINT_ERROR,
"Invalid dmabuf %s %d\n", __func__, __LINE__);
return -EINVAL;
}
if (!dmabuf_is_uvm(dmabuf)) {
aipq_print(PRINT_ERROR,
"%s: dmabuf is not uvm.dmabuf=%px, shared_fd=%d\n",
__func__, dmabuf, shared_fd);
dma_buf_put(dmabuf);
return -EINVAL;
}
is_dec_vf = is_valid_mod_type(dmabuf, VF_SRC_DECODER);
is_v4l_vf = is_valid_mod_type(dmabuf, VF_PROCESS_V4LVIDEO);
if (is_dec_vf) {
vf = dmabuf_get_vframe(dmabuf);
dmabuf_put_vframe(dmabuf);
} else {
uhmod = uvm_get_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
if (IS_ERR_OR_NULL(uhmod) || !uhmod->arg) {
aipq_print(PRINT_OTHER, "get vf err: no v4lvideo\n");
dma_buf_put(dmabuf);
return -EINVAL;
}
file_private_data = uhmod->arg;
uvm_put_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
if (!file_private_data)
aipq_print(PRINT_ERROR, "invalid fd no uvm/v4lvideo\n");
else
vf = &file_private_data->vf;
}
if (vf) {
for (i = 0; i < AI_PQ_TOP; i++)
vf->nn_value[i] = aipq_info->nn_value[i];
vf->ai_pq_enable = enable_aipq;
if (vf->vf_ext) {
vf = vf->vf_ext;
for (i = 0; i < AI_PQ_TOP; i++)
vf->nn_value[i] = aipq_info->nn_value[i];
vf->ai_pq_enable = true;
}
vf->ai_pq_enable = enable_aipq;
} else {
aipq_print(PRINT_ERROR, "not find vf\n");
dma_buf_put(dmabuf);
return -EINVAL;
}
dma_buf_put(dmabuf);
return 0;
}
struct vframe_s *aipq_get_dw_vf(struct uvm_aipq_info *aipq_info)
{
struct uvm_hook_mod *uhmod = NULL;
struct dma_buf *dmabuf = NULL;
bool is_dec_vf = false, is_v4l_vf = false;
struct vframe_s *vf = NULL;
struct vframe_s *di_vf = NULL;
struct file_private_data *file_private_data = NULL;
int shared_fd = aipq_info->shared_fd;
int interlace_mode = 0;
dmabuf = dma_buf_get(shared_fd);
if (IS_ERR_OR_NULL(dmabuf)) {
aipq_print(PRINT_ERROR,
"Invalid dmabuf %s %d\n", __func__, __LINE__);
return NULL;
}
if (!dmabuf_is_uvm(dmabuf)) {
aipq_print(PRINT_ERROR,
"%s: dmabuf is not uvm.dmabuf=%px, shared_fd=%d\n",
__func__, dmabuf, shared_fd);
dma_buf_put(dmabuf);
return NULL;
}
is_dec_vf = is_valid_mod_type(dmabuf, VF_SRC_DECODER);
is_v4l_vf = is_valid_mod_type(dmabuf, VF_PROCESS_V4LVIDEO);
if (is_dec_vf) {
vf = dmabuf_get_vframe(dmabuf);
if (IS_ERR_OR_NULL(vf)) {
aipq_print(PRINT_ERROR, "%s: vf is NULL.\n", __func__);
return NULL;
}
aipq_print(PRINT_OTHER,
"vf: %d*%d,flag=%x,type=%x\n",
vf->width,
vf->height,
vf->flag,
vf->type);
di_vf = vf->vf_ext;
interlace_mode = vf->type & VIDTYPE_TYPEMASK;
if (di_vf && (vf->flag & VFRAME_FLAG_CONTAIN_POST_FRAME)) {
if (interlace_mode != VIDTYPE_PROGRESSIVE) {
/*for interlace*/
aipq_print(PRINT_OTHER,
"use di vf: %d*%d,flag=%x,type=%x\n",
di_vf->width,
di_vf->height,
di_vf->flag,
di_vf->type);
vf = di_vf;
}
}
dmabuf_put_vframe(dmabuf);
} else {
uhmod = uvm_get_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
if (IS_ERR_OR_NULL(uhmod) || !uhmod->arg) {
aipq_print(PRINT_OTHER, "get dw vf err: no v4lvideo\n");
dma_buf_put(dmabuf);
return NULL;
}
file_private_data = uhmod->arg;
uvm_put_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
if (!file_private_data) {
aipq_print(PRINT_ERROR, "invalid fd no uvm/v4lvideo\n");
} else {
vf = &file_private_data->vf;
if (vf->vf_ext)
vf = vf->vf_ext;
}
}
if (!vf) {
aipq_print(PRINT_ERROR, "not find vf\n");
dma_buf_put(dmabuf);
return NULL;
}
dma_buf_put(dmabuf);
return vf;
}
static int aipq_canvas[4] = {-1, -1, -1, -1};
struct ge2d_context_s *context;
static int get_canvas(u32 index)
{
const char *owner = "aipq";
if (aipq_canvas[index] < 0)
aipq_canvas[index] = canvas_pool_map_alloc_canvas(owner);
if (aipq_canvas[index] < 0)
aipq_print(PRINT_ERROR, "no canvas\n");
return aipq_canvas[index];
}
int ge2d_224_process(struct vframe_s *vf, struct ge2d_output_t *output)
{
struct config_para_ex_s ge2d_config_s;
struct config_para_ex_s *ge2d_config = &ge2d_config_s;
struct canvas_s cs0, cs1, cs2, cd;
int interlace_mode, src_format;
int input_width, input_height;
u32 output_canvas = get_canvas(3);
if (!context) {
context = create_ge2d_work_queue();
if (IS_ERR_OR_NULL(context)) {
aipq_print(PRINT_ERROR, "creat ge2d work failed\n");
return -1;
}
}
memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
input_width = vf->width;
input_height = vf->height;
if (vf->canvas0Addr == (u32)-1) {
canvas_config_config(get_canvas(0),
&vf->canvas0_config[0]);
if (vf->plane_num == 2) {
canvas_config_config(get_canvas(1),
&vf->canvas0_config[1]);
} else if (vf->plane_num == 3) {
canvas_config_config(get_canvas(1),
&vf->canvas0_config[1]);
canvas_config_config(get_canvas(2),
&vf->canvas0_config[2]);
}
ge2d_config->src_para.canvas_index =
get_canvas(0)
| (get_canvas(1) << 8)
| (get_canvas(2) << 16);
ge2d_config->src_planes[0].addr =
vf->canvas0_config[0].phy_addr;
ge2d_config->src_planes[0].w =
vf->canvas0_config[0].width;
ge2d_config->src_planes[0].h =
vf->canvas0_config[0].height;
ge2d_config->src_planes[1].addr =
vf->canvas0_config[1].phy_addr;
ge2d_config->src_planes[1].w =
vf->canvas0_config[1].width;
ge2d_config->src_planes[1].h =
vf->canvas0_config[1].height >> 1;
if (vf->plane_num == 3) {
ge2d_config->src_planes[2].addr =
vf->canvas0_config[2].phy_addr;
ge2d_config->src_planes[2].w =
vf->canvas0_config[2].width;
ge2d_config->src_planes[2].h =
vf->canvas0_config[2].height >> 1;
}
} else {
canvas_read(vf->canvas0Addr & 0xff, &cs0);
canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1);
canvas_read((vf->canvas0Addr >> 16) & 0xff, &cs2);
ge2d_config->src_planes[0].addr = cs0.addr;
ge2d_config->src_planes[0].w = cs0.width;
ge2d_config->src_planes[0].h = cs0.height;
ge2d_config->src_planes[1].addr = cs1.addr;
ge2d_config->src_planes[1].w = cs1.width;
ge2d_config->src_planes[1].h = cs1.height;
ge2d_config->src_planes[2].addr = cs2.addr;
ge2d_config->src_planes[2].w = cs2.width;
ge2d_config->src_planes[2].h = cs2.height;
ge2d_config->src_para.canvas_index = vf->canvas0Addr;
}
aipq_print(PRINT_OTHER, "src width: %d, height: %d\n",
input_width, input_height);
src_format = get_ge2d_input_format(vf);
interlace_mode = vf->type & VIDTYPE_TYPEMASK;
if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM ||
interlace_mode == VIDTYPE_INTERLACE_TOP) {
input_height >>= 1;
} else if (vf->height > uvm_aipq_skip_height) {
/*used to reduce bandwidth by change format to interlace*/
aipq_print(PRINT_OTHER, "use interlace format.\n");
input_height >>= 1;
src_format |= (GE2D_FMT_M24_YUV420T & (3 << 3));
}
if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR)
ge2d_config->src_para.format |= GE2D_LITTLE_ENDIAN;
aipq_print(PRINT_OTHER, "src width: %d, height: %d format =%x\n",
input_width, input_height, ge2d_config->src_para.format);
canvas_config(output_canvas, output->addr, output->width * 3,
output->height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_read(output_canvas & 0xff, &cd);
ge2d_config->src_para.format = src_format;
ge2d_config->dst_planes[0].addr = cd.addr;
ge2d_config->dst_planes[0].w = cd.width;
ge2d_config->dst_planes[0].h = cd.height;
ge2d_config->src_key.key_enable = 0;
ge2d_config->src_key.key_mask = 0;
ge2d_config->src_key.key_mode = 0;
ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->src_para.fill_color_en = 0;
ge2d_config->src_para.fill_mode = 0;
ge2d_config->src_para.x_rev = 0;
ge2d_config->src_para.y_rev = 0;
ge2d_config->src_para.color = 0xffffffff;
ge2d_config->src_para.top = 0;
ge2d_config->src_para.left = 0;
ge2d_config->src_para.width = input_width;
ge2d_config->src_para.height = input_height;
ge2d_config->alu_const_color = 0;
ge2d_config->bitmask_en = 0;
ge2d_config->src1_gb_alpha = 0;/* 0xff; */
ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->dst_para.canvas_index = output_canvas;
ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->dst_para.format = output->format | GE2D_LITTLE_ENDIAN;
ge2d_config->dst_para.fill_color_en = 0;
ge2d_config->dst_para.fill_mode = 0;
ge2d_config->dst_para.x_rev = 0;
ge2d_config->dst_para.y_rev = 0;
ge2d_config->dst_para.color = 0;
ge2d_config->dst_para.top = 0;
ge2d_config->dst_para.left = 0;
ge2d_config->dst_para.width = output->width;
ge2d_config->dst_para.height = output->height;
ge2d_config->dst_xy_swap = 0;
if (ge2d_context_config_ex(context, ge2d_config) < 0) {
aipq_print(PRINT_ERROR,
"++ge2d configing error.\n");
return -1;
}
stretchblt_noalpha(context, 0, 0, input_width, input_height,
0, 0, output->width, output->height);
return 0;
}
void free_aipq_data(void *arg)
{
if (arg) {
aipq_print(PRINT_OTHER, "%s\n", __func__);
kfree((u8 *)arg);
} else {
aipq_print(PRINT_ERROR, "%s NULL\n", __func__);
}
}
int attach_aipq_hook_mod_info(int shared_fd,
char *buf, struct uvm_hook_mod_info *info)
{
struct vf_aipq_t *nn_aipq = NULL;
struct uvm_hook_mod *uhmod = NULL;
struct dma_buf *dmabuf = NULL;
struct uvm_handle *handle;
bool attached = false;
struct uvm_aipq_info *aipq_info = (struct uvm_aipq_info *)buf;
struct vframe_s *vf = NULL;
int ret = 0;
bool enable_aipq = true;
aipq_info->need_do_aipq = 1;
aipq_info->dw_height = 0;
aipq_info->dw_width = 0;
if (!uvm_open_aipq) {
aipq_info->need_do_aipq = 0;
enable_aipq = false;
} else {
vf = aipq_get_dw_vf(aipq_info);
if (IS_ERR_OR_NULL(vf)) {
aipq_print(PRINT_OTHER, "get no vf\n");
return -EINVAL;
}
aipq_info->dw_height = vf->height;
aipq_info->dw_width = vf->width;
if (vf->width > 3840 ||
vf->height > 2160 ||
vf->flag & VFRAME_FLAG_VIDEO_SECURE) {
aipq_print(PRINT_OTHER, "bypass %d %d\n",
vf->width, vf->height);
aipq_info->need_do_aipq = 0;
enable_aipq = false;
}
}
dmabuf = dma_buf_get(shared_fd);
if (IS_ERR_OR_NULL(dmabuf)) {
aipq_print(PRINT_ERROR,
"Invalid dmabuf %s %d\n", __func__, __LINE__);
return -EINVAL;
}
if (!dmabuf_is_uvm(dmabuf)) {
aipq_print(PRINT_ERROR,
"attach:dmabuf is not uvm.dmabuf=%px, shared_fd=%d\n",
dmabuf, shared_fd);
dma_buf_put(dmabuf);
return -EINVAL;
}
handle = dmabuf->priv;
uhmod = uvm_get_hook_mod(dmabuf, PROCESS_AIPQ);
if (IS_ERR_OR_NULL(uhmod)) {
nn_aipq = kzalloc(sizeof(*nn_aipq), GFP_KERNEL);
aipq_print(PRINT_OTHER, "attach:first attach, need alloc\n");
if (!nn_aipq) {
dma_buf_put(dmabuf);
return -ENOMEM;
}
} else {
uvm_put_hook_mod(dmabuf, PROCESS_AIPQ);
attached = true;
nn_aipq = uhmod->arg;
if (!nn_aipq) {
aipq_print(PRINT_ERROR,
"attach:aipq is null, dmabuf=%p\n", dmabuf);
dma_buf_put(dmabuf);
return -EINVAL;
}
aipq_print(PRINT_OTHER, "nn_aipq=%px\n", nn_aipq);
memset(nn_aipq, 0, sizeof(struct vf_aipq_t));
}
dma_buf_put(dmabuf);
if (dmabuf_last == dmabuf) {
aipq_info->repeat_frame = 1;
} else {
aipq_info->repeat_frame = 0;
dmabuf_last = dmabuf;
ret = aipq_vf_set_value(aipq_info, enable_aipq);
if (ret != 0)
aipq_print(PRINT_OTHER, "set aipq value err\n");
}
if (attached)
return 0;
info->type = PROCESS_AIPQ;
info->arg = nn_aipq;
info->free = free_aipq_data;
info->acquire_fence = NULL;
info->getinfo = aipq_getinfo;
info->setinfo = aipq_setinfo;
return 0;
}
int aipq_setinfo(void *arg, char *buf)
{
struct uvm_aipq_info *aipq_info = NULL;
int ret = -1;
aipq_info = (struct uvm_aipq_info *)buf;
ret = aipq_vf_set_value(aipq_info, true);
if (ret != 0)
aipq_print(PRINT_ERROR, "set aipq value err\n");
return ret;
}
static void dump_224(struct vframe_s *vf, phys_addr_t addr)
{
struct file *fp;
char name_buf[32];
int write_size;
u8 *data;
mm_segment_t fs;
loff_t pos;
snprintf(name_buf, sizeof(name_buf), "/data/tmp/224.bin");
fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644);
if (IS_ERR(fp))
return;
write_size = 224 * 224 * 3;
data = codec_mm_vmap(addr, write_size);
if (!data)
return;
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
vfs_write(fp, data, write_size, &pos);
vfs_fsync(fp, 0);
aipq_print(PRINT_ERROR, "aipq: write %u size to addr%p\n",
write_size, data);
codec_mm_unmap_phyaddr(data);
filp_close(fp, NULL);
set_fs(fs);
snprintf(name_buf, sizeof(name_buf), "/data/tmp/dec.yuv");
fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644);
if (IS_ERR(fp))
return;
write_size = vf->canvas0_config[0].width * vf->canvas0_config[0].height
* 3 / 2;
data = codec_mm_vmap(vf->canvas0_config[0].phy_addr, write_size);
if (!data)
return;
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
vfs_write(fp, data, write_size, &pos);
vfs_fsync(fp, 0);
aipq_print(PRINT_ERROR, "aipq: write %u size to addr%p\n",
write_size, data);
codec_mm_unmap_phyaddr(data);
filp_close(fp, NULL);
set_fs(fs);
}
int aipq_getinfo(void *arg, char *buf)
{
struct uvm_aipq_info *aipq_info = NULL;
int ret = -1;
phys_addr_t addr = 0;
struct vframe_s *vf = NULL;
s32 aipq_fd;
size_t len = 0;
struct ge2d_output_t output;
struct timeval begin_time;
struct timeval end_time;
int cost_time;
aipq_info = (struct uvm_aipq_info *)buf;
if (aipq_info->get_info_type == AIPQ_GET_224_DATA) {
vf = aipq_get_dw_vf(aipq_info);
if (IS_ERR_OR_NULL(vf)) {
aipq_print(PRINT_ERROR, "get no vf\n");
return -EINVAL;
}
aipq_fd = aipq_info->aipq_fd;
if (aipq_fd != -1) {
ret = meson_ion_share_fd_to_phys(aipq_fd, &addr, &len);
if (ret < 0) {
aipq_print(PRINT_ERROR,
"import fd %d failed\n", aipq_fd);
return -EINVAL;
}
}
memset(&output, 0, sizeof(struct ge2d_output_t));
output.width = 224;
output.height = 224;
output.format = GE2D_FORMAT_S24_BGR;
output.addr = addr;
do_gettimeofday(&begin_time);
ret = ge2d_224_process(vf, &output);
if (ret < 0) {
aipq_print(PRINT_ERROR, "ge2d err\n");
return -EINVAL;
}
do_gettimeofday(&end_time);
cost_time = (1000000 * (end_time.tv_sec - begin_time.tv_sec)
+ (end_time.tv_usec - begin_time.tv_usec)) / 1000;
aipq_print(PRINT_OTHER, "ge2d cost: %d ms\n", cost_time);
if (uvm_aipq_dump) {
uvm_aipq_dump = 0;
dump_224(vf, addr);
}
}
return 0;
}