Files
BPI-CM9-Linux/aml-comp/hardware/amlogic/video_encoder/tahoe_v4l/tahoe_entry.c
T

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);