video_sink: add amvideocap module This will add the amvideocap module to kernel 4...
authorPortisch <hugo.portisch@yahoo.de>
Mon, 8 Apr 2019 08:10:17 +0000 (08:10 +0000)
committerNick Xie <nick@khadas.com>
Fri, 12 Jul 2019 05:36:53 +0000 (13:36 +0800)
14 files changed:
arch/arm64/boot/dts/amlogic/mesong12b.dtsi
arch/arm64/boot/dts/amlogic/mesong12b_a.dtsi
drivers/amlogic/media/common/ge2d/stretchblt.c
drivers/amlogic/media/deinterlace/deinterlace_dbg.c
drivers/amlogic/media/video_sink/Makefile
drivers/amlogic/media/video_sink/amvideocap.c [new file with mode: 0644]
drivers/amlogic/media/video_sink/amvideocap.h [new file with mode: 0644]
drivers/amlogic/media/video_sink/amvideocap_priv.h
drivers/amlogic/media/video_sink/video.c
drivers/amlogic/media/video_sink/video_keeper.c
drivers/amlogic/media/video_sink/video_priv.h
drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.c
drivers/amlogic/media_modules/amvdec_ports/decoder/vdec_h264_if.c
include/linux/amlogic/media/vfm/vframe.h

index 4e82b3a..37ee59d 100644 (file)
                        "clk_hevcb_mux";
        };
 
+       amvideocap {
+               compatible = "amlogic, amvideocap";
+               dev_name = "amvideocap.0";
+               status = "okay";
+               max_size = <8>;//8M
+       };
+
        vdec {
                compatible = "amlogic, vdec";
                dev_name = "vdec.0";
index b2bf512..9fa235d 100644 (file)
                        "clk_hevcb_mux";
        };
 
+       amvideocap {
+               compatible = "amlogic, amvideocap";
+               dev_name = "amvideocap.0";
+               status = "okay";
+               max_size = <8>;//8M
+       };
+
        vdec {
                compatible = "amlogic, vdec";
                dev_name = "vdec.0";
index 0c04197..cae54f3 100644 (file)
@@ -115,7 +115,7 @@ static void _stretchblt_noalpha(struct ge2d_context_s *wq,
        ge2d_cmd_cfg->color_logic_op   = LOGIC_OPERATION_COPY;
        ge2d_cmd_cfg->alpha_blend_mode = OPERATION_LOGIC;
        ge2d_cmd_cfg->alpha_logic_op   = LOGIC_OPERATION_SET;
-       ge2d_cmd_cfg->wait_done_flag   = 1;
+       ge2d_cmd_cfg->wait_done_flag   = blk;
 
        ge2d_wq_add_work(wq);
 }
index 63f6102..603ddf6 100644 (file)
@@ -864,8 +864,8 @@ void dump_vframe(struct vframe_s *vf)
                vf->left_eye.width, vf->left_eye.height,
                vf->right_eye.start_x, vf->right_eye.start_y,
                vf->right_eye.width, vf->right_eye.height);
-       pr_info("mode_3d_enable %d, use_cnt %d,",
-               vf->mode_3d_enable, atomic_read(&vf->use_cnt));
+       pr_info("mode_3d_enable %d",
+               vf->mode_3d_enable);
        pr_info("early_process_fun 0x%p, process_fun 0x%p, private_data %p\n",
                vf->early_process_fun,
                vf->process_fun, vf->private_data);
index 7f5a423..5e3d8c8 100644 (file)
@@ -1,2 +1,6 @@
 obj-y  +=      amvideo.o
 amvideo-objs   +=      video.o video_keeper.o vpp.o
