blob: 42fddc90d081af45b7f9107b66f3e42b8ef34964 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/video_processor/v4lvideo/v4lvideo.c
*
* 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.
*
*/
#undef DEBUG
#define DEBUG
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
/* media module used media/registers/cpu_version.h since kernel 5.4 */
#include <linux/amlogic/media/registers/cpu_version.h>
#include "v4lvideo.h"
#include <media/videobuf2-core.h>
#include <media/videobuf2-v4l2.h>
#include <linux/platform_device.h>
#include <linux/amlogic/major.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/codec_mm_keeper.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/video_sink/video_keeper.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/vfm/amlogic_fbc_hook_v1.h>
#include <linux/amlogic/media/di/di.h>
#include "../../common/vfm/vfm.h"
#include <linux/amlogic/media/utils/am_com.h>
#include <linux/amlogic/media/amdolbyvision/dolby_vision.h>
#include <linux/amlogic/meson_uvm_core.h>
#define V4LVIDEO_MODULE_NAME "v4lvideo"
#define V4LVIDEO_VERSION "1.0"
#define RECEIVER_NAME "v4lvideo"
#define V4LVIDEO_DEVICE_NAME "v4lvideo"
//#define DUR2PTS(x) ((x) - ((x) >> 4))
static atomic_t global_set_cnt = ATOMIC_INIT(0);
static u32 alloc_sei = 1;
#define V4L2_CID_USER_AMLOGIC_V4LVIDEO_BASE (V4L2_CID_USER_BASE + 0x1100)
static unsigned int video_nr_base = 30;
static unsigned int n_devs = 9;
#define N_DEVS 9
static unsigned int debug;
static unsigned int get_count[N_DEVS];
static unsigned int put_count[N_DEVS];
static unsigned int q_count[N_DEVS];
static unsigned int dq_count[N_DEVS];
static unsigned int cts_use_di;
/*set 1 means video_composer use dec vf when di NR; only debug!!!*/
static unsigned int render_use_dec;
static unsigned int dec_count;
static unsigned int vf_dump;
static unsigned int open_fd_count;
static unsigned int release_fd_count;
static unsigned int link_fd_count;
static unsigned int link_put_fd_count;
static unsigned int v4lvideo_version = 2;
static unsigned int total_get_count[N_DEVS];
static unsigned int total_put_count[N_DEVS];
static unsigned int total_release_count[N_DEVS];
static unsigned int inactive_check_disp;
bool di_bypass_p;
/*dec set count mutex*/
struct mutex mutex_dec_count;
#define MAX_KEEP_FRAME 64
#define DI_NR_COUNT 1
struct keep_mem_info {
void *handle;
int keep_id;
int count;
};
struct keeper_mgr {
/* lock*/
spinlock_t lock;
struct keep_mem_info keep_list[MAX_KEEP_FRAME];
};
static struct keeper_mgr keeper_mgr_private;
static const struct file_operations v4lvideo_file_fops;
static u32 print_flag;
static u32 cts_video_flag;
const struct video_info_t cts_vp9_videos[] = {
{
.width = 426,
.height = 240,
},
{
.width = 2560,
.height = 1090,
},
{
.width = 1216,
.height = 2160,
},
};
const s32 num_vp9_videos = ARRAY_SIZE(cts_vp9_videos);
#define PRINT_ERROR 0X0
#define PRINT_QUEUE_STATUS 0X0001
#define PRINT_COUNT 0X0002
#define PRINT_OTHER 0X0040
int v4l_print(int index, int debug_flag, const char *fmt, ...)
{
if ((print_flag & debug_flag) ||
debug_flag == PRINT_ERROR) {
unsigned char buf[256];
int len = 0;
va_list args;
va_start(args, fmt);
len = sprintf(buf, "v4lvideo:[%d]", index);
vsnprintf(buf + len, 256 - len, fmt, args);
pr_info("%s", buf);
va_end(args);
}
return 0;
}
void v4lvideo_dec_count_increase(void)
{
mutex_lock(&mutex_dec_count);
dec_count++;
if (dec_count > DI_NR_COUNT && !di_bypass_p) {
dim_polic_cfg(K_DIM_BYPASS_ALL_P, 1);
di_bypass_p = true;
pr_info("v4lvideo: di_p bypass 1\n");
}
if (dec_count > 9)
pr_err("v4lvideo: dec count >9\n");
mutex_unlock(&mutex_dec_count);
}
EXPORT_SYMBOL(v4lvideo_dec_count_increase);
void v4lvideo_dec_count_decrease(void)
{
mutex_lock(&mutex_dec_count);
if (dec_count == 0)
pr_err("v4lvideo: dec count -1\n");
dec_count--;
if (dec_count <= DI_NR_COUNT && di_bypass_p) {
dim_polic_cfg(K_DIM_BYPASS_ALL_P, 0);
di_bypass_p = false;
pr_info("v4lvideo: di_p bypass 0\n");
}
mutex_unlock(&mutex_dec_count);
}
EXPORT_SYMBOL(v4lvideo_dec_count_decrease);
static struct keeper_mgr *get_keeper_mgr(void)
{
return &keeper_mgr_private;
}
static inline u64 dur2pts(u32 duration)
{
u32 ret;
ret = duration - (duration >> 4);
return ret;
}
void keeper_mgr_init(void)
{
struct keeper_mgr *mgr = get_keeper_mgr();
memset(mgr, 0, sizeof(struct keeper_mgr));
spin_lock_init(&mgr->lock);
}
static const struct v4lvideo_fmt formats[] = {
{.name = "RGB32 (LE)",
.fourcc = V4L2_PIX_FMT_RGB32, /* argb */
.depth = 32, },
{.name = "RGB565 (LE)",
.fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
.depth = 16, },
{.name = "RGB24 (LE)",
.fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
.depth = 24, },
{.name = "RGB24 (BE)",
.fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
.depth = 24, },
{.name = "12 Y/CbCr 4:2:0",
.fourcc = V4L2_PIX_FMT_NV12,
.depth = 12, },
{.name = "12 Y/CrCb 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 const struct v4lvideo_fmt *__get_format(u32 pixelformat)
{
const struct v4lvideo_fmt *fmt;
unsigned int k;
for (k = 0; k < ARRAY_SIZE(formats); k++) {
fmt = &formats[k];
if (fmt->fourcc == pixelformat)
break;
}
if (k == ARRAY_SIZE(formats))
return NULL;
return &formats[k];
}
static const struct v4lvideo_fmt *get_format(struct v4l2_format *f)
{
return __get_format(f->fmt.pix.pixelformat);
}
static void video_keeper_keep_mem(void *mem_handle, int type, int *id)
{
int ret;
int old_id = *id;
struct keeper_mgr *mgr = get_keeper_mgr();
int i;
unsigned long flags;
int have_samed = 0;
int keep_id = -1;
int slot_i = -1;
if (!mem_handle)
return;
spin_lock_irqsave(&mgr->lock, flags);
for (i = 0; i < MAX_KEEP_FRAME; i++) {
if (!mgr->keep_list[i].handle && slot_i < 0) {
slot_i = i;
} else if (mem_handle == mgr->keep_list[i].handle) {
mgr->keep_list[i].count++;
have_samed = true;
keep_id = mgr->keep_list[i].keep_id;
break;
}
}
if (have_samed) {
spin_unlock_irqrestore(&mgr->lock, flags);
*id = keep_id;
/* pr_info("v4lvideo: keep same mem handle=%p, keep_id=%d\n", */
/* mem_handle, keep_id); */
return;
}
mgr->keep_list[slot_i].count++;
if (mgr->keep_list[slot_i].count != 1)
pr_err("v4lvideo: keep error mem handle=%p\n", mem_handle);
mgr->keep_list[slot_i].handle = mem_handle;
spin_unlock_irqrestore(&mgr->lock, flags);
ret = codec_mm_keeper_mask_keep_mem(mem_handle,
type);
if (ret > 0) {
if (old_id > 0 && ret != old_id) {
/* wait 80 ms for vsync post. */
codec_mm_keeper_unmask_keeper(old_id, 0);
}
*id = ret;
}
mgr->keep_list[slot_i].keep_id = *id;
}
static void video_keeper_free_mem(int keep_id, int delayms)
{
struct keeper_mgr *mgr = get_keeper_mgr();
int i;
unsigned long flags;
int need_continue_keep = 0;
if (keep_id <= 0) {
pr_err("invalid keepid %d\n", keep_id);
return;
}
spin_lock_irqsave(&mgr->lock, flags);
for (i = 0; i < MAX_KEEP_FRAME; i++) {
if (keep_id == mgr->keep_list[i].keep_id) {
mgr->keep_list[i].count--;
if (mgr->keep_list[i].count > 0) {
need_continue_keep = 1;
break;
} else if (mgr->keep_list[i].count == 0) {
mgr->keep_list[i].keep_id = -1;
mgr->keep_list[i].handle = NULL;
} else {
pr_err("v4lvideo: free mem err count =%d\n",
mgr->keep_list[i].count);
}
}
}
spin_unlock_irqrestore(&mgr->lock, flags);
if (need_continue_keep) {
pr_info("v4lvideo: need_continue_keep keep_id=%d\n", keep_id);
return;
}
codec_mm_keeper_unmask_keeper(keep_id, delayms);
}
static void vf_keep(struct v4lvideo_dev *dev,
struct v4lvideo_file_s *v4lvideo_file,
struct file_private_data *file_private_data)
{
struct vframe_s *vf_p;
struct vframe_s *vf_ext_p = NULL;
int type = MEM_TYPE_CODEC_MM;
int keep_id = 0;
int keep_head_id = 0;
u32 flag;
u32 inst_id = dev->inst;
if (!file_private_data) {
V4LVID_ERR("%s error: file_private_data is NULL", __func__);
return;
}
vf_p = file_private_data->vf_p;
if (!vf_p || vf_p != v4lvideo_file->vf_p) {
v4l_print(dev->inst, PRINT_ERROR,
"maybe file has been released\n");
mutex_lock(&dev->mutex_opened);
if (dev->opened) {
mutex_unlock(&dev->mutex_opened);
v4lvideo_file->free_before_unreg = true;
} else {
mutex_unlock(&dev->mutex_opened);
v4l_print(dev->inst, PRINT_ERROR,
"vf keep: device has beed closed\n");
flag = v4lvideo_file->flag;
if (flag & V4LVIDEO_FLAG_DI_DEC)
v4l_print(dev->inst, PRINT_ERROR,
"vf keep shold not here");
if (v4lvideo_file->vf_type & VIDTYPE_DI_PW) {
vf_p = v4lvideo_file->vf_p;
dim_post_keep_cmd_release2(vf_p);
total_release_count[inst_id]++;
}
}
return;
}
if (file_private_data->flag & V4LVIDEO_FLAG_DI_NR) {
vf_ext_p = file_private_data->vf_ext_p;
if (!vf_ext_p) {
V4LVID_ERR("%s error: vf_ext is NULL", __func__);
return;
}
vf_p = vf_ext_p;
}
if (vf_p->type & VIDTYPE_SCATTER)
type = MEM_TYPE_CODEC_MM_SCATTER;
video_keeper_keep_mem(vf_p->mem_handle, type, &keep_id);
video_keeper_keep_mem(vf_p->mem_head_handle, MEM_TYPE_CODEC_MM,
&keep_head_id);
file_private_data->keep_id = keep_id;
file_private_data->keep_head_id = keep_head_id;
file_private_data->is_keep = true;
}
void v4lvideo_keep_vf(struct file *file)
{
struct vframe_s *vf_p;
struct vframe_s *vf_ext_p = NULL;
int type = MEM_TYPE_CODEC_MM;
int keep_id = 0;
int keep_head_id = 0;
struct file_private_data *file_private_data;
file_private_data = v4lvideo_get_file_private_data(file, false);
if (!file_private_data) {
V4LVID_ERR("vf_keep error: file_private_data is NULL");
return;
}
file_private_data->vf.flag |= VFRAME_FLAG_KEEPED;
file_private_data->vf_ext.flag |= VFRAME_FLAG_KEEPED;
vf_p = file_private_data->vf_p;
if (file_private_data->flag & V4LVIDEO_FLAG_DI_NR) {
vf_ext_p = file_private_data->vf_ext_p;
if (!vf_ext_p) {
V4LVID_ERR("file_vf_keep error: vf_ext is NULL");
return;
}
vf_p = vf_ext_p;
}
if (vf_p->type & VIDTYPE_SCATTER)
type = MEM_TYPE_CODEC_MM_SCATTER;
video_keeper_keep_mem(vf_p->mem_handle, type, &keep_id);
video_keeper_keep_mem(vf_p->mem_head_handle,
MEM_TYPE_CODEC_MM, &keep_head_id);
file_private_data->keep_id = keep_id;
file_private_data->keep_head_id = keep_head_id;
file_private_data->is_keep = true;
}
static void vf_free(struct file_private_data *file_private_data)
{
struct vframe_s *vf_p;
struct vframe_s *vf;
u32 flag;
u32 inst_id;
inst_id = file_private_data->v4l_inst_id;
if (file_private_data->keep_id > 0) {
video_keeper_free_mem(file_private_data->keep_id, 0);
file_private_data->keep_id = -1;
}
if (file_private_data->keep_head_id > 0) {
video_keeper_free_mem(file_private_data->keep_head_id, 0);
file_private_data->keep_head_id = -1;
}
vf = &file_private_data->vf;
vf_p = file_private_data->vf_p;
flag = file_private_data->flag;
if (flag & V4LVIDEO_FLAG_DI_DEC) {
vf = &file_private_data->vf_ext;
vf_p = file_private_data->vf_ext_p;
}
if (vf->type & VIDTYPE_DI_PW) {
v4l_print(inst_id, PRINT_OTHER,
"free: omx_index=%d\n", vf_p->omx_index);
dim_post_keep_cmd_release2(vf_p);
total_release_count[inst_id]++;
v4l_print(inst_id, PRINT_COUNT,
"di release1 get=%d, put=%d, release=%d\n",
total_get_count[inst_id], total_put_count[inst_id],
total_release_count[inst_id]);
}
}
static void vf_free_force(struct v4lvideo_file_s *v4lvideo_file)
{
struct vframe_s *vf_p;
u32 flag;
u32 inst_id;
inst_id = v4lvideo_file->inst_id;
vf_p = v4lvideo_file->vf_p;
flag = v4lvideo_file->flag;
if (flag & V4LVIDEO_FLAG_DI_DEC)
vf_p = v4lvideo_file->vf_ext_p;
if (v4lvideo_file->vf_type & VIDTYPE_DI_PW) {
dim_post_keep_cmd_release2(vf_p);
total_release_count[inst_id]++;
v4l_print(inst_id, PRINT_COUNT,
"di release2 get=%d, put=%d, release=%d\n",
total_get_count[inst_id], total_put_count[inst_id],
total_release_count[inst_id]);
}
}
void init_file_private_data(struct file_private_data *file_private_data)
{
if (file_private_data) {
memset(&file_private_data->vf, 0, sizeof(struct vframe_s));
memset(&file_private_data->vf_ext, 0, sizeof(struct vframe_s));
file_private_data->vf_p = NULL;
file_private_data->vf_ext_p = NULL;
file_private_data->is_keep = false;
file_private_data->keep_id = -1;
file_private_data->keep_head_id = -1;
file_private_data->file = NULL;
file_private_data->flag = 0;
file_private_data->cnt_file = NULL;
} else {
V4LVID_ERR("%s is NULL!!", __func__);
}
}
static DEFINE_SPINLOCK(devlist_lock);
static unsigned long v4lvideo_devlist_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&devlist_lock, flags);
return flags;
}
static void v4lvideo_devlist_unlock(unsigned long flags)
{
spin_unlock_irqrestore(&devlist_lock, flags);
}
static LIST_HEAD(v4lvideo_devlist);
int v4lvideo_assign_map(char **receiver_name, int *inst)
{
unsigned long flags;
struct v4lvideo_dev *dev = NULL;
struct list_head *p;
flags = v4lvideo_devlist_lock();
list_for_each(p, &v4lvideo_devlist) {
dev = list_entry(p, struct v4lvideo_dev, v4lvideo_devlist);
if (dev->inst == *inst) {
*receiver_name = dev->vf_receiver_name;
v4lvideo_devlist_unlock(flags);
return 0;
}
}
v4lvideo_devlist_unlock(flags);
return -ENODEV;
}
EXPORT_SYMBOL(v4lvideo_assign_map);
int v4lvideo_alloc_map(int *inst)
{
unsigned long flags;
struct v4lvideo_dev *dev = NULL;
struct list_head *p;
flags = v4lvideo_devlist_lock();
list_for_each(p, &v4lvideo_devlist) {
dev = list_entry(p, struct v4lvideo_dev, v4lvideo_devlist);
if (dev->inst >= 0 && !dev->mapped) {
dev->mapped = true;
*inst = dev->inst;
v4lvideo_devlist_unlock(flags);
return 0;
}
}
v4lvideo_devlist_unlock(flags);
return -ENODEV;
}
void v4lvideo_release_map(int inst)
{
unsigned long flags;
struct v4lvideo_dev *dev = NULL;
struct list_head *p;
flags = v4lvideo_devlist_lock();
list_for_each(p, &v4lvideo_devlist) {
dev = list_entry(p, struct v4lvideo_dev, v4lvideo_devlist);
if (dev->inst == inst && dev->mapped) {
dev->mapped = false;
pr_debug("%s %d OK\n", __func__, dev->inst);
break;
}
}
v4lvideo_devlist_unlock(flags);
}
void v4lvideo_release_map_force(struct v4lvideo_dev *dev)
{
unsigned long flags;
flags = v4lvideo_devlist_lock();
if (dev->mapped) {
dev->mapped = false;
pr_debug("%s %d OK\n", __func__, dev->inst);
}
v4lvideo_devlist_unlock(flags);
}
static struct ge2d_context_s *context;
static int canvas_src_id[3];
static int canvas_dst_id[3];
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 void dump_yuv_data(struct vframe_s *vf,
struct v4l_data_t *v4l_data)
{
struct file *fp;
mm_segment_t fs;
loff_t pos;
char name_buf[32];
u32 write_size;
snprintf(name_buf, sizeof(name_buf), "/data/tmp/%d-%d.raw",
vf->width, vf->height);
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644);
if (IS_ERR(fp)) {
pr_err("create %s fail.\n", name_buf);
} else {
write_size = v4l_data->byte_stride *
v4l_data->height * 3 / 2;
vfs_write(fp, phys_to_virt(v4l_data->phy_addr[0]),
write_size, &pos);
pr_info("write %u size to file.\n", write_size);
filp_close(fp, NULL);
}
set_fs(fs);
}
static void do_vframe_afbc_soft_decode(struct v4l_data_t *v4l_data,
struct vframe_s *vf)
{
int i, j, ret, y_size, free_cnt;
short *planes[4];
short *y_src, *u_src, *v_src, *s2c, *s2c1;
u8 *tmp, *tmp1;
u8 *y_dst, *vu_dst;
int bit_10;
struct timeval start, end;
unsigned long time_use = 0;
if ((vf->bitdepth & BITDEPTH_YMASK) == BITDEPTH_Y10)
bit_10 = 1;
else
bit_10 = 0;
y_size = vf->compWidth * vf->compHeight * sizeof(short);
pr_info("width: %d, height: %d, compWidth: %u, compHeight: %u.\n",
vf->width, vf->height, vf->compWidth, vf->compHeight);
for (i = 0; i < 4; i++) {
planes[i] = vmalloc(y_size);
if (!planes[i]) {
free_cnt = i;
pr_err("vmalloc fail in %s\n", __func__);
goto free;
}
pr_info("plane %d size: %d, vmalloc addr: %p.\n",
i, y_size, planes[i]);
}
free_cnt = 4;
do_gettimeofday(&start);
ret = AMLOGIC_FBC_vframe_decoder_v1((void **)planes, vf, 0, 0);
if (ret < 0) {
pr_err("amlogic_fbc_lib.ko error %d", ret);
goto free;
}
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
pr_debug("FBC Decompress time: %ldms\n", time_use);
y_src = planes[0];
u_src = planes[1];
v_src = planes[2];
y_dst = v4l_data->dst_addr;
vu_dst = v4l_data->dst_addr + v4l_data->byte_stride * v4l_data->height;
do_gettimeofday(&start);
for (i = 0; i < vf->compHeight; i++) {
for (j = 0; j < vf->compWidth; j++) {
s2c = y_src + j;
tmp = (u8 *)(s2c);
if (bit_10)
*(y_dst + j) = *s2c >> 2;
else
*(y_dst + j) = tmp[0];
}
y_dst += v4l_data->byte_stride;
y_src += vf->compWidth;
}
for (i = 0; i < (vf->compHeight / 2); i++) {
for (j = 0; j < vf->compWidth; j += 2) {
s2c = v_src + j / 2;
s2c1 = u_src + j / 2;
tmp = (u8 *)(s2c);
tmp1 = (u8 *)(s2c1);
if (bit_10) {
*(vu_dst + j) = *s2c >> 2;
*(vu_dst + j + 1) = *s2c1 >> 2;
} else {
*(vu_dst + j) = tmp[0];
*(vu_dst + j + 1) = tmp1[0];
}
}
vu_dst += v4l_data->byte_stride;
u_src += (vf->compWidth / 2);
v_src += (vf->compWidth / 2);
}
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
pr_debug("bitblk time: %ldms\n", time_use);
if (vf_dump)
dump_yuv_data(vf, v4l_data);
free:
for (i = 0; i < free_cnt; i++)
vfree(planes[i]);
}
/* for fbc output video:vp9 */
static bool need_do_extend_one_column_fbc(struct vframe_s *vf,
struct v4l_data_t *v4l_data)
{
u32 video_idx;
pr_info("vf->compwidth:%d v4l_data->byte_stride:%d num_vp9_videos:%d\n",
vf->compWidth, v4l_data->byte_stride, num_vp9_videos);
if (cts_video_flag || vf->compWidth >= v4l_data->byte_stride)
return false;
for (video_idx = 0; video_idx < num_vp9_videos; video_idx++) {
if (v4l_data->width == cts_vp9_videos[video_idx].width &&
v4l_data->height == cts_vp9_videos[video_idx].height) {
pr_info("no need_do_extend_one_column_vp9\n");
return false;
}
}
return true;
}
static bool need_do_extend_one_row_fbc(struct vframe_s *vf,
struct v4l_data_t *v4l_data)
{
u32 video_idx;
pr_info("vf->compHeight:%d v4l_data->height:%d num_vp9_videos:%d\n",
vf->compHeight, v4l_data->height, num_vp9_videos);
if (cts_video_flag || vf->compHeight >= v4l_data->height)
return false;
for (video_idx = 0; video_idx < num_vp9_videos; video_idx++) {
if (v4l_data->width == cts_vp9_videos[video_idx].width &&
v4l_data->height == cts_vp9_videos[video_idx].height) {
pr_info("no need_do_extend_one_row_vp9\n");
return false;
}
}
return true;
}
void v4lvideo_data_copy(struct v4l_data_t *v4l_data,
struct dma_buf *dmabuf,
u32 align)
{
struct uvm_hook_mod *uhmod = NULL;
struct config_para_ex_s ge2d_config;
struct canvas_config_s dst_canvas_config[3];
struct vframe_s *vf = NULL;
const char *keep_owner = "ge2d_dest_comp";
bool di_mode = false;
bool is_10bit = false;
bool is_dec_vf = false;
u32 aligned_height = 0;
char *y_vaddr = NULL;
char *uv_vaddr = NULL;
char *y_src = NULL;
char *uv_src = NULL;
u32 row;
struct file_private_data *file_private_data = NULL;
is_dec_vf = is_valid_mod_type(dmabuf, VF_SRC_DECODER);
if (is_dec_vf) {
vf = dmabuf_get_vframe(dmabuf);
pr_debug("vf=%p vf->vf_ext:%p vf->flags:%d\n",
vf, vf->vf_ext, vf->flag);
if (vf->vf_ext &&
(vf->flag & VFRAME_FLAG_CONTAIN_POST_FRAME)) {
if (print_flag)
pr_info("vf->type:%d\n", vf->type);
if (vf->type & VIDTYPE_INTERLACE) {
vf = vf->vf_ext;
if (print_flag)
pr_info("get vf_ext\n");
}
}
dmabuf_put_vframe(dmabuf);
} else {
if (!dmabuf) {
file_private_data = v4l_data->file_private_data;
} else {
uhmod = uvm_get_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
if (uhmod && uhmod->arg) {
file_private_data = uhmod->arg;
uvm_put_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO);
}
}
pr_debug("%s: uhmod: %p\n", __func__, uhmod);
if (!file_private_data) {
pr_err("file_private_data is NULL\n");
return;
}
if (file_private_data->flag & V4LVIDEO_FLAG_DI_NR)
vf = &file_private_data->vf_ext;
else
vf = &file_private_data->vf;
if (cts_use_di)
vf = &file_private_data->vf;
v4l_data->file_private_data = file_private_data;
}
if (!vf) {
pr_err("vf is NULL\n");
return;
}
pr_debug("%s: vf->type: %d vf->compWidth: %d\n",
__func__, vf->type, vf->compWidth);
/*
* fbc decoder for VIDTYPE_COMPRESS
*/
if ((vf->type & VIDTYPE_COMPRESS)) {
if (print_flag)
pr_info("fbc decoder path\n");
do_vframe_afbc_soft_decode(v4l_data, vf);
y_vaddr = v4l_data->dst_addr;
uv_vaddr = y_vaddr +
v4l_data->byte_stride * v4l_data->height;
do_vframe_afbc_soft_decode(v4l_data, vf);
if (need_do_extend_one_column_fbc(vf, v4l_data) == true) {
for (row = 0; row < vf->compHeight; row++) {
int cnt = vf->compWidth +
row * v4l_data->byte_stride;
if (print_flag)
pr_debug("before move y_vaddr[%d]=%d\n",
cnt, *(y_vaddr + cnt));
*(y_vaddr + cnt) = *(y_vaddr + cnt - 1);
if (print_flag)
pr_debug("after move y_vaddr[%d]=%d\n",
cnt, *(y_vaddr + cnt));
if (row < vf->compHeight / 2) {
cnt = vf->compWidth +
row * v4l_data->byte_stride;
if (print_flag)
pr_debug("before uv_vaddr[%d]=%d\n",
cnt,
*(uv_vaddr + cnt));
*(uv_vaddr + cnt) =
*(uv_vaddr + cnt - 2);
*(uv_vaddr + cnt + 1) =
*(uv_vaddr + cnt - 1);
if (print_flag)
pr_debug("after uv_vaddr[%d]=%d\n",
cnt,
*(uv_vaddr + cnt));
}
}
}
if (need_do_extend_one_row_fbc(vf, v4l_data) == false)
return;
if (print_flag)
pr_info("begin copy row\n");
y_src = y_vaddr + v4l_data->byte_stride * (vf->compHeight - 1);
uv_src = uv_vaddr +
v4l_data->byte_stride * (vf->compHeight / 2 - 1);
memcpy(y_src + v4l_data->byte_stride, y_src, vf->compWidth + 1);
memcpy(uv_src + v4l_data->byte_stride,
uv_src, vf->compWidth + 1);
return;
}
/*
* GE2D copy for non-compress
*/
is_10bit = vf->bitdepth & BITDEPTH_Y10;
di_mode = vf->type & VIDTYPE_DI_PW;
if (is_10bit && !di_mode) {
pr_err("vframe 10bit copy is not supported.\n");
return;
}
memset(&ge2d_config, 0, sizeof(ge2d_config));
memset(dst_canvas_config, 0, sizeof(dst_canvas_config));
if (!context)
context = create_ge2d_work_queue();
if (!context) {
pr_err("create_ge2d_work_queue failed.\n");
return;
}
aligned_height = ALIGN(v4l_data->height, align);
dst_canvas_config[0].phy_addr = v4l_data->phy_addr[0];
dst_canvas_config[0].width = v4l_data->byte_stride;
dst_canvas_config[0].height = v4l_data->height;
dst_canvas_config[0].block_mode = 0;
dst_canvas_config[0].endian = 0;
/*
* v4ldecoder:
* for non-compress videos like H264
* need align width and height
* v4lvideo:
* no requirement for height align
*/
dst_canvas_config[1].phy_addr = v4l_data->phy_addr[0] +
v4l_data->byte_stride * aligned_height;
dst_canvas_config[1].width = v4l_data->byte_stride;
dst_canvas_config[1].height = v4l_data->height / 2;
dst_canvas_config[1].block_mode = 0;
dst_canvas_config[1].endian = 0;
pr_debug("compWidth: %u, compHeight: %u.\n",
vf->compWidth, vf->compHeight);
pr_debug("vf-width:%u, vf-height:%u, vf-widht-align:%u\n",
vf->width, vf->height, ALIGN(vf->width, 64));
pr_debug("umm-bytestride: %d, umm-width:%u, umm-height:%u\n",
v4l_data->byte_stride, v4l_data->width, v4l_data->height);
pr_debug("y_addr:%lu, uv_addr:%lu.\n", dst_canvas_config[0].phy_addr,
dst_canvas_config[1].phy_addr);
if (vf->canvas0Addr == (u32)-1) {
if (canvas_src_id[0] <= 0)
canvas_src_id[0] =
canvas_pool_map_alloc_canvas(keep_owner);
if (canvas_src_id[1] <= 0)
canvas_src_id[1] =
canvas_pool_map_alloc_canvas(keep_owner);
if (canvas_src_id[0] <= 0 || canvas_src_id[1] <= 0) {
pr_err("canvas pool alloc fail.%d, %d, %d.\n",
canvas_src_id[0],
canvas_src_id[1],
canvas_src_id[2]);
return;
}
canvas_config_config(canvas_src_id[0], &vf->canvas0_config[0]);
canvas_config_config(canvas_src_id[1], &vf->canvas0_config[1]);
ge2d_config.src_para.canvas_index = canvas_src_id[0] |
canvas_src_id[1] << 8;
pr_debug("src index: %d.\n", ge2d_config.src_para.canvas_index);
} else {
ge2d_config.src_para.canvas_index = vf->canvas0Addr;
pr_debug("src1 : %d.\n", ge2d_config.src_para.canvas_index);
}
if (canvas_dst_id[0] <= 0)
canvas_dst_id[0] = canvas_pool_map_alloc_canvas(keep_owner);
if (canvas_dst_id[1] <= 0)
canvas_dst_id[1] = canvas_pool_map_alloc_canvas(keep_owner);
if (canvas_dst_id[0] <= 0 || canvas_dst_id[1] <= 0) {
pr_err("canvas pool alloc dst fail. %d, %d.\n",
canvas_dst_id[0], canvas_dst_id[1]);
return;
}
canvas_config_config(canvas_dst_id[0], &dst_canvas_config[0]);
canvas_config_config(canvas_dst_id[1], &dst_canvas_config[1]);
ge2d_config.dst_para.canvas_index = canvas_dst_id[0] |
canvas_dst_id[1] << 8;
pr_debug("dst canvas index: %d.\n", ge2d_config.dst_para.canvas_index);
ge2d_config.alu_const_color = 0;
ge2d_config.bitmask_en = 0;
ge2d_config.src1_gb_alpha = 0;
ge2d_config.dst_xy_swap = 0;
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.format = get_input_format(vf);
/*
* also need config ge2d src format
* VFRAME_FLAG_VIDEO_LINEAR -> little ednian
*/
if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR)
ge2d_config.src_para.format |= GE2D_LITTLE_ENDIAN;
pr_debug("%s: ge2d_config.src_para.format: %d\n",
__func__, ge2d_config.src_para.format);
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 = vf->width;
if (vf->type & VIDTYPE_INTERLACE)
ge2d_config.src_para.height = vf->height >> 1;
else
ge2d_config.src_para.height = vf->height;
ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID;
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.format = GE2D_FORMAT_M24_NV21
| GE2D_LITTLE_ENDIAN;
ge2d_config.dst_para.width = vf->width;
ge2d_config.dst_para.height = vf->height;
if (ge2d_context_config_ex(context, &ge2d_config) < 0) {
pr_err("ge2d_context_config_ex error.\n");
return;
}
if (vf->type & VIDTYPE_INTERLACE)
stretchblt_noalpha(context, 0, 0, vf->width, vf->height / 2,
0, 0, vf->width, vf->height);
else
stretchblt_noalpha(context, 0, 0, vf->width, vf->height,
0, 0, vf->width, vf->height);
if (vf_dump)
dump_yuv_data(vf, v4l_data);
}
s32 v4lvideo_release_sei_data(struct vframe_s *vf)
{
void *p;
s32 ret = -2;
u32 size = 0;
if (!vf)
return ret;
p = get_sei_from_src_fmt(vf, &size);
if (p) {
vfree(p);
atomic_dec(&global_set_cnt);
}
ret = clear_vframe_src_fmt(vf);
return ret;
}
static void v4lvideo_private_data_release(struct file_private_data *data)
{
if (!data)
return;
if (data->is_keep)
vf_free(data);
v4lvideo_release_sei_data(&data->vf);
memset(data, 0, sizeof(struct file_private_data));
}
void free_fd_private(void *arg)
{
if (arg) {
v4lvideo_private_data_release(arg);
kfree((u8 *)arg);
} else {
pr_err("free: arg is NULL\n");
}
}
struct file_private_data *v4lvideo_get_file_private_data(struct file *file_vf,
bool alloc_if_null)
{
struct file_private_data *file_private_data;
bool is_v4lvideo_fd = false;
struct uvm_hook_mod *uhmod;
struct uvm_hook_mod_info info;
int ret;
if (!file_vf) {
pr_err("v4lvideo: get_file_private_data fail\n");
return NULL;
}
if (is_v4lvideo_buf_file(file_vf))
is_v4lvideo_fd = true;
if (is_v4lvideo_fd) {
file_private_data =
(struct file_private_data *)(file_vf->private_data);
return file_private_data;
}
uhmod = uvm_get_hook_mod((struct dma_buf *)(file_vf->private_data),
VF_PROCESS_V4LVIDEO);
if (uhmod && uhmod->arg) {
file_private_data = uhmod->arg;
uvm_put_hook_mod((struct dma_buf *)(file_vf->private_data),
VF_PROCESS_V4LVIDEO);
return file_private_data;
} else if (!alloc_if_null) {
return NULL;
}
file_private_data = kzalloc(sizeof(*file_private_data), GFP_KERNEL);
if (!file_private_data)
return NULL;
info.type = VF_PROCESS_V4LVIDEO;
info.arg = file_private_data;
info.free = free_fd_private;
info.acquire_fence = NULL;
ret = uvm_attach_hook_mod((struct dma_buf *)(file_vf->private_data),
&info);
return file_private_data;
}
struct file_private_data *v4lvideo_get_vf(int fd)
{
struct file *file_vf = NULL;
struct file_private_data *file_private_data;
file_vf = fget(fd);
if (!file_vf) {
pr_err("%s file_vf is NULL\n", __func__);
return NULL;
}
file_private_data = v4lvideo_get_file_private_data(file_vf, false);
fput(file_vf);
if (!file_private_data) {
pr_err("%s private is NULL\n", __func__);
return NULL;
}
return file_private_data;
}
s32 v4lvideo_import_sei_data(struct vframe_s *vf,
struct vframe_s *dup_vf,
char *provider)
{
struct provider_aux_req_s req;
s32 ret = -2;
char *p;
u32 try_count = 0;
u32 max_count = 1;
bool fmt_update = false;
if (!vf || !dup_vf || !provider || !alloc_sei)
return ret;
if ((!(vf->flag & VFRAME_FLAG_DOUBLE_FRAM)) &&
(vf->type & VIDTYPE_DI_PW))
return ret;
if (!strcmp(provider, "dvbldec") && dup_vf->omx_index < 2)
max_count = 10;
while (try_count++ < max_count) {
req.vf = vf;
req.bot_flag = 0;
req.aux_buf = NULL;
req.aux_size = 0;
req.dv_enhance_exist = 0;
vf_notify_provider_by_name(provider,
VFRAME_EVENT_RECEIVER_GET_AUX_DATA,
(void *)&req);
if (req.aux_buf && req.aux_size) {
p = vmalloc(req.aux_size);
if (p) {
memcpy(p, req.aux_buf, req.aux_size);
ret = update_vframe_src_fmt(dup_vf, (void *)p,
(u32)req.aux_size,
req.dv_enhance_exist
? true : false,
provider, NULL);
if (!ret) {
/* FIXME: work around for sei/el out of sync */
/*if (dup_vf->src_fmt.fmt == */
/* VFRAME_SIGNAL_FMT_SDR && */
/* !strcmp(provider, "dvbldec")) */
/* dup_vf->src_fmt.fmt = */
/* VFRAME_SIGNAL_FMT_DOVI; */
atomic_inc(&global_set_cnt);
} else {
vfree(p);
}
} else {
ret = update_vframe_src_fmt(dup_vf, NULL,
0, false,
provider, NULL);
}
fmt_update = true;
break;
} else {
if (max_count > 1)
usleep_range(1000, 2000);
}
}
if (!fmt_update) {
ret = update_vframe_src_fmt(dup_vf, NULL, 0,
false, provider, NULL);
if ((alloc_sei & 2) && max_count > 1)
pr_info("try %d, no aux data\n", try_count);
}
if ((alloc_sei & 2) && max_count > 1)
pr_info("sei try_count %d\n", try_count);
if (alloc_sei & 2)
pr_info("import sei: provider:%s, vf:%p, dup_vf:%p, req.aux_buf:%p, req.aux_size:%d, req.dv_enhance_exist:%d, vf->src_fmt.fmt:%d\n",
provider, vf, dup_vf,
req.aux_buf, req.aux_size,
req.dv_enhance_exist, dup_vf->src_fmt.fmt);
return ret;
}
/* ------------------------------------------------------------------
* DMA and thread functions
* ------------------------------------------------------------------
*/
unsigned int get_v4lvideo_debug(void)
{
return debug;
}
EXPORT_SYMBOL(get_v4lvideo_debug);
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct v4lvideo_dev *dev = video_drvdata(file);
dprintk(dev, 2, "%s\n", __func__);
dev->provider_name = NULL;
dprintk(dev, 2, "returning from %s\n", __func__);
return 0;
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct v4lvideo_dev *dev = video_drvdata(file);
dprintk(dev, 2, "%s\n", __func__);
/*
* Typical driver might need to wait here until dma engine stops.
* In this case we can abort imiedetly, so it's just a noop.
*/
v4l2q_init(&dev->input_queue, V4LVIDEO_POOL_SIZE,
(void **)&dev->v4lvideo_input_queue[0]);
return 0;
}
/* ------------------------------------------------------------------
* Videobuf operations
* ------------------------------------------------------------------
*/
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *parms)
{
struct v4lvideo_dev *dev = video_drvdata(file);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memcpy(parms->parm.raw_data, (u8 *)&dev->am_parm,
sizeof(struct v4l2_amlogic_parm));
return 0;
}
/* ------------------------------------------------------------------
* IOCTL vidioc handling
* ------------------------------------------------------------------
*/
static int vidioc_open(struct file *file)
{
struct v4lvideo_dev *dev = video_drvdata(file);
int i;
if (dev->fd_num > 0) {
pr_err("%s error\n", __func__);
return -EBUSY;
}
dev->fd_num++;
dev->vf_wait_cnt = 0;
v4l2q_init(&dev->input_queue,
V4LVIDEO_POOL_SIZE,
(void **)&dev->v4lvideo_input_queue[0]);
memset(&dev->v4lvideo_file[0], 0,
sizeof(struct v4lvideo_file_s) * V4LVIDEO_POOL_SIZE);
for (i = 0; i < V4LVIDEO_POOL_SIZE; i++) {
atomic_set(&dev->v4lvideo_file[i].on_use, false);
dev->v4lvideo_file[i].free_before_unreg = false;
}
//dprintk(dev, 2, "vidioc_open\n");
V4LVID_DBG("v4lvideo open\n");
mutex_lock(&dev->mutex_opened);
dev->opened = true;
mutex_unlock(&dev->mutex_opened);
v4l_print(dev->inst, PRINT_COUNT, "open\n");
return 0;
}
static int vidioc_close(struct file *file)
{
struct v4lvideo_dev *dev = video_drvdata(file);
int i;
u32 inst_id = dev->inst;
V4LVID_DBG("vidioc_close!!!!\n");
if (dev->mapped)
v4lvideo_release_map_force(dev);
for (i = 0; i < V4LVIDEO_POOL_SIZE; i++) {
if (dev->v4lvideo_file[i].free_before_unreg) {
vf_free_force(&dev->v4lvideo_file[i]);
dev->v4lvideo_file[i].free_before_unreg = false;
}
}
if (dev->fd_num > 0)
dev->fd_num--;
mutex_lock(&dev->mutex_opened);
dev->opened = false;
mutex_unlock(&dev->mutex_opened);
v4l_print(dev->inst, PRINT_COUNT,
"close get=%d, put=%d, release=%d, %d\n",
total_get_count[inst_id], total_put_count[inst_id],
total_release_count[inst_id],
total_get_count[inst_id] - total_put_count[inst_id]
- total_release_count[inst_id]);
return 0;
}
static ssize_t vidioc_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
pr_info("v4lvideo read\n");
return 0;
}
static unsigned int v4lvideo_poll(struct file *file,
struct poll_table_struct *wait)
{
struct v4lvideo_dev *dev = video_drvdata(file);
if (dev->receiver_register) {
if (vf_peek(dev->vf_receiver_name))
return POLL_IN | POLLRDNORM;
else
return 0;
} else {
return 0;
}
}
static int vidioc_querycap(struct file *file,
void *priv,
struct v4l2_capability *cap)
{
struct v4lvideo_dev *dev = video_drvdata(file);
strcpy(cap->driver, "v4lvideo");
strcpy(cap->card, "v4lvideo");
snprintf(cap->bus_info,
sizeof(cap->bus_info),
"platform:%s",
dev->v4l2_dev.name);
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_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
const struct v4lvideo_fmt *fmt;
if (f->index >= ARRAY_SIZE(formats))
return -EINVAL;
fmt = &formats[f->index];
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct v4lvideo_dev *dev = video_drvdata(file);
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
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;
if (dev->fmt->is_yuv)
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
else
f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
static int vidioc_try_fmt_vid_cap(struct file *file,
void *priv,
struct v4l2_format *f)
{
struct v4lvideo_dev *dev = video_drvdata(file);
const struct v4lvideo_fmt *fmt;
fmt = get_format(f);
if (!fmt) {
dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
v4l_bound_align_image(&f->fmt.pix.width,
48,
MAX_WIDTH,
4,
&f->fmt.pix.height,
32,
MAX_HEIGHT,
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;
if (fmt->is_yuv)
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
else
f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
f->fmt.pix.priv = 0;
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct v4lvideo_dev *dev = video_drvdata(file);
int ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
dev->fmt = get_format(f);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
if (dev->width == 0 || dev->height == 0)
V4LVID_ERR("ion buffer w/h info is invalid!!!!!!!!!!!\n");
return 0;
}
static void push_to_display_q(struct v4lvideo_dev *dev,
struct file_private_data *file_private_data)
{
int i;
for (i = 0; i < V4LVIDEO_POOL_SIZE; i++) {
if (!atomic_read(&dev->v4lvideo_file[i].on_use))
break;
}
if (i == V4LVIDEO_POOL_SIZE) {
pr_err("v4lvideo: dq not find v4lvideo_file\n");
return;
}
atomic_set(&dev->v4lvideo_file[i].on_use, true);
file_private_data->private = (void *)&dev->v4lvideo_file[i];
dev->v4lvideo_file[i].private_data_p = file_private_data;
dev->v4lvideo_file[i].vf_p = file_private_data->vf_p;
dev->v4lvideo_file[i].vf_ext_p = file_private_data->vf_ext_p;
dev->v4lvideo_file[i].flag = file_private_data->flag;
dev->v4lvideo_file[i].vf_type = file_private_data->vf_p->type;
dev->v4lvideo_file[i].inst_id = file_private_data->v4l_inst_id;
v4l2q_push(&dev->display_queue, &dev->v4lvideo_file[i]);
}
static struct v4lvideo_file_s *pop_from_display_q(struct v4lvideo_dev *dev)
{
struct v4lvideo_file_s *v4lvideo_file;
v4lvideo_file = v4l2q_pop(&dev->display_queue);
if (!v4lvideo_file)
return NULL;
atomic_set(&v4lvideo_file->on_use, false);
return v4lvideo_file;
}
static bool pop_specific_from_display_q(struct v4lvideo_dev *dev,
struct v4lvideo_file_s *v4lvideo_file)
{
bool ret;
ret = v4l2q_pop_specific(&dev->display_queue, v4lvideo_file);
if (ret)
atomic_set(&v4lvideo_file->on_use, false);
return ret;
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct v4lvideo_dev *dev = video_drvdata(file);
struct vframe_s *vf_p;
struct vframe_s *vf_ext_p;
struct file *file_vf = NULL;
struct file_private_data *file_private_data = NULL;
u32 flag;
struct v4lvideo_file_s *v4lvideo_file;
u32 inst_id = dev->inst;
dev->v4lvideo_input[p->index] = *p;
q_count[inst_id]++;
file_vf = fget(p->m.fd);
if (!file_vf) {
pr_err("v4lvideo: qbuf fget fail\n");
return 0;
}
file_private_data = v4lvideo_get_file_private_data(file_vf, true);
if (!file_private_data) {
pr_err("v4lvideo: qbuf file_private_data NULL\n");
fput(file_vf);
return 0;
}
vf_p = file_private_data->vf_p;
vf_ext_p = file_private_data->vf_ext_p;
flag = file_private_data->flag;
v4lvideo_file = (struct v4lvideo_file_s *)file_private_data->private;
mutex_lock(&dev->mutex_input);
if (vf_p) {
if (!v4lvideo_file) {
pr_err("%s: v4lvideo_file NULL\n", __func__);
mutex_unlock(&dev->mutex_input);
return 0;
}
if (file_private_data->is_keep) {
vf_free(file_private_data);
} else {
if (pop_specific_from_display_q(dev, v4lvideo_file)) {
if (dev->receiver_register) {
if (flag & V4LVIDEO_FLAG_DI_DEC)
vf_p = vf_ext_p;
if (vf_p->type & VIDTYPE_DI_PW)
total_put_count[inst_id]++;
vf_put(vf_p, dev->vf_receiver_name);
put_count[inst_id]++;
} else {
vf_free(file_private_data);
pr_err("%s: vfm is unreg\n", __func__);
}
} else {
pr_err("%s: maybe in unreg\n", __func__);
if (v4lvideo_file->vf_type & VIDTYPE_DI_PW) {
dim_post_keep_cmd_release2(vf_p);
total_release_count[inst_id]++;
v4l_print(inst_id, PRINT_COUNT,
"qbuf: get=%d, put=%d, release=%d\n",
total_get_count[inst_id],
total_put_count[inst_id],
total_release_count[inst_id]);
}
}
}
} else {
dprintk(dev, 1,
"%s: vf is NULL, at the start of playback\n", __func__);
}
v4lvideo_release_sei_data(&file_private_data->vf);
init_file_private_data(file_private_data);
fput(file_vf);
v4l2q_push(&dev->input_queue, &dev->v4lvideo_input[p->index]);
mutex_unlock(&dev->mutex_input);
return 0;
}
static void canvas_to_addr(struct vframe_s *vf)
{
struct canvas_s src_cs0, src_cs1, src_cs2;
if (vf->canvas0Addr == (u32)-1)
return;
canvas_read(vf->canvas0Addr & 0xff, &src_cs0);
canvas_read(vf->canvas0Addr >> 8 & 0xff, &src_cs1);
canvas_read(vf->canvas0Addr >> 16 & 0xff, &src_cs2);
vf->canvas0_config[0].phy_addr = src_cs0.addr;
vf->canvas0_config[0].width = src_cs0.width;
vf->canvas0_config[0].height = src_cs0.height;
vf->canvas0_config[0].block_mode = src_cs0.blkmode;
vf->canvas0_config[0].endian = src_cs0.endian;
vf->canvas0_config[1].phy_addr = src_cs1.addr;
vf->canvas0_config[1].width = src_cs1.width;
vf->canvas0_config[1].height = src_cs1.height;
vf->canvas0_config[1].block_mode = src_cs1.blkmode;
vf->canvas0_config[1].endian = src_cs1.endian;
vf->canvas0_config[2].phy_addr = src_cs2.addr;
vf->canvas0_config[2].width = src_cs2.width;
vf->canvas0_config[2].height = src_cs2.height;
vf->canvas0_config[2].block_mode = src_cs2.blkmode;
vf->canvas0_config[2].endian = src_cs2.endian;
if ((vf->type & VIDTYPE_MVC) &&
(vf->canvas1Addr != (u32)-1)) {
canvas_read(vf->canvas1Addr & 0xff, &src_cs0);
canvas_read(vf->canvas1Addr >> 8 & 0xff, &src_cs1);
canvas_read(vf->canvas1Addr >> 16 & 0xff, &src_cs2);
vf->canvas1_config[0].phy_addr = src_cs0.addr;
vf->canvas1_config[0].width = src_cs0.width;
vf->canvas1_config[0].height = src_cs0.height;
vf->canvas1_config[0].block_mode = src_cs0.blkmode;
vf->canvas1_config[0].endian = src_cs0.endian;
vf->canvas1_config[1].phy_addr = src_cs1.addr;
vf->canvas1_config[1].width = src_cs1.width;
vf->canvas1_config[1].height = src_cs1.height;
vf->canvas1_config[1].block_mode = src_cs1.blkmode;
vf->canvas1_config[1].endian = src_cs1.endian;
vf->canvas1_config[2].phy_addr = src_cs2.addr;
vf->canvas1_config[2].width = src_cs2.width;
vf->canvas1_config[2].height = src_cs2.height;
vf->canvas1_config[2].block_mode = src_cs2.blkmode;
vf->canvas1_config[2].endian = src_cs2.endian;
}
if (vf->type & VIDTYPE_VIU_NV21)
vf->plane_num = 2;
else if (vf->type & VIDTYPE_VIU_444)
vf->plane_num = 1;
else if (vf->type & VIDTYPE_VIU_422)
vf->plane_num = 1;
else
vf->plane_num = 3;
vf->canvas0Addr = (u32)-1;
vf->canvas1Addr = (u32)-1;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct v4lvideo_dev *dev = video_drvdata(file);
struct v4l2_buffer *buf = NULL;
struct vframe_s *vf;
struct file *file_vf = NULL;
struct file_private_data *file_private_data = NULL;
u64 pts_us64 = 0;
u64 pts_tmp;
char *provider_name = NULL;
struct vframe_s *vf_ext = NULL;
u32 inst_id = dev->inst;
mutex_lock(&dev->mutex_input);
if (!dev->receiver_register) {
mutex_unlock(&dev->mutex_input);
return -EAGAIN;
}
buf = v4l2q_peek(&dev->input_queue);
if (!buf) {
dprintk(dev, 3, "No active queue to serve\n");
mutex_unlock(&dev->mutex_input);
return -EAGAIN;
}
vf = vf_peek(dev->vf_receiver_name);
if (!vf) {
dev->vf_wait_cnt++;
mutex_unlock(&dev->mutex_input);
return -EAGAIN;
}
vf = vf_get(dev->vf_receiver_name);
if (!vf) {
mutex_unlock(&dev->mutex_input);
return -EAGAIN;
}
if (vf->type & VIDTYPE_DI_PW)
total_get_count[inst_id]++;
if (vf->type & VIDTYPE_V4L_EOS) {
vf_put(vf, dev->vf_receiver_name);
mutex_unlock(&dev->mutex_input);
put_count[inst_id]++;
total_put_count[inst_id]++;
return -EAGAIN;
}
if (!dev->provider_name) {
provider_name = vf_get_provider_name(dev->vf_receiver_name);
while (provider_name) {
if (!vf_get_provider_name(provider_name))
break;
provider_name =
vf_get_provider_name(provider_name);
}
dev->provider_name = provider_name;
pr_info("v4lvideo: provider name: %s\n",
dev->provider_name ? dev->provider_name : "NULL");
}
get_count[inst_id]++;
vf->omx_index = dev->frame_num;
dev->am_parm.signal_type = vf->signal_type;
dev->am_parm.master_display_colour =
vf->prop.master_display_colour;
if (vf->hdr10p_data_size > 0 && vf->hdr10p_data_buf) {
if (vf->hdr10p_data_size <= 128) {
dev->am_parm.hdr10p_data_size =
vf->hdr10p_data_size;
memcpy(dev->am_parm.hdr10p_data_buf,
vf->hdr10p_data_buf,
vf->hdr10p_data_size);
} else {
pr_info("v4lvideo: hdr10+ data size is %d, skip it!\n",
vf->hdr10p_data_size);
dev->am_parm.hdr10p_data_size = 0;
}
} else {
dev->am_parm.hdr10p_data_size = 0;
}
buf = v4l2q_pop(&dev->input_queue);
if (!buf) {
pr_err("pop buf is NULL\n");
put_count[inst_id]++;
if (vf->type & VIDTYPE_DI_PW)
total_put_count[inst_id]++;
vf_put(vf, dev->vf_receiver_name);
mutex_unlock(&dev->mutex_input);
return -EAGAIN;
}
dev->vf_wait_cnt = 0;
file_vf = fget(buf->m.fd);
if (!file_vf) {
mutex_unlock(&dev->mutex_input);
pr_err("v4lvideo: dqbuf fget fail\n");
return -EAGAIN;
}
file_private_data = v4lvideo_get_file_private_data(file_vf, false);
if (!file_private_data) {
put_count[inst_id]++;
if (vf->type & VIDTYPE_DI_PW)
total_put_count[inst_id]++;
vf_put(vf, dev->vf_receiver_name);
mutex_unlock(&dev->mutex_input);
fput(file_vf);
pr_err("v4lvideo: file_private_data NULL\n");
return -EAGAIN;
}
if (vf->flag & VFRAME_FLAG_DOUBLE_FRAM) {
vf_ext = (struct vframe_s *)vf->vf_ext;
vf_ext->omx_index = vf->omx_index;
if (render_use_dec) {
file_private_data->vf = *vf_ext;
file_private_data->vf_p = vf_ext;
file_private_data->vf_ext = *vf;
file_private_data->vf_ext_p = vf;
file_private_data->flag |= V4LVIDEO_FLAG_DI_DEC;
canvas_to_addr(&file_private_data->vf);
canvas_to_addr(&file_private_data->vf_ext);
} else {
file_private_data->vf = *vf;
file_private_data->vf_p = vf;
file_private_data->vf_ext = *vf_ext;
file_private_data->vf_ext_p = vf_ext;
file_private_data->flag |= V4LVIDEO_FLAG_DI_NR;
canvas_to_addr(&file_private_data->vf);
canvas_to_addr(&file_private_data->vf_ext);
file_private_data->vf.canvas0_config[0].block_mode = 0;
file_private_data->vf.canvas0_config[0].endian = 0;
file_private_data->vf.vf_ext =
&file_private_data->vf_ext;
}
file_private_data->vf.vf_ext = &file_private_data->vf_ext;
} else {
file_private_data->vf = *vf;
file_private_data->vf_p = vf;
canvas_to_addr(&file_private_data->vf);
}
file_private_data->vf.src_fmt.md_buf = file_private_data->md.p_md;
file_private_data->vf.src_fmt.comp_buf = file_private_data->md.p_comp;
file_private_data->v4l_inst_id = dev->inst;
v4lvideo_import_sei_data(vf,
&file_private_data->vf,
dev->provider_name);
//pr_err("dqbuf: file_private_data=%p, vf=%p\n", file_private_data, vf);
push_to_display_q(dev, file_private_data);
fput(file_vf);
mutex_unlock(&dev->mutex_input);
if (vf->pts_us64) {
dev->first_frame = 1;
pts_us64 = vf->pts_us64;
} else if (dev->first_frame == 0) {
dev->first_frame = 1;
pts_us64 = 0;
} else {
pts_tmp = dur2pts(vf->duration) * 100;
do_div(pts_tmp, 9);
pts_us64 = dev->last_pts_us64
+ pts_tmp;
pts_tmp = pts_us64 * 9;
do_div(pts_tmp, 100);
vf->pts = pts_tmp;
}
/*workrun for decoder i pts err, if decoder fix it, this should remove*/
if ((!(vf->flag & VFRAME_FLAG_DOUBLE_FRAM)) &&
(vf->type & VIDTYPE_DI_PW || vf->type & VIDTYPE_INTERLACE) &&
vf->pts_us64 == dev->last_pts_us64) {
dprintk(dev, 1, "pts same\n");
pts_tmp = dur2pts(vf->duration) * 100;
do_div(pts_tmp, 9);
pts_us64 = dev->last_pts_us64
+ pts_tmp;
pts_tmp = pts_us64 * 9;
do_div(pts_tmp, 100);
vf->pts = pts_tmp;
}
*p = *buf;
p->timestamp.tv_sec = pts_us64 >> 32;
p->timestamp.tv_usec = pts_us64 & 0xFFFFFFFF;
dev->last_pts_us64 = pts_us64;
if ((vf->type & VIDTYPE_COMPRESS) != 0) {
p->timecode.type = vf->compWidth;
p->timecode.flags = vf->compHeight;
} else {
p->timecode.type = vf->width;
p->timecode.flags = vf->height;
}
p->sequence = dev->frame_num++;
//pr_err("dqbuf: frame_num=%d\n", p->sequence);
dq_count[inst_id]++;
return 0;
}
/* ------------------------------------------------------------------
* File operations for the device
* ------------------------------------------------------------------
*/
static const struct v4l2_file_operations v4lvideo_v4l2_fops = {
.owner = THIS_MODULE,
.open = vidioc_open,
.release = vidioc_close,
.read = vidioc_read,
.poll = v4lvideo_poll,
.unlocked_ioctl = video_ioctl2,/* V4L2 ioctl handler */
.mmap = vb2_fop_mmap,
};
static const struct v4l2_ioctl_ops v4lvideo_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.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_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_parm = vidioc_g_parm,
};
static const struct video_device v4lvideo_template = {
.name = "v4lvideo",
.fops = &v4lvideo_v4l2_fops,
.ioctl_ops = &v4lvideo_ioctl_ops,
.release = video_device_release,
};
static int v4lvideo_v4l2_release(void)
{
struct v4lvideo_dev *dev;
struct list_head *list;
unsigned long flags;
flags = v4lvideo_devlist_lock();
while (!list_empty(&v4lvideo_devlist)) {
list = v4lvideo_devlist.next;
list_del(list);
v4lvideo_devlist_unlock(flags);
dev = list_entry(list, struct v4lvideo_dev, v4lvideo_devlist);
v4l2_info(&dev->v4l2_dev,
"unregistering %s\n",
video_device_node_name(&dev->vdev));
video_unregister_device(&dev->vdev);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
flags = v4lvideo_devlist_lock();
}
/* vb2_dma_contig_cleanup_ctx(v4lvideo_dma_ctx); */
v4lvideo_devlist_unlock(flags);
return 0;
}
static int video_receiver_event_fun(int type, void *data, void *private_data)
{
struct v4lvideo_dev *dev = (struct v4lvideo_dev *)private_data;
struct file_private_data *file_private_data = NULL;
struct v4lvideo_file_s *v4lvideo_file;
u32 inst_id = dev->inst;
if (type == VFRAME_EVENT_PROVIDER_UNREG) {
dev->receiver_register = false;
mutex_lock(&dev->mutex_input);
while (!v4l2q_empty(&dev->display_queue)) {
v4lvideo_file = pop_from_display_q(dev);
if (!v4lvideo_file) {
mutex_unlock(&dev->mutex_input);
pr_err("v4lvideo: unreg pop fail\n");
return 0;
}
file_private_data = v4lvideo_file->private_data_p;
vf_keep(dev, v4lvideo_file, file_private_data);
/*pr_err("unreg:v4lvideo, keep last frame\n");*/
}
mutex_unlock(&dev->mutex_input);
pr_err("unreg:v4lvideo inst=%d\n", dev->inst);
v4l_print(dev->inst, PRINT_COUNT,
"unreg get=%d, put=%d, release=%d\n",
total_get_count[inst_id], total_put_count[inst_id],
total_release_count[inst_id]);
} else if (type == VFRAME_EVENT_PROVIDER_REG) {
mutex_lock(&dev->mutex_input);
v4l2q_init(&dev->display_queue,
V4LVIDEO_POOL_SIZE,
(void **)&dev->v4lvideo_display_queue[0]);
dev->frame_num = 0;
dev->first_frame = 0;
dev->last_pts_us64 = U64_MAX;
mutex_unlock(&dev->mutex_input);
get_count[inst_id] = 0;
put_count[inst_id] = 0;
q_count[inst_id] = 0;
dq_count[inst_id] = 0;
pr_err("reg:v4lvideo inst=%d\n", dev->inst);
dev->receiver_register = true;
} else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) {
if (dev->vf_wait_cnt > 1) {
if (!inactive_check_disp)
return RECEIVER_INACTIVE;
else if (v4l2q_empty(&dev->display_queue))
return RECEIVER_INACTIVE;
}
return RECEIVER_ACTIVE;
}
return 0;
}
static const struct vframe_receiver_op_s video_vf_receiver = {
.event_cb = video_receiver_event_fun
};
static int __init v4lvideo_create_instance(int inst)
{
struct v4lvideo_dev *dev;
struct video_device *vfd;
int ret;
unsigned long flags;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
snprintf(dev->v4l2_dev.name,
sizeof(dev->v4l2_dev.name),
"%s-%03d",
V4LVIDEO_MODULE_NAME,
inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
if (ret)
goto free_dev;
dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
dev->fd_num = 0;
vfd = &dev->vdev;
*vfd = v4lvideo_template;
vfd->dev_debug = 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,
inst + video_nr_base);
if (ret < 0)
goto unreg_dev;
video_set_drvdata(vfd, dev);
dev->inst = inst;
snprintf(dev->vf_receiver_name,
ION_VF_RECEIVER_NAME_SIZE,
RECEIVER_NAME ".%x",
inst & 0xff);
vf_receiver_init(&dev->video_vf_receiver,
dev->vf_receiver_name,
&video_vf_receiver, dev);
vf_reg_receiver(&dev->video_vf_receiver);
if (inst == 0 || inst == n_devs - 1)
v4l2_info(&dev->v4l2_dev,
"V4L2 device registered as %s\n",
video_device_node_name(vfd));
/* add to device list */
flags = v4lvideo_devlist_lock();
list_add_tail(&dev->v4lvideo_devlist, &v4lvideo_devlist);
v4lvideo_devlist_unlock(flags);
mutex_init(&dev->mutex_input);
mutex_init(&dev->mutex_opened);
return 0;
unreg_dev:
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
return ret;
}
static int v4lvideo_file_release(struct inode *inode, struct file *file)
{
struct file_private_data *file_private_data = file->private_data;
/*pr_err("v4lvideo_file_release\n");*/
if (file_private_data) {
if (file_private_data->cnt_file) {
fput(file_private_data->cnt_file);
link_put_fd_count++;
pr_debug("v4lvideo: %s pre file: %p\n",
__func__, file_private_data->cnt_file);
file_private_data->cnt_file = NULL;
} else {
if (file_private_data->is_keep)
vf_free(file_private_data);
v4lvideo_release_sei_data(&file_private_data->vf);
if (file_private_data->md.p_md)
vfree(file_private_data->md.p_md);
if (file_private_data->md.p_comp)
vfree(file_private_data->md.p_comp);
}
file_private_data->md.p_md = NULL;
file_private_data->md.p_comp = NULL;
memset(file_private_data, 0, sizeof(struct file_private_data));
v4lvideo_private_data_release(file_private_data);
kfree((u8 *)file_private_data);
file->private_data = NULL;
release_fd_count++;
}
return 0;
}
static const struct file_operations v4lvideo_file_fops = {
.release = v4lvideo_file_release,
//.poll = v4lvideo_file_poll,
//.unlocked_ioctl = v4lvideo_file_ioctl,
//.compat_ioctl = v4lvideo_file_ioctl,
};
int is_v4lvideo_buf_file(struct file *file)
{
return file->f_op == &v4lvideo_file_fops;
}
struct file *v4lvideo_alloc_file(void)
{
struct file *file = NULL;
struct file_private_data *private_data = NULL;
private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
if (!private_data)
return NULL;
init_file_private_data(private_data);
file = anon_inode_getfile("v4lvideo_file",
&v4lvideo_file_fops,
private_data, 0);
if (IS_ERR(file)) {
kfree((u8 *)private_data);
pr_err("%s: anon_inode_getfile fail\n", __func__);
return NULL;
}
private_data->md.p_md = vmalloc(MD_BUF_SIZE);
if (!private_data->md.p_md) {
kfree((u8 *)private_data);
return NULL;
}
private_data->md.p_comp = vmalloc(COMP_BUF_SIZE);
if (!private_data->md.p_comp) {
if (private_data->md.p_md) {
vfree(private_data->md.p_md);
private_data->md.p_md = NULL;
}
kfree((u8 *)private_data);
return NULL;
}
return file;
}
int v4lvideo_alloc_fd(int *fd)
{
struct file *file = NULL;
struct file_private_data *private_data = NULL;
int file_fd = get_unused_fd_flags(O_CLOEXEC);
if (file_fd < 0) {
pr_err("%s: get unused fd fail\n", __func__);
return -ENODEV;
}
private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
if (!private_data) {
put_unused_fd(file_fd);
pr_err("%s: private_date fail\n", __func__);
return -ENOMEM;
}
init_file_private_data(private_data);
file = anon_inode_getfile("v4lvideo_file",
&v4lvideo_file_fops,
private_data, 0);
if (IS_ERR(file)) {
kfree((u8 *)private_data);
put_unused_fd(file_fd);
pr_err("%s: anon_inode_getfile fail\n", __func__);
return -ENODEV;
}
private_data->md.p_md = vmalloc(MD_BUF_SIZE);
if (!private_data->md.p_md) {
kfree((u8 *)private_data);
put_unused_fd(file_fd);
return -ENOMEM;
}
private_data->md.p_comp = vmalloc(COMP_BUF_SIZE);
if (!private_data->md.p_comp) {
if (private_data->md.p_md) {
vfree(private_data->md.p_md);
private_data->md.p_md = NULL;
}
kfree((u8 *)private_data);
put_unused_fd(file_fd);
return -ENOMEM;
}
fd_install(file_fd, file);
*fd = file_fd;
open_fd_count++;
return 0;
}
static int v4lvideo_fd_link(int src_fd, int dst_fd)
{
struct file *file0 = NULL;
struct file *file1 = NULL;
struct file_private_data *private_data0 = NULL;
struct file_private_data *private_data1 = NULL;
file0 = fget(src_fd);
if (!file0) {
pr_err("v4lvideo: %s source file is NULL\n", __func__);
return -EINVAL;
}
file1 = fget(dst_fd);
if (!file1) {
fput(file0);
pr_err("v4lvideo: %s dst file is NULL\n", __func__);
return -EINVAL;
}
private_data0 = (struct file_private_data *)(file0->private_data);
private_data1 = (struct file_private_data *)(file1->private_data);
if (private_data1->cnt_file) {
fput(private_data1->cnt_file);
link_put_fd_count++;
} else {
if (private_data1->md.p_md) {
vfree(private_data1->md.p_md);
private_data1->md.p_md = NULL;
}
if (private_data1->md.p_comp) {
vfree(private_data1->md.p_comp);
private_data1->md.p_comp = NULL;
}
}
*private_data1 = *private_data0;
private_data1->cnt_file = file0;
fput(file1);
link_fd_count++;
return 0;
}
static ssize_t sei_cnt_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
ssize_t r;
int cnt;
cnt = atomic_read(&global_set_cnt);
r = sprintf(buf, "allocated sei buffer cnt: %d\n", cnt);
return r;
}
static ssize_t sei_cnt_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;
pr_info("set sei_cnt val:%d\n", val);
atomic_set(&global_set_cnt, val);
return count;
}
static ssize_t alloc_sei_debug_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "alloc sei: %d\n", alloc_sei);
}
static ssize_t alloc_sei_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)
alloc_sei = val;
else
alloc_sei = 0;
pr_info("set alloc_sei val:%d\n", alloc_sei);
return count;
}
static ssize_t video_nr_base_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80,
"current video_nr_base is %d\n",
video_nr_base);
}
static ssize_t video_nr_base_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0)
return ret;
video_nr_base = tmp;
return count;
}
static ssize_t n_devs_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80,
"current n_devs is %d\n",
n_devs);
}
static ssize_t n_devs_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0) {
pr_info("ERROR converting %s to long int!\n", buf);
return ret;
}
n_devs = tmp;
return count;
}
static ssize_t debug_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80,
"current debug is %d\n",
debug);
}
static ssize_t debug_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0) {
pr_info("ERROR converting %s to long int!\n", buf);
return ret;
}
debug = tmp;
return count;
}
static ssize_t get_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "get_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
get_count[0], get_count[1], get_count[2],
get_count[3], get_count[4], get_count[5],
get_count[6], get_count[7], get_count[8]);
}
static ssize_t get_count_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
int i;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0) {
pr_info("ERROR converting %s to long int!\n", buf);
return ret;
}
for (i = 0; i < N_DEVS; i++) {
if (tmp > 0)
get_count[i] = tmp;
else
get_count[i] = 0;
}
return count;
}
static ssize_t put_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "put_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
put_count[0], put_count[1], put_count[2],
put_count[3], put_count[4], put_count[5],
put_count[6], put_count[7], put_count[8]);
}
static ssize_t put_count_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
int i;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0) {
pr_info("ERROR converting %s to long int!\n", buf);
return ret;
}
for (i = 0; i < N_DEVS; i++) {
if (tmp > 0)
put_count[i] = tmp;
else
put_count[i] = 0;
}
return count;
}
static ssize_t q_count_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80, "q_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
q_count[0], q_count[1], q_count[2],
q_count[3], q_count[4], q_count[5],
q_count[6], q_count[7], q_count[8]);
}
static ssize_t q_count_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
int i;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
for (i = 0; i < N_DEVS; i++) {
if (val > 0)
put_count[i] = val;
else
put_count[i] = 0;
}
return count;
}
static ssize_t dq_count_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80, "dq_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
dq_count[0], dq_count[1], dq_count[2],
dq_count[3], dq_count[4], dq_count[5],
dq_count[6], dq_count[7], dq_count[8]);
}
static ssize_t dq_count_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
ssize_t r;
int val;
int i;
r = kstrtoint(buf, 0, &val);
if (r < 0)
return -EINVAL;
for (i = 0; i < N_DEVS; i++) {
if (val > 0)
put_count[i] = val;
else
put_count[i] = 0;
}
return count;
}
static ssize_t cts_use_di_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "cts_use_di: %d\n", cts_use_di);
}
static ssize_t cts_use_di_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)
cts_use_di = val;
else
cts_use_di = 0;
return count;
}
static ssize_t render_use_dec_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "render_use_dec: %d\n", render_use_dec);
}
static ssize_t render_use_dec_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)
render_use_dec = val;
else
render_use_dec = 0;
return count;
}
static ssize_t dec_count_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "dec_count: %d\n", dec_count);
}
static ssize_t dec_count_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)
dec_count = val;
else
dec_count = 0;
return count;
}
static ssize_t vf_dump_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80,
"current vf_dump is %d\n",
vf_dump);
}
static ssize_t vf_dump_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0)
return ret;
vf_dump = tmp;
return count;
}
static ssize_t open_fd_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "open_fd_count is %d\n", open_fd_count);
}
static ssize_t release_fd_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "release_fd_count is %d\n", release_fd_count);
}
static ssize_t link_fd_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "link_fd_count is %d\n", link_fd_count);
}
static ssize_t link_put_fd_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "link_put_fd_count is %d\n", link_put_fd_count);
}
static ssize_t v4lvideo_version_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "v4lvideo_version is %d\n", v4lvideo_version);
}
static ssize_t total_get_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "total_get_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
total_get_count[0], total_get_count[1], total_get_count[2],
total_get_count[3], total_get_count[4], total_get_count[5],
total_get_count[6], total_get_count[7], total_get_count[8]);
}
static ssize_t total_put_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "total_put_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
total_put_count[0], total_put_count[1], total_put_count[2],
total_put_count[3], total_put_count[4], total_put_count[5],
total_put_count[6], total_put_count[7], total_put_count[8]);
}
static ssize_t total_release_count_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "total_release_count: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
total_release_count[0], total_release_count[1],
total_release_count[2], total_release_count[3],
total_release_count[4], total_release_count[5],
total_release_count[6], total_release_count[7],
total_release_count[8]);
}
static ssize_t print_flag_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80, "print_flag is %d\n", print_flag);
}
static ssize_t print_flag_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0)
return ret;
print_flag = tmp;
return count;
}
static ssize_t cts_video_flag_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "cts_video_flag: %d\n", cts_video_flag);
}
static ssize_t cts_video_flag_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)
cts_video_flag = val;
else
cts_video_flag = 0;
return count;
}
static ssize_t inactive_check_disp_show(struct class *cla,
struct class_attribute *attr,
char *buf)
{
return snprintf(buf, 80,
"current inactive_check_disp is %d\n",
inactive_check_disp);
}
static ssize_t inactive_check_disp_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp;
int ret;
ret = kstrtol(buf, 0, &tmp);
if (ret != 0)
return ret;
inactive_check_disp = tmp;
return count;
}
static CLASS_ATTR_RW(sei_cnt);
static CLASS_ATTR_RW(alloc_sei_debug);
static CLASS_ATTR_RW(video_nr_base);
static CLASS_ATTR_RW(n_devs);
static CLASS_ATTR_RW(debug);
static CLASS_ATTR_RW(get_count);
static CLASS_ATTR_RW(put_count);
static CLASS_ATTR_RW(q_count);
static CLASS_ATTR_RW(dq_count);
static CLASS_ATTR_RW(cts_use_di);
static CLASS_ATTR_RW(render_use_dec);
static CLASS_ATTR_RW(dec_count);
static CLASS_ATTR_RW(vf_dump);
static CLASS_ATTR_RO(open_fd_count);
static CLASS_ATTR_RO(release_fd_count);
static CLASS_ATTR_RO(link_fd_count);
static CLASS_ATTR_RO(link_put_fd_count);
static CLASS_ATTR_RO(v4lvideo_version);
static CLASS_ATTR_RO(total_get_count);
static CLASS_ATTR_RO(total_put_count);
static CLASS_ATTR_RO(total_release_count);
static CLASS_ATTR_RW(print_flag);
static CLASS_ATTR_RW(cts_video_flag);
static CLASS_ATTR_RW(inactive_check_disp);
static struct attribute *v4lvideo_class_attrs[] = {
&class_attr_sei_cnt.attr,
&class_attr_alloc_sei_debug.attr,
&class_attr_video_nr_base.attr,
&class_attr_n_devs.attr,
&class_attr_debug.attr,
&class_attr_get_count.attr,
&class_attr_put_count.attr,
&class_attr_q_count.attr,
&class_attr_dq_count.attr,
&class_attr_cts_use_di.attr,
&class_attr_render_use_dec.attr,
&class_attr_dec_count.attr,
&class_attr_vf_dump.attr,
&class_attr_open_fd_count.attr,
&class_attr_release_fd_count.attr,
&class_attr_link_fd_count.attr,
&class_attr_link_put_fd_count.attr,
&class_attr_v4lvideo_version.attr,
&class_attr_total_get_count.attr,
&class_attr_total_put_count.attr,
&class_attr_total_release_count.attr,
&class_attr_print_flag.attr,
&class_attr_inactive_check_disp.attr,
&class_attr_cts_video_flag.attr,
NULL
};
ATTRIBUTE_GROUPS(v4lvideo_class);
static struct class v4lvideo_class = {
.name = "v4lvideo",
.class_groups = v4lvideo_class_groups,
};
static int v4lvideo_open(struct inode *inode, struct file *file)
{
return 0;
}
static int v4lvideo_release(struct inode *inode, struct file *file)
{
return 0;
}
static long v4lvideo_ioctl(struct file *file,
unsigned int cmd,
ulong arg)
{
long ret = 0;
void __user *argp = (void __user *)arg;
switch (cmd) {
case V4LVIDEO_IOCTL_ALLOC_ID:{
u32 v4lvideo_id = 0;
ret = v4lvideo_alloc_map(&v4lvideo_id);
if (ret != 0)
break;
put_user(v4lvideo_id, (u32 __user *)argp);
}
break;
case V4LVIDEO_IOCTL_FREE_ID:{
u32 v4lvideo_id;
get_user(v4lvideo_id, (u32 __user *)argp);
v4lvideo_release_map(v4lvideo_id);
}
break;
case V4LVIDEO_IOCTL_ALLOC_FD:{
u32 v4lvideo_fd = 0;
ret = v4lvideo_alloc_fd(&v4lvideo_fd);
if (ret != 0)
break;
put_user(v4lvideo_fd, (u32 __user *)argp);
}
break;
case V4LVIDEO_IOCTL_LINK_FD: {
int data[2];
int src_fd, dst_fd;
if (copy_from_user(data, argp, sizeof(data)) == 0) {
src_fd = data[0];
dst_fd = data[1];
ret = v4lvideo_fd_link(src_fd, dst_fd);
} else {
ret = -EFAULT;
}
}
break;
default:
return -EINVAL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long v4lvideo_compat_ioctl(struct file *file,
unsigned int cmd,
ulong arg)
{
long ret = 0;
ret = v4lvideo_ioctl(file, cmd, (ulong)compat_ptr(arg));
return ret;
}
#endif
static const struct file_operations v4lvideo_fops = {
.owner = THIS_MODULE,
.open = v4lvideo_open,
.release = v4lvideo_release,
.unlocked_ioctl = v4lvideo_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4lvideo_compat_ioctl,
#endif
.poll = NULL,
};
int __init v4lvideo_init(void)
{
int ret = -1, i;
struct device *devp;
mutex_init(&mutex_dec_count);
keeper_mgr_init();
ret = class_register(&v4lvideo_class);
if (ret < 0)
return ret;
ret = register_chrdev(V4LVIDEO_MAJOR, "v4lvideo", &v4lvideo_fops);
if (ret < 0) {
pr_err("Can't allocate major for v4lvideo device\n");
goto error1;
}
devp = device_create(&v4lvideo_class,
NULL,
MKDEV(V4LVIDEO_MAJOR, 0),
NULL,
V4LVIDEO_DEVICE_NAME);
if (IS_ERR(devp)) {
pr_err("failed to create v4lvideo device node\n");
ret = PTR_ERR(devp);
return ret;
}
if (n_devs <= 0)
n_devs = 1;
for (i = 0; i < n_devs; i++) {
ret = v4lvideo_create_instance(i);
if (ret) {
/* If some instantiations succeeded, keep driver */
if (i)
ret = 0;
break;
}
}
if (ret < 0) {
V4LVID_ERR("v4lvideo: error %d while loading driver\n", ret);
goto error1;
}
return 0;
error1:
unregister_chrdev(V4LVIDEO_MAJOR, "v4lvideo");
class_unregister(&v4lvideo_class);
return ret;
}
void __exit v4lvideo_exit(void)
{
v4lvideo_v4l2_release();
device_destroy(&v4lvideo_class, MKDEV(V4LVIDEO_MAJOR, 0));
unregister_chrdev(V4LVIDEO_MAJOR, V4LVIDEO_DEVICE_NAME);
class_unregister(&v4lvideo_class);
}
//MODULE_DESCRIPTION("Video Technology Magazine V4l Video Capture Board");
//MODULE_AUTHOR("Amlogic, Jintao Xu<jintao.xu@amlogic.com>");
//MODULE_LICENSE("Dual BSD/GPL");
//MODULE_VERSION(V4LVIDEO_VERSION);