From e659fb53ad5ff90a6fb2f4a6133fbd8f4af161c5 Mon Sep 17 00:00:00 2001 From: "xiaoqian.zhang" Date: Thu, 6 Aug 2026 05:23:45 +0000 Subject: [PATCH 1/3] encoder: CF1 add v4l2 support for tahoe. [1/1] PD#SWPL-275685 Problem: lack of v4l2 support. Solution: add v4l2 support for tahoe. Verify: a9 Change-Id: I50ac0fe1af047b4fa85f4f153c6ae14b75b3d554 Signed-off-by: xiaoqian.zhang --- firmware/fw.bin | Bin 655360 -> 655360 bytes tahoe_v4l/Makefile | 34 + tahoe_v4l/tahoe.h | 125 ++ tahoe_v4l/tahoe_assist.c | 73 ++ tahoe_v4l/tahoe_assist.h | 50 + tahoe_v4l/tahoe_common.c | 172 +++ tahoe_v4l/tahoe_common.h | 213 ++++ tahoe_v4l/tahoe_entry.c | 2212 ++++++++++++++++++++++++++++++++++++ tahoe_v4l/tahoe_hw.c | 405 +++++++ tahoe_v4l/tahoe_hw.h | 46 + tahoe_v4l/tahoe_register.h | 280 +++++ 11 files changed, 3610 insertions(+) create mode 100644 tahoe_v4l/Makefile create mode 100644 tahoe_v4l/tahoe.h create mode 100644 tahoe_v4l/tahoe_assist.c create mode 100644 tahoe_v4l/tahoe_assist.h create mode 100644 tahoe_v4l/tahoe_common.c create mode 100644 tahoe_v4l/tahoe_common.h create mode 100644 tahoe_v4l/tahoe_entry.c create mode 100644 tahoe_v4l/tahoe_hw.c create mode 100644 tahoe_v4l/tahoe_hw.h create mode 100644 tahoe_v4l/tahoe_register.h diff --git a/firmware/fw.bin b/firmware/fw.bin index 5298c08bd147537f8933fb59a4c27934ce8922a1..f41e1f507e36b0cdf2cbb7cc79cdba7b4965a237 100644 GIT binary patch delta 68846 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 diff --git a/tahoe_v4l/Makefile b/tahoe_v4l/Makefile new file mode 100644 index 0000000..dc6822c --- /dev/null +++ b/tahoe_v4l/Makefile @@ -0,0 +1,34 @@ +MODULE_NAME = tahoe + +ifeq ($(O),) +out_dir := . +else +out_dir := $(O) +endif +-include $(out_dir)/include/config/auto.conf + +obj-m = $(MODULE_NAME).o + +$(MODULE_NAME)-y := tahoe_entry.o tahoe_common.o tahoe_hw.o tahoe_assist.o + +PR_FMT = $(subst amlogic-,,$(MODULE_NAME)) +PR_FMT_DEFINE="-Dpr_fmt(fmt)= \"[$(PR_FMT)]: \" fmt" +ccflags-y += $(PR_FMT_DEFINE) + + +modules: + @$(MAKE) -C $(KERNEL_SRC) M=$(M) modules + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 M=$(M)/tahoe -C $(KERNEL_SRC) modules_install + $(Q)mkdir -p ${out_dir}/../vendor_lib/modules + $(Q)mkdir -p ${out_dir}/../vendor_lib/firmware/encoder + $(Q)cp $(KERNEL_SRC)/$(M)/firmware/* ${out_dir}/../vendor_lib/firmware/encoder/ -rf + $(Q)if [ -z "$(CONFIG_AMLOGIC_KERNEL_VERSION)" ]; then \ + cd ${out_dir}/$(M)/; find -name "*.ko" -exec cp {} ${out_dir}/../vendor_lib/modules/ \; ; \ + else \ + find $(INSTALL_MOD_PATH)/lib/modules/*/$(INSTALL_MOD_DIR) -name "*.ko" -exec cp {} ${out_dir}/../vendor_lib/modules \; ; \ + fi; + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(M) clean diff --git a/tahoe_v4l/tahoe.h b/tahoe_v4l/tahoe.h new file mode 100644 index 0000000..bc952bf --- /dev/null +++ b/tahoe_v4l/tahoe.h @@ -0,0 +1,125 @@ +/* + * 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: + */ +#ifndef __TAHOE_H__ +#define __TAHOE_H__ + +#include +#include +#include + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 3, 13) +#define KV_CLASS_CONST +#define KV_CLASS_ATTR_CONST +#else +#define KV_CLASS_CONST const //kernel 6.6 change the type of parameter to const +#define KV_CLASS_ATTR_CONST const //kernel 6.6 change the type of parameter to const +#endif + +/* Traditionally the remove callback returned an int which however is + * ignored by the driver core. This led to wrong expectations by driver + * authors who thought returning an error code was a valid error + * handling strategy. To convert to a callback returning void, new + * drivers should implement . + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 11, 0) +#define KV_INT_TO_VOID int +#define KV_RET_x_TO_VOID(a) (a) +#else +#define KV_INT_TO_VOID void +#define KV_RET_x_TO_VOID(a) +#endif + +#define CHIP_ID 0x0 +#define MAX_CHANNEL 1 + +#ifdef CONFIG_COMPAT +struct compat_venc_drv_buffer_t { + u32 size; + u32 cached; + compat_ulong_t phys_addr; + compat_ulong_t base; /* kernel logical address in use kernel */ + compat_ulong_t virt_addr; /* virtual user space address */ +}; +#endif + +struct venc_bit_firmware_info_t { + u32 size; /* size of this structure*/ + ulong reg_base_offset; + u16 bit_code[512]; +}; + +struct venc_inst_info_t { + u32 inst_idx; + s32 inst_open_count; /* for output only*/ +}; + +struct venc_drv_intr_info_t { + u32 timeout; + s32 intr_reason; +}; + +#if 0 +/* To track the allocated memory buffer */ +struct venc_drv_buffer_pool_t { + struct list_head list; + struct venc_drv_buffer_t vb; + struct file *filp; +}; +#endif +#define TAHOE_REG_BASE_ADDR 0xFE370000 +#define TAHOE_REG_SIZE 0x10000 + +enum { + IOC_NR_GET_REGISTER_INFO = 0, + IOC_NR_GET_COMMON_BUF_INFO, + IOC_NR_WAIT_INTERRUPT, + IOC_NR_GET_FW_BUF_INFO, + IOC_NR_ALLOCATE_PHYSICAL_MEMORY, + IOC_NR_FLUSH_BUFFER, + IOC_NR_CACHE_INV_BUFFER, + IOC_NR_FOR_DEBUG, +}; + +#define VENC_DRV_BUF_LEN struct venc_drv_buffer_t + +#ifdef CONFIG_COMPAT +#define VENC_DRV_BUF_LEN32 struct compat_venc_drv_buffer_t +#endif + +#define TAHOE_MAGIC 'x' +#define IOC_GET_REGISTER_INFO _IO(TAHOE_MAGIC, IOC_NR_GET_REGISTER_INFO) +#define IOC_GET_COMMON_BUF_INFO _IOR(TAHOE_MAGIC, IOC_NR_GET_COMMON_BUF_INFO, VENC_DRV_BUF_LEN) +#define IOC_WAIT_INTERRUPT _IO(TAHOE_MAGIC, IOC_NR_WAIT_INTERRUPT) +#define IOC_GET_FW_BUF_INFO _IOR(TAHOE_MAGIC, IOC_NR_GET_FW_BUF_INFO, VENC_DRV_BUF_LEN) +#define IOC_ALLOCATE_PHYSICAL_MEMORY _IOWR(TAHOE_MAGIC, IOC_NR_ALLOCATE_PHYSICAL_MEMORY, VENC_DRV_BUF_LEN) +#define IOC_FLUSH_BUFFER _IOW(TAHOE_MAGIC, IOC_NR_FLUSH_BUFFER, VENC_DRV_BUF_LEN) +#define IOC_CACHE_INV_BUFFER _IOW(TAHOE_MAGIC, IOC_NR_CACHE_INV_BUFFER, VENC_DRV_BUF_LEN) +#define IOC_NR_FOR_DEBUG _IOR(TAHOE_MAGIC, IOC_NR_FOR_DEBUG, u32) + +#ifdef CONFIG_COMPAT +#define IOC_GET_COMMON_BUF_INFO32 _IOR(TAHOE_MAGIC, IOC_NR_GET_COMMON_BUF_INFO, VENC_DRV_BUF_LEN32) +#define IOC_GET_FW_BUF_INFO32 _IOR(TAHOE_MAGIC, IOC_NR_GET_FW_BUF_INFO, VENC_DRV_BUF_LEN32) +#define IOC_ALLOCATE_PHYSICAL_MEMORY32 _IOWR(TAHOE_MAGIC, IOC_NR_ALLOCATE_PHYSICAL_MEMORY, VENC_DRV_BUF_LEN32) +#define IOC_FLUSH_BUFFER32 _IOW(TAHOE_MAGIC, IOC_NR_FLUSH_BUFFER, VENC_DRV_BUF_LEN32) +#define IOC_CACHE_INV_BUFFER32 _IOW(TAHOE_MAGIC, IOC_NR_CACHE_INV_BUFFER, VENC_DRV_BUF_LEN32) +#endif + + +#endif diff --git a/tahoe_v4l/tahoe_assist.c b/tahoe_v4l/tahoe_assist.c new file mode 100644 index 0000000..d28f27e --- /dev/null +++ b/tahoe_v4l/tahoe_assist.c @@ -0,0 +1,73 @@ +/* + * 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 "tahoe_assist.h" + +int instance_info[MAX_INSTANCE] = {0}; +s32 g_print_level = LOG_ERROR; + +void set_instance_valid(int id,bool bvalid) { + if (id < 0 || id >= MAX_INSTANCE) { + enc_pr(LOG_ERROR,"set_instance_valid,id invalid:%d",id); + return; + } + // venc_lock(); + instance_info[id] = bvalid; + enc_pr(LOG_INFO,"set_instance_valid %d %d",id,bvalid); + // venc_unlock(); +} + +bool find_valid_id(int *id) { + bool bfind = false; + for (int i = 0;i < MAX_INSTANCE;i++) { + if (instance_info[i] == 0) { + (*id) = i; + bfind = true; + set_instance_valid(i,true); + enc_pr(LOG_INFO,"%d find valid instance id:%d",instance_info[i],i); + break; + } + } + return bfind; +} + +int get_cur_instance(void) { + int num = 0; + // venc_lock(); + for (int i = 0;i < MAX_INSTANCE;i++) { + if (instance_info[i] != 0) { + num++; + } + } + // venc_unlock(); + enc_pr(LOG_INFO,"get_cur_instance %d",num); + return num; +} + +uint8_t find_valid_chn_cnt(void) { + uint8_t cnt = 0; + for (int i = 0;i < MAX_INSTANCE;i++) { + if (instance_info[i] == 1) { + cnt++; + } + } + enc_pr(LOG_INFO,"now have %d instance running",cnt); + return cnt; +} \ No newline at end of file diff --git a/tahoe_v4l/tahoe_assist.h b/tahoe_v4l/tahoe_assist.h new file mode 100644 index 0000000..ca7f0aa --- /dev/null +++ b/tahoe_v4l/tahoe_assist.h @@ -0,0 +1,50 @@ +/* + * 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: + */ + +#ifndef __TAHOE_ASSIST_H_ +#define __TAHOE_ASSIST_H_ + +#include +#include + + +#define LOG_ALL 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define LOG_ERROR 3 + +#define MAX_INSTANCE 4 + +extern s32 g_print_level; +#define enc_pr(level, x...) \ + do { \ + if (level >= g_print_level) \ + printk(x); \ + } while (0) + + + +void set_instance_valid(int id,bool bvalid); +bool find_valid_id(int *id); +int get_cur_instance(void); +uint8_t find_valid_chn_cnt(void); + + +#endif \ No newline at end of file diff --git a/tahoe_v4l/tahoe_common.c b/tahoe_v4l/tahoe_common.c new file mode 100644 index 0000000..ac37eff --- /dev/null +++ b/tahoe_v4l/tahoe_common.c @@ -0,0 +1,172 @@ +/* + * 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 +#include +#include "tahoe_common.h" + +u32 clock_a; +s32 clock_gate_count = 0; + + +void tahoe_hw_reset(void) +{ + #if 0 + if (IS_ERR(s_tahoe_rsts.core_rst) || + IS_ERR(s_tahoe_rsts.a_rst) || + IS_ERR(s_tahoe_rsts.apb_rst) || + IS_ERR(s_tahoe_rsts.sys_rst)) { + return; + } + reset_control_reset(s_tahoe_rsts.core_rst); + reset_control_reset(s_tahoe_rsts.a_rst); + reset_control_reset(s_tahoe_rsts.apb_rst); + reset_control_reset(s_tahoe_rsts.sys_rst); + #endif +} + +void tahoe_clk_put(struct device *dev, struct tahoe_clks *clks) +{ + if (!(clks->sys_tahoe == NULL || IS_ERR(clks->sys_tahoe))) + devm_clk_put(dev, clks->sys_tahoe); + if (!(clks->tahoe_clk == NULL || IS_ERR(clks->tahoe_clk))) + devm_clk_put(dev, clks->tahoe_clk); +} + +int tahoe_clk_get(struct device *dev, struct tahoe_clks *clks) +{ + int ret = 0; + + clks->sys_tahoe = devm_clk_get(dev, "sys_tahoe"); + + if (IS_ERR(clks->sys_tahoe)) { + enc_pr(LOG_ERROR, "cannot get sys_tahoe clock\n"); + clks->sys_tahoe = NULL; + //ret = -ENOENT; + //goto err; + } + clks->tahoe_clk = devm_clk_get(dev, "clk_tahoe"); + + if (IS_ERR(clks->tahoe_clk)) { + enc_pr(LOG_ERROR, "cannot get clk_tahoe clock\n"); + clks->tahoe_clk = NULL; + ret = -ENOENT; + goto err; + } + + return 0; +err: + tahoe_clk_put(dev, clks); + + return ret; +} + +int tahoe_rst_get(struct device *dev, struct tahoe_rsts *rsts) +{ + //int ret = 0; + + rsts->core_rst = devm_reset_control_get(dev, "tahoe_core_rst"); + + if (IS_ERR(rsts->core_rst)) { + enc_pr(LOG_ERROR, "cannot get core_rst\n"); + rsts->core_rst = NULL; + //ret = -ENOENT; + //goto err; + } + + rsts->a_rst = devm_reset_control_get(dev, "tahoe_a_rst"); + + if (IS_ERR(rsts->a_rst)) { + enc_pr(LOG_ERROR, "cannot get a_rst\n"); + rsts->a_rst = NULL; + //ret = -ENOENT; + //goto err; + } + + rsts->apb_rst = devm_reset_control_get(dev, "tahoe_apb_rst"); + + if (IS_ERR(rsts->apb_rst)) { + enc_pr(LOG_ERROR, "cannot get apb_rst\n"); + rsts->apb_rst = NULL; + //ret = -ENOENT; + //goto err; + } + + rsts->sys_rst = devm_reset_control_get(dev, "tahoe_sys_rst"); + + if (IS_ERR(rsts->sys_rst)) { + enc_pr(LOG_ERROR, "cannot get sys_rst\n"); + rsts->sys_rst = NULL; + //ret = -ENOENT; + //goto err; + } + return 0; +//err: + //return ret; +} + +void tahoe_clk_enable(struct tahoe_clks *clks,struct platform_device *tahoe_pdev) +{ + if (clks->sys_tahoe != NULL) { + clk_prepare_enable(clks->sys_tahoe); + } + if (clock_a > 0) { + enc_pr(LOG_INFO, "tahoe: desired clock_a freq %u\n", clock_a); + clk_set_rate(clks->tahoe_clk, clock_a); + } else + clk_set_rate(clks->tahoe_clk, 666666666); + + clk_prepare_enable(clks->tahoe_clk); + + enc_pr(LOG_DEBUG, "tahoe: %ld\n", clk_get_rate(clks->tahoe_clk)); + + /* the power on*/ + pm_runtime_get_sync(&tahoe_pdev->dev); + mdelay(5); + enc_pr(LOG_INFO, "tahoe power stauts after poweron: %d\n", pm_runtime_active(&tahoe_pdev->dev)); + + /* reset */ + tahoe_hw_reset(); //__need_modify__ + /* gate the clocks */ +#ifdef TAHOE_SUPPORT_CLOCK_CONTROL + enc_pr(LOG_INFO, "tahoe_clk_enable, now gate off the clock\n"); + clk_disable(clks->tahoe_clk); +#endif +} + +void tahoe_clk_disable(struct tahoe_clks *clks, struct platform_device *tahoe_pdev) +{ +#ifdef TAHOE_SUPPORT_CLOCK_CONTROL + if (clock_gate_count > 0) +#endif + { + enc_pr(LOG_INFO, "tahoe unclosed clock %d\n", clock_gate_count); + clk_disable(clks->tahoe_clk); + clock_gate_count = 0; + } + clk_unprepare(clks->tahoe_clk); + if (clks->sys_tahoe != NULL) { + clk_disable_unprepare(clks->sys_tahoe); + } + /* the power off */ + pm_runtime_put_sync(&tahoe_pdev->dev); + mdelay(5); + enc_pr(LOG_INFO, "tahoe power stauts after poweroff: %d\n", pm_runtime_active(&tahoe_pdev->dev)); +} \ No newline at end of file diff --git a/tahoe_v4l/tahoe_common.h b/tahoe_v4l/tahoe_common.h new file mode 100644 index 0000000..c6c2440 --- /dev/null +++ b/tahoe_v4l/tahoe_common.h @@ -0,0 +1,213 @@ +/* + * 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: + */ + +#ifndef __TAHOE_COMMON_H_ +#define __TAHOE_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tahoe_assist.h" + +extern s32 clock_gate_count; +extern u32 clock_a; + +typedef enum { + MFDI_INPUT_FMT_NV21 = 0, + MFDI_INPUT_FMT_NV12 = 1, + MFDI_INPUT_FMT_YUV420_PLANAR = 2, + MFDI_INPUT_FMT_YUV444_PACKET = 3, + MFDI_INPUT_FMT_RGB565 = 4, + MFDI_INPUT_FMT_BGR565 = 5, + MFDI_INPUT_FMT_ARGB1555 = 6, + MFDI_INPUT_FMT_BRGA5551 = 7, + MFDI_INPUT_FMT_ARGB8888 = 8, + MFDI_INPUT_FMT_BGRA8888 = 9, + MFDI_INPUT_FMT_RGBA8888 = 10, + MFDI_INPUT_FMT_RGB888 = 11, + MFDI_INPUT_FMT_BGR888 = 12, + MFDI_INPUT_FMT_COM_4U4V = 13, + MFDI_INPUT_FMT_COM_8U8V = 14, +} img_format_e; + +typedef enum codec_type { + CODEC_ID_H264 = 0, + CODEC_ID_H265, +} codec_type_e; + + +struct tahoe_param { + int id; + int poc; + int width; + int height; + int stride; + int plane_num; + img_format_e fmt; //input + codec_type_e codec_type; // 0:264 1:265 + int bitrate; + int gop; + int fps; + uint8_t rc_mode; + uint8_t init_qp; + uint8_t fix_qp_I; + uint8_t fix_qp_P; + uint8_t profile; + uint8_t level; + uint8_t tier; + uint8_t coloraspect_enable; + uint8_t video_full_range_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + int force_idr; + int golden_match_flag; + int bytesperline[3]; + int imagesize[3]; + uint64_t inpt_buf_addr[3]; + uint64_t es_addr; + int es_buf_size; + int src_buf_index; + uint8_t separate_header; + uint8_t cfg_update; +}; + +struct venc_drv_buffer_t { + u32 size; + u32 cached; + uint64_t phys_addr; + uint64_t base; /* kernel logical address in use kernel */ + uint64_t virt_addr; /* virtual user space address */ +}; + +struct tahoe_clks { + struct clk *sys_tahoe; + struct clk *tahoe_clk; +}; + +struct tahoe_rsts { + struct reset_control *core_rst; + struct reset_control *a_rst; + struct reset_control *apb_rst; + struct reset_control *sys_rst; +}; + + +struct venc_drv_context_t { + struct fasync_struct *async_queue; + ulong interrupt_reason; + u32 interrupt_flag; + u32 open_count; /*!<< device reference count. Not instance count */ + atomic_t request_ready; + wait_queue_head_t request_complete; +}; + + +struct addr_info { + uint64_t vir_addr; + uint64_t phy_addr_alloc; + uint64_t phy_addr_act; + int size; + int alloc_size; +}; + + +struct tahoe_inst { + //struct list_head list; + struct v4l2_fh v4l2_fh; + struct v4l2_m2m_dev *v4l2_m2m_dev; + struct v4l2_ctrl_handler v4l2_ctrl_hdl; + struct tahoe_device *dev; + struct tahoe_param param; + struct venc_drv_buffer_t s_video_memory; //整个固件需要使用的buffer,当前申请了64M,传到固件中; + void (*output_f)(struct tahoe_inst *inst); + int last_is_done; + struct addr_info sps; + struct addr_info pps; + struct addr_info vps; + volatile uint8_t hw_proc_count; + //uint64_t o_vaddr[16]; + struct mutex inst_lock; + uint32_t inst_id; + uint8_t inst_init_flag; +}; + + +struct tahoe_device { + struct device *dev; + struct v4l2_device v4l2_dev; + struct video_device *vdev; + struct v4l2_m2m_dev *tahoe_m2m_dev; + struct venc_drv_buffer_t s_tahoe_register; + struct venc_drv_buffer_t fw_buffer; + struct tahoe_clks s_tahoe_clks; + struct tahoe_rsts s_tahoe_rsts; + int s_tahoe_irq; + u32 cma_cfg_size; + struct venc_drv_context_t s_tahoe_drv_context; + bool s_tahoe_irq_requested; + struct tahoe_inst *instance[6]; + struct mutex dev_lock; + unsigned char cur_inst; +}; + + +void tahoe_hw_reset(void); +void tahoe_clk_put(struct device *dev, struct tahoe_clks *clks); +int tahoe_clk_get(struct device *dev, struct tahoe_clks *clks); +int tahoe_rst_get(struct device *dev, struct tahoe_rsts *rsts); +void tahoe_clk_enable(struct tahoe_clks *clks, struct platform_device *tahoe_pdev); +void tahoe_clk_disable(struct tahoe_clks *clks, struct platform_device *tahoe_pdev); + +#endif \ No newline at end of file diff --git a/tahoe_v4l/tahoe_entry.c b/tahoe_v4l/tahoe_entry.c new file mode 100644 index 0000000..0706b22 --- /dev/null +++ b/tahoe_v4l/tahoe_entry.c @@ -0,0 +1,2212 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tahoe.h" +#include "tahoe_register.h" +#include "tahoe_hw.h" +#include "tahoe_assist.h" +#include "tahoe_common.h" + +#define IRQ_FRM_DONE_FBC 0x1 +#define IRQ_FRM_START 0x2 +#define IRQ_SCR_WRITE 0x4 +#define IRQ_RDMA_DONE 0x8 +#define IRQ_PRELOAD_DONE 0x10 +#define IRQ_ROWRC_1 0x20 +#define IRQ_ROWRC_2 0x40 +#define IRQ_FRM_DONE_EC 0x80 +#define IRQ_FRM_DONE_LPF 0x100 +#define IRQ_FRM_DONE_RDO 0x200 +#define IRQ_FRM_DONE_ME 0x400 +#define IRQ_SLICE_DONE_FBC 0x800 +#define IRQ_SLICE_DONE_EC 0x1000 +#define IRQ_SLICE_DONE_LPF 0x2000 +#define IRQ_SLICE_DONE_RDO 0x4000 +#define IRQ_SLICE_DONE_ME 0x8000 +#define IRQ_ES_OVERFLOW 0x10000 +#define IRQ_RESERVED 0x20000 +#define IRQ_LOW_LATENCY 0x40000 + +#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) + +//static struct venc_drv_buffer_t fw_buffer; //fw addr +#define MC_SIZE (4096 * 1024) +//static int s_interrupt_flag; +//static wait_queue_head_t s_interrupt_wait_q; + +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 struct venc_drv_buffer_t s_video_memory = {0}; //整个固件需要使用的buffer,当前申请了64M,传到固件中; +//static struct venc_drv_buffer_t s_tahoe_register = {0}; +static bool use_reserve = false; +static u32 cma_cfg_size; + + +static struct tasklet_struct tahoe_tasklet; +//static struct venc_bit_firmware_info_t s_bit_firmware_info; + + + +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; +}; + +//static struct tahoe_clks s_tahoe_clks; + +//static struct tahoe_rsts s_tahoe_rsts; + +//static void tahoe_clk_enable(struct tahoe_clks *clks); +//static void tahoe_clk_disable(struct tahoe_clks *clks); +//static s32 tahoe_free_buffers(struct file *filp); + + +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 +}; +#if 0 +static void dma_flushh(struct device *dev, u32 buf_start, u32 buf_size) +{ + if (dev) + dma_sync_single_for_device( + dev, buf_start, + buf_size, DMA_TO_DEVICE); +} + +static void cache_flushh(struct device *dev, u64 buf_start, u32 buf_size) +{ + if (dev) + dma_sync_single_for_cpu( + dev, buf_start, + buf_size, DMA_FROM_DEVICE); +} +#endif + + + +//__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); + + //struct vb2_manage_st *src_vb = (struct vb2_manage_cap_st *)src_buf; + //src_vb->inst_on = (uint64_t)inst; + // struct venc_drv_context_t *s_context = &dev->s_tahoe_drv_context; + for (uint8_t i = 0; i < /*dev->s_tahoe_drv_context.open_count*/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 == (/*dev->s_tahoe_drv_context.open_count*/MAX_INSTANCE - 1)) { + enc_pr(LOG_ALL, "cannot find the instance,return\n"); + return; + } + } + + enc_pr(LOG_ALL, "inst:%lx\n", (ulong)inst); + + atomic_inc(&dev->s_tahoe_drv_context.request_ready); + wake_up_interruptible(&dev->s_tahoe_drv_context.request_complete); + 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; + struct venc_drv_context_t *dev = &root_dev->s_tahoe_drv_context; + + dev->interrupt_reason = tahoe_reg_read(root_dev,TAHOE_VRISC_IRQ_STAT); + enc_pr(LOG_INFO, "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) { + //preload_irq_flag = 1; + enc_pr(LOG_INFO, "preload irq received, preload done\n"); + } + + if (dev->interrupt_reason & IRQ_SCR_WRITE) { + //scrw_irq_flag = 1; + enc_pr(LOG_INFO, "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); + } + //tasklet_schedule(&tahoe_tasklet); + enc_pr(LOG_ALL, "[-]%s\n", __func__); + + //wake_up_interruptible(&s_interrupt_wait_q); + 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[]) { + enc_pr(LOG_INFO,"enter function tahoe_vb2ops_queue_setup\n"); + 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) { + enc_pr(LOG_INFO,"enter function tahoe_vb2ops_buffer_prepare\n"); + 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; + + enc_pr(LOG_INFO,"enter function tahoe_vb2ops_buffer_queue\n"); + 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,"enter function 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 + + enc_pr(LOG_INFO,"enter function tahoe_vb2ops_start_streaming end\n"); + 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); +#if 0 + for (int i = 0;i < sizeof(inst->o_vaddr);i++) { + inst->o_vaddr[i] = 0; + } +#endif + } + + 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) +{ + //venc_info_t *venc = &s_venc_info; + unsigned int esaddr_offset = 0; + unsigned long esPhyAddr; + int eslength; + unsigned long espVirAddr = 0; + unsigned long slice_header_length = 0; + //venc_buffer_t vdb; + //memset(&vdb, 0x00, sizeof(venc_buffer_t)); + + 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; + + //cache_flushh(dev->dev,esPhyAddr,eslength); + 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); + //mutex_lock(&inst->inst_lock); + ulong es_vir_addr = dst_man_buf->viraddr; + //ulong es_vir_addr = inst->o_vaddr[index]; + //mutex_unlock(&inst->inst_lock); + 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 tahoe_param *param = &inst->param; + 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; + + //is_idr = ((inst->param.poc - 1) == 0 || (inst->param.poc - 1) % 30 == 0) ? true : false; + + get_bsbframe(inst->dev,inst, &es_addr, &es_len, &frame_type); + + if ((0 == frame_type) || (1 == frame_type)) { + is_idr = 1; + } + /* + * The source buffer will not be found in the ready-queue as it has been + * dropped after sending of the encode firmware command, locate it in + * the videobuf2 queue directly + */ + 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 = inst->o_vaddr[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/*v4l2_m2m_is_last_draining_src_buf(m2m_ctx,src_buf)*/) { //draining over + enc_pr(LOG_INFO,"daining last buffer done!\n"); + //v4l2_m2m_last_buffer_done(m2m_ctx,dst_buf); + + 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); + } + 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: + //if ((ctrl->val != inst->param.profile) && (ctrl->val != 0)) { + 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: { + // if ((ctrl->val != inst->param.level) && (ctrl->val != 0)) { + 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: + // if ((ctrl->val != inst->param.level) && (ctrl->val != 0)) { + 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) { + enc_pr(LOG_INFO, "tahoe_v4l_open \n"); + 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_DEBUG, "[+] %s, filp=%lu, %lu, f_count=%lld\n", __func__, + //(unsigned long)filp, ( ((unsigned long)filp)%8), filp->f_count.counter); + 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; + enc_pr(LOG_INFO,"output_f:%lx\n",P_T_UL(inst_info->output_f)); + 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); + //filp->private_data = (void *)(&dev->s_tahoe_drv_context); + 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)memremap(inst_info->s_video_memory.phys_addr, inst_info->s_video_memory.size, MEMREMAP_WB); + 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_DEBUG, "allocating phys 0x%llx, ", inst_info->s_video_memory.phys_addr); + enc_pr(LOG_DEBUG, "virt addr 0x%llx, size %dk\n", inst_info->s_video_memory.virt_addr, inst_info->s_video_memory.size >> 10); + } 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) { + //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, "allocating fw phys 0x%llx,size:%d \n", dev->fw_buffer.phys_addr,dev->fw_buffer.size); + enc_pr(LOG_DEBUG, "virt addr 0x%llx, size %dk\n", dev->fw_buffer.virt_addr, dev->fw_buffer.size >> 10); + } 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) { + 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; + } + + /* enable tahoe clks and power*/ + tahoe_clk_enable(&dev->s_tahoe_clks, tahoe_pdev); + tahoe_top_sw_reset(dev); + } + + 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)); + } + + //mutex_init(&inst_info->inst_lock); + + enc_pr(LOG_INFO,"output_f:%lx 1111\n",(ulong)inst_info->output_f); + enc_pr(LOG_DEBUG, "[-] %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--; + + 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); + 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); + 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); + 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); + 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); + 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); + + // mutex_destroy(&pinst->inst_lock); + + if (pinst) { + for (int i = 0;i < /*dev->s_tahoe_drv_context.open_count*/MAX_INSTANCE;i++) { + if (dev->instance[i] && (dev->instance[i] == pinst)) { + dev->instance[i] = NULL; + } + } + kfree(pinst); + + } + + filp->private_data = NULL; + + enc_pr(LOG_INFO, "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,//tahoe_poll, +.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ +.mmap = v4l2_m2m_fop_mmap,//tahoe_mmap, +}; + +static int tahoe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + enc_pr(LOG_INFO,"enter function tahoe_querycap\n"); + 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; + enc_pr(LOG_INFO,"leave function tahoe_querycap\n"); + return 0; +} + +static int tahoe_enum_fmt_vid(struct file *file, void *fh, struct v4l2_fmtdesc *f) { + enc_pr(LOG_INFO,"enter function tahoe_enum_fmt_vid_cap\n"); + + 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,"enter 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) { + enc_pr(LOG_INFO,"enter function tahoe_g_fmt_vid_cap\n"); + + 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) { + enc_pr(LOG_INFO,"enter function tahoe_g_fmt_vid_out\n"); + 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,"enter 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) { + enc_pr(LOG_INFO,"enter function tahoe_try_fmt_vid_cap,fmt:%d\n",f->fmt.pix_mp.pixelformat); + + 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) { + enc_pr(LOG_INFO,"enter function tahoe_try_fmt_vid_out,fmt:%d,plane_num:%d\n",f->fmt.pix_mp.pixelformat,f->fmt.pix_mp.num_planes); + int width; + int height; + const struct v4l2_format_info *info = v4l2_format_info(f->fmt.pix_mp.pixelformat); + + 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].bytesperline = width * 4; + 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].bytesperline = width * 3; + f->fmt.pix_mp.plane_fmt[0].sizeimage = width * height * 3; + } + else { + //f->fmt.pix_mp.plane_fmt[0].bytesperline = width; + 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].bytesperline = width; + 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].bytesperline = width; + 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; +} + +#define valid_color(a) + +static int tahoe_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { + enc_pr(LOG_INFO,"enter function tahoe_s_fmt_vid_cap,format:%d\n",f->fmt.pix_mp.pixelformat); + 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) { + enc_pr(LOG_INFO,"enter function 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); + struct tahoe_inst *pinst = GET_PRI(fh); + int i = 0; + + 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) +{ + enc_pr(LOG_INFO,"tahoe_enc_s_parm enter\n"); + 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) { + 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); + //struct tahoe_device *dev = video_drvdata(file); + struct tahoe_inst *inst = GET_PRI(file->private_data); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + //mutex_lock(&inst->inst_lock); + 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); + #if 0 + int index = buf->index; + struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_dst_vq(m2m_ctx), index); + struct vb2_manage_cap_st *man_vb = (struct vb2_manage_cap_st *)vb; + + enc_pr(LOG_ERROR,"vb:%llx,vb plane_num:%d\n",(uint64_t)vb,vb->num_planes); + man_vb->viraddr = (uint64_t)vb2_plane_vaddr(vb, 0); + //inst->o_vaddr[index] = (uint64_t)vb2_plane_vaddr(vb, 0); + enc_pr(LOG_ERROR,"vaddr:%llx,index:%d",man_vb->viraddr,index); + #endif + //} + } + 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); + } + //mutex_unlock(&inst->inst_lock); + 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,//v4l2_m2m_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,//v4l2_m2m_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, +#if 0 +.vidioc_reqbufs = NULL,//vidioc_reqbufs, +.vidioc_querybuf = NULL,//vidioc_querybuf, +.vidioc_qbuf = NULL,//vidioc_qbuf, +.vidioc_expbuf = NULL,//vb2_ioctl_expbuf, +.vidioc_dqbuf = NULL,//vidioc_dqbuf, +.vidioc_s_std = NULL,//vidioc_s_std, +.vidioc_queryctrl = NULL,//vidioc_queryctrl, +.vidioc_g_ctrl = NULL,//vidioc_g_ctrl, +.vidioc_s_ctrl = NULL,//vidioc_s_ctrl, +.vidioc_g_ext_ctrls = NULL,//videoenc_g_ext_ctrls, +.vidioc_s_ext_ctrls = NULL,//videoenc_s_ext_ctrls, +.vidioc_streamon = NULL,//vidioc_streamon, +.vidioc_streamoff = NULL,//vidioc_streamoff, +.vidioc_enum_framesizes = NULL,//vidioc_enum_framesizes, +.vidioc_enum_frameintervals = NULL,//vidioc_enum_frameintervals, +.vidioc_s_input = NULL,//vidioc_s_input, +.vidioc_g_input = NULL,//vidioc_g_input, +.vidioc_g_selection = NULL,//vidioc_g_selection, +.vidioc_s_selection = NULL,//vidioc_s_selection, +.vidioc_s_output = NULL,//vidioc_s_output, +.vidioc_subscribe_event = NULL,//vidioc_subscribe_event, +.vidioc_unsubscribe_event = NULL,//vidioc_unsubscribe_event, +#endif +}; + + +#if 0 +static struct video_device tahoe_template = { +.name = "tahoe", +.fops = &tahoe_v4l_fops, +.ioctl_ops = &tahoe_ioctl_ops, +.release = video_device_release, +.tvnorms = V4L2_STD_525_60, +/* .current_norm = V4L2_STD_NTSC_M, */ +}; +#endif + + + +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; + } + //frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } 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); + + //frame_buf.stride = stride; + //pic_param.src_idx = src_buf->vb2_buf.index; + } + + + //mutex_lock(&dev->dev_lock); + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); + if (!dst_buf) { + //mutex_unlock(&dev->dev_lock); + 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; + + //enc_pr(LOG_ERROR,"vb:%llx,vb plane_num:%d\n",(uint64_t)vb,vb->num_planes); + man_vb->viraddr = (uint64_t)vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + enc_pr(LOG_INFO,"vaddr:%llx",man_vb->viraddr); + + //mutex_unlock(&dev->dev_lock); + 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); + //dma_flushh(dev->dev, param->es_addr, param->es_buf_size); + + if (!inst->inst_init_flag) { + create_channel(inst->dev,inst); + inst->inst_init_flag = 1; + } + encode_one_frame(dev,inst); + + //if (src_buf) { + //enc_pr(LOG_ERROR,"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); + // } + 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, reg_count; + struct resource res; + struct device_node *np, *child; + // struct video_device *vdev = NULL; + 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; + + //memset(&s_video_memory, 0, sizeof(struct venc_drv_buffer_t)); + //memset(&s_tahoe_register, 0, sizeof(struct venc_drv_buffer_t)); + //memset(&s_bit_firmware_info, 0, sizeof(s_bit_firmware_info)); + memset(&res, 0, sizeof(struct resource)); + + 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_video_memory, 0, sizeof(struct venc_drv_buffer_t)); + 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)); +#if 0 + dev->s_tahoe_register.phys_addr = TAHOE_REG_BASE_ADDR; + dev->s_tahoe_register.virt_addr = (unsigned long)ioremap(s_tahoe_register.phys_addr, TAHOE_REG_SIZE); + dev->s_tahoe_register.size = TAHOE_REG_SIZE; + + enc_pr(LOG_ERROR, "s_venc_register.phys_addr = 0x%lx\n", dev->s_tahoe_register.phys_addr); + enc_pr(LOG_ERROR, "s_venc_register.virt_addr = 0x%lx, size = 0x%x\n", dev->s_tahoe_register.virt_addr, TAHOE_REG_SIZE); +#endif + /* 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"); + + reg_count = 0; + np = pdev->dev.of_node; + + for_each_child_of_node(np, child) { + if (of_address_to_resource(child, 0, &res) || (reg_count > 1)) { + enc_pr(LOG_ERROR, "no reg ranges or more reg ranges %d\n", reg_count); + err = -ENXIO; + goto ERROR_PROVE_DEVICE; + } + + /* if platform driver is implemented */ + if (res.start != 0) { + dev->s_tahoe_register.phys_addr = res.start; + enc_pr(LOG_DEBUG,"res.start:%llx,dev->s_tahoe_register.phys_addr:%llx,size:%llx\n",res.start,dev->s_tahoe_register.phys_addr,resource_size(&res)); + dev->s_tahoe_register.virt_addr = (ulong)ioremap(res.start, resource_size(&res)); + dev->s_tahoe_register.size = res.end - res.start; + + enc_pr(LOG_DEBUG, "tahoe base address get from platform driver "); + enc_pr(LOG_DEBUG, "physical addr=0x%llx, virtual addr=0x%llx\n", + dev->s_tahoe_register.phys_addr, dev->s_tahoe_register.virt_addr); + } else { + dev->s_tahoe_register.phys_addr = TAHOE_REG_BASE_ADDR; + dev->s_tahoe_register.virt_addr = (ulong)ioremap(dev->s_tahoe_register.phys_addr, TAHOE_REG_SIZE); + dev->s_tahoe_register.size = TAHOE_REG_SIZE; + + enc_pr(LOG_DEBUG, "tahoe base address get from defined value "); + enc_pr(LOG_DEBUG, "physical addr=0x%llx, virtual addr=0x%llx\n", + dev->s_tahoe_register.phys_addr, dev->s_tahoe_register.virt_addr); + } + reg_count++; + } + + /* 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"); + + /* get the major number of the character device */ + /* if (init_tahoe_device()) { + err = -EBUSY; + enc_pr(LOG_ERROR, "could not allocate major number\n"); + + goto ERROR_PROVE_DEVICE; + }*/ + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + DEVICE_NAME); + + //platform_set_drvdata(pdev, &dev->v4l2_dev); + + 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); +#if 0 + vb2_q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vb2_q.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + + vb2_q.dev = &pdev->dev; + vb2_q.drv_priv = &s_tahoe_drv_context;//aml2_dev; + vb2_q.buf_struct_size = sizeof(struct vb2_manage_st);//sizeof(struct amlvideo2_node_buffer); + vb2_q.ops = &tahoe_vb2ops; + vb2_q.mem_ops = &vb2_dma_contig_memops; + vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q.min_queued_buffers = 1; + vb2_q.lock = &q_lock; + + ret = vb2_queue_init(&vb2_q); + if (ret < 0) { + enc_pr(LOG_INFO, "%s: vb2_queue_init fail\n", __func__); + goto ERROR_PROVE_DEVICE; + } +#endif + dev->vdev = video_device_alloc(); + if (!dev->vdev) { + goto ERROR_PROVE_DEVICE; + } + + //*dev->vdev = tahoe_template; + 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->queue = &vb2_q; + 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; + //return ret; + } + + 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); + //uninit_tahoe_device(); + 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 0 + if (s_video_memory.phys_addr) { + codec_mm_free_for_dma( + DEVICE_NAME, + s_video_memory.phys_addr); + memset(&s_video_memory, + 0, sizeof(struct venc_drv_buffer_t)); + } +#endif + 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); + //tahoe_pdev = NULL; + 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); + //uninit_tahoe_device(); + 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); diff --git a/tahoe_v4l/tahoe_hw.c b/tahoe_v4l/tahoe_hw.c new file mode 100644 index 0000000..11a58c7 --- /dev/null +++ b/tahoe_v4l/tahoe_hw.c @@ -0,0 +1,405 @@ +/* + * 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 +#include +#include + +#include "tahoe_register.h" +#include "tahoe_hw.h" + +#define PRELOAD_SIZE 64 + +#define FORCE_IDR_TYPE 2 + +void disable_host_scrw_irq(struct tahoe_device *dev) +{ + tahoe_reg_write(dev, TAHOE_VRISC_IRQ_MASK, + 0 | //frme + 1 << 1 | //frms + 1 << 2 | //scrw + 1 << 3 | //rdma + 1 << 4); //prel +} + +void enable_vrisc_scrw_irq(struct tahoe_device *dev) +{ + tahoe_reg_write(dev, TAHOE_VRISC_EIRQ_MASK, + 0 | //frme + 0 << 1 | //frms + 0 << 2 | //scrw + 0 << 3 | //rdma + 0 << 4); //prel +} + + + +int tahoe_load_fw(struct tahoe_device *dev, const char *fw_name) +{ + const struct firmware *fw; + int ret; + char *test = NULL; + + enc_pr(LOG_ERROR, "tahoe_load_firmware %s\n",fw_name); + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) { + enc_pr(LOG_ERROR, "request_firmware, fail: %d\n", ret); + return ret; + } + enc_pr(LOG_ERROR,"request_firmware success,len:%zu,data:%lx\n",fw->size,(unsigned long)fw->data); + + enc_pr(LOG_ERROR,"dev->fw_buffer.virt_addr:%llx\n",dev->fw_buffer.virt_addr); + memcpy((void*)dev->fw_buffer.virt_addr, fw->data, fw->size); + test = (char *)dev->fw_buffer.virt_addr; + enc_pr(LOG_ERROR,"tahoe_load_fw,%llx %px\n",dev->fw_buffer.virt_addr,(void*)dev->fw_buffer.virt_addr); + enc_pr(LOG_ERROR,"fw content:%x %x %x %x %x %x %x %x\n",test[0],test[1],test[2],test[3],test[4],test[5],test[6],test[7]); + + return 0; +} + + + +void create_channel(struct tahoe_device *dev,struct tahoe_inst *pinst) +{ + int data = 0; + struct tahoe_param *param = &pinst->param; + enc_pr(LOG_ERROR,"[%s][%d]create_channel start\n", __func__, __LINE__); + //venc_info_t *venc = &s_venc_info; + disable_host_scrw_irq(dev); + + tahoe_reg_write(dev, CHN_ID, param->id); + tahoe_reg_write(dev, ENC_FMT, param->codec_type); // 0: h264; 1: h265; + tahoe_reg_write(dev, MFDIN_INPUT_FMT, param->fmt); //input_fmt + tahoe_reg_write(dev, ENC_PIC_W, param->width); + tahoe_reg_write(dev, ENC_PIC_H, param->height); + + tahoe_reg_write(dev, MEM_POOL_ADDR_MSB, ((pinst->s_video_memory.phys_addr >> 32) & 0x03)); //addr bit num flag: tahoe:1, c4:0 + tahoe_reg_write(dev, MEM_POOL_ADDR_LSB, pinst->s_video_memory.phys_addr); //fw memory pool + enc_pr(LOG_ERROR,"channel %d, fw addr begin:%lx,codec type:%d\n",param->id,(ulong)pinst->s_video_memory.phys_addr,param->codec_type); + + tahoe_reg_write(dev, ENC_BITRATE, param->bitrate); //need default value + + tahoe_reg_write(dev, ENC_FPS, param->fps); + tahoe_reg_write(dev, ENC_GOP, param->gop); + + tahoe_reg_write(dev, ENC_PROFILE, param->profile); + tahoe_reg_write(dev, ENC_LEVEL, (param->level & 0xff) << 0 | (param->tier & 0xff ) << 16); + + tahoe_reg_write(dev, ENC_RC_MODE, (param->rc_mode & 0xff) | + (param->init_qp & 0xff) << 8 | + (param->fix_qp_I & 0xff) << 16 | + (param->fix_qp_P & 0xff) << 24); + + tahoe_reg_write(dev, MFDI_ROTATION_MODE, 0/*(handle->rotation_mode & 0x3) << 1 | + (handle->mirror_mode & 0x3) << 3*/); + + tahoe_reg_write(dev, MFDI_CROP_START, 0/*(handle->crop_x & 0x3fff) << 0 | + (handle->crop_y & 0x3fff) << 16*/); + tahoe_reg_write(dev, ENC_PARA_CROP_WH, 0/*(handle->crop_w & 0x3fff) << 0 | + (handle->crop_h & 0x3fff) << 16*/); + + tahoe_reg_write(dev, CRE_COLOR_ASPECT,(param->coloraspect_enable | (param->video_full_range_flag << 1) | (param->colour_primaries << 8) | + (param->transfer_characteristics << 16) | (param->matrix_coefficients << 24))); + + tahoe_reg_write(dev, GOLDEN_MATCH_FLAG, param->golden_match_flag); + + enc_pr(LOG_ERROR,"create_channel profile:%d,level:%d,bitrate:%d,fps:%d,gop:%d\n",param->profile,param->level,param->bitrate,param->fps,param->gop); + enable_vrisc_scrw_irq(dev); + + tahoe_reg_write(dev, CMD_OPT_CODE, CMD_CREATE_CHN); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + } + + param->poc = 0; + enc_pr(LOG_ERROR,"[%s][%d]create_channel end\n", __func__, __LINE__); +} + + + +void encode_header(struct tahoe_device *dev, struct tahoe_inst *inst) +{ + int data = 0; + + disable_host_scrw_irq(dev); + tahoe_reg_write(dev, CHN_ID, inst->param.id); + +//for vps/sps/pps + tahoe_reg_write(dev, HEADER_VPS_ADDR, inst->vps.phy_addr_alloc); + tahoe_reg_write(dev, HEADER_SPS_ADDR, inst->sps.phy_addr_alloc); + tahoe_reg_write(dev, HEADER_PPS_ADDR, inst->pps.phy_addr_alloc); + tahoe_reg_write(dev, HEADER_ADDR_HIGHER, (((inst->sps.phy_addr_alloc >> 32) & 0x3) | \ + (((inst->pps.phy_addr_alloc >> 32) & 0x03) << 2) | \ + (((inst->vps.phy_addr_alloc >> 32) & 0x03) << 4))); + enc_pr(LOG_INFO,"[%s][%d]handle->header_buff 0:0x%llx, 1:0x%llx, 2:0x%llx\n", \ + __func__,__LINE__, + inst->vps.phy_addr_alloc, \ + inst->sps.phy_addr_alloc, \ + inst->pps.phy_addr_alloc); + + enable_vrisc_scrw_irq(dev); + tahoe_reg_write(dev, CMD_OPT_CODE, CMD_GET_ENC_PARAMETERS); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + } + + enc_pr(LOG_INFO,"encode header done\n"); +} + + +void dynamic_parameter_set(struct tahoe_device *dev,struct tahoe_param *param) +{ + int data = 0; + + disable_host_scrw_irq(dev); + tahoe_reg_write(dev,CHN_ID, param->id); + tahoe_reg_write(dev,ENC_PARAM_FPS, param->fps); + tahoe_reg_write(dev,ENC_PARAM_GOP, param->gop); + tahoe_reg_write(dev,ENC_PARAM_BITRATE, param->bitrate); + tahoe_reg_write(dev,ENC_PARAM_RC_MODE, (param->rc_mode & 0xff)); + enc_pr(LOG_INFO,"[%s][%d]param->fps:%d, param->gop:%d, param->bitrate:%d, param->rc_mode:%d\n",__func__,__LINE__, + param->fps, param->gop, param->bitrate, param->rc_mode); + enable_vrisc_scrw_irq(dev); + tahoe_reg_write(dev,CMD_OPT_CODE, CMD_ENC_PARAMETER_SETS); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + } +} + + +int get_header(struct tahoe_device *dev, struct tahoe_inst *inst) +{ + //unsigned long vpsPhyAddr, spsPhyAddr, ppsPhyAddr; + int headerLength = 0;//, vpsLength, spsLength, ppsLength; + //unsigned int headeroffset = 0; + //unsigned long headerVirAddr = 0; +#if 0 + inst->vps.phy_addr_act = tahoe_reg_read(dev, HEADER_VPS_ADDR); + inst->sps.phy_addr_act = tahoe_reg_read(dev,HEADER_SPS_ADDR); + inst->pps.phy_addr_act = tahoe_reg_read(dev,HEADER_PPS_ADDR); +#endif + inst->vps.phy_addr_act = ((((uint64_t)(tahoe_reg_read(dev, HEADER_ADDR_HIGHER) >> 4) & 0x03)) << 32) | tahoe_reg_read(dev,HEADER_VPS_ADDR); + inst->sps.phy_addr_act = (((uint64_t)(tahoe_reg_read(dev, HEADER_ADDR_HIGHER) & 0x03)) << 32) | tahoe_reg_read(dev,HEADER_SPS_ADDR); + inst->pps.phy_addr_act = ((((uint64_t)(tahoe_reg_read(dev, HEADER_ADDR_HIGHER) >> 2) & 0x03)) << 32) | tahoe_reg_read(dev,HEADER_PPS_ADDR); + + headerLength = tahoe_reg_read(dev,HEADER_SIZE); + enc_pr(LOG_INFO,"[%s][%d]vpsPhyAddr:0x%llx\n", __func__, __LINE__,inst->vps.phy_addr_act); + enc_pr(LOG_INFO,"[%s][%d]spsPhyAddr:0x%llx\n", __func__, __LINE__,inst->sps.phy_addr_act); + enc_pr(LOG_INFO,"[%s][%d]ppsPhyAddr:0x%llx\n", __func__, __LINE__,inst->pps.phy_addr_act); + enc_pr(LOG_INFO,"[%s][%d]ppsPhyAddr:0x%x\n", __func__, __LINE__,headerLength); + +//00 vps sps pps,各占一个字节,最高8位没有 + inst->vps.size = (headerLength & 0xff0000) >> 16; + inst->sps.size = (headerLength & 0xff00) >> 8; + inst->pps.size = (headerLength & 0xff); +#if 0 + if (handle->enc_fmt == CODEC_ID_H265) { + vdi_read_memory(vpsPhyAddr, pHeader, vpsLength); + vdi_read_memory(spsPhyAddr, pHeader+vpsLength, spsLength); + vdi_read_memory(ppsPhyAddr, pHeader+vpsLength+spsLength, ppsLength); + *pLength = vpsLength + spsLength + ppsLength; + } else if (handle->enc_fmt == CODEC_ID_H264) { + vdi_read_memory(spsPhyAddr, pHeader, spsLength); + vdi_read_memory(ppsPhyAddr, pHeader+spsLength, ppsLength); + *pLength = spsLength + ppsLength; + } +#endif + enc_pr(LOG_INFO,"[%s][%d]headerLength:%d %d %d\n", __func__, __LINE__,inst->vps.size,inst->sps.size,inst->pps.size); + return 0; +} + + +void encode_one_frame(struct tahoe_device *dev,struct tahoe_inst *pinst) +{ + int data = 0; + int slice_qp = 0; + struct tahoe_param *param = &pinst->param; + + if (pinst->param.poc == 0) { + encode_header(dev,pinst); + get_header(dev,pinst); + } + + if (param->cfg_update == 1) { + dynamic_parameter_set(dev, param); + param->cfg_update = 0; + } + + tahoe_reg_write(dev, TAHOE_CFG, + 1 << 0 | //prp_enable + 1 << 1 | //imd_enable + 0 << 2 | //me_enable + 0 << 3 | //rdo_enable + 0 << 4 | //lpf_enable + 1 << 5 | //tctrl_enable + 1 << 6 | //fbcd_enable + 0 << 7 ); //ec_enable + disable_host_scrw_irq(dev); + tahoe_reg_write(dev, CHN_ID, param->id); + + ds_config(dev); + + enc_pr(LOG_INFO,"[%s][%d]handle->mfdin_input_fmt %d\n", __func__,__LINE__, param->fmt); + if ((param->fmt == MFDI_INPUT_FMT_NV12) || + (param->fmt == MFDI_INPUT_FMT_NV21)) { + tahoe_reg_write(dev, PRP_MFDI_STRIDE_0, /*handle->buf_stride*/param->stride); + tahoe_reg_write(dev, PRP_MFDI_STRIDE_1, /*handle->buf_stride*/param->stride); + } else if (param->fmt == MFDI_INPUT_FMT_YUV420_PLANAR) { + tahoe_reg_write(dev, PRP_MFDI_STRIDE_0, /*handle->buf_stride*/param->stride); + tahoe_reg_write(dev, PRP_MFDI_STRIDE_1, /*handle->buf_stride*/param->stride / 2); + tahoe_reg_write(dev, PRP_MFDI_STRIDE_2, /*handle->buf_stride*/param->stride / 2); + } else if (param->fmt == MFDI_INPUT_FMT_RGB888) { + tahoe_reg_write(dev, PRP_MFDI_STRIDE_0, /*handle->buf_stride*/param->stride * 3); + } else if (param->fmt == MFDI_INPUT_FMT_RGBA8888) { + tahoe_reg_write(dev, PRP_MFDI_STRIDE_0, /*handle->buf_stride*/param->stride * 4); + } + + if (param->force_idr) { + tahoe_reg_write(dev, ENC_PARA_FRAME_TYPE, FORCE_IDR_TYPE); + param->force_idr = 0; + enc_pr(LOG_INFO,"[%s][%d]encode_one_frame force idr %d \n", __func__, __LINE__, param->poc); + } + + if (param->poc <= 1) + tahoe_reg_write(dev, TAHOE_DQME_SEARCH_CFG, 0x840); + else { + if (param->codec_type == CODEC_ID_H264) + tahoe_reg_write(dev, TAHOE_DQME_SEARCH_CFG, 0x9a840); + else + tahoe_reg_write(dev, TAHOE_DQME_SEARCH_CFG, 0x66840); + } + + tahoe_reg_write(dev, TAHOE_VRISC_SCRATCH_29, param->inpt_buf_addr[0]);//传yuv的地址; + tahoe_reg_write(dev, TAHOE_VRISC_SCRATCH_30, param->inpt_buf_addr[1]); + tahoe_reg_write(dev, TAHOE_VRISC_SCRATCH_31, param->inpt_buf_addr[2]); + tahoe_reg_write(dev, INPUT_BUF_ADDR_MSB, ((param->inpt_buf_addr[0] >> 32) & 0x03) | \ + (((param->inpt_buf_addr[1] >> 32) & 0x03) << 2) | \ + (((param->inpt_buf_addr[2] >> 32) & 0x03) << 4)); + enc_pr(LOG_INFO,"[%s][%d]y:0x%lx, u:0x%lx, v:0x%lx, slice_qp:%d\n", \ + __func__,__LINE__, + (ulong)param->inpt_buf_addr[0], \ + (ulong)param->inpt_buf_addr[1], \ + (ulong)param->inpt_buf_addr[2], slice_qp); + + //venc_write_register(MFDI_ROTATION_MODE, (handle->rotation_mode & 0x3) << 1 | + // (handle->mirror_mode & 0x3) << 3); + + //venc_write_register(MFDI_CROP_START, (handle->crop_x & 0x3fff) << 0 | + // (handle->crop_y & 0x3fff) << 16); + //venc_write_register(ENC_PARA_CROP_WH, (handle->crop_w & 0x3fff) << 0 | + // (handle->crop_h & 0x3fff) << 16); + + enable_vrisc_scrw_irq(dev); + tahoe_reg_write(dev, CMD_OPT_CODE, CMD_ENC_ONE_FRAME); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + } + enc_pr(LOG_INFO,"[%s][%d]encode_one_frame end\n", __func__, __LINE__); +} + +void vrisc_reset(struct tahoe_device *dev) +{ + tahoe_reg_write(dev,TAHOE_VRISC_CORECFG, 0); +} + + +void destroy_channel(struct tahoe_device *dev, struct tahoe_inst *pinst) +{ + int data = 0; + struct tahoe_param *param = &pinst->param; + enc_pr(LOG_INFO,"[%s][%d]destroy_channel start\n", __func__, __LINE__); + disable_host_scrw_irq(dev); + + tahoe_reg_write(dev, CHN_ID, param->id); + + enable_vrisc_scrw_irq(dev); + + tahoe_reg_write(dev, CMD_OPT_CODE, CMD_DESTROY_CHN); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + } + enc_pr(LOG_INFO,"[%s][%d]destroy_channel end\n", __func__, __LINE__); +} + + + +void tahoe_top_sw_reset(struct tahoe_device *dev) +{ + tahoe_reg_write(dev, TAHOE_TOP_SW_RST, 0x3FFF); + tahoe_reg_write(dev, TAHOE_TOP_SW_RST, 0); +} + +int tahoe_boot(struct tahoe_device *dev) { + int data = 0; + enc_pr(LOG_ERROR, "tahoe boot begin,reg addr:%llx!\n",dev->s_tahoe_register.phys_addr); + //venc_info_t *venc = &s_venc_info; + tahoe_reg_write(dev, TAHOE_VRISC_MBASE, dev->fw_buffer.phys_addr); + tahoe_reg_write(dev, TAHOE_VRISC_MISC, ((dev->fw_buffer.phys_addr >> 32 & 0x03) << 16)); + + tahoe_reg_write(dev, TAHOE_VRISC_MPRELOAD, + (PRELOAD_SIZE << 0) | // mpreload_size_m1 + (0 << 12) | // mpreload_axi_id + (1 << 31)); // mpreload_enable); + data = tahoe_reg_read(dev, TAHOE_VRISC_MPRELOAD) >> 31; + + while (0 != data) { + data = tahoe_reg_read(dev, TAHOE_VRISC_MPRELOAD) >> 31; + //usleep(1000); + } + enc_pr(LOG_ERROR, "tahoe preload OK!\n"); + #if 1 + tahoe_reg_write(dev, TAHOE_VRISC_CORECFG, ((1 << 0) | // stop_on_reset release + (0 << 1) | // clkgate_bypass + (0 << 2) | // reset_bypass + (0 << 3) )); // reserved + + enc_pr(LOG_ERROR, "tahoe reset ok!\n"); + data = tahoe_reg_read(dev, CMD_OPT_CODE); + while (CMD_DONE != data) { + data = tahoe_reg_read(dev, CMD_OPT_CODE); + //usleep(100); + } + #endif + enc_pr(LOG_ERROR, "tahoe boot OK!\n"); + return 0; +} + + +void ds_config(struct tahoe_device *dev) +{ + tahoe_reg_write(dev, DS_PARENT_CHN, 0); + #if 0 + if (handle) { + printf("[%s][%d]handle->ds_h:%d\n",__func__,__LINE__, handle->ds_h); + printf("[%s][%d]handle->ds_w:%d\n",__func__,__LINE__, handle->ds_w); + printf("[%s][%d]handle->ds_osd:%d\n",__func__,__LINE__, handle->ds_osd); + printf("[%s][%d]handle->ds_only:%d\n",__func__,__LINE__, handle->ds_only); + //if (handle->ds_w > 0 && handle->ds_h > 0) + venc_write_register(DS_PARENT_CHN, ((handle->ds_h & 0xfff) << 14) | ((handle->ds_w & 0xfff) << 2) | ((handle->ds_osd & 0x1) << 1) | (handle->ds_only & 0x1)); + } + #endif +} \ No newline at end of file diff --git a/tahoe_v4l/tahoe_hw.h b/tahoe_v4l/tahoe_hw.h new file mode 100644 index 0000000..02d3161 --- /dev/null +++ b/tahoe_v4l/tahoe_hw.h @@ -0,0 +1,46 @@ +/* + * 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: + */ + +#ifndef __TAHOE_HW_H_ +#define __TAHOE_HW_H_ + +#include +#include "tahoe_assist.h" +#include "tahoe_common.h" + + +#define tahoe_reg_read(dev,addr) readl((void __iomem *)(dev->s_tahoe_register.virt_addr + addr)) + +#define tahoe_reg_write(dev,addr, val) writel((u32)val, (void __iomem *)(dev->s_tahoe_register.virt_addr + addr)) + + +int tahoe_load_fw(struct tahoe_device *dev, const char *fw_name); +int tahoe_boot(struct tahoe_device *dev); +void create_channel(struct tahoe_device *dev,struct tahoe_inst *pinst); +void encode_header(struct tahoe_device *dev, struct tahoe_inst *inst); +void dynamic_parameter_set(struct tahoe_device *dev,struct tahoe_param *param); +void encode_one_frame(struct tahoe_device *dev,struct tahoe_inst *pinst); +void vrisc_reset(struct tahoe_device *dev); +void destroy_channel(struct tahoe_device *dev, struct tahoe_inst *pinst); +void tahoe_top_sw_reset(struct tahoe_device *dev); +int get_header(struct tahoe_device *dev, struct tahoe_inst *inst); +void ds_config(struct tahoe_device *dev); + +#endif \ No newline at end of file diff --git a/tahoe_v4l/tahoe_register.h b/tahoe_v4l/tahoe_register.h new file mode 100644 index 0000000..b9fbbb2 --- /dev/null +++ b/tahoe_v4l/tahoe_register.h @@ -0,0 +1,280 @@ +/* + * 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: + */ +#ifndef __TAHOE_REGISTER_H__ +#define __TAHOE_REGISTER_H__ + +typedef union TAHOE_VRISC_MISC_FIELD { + unsigned int data; + struct { + unsigned int soft_reset : 16; + unsigned int baseaddr_ilm_higher : 2; + unsigned int baseaddr_rdma_higher : 2; + unsigned int scatchw_irq_mask : 1; + unsigned int vrisc_misc_rsv : 11; + } b; +} TAHOE_VRISC_MISC_FIELD_T; + + +#define TAHOE_VRISC_BASE 0x0000 + +#define TAHOE_VRISC_HAO (TAHOE_VRISC_BASE + 0x00) +#define TAHOE_VRISC_MISC (TAHOE_VRISC_BASE + 0x04) +#define TAHOE_VRISC_MPRELOAD (TAHOE_VRISC_BASE + 0x08) +#define TAHOE_VRISC_MBASE (TAHOE_VRISC_BASE + 0x0c) //fw addr +#define TAHOE_VRISC_RSTVEC (TAHOE_VRISC_BASE + 0x10) +#define TAHOE_VRISC_CORECFG (TAHOE_VRISC_BASE + 0x14) //__func__??? +#define TAHOE_VRISC_RDMABASE (TAHOE_VRISC_BASE + 0x18) +#define TAHOE_VRISC_RDMACMD (TAHOE_VRISC_BASE + 0x1c) +#define TAHOE_VRISC_SCRATCH_0 (TAHOE_VRISC_BASE + 0x20) +#define TAHOE_VRISC_SCRATCH_1 (TAHOE_VRISC_BASE + 0x24) +#define TAHOE_VRISC_SCRATCH_2 (TAHOE_VRISC_BASE + 0x28) +#define TAHOE_VRISC_SCRATCH_3 (TAHOE_VRISC_BASE + 0x2c) +#define TAHOE_VRISC_SCRATCH_4 (TAHOE_VRISC_BASE + 0x30) +#define TAHOE_VRISC_SCRATCH_5 (TAHOE_VRISC_BASE + 0x34) +#define TAHOE_VRISC_SCRATCH_6 (TAHOE_VRISC_BASE + 0x38) +#define TAHOE_VRISC_SCRATCH_7 (TAHOE_VRISC_BASE + 0x3c) +#define TAHOE_VRISC_SCRATCH_8 (TAHOE_VRISC_BASE + 0x40) +#define TAHOE_VRISC_SCRATCH_9 (TAHOE_VRISC_BASE + 0x44) +#define TAHOE_VRISC_SCRATCH_10 (TAHOE_VRISC_BASE + 0x48) +#define TAHOE_VRISC_SCRATCH_11 (TAHOE_VRISC_BASE + 0x4c) +#define TAHOE_VRISC_SCRATCH_12 (TAHOE_VRISC_BASE + 0x50) +#define TAHOE_VRISC_SCRATCH_13 (TAHOE_VRISC_BASE + 0x54) +#define TAHOE_VRISC_SCRATCH_14 (TAHOE_VRISC_BASE + 0x58) +#define TAHOE_VRISC_SCRATCH_15 (TAHOE_VRISC_BASE + 0x5c) +#define TAHOE_VRISC_SCRATCH_16 (TAHOE_VRISC_BASE + 0x60) +#define TAHOE_VRISC_SCRATCH_17 (TAHOE_VRISC_BASE + 0x64) +#define TAHOE_VRISC_SCRATCH_18 (TAHOE_VRISC_BASE + 0x68) +#define TAHOE_VRISC_SCRATCH_19 (TAHOE_VRISC_BASE + 0x6c) +#define TAHOE_VRISC_SCRATCH_20 (TAHOE_VRISC_BASE + 0x70) +#define TAHOE_VRISC_SCRATCH_21 (TAHOE_VRISC_BASE + 0x74) +#define TAHOE_VRISC_SCRATCH_22 (TAHOE_VRISC_BASE + 0x78) +#define TAHOE_VRISC_SCRATCH_23 (TAHOE_VRISC_BASE + 0x7c) +#define TAHOE_VRISC_SCRATCH_24 (TAHOE_VRISC_BASE + 0x80) +#define TAHOE_VRISC_SCRATCH_25 (TAHOE_VRISC_BASE + 0x84) +#define TAHOE_VRISC_SCRATCH_26 (TAHOE_VRISC_BASE + 0x88) +#define TAHOE_VRISC_SCRATCH_27 (TAHOE_VRISC_BASE + 0x8c) +#define TAHOE_VRISC_SCRATCH_28 (TAHOE_VRISC_BASE + 0x90) +#define TAHOE_VRISC_SCRATCH_29 (TAHOE_VRISC_BASE + 0x94) +#define TAHOE_VRISC_SCRATCH_30 (TAHOE_VRISC_BASE + 0x98) +#define TAHOE_VRISC_SCRATCH_31 (TAHOE_VRISC_BASE + 0x9c) +#define TAHOE_VRISC_ASSIST_IRQ (TAHOE_VRISC_BASE + 0xa0) +#define TAHOE_VRISC_IRQ_MASK (TAHOE_VRISC_BASE + 0xa4) +#define TAHOE_VRISC_IRQ_STAT (TAHOE_VRISC_BASE + 0xa8) +#define TAHOE_VRISC_EIRQ_MASK (TAHOE_VRISC_BASE + 0xac) +#define TAHOE_VRISC_EIRQ_STAT (TAHOE_VRISC_BASE + 0xb0) +#define TAHOE_DEBUG_0 (TAHOE_VRISC_BASE + 0xc0) +#define TAHOE_DEBUG_1 (TAHOE_VRISC_BASE + 0xc4) +#define TAHOE_DEBUG_2 (TAHOE_VRISC_BASE + 0xc8) +#define TAHOE_DEBUG_3 (TAHOE_VRISC_BASE + 0xcc) +#define TAHOE_DEBUG_4 (TAHOE_VRISC_BASE + 0xd0) +#define TAHOE_DEBUG_5 (TAHOE_VRISC_BASE + 0xd4) +#define TAHOE_DEBUG_6 (TAHOE_VRISC_BASE + 0xd8) +#define TAHOE_DEBUG_7 (TAHOE_VRISC_BASE + 0xdc) + +#define TAHOE_TCTRL_TOP (TAHOE_VRISC_BASE + 0x400) +#define TAHOE_TCTRL_WORK_MOD (TAHOE_VRISC_BASE + 0x404) +#define TAHOE_VCE_SCRATCH_0 (TAHOE_VRISC_BASE + 0x410) +#define TAHOE_VCE_SCRATCH_1 (TAHOE_VRISC_BASE + 0x414) +#define TAHOE_VCE_SCRATCH_2 (TAHOE_VRISC_BASE + 0x418) +#define TAHOE_VCE_SCRATCH_3 (TAHOE_VRISC_BASE + 0x41c) +#define TAHOE_VCE_SCRATCH_4 (TAHOE_VRISC_BASE + 0x420) +#define TAHOE_VCE_SCRATCH_5 (TAHOE_VRISC_BASE + 0x424) +#define TAHOE_VCE_SCRATCH_6 (TAHOE_VRISC_BASE + 0x428) +#define TAHOE_VCE_SCRATCH_7 (TAHOE_VRISC_BASE + 0x42c) +#define TAHOE_VCE_SCRATCH_8 (TAHOE_VRISC_BASE + 0x430) +#define TAHOE_VCE_SCRATCH_9 (TAHOE_VRISC_BASE + 0x434) +#define TAHOE_VCE_SCRATCH_10 (TAHOE_VRISC_BASE + 0x438) +#define TAHOE_VCE_SCRATCH_11 (TAHOE_VRISC_BASE + 0x43c) +#define TAHOE_VCE_SCRATCH_12 (TAHOE_VRISC_BASE + 0x440) +#define TAHOE_VCE_SCRATCH_13 (TAHOE_VRISC_BASE + 0x444) +#define TAHOE_VCE_SCRATCH_14 (TAHOE_VRISC_BASE + 0x448) +#define TAHOE_VCE_SCRATCH_15 (TAHOE_VRISC_BASE + 0x44c) + +/************************************************************************/ +/* ENC_ENCODE (CHN PARAM) */ +/************************************************************************/ +#define CMD_OPT_CODE TAHOE_VRISC_SCRATCH_0 +#define CHN_ID TAHOE_VRISC_SCRATCH_1 +#define ENC_FMT TAHOE_VRISC_SCRATCH_2 // 0: h264; 1: h265; +#define ENC_PROFILE TAHOE_VRISC_SCRATCH_3 //for es buffer +#define ENC_LEVEL TAHOE_VRISC_SCRATCH_4 //for poc +#define MFDIN_INPUT_FMT TAHOE_VRISC_SCRATCH_5 +#define MFDI_LOW_DELAY_MODE TAHOE_VRISC_SCRATCH_6 +#define MFDI_ROTATION_MODE TAHOE_VRISC_SCRATCH_7 // default = 0x0 rotation mode: 00:0 R 01:90R 10:180R 11:270R +#define MFDI_MIRROR_MODE TAHOE_VRISC_SCRATCH_8 // default = 0x0 mirror mode: bit1 : mirror; bit2: flip; +//#define MFDI_PADDING_MODE TAHOE_VRISC_SCRATCH_9 // default = 0x0 mfdi padding mode, 0: copy 1: special value +#define ENC_PARA_CROP_WH TAHOE_VRISC_SCRATCH_9 +#define MFDI_CROP_START TAHOE_VRISC_SCRATCH_10 // default = 0x0 [15:0] x, [31:16] y +#define MFDIN_INPUT_W TAHOE_VRISC_SCRATCH_11 +#define MFDIN_INPUT_STRIDE TAHOE_VRISC_SCRATCH_12 +#define MFDIN_INPUT_H TAHOE_VRISC_SCRATCH_13 +#define DS_PARENT_CHN TAHOE_VRISC_SCRATCH_14 +#define ENC_PIC_W TAHOE_VRISC_SCRATCH_15 // encode pic width +#define ENC_PIC_H TAHOE_VRISC_SCRATCH_16 // encode pic height +#define ENC_RC_MODE TAHOE_VRISC_SCRATCH_17 +#define ENC_BITRATE TAHOE_VRISC_SCRATCH_18 +#define ENC_FPS TAHOE_VRISC_SCRATCH_19 // fps +#define ENC_GOP TAHOE_VRISC_SCRATCH_20 // gop +#define ENC_SLICE_NUM TAHOE_VRISC_SCRATCH_21 // max slices +#define ENC_REF_NUM TAHOE_VRISC_SCRATCH_22 // ref num +#define ENC_B_FRAMES TAHOE_VRISC_SCRATCH_23 // b frames +#define CU_DEPTH_SEL TAHOE_VRISC_SCRATCH_24 // hevc depth num, 0-two depth, 1-three depth +#define MONO_FLAG TAHOE_VRISC_SCRATCH_25 // mono flag +#define WORK_MODE TAHOE_VRISC_SCRATCH_26 // 0: frame mode without epb wait1: frame mode with epb wait 2: epb mode3: epb segment mode +#define RFMU_EN TAHOE_VRISC_SCRATCH_27 +#define RFMU_LOOP_EN TAHOE_VRISC_SCRATCH_28 +#define EC_LOOP_EN TAHOE_VRISC_SCRATCH_29 +//#define MEM_POOL_ADDR_LSB TAHOE_VRISC_SCRATCH_30 +//#define MEM_POOL_ADDR_MSB TAHOE_VRISC_SCRATCH_31 +#define MEM_POOL_ADDR_MSB TAHOE_VCE_SCRATCH_12 +#define MEM_POOL_ADDR_LSB TAHOE_VCE_SCRATCH_13 + +#define ENC_MIN_QP_I TAHOE_VCE_SCRATCH_0 +#define ENC_MAX_QP_I TAHOE_VCE_SCRATCH_1 +#define ENC_MIN_QP_P TAHOE_VCE_SCRATCH_2 +#define ENC_MAX_QP_P TAHOE_VCE_SCRATCH_3 +#define ENC_QP_RANGE_DELTA TAHOE_VCE_SCRATCH_4 +#define ENC_INTRA_QP_OFFSET TAHOE_VCE_SCRATCH_5 +#define ENC_AQ_EN TAHOE_VCE_SCRATCH_6 + +/************************************************************************/ +/* ENC_SET_PARAM (PRP-OSD) */ +/************************************************************************/ +#define CMD_OPT_CODE TAHOE_VRISC_SCRATCH_0 +#define CHN_ID TAHOE_VRISC_SCRATCH_1 +#define PRP_OSD_COMMON_0 TAHOE_VRISC_SCRATCH_2 +#define PRP_OSD_COMMON_1 TAHOE_VRISC_SCRATCH_3 +#define PRP_OSD_COMMON_2 TAHOE_VRISC_SCRATCH_4 +#define PRP_OSD_COMMON_3 TAHOE_VRISC_SCRATCH_5 +#define PRP_OSD_COMMON_4 TAHOE_VRISC_SCRATCH_6 +#define PRP_OSD_COMMON_5 TAHOE_VCE_SCRATCH_0 +#define PRP_OSD_COMMON_6 TAHOE_VCE_SCRATCH_1 +#define PRP_OSD_COMMON_7 TAHOE_VCE_SCRATCH_2 +#define PRP_OSD_REGION_LT_0 TAHOE_VRISC_SCRATCH_7 +#define PRP_OSD_REGION_LT_1 TAHOE_VRISC_SCRATCH_8 +#define PRP_OSD_REGION_LT_2 TAHOE_VRISC_SCRATCH_9 +#define PRP_OSD_REGION_LT_3 TAHOE_VRISC_SCRATCH_10 +#define PRP_OSD_REGION_LT_4 TAHOE_VRISC_SCRATCH_11 +#define PRP_OSD_REGION_LT_5 TAHOE_VCE_SCRATCH_3 +#define PRP_OSD_REGION_LT_6 TAHOE_VCE_SCRATCH_4 +#define PRP_OSD_REGION_LT_7 TAHOE_VCE_SCRATCH_5 +#define PRP_OSD_REGION_WH_0 TAHOE_VRISC_SCRATCH_12 +#define PRP_OSD_REGION_WH_1 TAHOE_VRISC_SCRATCH_13 +#define PRP_OSD_REGION_WH_2 TAHOE_VRISC_SCRATCH_14 +#define PRP_OSD_REGION_WH_3 TAHOE_VRISC_SCRATCH_15 +#define PRP_OSD_REGION_WH_4 TAHOE_VRISC_SCRATCH_16 +#define PRP_OSD_REGION_WH_5 TAHOE_VCE_SCRATCH_6 +#define PRP_OSD_REGION_WH_6 TAHOE_VCE_SCRATCH_7 +#define PRP_OSD_REGION_WH_7 TAHOE_VCE_SCRATCH_8 +#define PRP_OSD_BITMAPA_0 TAHOE_VRISC_SCRATCH_17 +#define PRP_OSD_BITMAPA_1 TAHOE_VRISC_SCRATCH_18 +#define PRP_OSD_BITMAPA_2 TAHOE_VRISC_SCRATCH_19 +#define PRP_OSD_BITMAPA_3 TAHOE_VRISC_SCRATCH_20 +#define PRP_OSD_BITMAPA_4 TAHOE_VRISC_SCRATCH_21 +#define PRP_OSD_BITMAPA_5 TAHOE_VCE_SCRATCH_9 +#define PRP_OSD_BITMAPA_6 TAHOE_VCE_SCRATCH_10 +#define PRP_OSD_BITMAPA_7 TAHOE_VCE_SCRATCH_11 +#define PRP_OSD_Y_BASEADDR_0 TAHOE_VRISC_SCRATCH_22 +#define PRP_OSD_Y_BASEADDR_1 TAHOE_VRISC_SCRATCH_23 +#define PRP_OSD_Y_BASEADDR_2 TAHOE_VRISC_SCRATCH_24 +#define PRP_OSD_Y_BASEADDR_3 TAHOE_VRISC_SCRATCH_25 +#define PRP_OSD_Y_BASEADDR_4 TAHOE_VRISC_SCRATCH_26 +#define PRP_OSD_Y_BASEADDR_5 TAHOE_VCE_SCRATCH_12 +#define PRP_OSD_Y_BASEADDR_6 TAHOE_VCE_SCRATCH_13 +#define PRP_OSD_Y_BASEADDR_7 TAHOE_VCE_SCRATCH_14 +#define PRP_OSD_STRIDE_0_1 TAHOE_VRISC_SCRATCH_27 +#define PRP_OSD_STRIDE_2_3 TAHOE_VRISC_SCRATCH_28 +#define PRP_OSD_STRIDE_4_5 TAHOE_VRISC_SCRATCH_29 +#define PRP_OSD_STRIDE_6_7 TAHOE_VRISC_SCRATCH_30 + +/************************************************************************/ +/* ENC_PARAM_SET (DYNAMIC SETTING) */ +/************************************************************************/ +#define CMD_OPT_CODE TAHOE_VRISC_SCRATCH_0 +#define CHN_ID TAHOE_VRISC_SCRATCH_1 +#define ENC_PARAM_BITRATE TAHOE_VRISC_SCRATCH_2 +#define ENC_PARAM_GOP TAHOE_VRISC_SCRATCH_3 +#define ENC_PARAM_FPS TAHOE_VRISC_SCRATCH_4 +#define ENC_PARAM_RC_MODE TAHOE_VRISC_SCRATCH_5 +#define GOLDEN_MATCH_FLAG TAHOE_VRISC_SCRATCH_28 + + +#define CMD_CREATE_CHN 1 +#define CMD_DESTROY_CHN 2 +#define CMD_ENC_ONE_FRAME 3 +#define CMD_ENC_PARAMETER_SETS 4 +#define CMD_SET_ENC_PARAMETERS 5 +#define CMD_GET_ENC_PARAMETERS 6 +#define CMD_SET_ENC_PRP 7 +#define CMD_SET_ENC_IMD 8 +#define CMD_SET_ENC_ME 9 +#define CMD_SET_ENC_RDO 10 +#define CMD_SET_ENC_FBCD 11 +#define CMD_SET_ENC_LPF 12 +#define CMD_SET_ENC_EC 13 +#define CMD_RDMA_TEST 14 +#define CMD_DO_ENC_ONE_FRAME 15 +#define CMD_DO_ENC_ONE_FRAME_EXT 16 +#define CMD_TOPCASE_ENC_ONE_FRAME 17 + +#define CMD_SET_OSD_INFO 27 + +// host to vrisc +#define CMD_DONE (1 << 16) +#define CMD_ERROR (2 << 16) +#define CMD_PUSHED (3 << 16) +#define CMD_BUSY (4 << 16) + +#define TAHOE_CFG TAHOE_VCE_SCRATCH_15 +#define TAHOE_EC_DMA0 (TAHOE_VRISC_BASE + 0x1c08) +#define TAHOE_EC_DMA6 (TAHOE_VRISC_BASE + 0x1c20) +#define TAHOE_EC_LENGTH (TAHOE_VRISC_BASE + 0x1c78) +#define TAHOE_RDO_PREMD_PARA (TAHOE_VRISC_BASE + 0x1424) +#define TAHOE_DQME_SEARCH_CFG (TAHOE_VRISC_BASE + 0x1018) +#define PRP_MFDI_STRIDE_0 TAHOE_VRISC_SCRATCH_25 +#define PRP_MFDI_STRIDE_1 TAHOE_VRISC_SCRATCH_26 +#define PRP_MFDI_STRIDE_2 TAHOE_VRISC_SCRATCH_27 +#define INPUT_BUF_ADDR_MSB TAHOE_VRISC_SCRATCH_3 //bit 0~1:y 2~3:cb 4~5:cr + + +#define TAHOE_TOP_SW_RST (0x0f00 << 2) + +#define HEADER_VPS_ADDR TAHOE_VCE_SCRATCH_9 +#define HEADER_SPS_ADDR TAHOE_VCE_SCRATCH_10 +#define HEADER_PPS_ADDR TAHOE_VCE_SCRATCH_11 +#define HEADER_SIZE TAHOE_VCE_SCRATCH_12 +#define HEADER_ADDR_HIGHER TAHOE_VCE_SCRATCH_13 //bit 0~1:sps 2~3:pps 4~5:vps +#define ENC_PARA_FRAME_TYPE TAHOE_VRISC_SCRATCH_15 //force frame type I/IDR +#define CRE_COLOR_ASPECT TAHOE_VRISC_SCRATCH_23 + +//cannot repeate with create regs for v4l2 +#define OUTPUT_FRAME_TYPE TAHOE_VRISC_SCRATCH_30 +#define OUTPUT_CHN_ID TAHOE_VRISC_SCRATCH_31 + +enum profile_e +{ + PROFILE_BASELINE = 66, + PROFILE_MAIN = 77, + PROFILE_HIGH = 100, + PROFILE_HIGH10 = 110, + PROFILE_HIGH422 = 122, + PROFILE_HIGH444_PREDICTIVE = 244, +}; +#endif -- 2.43.2