1993 lines
66 KiB
C
1993 lines
66 KiB
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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Description:
|
|
*/
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/list.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/version.h>
|
|
#include <linux/kfifo.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/firmware.h>
|
|
|
|
#include <linux/reset.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/poll.h>
|
|
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/videobuf2-v4l2.h>
|
|
#include <media/videobuf2-core.h>
|
|
#include <media/videobuf2-dma-contig.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-fh.h>
|
|
#include <media/v4l2-mem2mem.h>
|
|
|
|
#include <linux/amlogic/media/codec_mm/codec_mm.h>
|
|
|
|
#include "tahoe.h"
|
|
#include "tahoe_register.h"
|
|
#include "tahoe_hw.h"
|
|
#include "tahoe_assist.h"
|
|
#include "tahoe_common.h"
|
|
|
|
#define DRIVER_NAME "tahoe"
|
|
#define DEVICE_NAME "tahoe"
|
|
#define CLASS_NAME "tahoe"
|
|
|
|
|
|
|
|
#define BITRATE_CHANGE 0x01
|
|
#define INTRA_PERIOD_CHANGE 0x02
|
|
#define FRAMERATE_CHANGE 0x03
|
|
|
|
|
|
#define ENC_STREAM_BUF_SIZE 0x100000
|
|
|
|
#define GET_PRI(fh) container_of(fh, struct tahoe_inst, v4l2_fh)
|
|
#define P_T_UL(p) ((unsigned long)(uintptr_t)(p))
|
|
#define ALIGN_32(x) ((((x) + 31) >> 5) << 5)
|
|
|
|
|
|
#define MC_SIZE (4096 * 1024)
|
|
|
|
static struct venc_drv_context_t s_tahoe_drv_context;
|
|
static s32 s_tahoe_major;
|
|
static s32 s_register_flag;
|
|
static struct device *tahoe_dev;
|
|
static struct platform_device *tahoe_pdev;
|
|
|
|
static s32 s_tahoe_open_ref_count;
|
|
static int s_tahoe_irq;
|
|
static bool s_tahoe_irq_requested;
|
|
|
|
static spinlock_t s_vpu_lock = __SPIN_LOCK_UNLOCKED(s_vpu_lock);
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 3, 13)
|
|
static DEFINE_SEMAPHORE(s_vpu_sem);
|
|
#else
|
|
static DEFINE_SEMAPHORE(s_vpu_sem, 1);
|
|
#endif
|
|
static struct list_head s_vbp_head = LIST_HEAD_INIT(s_vbp_head);
|
|
|
|
static bool use_reserve = false;
|
|
static u32 cma_cfg_size;
|
|
|
|
|
|
static struct tasklet_struct tahoe_tasklet;
|
|
|
|
struct vb2_queue vb2_q;
|
|
struct mutex q_lock;
|
|
struct mutex dev_lock;
|
|
static unsigned int video_nr = 90;
|
|
|
|
|
|
|
|
struct vb2_manage_st {
|
|
struct v4l2_m2m_buffer v4l2_m2m_buf;
|
|
};
|
|
|
|
struct vb2_manage_cap_st {
|
|
struct v4l2_m2m_buffer v4l2_m2m_buf;
|
|
uint64_t viraddr;
|
|
};
|
|
|
|
struct tahoe_fmt {
|
|
unsigned int v4l2_pix_fmt;
|
|
unsigned int max_width;
|
|
unsigned int min_width;
|
|
unsigned int max_height;
|
|
unsigned int min_height;
|
|
};
|
|
|
|
#define MAX_FMT_NUM 128
|
|
|
|
static const int fmt_list[MAX_FMT_NUM] = {
|
|
V4L2_PIX_FMT_YUV420,
|
|
V4L2_PIX_FMT_NV12,
|
|
V4L2_PIX_FMT_NV21,
|
|
V4L2_PIX_FMT_YUV420M,
|
|
V4L2_PIX_FMT_NV12M,
|
|
V4L2_PIX_FMT_NV21M,
|
|
};
|
|
|
|
|
|
static const int fmt_list_out[MAX_FMT_NUM] = {
|
|
V4L2_PIX_FMT_HEVC,
|
|
V4L2_PIX_FMT_H264
|
|
};
|
|
|
|
|
|
//__need_modify__
|
|
static void tahoe_isr_tasklet(ulong data)
|
|
{
|
|
#if 1
|
|
struct tahoe_inst *inst = NULL;
|
|
struct tahoe_device *dev = (struct tahoe_device *)data;
|
|
|
|
enc_pr(LOG_ALL, "[+]%s,data:%lx,open_count:%d,\n", __func__,data,dev->s_tahoe_drv_context.open_count);
|
|
|
|
for (uint8_t i = 0; i < MAX_INSTANCE; i++) {
|
|
enc_pr(LOG_ALL,"index:%d,inst_id:%d,instance addr:%lx\n",i,dev->cur_inst,P_T_UL(dev->instance[i]));
|
|
if (dev->instance[i] && (dev->instance[i]->inst_id == dev->cur_inst)) {
|
|
inst = dev->instance[i];
|
|
break;
|
|
}
|
|
|
|
if (i == (MAX_INSTANCE - 1)) {
|
|
enc_pr(LOG_ALL, "cannot find the instance,return\n");
|
|
return;
|
|
}
|
|
}
|
|
atomic_inc(&dev->s_tahoe_drv_context.request_ready);
|
|
inst->output_f(inst);
|
|
|
|
enc_pr(LOG_ALL, "[-]%s\n", __func__);
|
|
#endif
|
|
}
|
|
|
|
//__need_modify__
|
|
static irqreturn_t tahoe_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct tahoe_device *root_dev = (struct tahoe_device *)dev_id;
|
|
if (NULL == dev_id) {
|
|
enc_pr(LOG_ERROR,"irq_handler,receive null ptr!\n");
|
|
return IRQ_HANDLED;
|
|
}
|
|
struct venc_drv_context_t *dev = &root_dev->s_tahoe_drv_context;
|
|
if (NULL == dev) {
|
|
enc_pr(LOG_ERROR,"irq_handler,find null ptr!\n");
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
dev->interrupt_reason = tahoe_reg_read(root_dev,TAHOE_VRISC_IRQ_STAT);
|
|
enc_pr(LOG_DEBUG, "interrupt_reason 0x%lx\n", dev->interrupt_reason);
|
|
tahoe_reg_write(root_dev,TAHOE_VRISC_IRQ_STAT, ~dev->interrupt_reason);
|
|
if (dev->interrupt_reason & IRQ_PRELOAD_DONE) {
|
|
enc_pr(LOG_DEBUG, "preload irq received, preload done\n");
|
|
wake_up_interruptible(&dev->request_complete);
|
|
}
|
|
|
|
if (dev->interrupt_reason & IRQ_SCR_WRITE) {
|
|
if (CMD_DONE == tahoe_reg_read(root_dev,CMD_OPT_CODE)) {
|
|
enc_pr(LOG_DEBUG, "scr cmd done write irq received\n");
|
|
wake_up_interruptible(&dev->request_complete);
|
|
}
|
|
enc_pr(LOG_DEBUG, "scr write irq received\n");
|
|
}
|
|
|
|
if (dev->interrupt_reason & IRQ_FRM_DONE_FBC) {
|
|
root_dev->cur_inst = tahoe_reg_read(root_dev,OUTPUT_CHN_ID);
|
|
enc_pr(LOG_INFO, "frame done irq received,instance id:%d\n",root_dev->cur_inst);//0;
|
|
tasklet_schedule(&tahoe_tasklet);
|
|
}
|
|
|
|
enc_pr(LOG_ALL, "[-]%s\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
static int tahoe_vb2ops_queue_setup(struct vb2_queue *vq,
|
|
unsigned int *num_buffers,
|
|
unsigned int *num_planes,
|
|
unsigned int sizes[], struct device *alloc_devs[]) {
|
|
struct tahoe_inst *inst = vb2_get_drv_priv(vq);
|
|
int i = 0;
|
|
|
|
if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
(*num_planes) = inst->param.plane_num;
|
|
enc_pr(LOG_INFO,"plane_num:%d\n",inst->param.plane_num);
|
|
for (i = 0;i < inst->param.plane_num;i++) {
|
|
sizes[i] = inst->param.imagesize[i];
|
|
enc_pr(LOG_INFO,"size[%d]:%d\n",i,sizes[i]);
|
|
alloc_devs[i] = v4l_get_dev_from_codec_mm();
|
|
}
|
|
}
|
|
else {
|
|
(*num_planes) = 1;
|
|
alloc_devs[0] = v4l_get_dev_from_codec_mm();
|
|
sizes[0] = 1024 * 1024 * 5;
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"tahoe_vb2ops_queue_setup done,num_plane:%d,size:%d\n",inst->param.plane_num,sizes[0]);
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_vb2ops_buffer_prepare(struct vb2_buffer *vb) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void tahoe_vb2ops_buffer_queue(struct vb2_buffer *vb) {
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
struct tahoe_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
|
|
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
static int tahoe_vb2ops_buf_init(struct vb2_buffer *vb) {
|
|
enc_pr(LOG_INFO,"enter function tahoe_vb2ops_buf_init,index:%d,size:%d\n",vb->index,vb->planes[0].length);
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_vb2ops_start_streaming(struct vb2_queue *vq, unsigned int count) {
|
|
enc_pr(LOG_INFO,"tahoe_vb2ops_start_streaming,count:%d\n",count);
|
|
#if 0
|
|
struct tahoe_inst *inst = vb2_get_drv_priv(vq);
|
|
create_channel(inst->dev,inst);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
//multiinstance,need not stop hw
|
|
static void tahoe_vb2ops_stop_streaming(struct vb2_queue *vq) {
|
|
enc_pr(LOG_INFO,"enter function tahoe_vb2ops_stop_streaming\n");
|
|
struct tahoe_inst *inst = vb2_get_drv_priv(vq);
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
struct vb2_v4l2_buffer *buf;
|
|
uint8_t loop_flag = 1;
|
|
|
|
enc_pr(LOG_INFO,"stop_streaming,wait for process end!,hw_proc_count:%d,loop_flag:%d\n",inst->hw_proc_count,loop_flag);
|
|
while (loop_flag) {
|
|
if (inst->hw_proc_count == 0) {
|
|
loop_flag = 0;
|
|
}
|
|
}
|
|
enc_pr(LOG_INFO,"process end!\n");
|
|
|
|
if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
|
|
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
|
|
}
|
|
}
|
|
else {
|
|
while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
|
|
vb2_set_plane_payload(&buf->vb2_buf, 0, 0);
|
|
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
|
|
}
|
|
v4l2_m2m_clear_state(m2m_ctx);
|
|
}
|
|
|
|
inst->inst_init_flag = 0; //reset uninit state
|
|
return;
|
|
}
|
|
|
|
|
|
static struct vb2_ops tahoe_vb2ops = {
|
|
.queue_setup = tahoe_vb2ops_queue_setup,
|
|
.buf_prepare = tahoe_vb2ops_buffer_prepare,
|
|
.buf_queue = tahoe_vb2ops_buffer_queue,
|
|
.buf_init = tahoe_vb2ops_buf_init,
|
|
.start_streaming = tahoe_vb2ops_start_streaming,
|
|
.stop_streaming = tahoe_vb2ops_stop_streaming,
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
};
|
|
|
|
|
|
static int vb2_queue_tahoe_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
|
|
{
|
|
struct tahoe_inst *inst = priv;
|
|
int ret;
|
|
enc_pr(LOG_INFO, "vb2_queue_tahoe_init");
|
|
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
|
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
src_vq->mem_ops = &vb2_dma_contig_memops;
|
|
src_vq->ops = &tahoe_vb2ops;
|
|
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
src_vq->buf_struct_size = sizeof(struct vb2_manage_st);
|
|
src_vq->drv_priv = inst;
|
|
src_vq->lock = &inst->dev->dev_lock;
|
|
src_vq->dev = inst->dev->v4l2_dev.dev;
|
|
ret = vb2_queue_init(src_vq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
|
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
dst_vq->mem_ops = &vb2_dma_contig_memops;
|
|
dst_vq->ops = &tahoe_vb2ops;
|
|
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
dst_vq->buf_struct_size = sizeof(struct vb2_manage_cap_st);
|
|
dst_vq->drv_priv = inst;
|
|
dst_vq->lock = &inst->dev->dev_lock;
|
|
dst_vq->dev = inst->dev->v4l2_dev.dev;
|
|
ret = vb2_queue_init(dst_vq);
|
|
if (ret)
|
|
return ret;
|
|
enc_pr(LOG_INFO, "vb2_queue_tahoe_init");
|
|
return ret;
|
|
}
|
|
|
|
|
|
int get_bsbframe(struct tahoe_device *dev,struct tahoe_inst *inst , ulong *es_virt, int *es_length,uint32_t *frame_type)
|
|
{
|
|
unsigned int esaddr_offset = 0;
|
|
unsigned long esPhyAddr;
|
|
int eslength;
|
|
unsigned long espVirAddr = 0;
|
|
unsigned long slice_header_length = 0;
|
|
|
|
slice_header_length = tahoe_reg_read(dev, TAHOE_EC_DMA0) & 0x3F;
|
|
esPhyAddr = tahoe_reg_read(dev, TAHOE_EC_DMA0) - slice_header_length;
|
|
esPhyAddr |= (((ulong)(tahoe_reg_read(dev, TAHOE_EC_DMA6) & 0x03)) << 32);
|
|
eslength = tahoe_reg_read(dev, TAHOE_EC_LENGTH);
|
|
(*frame_type) = tahoe_reg_read(dev, OUTPUT_FRAME_TYPE);
|
|
esaddr_offset = esPhyAddr - inst->s_video_memory.phys_addr;
|
|
espVirAddr = inst->s_video_memory.virt_addr + esaddr_offset;
|
|
enc_pr(LOG_INFO,"[%s][%d]offset:0x%lx\n", __func__, __LINE__, slice_header_length);
|
|
enc_pr(LOG_INFO,"[%s][%d]esPhyAddr:0x%lx\n", __func__, __LINE__, esPhyAddr);
|
|
enc_pr(LOG_INFO,"[%s][%d]esaddr_offset:0x%x\n", __func__, __LINE__, esaddr_offset);
|
|
enc_pr(LOG_INFO,"[%s][%d]espVirAddr:0x%lx\n", __func__, __LINE__, espVirAddr);
|
|
enc_pr(LOG_INFO,"[%s][%d]eslength:%d\n", __func__, __LINE__, eslength);
|
|
(*es_length) = eslength;
|
|
(*es_virt) = (ulong)espVirAddr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void header_separate(struct tahoe_inst *inst,u64 timestamp) {
|
|
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
int header_len = 0;
|
|
struct vb2_v4l2_buffer *dst_buf = NULL;
|
|
struct vb2_manage_cap_st *dst_man_buf = NULL;
|
|
|
|
if ((inst->param.poc == 0) && inst->param.separate_header) {
|
|
enc_pr(LOG_INFO,"separate send header\n");
|
|
dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
|
|
if (!dst_buf) {
|
|
dev_warn(inst->dev->dev, "No bitstream buffer.");
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
return;
|
|
}
|
|
|
|
dst_man_buf = (struct vb2_manage_cap_st *)dst_buf;
|
|
|
|
int index = dst_buf->vb2_buf.index;
|
|
ulong es_phy_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
|
|
enc_pr(LOG_INFO,"es_phy_addr:%lx,index:%d",es_phy_addr,index);
|
|
ulong es_vir_addr = dst_man_buf->viraddr;
|
|
if (0 == es_vir_addr) {
|
|
enc_pr(LOG_INFO,"virtual addr is null!\n");
|
|
return;
|
|
}
|
|
|
|
if (inst->param.codec_type == CODEC_ID_H265) {
|
|
inst->vps.vir_addr = (ulong)memremap(inst->vps.phy_addr_act, inst->vps.size, MEMREMAP_WB);
|
|
inst->sps.vir_addr = (ulong)memremap(inst->sps.phy_addr_act, inst->sps.size, MEMREMAP_WB);
|
|
inst->pps.vir_addr = (ulong)memremap(inst->pps.phy_addr_act, inst->pps.size, MEMREMAP_WB);
|
|
if (es_vir_addr && inst->vps.vir_addr & inst->sps.vir_addr & inst->pps.vir_addr) {
|
|
memcpy((char *)es_vir_addr,(char *)inst->vps.vir_addr,inst->vps.size);
|
|
memcpy(((char *)es_vir_addr + inst->vps.size),(char *)inst->sps.vir_addr,inst->sps.size);
|
|
memcpy(((char *)es_vir_addr + inst->vps.size + inst->sps.size),(char *)inst->pps.vir_addr,inst->pps.size);
|
|
header_len = inst->vps.size + inst->sps.size + inst->pps.size;
|
|
}
|
|
} else if (inst->param.codec_type == CODEC_ID_H264) {
|
|
inst->sps.vir_addr = (ulong)memremap(inst->sps.phy_addr_act, inst->sps.size, MEMREMAP_WB);
|
|
inst->pps.vir_addr = (ulong)memremap(inst->pps.phy_addr_act, inst->pps.size, MEMREMAP_WB);
|
|
if (es_vir_addr & inst->sps.vir_addr & inst->pps.vir_addr) {
|
|
memcpy((char *)es_vir_addr,(char *)inst->sps.vir_addr,inst->sps.size);
|
|
memcpy(((char *)es_vir_addr + inst->sps.size),(char *)inst->pps.vir_addr,inst->pps.size);
|
|
header_len = inst->sps.size + inst->pps.size;
|
|
}
|
|
}
|
|
enc_pr(LOG_INFO,"output proc,es_vir_addr:%lx,map length:%d,es_phy_addr:%lx\n",es_vir_addr,dst_buf->vb2_buf.planes[0].length,es_phy_addr);
|
|
|
|
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, (header_len));
|
|
dst_buf->vb2_buf.timestamp = timestamp;
|
|
dst_buf->field = V4L2_FIELD_NONE;
|
|
|
|
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
|
|
enc_pr(LOG_INFO,"dst buf done!!,dst timestamp:%llx\n",dst_buf->vb2_buf.timestamp);
|
|
}
|
|
}
|
|
|
|
|
|
void output_proc(struct tahoe_inst *inst) {
|
|
enc_pr(LOG_INFO,"output_proc enter,inst:%lx\n",(ulong)inst);
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
int header_len = 0;
|
|
bool is_idr = false;
|
|
struct vb2_v4l2_buffer *src_buf = NULL;
|
|
struct vb2_v4l2_buffer *dst_buf = NULL;
|
|
struct vb2_manage_cap_st *dst_man_buf = NULL;
|
|
ulong es_addr = 0;
|
|
int es_len = 1024;
|
|
u64 timestamp = 0;
|
|
bool is_daring_last_src = false;
|
|
uint32_t frame_type = 0;
|
|
|
|
get_bsbframe(inst->dev,inst, &es_addr, &es_len, &frame_type);
|
|
|
|
if ((0 == frame_type) || (1 == frame_type)) {
|
|
is_idr = 1;
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"output_proc src_index:%d,frame_type:%d,is_idr:%d\n",inst->param.src_buf_index,frame_type,is_idr);
|
|
if (inst->param.src_buf_index >= 0) {
|
|
struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
|
|
inst->param.src_buf_index);
|
|
if (vb->state != VB2_BUF_STATE_ACTIVE)
|
|
enc_pr(LOG_ERROR,
|
|
"%s: encoded buffer (%d) was not in ready queue %i.",__func__, inst->param.src_buf_index, vb->state);
|
|
else
|
|
src_buf = to_vb2_v4l2_buffer(vb);
|
|
|
|
if (src_buf) {
|
|
timestamp = src_buf->vb2_buf.timestamp;
|
|
is_daring_last_src = v4l2_m2m_is_last_draining_src_buf(m2m_ctx,src_buf);
|
|
enc_pr(LOG_INFO,"remove src buf from vb2,index:%d\n",src_buf->vb2_buf.index);
|
|
v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
|
|
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
|
|
enc_pr(LOG_INFO,"src buf done!!,timestamp:%llx,last draining:%d\n",timestamp,is_daring_last_src);
|
|
} else {
|
|
enc_pr(LOG_INFO,"%s: no source buffer with index: %d found\n",
|
|
__func__, inst->param.src_buf_index);
|
|
}
|
|
}
|
|
else {
|
|
if (m2m_ctx->is_draining) { //no src buffer,draning mode
|
|
is_daring_last_src = true;
|
|
enc_pr(LOG_INFO,"no src buffer,draning mode,send last!\n");
|
|
}
|
|
}
|
|
|
|
header_separate(inst,timestamp);
|
|
|
|
dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
|
|
if (!dst_buf) {
|
|
dev_warn(inst->dev->dev, "No bitstream buffer.");
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
return;
|
|
}
|
|
enc_pr(LOG_INFO,"dst_buf flag before:%x\n",dst_buf->flags);
|
|
|
|
dst_man_buf = (struct vb2_manage_cap_st *)dst_buf;
|
|
int index = dst_buf->vb2_buf.index;
|
|
ulong es_phy_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
|
|
enc_pr(LOG_INFO,"es_phy_addr:%lx,index:%d",es_phy_addr,index);
|
|
ulong es_vir_addr = dst_man_buf->viraddr;
|
|
if (0 == es_vir_addr) {
|
|
enc_pr(LOG_INFO,"virtual addr is null!\n");
|
|
return;
|
|
}
|
|
|
|
if (is_idr && (!inst->param.separate_header)) {
|
|
if (inst->param.codec_type == CODEC_ID_H265) {
|
|
inst->vps.vir_addr = (ulong)memremap(inst->vps.phy_addr_act, inst->vps.size, MEMREMAP_WB);
|
|
inst->sps.vir_addr = (ulong)memremap(inst->sps.phy_addr_act, inst->sps.size, MEMREMAP_WB);
|
|
inst->pps.vir_addr = (ulong)memremap(inst->pps.phy_addr_act, inst->pps.size, MEMREMAP_WB);
|
|
if (es_vir_addr && inst->vps.vir_addr & inst->sps.vir_addr & inst->pps.vir_addr) {
|
|
memcpy((char *)es_vir_addr,(char *)inst->vps.vir_addr,inst->vps.size);
|
|
memcpy(((char *)es_vir_addr + inst->vps.size),(char *)inst->sps.vir_addr,inst->sps.size);
|
|
memcpy(((char *)es_vir_addr + inst->vps.size + inst->sps.size),(char *)inst->pps.vir_addr,inst->pps.size);
|
|
header_len = inst->vps.size + inst->sps.size + inst->pps.size;
|
|
}
|
|
} else if (inst->param.codec_type == CODEC_ID_H264) {
|
|
inst->sps.vir_addr = (ulong)memremap(inst->sps.phy_addr_act, inst->sps.size, MEMREMAP_WB);
|
|
inst->pps.vir_addr = (ulong)memremap(inst->pps.phy_addr_act, inst->pps.size, MEMREMAP_WB);
|
|
if (es_vir_addr & inst->sps.vir_addr & inst->pps.vir_addr) {
|
|
memcpy((char *)es_vir_addr,(char *)inst->sps.vir_addr,inst->sps.size);
|
|
memcpy(((char *)es_vir_addr + inst->sps.size),(char *)inst->pps.vir_addr,inst->pps.size);
|
|
header_len = inst->sps.size + inst->pps.size;
|
|
}
|
|
}
|
|
}
|
|
enc_pr(LOG_INFO,"output proc,es_vir_addr:%lx,map length:%d,es_phy_addr:%lx\n",es_vir_addr,dst_buf->vb2_buf.planes[0].length,es_phy_addr);
|
|
|
|
if (es_vir_addr) {
|
|
memcpy(((char*)es_vir_addr + header_len),(char *)es_addr,es_len);
|
|
enc_pr(LOG_INFO,"copy es data to vb2,len:%d\n",es_len);
|
|
}
|
|
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, (es_len + header_len));
|
|
dst_buf->vb2_buf.timestamp = timestamp;
|
|
dst_buf->field = V4L2_FIELD_NONE;
|
|
if (is_idr) {
|
|
dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
|
|
}
|
|
else {
|
|
dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
|
|
}
|
|
|
|
if (m2m_ctx->is_draining && is_daring_last_src) { //draining over
|
|
enc_pr(LOG_INFO,"daining last buffer done!\n");
|
|
|
|
static const struct v4l2_event tahoe_event_eos = {
|
|
.type = V4L2_EVENT_EOS
|
|
};
|
|
|
|
if (!WARN_ON(!dst_buf)) {
|
|
dst_buf->field = V4L2_FIELD_NONE;
|
|
v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf);
|
|
}
|
|
|
|
v4l2_event_queue_fh(&inst->v4l2_fh, &tahoe_event_eos);
|
|
}
|
|
else {
|
|
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"dst buf done!!,dst timestamp:%llx,flag:%x\n",dst_buf->vb2_buf.timestamp,dst_buf->flags);
|
|
|
|
inst->last_is_done = true;
|
|
inst->hw_proc_count--;
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
enc_pr(LOG_INFO, "output_proc done,job finish,hw_proc_count:%d\n",inst->hw_proc_count);
|
|
return;
|
|
}
|
|
|
|
static int tahoe_dynamic_change(struct tahoe_param *encode_param, int index, int value)
|
|
{
|
|
switch (index) {
|
|
case BITRATE_CHANGE: {
|
|
encode_param->bitrate = value;
|
|
encode_param->cfg_update = 1;
|
|
break;
|
|
}
|
|
case INTRA_PERIOD_CHANGE: {
|
|
encode_param->gop = value;
|
|
encode_param->cfg_update = 1;
|
|
break;
|
|
}
|
|
case FRAMERATE_CHANGE: {
|
|
encode_param->fps = value;
|
|
encode_param->cfg_update = 1;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
enc_pr(LOG_DEBUG,"tahoe_dynamic_change index:%d value:%d\n",index,value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint32_t toTahoeProfile(uint32_t profile) {
|
|
uint32_t tprofile = 0;
|
|
switch (profile) {
|
|
case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
|
|
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: {
|
|
tprofile = PROFILE_BASELINE;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: {
|
|
tprofile = PROFILE_MAIN;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: {
|
|
tprofile = PROFILE_HIGH;
|
|
break;
|
|
}
|
|
default: {
|
|
tprofile = PROFILE_HIGH;
|
|
break;
|
|
}
|
|
}
|
|
return tprofile;
|
|
}
|
|
|
|
uint32_t toTahoeAvcLevel(uint32_t level) {
|
|
uint32_t tlevel = 0;
|
|
switch (level) {
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: {
|
|
tlevel = 9;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_1B: {
|
|
tlevel = 10;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: {
|
|
tlevel = 11;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: {
|
|
tlevel = 12;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: {
|
|
tlevel = 13;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: {
|
|
tlevel = 20;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: {
|
|
tlevel = 21;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: {
|
|
tlevel = 22;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: {
|
|
tlevel = 30;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: {
|
|
tlevel = 31;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: {
|
|
tlevel = 32;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: {
|
|
tlevel = 40;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: {
|
|
tlevel = 41;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: {
|
|
tlevel = 42;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: {
|
|
tlevel = 50;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: {
|
|
tlevel = 51;
|
|
break;
|
|
}
|
|
default: {
|
|
tlevel = 40;
|
|
break;
|
|
}
|
|
}
|
|
return tlevel;
|
|
}
|
|
|
|
|
|
uint32_t toTahoeHevcLevel(uint32_t level) {
|
|
uint32_t tlevel = 0;
|
|
switch (level) {
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: {
|
|
tlevel = 10;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: {
|
|
tlevel = 20;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: {
|
|
tlevel = 21;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: {
|
|
tlevel = 30;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: {
|
|
tlevel = 31;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: {
|
|
tlevel = 40;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: {
|
|
tlevel = 41;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: {
|
|
tlevel = 50;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: {
|
|
tlevel = 51;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: {
|
|
tlevel = 60;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: {
|
|
tlevel = 61;
|
|
break;
|
|
}
|
|
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: {
|
|
tlevel = 62;
|
|
break;
|
|
}
|
|
default: {
|
|
tlevel = 51;
|
|
break;
|
|
}
|
|
}
|
|
return tlevel;
|
|
}
|
|
|
|
static int tahoe_enc_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct tahoe_inst *inst = container_of(ctrl->handler, struct tahoe_inst, v4l2_ctrl_hdl);
|
|
|
|
enc_pr(LOG_DEBUG,"%s: ctrl name: %s ---- ctrl value: %d\n", __func__, ctrl->name, ctrl->val);
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE:
|
|
if ((ctrl->val != inst->param.bitrate) && (ctrl->val != 0)) {
|
|
tahoe_dynamic_change(&inst->param,BITRATE_CHANGE,ctrl->val);
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
if (V4L2_MPEG_VIDEO_BITRATE_MODE_VBR == ctrl->val) {
|
|
inst->param.rc_mode = 2;
|
|
}
|
|
else {
|
|
inst->param.rc_mode = 1;
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
|
|
inst->param.force_idr = 1;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
|
|
case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD:
|
|
if ((ctrl->val != inst->param.gop) && (ctrl->val != 0)) {
|
|
tahoe_dynamic_change(&inst->param,INTRA_PERIOD_CHANGE,ctrl->val);
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
|
|
inst->param.profile = toTahoeProfile(ctrl->val);
|
|
enc_pr(LOG_DEBUG,"avc profile setting:%d\n",inst->param.profile);
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
|
|
if ((ctrl->val != inst->param.profile) && (ctrl->val != 0)) {
|
|
inst->param.profile = 0;//ctrl->val;
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
|
|
if ((ctrl->val != inst->param.tier) && (ctrl->val != 0)) {
|
|
inst->param.tier = 0;//ctrl->val;
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_LEVEL: {
|
|
inst->param.level = toTahoeAvcLevel(ctrl->val);
|
|
enc_pr(LOG_DEBUG,"avc level setting:%d\n",inst->param.level);
|
|
break;
|
|
}
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
|
|
inst->param.level = toTahoeHevcLevel(ctrl->val);
|
|
enc_pr(LOG_DEBUG,"hevc level setting:%d\n",inst->param.level);
|
|
break;
|
|
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
|
|
if (V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE == ctrl->val) {
|
|
inst->param.separate_header = 1;
|
|
}
|
|
else {
|
|
inst->param.separate_header = 0;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_ctrl_ops tahoe_enc_ctrl_ops = {
|
|
.s_ctrl = tahoe_enc_s_ctrl,
|
|
};
|
|
|
|
|
|
static void tahoe_init_param(struct tahoe_param *param) {
|
|
if (NULL == param) {
|
|
enc_pr(LOG_ERROR, "tahoe_init_param invalid param! \n");
|
|
return;
|
|
}
|
|
param->poc = 0;
|
|
param->bitrate = 2000000;
|
|
param->gop = 30;
|
|
param->fps = 30;
|
|
param->rc_mode = 1; //cbr
|
|
param->init_qp = 30;
|
|
param->fix_qp_I = 0;
|
|
param->fix_qp_P = 0;
|
|
param->golden_match_flag = 0;
|
|
param->separate_header = 0;
|
|
}
|
|
|
|
|
|
|
|
static int tahoe_v4l_open(struct file *filp) {
|
|
struct tahoe_device *dev = video_drvdata(filp);
|
|
struct video_device *vdev = video_devdata(filp);
|
|
struct tahoe_inst *inst_info = NULL;
|
|
struct v4l2_ctrl_handler *v4l2_ctrl_hdl;
|
|
bool first_open = false;
|
|
char *virt_ptr = NULL;
|
|
s32 r = 0;
|
|
|
|
enc_pr(LOG_ERROR, "[+] %s\n", __func__);
|
|
inst_info = kzalloc(sizeof(*inst_info), GFP_KERNEL);
|
|
if (!inst_info) {
|
|
enc_pr(LOG_ERROR, "alloc inst_info failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(inst_info, 0, sizeof(*inst_info));
|
|
inst_info->output_f = output_proc;
|
|
|
|
v4l2_ctrl_hdl = &inst_info->v4l2_ctrl_hdl;
|
|
inst_info->dev = dev;
|
|
v4l2_fh_init(&inst_info->v4l2_fh,vdev);
|
|
filp->private_data = &inst_info->v4l2_fh;
|
|
|
|
v4l2_fh_add(&inst_info->v4l2_fh);
|
|
inst_info->v4l2_m2m_dev = inst_info->dev->tahoe_m2m_dev;
|
|
inst_info->v4l2_fh.m2m_ctx =
|
|
v4l2_m2m_ctx_init(inst_info->v4l2_m2m_dev, inst_info, vb2_queue_tahoe_init);
|
|
|
|
v4l2_m2m_set_src_buffered(inst_info->v4l2_fh.m2m_ctx, true);
|
|
|
|
v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50);
|
|
if (v4l2_ctrl_hdl->error) {
|
|
enc_pr(LOG_ERROR,"v4l2_ctrl_hdl error!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_BITRATE,
|
|
0, 700000000, 1, 0);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_GOP_SIZE,
|
|
0, 2047, 1, 0);
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
|
|
0, 2047, 1, 0);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_H264_PROFILE,
|
|
V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0,
|
|
V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
|
|
V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0,
|
|
V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
|
|
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0,
|
|
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_TIER,
|
|
V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, 0,
|
|
V4L2_MPEG_VIDEO_HEVC_TIER_MAIN);
|
|
v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
|
|
V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, 1,
|
|
V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1);
|
|
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD,
|
|
0, 2047, 1, 0);
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1);
|
|
|
|
v4l2_ctrl_new_std_menu(
|
|
v4l2_ctrl_hdl,
|
|
&tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_HEADER_MODE,
|
|
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
|
|
0,
|
|
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME
|
|
);
|
|
|
|
v4l2_ctrl_new_std(v4l2_ctrl_hdl,
|
|
&tahoe_enc_ctrl_ops,
|
|
V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
|
|
0, 0, 0, 0);
|
|
|
|
inst_info->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
|
|
vdev->ctrl_handler = v4l2_ctrl_hdl;
|
|
v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
|
|
|
|
spin_lock(&s_vpu_lock);
|
|
inst_info->inst_id = dev->s_tahoe_drv_context.open_count;
|
|
inst_info->param.id = inst_info->inst_id;
|
|
dev->instance[dev->s_tahoe_drv_context.open_count] = inst_info;
|
|
enc_pr(LOG_DEBUG, "open_count:%d,inst_info:%lx",dev->s_tahoe_drv_context.open_count,P_T_UL(inst_info));
|
|
dev->s_tahoe_drv_context.open_count++;
|
|
if (dev->s_tahoe_drv_context.open_count == 1) {
|
|
first_open = true;
|
|
init_waitqueue_head(&dev->s_tahoe_drv_context.request_complete);
|
|
}
|
|
atomic_set(&dev->s_tahoe_drv_context.request_ready, 0);
|
|
spin_unlock(&s_vpu_lock);
|
|
enc_pr(LOG_DEBUG, "first_open:%d,use_reserve:%d,open_count:%d,instance id:%d",first_open,use_reserve,dev->s_tahoe_drv_context.open_count,inst_info->inst_id);
|
|
if (!use_reserve) {
|
|
#ifdef CONFIG_CMA
|
|
inst_info->s_video_memory.size = cma_cfg_size;
|
|
inst_info->s_video_memory.phys_addr = codec_mm_alloc_for_dma(DEVICE_NAME, cma_cfg_size >> PAGE_SHIFT, 0, 0);
|
|
|
|
if (inst_info->s_video_memory.phys_addr) {
|
|
inst_info->s_video_memory.virt_addr = (unsigned long)codec_mm_vmap_noncache(inst_info->s_video_memory.phys_addr, inst_info->s_video_memory.size);
|
|
enc_pr(LOG_ERROR, "allocating phys 0x%llx, ", inst_info->s_video_memory.phys_addr);
|
|
enc_pr(LOG_ERROR, "virt addr 0x%llx, size %dk\n", inst_info->s_video_memory.virt_addr, inst_info->s_video_memory.size >> 10);
|
|
if (0 == inst_info->s_video_memory.virt_addr) {
|
|
codec_mm_free_for_dma(DEVICE_NAME, inst_info->s_video_memory.phys_addr);
|
|
enc_pr(LOG_ERROR,"virtual addr is NULL\n");
|
|
r = -ENOMEM;
|
|
}
|
|
} else {
|
|
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%llx\n", DEVICE_NAME, inst_info->s_video_memory.phys_addr);
|
|
|
|
if (inst_info->s_video_memory.phys_addr)
|
|
codec_mm_free_for_dma(DEVICE_NAME, inst_info->s_video_memory.phys_addr);
|
|
|
|
inst_info->s_video_memory.phys_addr = 0;
|
|
r = -ENOMEM;
|
|
}
|
|
|
|
inst_info->vps.alloc_size = ENC_STREAM_BUF_SIZE;
|
|
inst_info->sps.alloc_size = ENC_STREAM_BUF_SIZE;
|
|
inst_info->pps.alloc_size = ENC_STREAM_BUF_SIZE;
|
|
|
|
inst_info->vps.phy_addr_alloc = codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(inst_info->vps.alloc_size) >> PAGE_SHIFT, 0, 0);
|
|
inst_info->sps.phy_addr_alloc = codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(inst_info->sps.alloc_size) >> PAGE_SHIFT, 0, 0);
|
|
inst_info->pps.phy_addr_alloc = codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(inst_info->pps.alloc_size) >> PAGE_SHIFT, 0, 0);
|
|
if (inst_info->vps.phy_addr_alloc && inst_info->sps.phy_addr_alloc && inst_info->pps.phy_addr_alloc) {
|
|
enc_pr(LOG_INFO,"alloc header memory success %llx %llx %llx\n",inst_info->vps.phy_addr_alloc
|
|
,inst_info->sps.phy_addr_alloc
|
|
,inst_info->pps.phy_addr_alloc);
|
|
}
|
|
else {
|
|
enc_pr(LOG_ERROR,"alloc header memory failed\n");
|
|
inst_info->vps.phy_addr_alloc = 0;
|
|
inst_info->sps.phy_addr_alloc = 0;
|
|
inst_info->pps.phy_addr_alloc = 0;
|
|
r = -ENOMEM;
|
|
}
|
|
#else
|
|
enc_pr(LOG_ERROR, "No CMA and reserved memory for tahoe!!!\n");
|
|
r = -ENOMEM;
|
|
#endif
|
|
}
|
|
|
|
if (first_open && !use_reserve) {
|
|
#ifdef CONFIG_CMA
|
|
dev->fw_buffer.size = MC_SIZE;
|
|
dev->fw_buffer.phys_addr = codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(dev->fw_buffer.size) >> PAGE_SHIFT, 0, 0);
|
|
|
|
if (dev->fw_buffer.phys_addr) {
|
|
enc_pr(LOG_ERROR, "allocating fw phys 0x%llx,size:%d \n", dev->fw_buffer.phys_addr,dev->fw_buffer.size);
|
|
//virt_ptr = ioremap(dev->fw_buffer.phys_addr, dev->fw_buffer.size);//memremap(dev->fw_buffer.phys_addr, dev->fw_buffer.size, MEMREMAP_WB);
|
|
virt_ptr = codec_mm_vmap_noncache(dev->fw_buffer.phys_addr, dev->fw_buffer.size);
|
|
dev->fw_buffer.virt_addr = P_T_UL(virt_ptr);
|
|
dev->fw_buffer.base = dev->fw_buffer.phys_addr;
|
|
enc_pr(LOG_DEBUG, "virt addr 0x%llx, size %dk\n", dev->fw_buffer.virt_addr, dev->fw_buffer.size >> 10);
|
|
if (NULL == virt_ptr) {
|
|
codec_mm_free_for_dma(DEVICE_NAME, dev->fw_buffer.phys_addr);
|
|
enc_pr(LOG_ERROR, "fw memory mmap failed,vir addr is NULL\n");
|
|
r = -ENOMEM;
|
|
}
|
|
} else {
|
|
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%llx\n", DEVICE_NAME, dev->fw_buffer.phys_addr);
|
|
|
|
if (dev->fw_buffer.phys_addr)
|
|
codec_mm_free_for_dma(DEVICE_NAME, dev->fw_buffer.phys_addr);
|
|
|
|
dev->fw_buffer.phys_addr = 0;
|
|
|
|
r = -ENOMEM;
|
|
}
|
|
#else
|
|
enc_pr(LOG_ERROR, "No CMA and reserved memory for tahoe!!!\n");
|
|
r = -ENOMEM;
|
|
#endif
|
|
} else if (!inst_info->s_video_memory.phys_addr) {
|
|
enc_pr(LOG_ERROR, "tahoe memory is not malloced yet wait & retry!\n");
|
|
r = -EBUSY;
|
|
}
|
|
if (first_open) {
|
|
/* enable tahoe clks and power*/
|
|
tahoe_clk_enable(&dev->s_tahoe_clks, tahoe_pdev);
|
|
tahoe_top_sw_reset(dev);
|
|
|
|
if ((dev->s_tahoe_irq >= 0) && (dev->s_tahoe_irq_requested == false)) {
|
|
s32 err;
|
|
|
|
err = request_irq(dev->s_tahoe_irq, tahoe_irq_handler, 0, "tahoe_irq0", (void *)(dev));
|
|
|
|
if (err) {
|
|
enc_pr(LOG_ERROR, "Failed to register irq handler\n");
|
|
spin_lock(&s_vpu_lock);
|
|
s_tahoe_drv_context.open_count--;
|
|
spin_unlock(&s_vpu_lock);
|
|
return -EFAULT;
|
|
}
|
|
|
|
dev->s_tahoe_irq_requested = true;
|
|
}
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"reg base memory:%llx,first_open:%d\n",dev->s_tahoe_register.virt_addr,first_open);
|
|
|
|
if (r != 0) {
|
|
spin_lock(&s_vpu_lock);
|
|
dev->s_tahoe_drv_context.open_count--;
|
|
spin_unlock(&s_vpu_lock);
|
|
}
|
|
|
|
if (first_open) {
|
|
tahoe_load_fw(dev,"encoder/fw.bin");
|
|
tahoe_boot(dev);
|
|
}
|
|
|
|
tahoe_init_param(&inst_info->param);
|
|
|
|
for (int i = 0; i < (dev->s_tahoe_drv_context.open_count - 1); i++) {
|
|
enc_pr(LOG_INFO,"instance %d,value:%lx,dev:%lx\n",i,P_T_UL(dev->instance[i]),P_T_UL(dev));
|
|
}
|
|
|
|
enc_pr(LOG_ERROR, "[-] %s, ret: %d\n", __func__, r);
|
|
return r;
|
|
}
|
|
|
|
|
|
static int tahoe_v4l_release(struct file *filp) {
|
|
struct tahoe_device *dev = video_drvdata(filp);
|
|
struct tahoe_inst *pinst = GET_PRI(filp->private_data);
|
|
dev->s_tahoe_drv_context.open_count--;
|
|
|
|
enc_pr(LOG_ERROR,"tahoe_v4l_release +\n");
|
|
|
|
if (pinst->s_video_memory.phys_addr && !use_reserve) {
|
|
enc_pr(LOG_DEBUG,
|
|
"tahoe_release, s_video_memory 0x%llx\n",
|
|
pinst->s_video_memory.phys_addr);
|
|
codec_mm_free_for_dma(
|
|
DEVICE_NAME,
|
|
pinst->s_video_memory.phys_addr);
|
|
codec_mm_unmap_phyaddr(
|
|
(unsigned char *)pinst->s_video_memory.virt_addr);
|
|
memset(&pinst->s_video_memory,
|
|
0, sizeof(struct venc_drv_buffer_t));
|
|
}
|
|
|
|
if (pinst->sps.phy_addr_alloc && !use_reserve) {
|
|
enc_pr(LOG_DEBUG,
|
|
"tahoe_release, sps 0x%llx\n",
|
|
pinst->sps.phy_addr_alloc);
|
|
codec_mm_free_for_dma(
|
|
DEVICE_NAME,
|
|
pinst->sps.phy_addr_alloc);
|
|
if (pinst->sps.vir_addr) {
|
|
memunmap((void *)pinst->sps.vir_addr);
|
|
}
|
|
memset(&pinst->sps,
|
|
0, sizeof(struct addr_info));
|
|
}
|
|
|
|
if (pinst->pps.phy_addr_alloc && !use_reserve) {
|
|
enc_pr(LOG_DEBUG,
|
|
"tahoe_release, pps 0x%llx\n",
|
|
pinst->pps.phy_addr_alloc);
|
|
codec_mm_free_for_dma(
|
|
DEVICE_NAME,
|
|
pinst->pps.phy_addr_alloc);
|
|
if (pinst->pps.vir_addr) {
|
|
memunmap((void *)pinst->pps.vir_addr);
|
|
}
|
|
memset(&pinst->pps,
|
|
0, sizeof(struct addr_info));
|
|
}
|
|
|
|
if (pinst->vps.phy_addr_alloc && !use_reserve) {
|
|
enc_pr(LOG_DEBUG,
|
|
"tahoe_release, vps 0x%llx\n",
|
|
pinst->vps.phy_addr_alloc);
|
|
codec_mm_free_for_dma(
|
|
DEVICE_NAME,
|
|
pinst->vps.phy_addr_alloc);
|
|
if (pinst->vps.vir_addr) {
|
|
memunmap((void *)pinst->vps.vir_addr);
|
|
}
|
|
memset(&pinst->vps,
|
|
0, sizeof(struct addr_info));
|
|
}
|
|
|
|
if (dev->s_tahoe_drv_context.open_count == 0) {
|
|
dev->s_tahoe_drv_context.interrupt_reason = 0;
|
|
dev->s_tahoe_drv_context.interrupt_flag = 0;
|
|
|
|
if (dev->fw_buffer.phys_addr) {
|
|
enc_pr(LOG_DEBUG,
|
|
"tahoe_release, fw_buffer.phys_addr 0x%llx\n",
|
|
dev->fw_buffer.phys_addr);
|
|
codec_mm_free_for_dma(
|
|
DEVICE_NAME,
|
|
dev->fw_buffer.phys_addr);
|
|
codec_mm_unmap_phyaddr(
|
|
(unsigned char *)dev->fw_buffer.virt_addr);
|
|
memset(&dev->fw_buffer,
|
|
0, sizeof(struct venc_drv_buffer_t));
|
|
}
|
|
|
|
if ((dev->s_tahoe_irq >= 0)
|
|
&& (dev->s_tahoe_irq_requested == true)) {
|
|
free_irq(dev->s_tahoe_irq, /*&dev->s_tahoe_drv_context*/dev);
|
|
dev->s_tahoe_irq_requested = false;
|
|
enc_pr(LOG_ERROR,"free irq action\n");
|
|
}
|
|
|
|
/* disable tahoe clks.*/
|
|
tahoe_clk_disable(&dev->s_tahoe_clks, tahoe_pdev);
|
|
|
|
enc_pr(LOG_ERROR,"disable clock action\n");
|
|
}
|
|
|
|
v4l2_m2m_ctx_release(pinst->v4l2_fh.m2m_ctx);
|
|
|
|
v4l2_fh_del(&pinst->v4l2_fh);
|
|
v4l2_fh_exit(&pinst->v4l2_fh);
|
|
|
|
if (pinst) {
|
|
for (int i = 0;i < MAX_INSTANCE;i++) {
|
|
if (dev->instance[i] && (dev->instance[i] == pinst)) {
|
|
dev->instance[i] = NULL;
|
|
}
|
|
}
|
|
kfree(pinst);
|
|
|
|
}
|
|
|
|
filp->private_data = NULL;
|
|
|
|
enc_pr(LOG_ERROR, "tahoe_v4l_release,dev:%lx \n",(ulong)dev);
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_file_operations tahoe_v4l_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tahoe_v4l_open,
|
|
.release = tahoe_v4l_release,
|
|
.read = vb2_fop_read,
|
|
.poll = v4l2_m2m_fop_poll,
|
|
.unlocked_ioctl = video_ioctl2,
|
|
.mmap = v4l2_m2m_fop_mmap,
|
|
};
|
|
|
|
static int tahoe_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
{
|
|
strscpy(cap->driver, "tahoe", sizeof(cap->driver));
|
|
strscpy(cap->card, "tahoe", sizeof(cap->card));
|
|
cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_enum_fmt_vid(struct file *file, void *fh, struct v4l2_fmtdesc *f) {
|
|
int *fmt_list_ptr = NULL;
|
|
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
|
fmt_list_ptr = (int *)&fmt_list[0];
|
|
}
|
|
else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
fmt_list_ptr = (int *)&fmt_list_out[0];
|
|
}
|
|
else {
|
|
enc_pr(LOG_ERROR,"enum fmt type error : %d\n",f->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (f->index < MAX_FMT_NUM && f->index >=0 && fmt_list_ptr[f->index]) {
|
|
f->pixelformat = fmt_list_ptr[f->index];
|
|
f->flags = 0;
|
|
}
|
|
else {
|
|
enc_pr(LOG_ERROR,"function query fmt support failed\n");
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
|
|
{
|
|
if (fsize->index)
|
|
return -EINVAL;
|
|
|
|
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
|
|
fsize->stepwise.min_width = 128;
|
|
fsize->stepwise.max_width = 3840;
|
|
fsize->stepwise.step_width = 2;
|
|
fsize->stepwise.min_height = 128;
|
|
fsize->stepwise.max_height = 3840;
|
|
fsize->stepwise.step_height = 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int trans_fmt(int v4l_fmt,img_format_e *hw_fmt) {
|
|
switch (v4l_fmt) {
|
|
case V4L2_PIX_FMT_YUV420:
|
|
case V4L2_PIX_FMT_YUV420M:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_YUV420_PLANAR;
|
|
break;
|
|
case V4L2_PIX_FMT_NV12:
|
|
case V4L2_PIX_FMT_NV12M:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_NV12;
|
|
break;
|
|
case V4L2_PIX_FMT_NV21:
|
|
case V4L2_PIX_FMT_NV21M:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_NV21;
|
|
break;
|
|
case V4L2_PIX_FMT_RGBA32:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_RGBA8888;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_RGB888;
|
|
break;
|
|
default:
|
|
(*hw_fmt) = MFDI_INPUT_FMT_NV12;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ditrans_fmt(img_format_e hw_fmt,int *v4l_fmt) {
|
|
switch (hw_fmt) {
|
|
case MFDI_INPUT_FMT_YUV420_PLANAR:
|
|
(*v4l_fmt) = V4L2_PIX_FMT_YUV420M;
|
|
break;
|
|
case MFDI_INPUT_FMT_NV12:
|
|
(*v4l_fmt) = V4L2_PIX_FMT_NV12M;
|
|
break;
|
|
case MFDI_INPUT_FMT_NV21:
|
|
(*v4l_fmt) = V4L2_PIX_FMT_NV21M;
|
|
break;
|
|
case MFDI_INPUT_FMT_RGBA8888:
|
|
(*v4l_fmt) = V4L2_PIX_FMT_RGBA32;
|
|
break;
|
|
case MFDI_INPUT_FMT_RGB888:
|
|
(*v4l_fmt) = V4L2_PIX_FMT_RGB24;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if 1
|
|
static int tahoe_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) {
|
|
struct tahoe_inst *pinst = GET_PRI(fh);
|
|
|
|
if ( pinst->param.codec_type == CODEC_ID_H264) {
|
|
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
|
|
}
|
|
else if ( pinst->param.codec_type == CODEC_ID_H265) {
|
|
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_HEVC;
|
|
}
|
|
else {
|
|
enc_pr(LOG_ERROR,"internal fmt error:%d\n",pinst->param.codec_type);
|
|
}
|
|
|
|
f->fmt.pix_mp.width = pinst->param.width;
|
|
f->fmt.pix_mp.height = pinst->param.height;
|
|
f->fmt.pix_mp.num_planes = pinst->param.plane_num;
|
|
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = 5 * 1024 * 1024;
|
|
|
|
ditrans_fmt(pinst->param.fmt,&f->fmt.pix_mp.pixelformat);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) {
|
|
int i = 0;
|
|
struct tahoe_inst *pinst = GET_PRI(fh);
|
|
|
|
f->fmt.pix_mp.width = pinst->param.width;
|
|
f->fmt.pix_mp.height = pinst->param.height;
|
|
f->fmt.pix_mp.num_planes = pinst->param.plane_num;
|
|
for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
|
|
f->fmt.pix_mp.plane_fmt[i].bytesperline = pinst->param.bytesperline[i];
|
|
f->fmt.pix_mp.plane_fmt[i].sizeimage = pinst->param.imagesize[i];
|
|
}
|
|
|
|
ditrans_fmt(pinst->param.fmt,&f->fmt.pix_mp.pixelformat);
|
|
//f->fmt.pix_mp.field = V4L2_FIELD_NONE;
|
|
enc_pr(LOG_INFO,"function tahoe_g_fmt_vid_out end,num_planes:%d\n",f->fmt.pix_mp.num_planes);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) {
|
|
|
|
f->fmt.pix_mp.flags = 0;
|
|
//f->fmt.pix_mp.field = V4L2_FIELD_NONE;
|
|
f->fmt.pix_mp.num_planes = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) {
|
|
int width;
|
|
int height;
|
|
const struct v4l2_format_info *info = v4l2_format_info(f->fmt.pix_mp.pixelformat);
|
|
|
|
enc_pr(LOG_INFO,"tahoe_try_fmt_vid_out,fmt:%d,plane_num:%d\n",f->fmt.pix_mp.pixelformat,f->fmt.pix_mp.num_planes);
|
|
|
|
if ((f->fmt.pix_mp.width < 128) || (f->fmt.pix_mp.height < 128)) {
|
|
f->fmt.pix_mp.width = 128;
|
|
f->fmt.pix_mp.height = 128;
|
|
enc_pr(LOG_ERROR,"invalid input size,resize!\n");
|
|
}
|
|
|
|
width = f->fmt.pix_mp.width;
|
|
height = f->fmt.pix_mp.height;
|
|
//for only one plane support
|
|
if (f->fmt.pix_mp.num_planes == 1) {
|
|
f->fmt.pix_mp.num_planes = 1;
|
|
enc_pr(LOG_INFO,"try format,plane_num = 1\n");
|
|
}
|
|
else {
|
|
f->fmt.pix_mp.num_planes = info->mem_planes;
|
|
}
|
|
|
|
if (f->fmt.pix_mp.num_planes == 1) {
|
|
if (V4L2_PIX_FMT_RGBA32 == f->fmt.pix_mp.pixelformat) {
|
|
enc_pr(LOG_INFO,"try set rgba32 format\n");
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height * 4;
|
|
}
|
|
else if (V4L2_PIX_FMT_RGB24 == f->fmt.pix_mp.pixelformat) {
|
|
enc_pr(LOG_INFO,"try set rgb24 format\n");
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height * 3;
|
|
}
|
|
else {
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height * 3 / 2; //fix me!!
|
|
}
|
|
}
|
|
else if (f->fmt.pix_mp.num_planes == 2) {
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height;
|
|
f->fmt.pix_mp.plane_fmt[1].sizeimage = width * height / 2; //fix me!! align need confirm
|
|
}
|
|
else if (f->fmt.pix_mp.num_planes == 3) {
|
|
f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height;
|
|
f->fmt.pix_mp.plane_fmt[1].sizeimage = width * height / 4;
|
|
f->fmt.pix_mp.plane_fmt[2].sizeimage = width * height / 4;
|
|
}
|
|
|
|
f->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
|
|
|
|
//f->fmt.pix_mp.field = V4L2_FIELD_NONE;
|
|
enc_pr(LOG_INFO,"tahoe_try_fmt_vid_out num_planes:%d\n",f->fmt.pix_mp.num_planes);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) {
|
|
struct tahoe_inst *pinst = GET_PRI(fh);
|
|
|
|
tahoe_try_fmt_vid_cap(file, fh, f);
|
|
|
|
if (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_H264) {
|
|
pinst->param.codec_type = CODEC_ID_H264;
|
|
}
|
|
else if (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) {
|
|
pinst->param.codec_type = CODEC_ID_H265;
|
|
}
|
|
else {
|
|
enc_pr(LOG_ERROR,"invalid cap format! %d\n",f->fmt.pix_mp.pixelformat);
|
|
}
|
|
|
|
pinst->param.colour_primaries = f->fmt.pix_mp.colorspace;
|
|
pinst->param.matrix_coefficients = f->fmt.pix_mp.ycbcr_enc;
|
|
pinst->param.transfer_characteristics = f->fmt.pix_mp.xfer_func;
|
|
pinst->param.video_full_range_flag = f->fmt.pix_mp.quantization;
|
|
enc_pr(LOG_INFO,"tahoe_s_fmt_vid_cap color aspect info:%d %d %d %d\n",pinst->param.colour_primaries,pinst->param.matrix_coefficients,
|
|
pinst->param.transfer_characteristics,pinst->param.video_full_range_flag);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) {
|
|
struct tahoe_inst *pinst = GET_PRI(fh);
|
|
int i = 0;
|
|
|
|
enc_pr(LOG_INFO,"tahoe_s_fmt_vid_out,type:%d,width:%d,height:%d,num_planes:%d,fmt:%d\n",f->type,f->fmt.pix_mp.width,f->fmt.pix_mp.height,
|
|
f->fmt.pix_mp.num_planes,f->fmt.pix_mp.pixelformat);
|
|
tahoe_try_fmt_vid_out(file, fh, f);
|
|
|
|
pinst->param.width = f->fmt.pix_mp.width;
|
|
pinst->param.height = f->fmt.pix_mp.height;
|
|
pinst->param.plane_num = f->fmt.pix_mp.num_planes;
|
|
pinst->param.stride = pinst->param.width;
|
|
|
|
for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
|
|
pinst->param.bytesperline[i] = f->fmt.pix_mp.plane_fmt[i].bytesperline;
|
|
pinst->param.imagesize[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage;
|
|
enc_pr(LOG_INFO,"tahoe_s_fmt_vid_out imagesize[%d]:%d,bytesperline:%d\n",i,pinst->param.imagesize[i],pinst->param.bytesperline[i]);
|
|
}
|
|
|
|
trans_fmt(f->fmt.pix_mp.pixelformat,&pinst->param.fmt);
|
|
|
|
if (V4L2_PIX_FMT_RGBA32 == f->fmt.pix_mp.pixelformat) {
|
|
pinst->param.stride = pinst->param.bytesperline[0] / 4;
|
|
}
|
|
else if (V4L2_PIX_FMT_RGB24 == f->fmt.pix_mp.pixelformat) {
|
|
pinst->param.stride = pinst->param.bytesperline[0] / 3;
|
|
}
|
|
else {
|
|
pinst->param.stride = pinst->param.bytesperline[0];
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"setting format:%d,stride:%d\n",pinst->param.fmt,pinst->param.stride);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param)
|
|
{
|
|
enc_pr(LOG_INFO,"tahoe_enc_g_parm enter\n");
|
|
struct tahoe_inst *inst = GET_PRI(fh);
|
|
|
|
enc_pr(LOG_INFO,"tahoe_enc_g_parm type:%u\n",param->type);
|
|
|
|
if (param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
|
return -EINVAL;
|
|
|
|
param->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
|
|
param->parm.output.timeperframe.numerator = 1;
|
|
param->parm.output.timeperframe.denominator = inst->param.fps;
|
|
|
|
enc_pr(LOG_INFO,"tahoe_enc_g_parm leave,numerator: %u && denominator: %u\n",param->parm.output.timeperframe.numerator,param->parm.output.timeperframe.denominator);
|
|
return 0;
|
|
}
|
|
|
|
static int tahoe_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *param)
|
|
{
|
|
struct tahoe_inst *inst = GET_PRI(fh);
|
|
|
|
enc_pr(LOG_INFO,"tahoe_enc_s_parm type:%u\n",param->type);
|
|
|
|
if (param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
|
return -EINVAL;
|
|
|
|
param->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
|
|
if (param->parm.output.timeperframe.denominator && param->parm.output.timeperframe.numerator) {
|
|
inst->param.fps = param->parm.output.timeperframe.denominator / param->parm.output.timeperframe.numerator;
|
|
} else {
|
|
param->parm.output.timeperframe.numerator = 1;
|
|
param->parm.output.timeperframe.denominator = inst->param.fps;
|
|
}
|
|
|
|
enc_pr(LOG_INFO,"tahoe_enc_s_parm numerator: %u && denominator: %u\n",param->parm.output.timeperframe.numerator,param->parm.output.timeperframe.denominator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tahoe_ioctl_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) {
|
|
enc_pr(LOG_INFO,"tahoe_ioctl_reqbufs memory type:%d,type:%d\n",rb->memory,rb->type);
|
|
return v4l2_m2m_ioctl_reqbufs(file, priv, rb);
|
|
}
|
|
|
|
int tahoe_ioctl_qbuf(struct file *file, void *priv,struct v4l2_buffer *buf) {
|
|
struct tahoe_inst *inst = GET_PRI(file->private_data);
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
|
|
enc_pr(LOG_INFO,"v4l2_m2m_ioctl_qbuf memory type:%d,type:%d,share_fd:%d\n",buf->memory,buf->type,buf->m.planes[0].m.fd);
|
|
int ret = v4l2_m2m_ioctl_qbuf(file, priv,buf);
|
|
|
|
if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
|
enc_pr(LOG_INFO,"output buffer transform!,buffer flags:%x\n",buf->flags);
|
|
}
|
|
else {
|
|
int index = buf->index;
|
|
enc_pr(LOG_INFO,"input buffer transform!,index:%d\n",index);
|
|
struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_dst_vq(m2m_ctx), index);
|
|
enc_pr(LOG_INFO,"vb:%llx,vb plane_num:%d\n",(uint64_t)vb,vb->num_planes);
|
|
}
|
|
enc_pr(LOG_INFO,"v4l2_m2m_ioctl_qbuf ret:%d",ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool tahoe_vpu_both_queues_are_streaming(struct tahoe_inst *inst)
|
|
{
|
|
struct vb2_queue *vq_cap =
|
|
v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
|
struct vb2_queue *vq_out =
|
|
v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
|
|
|
return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out);
|
|
}
|
|
|
|
|
|
|
|
int tahoe_ioctl_encoder_cmd(struct file *file, void *priv,struct v4l2_encoder_cmd *ec) {
|
|
enc_pr(LOG_INFO,"tahoe_ioctl_encoder_cmd,cmd:%d\n",ec->cmd);
|
|
|
|
struct tahoe_inst *inst = GET_PRI(file->private_data);
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
int ret;
|
|
|
|
ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, ec);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!tahoe_vpu_both_queues_are_streaming(inst))
|
|
return 0;
|
|
|
|
switch (ec->cmd) {
|
|
case V4L2_ENC_CMD_STOP:
|
|
if (m2m_ctx->is_draining)
|
|
return -EBUSY;
|
|
|
|
if (m2m_ctx->has_stopped)
|
|
return 0;
|
|
|
|
m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
|
|
enc_pr(LOG_INFO,"tahoe_ioctl_encoder_cmd,detect last src buf:%p,hw_proc_count:%d\n",m2m_ctx->last_src_buf,inst->hw_proc_count);
|
|
if ((NULL == m2m_ctx->last_src_buf) && (0 == inst->hw_proc_count)) { //no more src ,the cmd is just the last one,all data have send over,last is done need fix
|
|
return v4l2_m2m_ioctl_encoder_cmd(file, priv, ec);
|
|
}
|
|
m2m_ctx->is_draining = true;
|
|
break;
|
|
case V4L2_ENC_CMD_START:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|
|
static const struct v4l2_ioctl_ops tahoe_ioctl_ops = {
|
|
.vidioc_querycap = tahoe_querycap,
|
|
|
|
.vidioc_enum_fmt_vid_cap = tahoe_enum_fmt_vid,
|
|
.vidioc_enum_framesizes = tahoe_enum_framesizes,
|
|
.vidioc_g_fmt_vid_cap = tahoe_g_fmt_vid_cap,
|
|
.vidioc_try_fmt_vid_cap = tahoe_try_fmt_vid_cap,
|
|
.vidioc_s_fmt_vid_cap = tahoe_s_fmt_vid_cap,
|
|
.vidioc_g_fmt_vid_cap_mplane = tahoe_g_fmt_vid_cap,
|
|
.vidioc_try_fmt_vid_cap_mplane = tahoe_try_fmt_vid_cap,
|
|
.vidioc_s_fmt_vid_cap_mplane = tahoe_s_fmt_vid_cap,
|
|
|
|
.vidioc_enum_fmt_vid_out = tahoe_enum_fmt_vid,
|
|
.vidioc_g_fmt_vid_out = tahoe_g_fmt_vid_out,
|
|
.vidioc_try_fmt_vid_out = tahoe_try_fmt_vid_out,
|
|
.vidioc_s_fmt_vid_out = tahoe_s_fmt_vid_out,
|
|
.vidioc_s_fmt_vid_out_mplane = tahoe_s_fmt_vid_out,
|
|
.vidioc_g_fmt_vid_out_mplane = tahoe_g_fmt_vid_out,
|
|
.vidioc_try_fmt_vid_out_mplane = tahoe_try_fmt_vid_out,
|
|
|
|
.vidioc_reqbufs = tahoe_ioctl_reqbufs,
|
|
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
|
|
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
|
|
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
|
|
.vidioc_qbuf = tahoe_ioctl_qbuf,
|
|
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
|
|
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
|
|
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
|
|
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
|
|
|
|
.vidioc_g_parm = tahoe_enc_g_parm,
|
|
.vidioc_s_parm = tahoe_enc_s_parm,
|
|
|
|
.vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
|
|
.vidioc_encoder_cmd = tahoe_ioctl_encoder_cmd,
|
|
};
|
|
|
|
|
|
static void tahoe_device_run(void *priv)
|
|
{
|
|
struct tahoe_inst *inst = priv;
|
|
struct tahoe_param *param = &inst->param;
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
struct tahoe_device *dev = inst->dev;
|
|
struct vb2_v4l2_buffer *src_buf;
|
|
struct vb2_v4l2_buffer *dst_buf;
|
|
enc_pr(LOG_INFO,"enter function tahoe_device_run\n");
|
|
|
|
param->src_buf_index = -1;
|
|
inst->last_is_done = false; //state machine instead
|
|
|
|
src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
|
|
if (!src_buf) {
|
|
if (m2m_ctx->is_draining) {
|
|
dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
|
|
if (!dst_buf) {
|
|
enc_pr(LOG_ERROR, "device_run & draning mode,No bitstream buffer.");
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
return;
|
|
}
|
|
static const struct v4l2_event tahoe_event_eos = {
|
|
.type = V4L2_EVENT_EOS
|
|
};
|
|
|
|
if (!WARN_ON(!dst_buf)) {
|
|
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
|
|
dst_buf->field = V4L2_FIELD_NONE;
|
|
v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf);
|
|
}
|
|
v4l2_event_queue_fh(&inst->v4l2_fh, &tahoe_event_eos);
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
enc_pr(LOG_ERROR, "%s: No source buffer found,draing mode,return the last one\n", __func__);
|
|
return;
|
|
}
|
|
else {
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
return;
|
|
}
|
|
} else {
|
|
enc_pr(LOG_INFO,"%s: plane_num:%d\n",__func__, param->plane_num);
|
|
if (param->plane_num == 1) {
|
|
param->inpt_buf_addr[0] = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
|
|
if (param->fmt == MFDI_INPUT_FMT_NV21 || param->fmt == MFDI_INPUT_FMT_NV12 || param->fmt == MFDI_INPUT_FMT_YUV420_PLANAR) {
|
|
param->inpt_buf_addr[1] = param->inpt_buf_addr[0] + param->stride * param->height;
|
|
if (param->fmt == MFDI_INPUT_FMT_YUV420_PLANAR) {
|
|
param->inpt_buf_addr[2] = param->inpt_buf_addr[0] + param->stride * param->height * 5 / 4;
|
|
}
|
|
}
|
|
} else if (param->plane_num == 2) {
|
|
param->inpt_buf_addr[0] =
|
|
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
|
|
param->inpt_buf_addr[1] =
|
|
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
|
|
enc_pr(LOG_INFO,"input addr:%llx %llx\n",param->inpt_buf_addr[0],param->inpt_buf_addr[1]);
|
|
if (param->inpt_buf_addr[0] == param->inpt_buf_addr[1]) {
|
|
param->inpt_buf_addr[1] = param->inpt_buf_addr[0] + param->width * param->height;
|
|
}
|
|
} else if (param->plane_num == 3) {
|
|
param->inpt_buf_addr[0] =
|
|
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
|
|
param->inpt_buf_addr[1] =
|
|
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
|
|
param->inpt_buf_addr[2] =
|
|
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2);
|
|
}
|
|
param->src_buf_index = src_buf->vb2_buf.index;
|
|
enc_pr(LOG_INFO,"tahoe_device_run src_buf_index:%d\n",param->src_buf_index);
|
|
}
|
|
|
|
dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
|
|
if (!dst_buf) {
|
|
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
|
|
enc_pr(LOG_ERROR, "%s: No destination buffer found\n", __func__);
|
|
return;
|
|
}
|
|
struct vb2_manage_cap_st *man_vb = (struct vb2_manage_cap_st *)dst_buf;
|
|
|
|
man_vb->viraddr = (uint64_t)vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
|
|
enc_pr(LOG_INFO,"vaddr:%llx",man_vb->viraddr);
|
|
|
|
param->es_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
|
|
param->es_buf_size = vb2_plane_size(&dst_buf->vb2_buf, 0);
|
|
enc_pr(LOG_INFO,"get es_addr:%llx\n",param->es_addr);
|
|
|
|
if (!inst->inst_init_flag) {
|
|
create_channel(inst->dev,inst);
|
|
inst->inst_init_flag = 1;
|
|
}
|
|
encode_one_frame(dev,inst);
|
|
|
|
inst->param.poc++;
|
|
inst->hw_proc_count++;
|
|
enc_pr(LOG_INFO,"enter function tahoe_device_run end\n");
|
|
return;
|
|
}
|
|
|
|
static int tahoe_job_ready(void *priv)
|
|
{
|
|
struct tahoe_inst *inst = priv;
|
|
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
|
|
|
|
enc_pr(LOG_INFO,"enter function tahoe_job_ready,inst->last_is_done:%d,inst->param.poc:%d\n",inst->last_is_done,inst->param.poc);
|
|
if ((m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) && (inst->last_is_done || (inst->param.poc == 0))) {
|
|
return true;
|
|
}
|
|
else {
|
|
enc_pr(LOG_INFO,"tahoe_job_ready not ready,is_draining:%d,buf:%d\n",m2m_ctx->is_draining,v4l2_m2m_num_src_bufs_ready(m2m_ctx));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
static const struct v4l2_m2m_ops tahoe_m2m_ops = {
|
|
.device_run = tahoe_device_run,
|
|
.job_ready = tahoe_job_ready,
|
|
};
|
|
|
|
|
|
|
|
static s32 tahoe_probe(struct platform_device *pdev)
|
|
{
|
|
s32 err = 0, irq;
|
|
struct resource *res;
|
|
void __iomem *base;
|
|
struct tahoe_device *dev = NULL;
|
|
int ret = 0;
|
|
|
|
enc_pr(LOG_DEBUG, "tahoe_probe\n");
|
|
|
|
s_tahoe_major = 0;
|
|
use_reserve = false;
|
|
s_tahoe_irq = -1;
|
|
s_tahoe_irq_requested = false;
|
|
spin_lock(&s_vpu_lock);
|
|
s_tahoe_open_ref_count = 0;
|
|
spin_unlock(&s_vpu_lock);
|
|
|
|
tahoe_dev = NULL;
|
|
tahoe_pdev = NULL;
|
|
s_register_flag = 0;
|
|
|
|
cma_cfg_size = 36*SZ_1M; //__need_modify__
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
enc_pr(LOG_INFO, "probe dev:%lx \n",(unsigned long)dev);
|
|
dev_set_drvdata(&pdev->dev, dev);
|
|
dev->dev = &pdev->dev;
|
|
|
|
memset(&dev->s_tahoe_register, 0, sizeof(struct venc_drv_buffer_t));
|
|
memset(&dev->s_tahoe_drv_context,0,sizeof(struct venc_drv_context_t));
|
|
|
|
/* get interrupt resource */
|
|
irq = platform_get_irq_byname(pdev, "tahoe_irq0");
|
|
|
|
if (irq < 0) {
|
|
enc_pr(LOG_ERROR, "get tahoe irq resource error\n");
|
|
err = -EFAULT;
|
|
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
dev->s_tahoe_irq = irq;
|
|
enc_pr(LOG_DEBUG, "tahoe -irq: %d\n", dev->s_tahoe_irq);
|
|
|
|
/* get vpu clks */
|
|
if (tahoe_clk_get(&pdev->dev, &dev->s_tahoe_clks)) {
|
|
enc_pr(LOG_DEBUG, "get tahoe clks fail.\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
} else
|
|
enc_pr(LOG_DEBUG, "tahoe. clock get success\n");
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
enc_pr(LOG_ERROR, "failed to get encoder register resource\n");
|
|
err = -ENXIO;
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(base)) {
|
|
err = PTR_ERR(base);
|
|
enc_pr(LOG_ERROR, "failed to map encoder registers: %d\n", err);
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
dev->s_tahoe_register.phys_addr = res->start;
|
|
dev->s_tahoe_register.virt_addr = (ulong)base;
|
|
dev->s_tahoe_register.size = resource_size(res);
|
|
/* reset: tahoe_core_rst/tahoe_a_rst/tahoe_apb_rst/tahoe_sys_rst */
|
|
if (tahoe_rst_get(&pdev->dev, &dev->s_tahoe_rsts)) {
|
|
enc_pr(LOG_DEBUG, "get tahoe rsts fail.\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
} else
|
|
enc_pr(LOG_DEBUG, "tahoe. rsts get success\n");
|
|
|
|
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s",
|
|
DEVICE_NAME);
|
|
|
|
dev->tahoe_m2m_dev = v4l2_m2m_init(&tahoe_m2m_ops);
|
|
if (IS_ERR(dev->tahoe_m2m_dev)) {
|
|
enc_pr(LOG_ERROR, "v4l2_m2m_init, fail: %d\n", ret);
|
|
ret = -EINVAL;
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
if (v4l2_device_register(&pdev->dev, &dev->v4l2_dev) < 0) {
|
|
enc_pr(LOG_ERROR, "v4l2_device_register failed\n");
|
|
goto ERROR_PROVE_DEVICE;//probe_err0;
|
|
}
|
|
|
|
mutex_init(&q_lock);
|
|
mutex_init(&dev_lock);
|
|
|
|
mutex_init(&dev->dev_lock);
|
|
|
|
dev->vdev = video_device_alloc();
|
|
if (!dev->vdev) {
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
strscpy(dev->vdev->name, "tahoe_enc", sizeof(dev->vdev->name));
|
|
dev->vdev->fops = &tahoe_v4l_fops;
|
|
dev->vdev->ioctl_ops = &tahoe_ioctl_ops;
|
|
dev->vdev->release = video_device_release;
|
|
dev->vdev->dev_debug = 3;//debug;
|
|
dev->vdev->v4l2_dev = &dev->v4l2_dev;
|
|
dev->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING ;
|
|
dev->vdev->vfl_dir = VFL_DIR_M2M;
|
|
dev->vdev->lock = &dev_lock;
|
|
|
|
ret = video_register_device(dev->vdev, VFL_TYPE_VIDEO, video_nr);
|
|
if (ret < 0) {
|
|
ret = -ENODEV;
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
|
|
video_set_drvdata(dev->vdev, dev);
|
|
|
|
enc_pr(LOG_INFO, "SUCCESS alloc_chrdev_region\n");
|
|
|
|
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
|
|
|
|
tasklet_init(&tahoe_tasklet, tahoe_isr_tasklet, (ulong)dev);
|
|
tahoe_pdev = pdev;
|
|
pm_runtime_enable(&tahoe_pdev->dev);
|
|
return 0;
|
|
|
|
ERROR_PROVE_DEVICE:
|
|
if (dev->s_tahoe_register.virt_addr) {
|
|
iounmap((void *)dev->s_tahoe_register.virt_addr);
|
|
memset(&dev->s_tahoe_register, 0, sizeof(struct venc_drv_buffer_t));
|
|
}
|
|
|
|
video_unregister_device(dev->vdev);
|
|
if (dev->tahoe_m2m_dev)
|
|
v4l2_m2m_release(dev->tahoe_m2m_dev);
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
|
video_device_release(dev->vdev);
|
|
return err;
|
|
}
|
|
|
|
static KV_INT_TO_VOID tahoe_remove(struct platform_device *pdev)
|
|
{
|
|
struct tahoe_device *dev = dev_get_drvdata(&pdev->dev);
|
|
enc_pr(LOG_DEBUG, "tahoe_remove\n");
|
|
|
|
if (dev->s_tahoe_irq_requested == true) {
|
|
if (dev->s_tahoe_irq >= 0) {
|
|
free_irq(dev->s_tahoe_irq, /*&dev->s_tahoe_drv_context*/dev);
|
|
dev->s_tahoe_irq = -1;
|
|
}
|
|
dev->s_tahoe_irq_requested = false;
|
|
}
|
|
|
|
if (dev->s_tahoe_register.virt_addr) {
|
|
iounmap((void *)dev->s_tahoe_register.virt_addr);
|
|
memset(&dev->s_tahoe_register,
|
|
0, sizeof(struct venc_drv_buffer_t));
|
|
}
|
|
if (clock_gate_count > 0)
|
|
{
|
|
tahoe_clk_disable(&dev->s_tahoe_clks, tahoe_pdev);
|
|
}
|
|
tahoe_clk_put(&tahoe_pdev->dev, &dev->s_tahoe_clks);
|
|
mutex_destroy(&dev->dev_lock);
|
|
|
|
video_unregister_device(dev->vdev);
|
|
if (dev->tahoe_m2m_dev)
|
|
v4l2_m2m_release(dev->tahoe_m2m_dev);
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
|
if (dev) {
|
|
kfree(dev);
|
|
dev = NULL;
|
|
}
|
|
return KV_RET_x_TO_VOID(0);
|
|
}
|
|
|
|
static const struct of_device_id tahoe_dt_match[] = {
|
|
{
|
|
.compatible = "amlogic, tahoe",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver tahoe_driver = {
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.of_match_table = tahoe_dt_match,
|
|
},
|
|
.probe = tahoe_probe,
|
|
.remove = tahoe_remove,
|
|
};
|
|
|
|
static s32 __init tahoe_init(void)
|
|
{
|
|
s32 res;
|
|
|
|
enc_pr(LOG_DEBUG, "tahoe_init\n");
|
|
res = platform_driver_register(&tahoe_driver);
|
|
enc_pr(LOG_DEBUG, "tahoe_init result = 0x%x\n", res);
|
|
return res;
|
|
}
|
|
|
|
static void __exit tahoe_exit(void)
|
|
{
|
|
enc_pr(LOG_DEBUG, "tahoe_exit\n");
|
|
platform_driver_unregister(&tahoe_driver);
|
|
}
|
|
|
|
module_param(g_print_level, uint, 0664);
|
|
MODULE_PARM_DESC(g_print_level, "\n print_level\n");
|
|
|
|
module_param(clock_a, uint, 0664);
|
|
MODULE_PARM_DESC(clock_a, "\n clock_a\n");
|
|
|
|
MODULE_AUTHOR("Amlogic Inc.");
|
|
MODULE_DESCRIPTION("Tahoe linux driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
module_init(tahoe_init);
|
|
module_exit(tahoe_exit);
|