blob: 3f34c6eec4606efe118a2d08c8cad8b752c60b62 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
#include <linux/version.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-v4l2.h>
#include <linux/amlogic/major.h>
#include <linux/platform_device.h>
#include <uapi/linux/sched/types.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/vdetect/vdetect.h>
#include <linux/amlogic/media/utils/am_com.h>
#define MAX_INST 1
#define RECEIVER_NAME "vdetect"
#define PROVIDER_NAME "vdetect"
#define VDETECT_MAJOR_VERSION 0
#define VDETECT_MINOR_VERSION 7
#define VDETECT_RELEASE 1
#define VDETECT_VERSION \
KERNEL_VERSION(VDETECT_MAJOR_VERSION, \
VDETECT_MINOR_VERSION, \
VDETECT_RELEASE)
#define CMA_ALLOC_SIZE 1
#define MAGIC_RE_MEM 0x123039dc
#define VDETECT_MODULE_NAME "vdetect"
#define V4L2_AMLOGIC_VDETECT_BASE (V4L2_CID_USER_BASE | 0x1200)
#define PRINT_ERROR 0X0
#define PRINT_CAPTUREINFO 0X0001
#define PRINT_BUFFERINFO 0X0002
#define PRINT_THREADINFO 0X0004
#define PRINT_PATHINFO 0X0008
#define PRINT_TIMESTAMP 0X0010
#define PRINT_MN_SET_FLOW 0X0020
#define PRINT_PATH_OTHER 0X0040
#define PRINT_CAP_OTHER 0X0080
static int detect_debug;
static int detect_dump;
static int detect_thread;
static int tv_add_vdetect;
static int aipq_enable;
static int post_get_count;
static int post_put_count;
static int pre_put_count;
static DEFINE_SPINLOCK(lock);
static u32 vid_limit = 32;
static unsigned int vdetect_base = 40;
static u32 frame_number;
static struct vdetect_frame_nn_info nn_info[MAX_NN_INFO_BUF_COUNT];
static u32 nn_frame_num;
int vdetect_print(int index, int debug_flag, const char *fmt, ...)
{
if ((detect_debug & debug_flag) ||
debug_flag == PRINT_ERROR) {
unsigned char buf[256];
int len = 0;
va_list args;
va_start(args, fmt);
len = sprintf(buf, "vdetect.%d ", index);
vsnprintf(buf + len, 256 - len, fmt, args);
pr_info("%s", buf);
va_end(args);
}
return 0;
}
static char *result_vnn[] = {
"faceskin",
"bluesky",
"flowers",
"foods",
"breads",
"fruits",
"meats",
"document",
"ocean",
"pattern",
"group",
"animals",
"grass",
"iceskate",
"leaves",
"racetrack",
"fireworks",
"waterfall",
"beach",
"waterside",
"snows",
"nightscop",
"sunset"
};
static struct vdetect_fmt formats[] = {
{
.name = "RGB565 (BE)",
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
.depth = 16,
},
{
.name = "RGB888 (24)",
.fourcc = V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */
.depth = 24,
},
{
.name = "BGR888 (24)",
.fourcc = V4L2_PIX_FMT_BGR24, /* 24 BGR-8-8-8 */
.depth = 24,
},
{
.name = "RGBA888 (32)",
.fourcc = V4L2_PIX_FMT_RGB32, /* 32 RGBA-8-8-8 */
.depth = 32,
},
{
.name = "12 Y/CbCr 4:2:0",
.fourcc = V4L2_PIX_FMT_NV12,
.depth = 12,
},
{
.name = "12 Y/CbCr 4:2:0",
.fourcc = V4L2_PIX_FMT_NV21,
.depth = 12,
},
{
.name = "YUV420P",
.fourcc = V4L2_PIX_FMT_YUV420,
.depth = 12,
},
{
.name = "YVU420P",
.fourcc = V4L2_PIX_FMT_YVU420,
.depth = 12,
}
};
static struct vdetect_fmt *get_format(struct v4l2_format *f)
{
struct vdetect_fmt *fmt;
unsigned int k;
for (k = 0; k < ARRAY_SIZE(formats); k++) {
fmt = &formats[k];
if (fmt->fourcc == f->fmt.pix.pixelformat)
break;
}
if (k == ARRAY_SIZE(formats))
fmt = NULL;
return fmt;
}
static int alloc_canvas(struct vdetect_dev *dev)
{
const char *keep_owner = "vdetect";
if (dev->src_canvas[0] < 0)
dev->src_canvas[0] =
canvas_pool_map_alloc_canvas(keep_owner);
if (dev->src_canvas[1] < 0)
dev->src_canvas[1] =
canvas_pool_map_alloc_canvas(keep_owner);
if (dev->src_canvas[2] < 0)
dev->src_canvas[2] =
canvas_pool_map_alloc_canvas(keep_owner);
if (dev->dst_canvas < 0)
dev->dst_canvas =
canvas_pool_map_alloc_canvas(keep_owner);
if (dev->src_canvas[0] < 0 ||
dev->src_canvas[1] < 0 ||
dev->src_canvas[2] < 0 ||
dev->dst_canvas < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"canvas alloc failed!\n");
return -1;
}
return 0;
}
static int free_canvas(struct vdetect_dev *dev)
{
if (dev->src_canvas[0] >= 0) {
canvas_pool_map_free_canvas(dev->src_canvas[0]);
dev->src_canvas[0] = -1;
}
if (dev->src_canvas[1] >= 0) {
canvas_pool_map_free_canvas(dev->src_canvas[1]);
dev->src_canvas[1] = -1;
}
if (dev->src_canvas[2] >= 0) {
canvas_pool_map_free_canvas(dev->src_canvas[2]);
dev->src_canvas[2] = -1;
}
if (dev->dst_canvas >= 0) {
canvas_pool_map_free_canvas(dev->dst_canvas);
dev->dst_canvas = -1;
}
if (dev->src_canvas[0] >= 0 ||
dev->src_canvas[1] >= 0 ||
dev->src_canvas[2] >= 0 ||
dev->dst_canvas >= 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"canvas free failed!\n");
return -1;
}
return 0;
}
static int vdetect_cma_buf_init(struct vdetect_dev *dev)
{
int flags;
int ret;
flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR;
ret = alloc_canvas(dev);
if (ret < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"alloc cma failed!\n");
return -1;
}
dev->buffer_start =
codec_mm_alloc_for_dma(dev->recv_name,
(CMA_ALLOC_SIZE * SZ_1M) / PAGE_SIZE,
0, flags);
if (!(dev->buffer_start)) {
vdetect_print(dev->inst, PRINT_ERROR,
"alloc cma buffer failed\n");
return -1;
}
dev->buffer_size = CMA_ALLOC_SIZE * SZ_1M;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"vdetect video %d cma memory is %x , size is %x\n",
dev->inst, (unsigned int)dev->buffer_start,
(unsigned int)dev->buffer_size);
return 0;
}
static int vdetect_cma_buf_uninit(struct vdetect_dev *dev)
{
int ret;
ret = free_canvas(dev);
if (ret < 0)
vdetect_print(dev->inst, PRINT_ERROR,
"%s free canvas fail! line: %d",
__func__, __LINE__);
if (dev->buffer_start != 0) {
codec_mm_free_for_dma("vdetect", dev->buffer_start);
dev->buffer_start = 0;
dev->buffer_size = 0;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"vdetect video %d cma memory release succeed!\n",
dev->inst);
}
return 0;
}
static DEFINE_SPINLOCK(devlist_lock);
static unsigned long vdetect_devlist_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&devlist_lock, flags);
return flags;
}
static void vdetect_devlist_unlock(unsigned long flags)
{
spin_unlock_irqrestore(&devlist_lock, flags);
}
static struct vframe_s *vdetect_vf_peek(void *op_arg)
{
struct vdetect_dev *dev = (struct vdetect_dev *)op_arg;
return vf_peek(dev->recv_name);
}
static struct vframe_s *vdetect_vf_get(void *op_arg)
{
struct vframe_s *vf;
struct vdetect_dev *dev = (struct vdetect_dev *)op_arg;
unsigned long flags;
vf = vf_get(dev->recv_name);
if (!vf)
return NULL;
vf->nn_value[AI_PQ_TOP - 1].maxclass = dev->vf_num;
dev->vf_num++;
post_get_count++;
if (vf->flag & VFRAME_FLAG_GAME_MODE ||
vf->width > 3840 ||
vf->height > 2160) {
dev->last_vf = NULL;
} else {
vf->flag |= VFRAME_FLAG_THROUGH_VDETECT;
}
spin_lock_irqsave(&lock, flags);
if (atomic_read(&dev->vdect_status) != VDETECT_PREPARE)
dev->last_vf = vf;
spin_unlock_irqrestore(&lock, flags);
if (atomic_read(&dev->is_playing) == 0) {
atomic_set(&dev->is_playing, 1);
if (detect_thread)
up(&dev->thread_sem);
}
return vf;
}
static void vdetect_vf_put(struct vframe_s *vf, void *op_arg)
{
struct vdetect_dev *dev = (struct vdetect_dev *)op_arg;
unsigned long lock_flag;
spin_lock_irqsave(&lock, lock_flag);
if (atomic_read(&dev->vdect_status) == VDETECT_PREPARE ||
(vf->flag & VFRAME_FLAG_VIDEO_VDETECT)) {
vf->flag |= VFRAME_FLAG_VIDEO_VDETECT_PUT;
spin_unlock_irqrestore(&lock, lock_flag);
} else {
spin_unlock_irqrestore(&lock, lock_flag);
vf_put(vf, dev->recv_name);
pre_put_count++;
}
post_put_count++;
}
static int get_source_type(struct vframe_s *vf)
{
enum vframe_source_type ret;
int interlace_mode;
interlace_mode = vf->type & VIDTYPE_TYPEMASK;
if (vf->source_type == VFRAME_SOURCE_TYPE_HDMI ||
vf->source_type == VFRAME_SOURCE_TYPE_CVBS) {
if ((vf->bitdepth & BITDEPTH_Y10) &&
(!(vf->type & VIDTYPE_COMPRESS)) &&
(get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL))
ret = VDIN_10BIT_NORMAL;
else
ret = VDIN_8BIT_NORMAL;
} else {
if ((vf->bitdepth & BITDEPTH_Y10) &&
(!(vf->type & VIDTYPE_COMPRESS)) &&
(get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) {
if (interlace_mode == VIDTYPE_INTERLACE_TOP)
ret = DECODER_10BIT_TOP;
else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
ret = DECODER_10BIT_BOTTOM;
else
ret = DECODER_10BIT_NORMAL;
} else {
if (interlace_mode == VIDTYPE_INTERLACE_TOP)
ret = DECODER_8BIT_TOP;
else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
ret = DECODER_8BIT_BOTTOM;
else
ret = DECODER_8BIT_NORMAL;
}
}
return ret;
}
static int get_input_format(struct vframe_s *vf)
{
int format = GE2D_FORMAT_M24_YUV420;
enum vframe_source_type soure_type;
soure_type = get_source_type(vf);
switch (soure_type) {
case DECODER_8BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422;
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21;
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444;
else
format = GE2D_FORMAT_M24_YUV420;
break;
case DECODER_8BIT_BOTTOM:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422
| (GE2D_FORMAT_S16_YUV422B & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21
| (GE2D_FORMAT_M24_NV21B & (3 << 3));
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444
| (GE2D_FORMAT_S24_YUV444B & (3 << 3));
else
format = GE2D_FORMAT_M24_YUV420
| (GE2D_FMT_M24_YUV420B & (3 << 3));
break;
case DECODER_8BIT_TOP:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422
| (GE2D_FORMAT_S16_YUV422T & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21
| (GE2D_FORMAT_M24_NV21T & (3 << 3));
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444
| (GE2D_FORMAT_S24_YUV444T & (3 << 3));
else
format = GE2D_FORMAT_M24_YUV420
| (GE2D_FMT_M24_YUV420T & (3 << 3));
break;
case DECODER_10BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422;
else
format = GE2D_FORMAT_S16_12BIT_YUV422;
}
break;
case DECODER_10BIT_BOTTOM:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422
| (GE2D_FORMAT_S16_10BIT_YUV422B
& (3 << 3));
else
format = GE2D_FORMAT_S16_12BIT_YUV422
| (GE2D_FORMAT_S16_12BIT_YUV422B
& (3 << 3));
}
break;
case DECODER_10BIT_TOP:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422
| (GE2D_FORMAT_S16_10BIT_YUV422T
& (3 << 3));
else
format = GE2D_FORMAT_S16_12BIT_YUV422
| (GE2D_FORMAT_S16_12BIT_YUV422T
& (3 << 3));
}
break;
case VDIN_8BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422;
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21;
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444;
else
format = GE2D_FORMAT_M24_YUV420;
break;
case VDIN_10BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422;
else
format = GE2D_FORMAT_S16_12BIT_YUV422;
}
break;
default:
format = GE2D_FORMAT_M24_YUV420;
}
return format;
}
static int get_output_format(int v4l2_format)
{
int format = GE2D_FORMAT_S24_YUV444;
switch (v4l2_format) {
case V4L2_PIX_FMT_RGB565X:
format = GE2D_FORMAT_S16_RGB_565;
break;
case V4L2_PIX_FMT_YUV444:
format = GE2D_FORMAT_S24_YUV444;
break;
case V4L2_PIX_FMT_VYUY:
format = GE2D_FORMAT_S16_YUV422;
break;
case V4L2_PIX_FMT_BGR24:
format = GE2D_FORMAT_S24_RGB;
break;
case V4L2_PIX_FMT_RGB24:
format = GE2D_FORMAT_S24_BGR;
break;
case V4L2_PIX_FMT_RGB32:
format = GE2D_FORMAT_S32_ABGR;
break;
case V4L2_PIX_FMT_NV12:
format = GE2D_FORMAT_M24_NV12;
break;
case V4L2_PIX_FMT_NV21:
format = GE2D_FORMAT_M24_NV21;
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
format = GE2D_FORMAT_S8_Y;
break;
default:
break;
}
return format;
}
int get_vdetect_canvas_index(struct vdetect_output *output,
int start_canvas)
{
int canvas = start_canvas;
int v4l2_format = output->v4l2_format;
void *buf = (void *)output->vbuf;
int width = output->width;
int height = output->height;
int canvas_height = height;
switch (v4l2_format) {
case V4L2_PIX_FMT_RGB565X:
case V4L2_PIX_FMT_VYUY:
canvas = start_canvas;
canvas_config(canvas, (unsigned long)buf, width * 2,
canvas_height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
break;
case V4L2_PIX_FMT_YUV444:
case V4L2_PIX_FMT_BGR24:
case V4L2_PIX_FMT_RGB24:
canvas = start_canvas;
canvas_config(canvas, (unsigned long)buf, width * 3,
canvas_height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
break;
case V4L2_PIX_FMT_RGB32:
canvas = start_canvas;
canvas_config(canvas, (unsigned long)buf, width * 4,
canvas_height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
break;
default:
break;
}
return canvas;
}
static int copy_phybuf_to_file(ulong phys, u32 size,
struct file *fp, loff_t pos)
{
u32 span = SZ_1M;
u8 *p;
int remain_size = 0;
ssize_t ret;
remain_size = size;
while (remain_size > 0) {
if (remain_size < span)
span = remain_size;
p = codec_mm_vmap(phys, PAGE_ALIGN(span));
if (!p) {
pr_info("vdetect: vmap failed\n");
return -1;
}
codec_mm_dma_flush(p, span, DMA_FROM_DEVICE);
ret = vfs_write(fp, (char *)p, span, &pos);
if (ret <= 0)
pr_info("vdetect: vfs write failed!\n");
phys += span;
codec_mm_unmap_phyaddr(p);
remain_size -= span;
pr_info("pos: %lld, phys: %lx, remain_size: %d\n",
pos, phys, remain_size);
}
return 0;
}
static int vdetect_ge2d_process(struct config_para_ex_s *ge2d_config,
struct vdetect_output *output,
struct vdetect_dev *dev)
{
struct vframe_s *vf = dev->ge2d_vf;
struct ge2d_context_s *context = dev->context;
struct canvas_s cs0, cs1, cs2, cd;
int interlace_mode;
int output_canvas = output->canvas_id;
int input_width, input_height;
unsigned long lock_flag;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
mutex_lock(&dev->vf_mutex);
if (atomic_read(&dev->vdect_status) == VDETECT_DROP_VF) {
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_INIT);
vdetect_print(dev->inst, PRINT_ERROR,
"%s exit\n", __func__);
return -2;
}
input_width = vf->width;
input_height = vf->height;
if (vf->canvas0Addr == (u32)-1) {
canvas_config_config(dev->src_canvas[0],
&vf->canvas0_config[0]);
if (vf->plane_num == 2) {
canvas_config_config(dev->src_canvas[1],
&vf->canvas0_config[1]);
} else if (vf->plane_num == 3) {
canvas_config_config(dev->src_canvas[2],
&vf->canvas0_config[2]);
}
ge2d_config->src_para.canvas_index =
dev->src_canvas[0]
| (dev->src_canvas[1] << 8)
| (dev->src_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;
}
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"src canvas 0x%x 0x%x 0x%x\n",
dev->src_canvas[0],
dev->src_canvas[1],
dev->src_canvas[2]);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"src width: %d, height: %d\n",
input_width, input_height);
interlace_mode = vf->type & VIDTYPE_TYPEMASK;
if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM ||
interlace_mode == VIDTYPE_INTERLACE_TOP)
input_height >>= 1;
ge2d_config->src_para.format = get_input_format(vf);
mutex_unlock(&dev->vf_mutex);
canvas_read(output_canvas & 0xff, &cd);
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 =
get_output_format(output->v4l2_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 = dev->dst_width;
ge2d_config->dst_para.height = dev->dst_height;
ge2d_config->dst_xy_swap = 0;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"dst canvas 0X%x\n", dev->dst_canvas);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"dst width: %d, height: %d\n",
dev->dst_width, dev->dst_height);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"dst_buffer addr: 0X%lx\n", cd.addr);
if (ge2d_context_config_ex(context, ge2d_config) < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"++ge2d configing error.\n");
return -1;
}
stretchblt_noalpha(context, 0, 0, input_width, input_height,
0, 0, output->width, output->height);
mutex_lock(&dev->vf_mutex);
if (atomic_read(&dev->vdect_status) == VDETECT_DROP_VF) {
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_INIT);
vdetect_print(dev->inst, PRINT_ERROR,
"%s line: %d\n", __func__, __LINE__);
return -2;
}
spin_lock_irqsave(&lock, lock_flag);
if (vf->flag & VFRAME_FLAG_VIDEO_VDETECT)
vf->flag &= ~VFRAME_FLAG_VIDEO_VDETECT;
if (vf->flag & VFRAME_FLAG_VIDEO_VDETECT_PUT) {
vf->flag &= ~VFRAME_FLAG_VIDEO_VDETECT_PUT;
spin_unlock_irqrestore(&lock, lock_flag);
vf_put(vf, dev->recv_name);
pre_put_count++;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s put vf buffer!\n", __func__);
} else {
spin_unlock_irqrestore(&lock, lock_flag);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"ge2d don't put!\n");
}
mutex_unlock(&dev->vf_mutex);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s end line %d\n", __func__, __LINE__);
return 0;
}
static int vdetect_fill_buffer(struct vdetect_dev *dev)
{
struct vdetect_buffer *buf = NULL;
struct config_para_ex_s ge2d_config;
struct vdetect_output output;
struct vdetect_dmaqueue *dma_q = &dev->dma_q;
int ret = 0;
void *vbuf = NULL;
unsigned long flags = 0;
unsigned long flag;
mm_segment_t old_fs;
struct file *filp_scr = NULL;
struct canvas_s cd;
int result = 0;
ulong phys;
u32 width, height;
struct timeval time_begin;
struct timeval time_end;
int cost_time;
if (dev->ge2d_vf == dev->last_vf) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s: the same frame!\n", __func__);
return -1;
}
if (IS_ERR_OR_NULL(dev->last_vf)) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s: vf is NULL!\n", __func__);
return -1;
}
spin_lock_irqsave(&dev->slock, flags);
if (list_empty(&dma_q->active)) {
spin_unlock_irqrestore(&dev->slock, flags);
vdetect_print(dev->inst, PRINT_ERROR,
"%s: dma queue is inactive!\n", __func__);
return -1;
}
buf = list_entry(dma_q->active.next, struct vdetect_buffer, vb.queue);
buf->vb.state = VIDEOBUF_ACTIVE;
list_del(&buf->vb.queue);
spin_unlock_irqrestore(&dev->slock, flags);
vbuf = (void *)videobuf_to_res(&buf->vb);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s buf_addr: 0x%lx\n", __func__, (unsigned long)vbuf);
if (!vbuf) {
vdetect_print(dev->inst, PRINT_ERROR, "vbuf is invalue!\n");
goto fail;
}
/* memset(&ge2d_config,0,sizeof(struct config_para_ex_s)); */
memset(&output, 0, sizeof(struct vdetect_output));
output.v4l2_format = dev->fmt->fourcc;
output.vbuf = vbuf;
output.width = buf->vb.width;
output.height = buf->vb.height;
output.canvas_id =
get_vdetect_canvas_index(&output, dev->dst_canvas);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
mutex_lock(&dev->vf_mutex);
if (atomic_read(&dev->vdect_status) == VDETECT_DROP_VF) {
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_INIT);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
goto fail;
}
spin_lock_irqsave(&lock, flag);
atomic_set(&dev->vdect_status, VDETECT_PREPARE);
dev->last_vf->flag |= VFRAME_FLAG_VIDEO_VDETECT;
dev->ge2d_vf = dev->last_vf;
atomic_set(&dev->vdect_status, VDETECT_INIT);
spin_unlock_irqrestore(&lock, flag);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
if (detect_dump & 1) {
old_fs = get_fs();
set_fs(KERNEL_DS);
filp_scr = filp_open("/data/temp/screen_capture",
O_RDWR | O_CREAT, 0666);
if (IS_ERR(filp_scr)) {
pr_info("open /data/temp/screen_capture failed\n");
} else {
if (dev->ge2d_vf->canvas0Addr == (u32)-1) {
phys = (ulong)
dev->ge2d_vf->canvas0_config[0].phy_addr;
width = dev->ge2d_vf->canvas0_config[0].width;
height =
dev->ge2d_vf->canvas0_config[0].height;
} else {
canvas_read(dev->ge2d_vf->canvas0Addr & 0xff,
&cd);
phys = cd.addr;
width = cd.width;
height = cd.height;
}
result = copy_phybuf_to_file(phys, width * height,
filp_scr, 0);
if (result < 0)
pr_info("write screen_capture failed\n");
pr_info("scr addr: %0lx, width: %d, height: %d\n",
phys, width, height);
pr_info("dump source type: %d\n",
get_input_format(dev->ge2d_vf));
vfs_fsync(filp_scr, 0);
filp_close(filp_scr, NULL);
set_fs(old_fs);
}
}
mutex_unlock(&dev->vf_mutex);
do_gettimeofday(&time_begin);
ret = vdetect_ge2d_process(&ge2d_config, &output, dev);
if (ret < 0) {
if ((ret == -2) || atomic_read(&dev->is_playing) == 0) {
dev->ge2d_vf = NULL;
vdetect_print(dev->inst, PRINT_ERROR,
"%s exit\n", __func__);
goto fail;
}
mutex_lock(&dev->vf_mutex);
if (atomic_read(&dev->vdect_status) == VDETECT_DROP_VF) {
mutex_unlock(&dev->vf_mutex);
dev->ge2d_vf = NULL;
atomic_set(&dev->vdect_status, VDETECT_INIT);
vdetect_print(dev->inst, PRINT_ERROR,
"%s line: %d\n", __func__, __LINE__);
goto fail;
}
if (dev->ge2d_vf->flag & VFRAME_FLAG_VIDEO_VDETECT)
dev->ge2d_vf->flag &= ~VFRAME_FLAG_VIDEO_VDETECT;
if (dev->ge2d_vf->flag & VFRAME_FLAG_VIDEO_VDETECT_PUT) {
dev->ge2d_vf->flag &= ~VFRAME_FLAG_VIDEO_VDETECT_PUT;
vf_put(dev->ge2d_vf, dev->recv_name);
pre_put_count++;
}
mutex_unlock(&dev->vf_mutex);
dev->ge2d_vf = NULL;
vdetect_print(dev->inst, PRINT_ERROR, "ge2d deal failed!\n");
goto fail;
}
do_gettimeofday(&time_end);
cost_time = (time_end.tv_sec * 1000 * 1000 + time_end.tv_usec
- time_begin.tv_sec * 1000 * 1000
- time_begin.tv_usec) / 1000;
vdetect_print(dev->inst, PRINT_TIMESTAMP,
"ge2d cost time: %d ms\n", cost_time);
buf->vb.state = VIDEOBUF_DONE;
/*wait up vb done buffer workqueue*/
if (waitqueue_active(&buf->vb.done))
wake_up(&buf->vb.done);
return ret;
fail:
spin_lock_irqsave(&dev->slock, flags);
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &dma_q->active);
spin_unlock_irqrestore(&dev->slock, flags);
return ret;
}
static int vdetect_thread(void *data)
{
struct vdetect_dev *dev = data;
struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
int ret = 0;
sched_setscheduler(current, SCHED_FIFO, &param);
allow_signal(SIGTERM);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"start %s thread.\n", dev->recv_name);
while (down_interruptible(&dev->thread_sem) == 0) {
if (atomic_read(&dev->is_playing) == 0) {
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s has no vf source\n", __func__);
continue;
}
if (dev->begin_time.tv_sec == 0 &&
dev->begin_time.tv_usec == 0)
do_gettimeofday(&dev->begin_time);
if (kthread_should_stop()) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s kthread stop 1.\n", dev->recv_name);
break;
}
#ifdef USE_SEMA_QBUF
ret = wake_up_interruptible(&dev->dma_q.qbuf_comp);
#endif
vdetect_print(dev->inst, PRINT_THREADINFO,
"%s task_running = %d\n", dev->recv_name,
dev->dma_q.task_running);
if (!dev->dma_q.task_running) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s here break.\n", dev->recv_name);
break;
}
vdetect_fill_buffer(dev);
if (kthread_should_stop()) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s kthread stop 2.\n", dev->recv_name);
break;
}
vdetect_print(dev->inst, PRINT_THREADINFO,
"%s_thread while 1 .\n", dev->recv_name);
}
while (!kthread_should_stop()) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s_thread while 2 .\n", dev->recv_name);
usleep_range(9000, 10000);
}
/*msleep(10);*/
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"thread: exit\n");
return ret;
}
/* ------------------------------------------------------------------
* Videobuf operations
* ------------------------------------------------------------------
*/
static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct videobuf_res_privdata *res =
(struct videobuf_res_privdata *)vq->priv_data;
struct vdetect_dev *dev = (struct vdetect_dev *)res->priv;
int height = dev->height;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s line: %d\n", __func__, __LINE__);
if (height % 16 != 0)
height = ((height + 15) >> 4) << 4;
*size = (dev->width * height * dev->fmt->depth) >> 3;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s, width=%d, height=%d\n",
__func__, dev->width, height);
if (*count == 0)
*count = 32;
while (*size * *count > vid_limit * 1024 * 1024)
(*count)--;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s, count=%d, size=%d\n", __func__, *count, *size);
return 0;
}
static void free_buffer(struct videobuf_queue *vq, struct vdetect_buffer *buf)
{
struct videobuf_res_privdata *res =
(struct videobuf_res_privdata *)vq->priv_data;
struct vdetect_dev *dev = (struct vdetect_dev *)res->priv;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s state:%i\n", __func__, buf->vb.state);
videobuf_waiton(vq, &buf->vb, 0, 0);
if (in_interrupt())
WARN_ON(1);
videobuf_res_free(vq, &buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
#define norm_maxw() 2000
#define norm_maxh() 1600
static int buffer_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *videobuf,
enum v4l2_field field)
{
struct videobuf_res_privdata *res =
(struct videobuf_res_privdata *)vq->priv_data;
struct vdetect_dev *dev = (struct vdetect_dev *)res->priv;
struct vdetect_buffer *buf =
container_of(videobuf, struct vdetect_buffer, vb);
void *vbuf;
int rc;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s field: %d\n", __func__, field);
if (dev->width < 16 || dev->width > norm_maxw() ||
dev->height < 16 || dev->height > norm_maxh()) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s line: %d\n", __func__, __LINE__);
return -EINVAL;
}
buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s line: %d\n", __func__, __LINE__);
return -EINVAL;
}
/* These properties only change when queue is idle, see s_fmt */
buf->fmt = dev->fmt;
buf->vb.width = dev->width;
buf->vb.height = dev->height;
buf->vb.field = field;
vbuf = (void *)videobuf_to_res(&buf->vb);
if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s addr: 0x%lx, i: %d, magic:%d", __func__,
(unsigned long)vbuf, buf->vb.i, buf->vb.magic);
if (rc < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s line: %d\n", __func__, __LINE__);
goto fail;
}
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail: free_buffer(vq, buf);
return rc;
}
static void buffer_queue(struct videobuf_queue *vq,
struct videobuf_buffer *videobuf)
{
struct vdetect_buffer *buf =
container_of(videobuf, struct vdetect_buffer, vb);
struct videobuf_res_privdata *res =
(struct videobuf_res_privdata *)vq->priv_data;
struct vdetect_dev *dev = (struct vdetect_dev *)res->priv;
struct vdetect_dmaqueue *dma_q = &dev->dma_q;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s line: %d\n", __func__, __LINE__);
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &dma_q->active);
}
static void buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *videobuf)
{
struct vdetect_buffer *buf =
container_of(videobuf, struct vdetect_buffer, vb);
struct videobuf_res_privdata *res =
(struct videobuf_res_privdata *)vq->priv_data;
struct vdetect_dev *dev = (struct vdetect_dev *)res->priv;
vdetect_print(dev->inst, PRINT_BUFFERINFO,
"%s line: %d\n", __func__, __LINE__);
free_buffer(vq, buf);
}
static struct videobuf_queue_ops vdetect_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int vdetect_event_cb(int type, void *data, void *private_data)
{
struct vdetect_dev *dev = (struct vdetect_dev *)private_data;
if (type & VFRAME_EVENT_RECEIVER_PUT)
;
else if (type & VFRAME_EVENT_RECEIVER_GET)
;
else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT)
vdetect_print(dev->inst, PRINT_PATHINFO,
"receiver is waiting\n");
else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT)
vdetect_print(dev->inst, PRINT_PATHINFO,
"frame wait\n");
return 0;
}
static int vdetect_vf_states(struct vframe_states *states, void *op_arg)
{
states->vf_pool_size = 0;
states->buf_recycle_num = 0;
states->buf_free_num = 0;
states->buf_avail_num = 0;
/* spin_unlock_irqrestore(&lock, flags); */
return 0;
}
static const struct vframe_operations_s vdetect_vf_prov = {
.peek = vdetect_vf_peek,
.get = vdetect_vf_get,
.put = vdetect_vf_put,
.event_cb = vdetect_event_cb,
.vf_states = vdetect_vf_states,
};
static int video_receiver_event_fun(int type, void *data, void *private_data)
{
struct vdetect_dev *dev = (struct vdetect_dev *)private_data;
struct vframe_states states;
int i = 0;
switch (type) {
case VFRAME_EVENT_PROVIDER_VFRAME_READY:
vf_notify_receiver(dev->prov_name, type, NULL);
vdetect_print(dev->inst, PRINT_PATH_OTHER, "ready\n");
break;
case VFRAME_EVENT_PROVIDER_QUREY_STATE:
vdetect_print(dev->inst, PRINT_PATH_OTHER, "state\n");
vdetect_vf_states(&states, dev);
if (states.buf_avail_num >= 0)
return RECEIVER_ACTIVE;
if (vf_notify_receiver(dev->prov_name, type, NULL)
== RECEIVER_ACTIVE) {
return RECEIVER_ACTIVE;
}
return RECEIVER_INACTIVE;
case VFRAME_EVENT_PROVIDER_START:
vf_notify_receiver(dev->prov_name, type, NULL);
vdetect_print(dev->inst, PRINT_PATH_OTHER, "start\n");
break;
case VFRAME_EVENT_PROVIDER_REG:
vf_reg_provider(&dev->prov);
vdetect_print(dev->inst, PRINT_PATHINFO, "reg\n");
memset(nn_info, 0, sizeof(nn_info));
dev->vf_num = 0;
for (i = 0; i < AI_PQ_TOP; i++) {
dev->nn_value[i].maxclass = 0;
dev->nn_value[i].maxprob = 0;
}
mutex_lock(&dev->vf_mutex);
atomic_set(&dev->is_dev_reged, 1);
mutex_unlock(&dev->vf_mutex);
post_get_count = 0;
pre_put_count = 0;
post_put_count = 0;
break;
case VFRAME_EVENT_PROVIDER_UNREG:
mutex_lock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_DROP_VF);
atomic_set(&dev->is_dev_reged, 0);
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->is_playing, 0);
vf_unreg_provider(&dev->prov);
vdetect_print(dev->inst, PRINT_PATHINFO, "unreg\n");
break;
case VFRAME_EVENT_PROVIDER_LIGHT_UNREG:
mutex_lock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_DROP_VF);
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->is_playing, 0);
vf_light_unreg_provider(&dev->prov);
vdetect_print(dev->inst, PRINT_PATHINFO, "light unreg\n");
break;
case VFRAME_EVENT_PROVIDER_RESET:
mutex_lock(&dev->vf_mutex);
atomic_set(&dev->vdect_status, VDETECT_DROP_VF);
mutex_unlock(&dev->vf_mutex);
atomic_set(&dev->is_playing, 0);
vdetect_print(dev->inst, PRINT_PATHINFO, "reset\n");
vf_notify_receiver(dev->prov_name, type, NULL);
break;
case VFRAME_EVENT_PROVIDER_FR_HINT:
case VFRAME_EVENT_PROVIDER_FR_END_HINT:
vf_notify_receiver(dev->prov_name, type, data);
break;
default:
break;
}
return 0;
}
static const struct vframe_receiver_op_s vdetect_vf_recv = {
.event_cb = video_receiver_event_fun
};
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
strcpy(cap->driver, "vdetect");
strcpy(cap->card, "vdetect");
strlcpy(cap->bus_info,
dev->v4l2_dev.name,
sizeof(cap->bus_info));
cap->version = VDETECT_VERSION;
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
| V4L2_CAP_READWRITE;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct vdetect_dev *dev = NULL;
#ifdef USE_SEMA_QBUF
struct vdetect_dmaqueue *dma_q = NULL;
#endif
dev = video_drvdata(file);
#ifdef USE_SEMA_QBUF
dma_q = &dev->dma_q;
init_completion(&dma_q->qbuf_comp);
#endif
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
return videobuf_reqbufs(&dev->vbuf_queue, p);
}
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vdetect_dev *dev = NULL;
struct vdetect_output output;
int ret = 0;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
ret = videobuf_querybuf(&dev->vbuf_queue, p);
if (ret == 0) {
memset(&output, 0, sizeof(struct vdetect_output));
output.v4l2_format = dev->fmt->fourcc;
output.vbuf = NULL;
output.width = dev->width;
output.height = dev->height;
output.canvas_id = -1;
p->reserved = dev->dst_canvas;
} else {
p->reserved = 0;
}
return ret;
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vdetect_dev *dev = NULL;
#ifdef USE_SEMA_QBUF
struct vdetect_dmaqueue *dma_q = NULL;
#endif
int ret = 0;
dev = video_drvdata(file);
#ifdef USE_SEMA_QBUF
dma_q = &dev->dma_q;
complete(&dma_q->qbuf_comp);
#endif
if (detect_thread) {
dev->begin_time.tv_sec = 0;
dev->begin_time.tv_usec = 0;
up(&dev->thread_sem);
}
ret = videobuf_qbuf(&dev->vbuf_queue, p);
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s ret: %d line: %d\n", __func__, ret, __LINE__);
return ret;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vdetect_dev *dev = video_drvdata(file);
struct timeval time_begin;
struct timeval time_end;
int use_time;
int ret;
if (atomic_read(&dev->is_playing) == 0) {
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s has no vf source\n", __func__);
return -ENOMEM;
}
if (!aipq_enable) {
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s aipq function stop\n", __func__);
return -EAGAIN;
}
if (!dev->last_vf) {
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s: no need ai pq.\n", __func__);
return -EAGAIN;
}
if (!detect_thread) {
do_gettimeofday(&time_begin);
vdetect_fill_buffer(dev);
}
ret = videobuf_dqbuf(&dev->vbuf_queue, p, file->f_flags & O_NONBLOCK);
if (ret >= 0) {
mutex_lock(&dev->vf_mutex);
if (atomic_read(&dev->is_dev_reged) == 1) {
frame_number =
dev->last_vf->nn_value[AI_PQ_TOP - 1].maxclass;
vdetect_print(dev->inst, PRINT_MN_SET_FLOW,
"%d: get nn info from %d frame\n",
__LINE__, frame_number);
}
mutex_unlock(&dev->vf_mutex);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s ret: %d, index: %d\n",
__func__, ret, p->index);
} else {
if (detect_thread)
up(&dev->thread_sem);
vdetect_print(dev->inst, PRINT_CAP_OTHER,
"%s ret: %d\n", __func__, ret);
}
do_gettimeofday(&time_end);
if (!detect_thread) {
use_time = (time_end.tv_sec * 1000 * 1000 + time_end.tv_usec
- time_begin.tv_sec * 1000 * 1000
- time_begin.tv_usec) / 1000;
vdetect_print(dev->inst, PRINT_TIMESTAMP,
"%s block mode all cost time: %d ms line: %d\n",
__func__, use_time, __LINE__);
} else {
use_time = (time_end.tv_sec * 1000 * 1000 + time_end.tv_usec
- dev->begin_time.tv_sec * 1000 * 1000
- dev->begin_time.tv_usec) / 1000;
vdetect_print(dev->inst, PRINT_TIMESTAMP,
"%s thread mode all cost time: %d ms line: %d\n",
__func__, use_time, __LINE__);
}
return ret;
}
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
if (dev->set_format_flag) {
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
f->fmt.pix.field = dev->vbuf_queue.field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
} else {
f->fmt.pix.width = 0;
f->fmt.pix.height = 0;
}
return 0;
}
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vdetect_fmt *fmt = NULL;
struct vdetect_dev *dev = NULL;
enum v4l2_field field;
unsigned int maxw, maxh;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
fmt = get_format(f);
if (!fmt) {
vdetect_print(dev->inst, PRINT_ERROR,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
field = f->fmt.pix.field;
if (field == V4L2_FIELD_ANY) {
field = V4L2_FIELD_INTERLACED;
} else if (field != V4L2_FIELD_INTERLACED) {
vdetect_print(dev->inst, PRINT_ERROR,
"Field type invalid.\n");
return -EINVAL;
}
maxw = norm_maxw();
maxh = norm_maxh();
f->fmt.pix.field = field;
v4l_bound_align_image(&f->fmt.pix.width, 16, maxw, 2,
&f->fmt.pix.height, 16, maxh, 0, 0);
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
return 0;
}
/*FIXME: This seems to be generic enough to be at videodev2 */
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret = 0;
struct vdetect_dev *dev = NULL;
struct videobuf_queue *q = NULL;
dev = video_drvdata(file);
q = &dev->vbuf_queue;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
dev->dst_width = f->fmt.pix.width;
dev->dst_height = f->fmt.pix.height;
f->fmt.pix.width = (f->fmt.pix.width + 31) & (~31);
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420 ||
f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
f->fmt.pix.width =
(f->fmt.pix.width + 63) & (~63);
}
ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret < 0) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
return ret;
}
mutex_lock(&q->vb_lock);
if (videobuf_queue_is_busy(&dev->vbuf_queue)) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s queue busy\n", __func__);
ret = -EBUSY;
goto out;
}
dev->fmt = get_format(f);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
dev->vbuf_queue.field = f->fmt.pix.field;
dev->type = f->type;
dev->set_format_flag = true;
ret = 0;
out: mutex_unlock(&q->vb_lock);
return ret;
}
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *parms)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
return 0;
}
static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *parms)
{
struct vdetect_dev *dev = NULL;
int i, j;
struct nn_value_t last_saved_val[AI_PQ_TOP];
int buf_num, last_saved_num, behind_count;
dev = video_drvdata(file);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"not video capture.\n");
return 0;
}
vdetect_print(dev->inst, PRINT_MN_SET_FLOW,
"set nn info for %d frame\n",
frame_number);
if (nn_frame_num == 0)
buf_num = MAX_NN_INFO_BUF_COUNT - 1;
else
buf_num = nn_frame_num - 1;
last_saved_num = nn_info[buf_num].nn_frame_num;
if (last_saved_num != 0) {
memset(last_saved_val, 0, sizeof(last_saved_val));
for (i = 0; i < AI_PQ_TOP - 1; i++) {
last_saved_val[i].maxclass =
nn_info[buf_num].nn_value[i].maxclass;
last_saved_val[i].maxprob =
nn_info[buf_num].nn_value[i].maxprob;
}
behind_count = frame_number - last_saved_num;
for (i = 1; i < behind_count; i++) {
nn_info[nn_frame_num].nn_frame_num =
last_saved_num + i;
for (j = 0; j < AI_PQ_TOP - 1; j++) {
nn_info[nn_frame_num].nn_value[j].maxclass =
last_saved_val[j].maxclass;
nn_info[nn_frame_num].nn_value[j].maxprob =
last_saved_val[j].maxprob;
}
nn_info[nn_frame_num].nn_value[AI_PQ_TOP - 1].maxprob =
last_saved_num;
nn_frame_num++;
if (nn_frame_num == MAX_NN_INFO_BUF_COUNT)
nn_frame_num = 0;
}
}
nn_info[nn_frame_num].nn_frame_num = frame_number;
for (i = 0; i < AI_PQ_TOP - 1; i++) {
j = 4 * i;
nn_info[nn_frame_num].nn_value[i].maxclass =
parms->parm.raw_data[j];
nn_info[nn_frame_num].nn_value[i].maxprob =
parms->parm.raw_data[j + 1]
+ (parms->parm.raw_data[j + 2] << 8)
+ (parms->parm.raw_data[j + 3] << 16);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"top %d, vnn result: %s, probability: %d.\n",
i,
result_vnn[nn_info[nn_frame_num].nn_value[i].maxclass],
nn_info[nn_frame_num].nn_value[i].maxprob);
}
nn_info[nn_frame_num].nn_value[AI_PQ_TOP - 1].maxprob =
frame_number;
nn_frame_num++;
if (nn_frame_num == MAX_NN_INFO_BUF_COUNT)
nn_frame_num = 0;
return 0;
}
static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
return 0;
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line: %d\n", __func__, __LINE__);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
int ret;
struct vdetect_dev *dev = NULL;
struct vdetect_dmaqueue *dma_q = NULL;
dev = video_drvdata(file);
dma_q = &dev->dma_q;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s begin vdetect.%d\n", __func__, dev->inst);
if (dev->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || i != dev->type)
return -EINVAL;
ret = videobuf_streamon(&dev->vbuf_queue);
if (ret < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s ret:%d\n", __func__, ret);
return ret;
}
#ifdef USE_SEMA_QBUF
init_completion(&dma_q->qbuf_comp);
#endif
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line %d\n", __func__, __LINE__);
if (detect_thread) {
mutex_lock(&dev->vdetect_mutex);
if (dma_q->task_running) {
mutex_unlock(&dev->vdetect_mutex);
return 0;
}
dma_q->task_running = 1;
dma_q->kthread = kthread_run(vdetect_thread,
dev, dev->recv_name);
if (IS_ERR(dma_q->kthread)) {
pr_err("kernel_thread() failed\n");
dma_q->task_running = 0;
dma_q->kthread = NULL;
mutex_unlock(&dev->vdetect_mutex);
vdetect_print(dev->inst, PRINT_ERROR,
"start thread error.....\n");
return PTR_ERR(dma_q->kthread);
}
mutex_unlock(&dev->vdetect_mutex);
dev->begin_time.tv_sec = 0;
dev->begin_time.tv_usec = 0;
up(&dev->thread_sem);
vdetect_print(dev->inst, PRINT_THREADINFO,
"%s thread start successful!\n", __func__);
}
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s end vdetect.%d\n", __func__, dev->inst);
return 0;
}
static int vidioc_streamoff(struct file *file,
void *priv, enum v4l2_buf_type i)
{
int ret;
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s begin line %d\n", __func__, __LINE__);
if (dev->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || i != dev->type)
return -EINVAL;
ret = videobuf_streamoff(&dev->vbuf_queue);
if (ret < 0) {
vdetect_print(dev->inst, PRINT_ERROR, "%s fail\n", __func__);
return ret;
}
if (IS_ERR_OR_NULL(&dev->dma_q)) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s dma_q is NULL\n", __func__);
return -1;
}
if (detect_thread) {
vdetect_print(dev->inst, PRINT_THREADINFO,
"begin to stop %s thread\n", dev->recv_name);
mutex_lock(&dev->vdetect_mutex);
/* shutdown control thread */
if (!IS_ERR_OR_NULL(dev->dma_q.kthread)) {
up(&dev->thread_sem);
dev->dma_q.task_running = 0;
send_sig(SIGTERM, dev->dma_q.kthread, 1);
#ifdef USE_SEMA_QBUF
complete(&dev->dma_q.qbuf_comp);
#endif
wake_up_interruptible(&dev->dma_q.wq);
vdetect_print(dev->inst, PRINT_THREADINFO,
"ready to stop %s thread\n",
dev->recv_name);
ret = kthread_stop(dev->dma_q.kthread);
if (ret < 0)
vdetect_print(dev->inst, PRINT_ERROR,
"%s, ret = %d .\n",
__func__, ret);
dev->dma_q.kthread = NULL;
}
mutex_unlock(&dev->vdetect_mutex);
vdetect_print(dev->inst, PRINT_THREADINFO,
"finish stop %s thread\n", dev->recv_name);
}
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s end line %d\n", __func__, __LINE__);
return 0;
}
static const struct v4l2_ioctl_ops vdetect_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
};
static int vidioc_open(struct file *file)
{
struct vdetect_dev *dev = NULL;
struct videobuf_res_privdata *res = NULL;
int ret;
dev = video_drvdata(file);
mutex_lock(&dev->vdetect_mutex);
if (dev->fd_num > 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s line %d\n", __func__, __LINE__);
mutex_unlock(&dev->vdetect_mutex);
return -EBUSY;
}
dev->fd_num++;
ret = vdetect_cma_buf_init(dev);
if (ret < 0) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s alloc cma buffer fail\n", __func__);
dev->fd_num--;
mutex_unlock(&dev->vdetect_mutex);
return -ENOMEM;
}
dev->context = create_ge2d_work_queue();
if (!dev->context) {
vdetect_print(dev->inst, PRINT_ERROR,
"%s create ge2d fail\n", __func__);
dev->fd_num--;
mutex_unlock(&dev->vdetect_mutex);
return -ENOMEM;
}
if (detect_thread)
sema_init(&dev->thread_sem, 1);
dev->res.start = dev->buffer_start;
dev->res.end = dev->buffer_start + dev->buffer_size;
mutex_unlock(&dev->vdetect_mutex);
file->private_data = dev;
dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
atomic_set(&dev->vdect_status, VDETECT_INIT);
dev->fmt = &formats[0];
dev->width = 224;
dev->height = 224;
dev->set_format_flag = false;
dev->f_flags = file->f_flags;
dev->dma_q.kthread = NULL;
dev->ge2d_vf = NULL;
dev->res.priv = (void *)dev;
res = &dev->res;
videobuf_queue_res_init(&dev->vbuf_queue, &vdetect_qops, NULL,
&dev->slock, dev->type, V4L2_FIELD_INTERLACED,
sizeof(struct vdetect_buffer), (void *)res,
NULL);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s vdetect.%d\n", __func__, dev->inst);
return 0;
}
static ssize_t vidioc_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
struct vdetect_dev *dev = NULL;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line %d\n", __func__, __LINE__);
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
return videobuf_read_stream(&dev->vbuf_queue,
data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
}
return 0;
}
static unsigned int vidioc_poll(struct file *file,
struct poll_table_struct *wait)
{
struct vdetect_dev *dev = NULL;
struct videobuf_queue *q = &dev->vbuf_queue;
dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s line %d\n", __func__, __LINE__);
if (dev->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return POLLERR;
return videobuf_poll_stream(file, q, wait);
}
static int vidioc_close(struct file *file)
{
struct vdetect_dev *dev = NULL;
struct vdetect_dmaqueue *dma_q = NULL;
dev = video_drvdata(file);
dma_q = &dev->dma_q;
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"%s vdetect.%d\n", __func__, dev->inst);
atomic_set(&dev->vdect_status, VDETECT_INIT);
if (dma_q->kthread)
vidioc_streamoff(file, (void *)dev, dev->type);
videobuf_stop(&dev->vbuf_queue);
videobuf_mmap_free(&dev->vbuf_queue);
mutex_lock(&dev->vdetect_mutex);
if (dev->context)
destroy_ge2d_work_queue(dev->context);
vdetect_cma_buf_uninit(dev);
dev->fd_num--;
mutex_unlock(&dev->vdetect_mutex);
return 0;
}
static int vdetect_mmap(struct file *file, struct vm_area_struct *vma)
{
int ret = 0;
struct vdetect_dev *dev = video_drvdata(file);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"mmap called, vma=0x%08lx\n", (unsigned long)vma);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = videobuf_mmap_mapper(&dev->vbuf_queue, vma);
vdetect_print(dev->inst, PRINT_CAPTUREINFO,
"vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end
- (unsigned long)vma->vm_start,
ret);
return ret;
}
static const struct v4l2_file_operations vdetect_v4l2_fops = {
.owner = THIS_MODULE,
.open = vidioc_open,
.release = vidioc_close,
.read = vidioc_read,
.poll = vidioc_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vdetect_mmap,
};
static struct video_device vdetect_template = {
.name = "vdetect",
.fops = &vdetect_v4l2_fops,
.ioctl_ops = &vdetect_ioctl_ops,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
};
static LIST_HEAD(vdetect_devlist);
int vdetect_assign_map(char **receiver_name, int *inst)
{
unsigned long flags;
struct vdetect_dev *dev = NULL;
struct list_head *p;
flags = vdetect_devlist_lock();
list_for_each(p, &vdetect_devlist) {
dev = list_entry(p, struct vdetect_dev, vdetect_devlist);
if (dev->inst == *inst) {
*receiver_name = dev->recv_name;
vdetect_devlist_unlock(flags);
return 0;
}
}
vdetect_devlist_unlock(flags);
return -ENODEV;
}
EXPORT_SYMBOL(vdetect_assign_map);
int vdetect_get_frame_nn_info(struct vframe_s *vframe)
{
int ret = -ENODEV;
int i, j, work_frame_num, check_flag;
if (IS_ERR_OR_NULL(vframe)) {
vdetect_print(0, PRINT_MN_SET_FLOW,
"%s: NULL params.\n",
__func__);
return ret;
}
if (!(vframe->flag & VFRAME_FLAG_THROUGH_VDETECT)) {
vframe->ai_pq_enable = false;
vdetect_print(0, PRINT_MN_SET_FLOW,
"%s: no need ai-pq.\n",
__func__);
return 0;
}
work_frame_num = vframe->nn_value[AI_PQ_TOP - 1].maxclass;
for (i = 0; i < MAX_NN_INFO_BUF_COUNT; i++) {
if (work_frame_num == nn_info[i].nn_frame_num) {
vdetect_print(0, PRINT_MN_SET_FLOW,
"buf[%d] save nn for %d frame.\n",
i, work_frame_num);
for (j = 0; j < AI_PQ_TOP - 1; j++) {
vframe->nn_value[j].maxclass =
nn_info[i].nn_value[j].maxclass;
vframe->nn_value[j].maxprob =
nn_info[i].nn_value[j].maxprob;
}
check_flag = nn_info[i].nn_value[AI_PQ_TOP - 1].maxprob;
if (check_flag != nn_info[i].nn_frame_num) {
vdetect_print(0, PRINT_MN_SET_FLOW,
"%d frame no nn param.\n",
work_frame_num);
}
ret = 0;
break;
}
}
if (aipq_enable)
vframe->ai_pq_enable = true;
else
vframe->ai_pq_enable = false;
if (i == MAX_NN_INFO_BUF_COUNT) {
vdetect_print(0, PRINT_MN_SET_FLOW,
"don't save %d frame nn info.\n",
work_frame_num);
}
return ret;
}
EXPORT_SYMBOL(vdetect_get_frame_nn_info);
static int vdetect_alloc_map(int *inst)
{
unsigned long flags;
struct vdetect_dev *dev = NULL;
struct list_head *p;
flags = vdetect_devlist_lock();
list_for_each(p, &vdetect_devlist) {
dev = list_entry(p, struct vdetect_dev, vdetect_devlist);
if (dev->inst >= 0 && !dev->mapped) {
dev->mapped = true;
*inst = dev->inst;
vdetect_devlist_unlock(flags);
return 0;
}
}
vdetect_devlist_unlock(flags);
return -ENODEV;
}
static void vdetect_release_map(int inst)
{
unsigned long flags;
struct vdetect_dev *dev = NULL;
struct list_head *p;
flags = vdetect_devlist_lock();
list_for_each(p, &vdetect_devlist) {
dev = list_entry(p, struct vdetect_dev, vdetect_devlist);
if (dev->inst == inst && dev->mapped) {
dev->mapped = false;
pr_debug("%s %d OK\n", __func__, dev->inst);
break;
}
}
vdetect_devlist_unlock(flags);
}
static int vdetect_open(struct inode *inode, struct file *file)
{
int *vdetect_id = kzalloc(sizeof(int), GFP_KERNEL);
if (vdetect_id) {
*vdetect_id = -1;
file->private_data = vdetect_id;
}
return 0;
}
static int vdetect_release(struct inode *inode, struct file *file)
{
int *vdetect_id = file->private_data;
if (!IS_ERR_OR_NULL(vdetect_id) && (*vdetect_id) != -1)
vdetect_release_map(*vdetect_id);
if (vdetect_id)
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
static long vdetect_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
long ret = 0;
void __user *argp = (void __user *)arg;
switch (cmd) {
case VDETECT_IOCTL_ALLOC_ID:{
u32 vdetect_id = 0;
int *a = (int *)file->private_data;
ret = vdetect_alloc_map(&vdetect_id);
if (a)
*a = vdetect_id;
if (ret != 0)
break;
put_user(vdetect_id, (u32 __user *)argp);
}
break;
case VDETECT_IOCTL_FREE_ID:{
u32 vdetect_id;
int *a = (int *)file->private_data;
get_user(vdetect_id, (u32 __user *)argp);
if (a)
*a = -1;
vdetect_release_map(vdetect_id);
}
break;
default:
return -EINVAL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long vdetect_compat_ioctl(struct file *file,
unsigned int cmd, ulong arg)
{
long ret = 0;
ret = vdetect_ioctl(file, cmd, (ulong)compat_ptr(arg));
return ret;
}
#endif
static const struct file_operations vdetect_fops = {
.owner = THIS_MODULE,
.open = vdetect_open,
.release = vdetect_release,
.unlocked_ioctl = vdetect_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vdetect_compat_ioctl,
#endif
.poll = NULL,
};
static ssize_t vdetect_debug_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%-18s=%d\n%-18s=%d\n%-18s=%d\n%-18s=%d\n",
"detect_debug",
detect_debug,
"post_get_count",
post_get_count,
"post_put_count",
post_put_count,
"pre_put_count",
pre_put_count);
}
static ssize_t vdetect_debug_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
if (val > 0)
detect_debug = val;
else
detect_debug = 0;
pr_info("set detect_debug val:%d\n", detect_debug);
return count;
}
static ssize_t vdetect_dump_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "vdetect debug: %d\n", detect_dump);
}
static ssize_t vdetect_dump_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
if (val > 0)
detect_dump = val;
else
detect_dump = 0;
pr_info("set detect_debug val:%d\n", detect_dump);
return count;
}
static ssize_t vdetect_use_thread_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "vdetect use thread: %d\n", detect_thread);
}
static ssize_t vdetect_use_thread_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
if (val > 0)
detect_thread = val;
else
detect_thread = 0;
pr_info("set vdetect use thread val:%d\n", detect_thread);
return count;
}
static ssize_t tv_add_vdetect_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", tv_add_vdetect);
}
static ssize_t tv_add_vdetect_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
if (val > 0)
tv_add_vdetect = val;
else
tv_add_vdetect = 0;
pr_info("set tv_add_vdetect:%d\n", tv_add_vdetect);
return count;
}
static ssize_t aipq_enable_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", aipq_enable);
}
static ssize_t aipq_enable_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
if (val > 0)
aipq_enable = 1;
else
aipq_enable = 0;
return count;
}
static CLASS_ATTR_RW(vdetect_debug);
static CLASS_ATTR_RW(vdetect_dump);
static CLASS_ATTR_RW(vdetect_use_thread);
static CLASS_ATTR_RW(tv_add_vdetect);
static CLASS_ATTR_RW(aipq_enable);
static struct attribute *vdetect_class_attrs[] = {
&class_attr_vdetect_debug.attr,
&class_attr_vdetect_dump.attr,
&class_attr_vdetect_use_thread.attr,
&class_attr_tv_add_vdetect.attr,
&class_attr_aipq_enable.attr,
NULL
};
ATTRIBUTE_GROUPS(vdetect_class);
static struct class vdetect_class = {
.name = "vdetect",
.class_groups = vdetect_class_groups,
};
static int vdetect_create_instance(int inst)
{
struct vdetect_dev *dev = NULL;
struct video_device *vfd;
int ret;
unsigned long flags;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
vdetect_print(inst, PRINT_ERROR,
"%s vdetect.%d device alloc failed!\n",
__func__, inst);
return -ENOMEM;
}
dev->res.magic = MAGIC_RE_MEM;
dev->res.priv = NULL;
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VDETECT_MODULE_NAME, inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
if (ret) {
vdetect_print(inst, PRINT_ERROR,
"v4l2 device register fail!\n");
goto free_dev;
}
pr_info("vdetect.%d v4l2_dev.name=:%s\n", inst, dev->v4l2_dev.name);
dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
dev->fd_num = 0;
atomic_set(&dev->is_playing, 0);
dev->inst = inst;
atomic_set(&dev->vdect_status, VDETECT_INIT);
/* init video dma queues */
INIT_LIST_HEAD(&dev->dma_q.active);
init_waitqueue_head(&dev->dma_q.wq);
/* initialize locks */
spin_lock_init(&dev->slock);
mutex_init(&dev->vdetect_mutex);
mutex_init(&dev->vf_mutex);
vfd = video_device_alloc();
if (!vfd)
goto unreg_dev;
*vfd = vdetect_template;
vfd->dev_debug = detect_debug;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
| V4L2_CAP_READWRITE;
/*
* Provide a mutex to v4l2 core. It will be used to protect
* all fops and v4l2 ioctls.
*/
ret = video_register_device(vfd, VFL_TYPE_GRABBER,
dev->inst + vdetect_base);
if (ret < 0)
goto unreg_dev;
video_set_drvdata(vfd, dev);
dev->vdev = vfd;
dev->src_canvas[0] = -1;
dev->src_canvas[1] = -1;
dev->src_canvas[2] = -1;
dev->dst_canvas = -1;
dev->context = NULL;
dev->last_vf = NULL;
dev->ge2d_vf = NULL;
snprintf(dev->recv_name, RECEIVER_NAME_SIZE,
RECEIVER_NAME ".%x", inst & 0xff);
vf_receiver_init(&dev->recv, dev->recv_name, &vdetect_vf_recv, dev);
vf_reg_receiver(&dev->recv);
snprintf(dev->prov_name, RECEIVER_NAME_SIZE,
PROVIDER_NAME ".%x", inst & 0xff);
vf_provider_init(&dev->prov, dev->prov_name, &vdetect_vf_prov, dev);
pr_info("vdetect.%d v4l2 device registered as %s\n",
inst, video_device_node_name(vfd));
/* add to device list */
flags = vdetect_devlist_lock();
list_add_tail(&dev->vdetect_devlist, &vdetect_devlist);
vdetect_devlist_unlock(flags);
return 0;
unreg_dev:
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
return ret;
}
static int vdetect_v4l2_release(void)
{
struct vdetect_dev *dev;
struct list_head *list;
unsigned long flags;
int ret;
flags = vdetect_devlist_lock();
while (!list_empty(&vdetect_devlist)) {
list = vdetect_devlist.next;
list_del(list);
vdetect_devlist_unlock(flags);
dev = list_entry(list, struct vdetect_dev, vdetect_devlist);
if (dev->context)
destroy_ge2d_work_queue(dev->context);
vf_unreg_receiver(&dev->recv);
ret = free_canvas(dev);
if (ret < 0)
pr_info("vdetect free canvas failed!\n");
pr_info("vdetect unregistering %s\n",
video_device_node_name(dev->vdev));
video_unregister_device(dev->vdev);
video_device_release(dev->vdev);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
flags = vdetect_devlist_lock();
}
vdetect_devlist_unlock(flags);
return 0;
}
static int vdetect_drv_probe(struct platform_device *pdev)
{
int ret = -1, i;
struct device *devp;
pr_info("%s\n", __func__);
ret = class_register(&vdetect_class);
if (ret < 0)
return ret;
ret = register_chrdev(VDETECT_MAJOR, "vdetect", &vdetect_fops);
if (ret < 0) {
pr_err("Can't allocate major for vdetect device\n");
goto error1;
}
devp = device_create(&vdetect_class, NULL,
MKDEV(VDETECT_MAJOR, 0), NULL,
VDETECT_MODULE_NAME);
if (IS_ERR(devp)) {
pr_err("failed to create vdetect device node\n");
ret = PTR_ERR(devp);
return ret;
}
for (i = 0; i < MAX_INST; i++) {
ret = vdetect_create_instance(i);
if (ret) {
/* If some instantiations succeeded, keep driver */
if (i)
ret = 0;
break;
}
}
if (ret < 0) {
pr_err("vdetect: error %d while loading driver\n", ret);
goto error1;
}
return 0;
error1:
unregister_chrdev(VDETECT_MAJOR, "vdetect");
class_unregister(&vdetect_class);
return ret;
}
static int vdetect_drv_remove(struct platform_device *pdev)
{
pr_info("%s\n", __func__);
vdetect_v4l2_release();
device_destroy(&vdetect_class, MKDEV(VDETECT_MAJOR, 0));
unregister_chrdev(VDETECT_MAJOR, VDETECT_MODULE_NAME);
class_unregister(&vdetect_class);
return 0;
}
static const struct of_device_id vdetect_dt_match[] = {
{
.compatible = "amlogic, vdetect",
},
{}
};
/* general interface for a linux driver .*/
static struct platform_driver vdetect_drv = {
.probe = vdetect_drv_probe,
.remove = vdetect_drv_remove,
.driver = {
.name = "vdetect",
.owner = THIS_MODULE,
.of_match_table = vdetect_dt_match,
}
};
int __init vdetect_init(void)
{
pr_info("%s\n", __func__);
if (platform_driver_register(&vdetect_drv)) {
pr_err("Failed to register vdetect driver\n");
return -ENODEV;
}
return 0;
}
void __exit vdetect_exit(void)
{
pr_info("%s\n", __func__);
platform_driver_unregister(&vdetect_drv);
}