diff --git a/arch/arm/boot/dts/amlogic/meson-sc2.dtsi b/arch/arm/boot/dts/amlogic/meson-sc2.dtsi index c7bf9ba64..16c44cdcd 100644 --- a/arch/arm/boot/dts/amlogic/meson-sc2.dtsi +++ b/arch/arm/boot/dts/amlogic/meson-sc2.dtsi @@ -1823,6 +1823,12 @@ status = "okay"; }; + v2d { + compatible = "amlogic, v2d"; + dev_name = "v2d"; + status = "okay"; + }; + video_composer { compatible = "amlogic, video_composer"; dev_name = "video_composer"; diff --git a/arch/arm/configs/amlogic_a32.fragment b/arch/arm/configs/amlogic_a32.fragment index 3b28de45e..7d9df792b 100644 --- a/arch/arm/configs/amlogic_a32.fragment +++ b/arch/arm/configs/amlogic_a32.fragment @@ -206,6 +206,7 @@ CONFIG_AMLOGIC_VIDEO_PP_COMMON=y CONFIG_AMLOGIC_VIDEO_TUNNEL=y CONFIG_AMLOGIC_VIDEOQUEUE=y CONFIG_AMLOGIC_DI_PROCESS=y +CONFIG_AMLOGIC_MEDIA_V2D=y CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM=y CONFIG_AMLOGIC_MEDIA_FRC=y CONFIG_AMLOGIC_MEDIA_GDC=y diff --git a/arch/arm64/boot/dts/amlogic/meson-sc2.dtsi b/arch/arm64/boot/dts/amlogic/meson-sc2.dtsi index 36cf73780..ef613fd31 100644 --- a/arch/arm64/boot/dts/amlogic/meson-sc2.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-sc2.dtsi @@ -1823,6 +1823,12 @@ status = "okay"; }; + v2d { + compatible = "amlogic, v2d"; + dev_name = "v2d"; + status = "okay"; + }; + video_composer { compatible = "amlogic, video_composer"; dev_name = "video_composer"; diff --git a/arch/arm64/configs/amlogic_gki.fragment b/arch/arm64/configs/amlogic_gki.fragment index 626a1b949..39ea1a8c9 100644 --- a/arch/arm64/configs/amlogic_gki.fragment +++ b/arch/arm64/configs/amlogic_gki.fragment @@ -238,6 +238,7 @@ CONFIG_AMLOGIC_VIDEO_DISPLAY=y CONFIG_AMLOGIC_VIDEO_PP_COMMON=y CONFIG_AMLOGIC_VIDEO_TUNNEL=y CONFIG_AMLOGIC_VIDEOQUEUE=y +CONFIG_AMLOGIC_MEDIA_V2D=y CONFIG_AMLOGIC_DI_PROCESS=y CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM=y CONFIG_AMLOGIC_MEDIA_FRC=y diff --git a/drivers/media/BUILD.bazel b/drivers/media/BUILD.bazel index 2eea4717e..c2dd4915d 100644 --- a/drivers/media/BUILD.bazel +++ b/drivers/media/BUILD.bazel @@ -660,6 +660,14 @@ ddk_module( "video_processor/di_process/di_process.c", ], }, + "CONFIG_AMLOGIC_MEDIA_V2D": { + True: [ + "video_processor/v2d/v2d.c", + "video_processor/v2d/v2d_dewarp_composer.c", + "video_processor/v2d/v2d_ge2d_composer.c", + "video_processor/v2d/v2d_vicp_composer.c", + ], + }, "CONFIG_AMLOGIC_VIDEO_DISPLAY": { True: [ "video_processor/videodisplay/videodisplay_util.c", diff --git a/drivers/media/media_main.c b/drivers/media/media_main.c index afdedbf68..52e3dcc45 100644 --- a/drivers/media/media_main.c +++ b/drivers/media/media_main.c @@ -120,6 +120,7 @@ static int __init media_main_init(void) call_sub_init(amlogic_codec_mm_dma_buf_init); call_sub_init(amprime_sl_init); call_sub_init(di_process_module_init); + call_sub_init(v2d_module_init); pr_debug("### %s() end\n", __func__); return 0; } diff --git a/drivers/media/media_main.h b/drivers/media/media_main.h index f1c980891..4d78de76e 100644 --- a/drivers/media/media_main.h +++ b/drivers/media/media_main.h @@ -765,4 +765,13 @@ static inline int di_process_module_init(void) } #endif +#ifdef CONFIG_AMLOGIC_MEDIA_V2D +int v2d_module_init(void); +#else +static inline int v2d_module_init(void) +{ + return 0; +} +#endif + #endif diff --git a/drivers/media/video_processor/Kconfig b/drivers/media/video_processor/Kconfig index b4314ed0c..6aac8dd8c 100644 --- a/drivers/media/video_processor/Kconfig +++ b/drivers/media/video_processor/Kconfig @@ -28,6 +28,7 @@ source "$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/videoqueue/Kconfig" source "$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/common/Kconfig" source "$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/di_process/Kconfig" source "$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/videodisplay/Kconfig" +source "$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/v2d/Kconfig" endif endmenu diff --git a/drivers/media/video_processor/Makefile b/drivers/media/video_processor/Makefile index 159406849..7886b9963 100644 --- a/drivers/media/video_processor/Makefile +++ b/drivers/media/video_processor/Makefile @@ -11,3 +11,4 @@ include $(srctree)/$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/videoqueu include $(srctree)/$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/common/Makefile include $(srctree)/$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/di_process/Makefile include $(srctree)/$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/videodisplay/Makefile +include $(srctree)/$(COMMON_DRIVERS_DIR)/drivers/media/video_processor/v2d/Makefile diff --git a/drivers/media/video_processor/v2d/Kconfig b/drivers/media/video_processor/v2d/Kconfig new file mode 100644 index 000000000..7ff6a299a --- /dev/null +++ b/drivers/media/video_processor/v2d/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +# +# Amlogic v2d device configuration +# + +menu "Amlogic v2d support" + +config AMLOGIC_MEDIA_V2D + tristate "Amlogic v2d device support" + default y + + help + Select to enable "Amlogic v2d device support. + +endmenu diff --git a/drivers/media/video_processor/v2d/Makefile b/drivers/media/video_processor/v2d/Makefile new file mode 100644 index 000000000..383e6aad0 --- /dev/null +++ b/drivers/media/video_processor/v2d/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +asflags-y += -mfloat-abi=softfp -mfpu=neon + +$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_MEDIA_V2D) += video_processor/v2d/v2d.o +$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_MEDIA_V2D) += video_processor/v2d/v2d_ge2d_composer.o +$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_MEDIA_V2D) += video_processor/v2d/v2d_dewarp_composer.o +$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_MEDIA_V2D) += video_processor/v2d/v2d_vicp_composer.o diff --git a/drivers/media/video_processor/v2d/v2d.c b/drivers/media/video_processor/v2d/v2d.c new file mode 100644 index 000000000..bd81b8573 --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d.c @@ -0,0 +1,2634 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_MEDIA_VIDEO +#include +#endif +#include +#include +#include +#include <../../video_sink/video_priv.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "v2d.h" + +#define V2D_INSTANCE_NUM 2 +#define BUFFER_4K_WIDTH 3840 +#define BUFFER_4K_HEIGHT 2160 + +#define BUFFER_2K_WIDTH 2560 +#define BUFFER_2K_HEIGHT 1440 + +#define BUFFER_720_WIDTH 1280 +#define BUFFER_720_HEIGHT 720 + +static u32 v2d_instance_num = 2; +static enum composer_dev v2d_dev_choice; +static int use_full_axis_scaling = 1; +static int display_yuv444; +static int vicp_output_mode = 2; /*1 mif 2. fbc 3. mif + fbc*/ +static u32 vicp_shrink_mode = 1; /*0 2x, 1 4x, 2 8x*/ +static u32 lossy_compress_rate;//0: 100% compress; 1: 67% compress; 2: 83% compress +static u32 manual_buf_width; +static u32 manual_buf_height; +static u32 total_get_count; +static u32 total_put_count; +static u32 print_flag; +static u32 v2d_bypass; +static bool buf_config_444; +static bool enable_v2d_dump; + +int ge2d_com_debug; +int dewarp_com_dump; +int dewarp_print; + +static DEFINE_MUTEX(v2d_mutex); + +static struct v2d_port_s ports[] = { + { + .name = "v2d.0", + .index = 0, + .open_count = 0, + }, + { + .name = "v2d.1", + .index = 1, + .open_count = 0, + }, +}; + +int v2d_print(int index, int debug_flag, const char *fmt, ...) +{ + if ((print_flag & debug_flag) || + debug_flag == PRINT_ERROR) { + unsigned char buf[256]; + int len = 0; + va_list args; + + va_start(args, fmt); + len = sprintf(buf, "v2d:[%d]", index); + vsnprintf(buf + len, 256 - len, fmt, args); + pr_info("%s", buf); + va_end(args); + } + return 0; +} + +static void frames_put_file(struct v2d_dev *dev, + struct received_frames_t *current_frames) +{ + struct file *file_vf; + int current_count; + int i; + + current_count = current_frames->frames_info.frame_count; + for (i = 0; i < current_count; i++) { + file_vf = current_frames->input_file[i]; + fput(file_vf); + total_put_count++; + dev->fput_count++; + } +} + +static void file_q_init(struct v2d_dev *dev) +{ + int i; + struct dma_buf *dmabuf; + + INIT_KFIFO(dev->file_free_q); + kfifo_reset(&dev->file_free_q); + + for (i = 0; i < V2D_POOL_SIZE; i++) { + dmabuf = uvm_alloc_dmabuf(SZ_4K, 0, 0); + if (!dmabuf_is_uvm(dmabuf)) + v2d_print(dev->index, PRINT_ERROR, "dmabuf is not uvm\n"); + v2d_print(dev->index, PRINT_OTHER, "dmabuf is uvm:dmabuf=%px\n", dmabuf); + + dev->out_dmabuf[i] = dmabuf; + if (!kfifo_put(&dev->file_free_q, dev->out_dmabuf[i])) + v2d_print(dev->index, PRINT_ERROR, + "file_free_q put fail\n"); + } +} + +static void file_q_uninit(struct v2d_dev *dev) +{ + int i; + + for (i = 0; i < V2D_POOL_SIZE; i++) { + if (dev->out_dmabuf[i]) + dma_buf_put(dev->out_dmabuf[i]); + else + v2d_print(dev->index, PRINT_ERROR, "unreg: dmabuf is NULL\n"); + dev->out_dmabuf[i] = NULL; + } +} + +static void receive_q_init(struct v2d_dev *dev) +{ + int i = 0; + + INIT_KFIFO(dev->receive_q); + kfifo_reset(&dev->receive_q); + + for (i = 0; i < V2D_POOL_SIZE; i++) + atomic_set(&dev->received_frames[i].on_use, false); +} + +static void receive_q_uninit(struct v2d_dev *dev) +{ + int i = 0; + struct received_frames_t *received_frames = NULL; + + v2d_print(dev->index, PRINT_OTHER, "unit receive_q len=%d\n", + kfifo_len(&dev->receive_q)); + while (kfifo_len(&dev->receive_q) > 0) { + if (kfifo_get(&dev->receive_q, &received_frames)) + frames_put_file(dev, received_frames); + } + + for (i = 0; i < V2D_POOL_SIZE; i++) + atomic_set(&dev->received_frames[i].on_use, false); +} + +static void display_q_init(struct v2d_dev *dev) +{ + int i = 0; + + INIT_KFIFO(dev->display_q); + kfifo_reset(&dev->display_q); + + for (i = 0; i < V2D_POOL_SIZE; i++) + atomic_set(&dev->display_data[i].on_use, false); +} + +static void display_q_uninit(struct v2d_dev *dev) +{ + int i = 0; + + /*mak sure all fence signaled by videodisplay before release buffer*/ + while (dev->buffer_release_count != dev->fence_signal_count) { + v2d_print(dev->index, PRINT_OTHER, + "%s: should wait,buffer release count:%d signal_count:%d display_q:%d\n", + __func__, + dev->buffer_release_count, + dev->fence_signal_count, + kfifo_len(&dev->display_q)); + usleep_range(2000, 2100); + i++; + if (i > WAIT_DISPLAY_Q_TIMEOUT) { + v2d_print(dev->index, PRINT_ERROR, + "%s err, buffer release count:%d signal_count:%d display_q:%d\n", + __func__, + dev->buffer_release_count, + dev->fence_signal_count, + kfifo_len(&dev->display_q)); + break; + } + } +} + +static struct input_axis_s adjust_output_axis(struct v2d_dev *dev, + struct frame_info_t *vframe_info) +{ + int src_width = 0, src_height = 0; + int scaled_width = 0, scaled_height = 0; + int dst_width, dst_height; + struct input_axis_s output_axis; + int tmp_size; + + memset(&output_axis, 0, sizeof(struct input_axis_s)); + if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(vframe_info)) { + pr_info("%s: invalid param.\n", __func__); + return output_axis; + } + + src_width = vframe_info->crop_w; + src_height = vframe_info->crop_h; + dst_width = vframe_info->dst_w; + dst_height = vframe_info->dst_h; + + if (vframe_info->transform == V2D_TRANSFORM_ROT_90 || + vframe_info->transform == V2D_TRANSFORM_ROT_270) { + tmp_size = src_height; + src_height = src_width; + src_width = tmp_size; + } + if (!use_full_axis_scaling && src_width > 0 && src_height > 0) { + scaled_width = dst_width; + scaled_height = dst_width * src_height / src_width; + if (scaled_height > dst_height) { + scaled_height = dst_height; + scaled_width = dst_height * src_width / src_height; + } + } else { + scaled_width = dst_width; + scaled_height = dst_height; + } + output_axis.left = vframe_info->dst_x + (dst_width - scaled_width) / 2; + output_axis.top = vframe_info->dst_y + (dst_height - scaled_height) / 2; + output_axis.width = scaled_width; + output_axis.height = scaled_height; + + v2d_print(dev->index, PRINT_AXIS, + "frame out data axis left top width height: %d %d %d %d\n", + output_axis.left, output_axis.top, output_axis.width, output_axis.height); + return output_axis; +} + +static void *v2d_timeline_create(struct v2d_dev *dev) +{ + const char *tl_name = "v2d_timeline_0"; + + if (dev->index == 0) + tl_name = "v2d_timeline_0"; + else if (dev->index == 1) + tl_name = "v2d_timeline_1"; + + if (IS_ERR_OR_NULL(dev->v2d_timeline)) { + dev->cur_streamline_val = 0; + dev->v2d_timeline = aml_sync_create_timeline(tl_name); + v2d_print(dev->index, PRINT_FENCE, + "timeline create tlName =%s, video_timeline=%p\n", + tl_name, dev->v2d_timeline); + } + + return dev->v2d_timeline; +} + +static int v2d_timeline_create_fence(struct v2d_dev *dev) +{ + int out_fence_fd = -1; + u32 pt_val = 0; + + pt_val = dev->cur_streamline_val + 1; + v2d_print(dev->index, PRINT_FENCE, "pt_val:%d", pt_val); + + out_fence_fd = aml_sync_create_fence(dev->v2d_timeline, pt_val); + if (out_fence_fd >= 0) { + dev->cur_streamline_val++; + dev->fence_creat_count++; + } else { + v2d_print(dev->index, PRINT_ERROR, + "create fence returned %d", out_fence_fd); + } + return out_fence_fd; +} + +static void v2d_timeline_increase(struct v2d_dev *dev, + unsigned int value) +{ + aml_sync_inc_timeline(dev->v2d_timeline, value); + dev->fence_signal_count += value; + v2d_print(dev->index, PRINT_FENCE, + "receive_cnt=%d,new_cnt=%d,fen_creat_cnt=%d,fen_release_cnt=%d\n", + dev->received_count, + dev->received_new_count, + dev->fence_creat_count, + dev->fence_signal_count); +} + +static void v2d_free_fd_private(void *arg) +{ + if (arg) { + v2d_print(0, PRINT_OTHER, "free_fd_private\n"); + vfree((u8 *)arg); + } else { + v2d_print(0, PRINT_ERROR, "free: arg is NULL\n"); + } +} + +static void v2d_dump_output_buffer(struct vframe_s *vf) +{ +#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA + struct file *fp = NULL; + char name_buf[32]; + int data_size_y, data_size_uv; + u8 *data_y; + u8 *data_uv; + loff_t pos; + + if (!vf) + return; + + snprintf(name_buf, sizeof(name_buf), "/data/dst_vframe.yuv"); + fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644); + if (IS_ERR(fp)) + return; + data_size_y = vf->canvas0_config[0].width * + vf->canvas0_config[0].height; + data_size_uv = vf->canvas0_config[1].width * + vf->canvas0_config[1].height; + data_y = codec_mm_vmap(vf->canvas0_config[0].phy_addr, data_size_y); + data_uv = codec_mm_vmap(vf->canvas0_config[1].phy_addr, data_size_uv); + if (!data_y || !data_uv) { + pr_err("%s: vmap failed.\n", __func__); + return; + } + pos = fp->f_pos; + kernel_write(fp, data_y, data_size_y, &pos); + fp->f_pos = pos; + pr_info("%s: write %u size to addr%p\n", + __func__, data_size_y, data_y); + codec_mm_unmap_phyaddr(data_y); + pos = fp->f_pos; + kernel_write(fp, data_uv, data_size_uv, &pos); + fp->f_pos = pos; + pr_info("%s: write %u size to addr%p\n", + __func__, data_size_uv, data_uv); + codec_mm_unmap_phyaddr(data_uv); + filp_close(fp, NULL); +#endif +} + +struct file_private_data *v2d_get_file_private_data(struct file *file_vf, + bool alloc_if_null) +{ + struct file_private_data *file_private_data; + struct uvm_hook_mod *uhmod; + struct uvm_hook_mod_info info; + int ret; + + if (!file_vf) { + v2d_print(0, PRINT_ERROR, "get_file_private_data fail\n"); + return NULL; + } + + if (!dmabuf_is_uvm((struct dma_buf *)file_vf->private_data)) { + v2d_print(0, PRINT_ERROR, "%s: dmabuf is not uvm\n", __func__); + return NULL; + } + + uhmod = uvm_get_hook_mod((struct dma_buf *)(file_vf->private_data), + VF_PROCESS_V4LVIDEO); + if (uhmod && uhmod->arg) { + file_private_data = uhmod->arg; + uvm_put_hook_mod((struct dma_buf *)(file_vf->private_data), + VF_PROCESS_V4LVIDEO); + return file_private_data; + } else if (!alloc_if_null) { + return NULL; + } + + file_private_data = vmalloc(sizeof(*file_private_data)); + if (!file_private_data) + return NULL; + memset(file_private_data, 0, sizeof(struct file_private_data)); + + memset(&info, 0, sizeof(struct uvm_hook_mod_info)); + v2d_print(0, PRINT_OTHER, "uvm attch\n"); + info.type = VF_PROCESS_V4LVIDEO; + info.arg = file_private_data; + info.free = v2d_free_fd_private; + info.acquire_fence = NULL; + ret = uvm_attach_hook_mod((struct dma_buf *)(file_vf->private_data), + &info); + return file_private_data; +} + +static unsigned long get_dma_phy_addr(int fd, int index) +{ + unsigned long phy_addr = 0; + struct dma_buf *dbuf = NULL; + struct sg_table *table = NULL; + struct page *page = NULL; + struct dma_buf_attachment *attach = NULL; + + dbuf = dma_buf_get(fd); + if (IS_ERR(dbuf)) + v2d_print(index, PRINT_ERROR, "dbuf got from fd error!!!\n"); + attach = dma_buf_attach(dbuf, ports[index].pdev); + if (IS_ERR(attach)) + return 0; + + table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + page = sg_page(table->sgl); + phy_addr = PFN_PHYS(page_to_pfn(page)); + dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL); + dma_buf_detach(dbuf, attach); + dma_buf_put(dbuf); + return phy_addr; +} + +static int v2d_get_received_frame_free_index(struct v2d_dev *dev) +{ + int i = 0; + int j = 0; + + while (1) { + for (i = 0; i < V2D_POOL_SIZE; i++) { + if (!atomic_read(&dev->received_frames[i].on_use)) + break; + } + if (i == V2D_POOL_SIZE) { + j++; + if (j > WAIT_READY_Q_TIMEOUT) { + v2d_print(dev->index, PRINT_ERROR, + "receive_q is full, wait timeout!\n"); + return -1; + } + usleep_range(1000 * MAX_RECEIVE_WAIT_TIME, + 1000 * (MAX_RECEIVE_WAIT_TIME + 1)); + v2d_print(dev->index, PRINT_ERROR, + "receive_q is full!!! need wait =%d\n", j); + continue; + } else { + break; + } + } + return i; +} + +static int v2d_get_display_data_free_index(struct v2d_dev *dev) +{ + int i = 0; + int j = 0; + + while (1) { + for (i = 0; i < V2D_POOL_SIZE; i++) { + if (!atomic_read(&dev->display_data[i].on_use)) + break; + } + if (i == V2D_POOL_SIZE) { + j++; + if (j > WAIT_READY_Q_TIMEOUT) { + v2d_print(dev->index, PRINT_ERROR, + "display_data is empty, wait timeout!\n"); + return -1; + } + usleep_range(1000 * MAX_RECEIVE_WAIT_TIME, + 1000 * (MAX_RECEIVE_WAIT_TIME + 1)); + v2d_print(dev->index, PRINT_ERROR, + "display_data is empty!!! need wait =%d\n", j); + continue; + } else { + break; + } + } + return i; +} + +static void dev_get_vinfo(struct v2d_dev *dev) +{ + struct vinfo_s *display_vinfo; + struct vinfo_s default_vinfo = {.width = 1280, .height = 720, }; + u64 output_duration; + + display_vinfo = get_current_vinfo(); + if (IS_ERR_OR_NULL(display_vinfo)) { + v2d_print(dev->index, PRINT_ERROR, "get display vinfo err!!\n"); + display_vinfo = &default_vinfo; + } + output_duration = div64_u64(display_vinfo->sync_duration_num, + display_vinfo->sync_duration_den); + + dev->vinfo_w = display_vinfo->width; + dev->vinfo_h = display_vinfo->height; + dev->output_duration = output_duration; +} + +static u32 v2d_switch_buffer(struct dst_buf_t *buf, bool is_tvp, struct v2d_dev *dev) +{ + int flags, ret; + bool vicp_fbc_out_en = false; + u32 *virt_addr = NULL; + u32 temp_body_addr; + u32 offset_body_addr; + + if (IS_ERR_OR_NULL(buf) || IS_ERR_OR_NULL(dev)) { + v2d_print(dev->index, PRINT_ERROR, "%s: NULL param.\n", __func__); + return 0; + } + + if (is_tvp) + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_TVP; + else + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR; + + if (vicp_output_mode != 1 && dev->dev_choice == COMPOSER_WITH_VICP) + vicp_fbc_out_en = true; + + if (buf->phy_addr > 0) { + v2d_print(dev->index, PRINT_OTHER, "free buffer 0x%lx\n", buf->phy_addr); + codec_mm_free_for_dma(ports[dev->index].name, buf->phy_addr); + } + + buf->phy_addr = codec_mm_alloc_for_dma(ports[dev->index].name, + buf->buf_size / PAGE_SIZE, 0, flags); + v2d_print(dev->index, PRINT_ERROR, "%s: alloc buffer 0x%lx\n", __func__, buf->phy_addr); + + if (vicp_fbc_out_en) { + buf->afbc_body_addr = buf->phy_addr + buf->dw_size; + buf->afbc_head_addr = buf->afbc_body_addr + buf->afbc_body_size; + + if (buf->afbc_table_addr > 0) { + v2d_print(dev->index, PRINT_OTHER, "%s: free table buffer 0x%lx\n", + __func__, buf->afbc_table_addr); + codec_mm_dma_free_coherent(buf->afbc_table_handle); + } + virt_addr = (u32 *)codec_mm_dma_alloc_coherent(&buf->afbc_table_handle, + &buf->afbc_table_addr, buf->afbc_table_size, dev->port->name); + temp_body_addr = buf->afbc_body_addr & 0xffffffff; + memset(virt_addr, 0, buf->afbc_table_size); + for (offset_body_addr = 0; offset_body_addr < buf->afbc_body_size; + offset_body_addr += 4096) { + *virt_addr = ((offset_body_addr + temp_body_addr) >> 12) & 0x000fffff; + virt_addr++; + } + + v2d_print(dev->index, PRINT_ERROR, "%s: alloc buffer 0x%lx\n", __func__, + buf->afbc_table_addr); + } + + buf->is_tvp = is_tvp; + + if (buf->phy_addr == 0 || (vicp_fbc_out_en && buf->afbc_table_addr == 0)) + ret = 0; + else + ret = 1; + + return ret; +} + +static void choose_v2d_device(struct v2d_dev *dev, + struct received_frames_t *received_frames, struct vframe_s *input_vf) +{ + u32 frame_transform = 0; + int count = 0; + + if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(received_frames)) { + v2d_print(dev->index, PRINT_ERROR, "%s: invalid param.\n", __func__); + dev->dev_choice = COMPOSER_WITH_UNINITIAL; + return; + } + + frame_transform = received_frames->frames_info.input_buffer[0].transform; + count = received_frames->frames_info.frame_count; + + if (frame_transform == V2D_TRANSFORM_ROT_90 || + frame_transform == V2D_TRANSFORM_ROT_180 || + frame_transform == V2D_TRANSFORM_ROT_270 || + frame_transform == V2D_TRANSFORM_FLIP_H_ROT_90 || + frame_transform == V2D_TRANSFORM_FLIP_V_ROT_90) + dev->work_mode = V2D_MODE_ROTATE; + else + dev->work_mode = V2D_MODE_COMPOSER; + + if (v2d_dev_choice == COMPOSER_WITH_DEFAULT) { + if (check_dewarp_status(input_vf, count, dev->work_mode, dev->index)) + dev->dev_choice = COMPOSER_WITH_DEWARP; + else if (dev->work_mode == V2D_MODE_COMPOSER && + check_vicp_status()) + dev->dev_choice = COMPOSER_WITH_VICP; + else + dev->dev_choice = COMPOSER_WITH_GE2D; + } else { + if (v2d_dev_choice == COMPOSER_WITH_DEWARP && + check_dewarp_status(input_vf, count, dev->work_mode, dev->index)) + dev->dev_choice = COMPOSER_WITH_DEWARP; + else if (v2d_dev_choice == COMPOSER_WITH_VICP && + dev->work_mode == V2D_MODE_COMPOSER && check_vicp_status()) + dev->dev_choice = COMPOSER_WITH_VICP; + else + dev->dev_choice = COMPOSER_WITH_GE2D; + } +} + +static void init_output_buffer_size(struct v2d_dev *dev, + struct received_frames_t *received_frames) +{ + u32 buf_width, buf_height; + size_t usage = 0; + + switch (dev->buffer_status) { + case UNINITIAL: + break; + case INIT_SIZE_SUCCESS: + case INIT_BUFFER_SUCCESS: + case INIT_BUFFER_ERROR: + default: + return; + } + +#ifdef CONFIG_AMLOGIC_UVM_CORE + if (meson_uvm_get_usage(received_frames->input_file[0]->private_data, &usage) < 0) + v2d_print(dev->index, PRINT_ERROR, + "%s:meson_uvm_get_usage fail.\n", __func__); +#endif + received_frames->usage = usage; + buf_width = (dev->vinfo_w + 0x1f) & ~0x1f; + buf_height = dev->vinfo_h; + + if (dev->output_duration > 60) { + buf_width = 1920; + buf_height = 1080; + } + + if (usage == UVM_USAGE_IMAGE_PLAY) { + if (buf_width > BUFFER_4K_WIDTH) + buf_width = BUFFER_4K_WIDTH; + if (buf_height > BUFFER_4K_HEIGHT) + buf_height = BUFFER_4K_HEIGHT; + } else { + if (dev->work_mode == V2D_MODE_COMPOSER && + dev->dev_choice != COMPOSER_WITH_DEWARP) { + buf_width = BUFFER_720_WIDTH; + buf_height = BUFFER_720_HEIGHT; + } else if (dev->work_mode == V2D_MODE_COMPOSER && + dev->dev_choice == COMPOSER_WITH_GE2D) { + if (buf_width > BUFFER_2K_WIDTH) + buf_width = BUFFER_2K_WIDTH; + if (buf_height > BUFFER_2K_HEIGHT) + buf_height = BUFFER_2K_HEIGHT; + } else { + if (buf_width > BUFFER_4K_WIDTH) + buf_width = BUFFER_4K_WIDTH; + if (buf_height > BUFFER_4K_HEIGHT) + buf_height = BUFFER_4K_HEIGHT; + } + } + if (manual_buf_width != 0 && manual_buf_height != 0) { + buf_width = manual_buf_width; + buf_height = manual_buf_height; + } + dev->buf_width = buf_width; + dev->buf_height = buf_height; + dev->buffer_status = INIT_SIZE_SUCCESS; + v2d_print(dev->index, PRINT_OTHER, + "%s: init buffer size:w:%d h:%d.\n", + __func__, dev->buf_width, dev->buf_height); +} + +static void get_output_axis_crop(struct v2d_dev *dev, struct vframe_s *src_vf, + struct received_frames_t *received_frame, + struct frame_info_t *vframe_info_cur, int j) +{ + struct input_crop_s crop_info; + struct input_axis_s dst_axis; + struct input_axis_s display_axis; + struct output_axis area_bound; + + if (!dev || !received_frame || !vframe_info_cur) { + pr_info("%s input error\n", __func__); + return; + } + + memcpy(&area_bound, &received_frame->output_axis, sizeof(struct output_axis)); + if (src_vf && is_src_crop_valid(src_vf->src_crop)) { + crop_info.left = MAX(vframe_info_cur->crop_x, src_vf->src_crop.left); + crop_info.top = MAX(vframe_info_cur->crop_y, src_vf->src_crop.top); + if (!(src_vf->type_original & VIDTYPE_COMPRESS)) { + crop_info.width = MIN(vframe_info_cur->crop_w, src_vf->width - + src_vf->src_crop.left - src_vf->src_crop.right); + crop_info.height = MIN(vframe_info_cur->crop_h, src_vf->height - + src_vf->src_crop.top - src_vf->src_crop.bottom); + } else { + crop_info.width = MIN(vframe_info_cur->crop_w, src_vf->compWidth - + src_vf->src_crop.left - src_vf->src_crop.right); + crop_info.height = MIN(vframe_info_cur->crop_h, src_vf->compHeight - + src_vf->src_crop.top - src_vf->src_crop.bottom); + } + } else { + crop_info.left = vframe_info_cur->crop_x; + crop_info.top = vframe_info_cur->crop_y; + crop_info.width = vframe_info_cur->crop_w; + crop_info.height = vframe_info_cur->crop_h; + } + + dst_axis = adjust_output_axis(dev, vframe_info_cur); + display_axis.left = dst_axis.left * dev->buf_width / dev->vinfo_w; + display_axis.top = dst_axis.top * dev->buf_height / dev->vinfo_h; + display_axis.width = dst_axis.width * dev->buf_width / dev->vinfo_w; + display_axis.height = dst_axis.height * dev->buf_height / dev->vinfo_h; + + memcpy(&received_frame->crop_info[j], &crop_info, sizeof(struct input_crop_s)); + memcpy(&received_frame->axis_info[j], &display_axis, sizeof(struct input_axis_s)); + + if (area_bound.min_left > dst_axis.left) + area_bound.min_left = dst_axis.left; + if (area_bound.min_top > dst_axis.top) + area_bound.min_top = dst_axis.top; + if (area_bound.max_right < (dst_axis.left + dst_axis.width)) + area_bound.max_right = dst_axis.left + dst_axis.width; + if (area_bound.max_bottom < (dst_axis.top + dst_axis.height)) + area_bound.max_bottom = dst_axis.top + dst_axis.height; + + memcpy(&received_frame->output_axis, &area_bound, sizeof(struct output_axis)); +} + +static void set_output_buffer_area(struct v2d_dev *dev, struct frames_info_t *frames_info, + struct received_frames_t *received_frame, struct vframe_s *src_vf) +{ + struct frame_info_t *vframe_info_cur = NULL; + struct output_axis area_bound; + u32 out_crop[4] = {0}; + u32 dewarp_crop_top = 0, dewarp_crop_left = 0; + u32 dewarp_crop_bottom = 0, dewarp_crop_right = 0; + u32 dewarp_src_w, dewarp_src_h; + u32 dewarp_dst_w, dewarp_dst_h; + + memcpy(&area_bound, &received_frame->output_axis, sizeof(struct output_axis)); + if (dev->dev_choice != COMPOSER_WITH_DEWARP) { + out_crop[0] = area_bound.min_top * dev->buf_width / dev->vinfo_w; + out_crop[1] = area_bound.min_left * dev->buf_height / dev->vinfo_h; + out_crop[2] = dev->buf_height - area_bound.max_bottom * + dev->buf_height / dev->vinfo_h; + out_crop[3] = dev->buf_width - area_bound.max_right * + dev->buf_height / dev->vinfo_h; + } else { + v2d_print(dev->index, PRINT_DEWARP, "dewarp process out_buffer crop.\n"); + vframe_info_cur = &frames_info->input_buffer[0]; + if (vframe_info_cur->crop_w > 0 || vframe_info_cur->crop_h > 0) { + if (src_vf) { + if (src_vf->type & VIDTYPE_COMPRESS) { + dewarp_src_w = src_vf->compWidth; + dewarp_src_h = src_vf->compHeight; + } else { + dewarp_src_w = src_vf->width; + dewarp_src_h = src_vf->height; + } + v2d_print(dev->index, PRINT_DEWARP, + "src_vf: compWidth:%d compHeight:%d w:%d h:%d.\n", + src_vf->compWidth, + src_vf->compHeight, + src_vf->width, + src_vf->height); + } else { + dewarp_src_w = vframe_info_cur->buffer_w; + dewarp_src_h = vframe_info_cur->buffer_h; + } + if (src_vf && is_src_crop_valid(src_vf->src_crop)) { + dewarp_crop_top = + MAX(vframe_info_cur->crop_y, src_vf->src_crop.top); + dewarp_crop_left = + MAX(vframe_info_cur->crop_x, src_vf->src_crop.left); + dewarp_crop_bottom = MAX(dewarp_src_h - vframe_info_cur->crop_y + - vframe_info_cur->crop_h, src_vf->src_crop.bottom); + dewarp_crop_right = MAX(dewarp_src_w - vframe_info_cur->crop_x + - vframe_info_cur->crop_w, src_vf->src_crop.right); + } else { + dewarp_crop_top = vframe_info_cur->crop_y; + dewarp_crop_left = vframe_info_cur->crop_x; + dewarp_crop_bottom = dewarp_src_h - vframe_info_cur->crop_y + - vframe_info_cur->crop_h; + dewarp_crop_right = dewarp_src_w - vframe_info_cur->crop_x + - vframe_info_cur->crop_w; + } + dewarp_dst_w = (vframe_info_cur->dst_w * dev->buf_width / + dev->vinfo_w + 0xf) & ~0xf; + dewarp_dst_h = (vframe_info_cur->dst_h * dev->buf_height / + dev->vinfo_h + 0xf) & ~0xf; + if (dewarp_src_w <= 0 || dewarp_src_h <= 0) { + v2d_print(dev->index, PRINT_ERROR, + "%s: dewarp_src_w:%d dewarp_src_h:%d.\n", + __func__, dewarp_src_w, dewarp_src_h); + dewarp_src_w = BUFFER_4K_WIDTH; + dewarp_src_h = BUFFER_4K_HEIGHT; + } + if (vframe_info_cur->transform == V2D_TRANSFORM_ROT_270) { + out_crop[0] = dewarp_crop_right * dewarp_dst_h / dewarp_src_w; + out_crop[1] = dewarp_crop_top * dewarp_dst_w / dewarp_src_h; + out_crop[2] = dewarp_crop_left * dewarp_dst_h / dewarp_src_w; + out_crop[3] = dewarp_crop_bottom * dewarp_dst_w / dewarp_src_h; + } else if (vframe_info_cur->transform == V2D_TRANSFORM_ROT_180) { + out_crop[0] = dewarp_crop_bottom * dewarp_dst_h / dewarp_src_h; + out_crop[1] = dewarp_crop_right * dewarp_dst_w / dewarp_src_w; + out_crop[2] = dewarp_crop_top * dewarp_dst_h / dewarp_src_h; + out_crop[3] = dewarp_crop_left * dewarp_dst_w / dewarp_src_w; + } else if (vframe_info_cur->transform == V2D_TRANSFORM_ROT_90 || + vframe_info_cur->transform == V2D_TRANSFORM_FLIP_H_ROT_90 || + vframe_info_cur->transform == V2D_TRANSFORM_FLIP_V_ROT_90) { + out_crop[0] = dewarp_crop_left * dewarp_dst_h / dewarp_src_w; + out_crop[1] = dewarp_crop_bottom * dewarp_dst_w / dewarp_src_h; + out_crop[2] = dewarp_crop_right * dewarp_dst_h / dewarp_src_w; + out_crop[3] = dewarp_crop_top * dewarp_dst_w / dewarp_src_h; + } + } + } + + v2d_print(dev->index, PRINT_DEWARP, + "dst_vf->crop: top:%d left:%d bottom:%d right:%d.\n", + out_crop[0], out_crop[1], out_crop[2], out_crop[3]); + + frames_info->out_buffer.crop_y = out_crop[0]; + frames_info->out_buffer.crop_x = out_crop[1]; + frames_info->out_buffer.crop_h = dev->buf_height - out_crop[2] - out_crop[0]; + frames_info->out_buffer.crop_w = dev->buf_width - out_crop[3] - out_crop[1]; + + frames_info->out_buffer.dst_x = received_frame->output_axis.min_left; + frames_info->out_buffer.dst_y = received_frame->output_axis.min_top; + frames_info->out_buffer.dst_w = received_frame->output_axis.max_right - + received_frame->output_axis.min_left + 1; + frames_info->out_buffer.dst_h = received_frame->output_axis.max_bottom - + received_frame->output_axis.min_top + 1; +} + +static struct vframe_s *v2d_get_vf_from_file(struct v2d_dev *dev, + struct file *file_vf) +{ + struct vframe_s *vf = NULL; + bool is_dec_vf = false; + bool is_v4l_vf = false; + struct file_private_data *file_private_data = NULL; + + if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(file_vf)) { + v2d_print(dev->index, PRINT_ERROR, + "%s: invalid param.\n", + __func__); + return vf; + } + + is_dec_vf = is_valid_mod_type(file_vf->private_data, VF_SRC_DECODER); + is_v4l_vf = is_valid_mod_type(file_vf->private_data, VF_PROCESS_V4LVIDEO); + + if (is_dec_vf) { + v2d_print(dev->index, PRINT_OTHER, "vf is from decoder\n"); + vf = dmabuf_get_vframe((struct dma_buf *)(file_vf->private_data)); + if (!vf) { + v2d_print(dev->index, PRINT_ERROR, "vf is NULL.\n"); + return vf; + } + dmabuf_put_vframe((struct dma_buf *)(file_vf->private_data)); + } else if (is_v4l_vf) { + v2d_print(dev->index, PRINT_OTHER, "vf is from v4lvideo\n"); + file_private_data = v2d_get_file_private_data(file_vf, true); + if (!file_private_data) + v2d_print(dev->index, PRINT_ERROR, + "invalid fd: no uvm, no v4lvideo!!\n"); + else + vf = &file_private_data->vf; + } + return vf; +} + +static int v2d_wait_file_fence(struct v2d_dev *dev, + struct file *fence_file) +{ + struct sync_file *sync_file = NULL; + struct dma_fence *fence_obj = NULL; + int ret = 1; + u64 timestamp; + u64 time_cost; + + if (!IS_ERR_OR_NULL(fence_file)) { + sync_file = (struct sync_file *)fence_file->private_data; + } else { + v2d_print(dev->index, PRINT_FENCE, "wait: fence_file is NULL\n"); + return 1; + } + + if (!IS_ERR_OR_NULL(sync_file)) { + fence_obj = sync_file->fence; + } else { + v2d_print(dev->index, PRINT_FENCE, "sync_file is NULL\n"); + fput(fence_file); + return 1; + } + + if (fence_obj) { + v2d_print(dev->index, PRINT_FENCE, "sync_file=%px, seqno=%lld\n", + sync_file, fence_obj->seqno); + timestamp = local_clock(); + ret = dma_fence_wait_timeout(fence_obj, + false, msecs_to_jiffies(3000)); + if (ret == 0) { + v2d_print(dev->index, PRINT_ERROR, "fence wait timeout\n"); + fput(fence_file); + return 0; + } + + time_cost = local_clock() - timestamp; + dev->fence_wait_time_total += time_cost; + dev->fence_wait_count++; + if (dev->fence_wait_count == 100) { + v2d_print(dev->index, PRINT_FENCE, + "wait fence avg=%lldns\n", + div64_u64(dev->fence_wait_time_total, dev->fence_wait_count)); + dev->fence_wait_count = 0; + dev->fence_wait_time_total = 0; + } + + v2d_print(dev->index, PRINT_FENCE, + "wait fence, state: %d, wait cost time:%lldms\n", + ret, + div64_u64(time_cost, 1000000)); + } + + fput(fence_file); + return 1; +} + +static int v2d_init_ge2d_buffer(struct v2d_dev *dev, bool is_tvp) +{ + int i, flags; + u32 buf_width, buf_height, buf_size; + + buf_width = dev->buf_width; + buf_height = dev->buf_height; + + if (buf_config_444) + buf_size = buf_width * buf_height * 3; + else + buf_size = buf_width * buf_height * 3 / 2; + + buf_size = PAGE_ALIGN(buf_size); + if (is_tvp) + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_TVP; + else + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR; + + for (i = 0; i < BUFFER_LEN; i++) { + if (dev->dst_buf[i].phy_addr == 0) + dev->dst_buf[i].phy_addr = codec_mm_alloc_for_dma(ports[dev->index].name, + buf_size / PAGE_SIZE, 0, flags); + v2d_print(dev->index, PRINT_ERROR, + "%s: cma memory is %x , size is %x\n", + ports[dev->index].name, + (unsigned int)dev->dst_buf[i].phy_addr, + (unsigned int)buf_size); + + if (dev->dst_buf[i].phy_addr == 0) { + dev->buffer_status = INIT_BUFFER_ERROR; + v2d_print(dev->index, PRINT_ERROR, "cma memory config fail\n"); + return -1; + } + dev->dst_buf[i].index = i; + dev->dst_buf[i].dirty = true; + dev->dst_buf[i].buf_w = buf_width; + dev->dst_buf[i].buf_h = buf_height; + dev->dst_buf[i].buf_size = buf_size; + dev->dst_buf[i].is_tvp = is_tvp; + dev->dst_buf[i].buf_used = BUFFER_MODE_GE2D; + + if (!kfifo_put(&dev->free_q, &dev->dst_buf[i])) + v2d_print(dev->index, PRINT_ERROR, "init buffer free_q is full\n"); + } + return 0; +} + +static int v2d_init_dewarp_buffer(struct v2d_dev *dev, bool is_tvp) +{ + int i, flags; + u32 buf_width, buf_height, buf_size; + + buf_width = dev->buf_width; + buf_height = dev->buf_height; + + if (buf_config_444) + buf_size = buf_width * buf_height * 3; + else + buf_size = buf_width * buf_height * 3 / 2; + + buf_size = PAGE_ALIGN(buf_size); + + if (is_tvp) + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_TVP; + else + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR; + + for (i = 0; i < BUFFER_LEN; i++) { + if (dev->dst_buf[i].phy_addr == 0) + dev->dst_buf[i].phy_addr = codec_mm_alloc_for_dma(ports[dev->index].name, + buf_size / PAGE_SIZE, 0, flags); + v2d_print(dev->index, PRINT_ERROR, + "%s: cma memory is %lx , size is %x\n", + ports[dev->index].name, + (unsigned long)dev->dst_buf[i].phy_addr, + (unsigned int)buf_size); + + if (dev->dst_buf[i].phy_addr == 0) { + dev->buffer_status = INIT_BUFFER_ERROR; + v2d_print(dev->index, PRINT_ERROR, "cma memory config fail\n"); + return -1; + } + dev->dst_buf[i].index = i; + dev->dst_buf[i].dirty = true; + dev->dst_buf[i].buf_w = buf_width; + dev->dst_buf[i].buf_h = buf_height; + dev->dst_buf[i].buf_size = buf_size; + dev->dst_buf[i].is_tvp = is_tvp; + dev->dst_buf[i].buf_used = BUFFER_MODE_DEWARP; + + if (!kfifo_put(&dev->free_q, &dev->dst_buf[i])) + v2d_print(dev->index, PRINT_ERROR, "init buffer free_q is full\n"); + } + return 0; +} + +static int v2d_init_vicp_buffer(struct v2d_dev *dev, bool is_tvp) +{ + int i, j, flags; + u32 buf_addr = 0; + u32 buf_width, buf_height, buf_size; + int dw_size = 0, afbc_body_size = 0, afbc_head_size = 0, afbc_table_size = 0; + u32 *virt_addr = NULL, *temp_addr = NULL; + u32 temp_body_addr; + ulong buf_handle, buf_phy_addr; + + buf_width = dev->buf_width; + buf_height = dev->buf_height; + + if (vicp_output_mode == 1) { + buf_size = buf_width * buf_height * 3; + if (buf_config_444 == 0) + buf_size = buf_size * 3 / 2; + } else { + if (vicp_output_mode == 3)//mif + dw_size = roundup(buf_width >> 2, 32) * roundup(buf_height >> 2, 2); + + afbc_body_size = buf_width * buf_height + (1024 * 1658); + if (buf_config_444 == 0) { + dw_size = dw_size * 3 / 2; + afbc_body_size = afbc_body_size * 3 / 2; + } + + dw_size = PAGE_ALIGN(dw_size); + afbc_body_size = roundup(PAGE_ALIGN(afbc_body_size), PAGE_SIZE); + afbc_head_size = (roundup(buf_width, 64) * roundup(buf_height, 64)) / 32; + afbc_head_size = PAGE_ALIGN(afbc_head_size); + afbc_table_size = PAGE_ALIGN((afbc_body_size * 4) / PAGE_SIZE); + buf_size = dw_size + afbc_body_size + afbc_head_size; + } + + buf_size = PAGE_ALIGN(buf_size); + + if (is_tvp) + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_TVP; + else + flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR; + + for (i = 0; i < BUFFER_LEN; i++) { + if (dev->dst_buf[i].phy_addr == 0) + buf_addr = codec_mm_alloc_for_dma(ports[dev->index].name, + buf_size / PAGE_SIZE, 0, flags); + + if (buf_addr == 0) { + dev->buffer_status = INIT_BUFFER_ERROR; + v2d_print(dev->index, PRINT_ERROR, "cma memory config fail\n"); + return -1; + } + + dev->dst_buf[i].phy_addr = buf_addr; + v2d_print(dev->index, PRINT_ERROR, + "%s: cma memory is 0x%lx , size is 0x%x.\n", + ports[dev->index].name, dev->dst_buf[i].phy_addr, buf_size); + + dev->dst_buf[i].index = i; + dev->dst_buf[i].dirty = true; + dev->dst_buf[i].buf_w = buf_width; + dev->dst_buf[i].buf_h = buf_height; + dev->dst_buf[i].buf_size = buf_size; + dev->dst_buf[i].is_tvp = is_tvp; + dev->dst_buf[i].buf_used = BUFFER_MODE_VICP; + + if (vicp_output_mode != 1) { + dev->dst_buf[i].dw_size = dw_size; + dev->dst_buf[i].afbc_body_addr = dev->dst_buf[i].phy_addr + dw_size; + dev->dst_buf[i].afbc_body_size = afbc_body_size; + dev->dst_buf[i].afbc_head_addr = dev->dst_buf[i].afbc_body_addr + + afbc_body_size; + dev->dst_buf[i].afbc_head_size = afbc_head_size; + + virt_addr = (u32 *)codec_mm_dma_alloc_coherent(&buf_handle, &buf_phy_addr, + afbc_table_size, dev->port->name); + dev->dst_buf[i].afbc_table_handle = buf_handle; + dev->dst_buf[i].afbc_table_addr = buf_phy_addr; + dev->dst_buf[i].afbc_table_size = afbc_table_size; + if (dev->dst_buf[i].afbc_table_addr == 0) { + dev->buffer_status = INIT_BUFFER_ERROR; + v2d_print(dev->index, PRINT_ERROR, "alloc table buf fail.\n"); + return -1; + } + temp_body_addr = dev->dst_buf[i].afbc_body_addr & 0xffffffff; + memset(virt_addr, 0, afbc_table_size); + temp_addr = virt_addr; + for (j = 0; j < afbc_body_size; j += 4096) { + *virt_addr = ((j + temp_body_addr) >> 12) & 0x000fffff; + virt_addr++; + } + } + + if (!kfifo_put(&dev->free_q, &dev->dst_buf[i])) + v2d_print(dev->index, PRINT_ERROR, "init buffer free_q is full\n"); + } + + return 0; +} + +static int config_ge2d_param(struct v2d_dev *dev, + struct frame_info_t *vframe_info_cur, struct v2d_dev_config *v2d_composer_param) +{ + int ret = 0; + u32 cur_transform; + struct vframe_s *input_vf = NULL; + struct ge2d_src_para_s *ge2d_data = NULL; + + if (!dev || !vframe_info_cur || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + input_vf = v2d_composer_param->input_vf; + ge2d_data = &v2d_composer_param->composer_device_param.ge2d_data; + + v2d_print(dev->index, PRINT_OTHER, "use ge2d composer.\n"); + if (vframe_info_cur->buffer_format == YUV444) + ge2d_data->is_yuv444 = true; + else + ge2d_data->is_yuv444 = false; + ret = v2d_config_ge2d_data(v2d_composer_param->input_vf, + v2d_composer_param->addr, + vframe_info_cur->buffer_w, + vframe_info_cur->buffer_h, + vframe_info_cur->align_w, + vframe_info_cur->align_h, + v2d_composer_param->frame_crop.left, + v2d_composer_param->frame_crop.top, + v2d_composer_param->frame_crop.width, + v2d_composer_param->frame_crop.height, + ge2d_data); + if (ret < 0) { + v2d_print(dev->index, PRINT_ERROR, "config ge2d data error\n"); + return -1; + } + cur_transform = vframe_info_cur->transform; + if (input_vf && input_vf->flag & VFRAME_FLAG_MIRROR_H) { + if (cur_transform & V2D_TRANSFORM_FLIP_H) + cur_transform &= ~V2D_TRANSFORM_FLIP_H; + else + cur_transform |= V2D_TRANSFORM_FLIP_H; + } + if (input_vf && input_vf->flag & VFRAME_FLAG_MIRROR_V) { + if (cur_transform & V2D_TRANSFORM_FLIP_V) + cur_transform &= ~V2D_TRANSFORM_FLIP_V; + else + cur_transform |= V2D_TRANSFORM_FLIP_V; + } + v2d_print(dev->index, PRINT_AXIS, + "display_axis: left top width height: %d %d %d %d\n", + v2d_composer_param->frame_axis.left, v2d_composer_param->frame_axis.top, + v2d_composer_param->frame_axis.width, v2d_composer_param->frame_axis.height); + + dev->ge2d_para.angle = cur_transform; + dev->ge2d_para.position_left = v2d_composer_param->frame_axis.left; + dev->ge2d_para.position_top = v2d_composer_param->frame_axis.top; + dev->ge2d_para.position_width = v2d_composer_param->frame_axis.width; + dev->ge2d_para.position_height = v2d_composer_param->frame_axis.height; + + return 0; +} + +static int process_ge2d_data(struct v2d_dev *dev, struct v2d_dev_config *v2d_composer_param) +{ + struct ge2d_src_para_s *ge2d_data = NULL; + int ret = 0; + + if (!dev || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + ge2d_data = &v2d_composer_param->composer_device_param.ge2d_data; + ret = v2d_ge2d_data_composer(ge2d_data, &dev->ge2d_para); + return ret; +} + +static int config_dewarp_param(struct v2d_dev *dev, + struct frame_info_t *vframe_info_cur, struct v2d_dev_config *v2d_composer_param) +{ + int ret = 0; + struct dewarp_vf_para_s *dewarp_data = NULL; + struct dst_buf_t *dst_buf = NULL; + struct dewarp_common_para common_para; + + if (!dev || !vframe_info_cur || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + v2d_print(dev->index, PRINT_OTHER, "use dewarp composer.\n"); + + dewarp_data = &v2d_composer_param->composer_device_param.dewarp_data; + dst_buf = v2d_composer_param->dst_buf; + + //common_para ensures compatibility with older API functions. + common_para.input_para.vframe = v2d_composer_param->input_vf; + common_para.input_para.call_index = dev->index; + common_para.input_para.transform = vframe_info_cur->transform; + common_para.input_para.pic_info.format = vframe_info_cur->buffer_format; + common_para.input_para.pic_info.width = vframe_info_cur->buffer_w; + common_para.input_para.pic_info.height = vframe_info_cur->buffer_h; + common_para.input_para.pic_info.addr[0] = v2d_composer_param->addr; + common_para.input_para.pic_info.align_w = vframe_info_cur->align_w; + common_para.input_para.pic_info.align_h = vframe_info_cur->align_h; + + common_para.output_para.pic_info.align_w = + (vframe_info_cur->dst_w * dst_buf->buf_w / dev->vinfo_w + 0xf) & ~0xf; + common_para.output_para.pic_info.align_h = + (vframe_info_cur->dst_h * dst_buf->buf_h / dev->vinfo_h + 0xf) & ~0xf; + common_para.output_para.pic_info.addr[0] = dst_buf->phy_addr; + common_para.input_para.pic_info.is_tvp = v2d_composer_param->is_tvp; + + ret = v2d_config_dewarp_vframe(dewarp_data, &common_para); + if (ret < 0) + v2d_print(dev->index, PRINT_ERROR, "dewarp config err.\n"); + dev->dewarp_para.vf_para = dewarp_data; + ret = v2d_load_dewarp_firmware(&dev->dewarp_para); + if (ret != 0) { + v2d_print(dev->index, PRINT_ERROR, "load firmware failed.\n"); + return -1; + } + + return 0; +} + +static int process_dewarp_data(struct v2d_dev *dev, struct v2d_dev_config *v2d_composer_param) +{ + int ret = 0; + + if (!dev || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + //the second param is not used, is_tvp is in dev->dewarp_para + ret = v2d_dewarp_data_composer(&dev->dewarp_para, false); + + return ret; +} + +static int config_vicp_param(struct v2d_dev *dev, + struct frame_info_t *vframe_info_cur, struct v2d_dev_config *v2d_composer_param) +{ + struct vframe_s *input_vf = NULL; + struct vicp_data_config_s *vicp_data = NULL; + struct dst_buf_t *dst_buf = NULL; + int mifout_en = 1, fbcout_en = 1; + int fbc_init_ctrl, fbc_pip_mode; + ulong buf_addr[3]; + enum vicp_skip_mode_e skip_mode[MAX_LAYER_COUNT] = {VICP_SKIP_MODE_OFF}; + + if (!dev || !vframe_info_cur || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + input_vf = v2d_composer_param->input_vf; + vicp_data = &v2d_composer_param->composer_device_param.vicp_data; + dst_buf = v2d_composer_param->dst_buf; + + v2d_config_vicp_input_data(input_vf, + v2d_composer_param->addr, + vframe_info_cur->buffer_w, + vframe_info_cur->buffer_h, + vframe_info_cur->align_w, + vframe_info_cur->align_h, + 1, + VICP_COLOR_FORMAT_YUV420, + 8, + &vicp_data->input_data); + + mifout_en = (vicp_output_mode != 2); + fbcout_en = (vicp_output_mode != 1); + + buf_addr[0] = (ulong)dst_buf->phy_addr; + if (fbcout_en) { + buf_addr[1] = dst_buf->afbc_head_addr; + buf_addr[2] = dst_buf->afbc_table_addr; + } + + if (v2d_composer_param->count == 1 || v2d_composer_param->index == 0) { + fbc_init_ctrl = 1; + fbc_pip_mode = 1; + } else { + fbc_init_ctrl = 0; + fbc_pip_mode = 1; + } + + v2d_config_vicp_output_data(fbcout_en, + mifout_en, + buf_addr, + dst_buf->buf_w, + dst_buf->buf_w, + dst_buf->buf_h, + 1, + VICP_COLOR_FORMAT_YUV420, + 8, + VICP_COLOR_FORMAT_YUV420, + 8, + fbc_init_ctrl, + fbc_pip_mode, + VFRAME_SIGNAL_FMT_SDR, + &vicp_data->output_data); + vicp_data->data_option.rotation_mode = + map_rotationmode_from_v2d_to_vicp(vframe_info_cur->transform); + vicp_data->data_option.crop_info.left = v2d_composer_param->frame_crop.left; + vicp_data->data_option.crop_info.top = v2d_composer_param->frame_crop.top; + vicp_data->data_option.crop_info.width = v2d_composer_param->frame_crop.width; + vicp_data->data_option.crop_info.height = v2d_composer_param->frame_crop.height; + vicp_data->data_option.output_axis.left = v2d_composer_param->frame_axis.left; + vicp_data->data_option.output_axis.top = v2d_composer_param->frame_axis.top; + vicp_data->data_option.output_axis.width = v2d_composer_param->frame_axis.width; + vicp_data->data_option.output_axis.height = v2d_composer_param->frame_axis.height; + + vicp_data->data_option.shrink_mode = + (enum vicp_shrink_mode_e)vicp_shrink_mode; + if (v2d_composer_param->count > 1) + vicp_data->data_option.rdma_enable = true; + else + vicp_data->data_option.rdma_enable = false; + vicp_data->data_option.input_source_count = v2d_composer_param->count; + vicp_data->data_option.input_source_number = v2d_composer_param->index; + vicp_data->data_option.security_enable = v2d_composer_param->is_tvp; + vicp_data->data_option.skip_mode = skip_mode[v2d_composer_param->zorder]; + vicp_data->data_option.compress_rate = lossy_compress_rate; + + return 0; +} + +static int process_vicp_data(struct v2d_dev *dev, struct v2d_dev_config *v2d_composer_param) +{ + struct vicp_data_config_s *vicp_data = NULL; + int ret = 0; + + if (!dev || !v2d_composer_param) { + pr_info("%s:input param err\n", __func__); + return -1; + } + + vicp_data = &v2d_composer_param->composer_device_param.vicp_data; + ret = v2d_vicp_data_composer(vicp_data); + return ret; +} + +static void v2d_fence_signal_cb(struct dma_fence *fence, struct dma_fence_cb *cb) +{ + struct v2d_fence_cb_t *v2d_cb; + struct v2d_dev *dev; + struct file *need_recycle_file; + struct file *fence_file; + struct display_data_t *display_data = NULL; + int len, i; + + v2d_cb = container_of(cb, struct v2d_fence_cb_t, base_cb); + dev = v2d_cb->dev; + need_recycle_file = v2d_cb->buffer_file; + fence_file = v2d_cb->fence_file; + + len = kfifo_len(&dev->display_q); + for (i = 0; i < len; i++) { + if (!kfifo_get(&dev->display_q, &display_data)) { + v2d_print(dev->index, PRINT_ERROR, + "%s: error, display_q underflow!\n", __func__); + return; + } + if (display_data->output_dmabuf->file == need_recycle_file) + break; + + if (!kfifo_put(&dev->display_q, display_data)) + v2d_print(dev->index, PRINT_ERROR, "error, display_q overflow!\n"); + } + if (i == len) { + v2d_print(dev->index, PRINT_ERROR, + "recycle failed, could not find the match display_data!\n"); + return; + } + + fput(need_recycle_file); + fput(fence_file); + dev->buffer_release_count++; + + atomic_set(&display_data->on_use, false); + if (!kfifo_put(&dev->fence_cb_q, v2d_cb)) + v2d_print(dev->index, PRINT_ERROR, + "error, fence_cb_q is full\n"); + + if (!kfifo_put(&dev->file_free_q, display_data->output_dmabuf)) + v2d_print(dev->index, PRINT_ERROR, + "error, file_free_q is full\n"); + + if (!kfifo_put(&dev->free_q, display_data->output_buffer)) + v2d_print(dev->index, PRINT_ERROR, "error, free_q is empty\n"); + + v2d_print(dev->index, PRINT_FENCE, + "wait dma_fence:%px done, display_q:%d\n", + fence, kfifo_len(&dev->display_q)); +} + +static void config_output_vf_param(struct v2d_dev *dev, struct vframe_s *output_vf, + bool is_tvp, struct dst_buf_t *output_buffer, struct composer_info_t *composer_info) +{ + if (!dev || !output_vf) { + pr_info("%s:input param err\n", __func__); + return; + } + + output_vf->flag |= VFRAME_FLAG_COMPOSER_DONE; + output_vf->bitdepth = (BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8); + if (!display_yuv444) { + output_vf->flag |= VFRAME_FLAG_VIDEO_LINEAR; + output_vf->type = (VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | VIDTYPE_VIU_NV21); + } else { + output_vf->type = (VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD); + } + if (is_tvp) + output_vf->flag |= VFRAME_FLAG_VIDEO_SECURE; + output_vf->canvas0Addr = -1; + output_vf->canvas1Addr = -1; + output_vf->width = output_buffer->buf_w; + output_vf->height = output_buffer->buf_h; + v2d_print(dev->index, PRINT_AXIS, + "composer:vf_w: %d, vf_h: %d\n", output_vf->width, output_vf->height); + if (display_yuv444) { + output_vf->canvas0_config[0].phy_addr = output_buffer->phy_addr; + output_vf->canvas0_config[0].width = output_buffer->buf_w * 3; + output_vf->canvas0_config[0].height = output_buffer->buf_h; + output_vf->canvas0_config[0].block_mode = 0; + output_vf->plane_num = 1; + } else { + output_vf->canvas0_config[0].phy_addr = output_buffer->phy_addr; + output_vf->canvas0_config[0].width = output_vf->width; + output_vf->canvas0_config[0].height = output_vf->height; + output_vf->canvas0_config[0].block_mode = 0; + + output_vf->canvas0_config[1].phy_addr = output_buffer->phy_addr + + output_vf->width * output_vf->height; + output_vf->canvas0_config[1].width = output_vf->width; + output_vf->canvas0_config[1].height = output_vf->height >> 1; + output_vf->canvas0_config[1].block_mode = 0; + output_vf->plane_num = 2; + } + + output_vf->repeat_count = 0; + output_vf->composer_info = composer_info; +} + +static int v2d_init_buffer(struct v2d_dev *dev, bool is_tvp) +{ + int ret = 0; + + switch (dev->buffer_status) { + case UNINITIAL:/*not config size, return failure*/ + return -1; + case INIT_SIZE_SUCCESS: + break; + case INIT_BUFFER_SUCCESS:/*config before , return ok*/ + return 0; + case INIT_BUFFER_ERROR:/*config fail, won't retry , return failure*/ + return -1; + default: + return -1; + } + + if (dev->dev_choice == COMPOSER_WITH_GE2D) { + ret = v2d_init_ge2d_buffer(dev, is_tvp); + if (IS_ERR_OR_NULL(dev->ge2d_para.context)) + ret |= v2d_init_ge2d_composer(&dev->ge2d_para); + dev->config_param_func = config_ge2d_param; + dev->process_data_func = process_ge2d_data; + } else if (dev->dev_choice == COMPOSER_WITH_DEWARP) { + v2d_init_dewarp_buffer(dev, is_tvp); + if (IS_ERR_OR_NULL(dev->dewarp_para.context)) + ret |= v2d_init_dewarp_composer(&dev->dewarp_para); + dev->config_param_func = config_dewarp_param; + dev->process_data_func = process_dewarp_data; + v2d_print(dev->index, PRINT_ERROR, "only ge2d is config by now\n"); + } else { + ret = v2d_init_vicp_buffer(dev, is_tvp); + dev->config_param_func = config_vicp_param; + dev->process_data_func = process_vicp_data; + v2d_print(dev->index, PRINT_OTHER, "VICP buffer init success\n"); + } + if (ret < 0) + return -1; + dev->buffer_status = INIT_BUFFER_SUCCESS; + return 0; +} + +static void v2d_uninit_buffer(struct v2d_dev *dev) +{ + int i; + int ret = 0; + + if (dev->buffer_status == UNINITIAL) { + v2d_print(dev->index, PRINT_OTHER, + "%s buffer have uninit already finished!\n", __func__); + return; + } + + if (!IS_ERR_OR_NULL(dev->dewarp_para.context)) { + ret = v2d_uninit_dewarp_composer(&dev->dewarp_para); + if (ret < 0) + v2d_print(dev->index, PRINT_ERROR, "uninit dewarp composer fail!\n"); + dev->dewarp_para.context = NULL; + } + + if (!IS_ERR_OR_NULL(dev->ge2d_para.context)) { + ret = v2d_uninit_ge2d_composer(&dev->ge2d_para); + if (ret < 0) + v2d_print(dev->index, PRINT_ERROR, "uninit ge2d composer failed!\n"); + dev->ge2d_para.context = NULL; + } + + dev->buffer_status = UNINITIAL; + for (i = 0; i < BUFFER_LEN; i++) { + if (dev->dst_buf[i].phy_addr != 0) { + pr_info("%s: cma free addr is %x\n", + ports[dev->index].name, + (unsigned int)dev->dst_buf[i].phy_addr); + codec_mm_free_for_dma(ports[dev->index].name, + dev->dst_buf[i].phy_addr); + dev->dst_buf[i].phy_addr = 0; + if (vicp_output_mode != 1 && dev->dev_choice == COMPOSER_WITH_VICP) { + codec_mm_dma_free_coherent(dev->dst_buf[i].afbc_table_handle); + dev->dst_buf[i].afbc_table_addr = 0; + } + } + } + + dev->dev_choice = COMPOSER_WITH_UNINITIAL; + + INIT_KFIFO(dev->free_q); + kfifo_reset(&dev->free_q); +} + +static void v2d_fence_cb_q_init(struct v2d_dev *dev) +{ + int i; + + INIT_KFIFO(dev->fence_cb_q); + kfifo_reset(&dev->fence_cb_q); + + memset(dev->v2d_fence_cb, 0, sizeof(dev->v2d_fence_cb)); + for (i = 0; i < V2D_POOL_SIZE; i++) { + if (!kfifo_put(&dev->fence_cb_q, &dev->v2d_fence_cb[i])) + v2d_print(dev->index, PRINT_ERROR, "%s failed", __func__); + } +} + +static void v2d_config_init(struct v2d_dev *dev) +{ + if (!dev) + return; + + INIT_KFIFO(dev->free_q); + INIT_KFIFO(dev->file_wait_q); + INIT_KFIFO(dev->fence_wait_q); + + kfifo_reset(&dev->free_q); + kfifo_reset(&dev->file_wait_q); + kfifo_reset(&dev->fence_wait_q); + + file_q_init(dev); + display_q_init(dev); + receive_q_init(dev); + v2d_fence_cb_q_init(dev); + + dev->received_new_count = 0; + dev->fence_creat_count = 0; + dev->fence_signal_count = 0; + dev->buffer_release_count = 0; + dev->dev_choice = COMPOSER_WITH_UNINITIAL; + init_completion(&dev->file_task_done); + dev_get_vinfo(dev); +} + +static int v2d_config_uninit(struct v2d_dev *dev) +{ + int time_left = 0; + + dev->need_do_file = true; + wake_up_interruptible(&dev->file_wq); + time_left = wait_for_completion_timeout(&dev->file_task_done, + msecs_to_jiffies(500)); + if (!time_left) + v2d_print(dev->index, PRINT_ERROR, "unreg:wait file timeout\n"); + else if (time_left < 100) + v2d_print(dev->index, PRINT_ERROR, + "unreg:do file wait time left:%d\n", time_left); + + display_q_uninit(dev); + v2d_uninit_buffer(dev); + receive_q_uninit(dev); + file_q_uninit(dev); + + return 0; +} + +static int v2d_set_enable(struct v2d_dev *dev, u32 val) +{ + if (val > V2D_SET_ENABLE_MODE) + return -EINVAL; + + v2d_print(dev->index, PRINT_ERROR, "set enable index=%d, val=%d\n", dev->index, val); + + if (dev->status_enabled == val) { + v2d_print(dev->index, PRINT_ERROR, + "set_enable repeat, dev index =%d\n", dev->index); + return 0; + } + + if (val == V2D_SET_ENABLE_MODE) + v2d_config_init(dev); + else if (val == V2D_SET_DISABLE_MODE) + v2d_config_uninit(dev); + + dev->status_enabled = val; + + return 0; +} + +static int v2d_set_frames(struct v2d_dev *dev, + struct frames_info_t *frames_info) +{ + struct file *file_vf = NULL; + struct file *fence_file = NULL; + struct file *output_fence_file = NULL; + struct vframe_s *src_vf = NULL; + struct dma_buf *dmabuf; + struct frame_info_t *vframe_info_cur = NULL; + int out_fd; + int out_fence_fd; + int i = 0, j = 0; + bool is_dec_vf = false, is_v4l_vf = false; + bool is_tvp = 0; + + if (!dev || !frames_info) { + pr_err("%s: param is invalid.\n", __func__); + return -EINVAL; + } + + if (!dev->status_enabled) { + v2d_print(dev->index, PRINT_ERROR, + "%s: set_frame but not enabled\n", __func__); + return -EINVAL; + } + + if (frames_info->frame_count > MAX_LAYER_COUNT || + frames_info->frame_count < 1) { + v2d_print(dev->index, PRINT_ERROR, + "frame_count:%d, return\n", frames_info->frame_count); + return -EINVAL; + } + + v2d_print(dev->index, PRINT_OTHER, "%s: inside set_frames!\n", __func__); + + i = v2d_get_received_frame_free_index(dev); + if (!kfifo_get(&dev->file_free_q, &dmabuf)) { + v2d_print(dev->index, PRINT_ERROR, "peek free dma_buf fail!!!\n"); + return -EINVAL; + } +/* + * if (!kfifo_get(&dev->free_q, &dst_vf)) { + * v2d_print(dev->index, PRINT_ERROR, "set_frames; peek free_q failed\n"); + * return; + * } + * private_data = v2d_get_file_private_data(dmabuf->file, true); + */ + v2d_get_file_private_data(dmabuf->file, true); + + for (j = 0; j < frames_info->frame_count; j++) { + vframe_info_cur = &frames_info->input_buffer[j]; + file_vf = fget(vframe_info_cur->fd); + dev->fget_count++; + total_get_count++; + if (!file_vf) { + v2d_print(dev->index, PRINT_ERROR, + "%s: fd is invalid!!!\n", __func__); + return -EINVAL; + } + if (vframe_info_cur->fence_fd > 0) { + fence_file = fget(vframe_info_cur->fence_fd); + if (!fence_file) { + v2d_print(dev->index, PRINT_ERROR, + "%s: fence_fd is invalid!!!\n", __func__); + return -EINVAL; + } + dev->received_frames[i].input_fence[j] = fence_file; + } + dev->received_frames[i].input_file[j] = file_vf; + + v2d_print(dev->index, PRINT_OTHER, + "%s:input_fd:%d, file_vf = 0x%px, fget_count=%d, total_get_count:%d.\n", + __func__, + vframe_info_cur->fd, + file_vf, + dev->fget_count, + total_get_count); + is_dec_vf = is_valid_mod_type(file_vf->private_data, VF_SRC_DECODER); + is_v4l_vf = is_valid_mod_type(file_vf->private_data, VF_PROCESS_V4LVIDEO); + + if (is_dec_vf || is_v4l_vf) { + src_vf = v2d_get_vf_from_file(dev, file_vf); + if (!src_vf) { + v2d_print(dev->index, PRINT_ERROR, "get vf NULL\n"); + continue; + } + if (!is_tvp) { + if (src_vf->flag & VFRAME_FLAG_VIDEO_SECURE) + is_tvp = true; + } + v2d_print(dev->index, PRINT_OTHER, + "%s:frame_index:%d, vframe_type = 0x%x, vframe_flag = 0x%x.\n", + __func__, + src_vf->frame_index, + src_vf->type, + src_vf->flag); + } else { + dev->received_frames[i].phy_addr[j] = + get_dma_phy_addr(frames_info->input_buffer[j].fd, dev->index); + v2d_print(dev->index, PRINT_OTHER, "%s dma buffer not vf\n", __func__); + } + + if (j == 0) { + choose_v2d_device(dev, &dev->received_frames[i], src_vf); + init_output_buffer_size(dev, &dev->received_frames[i]); + } + + get_output_axis_crop(dev, src_vf, &dev->received_frames[i], vframe_info_cur, j); + } + + out_fence_fd = v2d_timeline_create_fence(dev); + if (!kfifo_put(&dev->file_wait_q, dmabuf)) + v2d_print(dev->index, PRINT_ERROR, "put file_wait fail\n"); + v2d_print(dev->index, PRINT_OTHER, + "%s file_wait_q count: %d\n", + __func__, kfifo_len(&dev->file_wait_q)); + + out_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fd < 0) { + v2d_print(dev->index, PRINT_ERROR, "not fd!!!\n"); + return -ENOMEM; + } + + /*out_fd is released by HWC*/ + fd_install(out_fd, dmabuf->file); + dma_buf_get(out_fd); + + frames_info->out_buffer.fd = out_fd; + frames_info->out_buffer.fence_fd = out_fence_fd; + frames_info->out_buffer.zorder = 0; + frames_info->out_buffer.buffer_w = dev->buf_width; + frames_info->out_buffer.buffer_h = dev->buf_height; + frames_info->out_buffer.align_w = ALIGN(dev->buf_width, 32); + frames_info->out_buffer.align_h = ALIGN(dev->buf_height, 32); + set_output_buffer_area(dev, frames_info, &dev->received_frames[i], src_vf); + + v2d_print(dev->index, PRINT_PATTERN, + "%s done, out_fd:%d out_fence:%d out_file:%px received_new_count:%d\n", + __func__, out_fd, out_fence_fd, dmabuf->file, dev->received_new_count); + + output_fence_file = fget(out_fence_fd); + fput(output_fence_file); + v2d_print(dev->index, PRINT_AXIS, + "output_axis:dst_x:%d dst_y:%d dst_w:%d dst_h:%d\n", + frames_info->out_buffer.dst_x, + frames_info->out_buffer.dst_y, + frames_info->out_buffer.dst_w, + frames_info->out_buffer.dst_h); + v2d_print(dev->index, PRINT_AXIS, + "output_crop:crop_x:%d crop_y:%d crop_w:%d crop_h:%d\n", + frames_info->out_buffer.crop_x, + frames_info->out_buffer.crop_y, + frames_info->out_buffer.crop_w, + frames_info->out_buffer.crop_h); + dev->received_frames[i].frames_info = *frames_info; + dev->received_frames[i].is_tvp = is_tvp; + atomic_set(&dev->received_frames[i].on_use, true); + dev->received_count++; + dev->received_new_count++; + + if (!kfifo_put(&dev->receive_q, &dev->received_frames[i])) + v2d_print(dev->index, PRINT_ERROR, "put ready fail\n"); + wake_up_interruptible(&dev->file_wq); + return 0; +} + +static int v2d_set_fence(struct v2d_dev *dev, struct release_info_t *release_info) +{ + struct file *buffer_file; + struct file *fence_file; + int fence_fd, fd; + struct sync_file *sync_file = NULL; + struct dma_fence *fence_obj = NULL; + struct v2d_fence_cb_t *cur_fence_cb; + int ret = 0; + + if (!dev) { + pr_err("%s: param is invalid.\n", __func__); + return -EINVAL; + } + + fd = release_info->release_fd; + fence_fd = release_info->release_fence_fd; + + v2d_print(dev->index, PRINT_FENCE, "%s: fd:%d fence:fd:%d\n", + __func__, fd, fence_fd); + buffer_file = fget(fd); + if (IS_ERR_OR_NULL(buffer_file)) { + v2d_print(dev->index, PRINT_ERROR, "%s: buffer_file is NULL\n", __func__); + return -EBADF; + } + + fence_file = fget(fence_fd); + if (IS_ERR_OR_NULL(fence_file)) { + v2d_print(dev->index, PRINT_ERROR, "%s: fence_file is NULL\n", __func__); + return -EBADF; + } + sync_file = (struct sync_file *)fence_file->private_data; + if (IS_ERR_OR_NULL(sync_file)) { + v2d_print(dev->index, PRINT_ERROR, "sync_file is NULL\n"); + fput(buffer_file); + fput(fence_file); + return -ENOBUFS; + } + + fence_obj = sync_file->fence; + if (IS_ERR_OR_NULL(fence_obj)) { + v2d_print(dev->index, PRINT_ERROR, "fence_obj is NULL\n"); + fput(buffer_file); + fput(fence_file); + return -ENOBUFS; + } + + v2d_print(dev->index, PRINT_FENCE, "sync_file=%px, dma_fence=%px, seqno=%lld\n", + sync_file, fence_obj, fence_obj->seqno); + + if (!kfifo_get(&dev->fence_cb_q, &cur_fence_cb)) { + v2d_print(dev->index, PRINT_ERROR, + "%s: fence_cb_q is empty\n", __func__); + fput(fence_file); + return -ENOBUFS; + } + cur_fence_cb->dev = dev; + cur_fence_cb->buffer_file = buffer_file; + cur_fence_cb->fence_file = fence_file; + ret = dma_fence_add_callback(fence_obj, &cur_fence_cb->base_cb, v2d_fence_signal_cb); + if (ret == -ENOENT) { + v2d_print(dev->index, PRINT_ERROR, "fence has already signaled\n"); + v2d_fence_signal_cb(fence_obj, &cur_fence_cb->base_cb); + return 0; + } + if (ret) + v2d_print(dev->index, PRINT_ERROR, "dma_fence_add_callback failed: %d\n", ret); + return ret; +} + +static void v2d_do_file_task(struct v2d_dev *dev) +{ + struct received_frames_t *received_frames = NULL; + struct frame_info_t *vframe_info[MAX_LAYER_COUNT]; + struct frame_info_t *vframe_info_cur = NULL; + struct dma_buf *output_dmabuf; + struct dst_buf_t *output_buffer = NULL; + struct vframe_s *output_vf = NULL; + struct vframe_s *input_vf = NULL; + struct file_private_data *private_data = NULL; + struct file *file_vf = NULL; + struct file *fence_file = NULL; + struct output_axis output_axis; + struct composer_info_t *composer_info; + struct display_data_t *display_data; + struct v2d_dev_config v2d_composer_param; + s64 start_time, end_time, cost_time; + int vf_dev[MAX_LAYER_COUNT]; + unsigned long input_addr = 0; + bool is_tvp = false; + bool is_dec_vf = false, is_v4l_vf = false; + int i, j, tmp; + int count; + int ret = 0; + int data_index; + u32 zd1, zd2; + + if (!dev) { + pr_info("%s: invalid param.\n", __func__); + return; + } + v2d_print(dev->index, PRINT_OTHER, + "%s file_wait_q count: %d\n", + __func__, kfifo_len(&dev->file_wait_q)); + + if (!kfifo_get(&dev->receive_q, &received_frames)) { + v2d_print(dev->index, PRINT_ERROR, "%s: receive_q is empty.\n", __func__); + return; + } + + if (!kfifo_get(&dev->file_wait_q, &output_dmabuf)) { + v2d_print(dev->index, PRINT_ERROR, "%s: no output buf, return.\n", __func__); + return; + } + private_data = v2d_get_file_private_data(output_dmabuf->file, true); + if (!private_data) { + v2d_print(dev->index, PRINT_ERROR, "%s:private_data is null\n", __func__); + return; + } + + if (v2d_bypass) + v2d_print(dev->index, PRINT_OTHER, "%s:bypass consider later\n", __func__); + start_time = ktime_to_ns(ktime_get()); + output_vf = &private_data->vf; + is_tvp = received_frames->is_tvp; + count = received_frames->frames_info.frame_count; + memcpy(&output_axis, &received_frames->output_axis, sizeof(struct output_axis)); + + ret = v2d_init_buffer(dev, is_tvp); + if (ret != 0) { + v2d_print(dev->index, PRINT_ERROR, "v2d: init buffer failed!\n"); + v2d_uninit_buffer(dev); + return; + } + while (!kfifo_get(&dev->free_q, &output_buffer)) { + v2d_print(dev->index, PRINT_ERROR, "free q is empty!\n"); + usleep_range(2000, 4000); + } + + composer_info = &output_buffer->composer_info; + memset(composer_info, 0, sizeof(struct composer_info_t)); + + if (is_tvp != output_buffer->is_tvp) { + ret = v2d_switch_buffer(output_buffer, is_tvp, dev); + if (ret == 0) { + v2d_print(dev->index, PRINT_ERROR, + "switch buffer from %s to %s failed\n", + output_buffer->is_tvp ? "tvp" : "non tvp", + is_tvp ? "tvp" : "non tvp"); + return; + } + } + + if (dev->dev_choice == COMPOSER_WITH_GE2D) { + if (display_yuv444) { + dev->ge2d_para.format = GE2D_FORMAT_S24_YUV444; + dev->ge2d_para.plane_num = 1; + } else { + dev->ge2d_para.format = GE2D_FORMAT_M24_NV21; + dev->ge2d_para.plane_num = 2; + } + dev->ge2d_para.is_tvp = is_tvp; + dev->ge2d_para.phy_addr[0] = output_buffer->phy_addr; + dev->ge2d_para.buffer_w = output_buffer->buf_w; + dev->ge2d_para.buffer_h = output_buffer->buf_h; + dev->ge2d_para.canvas0_addr = -1; + + if (output_buffer->dirty) { + ret = v2d_fill_vframe_black(&dev->ge2d_para); + if (ret < 0) + v2d_print(dev->index, PRINT_ERROR, "ge2d fill black failed\n"); + else + v2d_print(dev->index, PRINT_OTHER, "fill black\n"); + output_buffer->dirty = false; + } + } + + for (i = 0; i < count; i++) { + vf_dev[i] = i; + vframe_info[i] = &received_frames->frames_info.input_buffer[i]; + } + for (i = 0; i < count - 1; i++) { + for (j = 0; j < count - 1 - i; j++) { + zd1 = vframe_info[vf_dev[j]]->zorder; + zd2 = vframe_info[vf_dev[j + 1]]->zorder; + if (zd1 > zd2) { + tmp = vf_dev[j]; + vf_dev[j] = vf_dev[j + 1]; + vf_dev[j + 1] = tmp; + } + } + } + for (i = 0; i < count; i++) { + input_vf = NULL; + file_vf = received_frames->input_file[vf_dev[i]]; + fence_file = received_frames->input_fence[vf_dev[i]]; + if (v2d_wait_file_fence(dev, fence_file) == 0) + continue; + memcpy(&v2d_composer_param.frame_crop, &received_frames->crop_info[vf_dev[i]], + sizeof(struct input_crop_s)); + memcpy(&v2d_composer_param.frame_axis, &received_frames->axis_info[vf_dev[i]], + sizeof(struct input_axis_s)); + vframe_info_cur = vframe_info[vf_dev[i]]; + if (!vframe_info_cur) { + v2d_print(dev->index, PRINT_ERROR, "vframe_info_cur NULL\n"); + return; + } + v2d_print(dev->index, PRINT_AXIS, + "=========frame info:==========\n"); + v2d_print(dev->index, PRINT_AXIS, + "frame axis x,y,w,h: %d %d %d %d\n", + vframe_info_cur->dst_x, vframe_info_cur->dst_y, + vframe_info_cur->dst_w, vframe_info_cur->dst_h); + v2d_print(dev->index, PRINT_AXIS, + "frame crop t,l,w,h: %d %d %d %d\n", + vframe_info_cur->crop_y, vframe_info_cur->crop_x, + vframe_info_cur->crop_w, vframe_info_cur->crop_h); + v2d_print(dev->index, PRINT_AXIS, + "frame buffer Width X Height: %d X %d\n", + vframe_info_cur->buffer_w, vframe_info_cur->buffer_h); + v2d_print(dev->index, PRINT_AXIS, + "frame buffer stride Width X Height: %d X %d\n", + vframe_info_cur->align_w, vframe_info_cur->align_h); + + is_dec_vf = is_valid_mod_type(file_vf->private_data, VF_SRC_DECODER); + is_v4l_vf = is_valid_mod_type(file_vf->private_data, VF_PROCESS_V4LVIDEO); + + if (is_dec_vf || is_v4l_vf) { + v2d_print(dev->index, PRINT_OTHER, "%s dmabuf is vf\n", __func__); + input_vf = v2d_get_vf_from_file(dev, file_vf); + if (!input_vf) { + v2d_print(dev->index, PRINT_ERROR, "get vf NULL\n"); + continue; + } + v2d_print(dev->index, PRINT_OTHER, "frame_index:%d\n", + input_vf->frame_index); + } else { + input_addr = received_frames->phy_addr[vf_dev[i]]; + v2d_print(dev->index, PRINT_OTHER, + "%s dmabuf not vf, i=%d fd=%d phy_addr=0x%lx\n", + __func__, vf_dev[i], vframe_info_cur->fd, input_addr); + } + v2d_print(dev->index, PRINT_AXIS, + "===============================\n"); + + v2d_composer_param.input_vf = input_vf; + v2d_composer_param.addr = input_addr; + v2d_composer_param.is_tvp = is_tvp; + v2d_composer_param.count = count; + v2d_composer_param.index = i; + v2d_composer_param.zorder = vf_dev[i]; + + dev->config_param_func(dev, vframe_info_cur, &v2d_composer_param); + + dev->process_data_func(dev, &v2d_composer_param); + } + + frames_put_file(dev, received_frames); + end_time = ktime_to_ns(ktime_get()); + cost_time = end_time - start_time; + v2d_print(dev->index, PRINT_PERFORMANCE, + "vframe composer cost: %lld us\n", + div64_u64(cost_time, 1000)); + + composer_info->count = count; + for (i = 0; i < count; i++) { + composer_info->axis[i][0] = vframe_info[vf_dev[i]]->dst_x + - output_axis.min_left; + composer_info->axis[i][1] = vframe_info[vf_dev[i]]->dst_y + - output_axis.min_top; + composer_info->axis[i][2] = vframe_info[vf_dev[i]]->dst_w + + composer_info->axis[i][0] - 1; + composer_info->axis[i][3] = vframe_info[vf_dev[i]]->dst_h + + composer_info->axis[i][1] - 1; + v2d_print(dev->index, PRINT_AXIS, + "alpha index=%d %d %d %d %d\n", + i, + composer_info->axis[i][0], + composer_info->axis[i][1], + composer_info->axis[i][2], + composer_info->axis[i][3]); + } + + config_output_vf_param(dev, output_vf, is_tvp, output_buffer, composer_info); + + if (count == 1 && input_vf) + output_vf->duration = input_vf->duration; + + if (enable_v2d_dump) { + enable_v2d_dump = 0; + v2d_dump_output_buffer(output_vf); + } + data_index = v2d_get_display_data_free_index(dev); + atomic_set(&dev->display_data[data_index].on_use, true); + + display_data = &dev->display_data[data_index]; + display_data->output_buffer = output_buffer; + display_data->output_dmabuf = output_dmabuf; + if (!kfifo_put(&dev->display_q, display_data)) + v2d_print(dev->index, PRINT_ERROR, "display_q is full\n"); + + v2d_timeline_increase(dev, 1); + v2d_print(dev->index, PRINT_PERFORMANCE, + "composer done, dmabuf=%px dst_addr:%lx\n", + output_dmabuf, output_buffer->phy_addr); + v2d_print(dev->index, PRINT_PERFORMANCE, + "display_q len=%d\n", kfifo_len(&dev->display_q)); + + atomic_set(&received_frames->on_use, false); +} + +static int v2d_file_thread(void *data) +{ + struct v2d_dev *dev = data; + + v2d_print(dev->index, PRINT_OTHER, "%s: started\n", __func__); + dev->file_thread_stopped = 0; + while (1) { + if (kthread_should_stop()) + break; + if (dev->thread_need_stop) { + usleep_range(1000, 2000); + continue; + } + + if (kfifo_len(&dev->receive_q) == 0) + wait_event_interruptible_timeout(dev->file_wq, + (kfifo_len(&dev->receive_q) > 0 && + dev->status_enabled) || + dev->need_do_file || + dev->thread_need_stop, + msecs_to_jiffies(5000)); + + if (kfifo_len(&dev->receive_q) > 0 && dev->status_enabled) + v2d_do_file_task(dev); + if (dev->need_do_file) { + v2d_print(dev->index, PRINT_OTHER, + "ready to complete file task\n"); + dev->need_do_file = false; + complete(&dev->file_task_done); + } + } + dev->file_thread_stopped = 1; + v2d_print(dev->index, PRINT_OTHER, "%s: exit\n", __func__); + return 0; +} + +static int v2d_open(struct inode *inode, struct file *file) +{ + struct v2d_dev *dev; + struct v2d_port_s *port = NULL; + struct sched_param param = {.sched_priority = 2}; + + pr_info("%s iminor(inode) =%d\n", __func__, iminor(inode)); + if (iminor(inode) >= v2d_instance_num) + return -ENODEV; + + // coverity[illegal_address] I'm ensure it is ok. + port = &ports[iminor(inode)]; + mutex_lock(&v2d_mutex); + + if (port->open_count > 0) { + mutex_unlock(&v2d_mutex); + pr_err("v2d: instance %d is already opened", + port->index); + return -EBUSY; + } + + dev = vmalloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + + dev->ge2d_para.count = 0; + dev->ge2d_para.canvas_dst[0] = -1; + dev->ge2d_para.canvas_dst[1] = -1; + dev->ge2d_para.canvas_dst[2] = -1; + dev->ge2d_para.canvas_scr[0] = -1; + dev->ge2d_para.canvas_scr[1] = -1; + dev->ge2d_para.canvas_scr[2] = -1; + dev->ge2d_para.plane_num = 2; + + dev->dewarp_para.index = dev->index; + dev->dewarp_para.context = NULL; + dev->dewarp_para.fw_load.size_32bit = 0; + dev->dewarp_para.fw_load.phys_addr = 0; + dev->dewarp_para.fw_load.virt_addr = NULL; + dev->dewarp_para.vf_para = NULL; + + dev->buffer_status = UNINITIAL; + + dev->thread_need_stop = false; + dev->port = port; + dev->index = port->index; + file->private_data = dev; + port->open_count++; + mutex_unlock(&v2d_mutex); + + dev->file_thread = kthread_create(v2d_file_thread, + dev, dev->port->name); + if (IS_ERR(dev->file_thread)) { + pr_err("v2d_composer_thread creat failed\n"); + return -ENOMEM; + } + + init_waitqueue_head(&dev->file_wq); + if (sched_setscheduler(dev->file_thread, SCHED_FIFO, ¶m)) + pr_err("v2d_composer_thread :set realtime priority failed.\n"); + wake_up_process(dev->file_thread); + v2d_timeline_create(dev); + + return 0; +} + +static int v2d_release(struct inode *inode, struct file *file) +{ + struct v2d_dev *dev = file->private_data; + struct v2d_port_s *port = dev->port; + int ret = 0; + + pr_info("%s enable=%d\n", __func__, dev->status_enabled); + + if (iminor(inode) >= V2D_INSTANCE_NUM) + return -ENODEV; + if (dev->status_enabled) { + ret = v2d_set_enable(dev, 0); + if (ret != 0) + pr_err("%s: disable v2d failed\n", __func__); + } + dev->thread_need_stop = true; + if (dev->file_thread) { + kthread_stop(dev->file_thread); + wake_up_interruptible(&dev->file_wq); + } + + dev->file_thread = NULL; + dev->thread_need_stop = false; + + mutex_lock(&v2d_mutex); + port->open_count--; + mutex_unlock(&v2d_mutex); + vfree(dev); + + return 0; +} + +static long v2d_ioctl(struct file *file, unsigned int cmd, ulong arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + u32 val; + struct v2d_dev *dev = (struct v2d_dev *)file->private_data; + struct frames_info_t frames_info; + struct release_info_t release_info; + + switch (cmd) { + case V2D_IOCTL_SET_ENABLE: + if (copy_from_user(&val, argp, sizeof(u32)) == 0) + ret = v2d_set_enable(dev, val); + else + ret = -EFAULT; + break; + case V2D_IOCTL_SET_FRAMES: + if (copy_from_user(&frames_info, argp, sizeof(frames_info)) != 0) + return -EFAULT; + + ret = v2d_set_frames(dev, &frames_info); + if (copy_to_user(argp, &frames_info, sizeof(struct frames_info_t)) != 0) + return -EFAULT; + break; + case V2D_IOCTL_SET_FENCE: + if (copy_from_user(&release_info, argp, sizeof(release_info)) == 0) + ret = v2d_set_fence(dev, &release_info); + else + ret = -EFAULT; + break; + default: + pr_info("set cmd err!"); + ret = -EFAULT; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long v2d_compat_ioctl(struct file *file, unsigned int cmd, ulong arg) +{ + long ret = 0; + + ret = v2d_ioctl(file, cmd, (ulong)compat_ptr(arg)); + return ret; +} +#endif + +static const struct file_operations v2d_fops = { + .owner = THIS_MODULE, + .open = v2d_open, + .release = v2d_release, + .unlocked_ioctl = v2d_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v2d_compat_ioctl, +#endif + .poll = NULL, +}; + +static ssize_t print_flag_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current print_flag is %d\n", + print_flag); +} + +static ssize_t print_flag_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + print_flag = tmp; + return count; +} + +static ssize_t buffer_width_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current print_flag is %d\n", + manual_buf_width); +} + +static ssize_t buffer_width_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + manual_buf_width = tmp; + return count; +} + +static ssize_t buffer_height_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current print_flag is %d\n", + manual_buf_height); +} + +static ssize_t buffer_height_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + manual_buf_height = tmp; + return count; +} + +static ssize_t bypass_v2d_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current print_flag is %d\n", + v2d_bypass); +} + +static ssize_t bypass_v2d_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + v2d_bypass = tmp; + return count; +} + +static ssize_t enable_v2d_dump_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current enable_v2d_dump is %d\n", + enable_v2d_dump); +} + +static ssize_t enable_v2d_dump_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + enable_v2d_dump = tmp; + return count; +} + +static ssize_t ge2d_com_debug_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current ge2d_com_debug is %d\n", + ge2d_com_debug); +} + +static ssize_t ge2d_com_debug_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + ge2d_com_debug = tmp; + return count; +} + +static ssize_t dewarp_com_dump_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current dewarp_com_dump is %d\n", + dewarp_com_dump); +} + +static ssize_t dewarp_com_dump_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + dewarp_com_dump = tmp; + return count; +} + +static ssize_t dewarp_print_show(const struct class *cla, + const struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 80, + "current dewarp_print is %d\n", + dewarp_print); +} + +static ssize_t dewarp_print_store(const struct class *cla, + const struct class_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + int ret; + + ret = kstrtol(buf, 0, &tmp); + if (ret != 0) { + pr_info("ERROR converting %s to long int!\n", buf); + return ret; + } + dewarp_print = tmp; + return count; +} + +static CLASS_ATTR_RW(print_flag); +static CLASS_ATTR_RW(buffer_height); +static CLASS_ATTR_RW(buffer_width); +static CLASS_ATTR_RW(bypass_v2d); +static CLASS_ATTR_RW(enable_v2d_dump); +static CLASS_ATTR_RW(ge2d_com_debug); +static CLASS_ATTR_RW(dewarp_com_dump); +static CLASS_ATTR_RW(dewarp_print); + +static struct attribute *v2d_class_attrs[] = { + &class_attr_print_flag.attr, + &class_attr_buffer_height.attr, + &class_attr_buffer_width.attr, + &class_attr_bypass_v2d.attr, + &class_attr_enable_v2d_dump.attr, + &class_attr_ge2d_com_debug.attr, + &class_attr_dewarp_com_dump.attr, + &class_attr_dewarp_print.attr, + NULL +}; + +ATTRIBUTE_GROUPS(v2d_class); + +static struct class v2d_class = { + .name = "v2d", + .class_groups = v2d_class_groups, +}; + +static const struct of_device_id amlogic_v2d_dt_match[] = { + {.compatible = "amlogic, v2d", + }, + {}, +}; + +static int v2d_probe(struct platform_device *pdev) +{ + int ret = 0; + int i = 0; + struct v2d_port_s *st; + + ret = class_register(&v2d_class); + if (ret < 0) + return ret; + ret = register_chrdev(V2D_MAJOR, + "v2d", &v2d_fops); + if (ret < 0) { + pr_err("Can't allocate major for v2d device\n"); + goto error1; + } + + for (st = &ports[0], i = 0; i < V2D_INSTANCE_NUM; i++, st++) { + pr_debug("%s:ports[i].name=%s, i=%d\n", __func__, + ports[i].name, i); + st->pdev = &pdev->dev; + st->class_dev = device_create(&v2d_class, NULL, + MKDEV(V2D_MAJOR, i), + NULL, ports[i].name); + } + return ret; + +error1: + pr_err("%s error\n", __func__); + unregister_chrdev(V2D_MAJOR, "v2d"); + class_unregister(&v2d_class); + return ret; +} + +static void v2d_remove(struct platform_device *pdev) +{ + int i; + struct v2d_port_s *st; + + for (st = &ports[0], i = 0; i < V2D_INSTANCE_NUM; i++, st++) + device_destroy(&v2d_class, MKDEV(V2D_MAJOR, i)); + + unregister_chrdev(V2D_MAJOR, "v2d"); + class_destroy(&v2d_class); +}; + +static struct platform_driver v2d_driver = { + .probe = v2d_probe, + .remove = v2d_remove, + .driver = { + .owner = THIS_MODULE, + .name = "v2d", + .of_match_table = amlogic_v2d_dt_match, + } +}; + +int __init v2d_module_init(void) +{ + pr_err("v2d_module_init_1\n"); + + if (platform_driver_register(&v2d_driver)) { + pr_err("failed to register video_composer module\n"); + return -ENODEV; + } + return 0; +} + +void __exit v2d_module_exit(void) +{ + platform_driver_unregister(&v2d_driver); +} + diff --git a/drivers/media/video_processor/v2d/v2d.h b/drivers/media/video_processor/v2d/v2d.h new file mode 100644 index 000000000..fcd2aaed8 --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef V2D_H +#define V2D_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "v2d_ge2d_composer.h" +#include "v2d_dewarp_composer.h" +#include "v2d_vicp_composer.h" + +#define MAX_LAYER_COUNT 9 +#define V2D_POOL_SIZE 16 +#define BUFFER_LEN 4 +#define V2D_SET_ENABLE_MODE 1 +#define V2D_SET_DISABLE_MODE 0 + +#define PRINT_ERROR 0X0 +#define PRINT_QUEUE_STATUS 0X01 +#define PRINT_FENCE 0X02 +#define PRINT_PERFORMANCE 0X04 +#define PRINT_AXIS 0X08 +#define PRINT_INDEX_DISP 0X10 +#define PRINT_PATTERN 0X20 +#define PRINT_OTHER 0X040 +#define PRINT_DEWARP 0X0100 +#define PRINT_VICP 0x0200 + +#define WAIT_READY_Q_TIMEOUT 100 +#define WAIT_DISPLAY_Q_TIMEOUT 100 +#define MAX_RECEIVE_WAIT_TIME 15 /*15ms*/ + +#define V2D_IOC_MAGIC 'R' +#define V2D_IOCTL_SET_FRAMES _IOW(V2D_IOC_MAGIC, 0x00, struct frames_info_t) +#define V2D_IOCTL_SET_ENABLE _IOW(V2D_IOC_MAGIC, 0x01, int) +#define V2D_IOCTL_SET_FENCE _IOW(V2D_IOC_MAGIC, 0x02, struct release_info_t) + +struct frame_info_t { + s32 fd; + s32 fence_fd; + s32 buffer_w; + s32 buffer_h; + s32 align_w; + s32 align_h; + s32 dst_x; + s32 dst_y; + s32 dst_w; + s32 dst_h; + s32 crop_x; + s32 crop_y; + s32 crop_w; + s32 crop_h; + s32 zorder; + s32 buffer_format; + s32 transform; + s32 alpha; + s32 reserved[10]; +}; + +struct frames_info_t { + s32 frame_count; + struct frame_info_t input_buffer[MAX_LAYER_COUNT]; + struct frame_info_t out_buffer; + s32 reserved[4]; +}; + +struct release_info_t { + s32 release_fd; + s32 release_fence_fd; +}; + +enum v2d_buffer_status { + UNINITIAL = 0, + INIT_SIZE_SUCCESS, + INIT_BUFFER_SUCCESS, + INIT_BUFFER_ERROR, +}; + +struct input_crop_s { + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct input_axis_s { + int left; + int top; + int width; + int height; +}; + +struct output_axis { + u32 min_left; + u32 min_top; + u32 max_right; + u32 max_bottom; +}; + +enum v2d_transform_t { + /* flip source image horizontally */ + V2D_TRANSFORM_FLIP_H = 1, + /* flip source image vertically */ + V2D_TRANSFORM_FLIP_V = 2, + /* rotate source image 90 degrees clock-wise */ + V2D_TRANSFORM_ROT_90 = 4, + /* rotate source image 180 degrees */ + V2D_TRANSFORM_ROT_180 = 3, + /* rotate source image 270 degrees clock-wise */ + V2D_TRANSFORM_ROT_270 = 7, + /* flip source image horizontally, the rotate 90 degrees clock-wise */ + V2D_TRANSFORM_FLIP_H_ROT_90 = V2D_TRANSFORM_FLIP_H | V2D_TRANSFORM_ROT_90, + /* flip source image vertically, the rotate 90 degrees clock-wise */ + V2D_TRANSFORM_FLIP_V_ROT_90 = V2D_TRANSFORM_FLIP_V | V2D_TRANSFORM_ROT_90, +}; + +enum buffer_format_t { + NV21 = 0, + YUV444 = 1, +}; + +enum output_buf_mode_t { + BUFFER_MODE_UNINIT = 0, + BUFFER_MODE_GE2D, + BUFFER_MODE_DEWARP, + BUFFER_MODE_VICP, +}; + +struct received_frames_t { + int index; + atomic_t on_use; + struct input_crop_s crop_info[MAX_LAYER_COUNT]; + struct input_axis_s axis_info[MAX_LAYER_COUNT]; + struct output_axis output_axis; + struct frames_info_t frames_info; + struct file *input_file[MAX_LAYER_COUNT]; + struct file *input_fence[MAX_LAYER_COUNT]; + unsigned long phy_addr[MAX_LAYER_COUNT]; + bool is_tvp; + size_t usage; +}; + +struct dst_buf_t { + int index; + struct composer_info_t composer_info; + enum output_buf_mode_t buf_used; + bool dirty; + ulong phy_addr; + u32 buf_w; + u32 buf_h; + u32 buf_size; + bool is_tvp; + u32 dw_size; + ulong afbc_head_addr; + u32 afbc_head_size; + ulong afbc_body_addr; + u32 afbc_body_size; + ulong afbc_table_addr; + ulong afbc_table_handle; + u32 afbc_table_size; +}; + +struct v2d_fence_cb_t { + struct dma_fence_cb base_cb; + struct v2d_dev *dev; + struct file *buffer_file; + struct file *fence_file; +}; + +struct display_data_t { + struct dma_buf *output_dmabuf; + struct dst_buf_t *output_buffer; + atomic_t on_use; +}; + +enum composer_dev { + COMPOSER_WITH_DEFAULT = 0, + COMPOSER_WITH_GE2D, + COMPOSER_WITH_DEWARP, + COMPOSER_WITH_VICP, + COMPOSER_WITH_UNINITIAL, +}; + +enum v2d_work_mode { + V2D_MODE_UNINITIAL = 0, + V2D_MODE_ROTATE, + V2D_MODE_COMPOSER, +}; + +union composer_device_config { + struct ge2d_src_para_s ge2d_data; + struct dewarp_vf_para_s dewarp_data; + struct vicp_data_config_s vicp_data; +}; + +struct v2d_dev_config { + union composer_device_config composer_device_param; + unsigned long addr; + struct vframe_s *input_vf; + struct dst_buf_t *dst_buf; + struct input_axis_s frame_axis; + struct input_crop_s frame_crop; + int count; + int index; + int zorder; + bool is_tvp; +}; + +struct v2d_dev { + u32 index; + struct v2d_port_s *port; + enum composer_dev dev_choice; + enum v2d_work_mode work_mode; + bool status_enabled; + DECLARE_KFIFO(receive_q, struct received_frames_t *, V2D_POOL_SIZE); + DECLARE_KFIFO(free_q, struct dst_buf_t *, BUFFER_LEN); + DECLARE_KFIFO(file_free_q, struct dma_buf *, V2D_POOL_SIZE); + DECLARE_KFIFO(file_wait_q, struct dma_buf *, V2D_POOL_SIZE); + DECLARE_KFIFO(display_q, struct display_data_t *, BUFFER_LEN); + DECLARE_KFIFO(fence_wait_q, struct file *, V2D_POOL_SIZE); + DECLARE_KFIFO(fence_cb_q, struct v2d_fence_cb_t *, V2D_POOL_SIZE); + struct received_frames_t received_frames[V2D_POOL_SIZE]; + struct dst_buf_t dst_buf[BUFFER_LEN]; + struct v2d_fence_cb_t v2d_fence_cb[V2D_POOL_SIZE]; + struct display_data_t display_data[V2D_POOL_SIZE]; + struct task_struct *file_thread; + wait_queue_head_t file_wq; + struct completion file_task_done; + struct dma_buf *out_dmabuf[V2D_POOL_SIZE]; + void *v2d_timeline; + u32 cur_streamline_val; + u32 buf_width; + u32 buf_height; + int (*config_param_func)(struct v2d_dev *dev, + struct frame_info_t *vframe_info_cur, struct v2d_dev_config *v2d_composer_param); + int (*process_data_func)(struct v2d_dev *dev, struct v2d_dev_config *v2d_composer_param); + enum v2d_buffer_status buffer_status; + struct ge2d_composer_para ge2d_para; + struct dewarp_composer_para dewarp_para; + u32 vinfo_w; + u32 vinfo_h; + u32 output_duration; + u32 file_thread_stopped; + bool thread_need_stop; + bool need_do_file; + u32 fget_count; + u32 fput_count; + u32 received_count; + u32 received_new_count; + u32 fence_creat_count; + u32 fence_signal_count; + u32 buffer_release_count; + u32 fence_wait_count; + u32 fence_wait_time_total; +}; + +struct v2d_port_s { + const char *name; + u32 index; + u32 open_count; + struct device *class_dev; + struct device *pdev; +}; + +#endif diff --git a/drivers/media/video_processor/v2d/v2d_dewarp_composer.c b/drivers/media/video_processor/v2d/v2d_dewarp_composer.c new file mode 100644 index 000000000..cf16dd004 --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_dewarp_composer.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v2d_dewarp_composer.h" + +#define GDC_FIRMWARE_PATH "/vendor/lib/firmware/gdc/" + +static unsigned int dewarp_com_dump_last; +int v2d_get_dewarp_format(int index, struct vframe_s *vf) +{ + int format = NV12; + + if (IS_ERR_OR_NULL(vf)) { + pr_info("v2d:[%d] %s: vf is NULL.\n", index, __func__); + return -1; + } + + if ((vf->type & VIDTYPE_VIU_NV21) || + (vf->type & VIDTYPE_VIU_NV12)) + format = NV12; + else if (vf->type & VIDTYPE_VIU_422) + format = 0; + else if (vf->type & VIDTYPE_VIU_444) + format = YUV444_P; + else if (vf->type & VIDTYPE_RGB_444) + format = RGB444_P; + + return format; +} + +static int get_dewarp_rotation_value(int transform) +{ + int rotate_value = 0; + + if (transform == 4 || transform == 5 || transform == 6) + rotate_value = 270; + else if (transform == 3) + rotate_value = 180; + else if (transform == 7) + rotate_value = 90; + else + rotate_value = 0; + + return rotate_value; +} + +static int dump_dewarp_vframe(char *path, int width, int height, u32 phy_adr_y, u32 phy_adr_uv) +{ +#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA + int size = 0; + struct file *fp = NULL; + loff_t position = 0; + u8 *data; + + if (IS_ERR_OR_NULL(path)) { + pr_info("%s: path is NULL.\n", __func__); + return -1; + } + + /* open file to write */ + fp = filp_open(path, O_WRONLY | O_CREAT, 0644); + if (IS_ERR(fp)) { + pr_info("%s: open file error\n", __func__); + return -1; + } + + /* Write buf to file */ + size = width * height; + data = codec_mm_vmap(phy_adr_y, size); + if (!data) { + pr_info("%s: vmap failed\n", __func__); + return -1; + } + /* change to KERNEL_DS address limit */ + kernel_write(fp, data, size, &position); + codec_mm_unmap_phyaddr(data); + + size = width * height / 2; + data = codec_mm_vmap(phy_adr_uv, size); + if (!data) { + pr_info("%s: vmap failed\n", __func__); + return -1; + } + /* change to KERNEL_DS address limit */ + kernel_write(fp, data, size, &position); + codec_mm_unmap_phyaddr(data); + + filp_close(fp, NULL); + return 0; +#else + return -1; +#endif +} + +int v2d_load_dewarp_firmware(struct dewarp_composer_para *param) +{ +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + int ret = 0; +#endif + char file_name[64]; + struct firmware_rotate_s fw_param; + bool is_need_load = false; + int frame_rotation = 0; + + if (IS_ERR_OR_NULL(param)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + frame_rotation = get_dewarp_rotation_value(param->vf_para->src_vf_angle); + if (param->last_fw_param.in_width != param->vf_para->src_vf_width || + param->last_fw_param.in_height != param->vf_para->src_vf_height || + param->last_fw_param.out_width != param->vf_para->dst_vf_width || + param->last_fw_param.out_height != param->vf_para->dst_vf_height || + param->last_fw_param.degree != frame_rotation) { + param->last_fw_param.format = NV12; + param->last_fw_param.in_width = param->vf_para->src_vf_width; + param->last_fw_param.in_height = param->vf_para->src_vf_height; + param->last_fw_param.out_width = param->vf_para->dst_vf_width; + param->last_fw_param.out_height = param->vf_para->dst_vf_height; + param->last_fw_param.degree = frame_rotation; + is_need_load = true; + } + + if (dewarp_print) { + pr_info("v2d:[%d] need load firmware: %d.\n", param->index, is_need_load); + pr_info("v2d:[%d] src_vf, w:%d, h:%d, fromat:%d, rotation:%d.\n", + param->index, + param->vf_para->src_vf_width, + param->vf_para->src_vf_height, + param->vf_para->src_vf_format, + param->vf_para->src_vf_angle); + pr_info("v2d:[%d] src_buf, w0:%d, w1:%d.\n", param->index, + param->vf_para->src_buf_stride0, param->vf_para->src_buf_stride1); + pr_info("v2d:[%d] dst_vf, w:%d, h:%d.\n", param->index, + param->vf_para->dst_vf_width, param->vf_para->dst_vf_height); + pr_info("v2d:[%d] dst_buf, w:%d.\n", param->index, + param->vf_para->dst_buf_stride); + } + + if (param->fw_load.phys_addr == 0 || is_need_load) { + v2d_unload_dewarp_firmware(param); + pr_info("v2d:[%d] start load firmware.\n", param->index); + if (dewarp_load_flag) { + memset(file_name, 0, 64); + sprintf(file_name, "%dx%d-%dx%d-%d_nv12.bin", + param->vf_para->src_vf_width, + param->vf_para->src_vf_height, + param->vf_para->dst_vf_width, + param->vf_para->dst_vf_height, + frame_rotation); +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + ret = load_firmware_by_name(file_name, ¶m->fw_load); + if (ret <= 0) { + pr_info("v2d:[%d] %s: load firmware failed.\n", + param->index, __func__); + return -1; + } +#else + return -1; +#endif + } else { + fw_param.format = NV12; + fw_param.in_width = param->vf_para->src_vf_width; + fw_param.in_height = param->vf_para->src_vf_height; + fw_param.out_width = param->vf_para->dst_vf_width; + fw_param.out_height = param->vf_para->dst_vf_height; + fw_param.degree = frame_rotation; +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + ret = rotation_calc_and_load_firmware(&fw_param, ¶m->fw_load); + if (ret <= 0) { + pr_info("v2d:[%d] %s: calc and load firmware failed.\n", + param->index, __func__); + return -1; + } +#else + return -1; +#endif + } + } + return 0; +} + +int v2d_unload_dewarp_firmware(struct dewarp_composer_para *param) +{ + if (IS_ERR_OR_NULL(param)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + if (param->fw_load.phys_addr != 0) { +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + release_config_firmware(¶m->fw_load); +#endif + param->fw_load.phys_addr = 0; + } + + return 0; +} + +bool check_dewarp_status(struct vframe_s *input_vf, int count, int work_mode, int index) +{ + int src_formate; + +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + if (!is_aml_gdc_supported()) + return false; +#endif + + if (count > 1) { + pr_info("%s: dewarp not support composer.\n", __func__); + return false; + } + + if (work_mode != 1) + return false; + + if (!input_vf) + src_formate = NV12; + else + src_formate = v2d_get_dewarp_format(index, input_vf); + if (src_formate != NV12) + return false; + + return true; +} + +int v2d_init_dewarp_composer(struct dewarp_composer_para *param) +{ + if (IS_ERR_OR_NULL(param)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + if (IS_ERR_OR_NULL(param->context)) { +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + param->context = create_gdc_work_queue(AML_GDC); +#else + param->context = NULL; +#endif + if (IS_ERR_OR_NULL(param->context)) { + pr_info("v2d:[%d] %s: create dewrap work_queue failed.\n", + param->index, + __func__); + return -1; + } + } else { + pr_info("v2d:[%d] %s: dewrap work queue exist.\n", + param->index, __func__); + } + + return 0; +} + +int v2d_uninit_dewarp_composer(struct dewarp_composer_para *param) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(param)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + v2d_unload_dewarp_firmware(param); + + if (!IS_ERR_OR_NULL(param->context)) { +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + ret = destroy_gdc_work_queue(param->context); +#else + ret = -1; +#endif + if (ret != 0) { + pr_info("v2d:[%d] %s: destroy dewarp work queue failed.\n", + param->index, + __func__); + return -1; + } + pr_info("v2d:[%d] %s: destroy dewarp work queue success.\n", + param->index, __func__); + } else { + pr_info("v2d:[%d] %s: dewarp work queue not create.\n", + param->index, + __func__); + } + + param->context = NULL; + return 0; +} + +int v2d_config_dewarp_vframe(struct dewarp_vf_para_s *vframe_para, + struct dewarp_common_para *common_para) +{ + struct vframe_s *vf = NULL; + struct vframe_s *src_vf = common_para->input_para.vframe; + int call_index = common_para->input_para.call_index; + struct pic_s *pic_info_in = &common_para->input_para.pic_info; + struct pic_s *pic_info_out = &common_para->output_para.pic_info; + + if (IS_ERR_OR_NULL(vframe_para)) { + pr_info("v2d:[%d] %s: vframe_para is null.\n", call_index, __func__); + return -1; + } + + if (src_vf) { + if (src_vf->canvas0_config[0].phy_addr == 0) { + if ((src_vf->flag & VFRAME_FLAG_DOUBLE_FRAM) && + src_vf->vf_ext) { + vf = src_vf->vf_ext; + } else { + pr_info("v2d:[%d] %s: vf no yuv data.\n", call_index, __func__); + return -1; + } + } else { + vf = src_vf; + } + + vframe_para->src_vf_width = vf->width; + vframe_para->src_vf_height = vf->height; + vframe_para->src_vf_format = v2d_get_dewarp_format(call_index, vf); + vframe_para->src_vf_plane_count = 2; + vframe_para->src_buf_addr0 = vf->canvas0_config[0].phy_addr; + vframe_para->src_buf_stride0 = vf->canvas0_config[0].width; + vframe_para->src_buf_addr1 = vf->canvas0_config[1].phy_addr; + vframe_para->src_buf_stride1 = vf->canvas0_config[1].width; + vframe_para->src_vf_angle = common_para->input_para.transform; + vframe_para->src_endian = vf->canvas0_config[0].endian; + if (vf->type & VIDTYPE_VIU_NV12) + vframe_para->uvswap_enable = 1; + + vframe_para->dst_vf_width = pic_info_out->align_w; + vframe_para->dst_vf_height = pic_info_out->align_h; + vframe_para->dst_vf_plane_count = 2; + vframe_para->dst_buf_addr = pic_info_out->addr[0]; + vframe_para->dst_buf_stride = pic_info_out->align_w; + vframe_para->dst_endian = 0; + vframe_para->is_tvp = pic_info_in->is_tvp; + } else { + if (pic_info_in->format == 1) { + vframe_para->src_vf_format = YUV444_P; + vframe_para->src_buf_stride0 = pic_info_in->align_w * 3; + } else { + vframe_para->src_vf_format = NV12; + vframe_para->src_buf_stride0 = pic_info_in->align_w; + } + + vframe_para->src_vf_width = pic_info_in->width; + vframe_para->src_vf_height = pic_info_in->height; + vframe_para->src_vf_plane_count = 2; + vframe_para->src_buf_addr0 = pic_info_in->addr[0]; + vframe_para->src_buf_addr1 = pic_info_in->addr[0] + + vframe_para->src_buf_stride0 * pic_info_in->align_h; + vframe_para->src_buf_stride1 = vframe_para->src_buf_stride0; + vframe_para->src_vf_angle = common_para->input_para.transform; + vframe_para->src_endian = 0; + + vframe_para->dst_vf_width = pic_info_out->align_w; + vframe_para->dst_vf_height = pic_info_out->align_h; + vframe_para->dst_vf_plane_count = 2; + vframe_para->dst_buf_addr = pic_info_out->addr[0]; + vframe_para->dst_buf_stride = pic_info_out->align_w; + vframe_para->dst_endian = 0; + vframe_para->is_tvp = pic_info_in->is_tvp; + } + return 0; +} + +int v2d_dewarp_data_composer(struct dewarp_composer_para *param, bool is_tvp) +{ + int ret; + struct gdc_phy_setting gdc_config; + char dump_name[32]; + + if (IS_ERR_OR_NULL(param)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + memset(&gdc_config, 0, sizeof(struct gdc_phy_setting)); + gdc_config.format = param->vf_para->src_vf_format; + gdc_config.in_width = param->vf_para->src_vf_width; + gdc_config.in_height = param->vf_para->src_vf_height; + /*16-byte alignment*/ + gdc_config.in_y_stride = AXI_WORD_ALIGN(param->vf_para->src_buf_stride0); + /*16-byte alignment*/ + gdc_config.in_c_stride = AXI_WORD_ALIGN(param->vf_para->src_buf_stride1); + gdc_config.in_plane_num = param->vf_para->src_vf_plane_count; + gdc_config.out_width = param->vf_para->dst_vf_width; + gdc_config.out_height = param->vf_para->dst_vf_height; + /*16-byte alignment*/ + gdc_config.out_y_stride = AXI_WORD_ALIGN(param->vf_para->dst_buf_stride); + /*16-byte alignment*/ + gdc_config.out_c_stride = AXI_WORD_ALIGN(param->vf_para->dst_buf_stride); + gdc_config.out_plane_num = param->vf_para->dst_vf_plane_count; + gdc_config.in_paddr[0] = param->vf_para->src_buf_addr0; + gdc_config.in_paddr[1] = param->vf_para->src_buf_addr1; + gdc_config.out_paddr[0] = param->vf_para->dst_buf_addr; + gdc_config.out_paddr[1] = param->vf_para->dst_buf_addr + + AXI_WORD_ALIGN(gdc_config.out_width) + * AXI_WORD_ALIGN(gdc_config.out_height); + gdc_config.config_paddr = param->fw_load.phys_addr; + gdc_config.config_size = param->fw_load.size_32bit; /* in 32bit */ + gdc_config.uvswap_enable = param->vf_para->uvswap_enable; + if (param->vf_para->is_tvp) + gdc_config.use_sec_mem = 1; /* secure mem access */ + else + gdc_config.use_sec_mem = 0; + + if (param->vf_para->src_endian != 0) + gdc_config.in_endian = GDC_ENDIAN_BIG_8BYTES; + else + gdc_config.in_endian = GDC_ENDIAN_LITTLE; + + if (param->vf_para->dst_endian != 0) + gdc_config.out_endian = GDC_ENDIAN_BIG_8BYTES; + else + gdc_config.out_endian = GDC_ENDIAN_LITTLE; +#ifdef CONFIG_AMLOGIC_MEDIA_GDC + ret = gdc_process_phys(param->context, &gdc_config); +#else + ret = -1; +#endif + if (ret < 0) { + pr_info("v2d:[%d] %s: dewrap process failed.\n", param->index, __func__); + } else { + if (dewarp_com_dump != dewarp_com_dump_last) { + sprintf(dump_name, "/data/src_%d.yuv", dewarp_com_dump); + dump_dewarp_vframe(dump_name, + param->vf_para->src_vf_width, + param->vf_para->src_vf_height, + param->vf_para->src_buf_addr0, + param->vf_para->src_buf_addr1); + + sprintf(dump_name, "/data/dst_%d.yuv", dewarp_com_dump); + dump_dewarp_vframe(dump_name, + param->vf_para->dst_vf_width, + param->vf_para->dst_vf_height, + gdc_config.out_paddr[0], + gdc_config.out_paddr[1]); + dewarp_com_dump_last = dewarp_com_dump; + } + } + return ret; +} diff --git a/drivers/media/video_processor/v2d/v2d_dewarp_composer.h b/drivers/media/video_processor/v2d/v2d_dewarp_composer.h new file mode 100644 index 000000000..0457855bb --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_dewarp_composer.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef VFRAME_DEWARP_COMPOSER_H +#define VFRAME_DEWARP_COMPOSER_H +#include +#include +#include +#include +#include + +struct dewarp_vf_para_s { + int src_vf_format; + int src_vf_width; + int src_vf_height; + int src_vf_plane_count; + int src_vf_angle; + ulong src_buf_addr0; + int src_buf_stride0; + ulong src_buf_addr1; + int src_buf_stride1; + int src_endian; + int dst_vf_format; + int dst_vf_width; + int dst_vf_height; + int dst_vf_plane_count; + ulong dst_buf_addr; + int dst_buf_stride; + int dst_endian; + bool is_tvp; + bool uvswap_enable; +}; + +struct pic_s { + int format; + u32 width; + u32 height; + ulong addr[2]; + u32 align_w; + u32 align_h; + int plane_count; + bool is_tvp; +}; + +struct composer_input_para { + int call_index; + struct vframe_s *vframe; + struct pic_s pic_info; + int transform; +}; + +struct composer_output_para { + struct pic_s pic_info; +}; + +struct dewarp_common_para { + struct composer_input_para input_para; + struct composer_output_para output_para; +}; + +struct dewarp_composer_para { + int index; + struct gdc_context_s *context; + struct firmware_load_s fw_load; + struct dewarp_vf_para_s *vf_para; + struct firmware_rotate_s last_fw_param; +}; + +extern u32 dewarp_load_flag; +extern int dewarp_com_dump; +extern int dewarp_print; + +int v2d_get_dewarp_format(int index, struct vframe_s *vf); +int v2d_load_dewarp_firmware(struct dewarp_composer_para *param); +int v2d_unload_dewarp_firmware(struct dewarp_composer_para *param); +bool check_dewarp_status(struct vframe_s *input_vf, int count, int work_mode, int index); +int v2d_init_dewarp_composer(struct dewarp_composer_para *param); +int v2d_uninit_dewarp_composer(struct dewarp_composer_para *param); +int v2d_config_dewarp_vframe(struct dewarp_vf_para_s *vframe_para, + struct dewarp_common_para *common_para); +int v2d_dewarp_data_composer(struct dewarp_composer_para *param, bool is_tvp); + +#endif diff --git a/drivers/media/video_processor/v2d/v2d_ge2d_composer.c b/drivers/media/video_processor/v2d/v2d_ge2d_composer.c new file mode 100644 index 000000000..f18c2a182 --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_ge2d_composer.c @@ -0,0 +1,949 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +/* media module used media/registers/cpu_version.h since kernel 5.4 */ +#include +#include +#include +#include +#include +#include +#include "v2d_ge2d_composer.h" + +#define WIDTH_8K 7680 +#define HEIGHT_8K 7680 +#define IS_DI_PSTLINK(di_flag) ((di_flag) & DI_FLAG_DI_PSTVPPLINK) + +#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA +static int dump_src_count; +static int dump_before_dst_count; +static int dump_dst_count; +static int dump_black_count; +#endif + +#ifndef CONFIG_AMLOGIC_MEDIA_GE2D +void stretchblt_noalpha(struct ge2d_context_s *wq, + int src_x, int src_y, int src_w, int src_h, + int dst_x, int dst_y, int dst_w, int dst_h) +{ +} + +int destroy_ge2d_work_queue(struct ge2d_context_s *ge2d_work_queue) +{ + return 0; +} + +void fillrect(struct ge2d_context_s *wq, + int x, int y, int w, int h, unsigned int color) +{ +} +#endif + +#ifndef CONFIG_AMLOGIC_UVM_CORE +struct vframe_s *dmabuf_get_vframe(struct dma_buf *dmabuf) +{ + return NULL; +} +#endif + +static int get_source_type(struct ge2d_src_para_s *src_data) +{ + enum videocom_source_type ret; + int interlace_mode; + + interlace_mode = src_data->type & VIDTYPE_TYPEMASK; + if (src_data->source_type == VFRAME_SOURCE_TYPE_HDMI || + src_data->source_type == VFRAME_SOURCE_TYPE_CVBS) { + if ((src_data->bitdepth & BITDEPTH_Y10) && + (!(src_data->type & VIDTYPE_COMPRESS)) && + (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) + ret = VDIN_10BIT_NORMAL; + else + ret = VDIN_8BIT_NORMAL; + } else { + if ((src_data->bitdepth & BITDEPTH_Y10) && + (!(src_data->type & VIDTYPE_COMPRESS)) && + (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) { + if (interlace_mode == VIDTYPE_INTERLACE_TOP) + ret = DECODER_10BIT_TOP; + else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM) + ret = DECODER_10BIT_BOTTOM; + else + ret = DECODER_10BIT_NORMAL; + } else { + if (interlace_mode == VIDTYPE_INTERLACE_TOP) + ret = DECODER_8BIT_TOP; + else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM) + ret = DECODER_8BIT_BOTTOM; + else + ret = DECODER_8BIT_NORMAL; + } + } + return ret; +} + +static int get_input_format(struct ge2d_src_para_s *src_data) +{ + int format = GE2D_FORMAT_M24_YUV420; + enum videocom_source_type source_type; + + source_type = get_source_type(src_data); + switch (source_type) { + case DECODER_8BIT_NORMAL: + if (src_data->type & VIDTYPE_VIU_422) + format = GE2D_FORMAT_S16_YUV422; + else if (src_data->type & VIDTYPE_VIU_NV21) + format = GE2D_FORMAT_M24_NV21; + else if (src_data->type & VIDTYPE_VIU_NV12) + format = GE2D_FORMAT_M24_NV12; + else if (src_data->type & VIDTYPE_VIU_444) + format = GE2D_FORMAT_S24_YUV444; + else + format = GE2D_FORMAT_M24_YUV420; + break; + case DECODER_8BIT_BOTTOM: + if (src_data->type & VIDTYPE_VIU_422) + format = GE2D_FORMAT_S16_YUV422 + | (GE2D_FORMAT_S16_YUV422B & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_NV21) + format = GE2D_FORMAT_M24_NV21 + | (GE2D_FORMAT_M24_NV21B & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_NV12) + format = GE2D_FORMAT_M24_NV12 + | (GE2D_FORMAT_M24_NV12B & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_444) + format = GE2D_FORMAT_S24_YUV444 + | (GE2D_FORMAT_S24_YUV444B & (3 << 3)); + else + format = GE2D_FORMAT_M24_YUV420 + | (GE2D_FMT_M24_YUV420B & (3 << 3)); + break; + case DECODER_8BIT_TOP: + if (src_data->type & VIDTYPE_VIU_422) + format = GE2D_FORMAT_S16_YUV422 + | (GE2D_FORMAT_S16_YUV422T & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_NV21) + format = GE2D_FORMAT_M24_NV21 + | (GE2D_FORMAT_M24_NV21T & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_NV12) + format = GE2D_FORMAT_M24_NV12 + | (GE2D_FORMAT_M24_NV12T & (3 << 3)); + else if (src_data->type & VIDTYPE_VIU_444) + format = GE2D_FORMAT_S24_YUV444 + | (GE2D_FORMAT_S24_YUV444T & (3 << 3)); + else + format = GE2D_FORMAT_M24_YUV420 + | (GE2D_FMT_M24_YUV420T & (3 << 3)); + break; + case DECODER_10BIT_NORMAL: + if (src_data->type & VIDTYPE_VIU_422) { + if (src_data->bitdepth & FULL_PACK_422_MODE) + format = GE2D_FORMAT_S16_10BIT_YUV422; + else + format = GE2D_FORMAT_S16_12BIT_YUV422; + } + break; + case DECODER_10BIT_BOTTOM: + if (src_data->type & VIDTYPE_VIU_422) { + if (src_data->bitdepth & FULL_PACK_422_MODE) + format = GE2D_FORMAT_S16_10BIT_YUV422 + | (GE2D_FORMAT_S16_10BIT_YUV422B + & (3 << 3)); + else + format = GE2D_FORMAT_S16_12BIT_YUV422 + | (GE2D_FORMAT_S16_12BIT_YUV422B + & (3 << 3)); + } + break; + case DECODER_10BIT_TOP: + if (src_data->type & VIDTYPE_VIU_422) { + if (src_data->bitdepth & FULL_PACK_422_MODE) + format = GE2D_FORMAT_S16_10BIT_YUV422 + | (GE2D_FORMAT_S16_10BIT_YUV422T + & (3 << 3)); + else + format = GE2D_FORMAT_S16_12BIT_YUV422 + | (GE2D_FORMAT_S16_12BIT_YUV422T + & (3 << 3)); + } + break; + case VDIN_8BIT_NORMAL: + if (src_data->type & VIDTYPE_VIU_422) { + format = GE2D_FORMAT_S16_YUV422; + } else if (src_data->type & VIDTYPE_VIU_NV21) { + format = GE2D_FORMAT_M24_NV21; + } else if (src_data->type & VIDTYPE_VIU_NV12) { + format = GE2D_FORMAT_M24_NV12; + } else if (src_data->type & VIDTYPE_VIU_444) { + format = GE2D_FORMAT_S24_YUV444; + } else if (src_data->type & VIDTYPE_RGB_444) { + format = GE2D_FORMAT_S24_RGB; + if (src_data->is_vframe) + format &= (~GE2D_LITTLE_ENDIAN); + } else { + format = GE2D_FORMAT_M24_YUV420; + } + break; + case VDIN_10BIT_NORMAL: + if (src_data->type & VIDTYPE_VIU_422) { + if (src_data->bitdepth & FULL_PACK_422_MODE) + format = GE2D_FORMAT_S16_10BIT_YUV422; + else + format = GE2D_FORMAT_S16_12BIT_YUV422; + } else if (src_data->type & VIDTYPE_VIU_444) { + format = GE2D_FORMAT_S24_10BIT_YUV444; + } else if (src_data->type & VIDTYPE_RGB_444) { + format = GE2D_FORMAT_S24_10BIT_RGB888; + if (!src_data->is_vframe) + format |= GE2D_LITTLE_ENDIAN; + } + break; + default: + format = GE2D_FORMAT_M24_YUV420; + } + return format; +} + +static enum ge2d_angle_type config_ge2d_rotation(int vf_transform) +{ + int ge2d_angle = 0; + + if (vf_transform == 1) + ge2d_angle = GE2D_ANGLE_TYPE_FLIP_H; + else if (vf_transform == 2) + ge2d_angle = GE2D_ANGLE_TYPE_FLIP_V; + else if (vf_transform == 4 || vf_transform == 5 || vf_transform == 6) + ge2d_angle = GE2D_ANGLE_TYPE_ROT_90; + else if (vf_transform == 3) + ge2d_angle = GE2D_ANGLE_TYPE_ROT_180; + else if (vf_transform == 7) + ge2d_angle = GE2D_ANGLE_TYPE_ROT_270; + else if (vf_transform != 0) + VIDEOCOM_INFO("%s: not support transform=%d\n", __func__, vf_transform); + + return ge2d_angle; +} + +static int alloc_src_canvas(struct ge2d_composer_para *ge2d_comp_para) +{ + const char *keep_owner = "ge2d_composer"; + + if (ge2d_comp_para->canvas_scr[0] < 0) + ge2d_comp_para->canvas_scr[0] = + canvas_pool_map_alloc_canvas(keep_owner); + if (ge2d_comp_para->canvas_scr[1] < 0) + ge2d_comp_para->canvas_scr[1] = + canvas_pool_map_alloc_canvas(keep_owner); + if (ge2d_comp_para->canvas_scr[2] < 0) + ge2d_comp_para->canvas_scr[2] = + canvas_pool_map_alloc_canvas(keep_owner); + + if (ge2d_comp_para->canvas_scr[0] < 0 || + ge2d_comp_para->canvas_scr[1] < 0 || + ge2d_comp_para->canvas_scr[2] < 0) { + VIDEOCOM_INFO("%s failed\n", __func__); + return -1; + } + return 0; +} + +static int free_src_canvas(struct ge2d_composer_para *ge2d_comp_para) +{ + if (ge2d_comp_para->canvas_scr[0] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_scr[0]); + ge2d_comp_para->canvas_scr[0] = -1; + } + + if (ge2d_comp_para->canvas_scr[1] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_scr[1]); + ge2d_comp_para->canvas_scr[1] = -1; + } + + if (ge2d_comp_para->canvas_scr[2] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_scr[2]); + ge2d_comp_para->canvas_scr[2] = -1; + } + if (ge2d_comp_para->canvas_scr[0] >= 0 || + ge2d_comp_para->canvas_scr[1] >= 0 || + ge2d_comp_para->canvas_scr[2] >= 0) { + VIDEOCOM_INFO("%s failed!\n", __func__); + return -1; + } + return 0; +} + +int v2d_init_ge2d_composer(struct ge2d_composer_para *ge2d_comp_para) +{ + const char *keep_owner = "ge2d_dest_comp"; + + ge2d_comp_para->context = create_ge2d_work_queue(); + if (IS_ERR_OR_NULL(ge2d_comp_para->context)) { + VIDEOCOM_INFO("creat ge2d work failed\n"); + return -1; + } + + if (ge2d_comp_para->plane_num > 0) { + if (ge2d_comp_para->canvas_dst[0] < 0) + ge2d_comp_para->canvas_dst[0] = + canvas_pool_map_alloc_canvas(keep_owner); + if (ge2d_comp_para->canvas_dst[0] < 0) { + VIDEOCOM_ERR("ge2d init dst canvas_0 failed!\n"); + return -1; + } + } + + if (ge2d_comp_para->plane_num > 1) { + if (ge2d_comp_para->canvas_dst[1] < 0) + ge2d_comp_para->canvas_dst[1] = + canvas_pool_map_alloc_canvas(keep_owner); + if (ge2d_comp_para->canvas_dst[1] < 0) { + VIDEOCOM_ERR("ge2d init dst canvas_1 failed!\n"); + return -1; + } + } + + if (ge2d_comp_para->plane_num > 2) { + if (ge2d_comp_para->canvas_dst[2] < 0) + ge2d_comp_para->canvas_dst[2] = + canvas_pool_map_alloc_canvas(keep_owner); + if (ge2d_comp_para->canvas_dst[2] < 0) { + VIDEOCOM_ERR("ge2d init dst canvas_2 failed!\n"); + return -1; + } + } + return 0; +} + +int v2d_uninit_ge2d_composer(struct ge2d_composer_para *ge2d_comp_para) +{ + destroy_ge2d_work_queue(ge2d_comp_para->context); + ge2d_comp_para->context = NULL; + + if (free_src_canvas(ge2d_comp_para) < 0) { + VIDEOCOM_ERR("free src canvas failed!\n"); + return -1; + } + + if (ge2d_comp_para->canvas_dst[0] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_dst[0]); + ge2d_comp_para->canvas_dst[0] = -1; + } + + if (ge2d_comp_para->canvas_dst[1] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_dst[1]); + ge2d_comp_para->canvas_dst[1] = -1; + } + + if (ge2d_comp_para->canvas_dst[2] >= 0) { + canvas_pool_map_free_canvas(ge2d_comp_para->canvas_dst[2]); + ge2d_comp_para->canvas_dst[2] = -1; + } + + if (ge2d_comp_para->canvas_dst[0] >= 0 || + ge2d_comp_para->canvas_dst[1] >= 0 || + ge2d_comp_para->canvas_dst[2] >= 0) { + VIDEOCOM_ERR("free dst canvas failed!\n"); + return -1; + } + return 0; +} + +#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA +static int copy_phybuf_to_file(struct canvas_config_s *config, + struct file *fp, loff_t pos) +{ + u32 span = SZ_1M; + u8 *p; + int remain_size = 0; + ssize_t ret; + ulong phys; + + if (IS_ERR_OR_NULL(config)) + return -1; + + phys = config->phy_addr; + remain_size = config->width * config->height; + while (remain_size > 0) { + if (remain_size < span) + span = remain_size; + p = codec_mm_vmap(phys, PAGE_ALIGN(span)); + if (!p) { + VIDEOCOM_ERR("vmap failed\n"); + return -1; + } + codec_mm_dma_flush(p, span, DMA_FROM_DEVICE); + ret = kernel_write(fp, (char *)p, span, &pos); + if (ret <= 0) + VIDEOCOM_ERR("vfs write failed!\n"); + phys += span; + codec_mm_unmap_phyaddr(p); + remain_size -= span; + + VIDEOCOM_INFO("pos: %lld, phys: %lx, remain_size: %d\n", + pos, phys, remain_size); + } + return 0; +} +#endif +static bool dump_data(struct dump_param *para, enum buffer_data type) +{ +#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA + bool ret = false; + struct file *filp_dst = NULL; + char dst_path[64] = {'\0'}; + struct canvas_config_s *dump_config; + int result = 0; + int offset = 0; + + offset = 0; + if (type == BLACK_BUFFER) + sprintf(dst_path, "/data/temp/black_%d", dump_black_count++); + else if (type == SCR_BUFFER) + sprintf(dst_path, "/data/temp/scr_%d", dump_src_count++); + else if (type == DST_EMPTY_BUFFER) + sprintf(dst_path, + "/data/temp/before_dst_%d", + dump_before_dst_count++); + else if (type == DST_BUFFER_DATA) + sprintf(dst_path, "/data/temp/dst_%d", dump_dst_count++); + filp_dst = filp_open(dst_path, O_RDWR | O_CREAT, 0666); + if (IS_ERR(filp_dst)) { + VIDEOCOM_ERR("open %s failed\n", dst_path); + ret = false; + } else { + dump_config = ¶->canvas0_config[0]; + result = copy_phybuf_to_file(dump_config, filp_dst, 0); + if (result < 0) { + VIDEOCOM_ERR("write %s failed\n", dst_path); + ret = false; + goto end; + } + if (para->plane_num >= 2) { + offset = dump_config->width * + dump_config->height; + dump_config = ¶->canvas0_config[1]; + result = + copy_phybuf_to_file(dump_config, + filp_dst, offset); + if (result < 0) { + VIDEOCOM_ERR("#1 write %s failed\n", + dst_path); + ret = false; + goto end; + } + } + if (para->plane_num >= 3) { + offset += dump_config->width * + dump_config->height; + dump_config = ¶->canvas0_config[2]; + result = + copy_phybuf_to_file(dump_config, + filp_dst, offset); + if (result < 0) { + VIDEOCOM_ERR("#2 write %s failed\n", + dst_path); + ret = false; + goto end; + } + } + filp_close(filp_dst, NULL); + ret = true; + } +end: + return ret; +#else + return false; +#endif +} + +int v2d_fill_vframe_black(struct ge2d_composer_para *ge2d_comp_para) +{ + struct canvas_config_s dst_canvas0_config[3]; + struct config_para_ex_s ge2d_config; + u32 dst_plane_num; + struct dump_param para; + bool ret = false; + int temp = 0; + int canvas_index = 0; + + memset(dst_canvas0_config, 0, sizeof(dst_canvas0_config)); + memset(&ge2d_config, 0, sizeof(struct config_para_ex_s)); + + if (ge2d_comp_para->format == GE2D_FORMAT_S24_YUV444) { + dst_canvas0_config[0].phy_addr = ge2d_comp_para->phy_addr[0]; + dst_canvas0_config[0].width = ge2d_comp_para->buffer_w * 3; + dst_canvas0_config[0].height = ge2d_comp_para->buffer_h; + dst_canvas0_config[0].block_mode = 0; + dst_canvas0_config[0].endian = 0; + } else if (ge2d_comp_para->format == GE2D_FORMAT_M24_NV21) { + dst_canvas0_config[0].phy_addr = ge2d_comp_para->phy_addr[0]; + dst_canvas0_config[0].width = ge2d_comp_para->buffer_w; + dst_canvas0_config[0].height = ge2d_comp_para->buffer_h; + dst_canvas0_config[0].block_mode = 0; + dst_canvas0_config[0].endian = 0; + dst_canvas0_config[1].phy_addr = ge2d_comp_para->phy_addr[0] + + ge2d_comp_para->buffer_w * ge2d_comp_para->buffer_h; + dst_canvas0_config[1].width = ge2d_comp_para->buffer_w; + dst_canvas0_config[1].height = ge2d_comp_para->buffer_h >> 1; + dst_canvas0_config[1].block_mode = 0; + dst_canvas0_config[1].endian = 0; + } + dst_plane_num = ge2d_comp_para->plane_num; + + if (ge2d_comp_para->canvas0_addr == (u32)-1) { + canvas_config_config(ge2d_comp_para->canvas_dst[0], + &dst_canvas0_config[0]); + if (dst_plane_num == 2) { + canvas_config_config(ge2d_comp_para->canvas_dst[1], + &dst_canvas0_config[1]); + } else if (dst_plane_num == 3) { + canvas_config_config(ge2d_comp_para->canvas_dst[2], + &dst_canvas0_config[2]); + } + canvas_index = ge2d_comp_para->canvas_dst[0] + | (ge2d_comp_para->canvas_dst[1] << 8) + | (ge2d_comp_para->canvas_dst[2] << 16); + + } else { + canvas_index = ge2d_comp_para->canvas0_addr; + } + + ge2d_config.src_para.canvas_index = canvas_index; + ge2d_config.alu_const_color = 0;/*0x000000ff;*/ + ge2d_config.bitmask_en = 0; + ge2d_config.src1_gb_alpha = 0;/*0xff;*/ + ge2d_config.dst_xy_swap = 0; + + ge2d_config.src_key.key_enable = 0; + ge2d_config.src_key.key_mask = 0; + ge2d_config.src_key.key_mode = 0; + ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.src_para.format = ge2d_comp_para->format; + ge2d_config.src_para.fill_color_en = 0; + ge2d_config.src_para.fill_mode = 0; + ge2d_config.src_para.x_rev = 0; + ge2d_config.src_para.y_rev = 0; + ge2d_config.src_para.color = 0; + ge2d_config.src_para.top = 0; + ge2d_config.src_para.left = 0; + ge2d_config.src_para.width = ge2d_comp_para->buffer_w; + ge2d_config.src_para.height = ge2d_comp_para->buffer_h; + ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID; + + ge2d_config.dst_para.canvas_index = canvas_index; + ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.dst_para.fill_color_en = 0; + ge2d_config.dst_para.fill_mode = 0; + ge2d_config.dst_para.x_rev = 0; + ge2d_config.dst_para.y_rev = 0; + ge2d_config.dst_para.color = 0; + ge2d_config.dst_para.top = 0; + ge2d_config.dst_para.left = 0; + ge2d_config.dst_para.format = ge2d_comp_para->format; + if (ge2d_comp_para->format == GE2D_FORMAT_M24_NV21) + ge2d_config.dst_para.format |= GE2D_LITTLE_ENDIAN; + ge2d_config.dst_para.width = ge2d_comp_para->buffer_w; + ge2d_config.dst_para.height = ge2d_comp_para->buffer_h; + ge2d_config.mem_sec = ge2d_comp_para->is_tvp; + + temp = ge2d_context_config_ex(ge2d_comp_para->context, &ge2d_config); + if (temp < 0) { + VIDEOCOM_ERR("++ge2d configing error.\n"); + return -1; + } + + fillrect(ge2d_comp_para->context, 0, 0, + ge2d_comp_para->buffer_w, + ge2d_comp_para->buffer_h, + 0x008080ff); + if (ge2d_com_debug & 2) { + para.canvas0_config[0] = dst_canvas0_config[0]; + para.canvas0_config[1] = dst_canvas0_config[1]; + para.canvas0_config[2] = dst_canvas0_config[2]; + para.plane_num = dst_plane_num; + ret = dump_data(¶, BLACK_BUFFER); + if (ret) + VIDEOCOM_INFO("dump black buffer successful.\n"); + } + + return 0; +} + +int v2d_config_ge2d_data(struct vframe_s *src_vf, unsigned long addr, int buf_w, int buf_h, + int data_w, int data_h, int crop_x, int crop_y, int crop_w, int crop_h, + struct ge2d_src_para_s *data) +{ + struct vframe_s *vf = NULL; + int vf_height; + + if (IS_ERR_OR_NULL(data)) { + pr_info("%s: invalid param.\n", __func__); + return -1; + } + + if (src_vf) { + if (src_vf->canvas0_config[0].phy_addr == 0) { + if ((src_vf->flag & VFRAME_FLAG_DOUBLE_FRAM) && src_vf->vf_ext) { + vf = src_vf->vf_ext; + } else { + VIDEOCOM_ERR("%s: vf no yuv data, composer fail\n", __func__); + return -1; + } + } else { + vf = src_vf; + } + + if (vf && vf->di_flag && IS_DI_PSTLINK(vf->di_flag)) + vf_height = vf->height >> 1; + else + vf_height = vf->height; + + data->canvas0Addr = vf->canvas0Addr; + data->canvas1Addr = vf->canvas1Addr; + data->canvas0_config[0] = vf->canvas0_config[0]; + data->canvas0_config[1] = vf->canvas0_config[1]; + data->canvas0_config[2] = vf->canvas0_config[2]; + data->canvas1_config[0] = vf->canvas1_config[0]; + data->canvas1_config[1] = vf->canvas1_config[1]; + data->canvas1_config[2] = vf->canvas1_config[2]; + data->bitdepth = vf->bitdepth; + data->source_type = vf->source_type; + data->type = vf->type; + data->plane_num = vf->plane_num; + + if (vf->type & VIDTYPE_COMPRESS) { + if ((crop_w > WIDTH_8K || crop_w < 0) || + (crop_h > HEIGHT_8K || crop_h < 0)) { + data->width = vf->width; + data->height = vf_height; + } else { + if (crop_w > vf->compWidth) + crop_w = vf->compWidth; + if (crop_h > vf->compHeight) + crop_h = vf->compHeight; + data->position_x = crop_x * vf->width / vf->compWidth; + data->position_y = crop_y * vf->width / vf->compWidth; + data->width = + crop_w * vf->width / vf->compWidth; + data->height = + crop_h * vf->width / vf->compWidth * vf_height / vf->height; + } + } else { + if ((crop_w > WIDTH_8K || crop_w < 0) || + (crop_h > HEIGHT_8K || crop_h < 0)) { + data->width = vf->width; + data->height = vf_height; + } else { + data->position_x = crop_x; + data->position_y = crop_y; + data->width = crop_w; + data->height = crop_h; + if (vf && vf->di_flag && IS_DI_PSTLINK(vf->di_flag)) { + data->position_y >>= 1; + data->height >>= 1; + } + } + } + + if (ge2d_com_debug & 1) { + VIDEOCOM_INFO("scr:vf->canvas0_config[0]: paddr=%lu width=%d height=%d\n", + vf->canvas0_config[0].phy_addr, + vf->canvas0_config[0].width, + vf->canvas0_config[0].height); + VIDEOCOM_INFO("scr:vf->canvas0_config[1]: paddr=%lu width=%d height=%d\n", + vf->canvas0_config[1].phy_addr, + vf->canvas0_config[1].width, + vf->canvas0_config[1].height); + VIDEOCOM_INFO("scr:vf->width=%d vf->height=%d vf->ComW=%d vf->ComH=%d\n", + vf->width, + vf->height, + vf->compWidth, + vf->compHeight); + VIDEOCOM_INFO("scr:crop %d %d %d %d\n", crop_x, crop_y, crop_w, crop_h); + VIDEOCOM_INFO("scr:data %d %d %d %d\n", + data->position_x, data->position_y, data->width, data->height); + } + if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR) + data->is_vframe = false; + else + data->is_vframe = true; + } else { + data->canvas0Addr = -1; + data->canvas1Addr = -1; + data->canvas0_config[0].phy_addr = addr; + + VIDEOCOM_INFO("buffer_w(%d), data_w(%d)\n", buf_w, data_w); + if (buf_w > data_w) { + if (data->is_yuv444) + data->canvas0_config[0].width = buf_w * 3; + else + data->canvas0_config[0].width = buf_w; + } else { + if (data->is_yuv444) + data->canvas0_config[0].width = data_w * 3; + else + data->canvas0_config[0].width = data_w; + } + VIDEOCOM_INFO("buffer_h(%d), data_h(%d)\n", buf_h, data_h); + + if (buf_h > data_h) + data->canvas0_config[0].height = buf_h; + else + data->canvas0_config[0].height = data_h; + + data->canvas0_config[0].block_mode = CANVAS_BLKMODE_LINEAR; + data->canvas0_config[0].endian = 0; + data->canvas0_config[1].phy_addr = + (u32)(addr + (data->canvas0_config[0].width) + * (data->canvas0_config[0].height)); + data->canvas0_config[1].width = + data->canvas0_config[0].width; + data->canvas0_config[1].height = + data->canvas0_config[0].height / 2; + data->canvas0_config[1].block_mode = + CANVAS_BLKMODE_LINEAR; + data->canvas0_config[1].endian = 0; + data->bitdepth = BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8; + data->source_type = 0; + if (data->is_yuv444) { + data->type = VIDTYPE_VIU_SINGLE_PLANE + | VIDTYPE_VIU_FIELD + | VIDTYPE_VIU_444; + data->plane_num = 1; + } else { + data->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | VIDTYPE_VIU_NV21; + data->plane_num = 2; + } + + VIDEOCOM_INFO("crop %d %d %d %d\n", crop_x, crop_y, crop_w, crop_h); + data->position_x = crop_x; + data->position_y = crop_y; + data->is_vframe = false; + if (crop_w <= 0 || crop_h <= 0) { + data->width = buf_w; + data->height = buf_h; + } else { + data->width = crop_w; + data->height = crop_h; + } + } + + return 0; +} + +int v2d_ge2d_data_composer(struct ge2d_src_para_s *scr_data, + struct ge2d_composer_para *ge2d_comp_para) +{ + int ret; + struct canvas_config_s dst_canvas0_config[3]; + struct config_para_ex_s ge2d_config; + u32 dst_plane_num; + int src_canvas_id, dst_canvas_id; + int input_x, input_y, input_width, input_height; + int position_left, position_top; + int position_width, position_height; + struct dump_param para; + enum ge2d_angle_type ge2d_angle; + bool result = false; + + memset(&ge2d_config, 0, sizeof(struct config_para_ex_s)); + memset(dst_canvas0_config, 0, sizeof(dst_canvas0_config)); + + if (ge2d_comp_para->format == GE2D_FORMAT_S24_YUV444) { + dst_canvas0_config[0].phy_addr = ge2d_comp_para->phy_addr[0]; + dst_canvas0_config[0].width = ge2d_comp_para->buffer_w * 3; + dst_canvas0_config[0].height = ge2d_comp_para->buffer_h; + dst_canvas0_config[0].block_mode = 0; + dst_canvas0_config[0].endian = 0; + } else if (ge2d_comp_para->format == GE2D_FORMAT_M24_NV21) { + dst_canvas0_config[0].phy_addr = ge2d_comp_para->phy_addr[0]; + dst_canvas0_config[0].width = ge2d_comp_para->buffer_w; + dst_canvas0_config[0].height = ge2d_comp_para->buffer_h; + dst_canvas0_config[0].block_mode = 0; + dst_canvas0_config[0].endian = 0; + dst_canvas0_config[1].phy_addr = ge2d_comp_para->phy_addr[0] + + ge2d_comp_para->buffer_w * ge2d_comp_para->buffer_h; + dst_canvas0_config[1].width = ge2d_comp_para->buffer_w; + dst_canvas0_config[1].height = ge2d_comp_para->buffer_h >> 1; + dst_canvas0_config[1].block_mode = 0; + dst_canvas0_config[1].endian = 0; + } + dst_plane_num = ge2d_comp_para->plane_num; + if (scr_data->canvas0Addr == (u32)-1) { + ret = alloc_src_canvas(ge2d_comp_para); + if (ret < 0) { + VIDEOCOM_ERR("alloc src canvas failed!\n"); + return -1; + } + canvas_config_config(ge2d_comp_para->canvas_scr[0], + &scr_data->canvas0_config[0]); + if (scr_data->plane_num == 2) { + canvas_config_config(ge2d_comp_para->canvas_scr[1], + &scr_data->canvas0_config[1]); + } else if (scr_data->plane_num == 3) { + canvas_config_config(ge2d_comp_para->canvas_scr[2], + &scr_data->canvas0_config[2]); + } + src_canvas_id = ge2d_comp_para->canvas_scr[0] + | (ge2d_comp_para->canvas_scr[1] << 8) + | (ge2d_comp_para->canvas_scr[2] << 16); + } else { + src_canvas_id = scr_data->canvas0Addr; + } + + if (ge2d_comp_para->canvas0_addr == (u32)-1) { + canvas_config_config(ge2d_comp_para->canvas_dst[0], + &dst_canvas0_config[0]); + if (dst_plane_num == 2) { + canvas_config_config(ge2d_comp_para->canvas_dst[1], + &dst_canvas0_config[1]); + } else if (dst_plane_num == 3) { + canvas_config_config(ge2d_comp_para->canvas_dst[2], + &dst_canvas0_config[2]); + } + dst_canvas_id = ge2d_comp_para->canvas_dst[0] + | (ge2d_comp_para->canvas_dst[1] << 8) + | (ge2d_comp_para->canvas_dst[2] << 16); + } else { + dst_canvas_id = ge2d_comp_para->canvas0_addr; + } + input_width = scr_data->width; + input_height = scr_data->height; + input_x = scr_data->position_x; + input_y = scr_data->position_y; + + if (scr_data->type & VIDTYPE_INTERLACE) + input_height = scr_data->height >> 1; + else + input_height = scr_data->height; + + ge2d_config.alu_const_color = 0; + ge2d_config.bitmask_en = 0; + ge2d_config.src1_gb_alpha = 0; + ge2d_config.dst_xy_swap = 0; + ge2d_config.src_key.key_enable = 0; + ge2d_config.src_key.key_mask = 0; + ge2d_config.src_key.key_mode = 0; + ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.src_para.format = get_input_format(scr_data); + if (!scr_data->is_vframe) + ge2d_config.src_para.format |= GE2D_LITTLE_ENDIAN; + ge2d_config.src_para.fill_color_en = 0; + ge2d_config.src_para.fill_mode = 0; + ge2d_config.src_para.x_rev = 0; + ge2d_config.src_para.y_rev = 0; + ge2d_config.src_para.color = 0xffffffff; + ge2d_config.src_para.top = input_y; + ge2d_config.src_para.left = input_x; + ge2d_config.src_para.width = input_width; + ge2d_config.src_para.height = input_height; + ge2d_config.src_para.canvas_index = src_canvas_id; + ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.dst_para.fill_color_en = 0; + ge2d_config.dst_para.fill_mode = 0; + ge2d_config.dst_para.x_rev = 0; + ge2d_config.dst_para.y_rev = 0; + ge2d_config.dst_xy_swap = 0; + ge2d_angle = config_ge2d_rotation(ge2d_comp_para->angle); + + if (ge2d_angle == GE2D_ANGLE_TYPE_ROT_90) { + ge2d_config.dst_xy_swap = 1; + ge2d_config.dst_para.x_rev = 1; + } else if (ge2d_angle == GE2D_ANGLE_TYPE_ROT_180) { + ge2d_config.dst_para.x_rev = 1; + ge2d_config.dst_para.y_rev = 1; + } else if (ge2d_angle == GE2D_ANGLE_TYPE_ROT_270) { + ge2d_config.dst_xy_swap = 1; + ge2d_config.dst_para.y_rev = 1; + } else if (ge2d_angle == GE2D_ANGLE_TYPE_FLIP_H) { + ge2d_config.dst_xy_swap = 0; + ge2d_config.dst_para.x_rev = 1; + ge2d_config.dst_para.y_rev = 0; + } else if (ge2d_angle == GE2D_ANGLE_TYPE_FLIP_V) { + ge2d_config.dst_xy_swap = 0; + ge2d_config.dst_para.x_rev = 0; + ge2d_config.dst_para.y_rev = 1; + } + + ge2d_config.dst_para.canvas_index = dst_canvas_id; + ge2d_config.dst_para.color = 0; + ge2d_config.dst_para.top = 0; + ge2d_config.dst_para.left = 0; + ge2d_config.dst_para.format = ge2d_comp_para->format; + if (ge2d_comp_para->format == GE2D_FORMAT_M24_NV21) + ge2d_config.dst_para.format |= GE2D_LITTLE_ENDIAN; + ge2d_config.dst_para.width = ge2d_comp_para->buffer_w; + ge2d_config.dst_para.height = ge2d_comp_para->buffer_h; + ge2d_config.mem_sec = ge2d_comp_para->is_tvp; + + ret = ge2d_context_config_ex(ge2d_comp_para->context, &ge2d_config); + if (ret < 0) { + VIDEOCOM_ERR("++ge2d configing error.\n"); + return -1; + } + if (ge2d_com_debug & 4) { + para.canvas0_config[0] = scr_data->canvas0_config[0]; + para.canvas0_config[1] = scr_data->canvas0_config[1]; + para.canvas0_config[2] = scr_data->canvas0_config[2]; + VIDEOCOM_INFO("canvas0:%d %d canvas1:%d %d canvas2:%d %d.\n", + scr_data->canvas0_config[0].width, scr_data->canvas0_config[0].height, + scr_data->canvas0_config[1].width, scr_data->canvas0_config[1].height, + scr_data->canvas0_config[2].width, scr_data->canvas0_config[2].height); + para.plane_num = scr_data->plane_num; + result = dump_data(¶, SCR_BUFFER); + if (result) + VIDEOCOM_INFO("dump scr buffer successful.\n"); + } + if (ge2d_com_debug & 8) { + para.canvas0_config[0] = dst_canvas0_config[0]; + para.canvas0_config[1] = dst_canvas0_config[1]; + para.canvas0_config[2] = dst_canvas0_config[2]; + para.plane_num = dst_plane_num; + result = dump_data(¶, DST_EMPTY_BUFFER); + if (result) + VIDEOCOM_INFO("dump dst empty buffer successful.\n"); + } + + position_left = ge2d_comp_para->position_left; + position_top = ge2d_comp_para->position_top; + position_width = ge2d_comp_para->position_width; + position_height = ge2d_comp_para->position_height; + + stretchblt_noalpha(ge2d_comp_para->context, + input_x, + input_y, + input_width, + input_height, + position_left, + position_top, + position_width, + position_height); + if (ge2d_com_debug & 1) + VIDEOCOM_INFO("scr %0x,dst: %0x!,%d, %d, %d, %d\n", + ge2d_config.src_para.format, + ge2d_config.dst_para.format, + position_left, + position_top, + position_width, + position_height); + if (ge2d_com_debug & 16) { + para.canvas0_config[0] = dst_canvas0_config[0]; + para.canvas0_config[1] = dst_canvas0_config[1]; + para.canvas0_config[2] = dst_canvas0_config[2]; + para.plane_num = dst_plane_num; + result = dump_data(¶, DST_BUFFER_DATA); + if (result) + VIDEOCOM_INFO("dump dst data successful.\n"); + } + + return 0; +} + diff --git a/drivers/media/video_processor/v2d/v2d_ge2d_composer.h b/drivers/media/video_processor/v2d/v2d_ge2d_composer.h new file mode 100644 index 000000000..18dba125e --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_ge2d_composer.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef VFRAME_GE2D_COMPOSER_H +#define VFRAME_GE2D_COMPOSER_H +#include +#include +#include +#include +//#include "video_composer.h" + +#define VIDEOCOM_INFO(fmt, args...) \ + pr_info("video_composer: info:" fmt "", ## args) +#define VIDEOCOM_DBG(fmt, args...) \ + pr_debug("video_composer: dbg:" fmt "", ## args) +#define VIDEOCOM_WARN(fmt, args...) \ + pr_warn("video_composer: warn:" fmt "", ## args) +#define VIDEOCOM_ERR(fmt, args...) \ + pr_err("video_composer: err:" fmt "", ## args) + +enum ge2d_angle_type { + GE2D_ANGLE_TYPE_ROT_90 = 1, + GE2D_ANGLE_TYPE_ROT_180, + GE2D_ANGLE_TYPE_ROT_270, + GE2D_ANGLE_TYPE_FLIP_H, + GE2D_ANGLE_TYPE_FLIP_V, + GE2D_ANGLE_TYPE_MAX, +}; + +enum videocom_source_type { + DECODER_8BIT_NORMAL = 0, + DECODER_8BIT_BOTTOM, + DECODER_8BIT_TOP, + DECODER_10BIT_NORMAL, + DECODER_10BIT_BOTTOM, + DECODER_10BIT_TOP, + VDIN_8BIT_NORMAL, + VDIN_10BIT_NORMAL, +}; + +struct ge2d_composer_para { + int count; + int format; + int position_left; + int position_top; + int position_width; + int position_height; + int buffer_w; + int buffer_h; + int plane_num; + int canvas_scr[3]; + int canvas_dst[3]; + ulong phy_addr[3]; + u32 canvas0_addr; + struct ge2d_context_s *context; + int angle; + bool is_tvp; +}; + +struct ge2d_src_para_s { + u32 canvas0Addr; + u32 canvas1Addr; + u32 plane_num; + u32 type; + u32 position_x; + u32 position_y; + u32 width; + u32 height; + u32 bitdepth; + struct canvas_config_s canvas0_config[3]; + struct canvas_config_s canvas1_config[3]; + enum vframe_source_type_e source_type; + bool is_vframe; + bool is_yuv444; +}; + +struct dump_param { + u32 plane_num; + struct canvas_config_s canvas0_config[3]; +}; + +enum buffer_data { + BLACK_BUFFER = 0, + SCR_BUFFER, + DST_EMPTY_BUFFER, + DST_BUFFER_DATA, + OTHER_BUFFER, +}; + +extern int ge2d_com_debug; +int v2d_init_ge2d_composer(struct ge2d_composer_para *ge2d_comp_para); + +int v2d_uninit_ge2d_composer(struct ge2d_composer_para *ge2d_comp_para); + +int v2d_fill_vframe_black(struct ge2d_composer_para *ge2d_comp_para); + +int v2d_config_ge2d_data(struct vframe_s *src_vf, unsigned long addr, int buf_w, int buf_h, + int data_w, int data_h, int crop_x, int crop_y, int crop_w, int crop_h, + struct ge2d_src_para_s *data); + +int v2d_ge2d_data_composer(struct ge2d_src_para_s *src_para, + struct ge2d_composer_para *ge2d_comp_para); + +#endif diff --git a/drivers/media/video_processor/v2d/v2d_vicp_composer.c b/drivers/media/video_processor/v2d/v2d_vicp_composer.c new file mode 100644 index 000000000..73855831b --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_vicp_composer.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v2d_vicp_composer.h" + +bool check_vicp_status(void) +{ + int cpu_type = 0; + + cpu_type = get_cpu_type(); + if (cpu_type == MESON_CPU_MAJOR_ID_S5 || + cpu_type == MESON_CPU_MAJOR_ID_T3X || + cpu_type == MESON_CPU_MAJOR_ID_S6) + return true; + else + return false; +} + +enum vicp_rotation_mode_e map_rotationmode_from_v2d_to_vicp(int v2d_rotation) +{ + enum vicp_rotation_mode_e rotation = VICP_ROTATION_MAX; + + if (v2d_rotation == 1) + rotation = VICP_ROTATION_MIRROR_H; + else if (v2d_rotation == 2) + rotation = VICP_ROTATION_MIRROR_V; + else if (v2d_rotation == 3) + rotation = VICP_ROTATION_180; + else if (v2d_rotation == 4) + rotation = VICP_ROTATION_90; + else if (v2d_rotation == 7) + rotation = VICP_ROTATION_270; + else + rotation = VICP_ROTATION_0; + + return rotation; +} + +int v2d_config_vicp_input_data(struct vframe_s *vf, ulong addr, int width, int height, + int stride_w, int stride_y, int endian, int color_fmt, int color_depth, + struct input_data_param_s *input_data) +{ + if (IS_ERR_OR_NULL(input_data)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + + memset(input_data, 0, sizeof(struct input_data_param_s)); + if (vf) { + input_data->is_vframe = true; + input_data->data_vf = vf; + } else { + input_data->is_vframe = false; + if (stride_w > width) + input_data->data_dma.buf_stride_w = stride_w; + else + input_data->data_dma.buf_stride_w = width; + + if (stride_y > height) + input_data->data_dma.buf_stride_h = stride_y; + else + input_data->data_dma.buf_stride_h = height; + + input_data->data_dma.buf_addr = addr; + input_data->data_dma.data_width = width; + input_data->data_dma.data_height = height; + input_data->data_dma.color_format = color_fmt; + input_data->data_dma.color_depth = color_depth; + input_data->data_dma.plane_count = 2; + input_data->data_dma.endian = endian; + input_data->data_dma.need_swap_cbcr = 0; + } + + return 0; +} + +int v2d_config_vicp_output_data(int fbc_out_en, int mif_out_en, ulong *phy_addr, int stride, + int width, int height, int endian, enum vicp_color_format_e cfmt_mif, int cdep_mif, + enum vicp_color_format_e cfmt_fbc, int cdep_fbc, int init_ctrl, int pip_mode, + int out_sig_fmt, struct output_data_param_s *output_data) +{ + if (IS_ERR_OR_NULL(output_data)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } + memset(output_data, 0, sizeof(struct output_data_param_s)); + output_data->width = width; + output_data->height = height; + output_data->phy_addr[0] = phy_addr[0]; + output_data->stride[0] = stride; + output_data->mif_out_en = mif_out_en; + output_data->endian = endian; + output_data->need_swap_cbcr = 1; + output_data->out_sig_fmt = out_sig_fmt; + if (mif_out_en) { + output_data->mif_color_fmt = cfmt_mif; + output_data->mif_color_dep = cdep_mif; + } + + output_data->fbc_out_en = fbc_out_en; + if (fbc_out_en) { + output_data->fbc_color_fmt = cfmt_fbc; + output_data->fbc_color_dep = cdep_fbc; + output_data->phy_addr[1] = phy_addr[1]; + output_data->phy_addr[2] = phy_addr[2]; + output_data->fbc_init_ctrl = init_ctrl; + output_data->fbc_pip_mode = pip_mode; + } + + return 0; +} + +int v2d_vicp_data_composer(struct vicp_data_config_s *data_config) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(data_config)) { + pr_info("%s: NULL param, please check.\n", __func__); + return -1; + } +#ifdef CONFIG_AMLOGIC_MEDIA_VICP + ret = vicp_process(data_config); +#endif + if (ret < 0) + pr_info("%s: vicp_process failed.\n", __func__); + + return ret; +} diff --git a/drivers/media/video_processor/v2d/v2d_vicp_composer.h b/drivers/media/video_processor/v2d/v2d_vicp_composer.h new file mode 100644 index 000000000..8bfa0cd10 --- /dev/null +++ b/drivers/media/video_processor/v2d/v2d_vicp_composer.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef V2D_VICP_COMPOSER_H +#define V2D_VICP_COMPOSER_H +#include +#include +#include +#include +#include + +bool check_vicp_status(void); +enum vicp_rotation_mode_e map_rotationmode_from_v2d_to_vicp(int v2d_rotation); +int v2d_config_vicp_input_data(struct vframe_s *vf, ulong addr, int width, int height, + int stride_w, int stride_y, int endian, int color_fmt, int color_depth, + struct input_data_param_s *input_data); +int v2d_config_vicp_output_data(int fbc_out_en, int mif_out_en, ulong *phy_addr, int stride, + int width, int height, int endian, enum vicp_color_format_e cfmt_mif, int cdep_mif, + enum vicp_color_format_e cfmt_fbc, int cdep_fbc, int init_ctrl, int pip_mode, + int out_sig_fmt, struct output_data_param_s *output_data); +int v2d_vicp_data_composer(struct vicp_data_config_s *data_config); +#ifdef CONFIG_AMLOGIC_MEDIA_VICP +int vicp_process(struct vicp_data_config_s *data_config); +#endif +#endif diff --git a/include/linux/amlogic/major.h b/include/linux/amlogic/major.h index 516883a2d..8bbe2ffc2 100644 --- a/include/linux/amlogic/major.h +++ b/include/linux/amlogic/major.h @@ -50,4 +50,5 @@ #define DI_PROCESS_MAJOR (32 + (AML_BASE)) #define AML_DHP_MAJOR (33 + (AML_BASE)) #define VIDEODISPLAY_MAJOR (34 + (AML_BASE)) +#define V2D_MAJOR (35 + (AML_BASE)) #endif