+
+ifeq ($(CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE),y)
+amvideo-objs   +=      amvideocap.o
+endif
diff --git a/drivers/amlogic/media/video_sink/amvideocap.c b/drivers/amlogic/media/video_sink/amvideocap.c
new file mode 100644 (file)
index 0000000..172cd9c
--- /dev/null
@@ -0,0 +1,1225 @@
+/*
+ * drivers/amlogic/media/video_sink/amvideocap.c
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include <linux/mutex.h>
+#include <linux/amlogic/media/video_sink/amvideocap.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/canvas/canvas.h>
+#include <linux/amlogic/media/canvas/canvas_mgr.h>
+#include <linux/amlogic/media/ge2d/ge2d.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+#include "../../media_modules/stream_input/amports/amports_priv.h"
+#include "amvideocap_priv.h"
+/*
+#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6
+#include <mach/mod_gate.h>
+#endif
+*/
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#define DRIVER_NAME "amvideocap"
+#define MODULE_NAME "amvideocap"
+#define DEVICE_NAME "amvideocap"
+
+#define CAP_WIDTH_MAX      3840
+#define CAP_HEIGHT_MAX     2160
+
+MODULE_DESCRIPTION("Video Frame capture");
+MODULE_AUTHOR("amlogic-bj");
+MODULE_LICENSE("GPL");
+
+#define AMCAP_MAX_OPEND 16
+struct amvideocap_global_data {
+       struct class *class;
+       struct device *dev;
+       struct device *micro_dev;
+       int major;
+       unsigned long phyaddr;
+       unsigned long vaddr;
+       unsigned long size;
+       int opened_cnt;
+       int flags;
+       struct mutex lock;
+       struct video_frame_info want;
+       u64 wait_max_ms;
+};
+static struct amvideocap_global_data amvideocap_gdata;
+static struct ge2d_context_s *ge2d_amvideocap_context;
+static inline struct amvideocap_global_data *getgctrl(void)
+{
+       return &amvideocap_gdata;
+}
+
+static int use_cma;
+#ifdef CONFIG_CMA
+static struct platform_device *amvideocap_pdev;
+static int cma_max_size;
+#define CMA_NAME "amvideocap"
+#endif
+#define gLOCK() mutex_lock(&(getgctrl()->lock))
+#define gUNLOCK() mutex_unlock(&(getgctrl()->lock))
+#define gLOCKINIT() mutex_init(&(getgctrl()->lock))
+
+/*********************************************************
+ * /dev/amvideo APIs
+ *********************************************************/
+static int amvideocap_open(struct inode *inode, struct file *file)
+{
+       struct amvideocap_private *priv;
+       gLOCK();
+#ifdef CONFIG_CMA
+       if (use_cma && amvideocap_pdev) {
+               unsigned long phybufaddr;
+               phybufaddr = codec_mm_alloc_for_dma(CMA_NAME,
+                               cma_max_size * SZ_1M / PAGE_SIZE,
+                               4 + PAGE_SHIFT, CODEC_MM_FLAGS_CPU);
+               /* pr_err("%s: codec_mm_alloc_for_dma:%p\n",
+                               __func__, (void *)phybufaddr);
+               */
+               amvideocap_register_memory((unsigned char *)phybufaddr,
+                                       cma_max_size * SZ_1M);
+       }
+#endif
+       if (!getgctrl()->phyaddr) {
+               gUNLOCK();
+               pr_err("Error,no memory have register for amvideocap\n");
+               return -ENOMEM;
+       }
+#ifdef CONFIG_CMA
+       if (!getgctrl()->vaddr) {
+               getgctrl()->vaddr = (unsigned long)codec_mm_phys_to_virt(
+                                               getgctrl()->phyaddr);
+               if (!getgctrl()->vaddr) {
+                       pr_err("%s: failed to remap y addr\n", __func__);
+                       gUNLOCK();
+                       return -ENOMEM;
+               }
+       }
+#endif
+       if (getgctrl()->opened_cnt > AMCAP_MAX_OPEND) {
+               gUNLOCK();
+               pr_err("Too Many opend video cap files\n");
+               return -EMFILE;
+       }
+       priv = kmalloc(sizeof(struct amvideocap_private), GFP_KERNEL);
+       if (!priv) {
+               gUNLOCK();
+               pr_err("alloc memory failed for amvideo cap\n");
+               return -ENOMEM;
+       }
+       memset(priv, 0, sizeof(struct amvideocap_private));
+       getgctrl()->opened_cnt++;
+       gUNLOCK();
+       file->private_data = priv;
+       priv->phyaddr = getgctrl()->phyaddr;
+       priv->vaddr = (u8 *) getgctrl()->vaddr;
+       priv->want = getgctrl()->want;
+       priv->src_rect.x = -1;
+       priv->src_rect.y = -1;
+       priv->src_rect.width = -1;
+       priv->src_rect.height = -1;
+       return 0;
+}
+
+static int amvideocap_release(struct inode *inode, struct file *file)
+{
+       struct amvideocap_private *priv = file->private_data;
+       kfree(priv);
+#ifdef CONFIG_CMA
+       if (use_cma && amvideocap_pdev) {
+               codec_mm_free_for_dma(CMA_NAME, getgctrl()->phyaddr);
+               /*
+                  pr_err("%s: codec_mm_free_for_dma:%p\n", __func__,
+                  (void *)getgctrl()->phyaddr);
+                */
+       }
+#endif
+       gLOCK();
+       getgctrl()->opened_cnt--;
+
+       gUNLOCK();
+       return 0;
+}
+
+static int amvideocap_format_to_byte4pix(int fmt)
+{
+       switch (fmt) {
+       case GE2D_FORMAT_S16_RGB_565:
+               return 2;
+       case GE2D_FORMAT_S24_BGR:
+               return 3;
+       case GE2D_FORMAT_S24_RGB:
+               return 3;
+       case GE2D_FORMAT_S32_ABGR:
+               return 4;
+       case GE2D_FORMAT_S32_RGBA:
+               return 4;
+       case GE2D_FORMAT_S32_BGRA:
+               return 4;
+       case GE2D_FORMAT_S32_ARGB:
+               return 4;
+       default:
+               return 4;
+       }
+};
+
+static int amvideocap_capture_get_frame
+(struct amvideocap_private *priv, struct vframe_s **vf, int *cur_index)
+{
+       int ret;
+       ret = ext_get_cur_video_frame(vf, cur_index);
+       return ret;
+}
+
+static int amvideocap_capture_put_frame
+(struct amvideocap_private *priv, struct vframe_s *vf)
+{
+       return ext_put_video_frame(vf);
+}
+
+static int amvideocap_get_input_format(struct vframe_s *vf)
+{
+       int format = GE2D_FORMAT_M24_NV21;
+       /* pr_debug("vf->type:0x%x\n", vf->type); */
+
+       if ((vf->type & VIDTYPE_VIU_422) == VIDTYPE_VIU_422) {
+               pr_debug
+               ("*****************Into VIDTYPE_VIU_422******************\n");
+               format = GE2D_FORMAT_S16_YUV422;
+       } else if ((vf->type & VIDTYPE_VIU_444) == VIDTYPE_VIU_444) {
+               pr_debug
+               ("*****************Into VIDTYPE_VIU_444******************\n");
+               format = GE2D_FORMAT_S24_YUV444;
+       } else if ((vf->type & VIDTYPE_VIU_NV21) == VIDTYPE_VIU_NV21) {
+               pr_debug
+               ("****************Into VIDTYPE_VIU_NV21******************\n");
+               format = GE2D_FORMAT_M24_NV21;
+       }
+       return format;
+}
+
+static ssize_t amvideocap_YUV_to_RGB(
+       struct amvideocap_private *priv,
+       u32 cur_index, int w, int h,
+       struct vframe_s *vf, int outfmt)
+{
+       struct config_para_ex_s ge2d_config;
+       struct canvas_s cs0, cs1, cs2, cd;
+
+       void __iomem *psrc;
+       void __iomem *pdst;
+       int temp_cma_buf_size = 0;
+       unsigned long phybufaddr_8bit = 0;
+       struct timeval start, end;
+       unsigned long time_use;
+       int ret = 0;
+       struct canvas_s temp_cs0, temp_cs1, temp_cs2;
+       int temp_canvas_idx = -1;
+       int temp_y_index = -1;
+       int temp_u_index = -1;
+       int temp_v_index = -1;
+       unsigned char buf1[16];
+       unsigned char buf2[16];
+       unsigned char buf3[16];
+       unsigned char buf_in[48];
+       unsigned char buf_out[32];
+       unsigned char tmp_buf[8];
+       unsigned char in_cursor;
+       unsigned char out_cursor;
+       unsigned long read_size = 0;
+       unsigned int i;
+       unsigned char *line_start;
+       unsigned long counter = 0;
+       unsigned int w_align;
+       unsigned int h_align;
+       unsigned char tmp_char1;
+       int height_after_di;
+
+       const char *amvideocap_owner = "amvideocapframe";
+       int canvas_idx = canvas_pool_map_alloc_canvas(amvideocap_owner);
+       int y_index = cur_index & 0xff;
+       int u_index = (cur_index >> 8) & 0xff;
+       int v_index = (cur_index >> 16) & 0xff;
+       int input_x, input_y, input_width, input_height, intfmt;
+       memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
+       intfmt = amvideocap_get_input_format(vf);
+
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               (intfmt == GE2D_FORMAT_S16_YUV422) &&
+               (get_cpu_type() < MESON_CPU_MAJOR_ID_TXL)) {
+               temp_canvas_idx =
+                       canvas_pool_map_alloc_canvas(amvideocap_owner);
+               if (temp_canvas_idx < 0) {
+                       pr_err("alloc temp_canvas_idx failed");
+                       return -1;
+               }
+               temp_y_index = temp_canvas_idx & 0xff;
+               temp_u_index = (temp_canvas_idx >> 8) & 0xff;
+               temp_v_index = (temp_canvas_idx >> 16) & 0xff;
+       }
+       /* /unsigned long RGB_phy_addr=getgctrl()->phyaddr; */
+
+       if (!priv->phyaddr) {
+               pr_err("%s: failed to alloc y addr\n", __func__);
+               return -1;
+       }
+       pr_debug("RGB_phy_addr:%x\n", (unsigned int)priv->phyaddr);
+
+       if (vf == NULL) {
+               pr_err("%s: vf is NULL\n", __func__);
+               return -1;
+       }
+
+       canvas_config(canvas_idx, priv->phyaddr,
+                                 w * amvideocap_format_to_byte4pix(outfmt), h,
+                                 CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
+       if (priv->src_rect.x < 0 || priv->src_rect.x > vf->width)
+               input_x = 0;
+       else
+               input_x = priv->src_rect.x;
+
+       if (priv->src_rect.y < 0 || priv->src_rect.y > vf->height)
+               input_y = 0;
+       else
+               input_y = priv->src_rect.y;
+
+       if (priv->src_rect.width < 0)
+               input_width = vf->width;
+       else if ((priv->src_rect.x + priv->src_rect.width) > vf->width) {
+               input_width =
+                       priv->src_rect.x + priv->src_rect.width - vf->width;
+       } else
+               input_width = priv->src_rect.width;
+
+       if (priv->src_rect.height < 0)
+               input_height = vf->height;
+       else if ((priv->src_rect.y + priv->src_rect.height) > vf->height) {
+               input_height =
+                       priv->src_rect.y + priv->src_rect.height - vf->height;
+       } else
+               input_height = priv->src_rect.height;
+
+       height_after_di = vf->height;
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               ((intfmt == GE2D_FORMAT_S16_YUV422) ||
+               ((intfmt == GE2D_FORMAT_S24_YUV444) &&
+               (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)))) {
+               pr_debug("input_height = %d , vf->type_original = %x\n" ,
+                       input_height, vf->type_original);
+               if ((vf->source_type == VFRAME_SOURCE_TYPE_HDMI) ||
+                       (vf->source_type == VFRAME_SOURCE_TYPE_CVBS) ||
+                       (vf->source_type == VFRAME_SOURCE_TYPE_TUNER)) {
+                       if (vf->type_original & VIDTYPE_INTERLACE) {
+                               height_after_di = vf->height >> 1;
+                               input_height >>= 1;
+                       } else {
+                               height_after_di = vf->height;
+                       }
+               } else {
+                       /*local playback and DTV*/
+                       pr_debug("vf->prog_proc_config = %d",
+                               vf->prog_proc_config);
+                       if ((!vf->prog_proc_config) &&
+                               (!(vf->type_original & VIDTYPE_INTERLACE))) {
+                               height_after_di = vf->height;
+                       } else {
+                               height_after_di = vf->height >> 1;
+                               input_height >>= 1;
+                       }
+               }
+       } else if (is_meson_g9tv_cpu() || is_meson_gxtvbb_cpu()) {
+               if (vf->type_original & VIDTYPE_INTERLACE)
+                       input_height = input_height / 2;
+       } else {
+               if (intfmt == GE2D_FORMAT_S16_YUV422)
+                       input_height = input_height / 2;
+       }
+       ge2d_config.alu_const_color = 0;
+       ge2d_config.bitmask_en = 0;
+       ge2d_config.src1_gb_alpha = 0;
+       ge2d_config.dst_xy_swap = 0;
+
+       canvas_read(y_index, &cs0);
+       canvas_read(u_index, &cs1);
+       canvas_read(v_index, &cs2);
+
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               (intfmt == GE2D_FORMAT_S16_YUV422) &&
+               (get_cpu_type() < MESON_CPU_MAJOR_ID_TXL)) {
+               pr_debug("vf->width = %d , vf->height = %d , vf->bitdepth = %d\n",
+               vf->width, vf->height, vf->bitdepth);
+               do_gettimeofday(&start);
+               psrc = phys_to_virt(cs0.addr);
+               w_align = ((vf->width + 32 - 1) & ~(32 - 1));
+               h_align = ((vf->height + 32 - 1) & ~(32 - 1));
+               temp_cma_buf_size =
+               (int)((w_align * h_align * 2)/(1024 * 1024)) + 1;
+               pr_debug("phybufaddr_8bit buffer size = %d\n",
+                       temp_cma_buf_size);
+               phybufaddr_8bit = codec_mm_alloc_for_dma(CMA_NAME,
+                       temp_cma_buf_size * SZ_1M / PAGE_SIZE,
+                       4 + PAGE_SHIFT, CODEC_MM_FLAGS_CPU);
+               if (!phybufaddr_8bit)
+                       pr_err("failed to alloc phybufaddr_8bit\n");
+
+               canvas_config(temp_canvas_idx, phybufaddr_8bit,
+                       w_align * 2, h_align,
+                       CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
+               canvas_read(temp_y_index, &temp_cs0);
+               canvas_read(temp_u_index, &temp_cs1);
+               canvas_read(temp_v_index, &temp_cs2);
+
+               pdst = phys_to_virt(temp_cs0.addr);
+
+               pr_debug("height_after_di = %d" , height_after_di);
+               line_start = psrc;
+               for (i = 0; i < height_after_di; i++) {
+                       for (read_size = 0; read_size < w_align*3;
+                               read_size += 48) {
+                               /* swap 64bit */
+                               memcpy(buf1, line_start+read_size, 16);
+                               memcpy(tmp_buf, buf1,           8);
+                               memcpy(buf1,    buf1+8,         8);
+                               memcpy(buf1+8,  tmp_buf,        8);
+                               memcpy(buf_in+32, buf1, 16);
+
+                               memcpy(buf2, line_start+read_size+16, 16);
+                               memcpy(tmp_buf, buf2,           8);
+                               memcpy(buf2,    buf2+8,         8);
+                               memcpy(buf2+8,  tmp_buf,        8);
+                               memcpy(buf_in+16, buf2, 16);
+
+                               memcpy(buf3, line_start+read_size+32, 16);
+                               memcpy(tmp_buf, buf3,           8);
+                               memcpy(buf3,    buf3+8,         8);
+                               memcpy(buf3+8,  tmp_buf,        8);
+                               memcpy(buf_in, buf3, 16);
+
+                               in_cursor = 47;
+                               out_cursor = 0;
+
+                               for (out_cursor = 0; out_cursor <= 30;
+                                       out_cursor += 2, in_cursor -= 3) {
+                                       buf_out[out_cursor] =
+                                       (buf_in[in_cursor-1] << 4) |
+                                       (buf_in[in_cursor-2] >> 4);
+                                       buf_out[out_cursor+1] =
+                                               buf_in[in_cursor];
+                               }
+                               for (out_cursor = 0; out_cursor <= 24;
+                                       out_cursor += 8) {
+                                       /* y1 y4 */
+                                       tmp_char1 = buf_out[1+out_cursor];
+                                       buf_out[1+out_cursor] =
+                                               buf_out[7+out_cursor];
+                                       buf_out[7+out_cursor] = tmp_char1;
+
+                                       /* y2 y3 */
+                                       tmp_char1 = buf_out[3+out_cursor];
+                                       buf_out[3+out_cursor] =
+                                               buf_out[5+out_cursor];
+                                       buf_out[5+out_cursor] = tmp_char1;
+
+                                       /* u1 u2 */
+                                       tmp_char1 = buf_out[out_cursor];
+                                       buf_out[out_cursor] =
+                                               buf_out[2+out_cursor];
+                                       buf_out[2+out_cursor] = tmp_char1;
+
+                                       /* v1 v2 */
+                                       tmp_char1 = buf_out[4+out_cursor];
+                                       buf_out[4+out_cursor] =
+                                               buf_out[6+out_cursor];
+                                       buf_out[6+out_cursor] = tmp_char1;
+                               }
+
+                               memcpy(pdst+counter*32, buf_out, 32);
+                               counter += 1;
+                       }
+                       line_start += cs0.width;
+               }
+
+               codec_mm_dma_flush(pdst,
+                       temp_cma_buf_size * SZ_1M, DMA_TO_DEVICE);
+
+               counter = 0;
+               do_gettimeofday(&end);
+               time_use = (end.tv_sec - start.tv_sec) * 1000 +
+               (end.tv_usec - start.tv_usec) / 1000;
+               pr_debug("10to8 conversion cost time: %ldms\n", time_use);
+       }
+
+       pr_debug("y_index=[0x%x]  u_index=[0x%x] cur_index:%x\n", y_index,
+                       u_index, cur_index);
+
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               (intfmt == GE2D_FORMAT_S16_YUV422) &&
+               (get_cpu_type() < MESON_CPU_MAJOR_ID_TXL)) {
+               ge2d_config.src_planes[0].addr = temp_cs0.addr;
+               ge2d_config.src_planes[0].w = temp_cs0.width;
+               ge2d_config.src_planes[0].h = temp_cs0.height;
+               ge2d_config.src_planes[1].addr = temp_cs1.addr;
+               ge2d_config.src_planes[1].w = temp_cs1.width;
+               ge2d_config.src_planes[1].h = temp_cs1.height;
+               ge2d_config.src_planes[2].addr = temp_cs2.addr;
+               ge2d_config.src_planes[2].w = temp_cs2.width;
+               ge2d_config.src_planes[2].h = temp_cs2.height;
+               pr_debug("w=%d-height=%d\n", temp_cs0.width, temp_cs0.height);
+               pr_debug("cs0.width=%d, cs0.height=%d\n", cs0.width, cs0.height);
+       } else {
+               ge2d_config.src_planes[0].addr = cs0.addr;
+               ge2d_config.src_planes[0].w = cs0.width;
+               ge2d_config.src_planes[0].h = cs0.height;
+               ge2d_config.src_planes[1].addr = cs1.addr;
+               ge2d_config.src_planes[1].w = cs1.width;
+               ge2d_config.src_planes[1].h = cs1.height;
+               ge2d_config.src_planes[2].addr = cs2.addr;
+               ge2d_config.src_planes[2].w = cs2.width;
+               ge2d_config.src_planes[2].h = cs2.height;
+               pr_debug("w=%d-height=%d cur_index:%x\n",
+                       cs0.width, cs0.height, cur_index);
+       }
+
+       ge2d_config.src_key.key_enable = 0;
+       ge2d_config.src_key.key_mask = 0;
+       ge2d_config.src_key.key_mode = 0;
+       ge2d_config.src_key.key_color = 0;
+
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               (intfmt == GE2D_FORMAT_S16_YUV422) &&
+               (get_cpu_type() < MESON_CPU_MAJOR_ID_TXL))
+               ge2d_config.src_para.canvas_index = temp_canvas_idx;
+       else
+               ge2d_config.src_para.canvas_index = cur_index;
+
+       ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID;
+       if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL) {
+               if (intfmt == GE2D_FORMAT_S16_YUV422) {
+                       if ((vf->bitdepth & BITDEPTH_Y10) &&
+                               (vf->bitdepth & FULL_PACK_422_MODE)) {
+                               pr_debug("format is yuv422 10bit .\n");
+                               ge2d_config.src_para.format =
+                                       GE2D_FORMAT_S16_10BIT_YUV422;
+                       } else if (vf->bitdepth & BITDEPTH_Y10) {
+                               pr_debug("format is yuv422 12bit .\n");
+                               ge2d_config.src_para.format =
+                                       GE2D_FORMAT_S16_12BIT_YUV422;
+                       } else {
+                               ge2d_config.src_para.format = intfmt;
+                       }
+               } else if (intfmt == GE2D_FORMAT_S24_YUV444) {
+                       if (vf->bitdepth & BITDEPTH_Y10) {
+                               pr_debug("format is yuv444 10bit .\n");
+                               ge2d_config.src_para.format =
+                                       GE2D_FORMAT_S24_10BIT_YUV444;
+                       } else {
+                               ge2d_config.src_para.format = intfmt;
+                       }
+               } else {
+                       ge2d_config.src_para.format = intfmt;
+               }
+       } else {
+               ge2d_config.src_para.format = intfmt;
+       }
+       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.left = input_x;
+       ge2d_config.src_para.top = input_y;
+       ge2d_config.src_para.width = input_width;
+       ge2d_config.src_para.height = input_height;
+
+       canvas_read(canvas_idx, &cd);
+       pr_debug("cd.addr:%x\n", (unsigned int)cd.addr);
+       ge2d_config.dst_planes[0].addr = cd.addr;
+       ge2d_config.dst_planes[0].w = cd.width;
+       ge2d_config.dst_planes[0].h = cd.height;
+
+       ge2d_config.dst_para.canvas_index = canvas_idx;
+       ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID;
+       ge2d_config.dst_para.format = outfmt;
+       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_config.dst_para.color = 0;
+       ge2d_config.dst_para.top = 0;
+       ge2d_config.dst_para.left = 0;
+       ge2d_config.dst_para.width = w;
+       ge2d_config.dst_para.height = h;
+
+       if (ge2d_context_config_ex(ge2d_amvideocap_context, &ge2d_config) < 0) {
+               pr_err("++ge2d configuration error.\n");
+               return -1;
+       }
+
+       stretchblt_noalpha_noblk(ge2d_amvideocap_context,
+                                          0,
+                                          0,
+                                          ge2d_config.src_para.width,
+                                          ge2d_config.src_para.height,
+                                          0,
+                                          0,
+                                          ge2d_config.dst_para.width,
+                                          ge2d_config.dst_para.height);
+       if (canvas_idx)
+               canvas_pool_map_free_canvas(canvas_idx);
+
+       if (((vf->bitdepth & BITDEPTH_Y10)) &&
+               (intfmt == GE2D_FORMAT_S16_YUV422) &&
+               (get_cpu_type() < MESON_CPU_MAJOR_ID_TXL)) {
+               if (phybufaddr_8bit) {
+                       ret = codec_mm_free_for_dma(CMA_NAME, phybufaddr_8bit);
+                       if (ret != 0)
+                               pr_err("phybufaddr_8bit cma buffer free failed .\n");
+               }
+       if (temp_canvas_idx)
+               canvas_pool_map_free_canvas(temp_canvas_idx);
+       }
+
+       return 0;
+}
+
+static int amvideocap_capture_one_frame_l(
+               struct amvideocap_private *priv,
+               int curindex, int w, int h,
+               struct vframe_s *vf, int outge2dfmt)
+{
+       int ret;
+       /* TODO: mod gate */
+       /* switch_mod_gate_by_name("ge2d", 1); */
+       ret = amvideocap_YUV_to_RGB(priv, curindex, w, h, vf, outge2dfmt);
+       /* TODO: mod gate */
+       /* switch_mod_gate_by_name("ge2d", 0); */
+       return ret;
+}
+
+static int amvideocap_capture_one_frame(
+       struct amvideocap_private *priv,
+       struct vframe_s *vfput, int index)
+{
+       int w, h, ge2dfmt;
+       int curindex;
+       struct vframe_s *vf = vfput;
+       int ret = 0;
+       pr_debug("%s:start vf=%p,index=%x\n", __func__, vf, index);
+       if (!vf)
+               ret = amvideocap_capture_get_frame(priv, &vf, &curindex);
+       else
+               curindex = index;
+       if (ret < 0 || !vf)
+               return -EAGAIN;
+       pr_debug("%s: get vf type=%x\n", __func__, vf->type);
+
+#define CHECK_AND_SETVAL(want, def) ((want) > 0 ? (want) : (def))
+       ge2dfmt = CHECK_AND_SETVAL(priv->want.fmt, vf->type);
+       w = CHECK_AND_SETVAL(priv->want.width, vf->width);
+       h = CHECK_AND_SETVAL(priv->want.height, vf->height);
+#undef CHECK_AND_SETVAL
+
+       w = (w < CAP_WIDTH_MAX) ? w : CAP_WIDTH_MAX;
+       h = (h < CAP_HEIGHT_MAX) ? h : CAP_HEIGHT_MAX;
+
+       ret = amvideocap_capture_one_frame_l(priv, curindex, w, h, vf,
+                                               priv->want.fmt);
+       amvideocap_capture_put_frame(priv, vf);
+
+       if (!ret) {
+               pr_debug("%s: capture ok priv->want.fmt=%d\n", __func__,
+                               priv->want.fmt);
+               priv->state = AMVIDEOCAP_STATE_FINISHED_CAPTURE;
+               priv->src.width = vf->width;
+               priv->src.height = vf->height;
+               priv->out.timestamp_ms = vf->pts * 1000 / 90;
+               priv->out.width = w;
+               priv->out.height = h;
+               priv->out.fmt = priv->want.fmt;
+               priv->out.width_aligned = priv->out.width;
+               priv->out.byte_per_pix = amvideocap_format_to_byte4pix(
+                       priv->out.fmt); /* RGBn */
+       } else
+               priv->state = AMVIDEOCAP_STATE_ERROR;
+       pr_debug("amvideocap_capture_one_frame priv->state=%d\n", priv->state);
+       return ret;
+}
+
+static int amvideocap_capture_one_frame_wait(
+               struct amvideocap_private *priv,
+               int waitms)
+{
+       unsigned long timeout = jiffies + waitms * HZ / 1000;
+       int ret = 0;
+       struct amvideocap_req_data reqdata;
+       struct amvideocap_req req;
+       priv->sended_end_frame_cap_req = -EAGAIN;
+       priv->state = AMVIDEOCAP_STATE_ON_CAPTURE;
+       do {
+               if (ret == -EAGAIN)
+                       usleep_range(500, 1000);
+               if (priv->want.at_flags == CAP_FLAG_AT_END) {
+                       if (priv->sended_end_frame_cap_req == -EAGAIN) {
+                               reqdata.privdata = priv;
+                               req.callback =
+                                       amvideocap_capture_one_frame;
+                               req.data = (unsigned long)&reqdata;
+                               req.at_flags = priv->want.at_flags;
+                               req.timestamp_ms = priv->want.timestamp_ms;
+                               priv->sended_end_frame_cap_req =
+                                       ext_register_end_frame_callback(&req);
+                               ret = -EAGAIN;
+                       } else
+                               if (priv->sended_end_frame_cap_req == -ENODATA ||
+                                                       priv->state == AMVIDEOCAP_STATE_ERROR) {
+                               ret = -ENODATA;
+                       } else
+                               if (priv->state == AMVIDEOCAP_STATE_FINISHED_CAPTURE)
+                                       ret = 0;
+               } else {
+                       ret = amvideocap_capture_one_frame(priv, NULL, 0);
+                       pr_debug("amvideocap_capture_one_frame_wait ret=%d\n",
+                                       ret);
+               }
+       } while (ret == -EAGAIN && time_before(jiffies, timeout));
+       ext_register_end_frame_callback(NULL);  /*del req */
+       return ret;
+}
+
+static long amvideocap_ioctl(struct file *file,
+       unsigned int cmd, ulong arg)
+{
+       int ret = 0;
+       struct amvideocap_private *priv = file->private_data;
+       switch (cmd) {
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_FORMAT: {
+               priv->want.fmt = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH: {
+               priv->want.width = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT: {
+               priv->want.height = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_TIMESTAMP_MS: {
+               priv->want.timestamp_ms = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS: {
+               priv->want.at_flags = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_FRAME_FORMAT: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->out.fmt,
+                        sizeof(priv->out.fmt))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_FRAME_WIDTH: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->out.width,
+                        sizeof(priv->out.width))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_FRAME_HEIGHT: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->out.height,
+                        sizeof(priv->out.height))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_FRAME_TIMESTAMP_MS: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->out.timestamp_ms,
+                        sizeof(priv->out.timestamp_ms))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_SRCFRAME_FORMAT: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->src.fmt,
+                        sizeof(priv->src.fmt))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_SRCFRAME_WIDTH: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->src.width,
+                        sizeof(priv->src.width))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_SRCFRAME_HEIGHT: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->src.height,
+                        sizeof(priv->src.height))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_GET_STATE: {
+               if (copy_to_user
+                       ((void *)arg, (void *)&priv->state,
+                        sizeof(priv->state))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS: {
+               priv->wait_max_ms = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_START_CAPTURE: {
+               ret = amvideocap_capture_one_frame_wait(priv, arg);
+               break;
+       }
+       case AMVIDEOCAP_IOW_SET_CANCEL_CAPTURE: {
+               if (priv->sended_end_frame_cap_req) {
+                       ext_register_end_frame_callback(NULL);  /*del req */
+                       priv->sended_end_frame_cap_req = 0;
+                       priv->state = AMVIDEOCAP_STATE_INIT;
+               }
+               break;
+       }
+       case AMVIDEOCAP_IOR_SET_SRC_X: {
+               priv->src_rect.x = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOR_SET_SRC_Y: {
+               priv->src_rect.y = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOR_SET_SRC_WIDTH: {
+               priv->src_rect.width = arg;
+               break;
+       }
+       case AMVIDEOCAP_IOR_SET_SRC_HEIGHT: {
+               priv->src_rect.height = arg;
+               break;
+       }
+       default:
+               pr_err("unkonw cmd=%x\n", cmd);
+               ret = -1;
+       }
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long amvideocap_compat_ioctl(struct file *file,
+                               unsigned int cmd, ulong arg)
+{
+       return amvideocap_ioctl(file, cmd, (ulong)compat_ptr(arg));
+}
+#endif
+static int amvideocap_mmap(struct file *file,
+       struct vm_area_struct *vma)
+{
+       struct amvideocap_private *priv = file->private_data;
+       unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+       unsigned vm_size = vma->vm_end - vma->vm_start;
+       if (!priv->phyaddr)
+               return -EIO;
+
+       if (vm_size == 0)
+               return -EAGAIN;
+       /* pr_debug("mmap:%x\n",vm_size); */
+       off += priv->phyaddr;
+
+       vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
+       if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+                               vma->vm_end - vma->vm_start,
+                               vma->vm_page_prot)) {
+               pr_err("set_cached: failed remap_pfn_range\n");
+               return -EAGAIN;
+       }
+       pr_debug("amvideocap_mmap ok\n");
+       return 0;
+}
+
+static ssize_t amvideocap_read(struct file *file, char __user *buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct amvideocap_private *priv = file->private_data;
+       int waitdelay;
+       int ret = 0;
+       int copied;
+       loff_t pos;
+       pos = *ppos;
+       if (priv->wait_max_ms > 0)
+               waitdelay = priv->wait_max_ms;
+       else {
+               if (priv->want.at_flags == CAP_FLAG_AT_END)     /*need end */
+                       waitdelay = file->f_flags & O_NONBLOCK ? HZ : HZ * 100;
+               else
+                       waitdelay =
+                               file->f_flags & O_NONBLOCK ?
+                               HZ / 100 : HZ * 10;
+       }
+       if (!pos) {             /*trigger a new capture, */
+               pr_debug("start amvideocap_read waitdelay=%d\n", waitdelay);
+               ret = amvideocap_capture_one_frame_wait(priv, waitdelay);
+               pr_debug("amvideocap_read=%d,priv->state=%d,priv->vaddr=%p\n",
+                               ret, priv->state, priv->vaddr);
+               if ((ret == 0)
+                       && (priv->state == AMVIDEOCAP_STATE_FINISHED_CAPTURE)
+                       && (priv->vaddr != NULL)) {
+                       int size = min((int)count, (priv->out.byte_per_pix *
+                                               priv->out.width_aligned *
+                                               priv->out.height));
+                       pr_debug
+                       ("priv->out_width=%d priv->out_height=%d",
+                        priv->out.width, priv->out.height);
+                       pr_debug
+                       (" priv->outfmt_byteppix=%d, size=%d\n",
+                        priv->out.byte_per_pix, size);
+#ifdef CONFIG_CMA
+                       codec_mm_dma_flush(priv->vaddr,
+                                       cma_max_size * SZ_1M, DMA_FROM_DEVICE);
+#endif
+
+                       copied = copy_to_user(buf, priv->vaddr, size);
+                       if (copied) {
+                               pr_err
+                               ("amvideocap_read %d copy_to_user failed\n",
+                                size);
+                       }
+                       ret = size;
+               }
+       } else {
+               /*read from old capture. */
+               if (priv->state != AMVIDEOCAP_STATE_FINISHED_CAPTURE
+                       || priv->vaddr == NULL) {
+                       ret = 0;        /*end. */
+               } else {
+                       int maxsize =
+                               priv->out.byte_per_pix *
+                               priv->out.width_aligned * priv->out.height;
+                       if (pos < maxsize) {
+                               int rsize =
+                                       min((int)count, (maxsize - (int)pos));
+                               copied =
+                                       copy_to_user(buf, priv->vaddr + pos,
+                                       rsize);
+                               if (copied) {
+                                       pr_err
+                                       ("%s %d copy_to_user failed\n",
+                                        __func__, rsize);
+                               }
+                               ret = rsize;
+                       } else {
+                               ret = 0;        /*end. */
+                       }
+               }
+       }
+       if (ret > 0) {
+               pos += ret;
+               *ppos = pos;
+       }
+       return ret;
+}
+
+static const struct file_operations amvideocap_fops = {
+       .owner = THIS_MODULE,
+       .open = amvideocap_open,
+       .read = amvideocap_read,
+       .mmap = amvideocap_mmap,
+       .release = amvideocap_release,
+       .unlocked_ioctl = amvideocap_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = amvideocap_compat_ioctl,
+#endif
+       /* /  .poll     = amvideocap_poll, */
+};
+
+static ssize_t show_amvideocap_config(struct class *class,
+                                         struct class_attribute *attr,
+                                         char *buf)
+{
+       char *pbuf = buf;
+       ssize_t size = 0;
+       pbuf += sprintf(pbuf, "at_flags:%d\n", getgctrl()->want.at_flags);
+       pbuf += sprintf(pbuf, "timestampms:%lld\n",
+                                       getgctrl()->want.timestamp_ms);
+       pbuf += sprintf(pbuf, "width:%d\n", getgctrl()->want.width);
+       pbuf += sprintf(pbuf, "height:%d\n", getgctrl()->want.height);
+       pbuf += sprintf(pbuf, "format:%d\n", getgctrl()->want.fmt);
+       pbuf += sprintf(pbuf, "waitmaxms:%lld\n", getgctrl()->wait_max_ms);
+       size = pbuf - buf;
+       return size;
+}
+
+static ssize_t store_amvideocap_config(struct class *class,
+                                               struct class_attribute *attr,
+                                               const char *buf, size_t size)
+{
+       int ret, val;
+       const char *pbuf = buf;
+       for (; pbuf && pbuf[0] != '\0';) {
+#ifdef GETVAL
+#undef GETVAL
+#endif
+#define GETVAL(tag, v) do { \
+               val = 0;\
+               ret = sscanf(pbuf, tag ":%d", &val); \
+               if (ret == 1) { \
+                       v = val; \
+                       pbuf += strlen(tag); \
+                       goto tonext; \
+               }; \
+       } while (0)
+               GETVAL("timestamp", getgctrl()->want.timestamp_ms);
+               GETVAL("width", getgctrl()->want.width);
+               GETVAL("height", getgctrl()->want.height);
+               GETVAL("format", getgctrl()->want.fmt);
+               GETVAL("waitmaxms", getgctrl()->wait_max_ms);
+               GETVAL("at_flags", getgctrl()->want.at_flags);
+#undef GETVAL
+               pbuf++;
+tonext:
+               while (pbuf[0] != ';' && pbuf[0] != '\0')
+                       pbuf++;
+               if (pbuf[0] == ';')
+                       pbuf++;
+       }
+       return size;
+}
+
+static struct class_attribute amvideocap_class_attrs[] = {
+       __ATTR(config, S_IRUGO | S_IWUSR | S_IWGRP, show_amvideocap_config,
+       store_amvideocap_config),
+       __ATTR_NULL
+};
+
+static struct class amvideocap_class = {
+               .name = MODULE_NAME,
+               .class_attrs = amvideocap_class_attrs,
+       };
+
+s32 amvideocap_register_memory(unsigned char *phybufaddr,
+                                       int phybufsize)
+{
+       pr_debug("amvideocap_register_memory %p %d\n", phybufaddr, phybufsize);
+       getgctrl()->phyaddr = (unsigned long)phybufaddr;
+       getgctrl()->size = (unsigned long)phybufsize;
+       getgctrl()->vaddr = 0;
+       return 0;
+}
+
+s32 amvideocap_dev_register(unsigned char *phybufaddr, int phybufsize)
+{
+       s32 r = 0;
+       pr_debug("amvideocap_dev_register\n");
+
+       gLOCKINIT();
+       r = register_chrdev(0, DEVICE_NAME, &amvideocap_fops);
+       if (r < 0) {
+               pr_err("Can't register major for amvideocap device\n");
+               return r;
+       }
+       getgctrl()->major = r;
+       r = class_register(&amvideocap_class);
+       if (r) {
+               pr_err("amvideocap class create fail.\n");
+               goto err1;
+       }
+       getgctrl()->class = &amvideocap_class;
+       getgctrl()->dev = device_create(getgctrl()->class,
+                                       NULL, MKDEV(getgctrl()->major, 0),
+                                       NULL, DEVICE_NAME "0");
+       if (getgctrl()->dev == NULL) {
+               pr_err("amvideocap device_create fail.\n");
+               r = -EEXIST;
+               goto err2;
+       }
+       if (phybufaddr != NULL) {
+               getgctrl()->phyaddr = (unsigned long)phybufaddr;
+               getgctrl()->size = (unsigned long)phybufsize;
+       }
+       getgctrl()->wait_max_ms = 0;
+       getgctrl()->want.fmt = GE2D_FORMAT_S24_RGB;
+       getgctrl()->want.width = 0;
+       getgctrl()->want.height = 0;
+       getgctrl()->want.timestamp_ms = 0;
+       /*get last frame */
+       getgctrl()->want.at_flags = CAP_FLAG_AT_CURRENT;
+       return 0;
+err2:
+       class_unregister(&amvideocap_class);
+err1:
+       unregister_chrdev(getgctrl()->major, DEVICE_NAME);
+       return r;
+}
+
+s32 amvideocap_dev_unregister(void)
+{
+       device_destroy(getgctrl()->class, MKDEV(getgctrl()->major, 0));
+       class_unregister(getgctrl()->class);
+       unregister_chrdev(getgctrl()->major, DEVICE_NAME);
+
+       return 0;
+}
+
+/*******************************************************************
+ *
+ * interface for Linux driver
+ *
+ * ******************************************************************/
+
+static struct resource memobj;
+/* for driver. */
+static int amvideocap_probe(struct platform_device *pdev)
+{
+       unsigned int buf_size;
+       struct resource *mem;
+
+#ifdef CONFIG_CMA
+       char buf[32];
+       u32 value;
+       int ret;
+#endif
+
+       pr_err("amvideocap_probe,%s\n", pdev->dev.of_node->name);
+
+#ifdef CONFIG_CMA
+       snprintf(buf, sizeof(buf), "max_size");
+       ret = of_property_read_u32(pdev->dev.of_node, buf, &value);
+       if (ret < 0) {
+               pr_err("cma size undefined.\n");
+               use_cma = 0;
+       } else {
+               pr_err("use cma buf.\n");
+               mem = &memobj;
+               mem->start = 0;
+               buf_size = 0;
+               cma_max_size = value;
+               amvideocap_pdev = pdev;
+               use_cma = 1;
+       }
+#endif
+
+       amvideocap_dev_register((unsigned char *)mem->start, buf_size);
+       return 0;
+}
+
+static int amvideocap_remove(struct platform_device *plat_dev)
+{
+       /* struct rtc_device *rtc = platform_get_drvdata(plat_dev); */
+       /* rtc_device_unregister(rtc); */
+       /* device_remove_file(&plat_dev->dev, &dev_attr_irq); */
+       amvideocap_dev_unregister();
+       return 0;
+}
+
+
+static const struct of_device_id amlogic_amvideocap_dt_match[] = {
+       {
+               .compatible = "amlogic, amvideocap",
+       },
+       {},
+};
+
+/* general interface for a linux driver .*/
+struct platform_driver amvideocap_drv = {
+       .probe = amvideocap_probe,
+       .remove = amvideocap_remove,
+       .driver = {
+               .name = "amvideocap",
+               .of_match_table = amlogic_amvideocap_dt_match,
+       }
+};
+
+static int __init amvideocap_init_module(void)
+{
+
+       pr_debug("amvideocap_init_module\n");
+       if (ge2d_amvideocap_context == NULL)
+               ge2d_amvideocap_context = create_ge2d_work_queue();
+       if ((platform_driver_register(&amvideocap_drv))) {
+               pr_err("failed to register amstream module\n");
+               return -ENODEV;
+       }
+       return 0;
+
+}
+
+static void __exit amvideocap_remove_module(void)
+{
+       platform_driver_unregister(&amvideocap_drv);
+       if (ge2d_amvideocap_context) {
+               destroy_ge2d_work_queue(ge2d_amvideocap_context);
+               ge2d_amvideocap_context = NULL;
+       }
+       pr_debug("amvideocap module removed.\n");
+}
+
+module_init(amvideocap_init_module);
+module_exit(amvideocap_remove_module);
+
+MODULE_DESCRIPTION("AMLOGIC  amvideocap driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wang Jian <jian.wang@amlogic.com>");
diff --git a/drivers/amlogic/media/video_sink/amvideocap.h b/drivers/amlogic/media/video_sink/amvideocap.h
new file mode 100644 (file)
index 0000000..4712b50
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * drivers/amlogic/media/video_sink/amvideocap.h
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+*/
+
+#ifndef __AMVIDEOCAP_HEADHER_
+#define __AMVIDEOCAP_HEADHER_
+#define AC_MAGIC  'V'
+#include <linux/videodev2.h>
+
+#define CAP_FLAG_AT_CURRENT            0
+#define CAP_FLAG_AT_TIME_WINDOW        1
+#define CAP_FLAG_AT_END                        2
+
+/*
+format see linux/ge2d/ge2d.h
+like:
+GE2D_FORMAT_S24_RGB
+*/
+
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_FORMAT    _IOW((AC_MAGIC), 0x01, int)
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH     _IOW((AC_MAGIC), 0x02, int)
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT    _IOW((AC_MAGIC), 0x03, int)
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_TIMESTAMP_MS _IOW((AC_MAGIC), 0x04, u64)
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS _IOW((AC_MAGIC), 0x05, u64)
+#define AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS  _IOW((AC_MAGIC), 0x06, int)
+
+#define AMVIDEOCAP_IOR_GET_FRAME_FORMAT        _IOR((AC_MAGIC), 0x10, int)
+#define AMVIDEOCAP_IOR_GET_FRAME_WIDTH _IOR((AC_MAGIC), 0x11, int)
+#define AMVIDEOCAP_IOR_GET_FRAME_HEIGHT        _IOR((AC_MAGIC), 0x12, int)
+#define AMVIDEOCAP_IOR_GET_FRAME_TIMESTAMP_MS  _IOR((AC_MAGIC), 0x13, int)
+
+#define AMVIDEOCAP_IOR_GET_SRCFRAME_FORMAT     _IOR((AC_MAGIC), 0x20, int)
+#define AMVIDEOCAP_IOR_GET_SRCFRAME_WIDTH      _IOR((AC_MAGIC), 0x21, int)
+#define AMVIDEOCAP_IOR_GET_SRCFRAME_HEIGHT     _IOR((AC_MAGIC), 0x22, int)
+
+#define AMVIDEOCAP_IOR_GET_STATE       _IOR((AC_MAGIC), 0x31, int)
+#define AMVIDEOCAP_IOW_SET_START_CAPTURE       _IOW((AC_MAGIC), 0x32, int)
+#define AMVIDEOCAP_IOW_SET_CANCEL_CAPTURE      _IOW((AC_MAGIC), 0x33, int)
+
+#define AMVIDEOCAP_IOR_SET_SRC_X _IOR((AC_MAGIC), 0x40, int)
+#define AMVIDEOCAP_IOR_SET_SRC_Y _IOR((AC_MAGIC), 0x41, int)
+#define AMVIDEOCAP_IOR_SET_SRC_WIDTH _IOR((AC_MAGIC), 0x42, int)
+#define AMVIDEOCAP_IOR_SET_SRC_HEIGHT _IOR((AC_MAGIC), 0x43, int)
+
+enum amvideocap_state {
+       AMVIDEOCAP_STATE_INIT = 0,
+       AMVIDEOCAP_STATE_ON_CAPTURE = 200,
+       AMVIDEOCAP_STATE_FINISHED_CAPTURE = 300,
+       AMVIDEOCAP_STATE_ERROR = 0xffff,
+};
+#endif                         /* __AMVIDEOCAP_HEADHER_ */
index 40b2fb2..3667ff0 100644 (file)
@@ -57,7 +57,7 @@ struct amvideocap_req_data {
 };
 
 struct amvideocap_req {
-       int (*callback)(unsigned long data, struct vframe_s *vfput,
+       int (*callback)(struct amvideocap_private *privdata, struct vframe_s *vfput,
                                        int index);
        unsigned long data;
        int at_flags;           /*AT_ */
index 2b7bc8a..8b5ea3d 100644 (file)
@@ -273,6 +273,16 @@ static u32 frame_detect_drop_count;
 #define BRIDGE_IRQ_SET() WRITE_CBUS_REG(ISA_TIMERC, 1)
 #endif
 
+#ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
+enum video_capture_state {
+       CAPTURE_STATE_OFF = 0,
+       CAPTURE_STATE_ON = 1,
+       CAPTURE_STATE_CAPTURE = 2,
+};
+
+atomic_t capture_use_cnt = ATOMIC_INIT(CAPTURE_STATE_OFF);
+#endif
+
 #define VD1_MEM_POWER_ON() \
        do { \
                unsigned long flags; \
@@ -330,8 +340,16 @@ static u32 frame_detect_drop_count;
                unsigned long flags; \
                spin_lock_irqsave(&video_onoff_lock, flags); \
                video_onoff_state = VIDEO_ENABLE_STATE_OFF_REQ; \
-               video_enabled = 0; \
-               video_status_saved = 0; \
+               video_enabled = 0;\
+               video_status_saved = 0;\
+               atomic_set(&capture_use_cnt, CAPTURE_STATE_OFF); \
+               if (capture_frame_req && capture_frame_req->data) { \
+                       struct amvideocap_req_data *reqdata = \
+                               (struct amvideocap_req_data *)capture_frame_req->data; \
+                       if (reqdata && reqdata->privdata) \
+                               reqdata->privdata->state = 0xffff; \
+               } \
+               capture_frame_req = NULL; \
                spin_unlock_irqrestore(&video_onoff_lock, flags); \
        } while (0)
 
@@ -624,7 +642,7 @@ bool is_dolby_vision_stb_mode(void)
 }
 #endif
 
-static struct amvideocap_req *capture_frame_req;
+static struct amvideocap_req *capture_frame_req = NULL;
 u32 get_video_angle(void)
 {
        return glayer_info[0].angle;
@@ -1184,10 +1202,6 @@ static inline struct vframe_s *pip_vf_get(void)
 
        vf = vf_get(RECEIVERPIP_NAME);
 
-       if (vf) {
-               /* video_notify_flag |= VIDEO_NOTIFY_PROVIDER_GET; */
-               atomic_set(&vf->use_cnt, 1);
-       }
        return vf;
 
 }
@@ -1212,7 +1226,7 @@ static inline void pip_vf_put(struct vframe_s *vf)
        if (pip_loop)
                return;
 
-       if (vfp && vf && atomic_dec_and_test(&vf->use_cnt)) {
+       if (vfp && vf) {
                vf_put(vf, RECEIVERPIP_NAME);
                /* video_notify_flag |= VIDEO_NOTIFY_PROVIDER_PUT; */
        }
@@ -1246,8 +1260,6 @@ static inline struct vframe_s *video_vf_get(void)
                        vf->disp_pts_us64 = 0;
                }
                video_notify_flag |= VIDEO_NOTIFY_PROVIDER_GET;
-               atomic_set(&vf->use_cnt, 1);
-               /*always to 1,for first get from vfm provider */
                if ((vf->type & VIDTYPE_MVC) && (framepacking_support)
                &&(framepacking_width) && (framepacking_height)) {
                        vf->width = framepacking_width;
@@ -1317,9 +1329,7 @@ static int video_vf_get_states(struct vframe_states *states)
 
 static inline void video_vf_put(struct vframe_s *vf)
 {
-       struct vframe_provider_s *vfp = vf_get_provider(RECEIVER_NAME);
-
-       if (vfp && vf && atomic_dec_and_test(&vf->use_cnt)) {
+       if (vf) {
                vf_put(vf, RECEIVER_NAME);
 #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
                if (is_dolby_vision_enable())
@@ -1329,15 +1339,6 @@ static inline void video_vf_put(struct vframe_s *vf)
        }
 }
 
-int ext_get_cur_video_frame(struct vframe_s **vf, int *canvas_index)
-{
-       if (cur_dispbuf == NULL)
-               return -1;
-       atomic_inc(&cur_dispbuf->use_cnt);
-       *canvas_index = READ_VCBUS_REG(VD1_IF0_CANVAS0 + cur_dev->viu_off);
-       *vf = cur_dispbuf;
-       return 0;
-}
 static void dump_vframe_status(const char *name)
 {
        int ret = -1;
@@ -1371,12 +1372,17 @@ static void dump_vdin_reg(void)
 }
 
 #ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
+int ext_get_cur_video_frame(struct vframe_s **vf, int *canvas_index)
+{
+       if (cur_dispbuf == NULL)
+               return -1;
+       *canvas_index = READ_VCBUS_REG(VD1_IF0_CANVAS0 + cur_dev->viu_off);
+       *vf = cur_dispbuf;
+       return 0;
+}
 
 int ext_put_video_frame(struct vframe_s *vf)
 {
-       if (vf == &vf_local)
-               return 0;
-       video_vf_put(vf);
        return 0;
 }
 
@@ -1393,28 +1399,49 @@ static int is_need_framepacking_output(void)
 
 int ext_register_end_frame_callback(struct amvideocap_req *req)
 {
-       mutex_lock(&video_module_mutex);
-       capture_frame_req = req;
-       mutex_unlock(&video_module_mutex);
-       return 0;
+       int ret = -EAGAIN;
+
+       if (!req) {
+               atomic_set(&capture_use_cnt, CAPTURE_STATE_ON);
+               ret = 0;
+       }
+       else if (atomic_read(&capture_use_cnt) == CAPTURE_STATE_ON && req)
+       {
+               capture_frame_req = req;
+               atomic_set(&capture_use_cnt, CAPTURE_STATE_CAPTURE);
+               ret = 0;
+       }
+       else if ( atomic_read(&capture_use_cnt) == CAPTURE_STATE_OFF)
+               ret = -ENODATA;
+
+       return ret;
 }
-int ext_frame_capture_poll(int endflags)
+
+int ext_frame_capture_poll(struct vframe_s *vf)
 {
-       mutex_lock(&video_module_mutex);
-       if (capture_frame_req && capture_frame_req->callback) {
-               struct vframe_s *vf;
-               int index;
-               int ret;
-               struct amvideocap_req *req = capture_frame_req;
-
-               ret = ext_get_cur_video_frame(&vf, &index);
-               if (!ret) {
-                       req->callback(req->data, vf, index);
+       int ret = -EAGAIN;
+       static int capture_frame_toggle = 0;
+
+       if (capture_frame_toggle == 0 && vf) {
+               if (vf->duration > 0 && (96000 / vf->duration) > 30)
+                       capture_frame_toggle = 1;
+
+               if (atomic_read(&capture_use_cnt) == CAPTURE_STATE_CAPTURE && capture_frame_req && capture_frame_req->callback) {
+                       struct amvideocap_req_data *reqdata =
+                               (struct amvideocap_req_data *)capture_frame_req->data;
+                       int index = READ_VCBUS_REG(VD1_IF0_CANVAS0 + cur_dev->viu_off);
+
+                       if (reqdata && reqdata->privdata)
+                               ret = capture_frame_req->callback(reqdata->privdata, vf, index);
+
                        capture_frame_req = NULL;
+                       atomic_set(&capture_use_cnt, CAPTURE_STATE_ON);
                }
        }
-       mutex_unlock(&video_module_mutex);
-       return 0;
+       else
+               capture_frame_toggle = 0;
+
+       return ret;
 }
 #endif
 
@@ -3987,6 +4014,9 @@ static void vsync_toggle_frame(struct vframe_s *vf)
        else
                last_mvc_status = false;
 
+#ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
+       ext_frame_capture_poll(cur_dispbuf);
+#endif
        if (first_picture) {
                frame_par_ready_to_set = 1;
                first_frame_toggled = 1;
index 1c85320..262b93d 100644 (file)
@@ -920,10 +920,6 @@ static unsigned int vf_keep_current_locked(
                return 0;
        }
 
-#ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
-       ext_frame_capture_poll(1); /*pull  if have capture end frame */
-#endif
-
        if (get_blackout_policy()) {
                pr_info("keep exit is skip current\n");
                return 0;
@@ -1156,10 +1152,6 @@ unsigned int vf_keep_pip_current_locked(
                return 0;
        }
 
-#ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
-       ext_frame_capture_poll(1); /*pull  if have capture end frame */
-#endif
-
        if (get_blackout_pip_policy()) {
                pr_info("policy: keep exit is skip current\n");
                return 0;
index 34e19ee..bbaa040 100644 (file)
@@ -152,7 +152,7 @@ u32 get_videopip_enabled(void);
 struct device *get_video_device(void);
 
 #ifdef CONFIG_AMLOGIC_MEDIA_VIDEOCAPTURE
-int ext_frame_capture_poll(int endflags);
+int ext_frame_capture_poll(struct vframe_s *vf);
 #endif
 
 extern u32 disp_canvas_index[2][6];
index ac48b9c..c4dcf1e 100644 (file)
@@ -78,7 +78,7 @@ void video_vf_put(char *receiver, struct vdec_fb *fb, int id)
        aml_v4l2_debug(2, "[%d] TO   (%s) vf: %p, idx: %d",
                id, vfp->name, vf, vf->index);
 
-       if (vfp && vf && atomic_dec_and_test(&vf->use_cnt))
+       if (vfp && vf)
                vf_put(vf, receiver);
 }
 
index 324d30e..0790ea9 100644 (file)
@@ -545,8 +545,6 @@ static void vdec_h264_get_vf(struct vdec_h264_inst *inst, struct vdec_fb **out)
                return;
        }
 
-       atomic_set(&vf->use_cnt, 1);
-
        aml_vcodec_debug(inst, "%s() [%d], vf: %p, v4l_mem_handle: %lx, idx: %d\n",
                __func__, __LINE__, vf, vf->v4l_mem_handle, vf->index);
 
index 2a62b67..7cc65e1 100644 (file)
@@ -389,7 +389,6 @@ struct vframe_s {
        u64 ready_jiffies64;    /* ready from decode on  jiffies_64 */
        long long ready_clock[5];/*ns*/
        long long ready_clock_hist[2];/*ns*/
-       atomic_t use_cnt;
        u32 frame_dirty;
        /*
         *prog_proc_config: