// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * drivers/amlogic/media/video_processor/ppmgr/ppmgr_vpp.c
 *
 * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
/*#include <mach/am_regs.h>*/
#ifdef CONFIG_AMLOGIC_MEDIA_FRAME_SYNC
#include <linux/amlogic/media/frame_sync/timestamp.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#endif
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vfm/vframe.h>
/*#include <linux/amlogic/amports/vfp.h>*/
#include "vfp.h"
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/utils/amlog.h>
/* media module used media/registers/cpu_version.h since kernel 5.4 */
#include <linux/amlogic/media/registers/cpu_version.h>
/*#include <linux/amlogic/ge2d/ge2d_main.h>*/
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/kthread.h>
#include <linux/delay.h>
//#include <linux/semaphore.h>
#include <linux/sched/rt.h>
#include "ppmgr_log.h"
#include "ppmgr_pri.h"
#include "ppmgr_dev.h"
#include <linux/mm.h>
#include <linux/amlogic/media/ppmgr/ppmgr.h>
#include <linux/amlogic/media/ppmgr/ppmgr_status.h>
/*#include "../amports/video.h"*/
#ifdef CONFIG_AMLOGIC_MEDIA_VIDEO
#include <linux/amlogic/media/video_sink/video.h>
#endif
/*#include "../amports/vdec_reg.h"*/
#include <linux/amlogic/media/utils/vdec_reg.h>
/*#include "../display/osd/osd_reg.h"*/
#include "../../osd/osd_reg.h"
#include "../../common/vfm/vfm.h"
#include "ppmgr_vpp.h"
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#endif
#include <linux/dma-mapping.h>
#include <linux/dma-map-ops.h>
#include <linux/amlogic/media/ppmgr/tbff.h>
#include <uapi/linux/sched/types.h>
/*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
/*#include <mach/mod_gate.h>*/
/*#endif*/

#define PPMGRVPP_INFO(fmt, args...) pr_info("PPMGRVPP: info: " fmt " ", ## args)
#define PPMGRVPP_DBG(fmt, args...) pr_debug("PPMGRVPP: dbg: " fmt " ", ## args)
#define PPMGRVPP_WARN(fmt, args...) pr_warn("PPMGRVPP: warn: " fmt " ", ## args)
#define PPMGRVPP_ERR(fmt, args...) pr_err("PPMGRVPP: err: " fmt " ", ## args)

#define VF_POOL_SIZE 10

#define PPMGR_TB_DETECT
/* #define CANVAS_RESERVED */

#define RECEIVER_NAME "ppmgr"
#define PROVIDER_NAME   "ppmgr"

#define MM_ALLOC_SIZE (SZ_16M + SZ_4M + SZ_2M)
#define MAX_WIDTH  1024
#define MAX_HEIGHT 576
#define THREAD_INTERRUPT 0
#define THREAD_RUNNING 1
#define INTERLACE_DROP_MODE 1
#define enableVideoLayer() \
		SET_MPEG_REG_MASK(VPP_MISC, \
				  VPP_VD1_PREBLEND \
				  | VPP_PREBLEND_EN \
				  | VPP_VD1_POSTBLEND)

#define disableVideoLayer() \
		CLEAR_MPEG_REG_MASK(VPP_MISC, \
				    VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND)

#define disableVideoLayer_PREBELEND() \
		CLEAR_MPEG_REG_MASK(VPP_MISC, \
				    VPP_VD1_PREBLEND)

static int ass_index = -1;

static DEFINE_SPINLOCK(lock);
static bool ppmgr_blocking;
static bool ppmgr_inited;
static int ppmgr_buffer_status;
static struct ppframe_s vfp_pool[VF_POOL_SIZE];
static struct vframe_s *vfp_pool_free[VF_POOL_SIZE + 1];
static struct vframe_s *vfp_pool_ready[VF_POOL_SIZE + 1];
struct buf_status_s {
	int index;
	int dirty;
};

static struct buf_status_s buf_status[VF_POOL_SIZE];

struct vfq_s q_ready, q_free;
//static struct semaphore thread_sem;
static DEFINE_MUTEX(ppmgr_mutex);
static bool ppmgr_quit_flag;

#ifdef PPMGR_TB_DETECT
#define TB_DETECT_BUFFER_MAX_SIZE 16
#define TB_DETECT_W 128
#define TB_DETECT_H 96

struct tb_buf_s {
	ulong vaddr;
	ulong paddr;
};

enum tb_status {
	tb_idle,
	tb_running,
	tb_done,
};

static DEFINE_MUTEX(tb_mutex);

static struct tb_buf_s detect_buf[TB_DETECT_BUFFER_MAX_SIZE];
static struct task_struct *tb_detect_task;
static int tb_task_running;
//static struct semaphore tb_sem;
static atomic_t detect_status;
static atomic_t tb_detect_flag;
static u8 tb_last_flag;
static u32 tb_buff_wptr;
static u32 tb_buff_rptr;
static s8 tb_buffer_status;
static u32 tb_buffer_start;
static u32 tb_buffer_size;
static u8 tb_first_frame_type;
static u32 tb_buffer_len = TB_DETECT_BUFFER_MAX_SIZE;
static atomic_t tb_reset_flag;
static u32 tb_init_mute;
static atomic_t tb_skip_flag;
static atomic_t tb_run_flag;
static bool tb_quit_flag;
static struct TB_DetectFuncPtr *gfunc;
static int tb_buffer_init(void);
static int tb_buffer_uninit(void);
#endif
static s32 ppmgr_src_canvas[3] = {-1, -1, -1};
static s32 ppmgr_dst_canvas[2] = {-1, -1};
static s8 local_canvas_status;
static bool need_data_notify;
static int dumpfirstframe;
static int count_scr;
static int count_dst;

static unsigned int ppmgr_color_format;
MODULE_PARM_DESC(ppmgr_color_format, "\n ppmgr_color_format\n");
module_param(ppmgr_color_format, uint, 0664);

const struct vframe_receiver_op_s *vf_ppmgr_reg_provider(void);

/* void vf_ppmgr_unreg_provider(void);
 * void vf_ppmgr_reset(int type);
 * void ppmgr_vf_put_dec(struct vframe_s *vf);
 */

/* extern u32 timestamp_pcrscr_enable_state(void); */

bool is_valid_ppframe(struct ppframe_s *pp_vf)
{
	return ((pp_vf >= &vfp_pool[0]) && (pp_vf <= &vfp_pool[VF_POOL_SIZE - 1]));
}

/* ***********************************************
 *
 *   Canvas helpers.
 *
 * ************************************************
 */

#include "decontour.h"

#define DCNTR_POOL_SIZE VF_POOL_SIZE

static s8 ppmgr_check_tvp_state(void)
{
	struct provider_state_req_s req;
	s8 ret = -1;
	char *provider_name = vf_get_provider_name(PROVIDER_NAME);

	while (provider_name) {
		if (!vf_get_provider_name(provider_name))
			break;
		provider_name =
			vf_get_provider_name(provider_name);
	}
	if (provider_name) {
		req.vf = NULL;
		req.req_type = REQ_STATE_SECURE;
		req.req_result[0] = 0xffffffff;
		vf_notify_provider_by_name(provider_name,
			VFRAME_EVENT_RECEIVER_REQ_STATE,
			(void *)&req);
		if (req.req_result[0] == 0)
			ret = 0;
		else if (req.req_result[0] != 0xffffffff)
			ret = 1;
	}
	return ret;
}

#define Rd(adr) aml_read_vcbus(adr)
#define Wr(adr, val) aml_write_vcbus(adr, val)

struct dcntr_mem_s dcntr_mem_info[DCNTR_POOL_SIZE];
struct dcntr_mem_s dcntr_mem_info_last;

static struct completion isr_done;
static u32 last_w;
static u32 last_h;
static u32 last_type;

#define DCT_PRINT_INFO       0X1
#define DCT_BYPASS           0X2
#define DCT_USE_DS_SCALE     0X4
#define DCT_DUMP_REG         0X8
#define DCT_PROCESS_I        0X10
#define DCT_REWRITE_REG      0X20

void wr_bits(unsigned int adr,
	unsigned int val,
	unsigned int start,
	unsigned int len)
{
	unsigned int mask = ((1 << len) - 1) << start;

	return aml_vcbus_update_bits(adr, mask, val << start);
}

int dc_print(const char *fmt, ...)
{
	if (ppmgr_device.debug_decontour & DCT_PRINT_INFO) {
		char buf[256];
		int len = 0;
		va_list args;

		va_start(args, fmt);
		len = sprintf(buf, "dc:[%d]", 0);
		vsnprintf(buf + len, 256 - len, fmt, args);
		pr_info("%s", buf);
		va_end(args);
	}
	return 0;
}

static bool is_decontour_supported(void)
{
	bool ret = false;

	if (ppmgr_device.reg_dct_irq_success)
		ret = true;

	return ret;
}

static void decontour_init(void)
{
	int mem_flags;
	int grd_size = SZ_512K;             /*(81*8*16 byte)*45 = 455K*/
	int yds_size = SZ_512K + SZ_32K;    /*960 * 576 = 540K*/
	int cds_size = yds_size / 2;        /*960 * 576 / 2= 270K*/
	int total_size = grd_size + yds_size + cds_size;
	int buffer_count = DCNTR_POOL_SIZE;
	int i;
	bool is_tvp = false;

	if (ppmgr_device.decontour_addr)
		return;

	mem_flags = CODEC_MM_FLAGS_DMA | CODEC_MM_FLAGS_CMA_CLEAR;
	if (ppmgr_check_tvp_state() == 1) {
		mem_flags |= CODEC_MM_FLAGS_TVP;
		is_tvp = true;
	}
	ppmgr_device.decontour_addr = codec_mm_alloc_for_dma("ppmgr-decontour",
				buffer_count * total_size / PAGE_SIZE, 0, mem_flags);
	if (ppmgr_device.decontour_addr == 0)
		pr_err("decontour: alloc fail\n");

	dc_print("decontour:alloc success %d, is_tvp=%d\n",
		 ppmgr_device.decontour_addr, is_tvp);

	for (i = 0; i < DCNTR_POOL_SIZE; i++) {
		dcntr_mem_info[i].index = i;
		dcntr_mem_info[i].grd_addr = 0;
		dcntr_mem_info[i].yds_addr = 0;
		dcntr_mem_info[i].cds_addr = 0;
		dcntr_mem_info[i].grd_size = 0;
		dcntr_mem_info[i].yds_size = 0;
		dcntr_mem_info[i].cds_size = 0;
		dcntr_mem_info[i].yds_canvas_mode = 0;
		dcntr_mem_info[i].yds_canvas_mode = 0;
		dcntr_mem_info[i].ds_ratio = 0;
		dcntr_mem_info[i].free = false;
		dcntr_mem_info[i].use_org = true;
		dcntr_mem_info[i].pre_out_fmt = VIDTYPE_VIU_NV21;
		dcntr_mem_info[i].grd_swap_64bit = false;
		dcntr_mem_info[i].yds_swap_64bit = false;
		dcntr_mem_info[i].cds_swap_64bit = false;
		dcntr_mem_info[i].grd_little_endian = true;
		dcntr_mem_info[i].yds_little_endian = true;
		dcntr_mem_info[i].cds_little_endian = true;
	}

	for (i = 0; i < buffer_count; i++) {
		dcntr_mem_info[i].grd_addr = ppmgr_device.decontour_addr + i * total_size;
		dcntr_mem_info[i].yds_addr = dcntr_mem_info[i].grd_addr + grd_size;
		dcntr_mem_info[i].cds_addr = dcntr_mem_info[i].yds_addr + yds_size;
		dcntr_mem_info[i].grd_size = grd_size;
		dcntr_mem_info[i].yds_size = yds_size;
		dcntr_mem_info[i].cds_size = cds_size;
		dcntr_mem_info[i].free = true;

		dc_print("i=%d, grd_addr=%lx, yds_addr=%lx,cds_addr=%lx, %d, %d, %d\n",
			i,
			dcntr_mem_info[i].grd_addr,
			dcntr_mem_info[i].yds_addr,
			dcntr_mem_info[i].cds_addr,
			grd_size,
			yds_size,
			cds_size);
	}
	init_completion(&isr_done);
	last_w = 0;
	last_h = 0;
	last_type = 0;
	dc_print("decontour: init\n");
}

static void decontour_buf_reset(void)
{
	int i;

	if (ppmgr_device.decontour_addr == 0)
		return;

	pr_info("decontour buf reset\n");
	for (i = 0; i < DCNTR_POOL_SIZE; i++)
		dcntr_mem_info[i].free = true;
}

static void decontour_uninit(void)
{
	if (ppmgr_device.decontour_addr)
		codec_mm_free_for_dma("ppmgr-decontour",
			ppmgr_device.decontour_addr);
	ppmgr_device.decontour_addr = 0;
	dc_print("decontour: uninit\n");
}

irqreturn_t decontour_pre_isr(int irq, void *dev_id)
{
	complete(&isr_done);
	dc_print("decontour: isr\n");
	return IRQ_HANDLED;
}

static void decontour_dump_reg(void)
{
	u32 value;
	int i;

	for (i = 0x4a00; i < 0x4a12; i++) {
		value = Rd(i);
		pr_info("reg=%x, value= %x\n", i, value);
	}
	value = Rd(DCNTR_GRD_WMIF_CTRL1);
	dc_print("cds: DCNTR_GRD_WMIF_CTRL1: %x\n", value);
	value = Rd(DCNTR_GRD_WMIF_CTRL3);
	dc_print("cds: DCNTR_GRD_WMIF_CTRL3: %x\n", value);
	value = Rd(DCNTR_GRD_WMIF_CTRL4);
	dc_print("cds: DCNTR_GRD_WMIF_CTRL4: %x\n", value);
	value = Rd(DCNTR_GRD_WMIF_SCOPE_X);
	dc_print("cds: DCNTR_GRD_WMIF_SCOPE_X: %x\n", value);
	value = Rd(DCNTR_GRD_WMIF_SCOPE_Y);
	dc_print("cds: DCNTR_GRD_WMIF_SCOPE_Y: %x\n", value);

	value = Rd(DCNTR_YDS_WMIF_CTRL1);
	dc_print("grd: DCNTR_YDS_WMIF_CTRL1: %x\n", value);
	value = Rd(DCNTR_YDS_WMIF_CTRL3);
	dc_print("grd: DCNTR_YDS_WMIF_CTRL3: %x\n", value);
	value = Rd(DCNTR_YDS_WMIF_CTRL4);
	dc_print("grd: DCNTR_YDS_WMIF_CTRL4: %x\n", value);
	value = Rd(DCNTR_YDS_WMIF_SCOPE_X);
	dc_print("grd: DCNTR_YDS_WMIF_SCOPE_X: %x\n", value);
	value = Rd(DCNTR_YDS_WMIF_SCOPE_Y);
	dc_print("grd: DCNTR_YDS_WMIF_SCOPE_Y: %x\n", value);

	value = Rd(DCNTR_CDS_WMIF_CTRL1);
	dc_print("yds: DCNTR_CDS_WMIF_CTRL1: %x\n", value);
	value = Rd(DCNTR_CDS_WMIF_CTRL3);
	dc_print("yds: DCNTR_CDS_WMIF_CTRL3: %x\n", value);
	value = Rd(DCNTR_CDS_WMIF_CTRL4);
	dc_print("yds: DCNTR_CDS_WMIF_CTRL4: %x\n", value);
	value = Rd(DCNTR_CDS_WMIF_SCOPE_X);
	dc_print("yds: DCNTR_CDS_WMIF_SCOPE_X: %x\n", value);
	value = Rd(DCNTR_CDS_WMIF_SCOPE_Y);
	dc_print("yds: DCNTR_CDS_WMIF_SCOPE_Y: %x\n", value);

	value = Rd(DCNTR_PRE_ARB_MODE);
	dc_print("DCNTR_PRE_ARB_MODE: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_REQEN_SLV);
	dc_print("DCNTR_PRE_ARB_REQEN_SLV: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_WEIGH0_SLV);
	dc_print("DCNTR_PRE_ARB_WEIGH0_SLV: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_WEIGH1_SLV);
	dc_print("DCNTR_PRE_ARB_WEIGH1_SLV: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_UGT);
	dc_print("DCNTR_PRE_ARB_UGT: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_LIMT0);
	dc_print("DCNTR_PRE_ARB_LIMT0: %x\n", value);

	value = Rd(DCNTR_PRE_ARB_STATUS);
	dc_print("DCNTR_PRE_ARB_STATUS: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_DBG_CTRL);
	dc_print("DCNTR_PRE_ARB_DBG_CTRL: %x\n", value);
	value = Rd(DCNTR_PRE_ARB_PROT);
	dc_print("DCNTR_PRE_ARB_PROT: %x\n", value);

	value = Rd(DCTR_BGRID_TOP_FSIZE);
	dc_print("DCTR_BGRID_TOP_FSIZE: %x\n", value);
	value = Rd(DCTR_BGRID_TOP_HDNUM);
	dc_print("DCTR_BGRID_TOP_HDNUM: %x\n", value);
	value = Rd(DCTR_BGRID_TOP_CTRL0);
	dc_print("DCTR_BGRID_TOP_CTRL0: %x\n", value);
	value = Rd(DCTR_BGRID_TOP_FMT);
	dc_print("DCTR_BGRID_TOP_FMT: %x\n", value);
	value = Rd(DCTR_BGRID_TOP_GCLK);
	dc_print("DCTR_BGRID_TOP_GCLK: %x\n", value);
	value = Rd(DCTR_BGRID_TOP_HOLD);
	dc_print("DCTR_BGRID_TOP_HOLD: %x\n", value);

	value = Rd(DCNTR_GRID_GEN_REG); //grd_num_use
	dc_print("decontour: DCNTR_GRID_GEN_REG=%x\n", value);
	value = Rd(DCNTR_GRID_GEN_REG2); //grd_num_use
	dc_print("decontour: DCNTR_GRID_GEN_REG2=%x\n", value);
	value = Rd(DCNTR_GRID_CANVAS0); //grd_num_use
	dc_print("decontour: DCNTR_GRID_CANVAS0=%x\n", value);
	value = Rd(DCNTR_GRID_LUMA_X0); //grd_num_use
	dc_print("decontour: DCNTR_GRID_LUMA_X0=%x\n", value);
	value = Rd(DCNTR_GRID_LUMA_Y0); //grd_num_use
	dc_print("decontour: DCNTR_GRID_LUMA_Y0=%x\n", value);
	value = Rd(DCNTR_GRID_RPT_LOOP); //grd_num_use
	dc_print("decontour: DCNTR_GRID_RPT_LOOP=%x\n", value);
	value = Rd(DCNTR_GRID_LUMA0_RPT_PAT); //grd_num_use
	dc_print("decontour: DCNTR_GRID_LUMA0_RPT_PAT=%x\n", value);
	value = Rd(DCNTR_GRID_CHROMA0_RPT_PAT); //grd_num_use
	dc_print("decontour: DCNTR_GRID_CHROMA0_RPT_PAT=%x\n", value);
	value = Rd(DCNTR_GRID_DUMMY_PIXEL); //grd_num_use
	dc_print("decontour: DCNTR_GRID_DUMMY_PIXEL=%x\n", value);

	value = Rd(DCNTR_GRID_LUMA_FIFO_SIZE); //grd_num_use
	dc_print("decontour: DCNTR_GRID_LUMA_FIFO_SIZE=%x\n", value);

	value = Rd(DCNTR_GRID_RANGE_MAP_Y); //grd_num_use
	dc_print("decontour: DCNTR_GRID_RANGE_MAP_Y=%x\n", value);
	value = Rd(DCNTR_GRID_RANGE_MAP_CB); //grd_num_use
	dc_print("decontour: DCNTR_GRID_RANGE_MAP_CB=%x\n", value);
	value = Rd(DCNTR_GRID_RANGE_MAP_CR); //grd_num_use
	dc_print("decontour: DCNTR_GRID_RANGE_MAP_CR=%x\n", value);
	value = Rd(DCNTR_GRID_URGENT_CTRL); //grd_num_use
	dc_print("decontour: DCNTR_GRID_URGENT_CTRL=%x\n", value);

	value = Rd(DCNTR_GRID_GEN_REG3); //grd_num_use
	dc_print("decontour: DCNTR_GRID_GEN_REG3=%x\n", value);
	value = Rd(DCNTR_GRID_AXI_CMD_CNT); //grd_num_use
	dc_print("decontour: DCNTR_GRID_AXI_CMD_CNT=%x\n", value);
	value = Rd(DCNTR_GRID_AXI_RDAT_CNT); //grd_num_use
	dc_print("decontour: DCNTR_GRID_AXI_RDAT_CNT=%x\n", value);
	value = Rd(DCNTR_GRID_FMT_CTRL); //grd_num_use
	dc_print("decontour: DCNTR_GRID_FMT_CTRL=%x\n", value);
	value = Rd(DCNTR_GRID_FMT_W); //grd_num_use
	dc_print("decontour: DCNTR_GRID_FMT_W=%x\n", value);

	if (get_cpu_type() <= MESON_CPU_MAJOR_ID_T5)
		return;

	value = Rd(DCNTR_GRID_BADDR_Y);
	dc_print("decontour: DCNTR_GRID_BADDR_Y=%x\n", value);
	value = Rd(DCNTR_GRID_BADDR_CB);
	dc_print("decontour: DCNTR_GRID_BADDR_CB=%x\n", value);
	value = Rd(DCNTR_GRID_BADDR_CR);
	dc_print("decontour: DCNTR_GRID_BADDR_CR=%x\n", value);

	value = Rd(DCNTR_GRID_STRIDE_0);
	dc_print("decontour: DCNTR_GRID_STRIDE_0=%x\n", value);
	value = Rd(DCNTR_GRID_STRIDE_1);
	dc_print("decontour: DCNTR_GRID_STRIDE_1=%x\n", value);

	value = Rd(DCNTR_GRID_BADDR_Y_F1);
	dc_print("decontour: DCNTR_GRID_BADDR_Y_F1=%x\n", value);
	value = Rd(DCNTR_GRID_BADDR_CB_F1);
	dc_print("decontour: DCNTR_GRID_BADDR_CB_F1=%x\n", value);
	value = Rd(DCNTR_GRID_BADDR_CR_F1);
	dc_print("decontour: DCNTR_GRID_BADDR_CR_F1=%x\n", value);
	value = Rd(DCNTR_GRID_STRIDE_0_F1);
	dc_print("decontour: DCNTR_GRID_STRIDE_0_F1=%x\n", value);
	value = Rd(DCNTR_GRID_STRIDE_1_F1);
	dc_print("decontour: DCNTR_GRID_STRIDE_1_F1=%x\n", value);
}

static void decontour_print_parm(struct dcntr_mem_s *dcntr_mem)
{
	dc_print("i=%d,grd_addr=%lx,y_addr=%lx,c_addr=%lx,g_size=%d,y_size=%d,c_size=%d,ratio=%d\n",
		dcntr_mem->index,
		dcntr_mem->grd_addr,
		dcntr_mem->yds_addr,
		dcntr_mem->cds_addr,
		dcntr_mem->grd_size,
		dcntr_mem->yds_size,
		dcntr_mem->cds_size,
		dcntr_mem->ds_ratio);
	dc_print("pre_out_fmt=%d,yflt_wrmif_length=%d,cflt_wrmif_length=%d,use_org=%d\n",
		dcntr_mem->pre_out_fmt,
		dcntr_mem->yflt_wrmif_length,
		dcntr_mem->cflt_wrmif_length,
		dcntr_mem->use_org);

	dc_print("grd_swap=%d,yds_swap=%x,cds_swap=%x,grd_endian=%x,yds_endian=%d,cds_endian=%dn",
		dcntr_mem->grd_swap_64bit,
		dcntr_mem->yds_swap_64bit,
		dcntr_mem->cds_swap_64bit,
		dcntr_mem->grd_little_endian,
		dcntr_mem->yds_little_endian,
		dcntr_mem->cds_little_endian);

	dc_print("yds_canvas_mode=%d,cds_canvas_mode=%d\n",
		dcntr_mem->yds_canvas_mode,
		dcntr_mem->cds_canvas_mode);
}

static int decontour_dump_output(u32 w, u32 h, struct dcntr_mem_s *dcntr_mem, int skip)
{
#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA
	struct file *fp;
	loff_t pos;
	char name_buf[32];
	int write_size;
	u8 *data;

	if (w == 0 || h == 0)
		return -7;
	write_size = dcntr_mem->grd_size;
	dc_print("addr =%lx, size=%d\n", dcntr_mem->grd_addr, write_size);
	snprintf(name_buf, sizeof(name_buf), "/data/tmp/%d-%d.grid",
		w, h);
	fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644);

	if (IS_ERR(fp))
		return -1;
	dc_print("decontour:dump_2: name_buf=%s\n", name_buf);
	data = codec_mm_vmap(dcntr_mem->grd_addr, write_size);
	dc_print("(ulong)data =%lx\n", (ulong)data);
	if (!data)
		return -2;
	pos = 0;
	kernel_write(fp, (const char *)data, write_size, &pos);
	dc_print("decontour: write %u size to addr%p\n", write_size, data);
	codec_mm_unmap_phyaddr(data);
	filp_close(fp, NULL);

	dc_print("decontour:dump_2.0/2");
	write_size = w * h;
	dc_print("decontour:dump_4:yds_addr =%lx, write_size=%d\n",
		dcntr_mem->yds_addr, write_size);
	dc_print("decontour:dump_4:cds_addr =%lx\n", dcntr_mem->cds_addr);
	snprintf(name_buf, sizeof(name_buf), "/data/tmp/ds_out.yuv");

	fp = filp_open(name_buf, O_CREAT | O_RDWR, 0644);
	if (IS_ERR(fp)) {
		pr_err("decontour: create %s fail.\n", name_buf);
		return -4;
	}
	dc_print("decontour:dump_4: name_buf=%s\n", name_buf);

	data = codec_mm_vmap(dcntr_mem->yds_addr, dcntr_mem->yds_size);
	dc_print("decontour:dump_3\n");
	if (!data)
		return -5;
	pos = 0;
	kernel_write(fp, (const char *)data, write_size, &pos);
	dc_print("decontour: write %u size to file.addr%p\n", write_size, data);
	codec_mm_unmap_phyaddr(data);
	pos = write_size;

	write_size = w * h / 2;
	data = codec_mm_vmap(dcntr_mem->cds_addr, dcntr_mem->cds_size);
	dc_print("decontour:dump_4\n");
	if (!data)
		return -6;
	kernel_write(fp, (const char *)data, write_size, &pos);
	dc_print("decontour: write %u size to file.addr%p\n", write_size, data);
	codec_mm_unmap_phyaddr(data);
	filp_close(fp, NULL);
#endif
	return 0;
}

static int decontour_pre_process(struct vframe_s *vf)
{
	int mif_read_width; /*grid read mif input*/
	int mif_read_height;
	int mif_out_width;  /*grid read mif output; also dct input*/
	int mif_out_height;
	u32 ds_out_width = 0; /*dct ds output*/
	u32 ds_out_height = 0;
	int canvas_width;
	int canvas_height;
	u32 vf_org_width;
	u32 vf_org_height;
	u32 grd_num;
	u32 reg_grd_xnum;
	u32 reg_grd_ynum;
	u32 grd_xsize;
	u32 grd_ysize;
	/* u32 grid_wrmif_length; */
	u32 yflt_wrmif_length;
	u32 cflt_wrmif_length;
	int ds_ratio = 0;/*0:(1:1)  1:(1:2)  2:(1:4)*/
	u32 phy_addr_0, phy_addr_1, phy_addr_2;
	struct canvas_s src_cs0, src_cs1, src_cs2;
	int canvas_id_0, canvas_id_1, canvas_id_2;
	int src_fmt = 2; /*default 420*/
	struct dcntr_mem_s *dcntr_mem;
	int timeout = 0;
	int i;
	int mif_reverse;
	int swap_64bit;
	int ret;
	int skip = 0;
	u32 pic_struct;
	u32 h_avg;
	u32 bit_mode;
	bool need_ds = false;
	u32 burst_len = 2;
	bool format_changed = false;
	bool unsupported_resolution = false;
	u32 is_interlace = 0;
	u32 block_mode;
	u32 endian;
	u32 pic_32byte_aligned = 0;
	bool support_canvas = false;
	int src_hsize;
	int src_vsize;

	if (!vf)
		return -1;

	vf->decontour_pre = NULL;

	if (ppmgr_device.bypass_decontour == 1)
		return 0;

	if (get_cpu_type() <= MESON_CPU_MAJOR_ID_T5)
		support_canvas = true;

	if (vf->type & VIDTYPE_INTERLACE)
		if (!ppmgr_device.i_do_decontour)
			return 0;

	if (vf->type & VIDTYPE_COMPRESS) {
		if ((vf->type & VIDTYPE_NO_DW) || vf->canvas0Addr == 0) {
			dc_print("is afbc, but no dw\n");
			return 0;
		}
	}

	if (vf->width == 0 || vf->height == 0) {
		dc_print("w*h =%d*%d\n", vf->width, vf->height);
		return 0;
	}

	if ((vf->width * vf->height) < (160 * 120))
		return 0;

	if ((vf->type & VIDTYPE_VIU_422) && !(vf->type & 0x10000000)) {
		src_fmt = 0;
		need_ds = true;/*422 is one plane, post not support, need pre out nv21*/
	} else if ((vf->type & VIDTYPE_VIU_NV21) ||
		(vf->type & 0x10000000)) {/*hdmi in dw is nv21 VIDTYPE_DW_NV21*/
		src_fmt = 2;
	} else {
		dc_print("not support format vf->type =%x\n", vf->type);
		return 0; /*current one support 422 nv21*/
	}

	if (vf->type & VIDTYPE_COMPRESS) {
		vf_org_width = vf->compWidth;
		vf_org_height = vf->compHeight;
	} else {
		vf_org_width = vf->width;
		vf_org_height = vf->height;
	}

	if (ppmgr_device.decontour_addr == 0)
		decontour_init();

	if (ppmgr_device.decontour_addr == 0)
		return 0;

	pic_struct = 0;
	h_avg = 0;
	mif_out_width = vf->width;
	mif_out_height = vf->height;
	mif_read_width = vf->width;
	mif_read_height = vf->height;

	if (vf->type & VIDTYPE_INTERLACE) {
		is_interlace = 1;
		if (src_fmt == 2) {/*decoder output, top in odd rows*/
			if ((vf->type & VIDTYPE_INTERLACE_BOTTOM) == 0x3)
				pic_struct = 6;/*4 line read 1*/
			else
				pic_struct = 5;/*4 line read 1*/
			h_avg = 1;
			skip = 1;
			mif_out_width = vf->width >> 1;
			mif_out_height = vf->height >> 2;
		} else if (src_fmt == 0) {/*hdmiin output, In the first half of the line*/
			mif_out_width = vf->width;
			mif_out_height = vf->height >> 1;
			mif_read_height = vf->height >> 1;
			need_ds = 1;
			if (mif_out_width > 960 || mif_out_height > 540)
				ds_ratio = 1;
		}
	} else {
		if (vf->width > 1920 || vf->height > 1080) {
			ds_ratio = 1;
			skip = 1;
			mif_out_width = vf->width >> 1;
			mif_out_height = vf->height >> 1;
		} else if (vf->width > 960 || vf->height > 540) {
			if ((ppmgr_device.debug_decontour & DCT_USE_DS_SCALE) ||
				src_fmt == 0) { /*hdmi in always use ds*/
				ds_ratio = 1;
			} else {
				ds_ratio = 0; /*decoder use mif skip for save ddr*/
				skip = 1;
				dc_print("1080p use mif skip\n");
				mif_out_width = vf->width >> 1;
				mif_out_height = vf->height >> 1;
			}
		}

		if (skip && src_fmt == 2) {
			pic_struct = 4;
			h_avg = 1;
		}
	}

	ds_out_width = mif_out_width >> ds_ratio;
	ds_out_height = mif_out_height >> ds_ratio;

	if (ds_out_width < 16 || ds_out_height < 120) {
		dc_print("not supported: vf:%d * %d", vf->width, vf->height);
		return 0;
	}

	dc_print("src_fmt=%d, pic_struct=%d, h_avg =%d, bitdepth=%x\n",
		 src_fmt, pic_struct, h_avg, vf->bitdepth);

	dc_print("vf:%d * %d; mif_read:%d*%d;mif_out:%d * %d; ds_ratio=%d, skip=%d;ds_out:%d*%d\n",
		vf->width, vf->height,
		mif_read_width, mif_read_height,
		mif_out_width, mif_out_height,
		ds_ratio, skip,
		ds_out_width, ds_out_height);

	for (i = 0; i < DCNTR_POOL_SIZE; i++) {
		if (dcntr_mem_info[i].free) {
			dcntr_mem_info[i].free = false;
			break;
		}
	}
	if (i == DCNTR_POOL_SIZE) {
		pr_err("decontour: no free mem\n");
		return -1;
	}
	dcntr_mem = &dcntr_mem_info[i];

	dcntr_mem->use_org = true;
	dcntr_mem->ds_ratio = 0;

	if (ds_ratio || skip || need_ds)
		dcntr_mem->use_org = false;

	if (!dcntr_mem->use_org) {
		dcntr_mem->ds_ratio = ds_ratio;
		if (skip)
			dcntr_mem->ds_ratio = dcntr_mem->ds_ratio + 1;

		if (need_ds && (vf->type & VIDTYPE_COMPRESS))
			dcntr_mem->ds_ratio = (vf->compWidth / vf->width) >> 1;
	}

	if (dcntr_mem->use_org) {
		if (vf->type & VIDTYPE_COMPRESS) {
			if ((vf->compWidth / vf->width) * vf->width != vf->compWidth)
				unsupported_resolution = true;
			if ((vf->compHeight / vf->height) * vf->height != vf->compHeight)
				unsupported_resolution = true;
		}
	} else {
		if ((ds_out_width << dcntr_mem->ds_ratio) != vf_org_width)
			unsupported_resolution = true;
		if ((ds_out_height << dcntr_mem->ds_ratio) != (vf_org_height >> is_interlace))
			unsupported_resolution = true;
	}
	if (unsupported_resolution) {
		dcntr_mem->free = true;
		dc_print("unsupported resolution %d*%d, com %d*%d, ds_ratio=%d\n",
			vf->width, vf->height,
			vf->compWidth, vf->compHeight,
			dcntr_mem->ds_ratio);
		return 0;
	}

	vf->decontour_pre = (void *)dcntr_mem;

	if (vf->canvas0Addr == (u32)-1) {
		phy_addr_0 = vf->canvas0_config[0].phy_addr;
		phy_addr_1 = vf->canvas0_config[1].phy_addr;
		phy_addr_2 = vf->canvas0_config[2].phy_addr;
		canvas_config_config(ppmgr_src_canvas[0],
			&vf->canvas0_config[0]);
		canvas_config_config(ppmgr_src_canvas[1],
			&vf->canvas0_config[1]);
		canvas_config_config(ppmgr_src_canvas[2],
			&vf->canvas0_config[2]);
		canvas_id_0 = ppmgr_src_canvas[0];
		canvas_id_1 = ppmgr_src_canvas[1];
		canvas_id_2 = ppmgr_src_canvas[2];
		canvas_width = vf->canvas0_config[0].width;
		canvas_height = vf->canvas0_config[0].height;
		block_mode = vf->canvas0_config[0].block_mode;
		endian = vf->canvas0_config[0].endian;
	} else {
		dc_print("source is canvas\n");
		canvas_read(vf->canvas0Addr & 0xff, &src_cs0);
		canvas_read(vf->canvas0Addr >> 8 & 0xff, &src_cs1);
		canvas_read(vf->canvas0Addr >> 16 & 0xff, &src_cs2);
		phy_addr_0 = src_cs0.addr;
		phy_addr_1 = src_cs1.addr;
		phy_addr_2 = src_cs2.addr;
		canvas_id_0 = vf->canvas0Addr & 0xff;
		canvas_id_1 = vf->canvas0Addr >> 8 & 0xff;
		canvas_id_2 = vf->canvas0Addr >> 16 & 0xff;
		canvas_width = src_cs0.width;
		canvas_height = src_cs0.height;
		block_mode = src_cs0.blkmode;
		endian = src_cs0.endian;
	}
	dc_print("block_mode=%d, endian=%d\n", block_mode, endian);

	if (last_w != vf->width ||
		last_h != vf->height ||
		last_type != vf->type ||
		(ppmgr_device.debug_decontour & DCT_REWRITE_REG))
		format_changed = true;

	if (!format_changed) {
		dcntr_mem->grd_swap_64bit = dcntr_mem_info_last.grd_swap_64bit;
		dcntr_mem->yds_swap_64bit = dcntr_mem_info_last.yds_swap_64bit;
		dcntr_mem->cds_swap_64bit = dcntr_mem_info_last.cds_swap_64bit;
		dcntr_mem->grd_little_endian = dcntr_mem_info_last.grd_little_endian;
		dcntr_mem->yds_little_endian = dcntr_mem_info_last.yds_little_endian;
		dcntr_mem->cds_little_endian = dcntr_mem_info_last.cds_little_endian;
		dcntr_mem->yflt_wrmif_length = dcntr_mem_info_last.yflt_wrmif_length;
		dcntr_mem->cflt_wrmif_length = dcntr_mem_info_last.cflt_wrmif_length;
		goto SET_ADDR;
	}
	last_w = vf->width;
	last_h = vf->height;
	last_type = vf->type;

	if (src_fmt == 0) {
		src_hsize = canvas_width >> 1;
		src_vsize = canvas_height;
	} else {
		src_hsize = canvas_width;
		src_vsize = canvas_height;
	}
	ini_dcntr_pre(mif_out_width, mif_out_height, 2, ds_ratio);
	grd_num = Rd(DCTR_BGRID_PARAM3_PRE);
	reg_grd_xnum = (grd_num >> 16) & (0x3ff);
	reg_grd_ynum = grd_num & (0x3ff);

	grd_xsize = reg_grd_xnum << 3;
	grd_ysize = reg_grd_ynum;

	/* grid_wrmif_length = 81 * 46 * 8; */
	yflt_wrmif_length = ds_out_width;
	cflt_wrmif_length = ds_out_width;

	dc_print("canvas_width=%d, canvas_height=%d, src_hsize=%d, src_vsize=%d\n",
		canvas_width, canvas_height,
		src_hsize, src_vsize);

	if (canvas_width % 32)
		burst_len = 0;
	else if (canvas_width % 64)
		burst_len = 1;

	if (block_mode && !support_canvas)
		burst_len = block_mode;

	dcntr_grid_rdmif(canvas_id_0,         /*int canvas_id0,*/
		canvas_id_1,         /*int canvas_id1,*/
		canvas_id_2,         /*int canvas_id2,*/
		phy_addr_0,  /*int canvas_baddr0,*/
		phy_addr_1,           /*int canvas_baddr1,*/
		phy_addr_2,           /*int canvas_baddr2,*/
		src_hsize,   /*int src_hsize,*/
		src_vsize,   /*int src_vsize,*/
		src_fmt, /*1 = RGB/YCBCR(3 bytes/pixel), 0=422 (2 bytes/pixel) 2:420 (two canvas)*/
		0,           /*int mif_x_start,*/
		mif_read_width - 1, /*int mif_x_end  ,*/
		0,           /*int mif_y_start,*/
		mif_read_height - 1, /*int mif_y_end  ,*/
		0,            /*int mif_reverse  // 0 : no reverse*/
		pic_struct,/*0 : frame; 2:top_field; 3:bot_field; 4:y skip, uv no skip;*/
				/*5:top_field 1/4; 6 bot_field 1/4*/
		h_avg);    /*0 : no avg 1:y_avg  2:c_avg  3:y&c avg*/

	mif_reverse = 1;
	swap_64bit = 0;
	if (mif_reverse == 1) {
		dcntr_mem->grd_swap_64bit = false;
		dcntr_mem->yds_swap_64bit = false;
		dcntr_mem->cds_swap_64bit = false;
		dcntr_mem->grd_little_endian = true;
		dcntr_mem->yds_little_endian = true;
		dcntr_mem->cds_little_endian = true;
	}
	dcntr_grid_wrmif(1,  /*int mif_index,  //0:cds  1:grd   2:yds*/
		0,  /*int mem_mode,  //0:linear address mode  1:canvas mode*/
		5,  /*int src_fmt ,  //0:4bit 1:8bit 2:16bit 3:32bit 4:64bit 5:128bit*/
		ass_index,  /*int canvas_id*/
		0,  /*int mif_x_start*/
		grd_xsize - 1,  /*int mif_x_end*/
		0,  /*int mif_y_start*/
		grd_ysize - 1,  /*int mif_y_end*/
		swap_64bit,
		mif_reverse,  /*int mif_reverse, /0 : no reverse*/
		dcntr_mem->grd_addr,  /*int linear_baddr*/
		grd_xsize);  /*int linear_length // DDR read length = linear_length*128 bits*/

	if (ds_ratio || skip || need_ds) {
		yflt_wrmif_length = yflt_wrmif_length * 8 / 128;
		cflt_wrmif_length = cflt_wrmif_length * 8 / 128;
		dcntr_mem->yflt_wrmif_length = yflt_wrmif_length;
		dcntr_mem->cflt_wrmif_length = cflt_wrmif_length;

		dcntr_grid_wrmif(2,  /*int mif_index,  //0:cds  1:grd   2:yds*/
			0,  /*int mem_mode,  //0:linear address mode  1:canvas mode*/
			1,  /*int src_fmt,  //0:4bit 1:8bit 2:16bit 3:32bit 4:64bit 5:128bit*/
			ppmgr_dst_canvas[0],  /*int canvas_id*/
			0,  /*int mif_x_start*/
			ds_out_width - 1,  /*int mif_x_end*/
			0,  /*int mif_y_start*/
			ds_out_height - 1,  /*int mif_y_end*/
			swap_64bit,
			mif_reverse,  /*int mif_reverse   ,  //0 : no reverse*/
			dcntr_mem->yds_addr,  /*int linear_baddr*/
			yflt_wrmif_length); /*DDR read length = linear_length*128 bits*/

		dcntr_grid_wrmif(0,  /*int mif_index,  //0:cds  1:grd   2:yds*/
			0,  /*int mem_mode,  //0:linear address mode  1:canvas mode*/
			2,  /*int src_fmt,  //0:4bit 1:8bit 2:16bit 3:32bit 4:64bit 5:128bit*/
			ppmgr_dst_canvas[1],  /*int canvas_id*/
			0,  /*int mif_x_start*/
			(ds_out_width >> 1) - 1,  /*int mif_x_end*/
			0,  /*int mif_y_start*/
			(ds_out_height >> 1) - 1,  /*int mif_y_end*/
			swap_64bit,
			mif_reverse,  /*int mif_reverse   ,  //0 : no reverse*/
			dcntr_mem->cds_addr,  /*int linear_baddr*/
			cflt_wrmif_length); /*DDR read length = linear_length*128 bits*/
	}

	wr_bits(DCTR_BGRID_TOP_CTRL0, 1, 2, 2);/*reg_din_sel: 1:dos 2:vdin  0/3:disable*/

	if (ds_ratio || skip || need_ds) {
		wr_bits(DCTR_BGRID_TOP_CTRL0, 1, 1, 1);/*reg_ds_mif_en: 1=on 0=off*/
		wr_bits(DCTR_BGRID_TOP_FMT, 2, 19, 2); /*reg_fmt_mode, 2=420, 1=422, 0=444*/

		if (ds_ratio == 0)
			wr_bits(DCTR_BGRID_PATH_PRE, 1, 4, 1); /*reg_grd_path 0=ds 1=ori*/
		else
			wr_bits(DCTR_BGRID_PATH_PRE, 0, 4, 1); /*reg_grd_path 0=ds 1=ori*/

		if (ds_ratio == 1)
			wr_bits(DCTR_DS_PRE, 0x5, 0, 4); /*reg_ds_rate_xy*/
		else if (ds_ratio == 0)
			wr_bits(DCTR_DS_PRE, 0x0, 0, 4); /*reg_ds_rate_xy*/
	} else {
		wr_bits(DCTR_BGRID_TOP_CTRL0, 0, 1, 1); /*reg_ds_mif_en: 1=on 0=off*/
		wr_bits(DCTR_BGRID_PATH_PRE, 1, 4, 1); /*reg_grd_path 0=ds 1=ori*/
		wr_bits(DCTR_DS_PRE, 0x0, 0, 4);	   /*reg_ds_rate_xy*/
	}

	if (src_fmt == 0 && (vf->bitdepth & BITDEPTH_Y10) &&
		((vf->type & VIDTYPE_COMPRESS) == 0)) {/*all dw is 8bit*/
		if (vf->bitdepth & FULL_PACK_422_MODE)
			bit_mode = 3;
		else
			bit_mode = 1;
		wr_bits(DCNTR_GRID_GEN_REG3, bit_mode, 8, 2);/*0->8bit; 1->10bit422; 2->10bit444*/
		wr_bits(DCNTR_GRID_GEN_REG3, 0x3, 4, 3); /*cntl_blk_len: vd1 default is 3*/
	}
	wr_bits(DCNTR_GRID_GEN_REG3, (burst_len & 0x3), 1, 2);
	dcntr_mem_info_last = *dcntr_mem;

SET_ADDR:
	if (support_canvas) {
		Wr(DCNTR_GRID_CANVAS0,
			(canvas_id_2 << 16) |
			(canvas_id_1 << 8) |
			(canvas_id_0 << 0));
		Wr(DCNTR_GRD_WMIF_CTRL4, dcntr_mem->grd_addr);
		Wr(DCNTR_YDS_WMIF_CTRL4, dcntr_mem->yds_addr);
		Wr(DCNTR_CDS_WMIF_CTRL4, dcntr_mem->cds_addr);
	} else {
		Wr(DCNTR_GRID_BADDR_Y, phy_addr_0 >> 4);
		Wr(DCNTR_GRID_BADDR_CB, phy_addr_1 >> 4);
		Wr(DCNTR_GRID_BADDR_CR, phy_addr_2 >> 4);

		Wr(DCNTR_GRD_WMIF_CTRL4, dcntr_mem->grd_addr >> 4);
		Wr(DCNTR_YDS_WMIF_CTRL4, dcntr_mem->yds_addr >> 4);
		Wr(DCNTR_CDS_WMIF_CTRL4, dcntr_mem->cds_addr >> 4);

		wr_bits(DCNTR_GRID_GEN_REG3, block_mode, 12, 2);
		wr_bits(DCNTR_GRID_GEN_REG3, block_mode, 14, 2);
		if (block_mode)
			pic_32byte_aligned = 7;
		wr_bits(DCNTR_GRID_GEN_REG3,
			(pic_32byte_aligned << 7) |
			(block_mode << 4) |
			(block_mode << 2) |
			(block_mode << 0),
			18, 9);
		if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR) {
			wr_bits(DCNTR_GRID_GEN_REG3, 0, 0, 1);
			dc_print("LINEAR:flag=%x\n", vf->flag);
		} else {
			wr_bits(DCNTR_GRID_GEN_REG3, 1, 0, 1);
			dc_print("block_mode:flag=%x\n", vf->flag);
		}
	}

	if (ppmgr_device.debug_decontour & DCT_DUMP_REG)
		decontour_dump_reg();

	dc_print("decontour:start_1\n");
	wr_bits(DCTR_BGRID_TOP_CTRL0, 1, 31, 1);
	dc_print("decontour:start_2\n");

	timeout = wait_for_completion_timeout(&isr_done,
		msecs_to_jiffies(200));
	if (!timeout)
		pr_err("decontour:wait isr timeout\n");

	if (ppmgr_device.dump_grid) {
		ret = decontour_dump_output(ds_out_width, ds_out_height,
			dcntr_mem, skip);
		if (ret)
			pr_err("decontour_dump_output error, ret=%d\n", ret);
		ppmgr_device.dump_grid = 0;
	}

	if (ppmgr_device.debug_decontour & DCT_PRINT_INFO)
		decontour_print_parm(dcntr_mem);

	dc_print("decontour:ok index=%d\n", vf->omx_index);
	return 0;
}

/************************************************
 *
 *   ppmgr as a frame provider
 *
 * ***********************************************
 */
static int task_running;
static int still_picture_notify;
/*static int q_free_set = 0 ;*/
static struct vframe_s *ppmgr_vf_peek(void *op_arg)
{
	struct vframe_s *vf;

	if (ppmgr_blocking)
		return NULL;
	vf = vfq_peek(&q_ready);
	return vf;
}

static struct vframe_s *ppmgr_vf_get(void *op_arg)
{
	struct vframe_s *vf;

	if (ppmgr_blocking)
		return NULL;
	vf = vfq_pop(&q_ready);
	if (vf)
		ppmgr_device.get_count++;
	return vf;
}

static void ppmgr_vf_put(struct vframe_s *vf, void *op_arg)
{
	struct ppframe_s *vf_local;
	int i;
	int index;
	struct ppframe_s *pp_vf = to_ppframe(vf);

	if (ppmgr_blocking) {
		pr_info("%s: ppmgr_blocking is 1\n", __func__);
		return;
	}
	ppmgr_device.put_count++;

	i = vfq_level(&q_free);

	while (i > 0) {
		index = (q_free.rp + i - 1) % (q_free.size);
		vf_local = to_ppframe(q_free.pool[index]);
		if (vf_local->index == pp_vf->index) {
			pr_err("ppmgr put error1 %d\n", pp_vf->index);
			return;
		}

		i--;
	}
	i = vfq_level(&q_ready);
	while (i > 0) {
		index = (q_ready.rp + i - 1) % (q_ready.size);
		vf_local = to_ppframe(q_ready.pool[index]);
		if (vf_local->index == pp_vf->index) {
			pr_err("ppmgr put error2 %d\n", pp_vf->index);
			return;
		}

		i--;
	}

	/* the frame is in bypass mode, put the decoder frame */
	if (pp_vf->dec_frame) {
		ppmgr_vf_put_dec(pp_vf->dec_frame);
		pp_vf->dec_frame = NULL;
	}
	vfq_push(&q_free, vf);
}

int vf_ppmgr_get_states(struct vframe_states *states)
{
	int ret = -1;
	unsigned long flags;
	struct vframe_provider_s *vfp = NULL;

#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vfp = vf_get_provider(RECEIVER_NAME);
#endif
	spin_lock_irqsave(&lock, flags);
	if (vfp && vfp->ops && vfp->ops->vf_states)
		ret = vfp->ops->vf_states(states, vfp->op_arg);

	spin_unlock_irqrestore(&lock, flags);
	return ret;
}

static int get_source_type(struct vframe_s *vf)
{
	enum ppmgr_source_type ret;
	int interlace_mode;

	interlace_mode = vf->type & VIDTYPE_TYPEMASK;
	if (vf->source_type == VFRAME_SOURCE_TYPE_HDMI ||
	    vf->source_type == VFRAME_SOURCE_TYPE_CVBS) {
		if ((vf->bitdepth & BITDEPTH_Y10) &&
		    (!(vf->type & VIDTYPE_COMPRESS)) &&
		    (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL))
			ret = VDIN_10BIT_NORMAL;
		else
			ret = VDIN_8BIT_NORMAL;
	} else {
		if ((vf->bitdepth & BITDEPTH_Y10) &&
		    (!(vf->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 vframe_s *vf)
{
	int format = GE2D_FORMAT_M24_YUV420;
	enum ppmgr_source_type soure_type;

	soure_type = (enum ppmgr_source_type)get_source_type(vf);
	switch (soure_type) {
	case DECODER_8BIT_NORMAL:
		if (vf->type & VIDTYPE_VIU_422)
			format = GE2D_FORMAT_S16_YUV422;
		else if (vf->type & VIDTYPE_VIU_NV21)
			format = GE2D_FORMAT_M24_NV21;
		else if (vf->type & VIDTYPE_VIU_444)
			format = GE2D_FORMAT_S24_YUV444;
		else
			format = GE2D_FORMAT_M24_YUV420;
		break;
	case DECODER_8BIT_BOTTOM:
		if (vf->type & VIDTYPE_VIU_422)
			format = GE2D_FORMAT_S16_YUV422
				| (GE2D_FORMAT_S16_YUV422B & (3 << 3));
		else if (vf->type & VIDTYPE_VIU_NV21)
			format = GE2D_FORMAT_M24_NV21
				| (GE2D_FORMAT_M24_NV21B & (3 << 3));
		else if (vf->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 (vf->type & VIDTYPE_VIU_422)
			format = GE2D_FORMAT_S16_YUV422
				| (GE2D_FORMAT_S16_YUV422T & (3 << 3));
		else if (vf->type & VIDTYPE_VIU_NV21)
			format = GE2D_FORMAT_M24_NV21
				| (GE2D_FORMAT_M24_NV21T & (3 << 3));
		else if (vf->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 (vf->type & VIDTYPE_VIU_422) {
			if (vf->bitdepth & FULL_PACK_422_MODE)
				format = GE2D_FORMAT_S16_10BIT_YUV422;
			else
				format = GE2D_FORMAT_S16_12BIT_YUV422;
		}
		break;
	case DECODER_10BIT_BOTTOM:
		if (vf->type & VIDTYPE_VIU_422) {
			if (vf->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 (vf->type & VIDTYPE_VIU_422) {
			if (vf->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 (vf->type & VIDTYPE_VIU_422) {
			format = GE2D_FORMAT_S16_YUV422;
		} else if (vf->type & VIDTYPE_VIU_NV21) {
			format = GE2D_FORMAT_M24_NV21;
		} else if (vf->type & VIDTYPE_VIU_444) {
			format = GE2D_FORMAT_S24_YUV444;
		} else if (vf->type & VIDTYPE_RGB_444) {
			format = GE2D_FORMAT_S24_RGB;
			if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR)
				;
			else
				format &= (~GE2D_LITTLE_ENDIAN);
		} else {
			format = GE2D_FORMAT_M24_YUV420;
		}
		break;
	case VDIN_10BIT_NORMAL:
		if (vf->type & VIDTYPE_VIU_422) {
			if (vf->bitdepth & FULL_PACK_422_MODE)
				format = GE2D_FORMAT_S16_10BIT_YUV422;
			else
				format = GE2D_FORMAT_S16_12BIT_YUV422;
		} else if (vf->type & VIDTYPE_VIU_444) {
			format = GE2D_FORMAT_S24_10BIT_YUV444;
		} else if (vf->type & VIDTYPE_RGB_444) {
			format = GE2D_FORMAT_S24_10BIT_RGB888;
			if (vf->flag & VFRAME_FLAG_VIDEO_LINEAR)
				format |= GE2D_LITTLE_ENDIAN;
		}
		break;
	default:
		format = GE2D_FORMAT_M24_YUV420;
	}

	return format;
}

/* extern int get_property_change(void); */
/* extern void set_property_change(int flag); */

static int ppmgr_event_cb(int type, void *data, void *private_data)
{
	if (type & VFRAME_EVENT_RECEIVER_PUT) {
#ifdef DDD
		PPMGRVPP_WARN("video put, avail=%d, free=%d\n",
			      vfq_level(&q_ready), vfq_level(&q_free));
#endif
		up(&ppmgr_device.ppmgr_sem);
	}
	if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT) {
		if (task_running) {
#ifdef CONFIG_AMLOGIC_MEDIA_FRAME_SYNC
			if (timestamp_pcrscr_enable_state())
				return 0;
#endif
			if (get_property_change()) {
				/*printk("--ppmgr: get angle changed msg.\n");*/
				set_property_change(0);
				still_picture_notify = 1;
				up(&ppmgr_device.ppmgr_sem);
			} else {
				up(&ppmgr_device.ppmgr_sem);
			}
		}
	}
	return 0;
}

static int ppmgr_vf_states(struct vframe_states *states, void *op_arg)
{
	unsigned long flags;

	spin_lock_irqsave(&lock, flags);

	states->vf_pool_size = VF_POOL_SIZE;
	states->buf_recycle_num = 0;
	states->buf_free_num = vfq_level(&q_free);
	states->buf_avail_num = vfq_level(&q_ready);

	spin_unlock_irqrestore(&lock, flags);

	return 0;
}

static const struct vframe_operations_s ppmgr_vf_provider = {
		.peek = ppmgr_vf_peek,
		.get = ppmgr_vf_get,
		.put = ppmgr_vf_put,
		.event_cb = ppmgr_event_cb,
		.vf_states = ppmgr_vf_states, };
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
static struct vframe_provider_s ppmgr_vf_prov;
#endif
/************************************************
 *
 *   ppmgr as a frame receiver
 *
 *************************************************/

static int ppmgr_receiver_event_fun(int type, void *data, void*);

static const struct vframe_receiver_op_s ppmgr_vf_receiver = {.event_cb =
		ppmgr_receiver_event_fun};
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
static struct vframe_receiver_s ppmgr_vf_recv;
#endif
static int ppmgr_receiver_event_fun(int type, void *data, void *private_data)
{
	struct vframe_states states;

	switch (type) {
	case VFRAME_EVENT_PROVIDER_VFRAME_READY:
#ifdef DDD
		PPMGRVPP_WARN("dec put, avail=%d, free=%d\n",
			      vfq_level(&q_ready), vfq_level(&q_free));
#endif
		up(&ppmgr_device.ppmgr_sem);
		break;
	case VFRAME_EVENT_PROVIDER_QUREY_STATE:
		ppmgr_vf_states(&states, NULL);
		if (states.buf_avail_num > 0)
			return RECEIVER_ACTIVE;
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
		if (vf_notify_receiver(PROVIDER_NAME,
				       VFRAME_EVENT_PROVIDER_QUREY_STATE,
				       NULL) == RECEIVER_ACTIVE)
			return RECEIVER_ACTIVE;
#endif
		return RECEIVER_INACTIVE;
	case VFRAME_EVENT_PROVIDER_START:
#ifdef DDD
		PPMGRVPP_WARN("register now\n");
#endif
		up(&ppmgr_device.ppmgr_sem);
		vf_ppmgr_reg_provider();
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
		vf_notify_receiver(PROVIDER_NAME,
				   VFRAME_EVENT_PROVIDER_START,
				   NULL);
#endif
		break;
	case VFRAME_EVENT_PROVIDER_UNREG:
#ifdef DDD
		PPMGRVPP_WARN("unregister now\n");
#endif
		vf_ppmgr_unreg_provider();
		break;
	case VFRAME_EVENT_PROVIDER_LIGHT_UNREG:
		break;
	case VFRAME_EVENT_PROVIDER_RESET:
		vf_ppmgr_reset(0);
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
		vf_notify_receiver(PROVIDER_NAME,
				   VFRAME_EVENT_PROVIDER_RESET,
				   NULL);
#endif
		break;
	case VFRAME_EVENT_PROVIDER_FR_HINT:
	case VFRAME_EVENT_PROVIDER_FR_END_HINT:
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
		vf_notify_receiver(PROVIDER_NAME, type, data);
#endif
		break;
	default:
		break;
	}
	return 0;
}

void vf_local_init(void)
{
	int i;

	set_property_change(0);
	still_picture_notify = 0;
	vfq_init(&q_free, VF_POOL_SIZE + 1, &vfp_pool_free[0]);
	vfq_init(&q_ready, VF_POOL_SIZE + 1, &vfp_pool_ready[0]);
	ppmgr_device.get_count = 0;
	ppmgr_device.put_count = 0;
	ppmgr_device.get_dec_count = 0;
	ppmgr_device.put_dec_count = 0;
	need_data_notify = true;
	pr_info("ppmgr local_init\n");
	for (i = 0; i < VF_POOL_SIZE; i++) {
		vfp_pool[i].index = i;
		vfp_pool[i].dec_frame = NULL;
		vfq_push(&q_free, &vfp_pool[i].frame);
	}

	for (i = 0; i < VF_POOL_SIZE; i++) {
		buf_status[i].index = -1; /* ppmgr_canvas_tab[i]; */
		buf_status[i].dirty = 1;
	}
	//up(&ppmgr_device.ppmgr_sem);
}

static void local_canvas_uninit(void)
{
	int i;

#ifdef CANVAS_RESERVED
	for (i = 0; i < 3; i++)
		ppmgr_src_canvas[i] = -1;
	for (i = 0; i < 2; i++)
		ppmgr_dst_canvas[i] = -1;
	ass_index = -1;
#else
	for (i = 0; i < 3; i++) {
		if (ppmgr_src_canvas[i] >= 0)
			canvas_pool_map_free_canvas(ppmgr_src_canvas[i]);
		ppmgr_src_canvas[i] = -1;
	}

	for (i = 0; i < 2; i++) {
		if (ppmgr_dst_canvas[i] >= 0)
			canvas_pool_map_free_canvas(ppmgr_dst_canvas[i]);
		ppmgr_dst_canvas[i] = -1;
	}

	if (ass_index >= 0)
		canvas_pool_map_free_canvas(ass_index);
	ass_index = -1;
#endif
	local_canvas_status = 0;
}

static int local_canvas_init(void)
{
	int i;
	bool alloc_fail = false;

	if (local_canvas_status)
		return local_canvas_status;

#ifdef CANVAS_RESERVED
	for (i = 0; i < 3; i++)
		ppmgr_src_canvas[i] = PPMGR_CANVAS_INDEX + i;
	for (i = 0; i < 2; i++)
		ppmgr_src_canvas[i] = PPMGR_CANVAS_INDEX + i + 3;
	ass_index = PPMGR_CANVAS_INDEX + 5;
#else
	for (i = 0; i < 3; i++) {
		if (ppmgr_src_canvas[i] < 0)
			ppmgr_src_canvas[i] =
				canvas_pool_map_alloc_canvas("ppmgr_src");

		if (ppmgr_src_canvas[i] < 0) {
			PPMGRVPP_INFO("%s ppmgr_src_canvas[%d] alloc failed\n",
				__func__, i);
			alloc_fail = true;
			break;
		}
	}

	for (i = 0; i < 2; i++) {
		if (ppmgr_dst_canvas[i] < 0)
			ppmgr_dst_canvas[i] =
				canvas_pool_map_alloc_canvas("ppmgr_dst");

		if (ppmgr_dst_canvas[i] < 0) {
			PPMGRVPP_INFO("%s ppmgr_dst_canvas[%d] alloc failed\n",
				__func__, i);
			alloc_fail = true;
			break;
		}
	}

	if (ass_index < 0)
		ass_index = canvas_pool_map_alloc_canvas("ppmgr_temp");

	if (ass_index < 0) {
		PPMGRVPP_INFO("%s ass_index alloc failed\n", __func__);
		alloc_fail = true;
	}
#endif
	if (alloc_fail)
		local_canvas_uninit();
	else
		local_canvas_status = 1;
	return local_canvas_status;
}

const struct vframe_receiver_op_s *vf_ppmgr_reg_provider(void)
{
	const struct vframe_receiver_op_s *r = NULL;

	ppmgr_device.is_used = true;

	if (ppmgr_device.debug_first_frame == 1) {
		dumpfirstframe = 1;
		count_scr = 0;
		count_dst = 0;
		PPMGRVPP_INFO("need dump first frame!\n");
	}

	mutex_lock(&ppmgr_mutex);

	vf_local_init();
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf_reg_provider(&ppmgr_vf_prov);
#endif
	if (start_ppmgr_task() == 0)
		r = &ppmgr_vf_receiver;

	ppmgr_device.started = 1;

	mutex_unlock(&ppmgr_mutex);

	return r;
}

void vf_ppmgr_unreg_provider(void)
{
	mutex_lock(&ppmgr_mutex);

	stop_ppmgr_task();

	vf_unreg_provider(&ppmgr_vf_prov);

#ifdef PPMGR_TB_DETECT
	tb_buffer_uninit();
#endif
	ppmgr_buffer_uninit();

	ppmgr_device.started = 0;

	mutex_unlock(&ppmgr_mutex);

	if (is_decontour_supported())
		decontour_uninit();
	ppmgr_device.is_used = false;
}

/*skip the second reset request*/
static unsigned long last_reset_time;
static unsigned long current_reset_time;
#define PPMGR_RESET_INTERVAL  (HZ / 5)
void vf_ppmgr_reset(int type)
{
	if (ppmgr_inited) {
		current_reset_time = jiffies;
		if (abs(current_reset_time - last_reset_time)
			< PPMGR_RESET_INTERVAL)
			return;

		ppmgr_blocking = true;
		up(&ppmgr_device.ppmgr_sem);

		last_reset_time = current_reset_time;
	}
}

void vf_ppmgr_init_receiver(void)
{
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf_receiver_init(&ppmgr_vf_recv, RECEIVER_NAME,
			 &ppmgr_vf_receiver, NULL);
#endif
}

void vf_ppmgr_reg_receiver(void)
{
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf_reg_receiver(&ppmgr_vf_recv);
#endif
}

void vf_ppmgr_init_provider(void)
{
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf_provider_init(&ppmgr_vf_prov, PROVIDER_NAME,
			 &ppmgr_vf_provider, NULL);
#endif
}

static inline struct vframe_s *ppmgr_vf_peek_dec(void)
{
	struct vframe_s *vf = NULL;
#ifdef DDD
	struct vframe_provider_s *vfp;
	struct vframe_s *vf;

	vfp = vf_get_provider(RECEIVER_NAME);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return NULL;

	vf = vfp->ops->peek(vfp->op_arg);
	return vf;
#else
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf = vf_peek(RECEIVER_NAME);
#endif
	return vf;
#endif
}

static inline struct vframe_s *ppmgr_vf_get_dec(void)
{
	struct vframe_s *vf = NULL;
#ifdef DDD
	struct vframe_provider_s *vfp;
	struct vframe_s *vf;

	vfp = vf_get_provider(RECEIVER_NAME);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return NULL;
	vf = vfp->ops->get(vfp->op_arg);
	return vf;
#else
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
	vf = vf_get(RECEIVER_NAME);
#endif
	if (vf) {
		ppmgr_device.get_dec_count++;
		vf->decontour_pre = NULL;
	}
	return vf;
#endif
}

void ppmgr_vf_put_dec(struct vframe_s *vf)
{
	struct dcntr_mem_s *dcntr_mem;
#ifdef DDD
	struct vframe_provider_s *vfp;

	vfp = vf_get_provider(RECEIVER_NAME);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return;
	vfp->ops->put(vf, vfp->op_arg);
#else
	if (vf->decontour_pre) {
		dcntr_mem = (struct dcntr_mem_s *)vf->decontour_pre;
		dcntr_mem->free = true;
		vf->decontour_pre = NULL;
	}
	vf_put(vf, RECEIVER_NAME);
	ppmgr_device.put_dec_count++;

#endif
}

/************************************************
 *
 *   main task functions.
 *
 *************************************************/
static void vf_rotate_adjust(struct vframe_s *vf,
			     struct vframe_s *new_vf,
			     int angle)
{
	int w = 0, h = 0, disp_w = 0, disp_h = 0;
	int input_height = 0, input_width = 0;
	int scale_down_value = 1;
	int interlace_mode = 0;

	disp_w = ppmgr_device.disp_width / scale_down_value;
	disp_h = ppmgr_device.disp_height / scale_down_value;
	interlace_mode = vf->type & VIDTYPE_TYPEMASK;
	input_width = vf->width;
	if (interlace_mode)
		input_height = vf->height * 2;
	else
		input_height = vf->height;
	if (ppmgr_device.ppmgr_debug & 1) {
		PPMGRVPP_INFO("disp_width: %d, disp_height: %d\n",
			      disp_w, disp_h);
		PPMGRVPP_INFO("input_width: %d, input_height: %d\n",
			      input_width, input_height);
	}
	if (angle & 1) {
		int ar = (vf->ratio_control
			  >> DISP_RATIO_ASPECT_RATIO_BIT) & 0x3ff;
		if (ppmgr_device.ppmgr_debug == 2)
			ar = 0;

		if (input_width > input_height) {
			h = min_t(int, input_width, disp_h);

			if (ar == 0 || ar == 0x3ff)
				w = input_height * h / input_width;
			else
				w = (ar * h) >> 8;

			if (w > disp_w) {
				h = (h * disp_w) / w;
				w = disp_w;
			}
		} else {
			w = min_t(int, input_height, disp_w);

			if (ar == 0 || ar == 0x3ff)
				h = input_width * w / input_height;
			else
				h = (w << 8) / ar;

			if (h > disp_h) {
				w = (w * disp_h) / h;
				h = disp_h;
			}
		}
		if (ppmgr_device.ppmgr_debug & 1)
			PPMGRVPP_INFO("width: %d, height: %d, ar: %d\n",
				      w, h, ar);
		new_vf->ratio_control = DISP_RATIO_PORTRAIT_MODE;
		new_vf->ratio_control |=
				(h * 0x100 / w) << DISP_RATIO_ASPECT_RATIO_BIT;
		/*set video aspect ratio*/
	} else {
		if (input_width < disp_w && input_height < disp_h) {
			w = input_width;
			h = input_height;
		} else {
			if ((input_width * disp_h) > (disp_w * input_height)) {
				w = disp_w;
				h = disp_w * input_height / input_width;
			} else {
				h = disp_h;
				w = disp_h * input_width / input_height;
			}
		}

		new_vf->ratio_control = vf->ratio_control;
	}

	if (h > 1280) {
		w = w * 1280 / h;
		h = 1280;
	}

	new_vf->width = (w + 0x1f) & ~0x1f;
	new_vf->height = h;
}

#ifdef PPMGR_TB_DETECT
static int process_vf_tb_detect(struct vframe_s *vf,
				struct ge2d_context_s *context,
				struct config_para_ex_s *ge2d_config)
{
	struct canvas_s cs0, cs1, cs2, cd;
	int interlace_mode;
	u32 canvas_id;
	u32 format = GE2D_FORMAT_M24_YUV420;
	u32 h_scale_coef_type =
		context->config.h_scale_coef_type;
	u32 v_scale_coef_type =
		context->config.v_scale_coef_type;

	if (unlikely(!vf) || !context)
		return -1;

	interlace_mode = vf->type & VIDTYPE_TYPEMASK;
	if (!interlace_mode)
		return 0;

	if (vf->type & VIDTYPE_VIU_422)
		format = GE2D_FORMAT_S16_YUV422;
	else if (vf->type & VIDTYPE_VIU_NV21)
		format = GE2D_FORMAT_M24_NV21;
	else
		format = GE2D_FORMAT_M24_YUV420;
	if (tb_buff_wptr & 1) {
		format = format
			| (GE2D_FORMAT_M24_NV21B & (3 << 3));
		context->config.h_scale_coef_type =
			FILTER_TYPE_GAU0;
		context->config.v_scale_coef_type =
			FILTER_TYPE_GAU0_BOT;
	} else {
		format = format
			| (GE2D_FORMAT_M24_NV21T & (3 << 3));
		context->config.h_scale_coef_type =
			FILTER_TYPE_GAU0;
		context->config.v_scale_coef_type =
			FILTER_TYPE_GAU0;
	}
	canvas_config(ppmgr_dst_canvas[0],
		      detect_buf[tb_buff_wptr].paddr,
		      TB_DETECT_W, TB_DETECT_H,
		      CANVAS_ADDR_NOWRAP,
		      CANVAS_BLKMODE_LINEAR);
	memset(ge2d_config, 0, sizeof(struct config_para_ex_s));

	ge2d_config->alu_const_color = 0;
	ge2d_config->bitmask_en = 0;
	ge2d_config->src1_gb_alpha = 0;/* 0xff; */
	ge2d_config->dst_xy_swap = 0;

	if (vf->canvas0Addr == (u32)-1) {
		canvas_config_config(ppmgr_src_canvas[0] & 0xff,
				     &vf->canvas0_config[0]);
		ge2d_config->src_planes[0].addr =
			vf->canvas0_config[0].phy_addr;
		ge2d_config->src_planes[0].w = vf->canvas0_config[0].width;
		ge2d_config->src_planes[0].h = vf->canvas0_config[0].height;

		if (vf->plane_num > 1) {
			canvas_config_config(ppmgr_src_canvas[1] & 0xff,
					     &vf->canvas0_config[1]);
			ge2d_config->src_planes[1].addr =
				vf->canvas0_config[1].phy_addr;
			ge2d_config->src_planes[1].w =
				vf->canvas0_config[1].width;
			ge2d_config->src_planes[1].h =
				vf->canvas0_config[1].height;
		}
		if (vf->plane_num > 2) {
			canvas_config_config(ppmgr_src_canvas[2] & 0xff,
					     &vf->canvas0_config[2]);
			ge2d_config->src_planes[2].addr =
				vf->canvas0_config[2].phy_addr;
			ge2d_config->src_planes[2].w =
				vf->canvas0_config[2].width;
			ge2d_config->src_planes[2].h =
				vf->canvas0_config[2].height;
		}
		canvas_id =
			(ppmgr_src_canvas[0] & 0xff)
			| ((ppmgr_src_canvas[1] & 0xff) << 8)
			| ((ppmgr_src_canvas[2] & 0xff) << 16);
		ge2d_config->src_para.canvas_index = canvas_id;

	} else {
		canvas_read(vf->canvas0Addr & 0xff, &cs0);
		canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1);
		canvas_read((vf->canvas0Addr >> 16) & 0xff, &cs2);
		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;
		ge2d_config->src_para.canvas_index = vf->canvas0Addr;
	}
	canvas_read(ppmgr_dst_canvas[0] & 0xff, &cd);
	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->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 = 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 = 0xffffffff;
	ge2d_config->src_para.top = 0;
	ge2d_config->src_para.left = 0;
	ge2d_config->src_para.width = vf->width;
	ge2d_config->src_para.height = vf->height / 2;
	ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.canvas_index = ppmgr_dst_canvas[0];

	ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.format =
		GE2D_FORMAT_S8_Y | GE2D_LITTLE_ENDIAN;
	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.width = TB_DETECT_W;
	ge2d_config->dst_para.height = TB_DETECT_H;

	if (ge2d_context_config_ex(context, ge2d_config) < 0) {
		pr_err("++ge2d configing error.\n");
		context->config.h_scale_coef_type =
			h_scale_coef_type;
		context->config.v_scale_coef_type =
			v_scale_coef_type;
		return -1;
	}

	stretchblt_noalpha(context, 0, 0, vf->width,
			   vf->height / 2,
			   0, 0, TB_DETECT_W,
			   TB_DETECT_H);
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
	codec_mm_dma_flush((void *)detect_buf[tb_buff_wptr].vaddr,
			   TB_DETECT_W * TB_DETECT_H,
			   DMA_FROM_DEVICE);
#endif
	context->config.h_scale_coef_type =
		h_scale_coef_type;
	context->config.v_scale_coef_type =
		v_scale_coef_type;
	return 1;
}
#endif

#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA
static int copy_phybuf_to_file(ulong phys, u32 size,
			       struct file *fp, loff_t pos)
{
	u32 span = SZ_1M;
	u8 *p;
	int remain_size = 0;
	ssize_t ret;

	remain_size = size;
	while (remain_size > 0) {
		if (remain_size < span)
			span = remain_size;
		p = codec_mm_vmap(phys, PAGE_ALIGN(span));
		if (!p) {
			PPMGRVPP_INFO("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)
			PPMGRVPP_INFO("vfs write failed!\n");
		phys += span;
		codec_mm_unmap_phyaddr(p);
		remain_size -= span;

		PPMGRVPP_INFO("pos: %lld, phys: %lx, remain_size: %d\n",
			      pos, phys, remain_size);
	}
	return 0;
}
#endif

/*
 * 1: yuv
 * 0: afbc
 */
static void notify_data(u32 format)
{
	char *provider_name = vf_get_provider_name(RECEIVER_NAME);

	while (provider_name) {
		if (!vf_get_provider_name(provider_name))
			break;
		provider_name =
			vf_get_provider_name(provider_name);
	}
	if (provider_name)
		vf_notify_provider_by_name(provider_name,
					   VFRAME_EVENT_RECEIVER_NEED_NO_COMP,
					   (void *)&format);
	if (ppmgr_device.ppmgr_debug & 1)
		PPMGRVPP_INFO("%s provider_name: %s, data: %d\n",
			      __func__, provider_name, format);
}

#endif

static void process_vf_rotate(struct vframe_s *vf,
			      struct ge2d_context_s *context,
			      struct config_para_ex_s *ge2d_config)
{
	struct vframe_s *new_vf;
	struct ppframe_s *pp_vf;
	u32 canvas_id;
	u32 dst_canvas_id;
	u32 buf_start, buf_start_ref, buf_end;
	u32 buf_size;
	int ret = 0;
	unsigned int cur_angle = 0;
	int interlace_mode;
#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA
	struct file *filp_scr = NULL;
	struct file *filp_dst = NULL;
	char source_path[64];
	char dst_path[64];
	int count;
	int result = 0;
#endif
	if (ppmgr_device.debug_ppmgr_flag)
		pr_info("ppmgr:rotate\n");

	if (!context) {
		pr_info("ppmgr:rotate but no ge2d context\n");
		return;
	}

	new_vf = vfq_pop(&q_free);

	if (unlikely(!new_vf || !vf)) {
		pr_info("ppmgr:rotate null, %p, %p\n", new_vf, vf);
		return;
	}

	memset(new_vf, 0, sizeof(struct vframe_s));

	interlace_mode = vf->type & VIDTYPE_TYPEMASK;

	pp_vf = to_ppframe(new_vf);
	pp_vf->angle = 0;
	cur_angle = (ppmgr_device.videoangle + vf->orientation) % 4;
	pp_vf->dec_frame = (ppmgr_device.bypass ||
			    (cur_angle == 0 &&
			     ppmgr_device.mirror_flag == 0)) ? vf : NULL;

	if (ppmgr_device.ppmgr_debug & 4)
		need_data_notify = true;

	if (vf->type & VIDTYPE_MVC)
		pp_vf->dec_frame = vf;

	if (vf->type & VIDTYPE_PIC)
		pp_vf->dec_frame = vf;

	/* canvas invalid, force bypass */
	if (!local_canvas_status)
		pp_vf->dec_frame = vf;

	if (vf->type & VIDTYPE_COMPRESS) {
		if (cur_angle != 0 && (vf->type & VIDTYPE_NO_DW)) {
			vf->type &= ~VIDTYPE_SUPPORT_COMPRESS;
			vf->type_original &= ~VIDTYPE_SUPPORT_COMPRESS;
			if (need_data_notify) {
				need_data_notify = false;
				notify_data(1);
				PPMGRVPP_INFO("notify need yuv data\n");
			}
		}
		if (vf->canvas0Addr == (u32)-1 && vf->plane_num == 0) {
			pp_vf->dec_frame = vf;
			if (ppmgr_device.ppmgr_debug & 1)
				PPMGRVPP_INFO("vframe is compress!\n");
		}
		if (dumpfirstframe == 1)
			dumpfirstframe = 2;
	}

	if (pp_vf->dec_frame) {
		if (is_decontour_supported() && local_canvas_status) {
			ret = decontour_pre_process(vf);
			if (ret != 0)
				dc_print("pre process fail ret=%d\n", ret);
		}
		/* bypass mode */
		*new_vf = *vf;
		vfq_push(&q_ready, new_vf);
		return;
	}

	if (!(ppmgr_device.ppmgr_debug & 8)) {
		if (vf->width > vf->height) {
			if (cur_angle % 2) {
				ppmgr_device.disp_width = MAX_HEIGHT;
				ppmgr_device.disp_height = MAX_WIDTH;
			} else {
				ppmgr_device.disp_width = MAX_WIDTH;
				ppmgr_device.disp_height = MAX_HEIGHT;
			}
		} else {
			if (cur_angle % 2) {
				ppmgr_device.disp_width = MAX_WIDTH;
				ppmgr_device.disp_height = MAX_HEIGHT;
			} else {
				ppmgr_device.disp_width = MAX_HEIGHT;
				ppmgr_device.disp_height = MAX_WIDTH;
			}
		}
	}

	ret = ppmgr_buffer_init(vf->mem_sec);

	if (ret < 0) {
		pp_vf->dec_frame = vf;
		*new_vf = *vf;
		vfq_push(&q_ready, new_vf);
		return;
	}
	if (vf->type & VIDTYPE_V4L_EOS) {
		new_vf->type |= VIDTYPE_V4L_EOS;
		goto rotate_done;
	}
#ifdef INTERLACE_DROP_MODE
	if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM) {
		ppmgr_vf_put_dec(vf);
		vfq_push(&q_free, new_vf);
		return;
	}
	pp_vf->angle = cur_angle;
	if (interlace_mode)
		new_vf->duration = vf->duration * 2;
	else
		new_vf->duration = vf->duration;
#else
	pp_vf->angle = cur_angle;
	new_vf->duration = vf->duration;
#endif
	new_vf->ready_jiffies64 = vf->ready_jiffies64;
	new_vf->ready_clock[0] = vf->ready_clock[0];
	new_vf->ready_clock[1] = vf->ready_clock[1];
	new_vf->ready_clock[2] = vf->ready_clock[2];
	new_vf->duration_pulldown = vf->duration_pulldown;
	new_vf->mem_sec = vf->mem_sec;
	new_vf->pts = vf->pts;
	new_vf->pts_us64 = vf->pts_us64;
	new_vf->bitdepth = BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8;
	new_vf->signal_type = vf->signal_type;
	new_vf->omx_index = vf->omx_index;
	new_vf->type = VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE
			| VIDTYPE_VIU_FIELD;
	new_vf->canvas0Addr = -1;
	new_vf->canvas1Addr = -1;
	new_vf->plane_num = 1;
	dst_canvas_id = ppmgr_dst_canvas[0];
	new_vf->orientation = vf->orientation;
	new_vf->flag = vf->flag;

	if (interlace_mode == VIDTYPE_INTERLACE_TOP ||
	    interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
		vf->height >>= 1;

	vf_rotate_adjust(vf, new_vf, cur_angle);

	get_ppmgr_buf_info(&buf_start_ref, &buf_size);
	buf_start = buf_start_ref;
	buf_start += pp_vf->index * ppmgr_device.disp_width
			* ppmgr_device.disp_height * 3;
	buf_end = buf_start;
	buf_end += ppmgr_device.disp_width * ppmgr_device.disp_height * 3;
	if (buf_end > buf_start_ref + buf_size) {
		PPMGRVPP_INFO
			("%s dst overflow, pool:%x %x, buf:%x %x, id:%d\n",
			 __func__,
			 buf_start_ref, buf_size,
			 buf_start, buf_end - buf_start,
			 pp_vf->index);
		buf_start = buf_start_ref;
	}
	new_vf->canvas0_config[0].phy_addr = buf_start;
	new_vf->canvas0_config[0].width = new_vf->width * 3;
	new_vf->canvas0_config[0].height = new_vf->height;
	new_vf->canvas0_config[0].block_mode = 0;
	new_vf->canvas0_config[0].endian = 0;

	canvas_config(dst_canvas_id,
		      (ulong)buf_start,
		      new_vf->width * 3,
		      new_vf->height,
		      CANVAS_ADDR_NOWRAP,
		      CANVAS_BLKMODE_LINEAR);

	memset(ge2d_config, 0, sizeof(struct config_para_ex_s));

	if (pp_vf->index < VF_POOL_SIZE &&
	    buf_status[pp_vf->index].dirty == 1) {
		buf_status[pp_vf->index].dirty = 0;

		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.canvas_index = dst_canvas_id;
		ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
		ge2d_config->src_para.format = GE2D_FORMAT_S24_YUV444;
		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 = new_vf->width;
		ge2d_config->src_para.height = new_vf->height;

		ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;

		ge2d_config->dst_para.canvas_index = dst_canvas_id;
		ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
		ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
		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.width = new_vf->width;
		ge2d_config->dst_para.height = new_vf->height;
		ge2d_config->mem_sec = vf->mem_sec;

		if (ge2d_context_config_ex(context, ge2d_config) < 0) {
			PPMGRVPP_ERR("++ge2d configing error.\n");
			ppmgr_vf_put_dec(vf);
			vfq_push(&q_free, new_vf);
			return;
		}
		fillrect(context, 0, 0,
				new_vf->width, new_vf->height, 0x008080ff);
		memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
	}

	/* data operating. */
	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;

	if (vf->canvas0Addr != (u32)-1) {
		canvas_copy(vf->canvas0Addr & 0xff,
			ppmgr_src_canvas[0]);
		canvas_copy((vf->canvas0Addr >> 8) & 0xff,
			ppmgr_src_canvas[1]);
		canvas_copy((vf->canvas0Addr >> 16) & 0xff,
			ppmgr_src_canvas[2]);
	} else if (vf->plane_num > 0) {
		canvas_config_config(ppmgr_src_canvas[0],
			&vf->canvas0_config[0]);
		if (vf->plane_num > 1)
			canvas_config_config(ppmgr_src_canvas[1],
				&vf->canvas0_config[1]);
		if (vf->plane_num > 2)
			canvas_config_config(ppmgr_src_canvas[2],
				&vf->canvas0_config[2]);
	}
	canvas_id = ppmgr_src_canvas[0]
		| (ppmgr_src_canvas[1] << 8)
		| (ppmgr_src_canvas[2] << 16);

	ge2d_config->src_para.canvas_index = canvas_id;
	if (ppmgr_color_format == 0)
		ge2d_config->src_para.format = get_input_format(vf);
	else
		ge2d_config->src_para.format = ppmgr_color_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 = 0xffffffff;
	ge2d_config->src_para.top = 0;
	ge2d_config->src_para.left = 0;
	ge2d_config->src_para.width = vf->width;
	ge2d_config->src_para.height = vf->height;

	ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;

	ge2d_config->dst_para.canvas_index = dst_canvas_id;
	ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
	/*ge2d_config->dst_para.mem_type = CANVAS_OSD0;*/
	/*ge2d_config->dst_para.format = GE2D_FORMAT_M24_YUV420;*/
	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;

	if (cur_angle == 1) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.x_rev = 1;
	} else if (cur_angle == 2) {
		ge2d_config->dst_para.x_rev = 1;
		ge2d_config->dst_para.y_rev = 1;
	} else if (cur_angle == 3) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.y_rev = 1;
	}
	if (ppmgr_device.mirror_flag != 0) {
		if (ppmgr_device.mirror_flag == 1) {
			if (cur_angle == 2 || cur_angle == 3)
				ge2d_config->dst_para.y_rev = 0;
			else
				ge2d_config->dst_para.y_rev = 1;
		} else if (ppmgr_device.mirror_flag == 2) {
			if (cur_angle == 1 || cur_angle == 2)
				ge2d_config->dst_para.x_rev = 0;
			else
				ge2d_config->dst_para.x_rev = 1;
		}
	}
	ge2d_config->dst_para.color = 0;
	ge2d_config->dst_para.top = 0;
	ge2d_config->dst_para.left = 0;
	if (ppmgr_device.receiver == 0) {
		ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
		ge2d_config->dst_para.width = new_vf->width;
		ge2d_config->dst_para.height = new_vf->height;
	} else {
		ge2d_config->dst_para.format = ppmgr_device.receiver_format;
		ge2d_config->dst_para.width = ppmgr_device.canvas_width;
		ge2d_config->dst_para.height = ppmgr_device.canvas_height;
		new_vf->width = ppmgr_device.canvas_width;
		new_vf->height = ppmgr_device.canvas_height;
		ge2d_config->dst_para.x_rev = 0;
		ge2d_config->dst_para.y_rev = 0;
		ge2d_config->dst_xy_swap = 0;
	}
	ge2d_config->mem_sec = vf->mem_sec;
	if (ge2d_context_config_ex(context, ge2d_config) < 0) {
		PPMGRVPP_ERR("++ge2d configing error.\n");
		vfq_push(&q_free, new_vf);
		return;
	}

	pp_vf->angle = cur_angle;
	stretchblt_noalpha(context, 0, 0, vf->width, vf->height,
			   0, 0, new_vf->width, new_vf->height);
#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA
	if (strstr(ppmgr_device.dump_path, "scr") && dumpfirstframe == 2) {
		count = strlen(ppmgr_device.dump_path);
		ppmgr_device.dump_path[count] = count_scr;
		sprintf(source_path, "%s_scr", ppmgr_device.dump_path);
		count_scr++;
		filp_scr = filp_open(source_path, O_RDWR | O_CREAT, 0666);
		if (IS_ERR(filp_scr)) {
			PPMGRVPP_INFO("open %s failed\n", source_path);
		} else {
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
			result = copy_phybuf_to_file(vf->canvas0_config[0]
						     .phy_addr,
						     vf->canvas0_config[0]
						     .width
						     * vf->canvas0_config[0]
						     .height,
						     filp_scr, 0);
#endif
			if (result < 0)
				PPMGRVPP_INFO("write %s failed\n",
					      source_path);
			PPMGRVPP_INFO("scr addr: %lx, width: %d, height: %d\n",
				      vf->canvas0_config[0].phy_addr,
				      vf->canvas0_config[0].width,
				      vf->canvas0_config[0].height);
			PPMGRVPP_INFO("dump source type: %d\n",
				      get_input_format(vf));
			filp_close(filp_scr, NULL);
		}
	}
#endif
rotate_done:
	ppmgr_vf_put_dec(vf);
	new_vf->source_type = VFRAME_SOURCE_TYPE_PPMGR;
	if (dumpfirstframe != 2)
		vfq_push(&q_ready, new_vf);
#ifdef CONFIG_AMLOGIC_ENABLE_VIDEO_PIPELINE_DUMP_DATA
	if (strstr(ppmgr_device.dump_path, "dst") && dumpfirstframe == 2) {
		count = strlen(ppmgr_device.dump_path);
		ppmgr_device.dump_path[count] = count_dst;
		sprintf(dst_path, "%s_dst", ppmgr_device.dump_path);
		count_dst++;
		filp_dst = filp_open(dst_path,	O_RDWR | O_CREAT, 0666);
		if (IS_ERR(filp_dst)) {
			PPMGRVPP_INFO("open %s failed\n", dst_path);
		} else {
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
			ulong phy_addr = new_vf->canvas0_config[0].phy_addr;
			u32 copy_size = new_vf->canvas0_config[0].width *
				new_vf->canvas0_config[0].height;

			result = copy_phybuf_to_file
				(phy_addr, copy_size, filp_dst, 0);
			PPMGRVPP_INFO("dst addr: %lx, width: %d, height: %d\n",
				      phy_addr, new_vf->canvas0_config[0].width,
				      new_vf->canvas0_config[0].height);
			PPMGRVPP_INFO("dump dst type: %d\n",
				      get_input_format(new_vf));
#endif
			if (result < 0)
				PPMGRVPP_INFO("write %s failed\n", dst_path);
			filp_close(filp_dst, NULL);
		}
		if (count_dst >= ppmgr_device.ppmgr_debug)
			dumpfirstframe = 0;
	}
#endif

#ifdef DDD
	PPMGRVPP_WARN("rotate avail=%d, free=%d\n",
		      vfq_level(&q_ready), vfq_level(&q_free));
#endif
}

static void process_vf_change(struct vframe_s *vf,
			      struct ge2d_context_s *context,
			      struct config_para_ex_s *ge2d_config)
{
	static struct vframe_s temp_vf;
	struct ppframe_s *pp_vf = to_ppframe(vf);
	struct canvas_s cs0, cs1, cs2, cd;
	u32 canvas_id;
	u32 dst_canvas_id;
	int interlace_mode;
	unsigned int temp_angle = 0;
	unsigned int cur_angle = 0;
	int ret = 0;
	u32 buf_start, buf_start_ref, buf_end;
	u32 buf_size;

	if (!context || !vf || !local_canvas_status)
		return;

	cur_angle = (ppmgr_device.videoangle + vf->orientation) % 4;
	if (cur_angle == 0 || ppmgr_device.bypass)
		return;

	ret = ppmgr_buffer_init(vf->mem_sec);

	if (ret < 0)
		return;
	if (vf->type & VIDTYPE_V4L_EOS)
		goto change_done;
	temp_vf.duration = vf->duration;
	temp_vf.duration_pulldown = vf->duration_pulldown;
	temp_vf.pts = vf->pts;
	temp_vf.pts_us64 = vf->pts_us64;
	temp_vf.flag = vf->flag;
	temp_vf.bitdepth = BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8;
	temp_vf.signal_type = vf->signal_type;
	temp_vf.omx_index = vf->omx_index;
	temp_vf.type = VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE
			| VIDTYPE_VIU_FIELD;
	temp_vf.canvas0Addr = ass_index;
	temp_vf.canvas1Addr = ass_index;
	temp_angle = (cur_angle >= pp_vf->angle) ?
		(cur_angle - pp_vf->angle) :
		(cur_angle + 4 - pp_vf->angle);

	pp_vf->angle = cur_angle;
	vf_rotate_adjust(vf, &temp_vf, temp_angle);

	get_ppmgr_buf_info(&buf_start_ref, &buf_size);
	buf_start = buf_start_ref;
	buf_start += VF_POOL_SIZE * ppmgr_device.disp_width
			* ppmgr_device.disp_height * 3;
	buf_end = buf_start;
	buf_end += ppmgr_device.disp_width * ppmgr_device.disp_height * 3;
	if (buf_end > buf_start_ref + buf_size) {
		PPMGRVPP_INFO("%s tmp overflow, pool:%x %x, buf:%x %x\n",
			__func__,
			buf_start_ref, buf_size,
			buf_start, buf_end - buf_start);
		return;
	}

	canvas_config(ass_index,
		      (ulong)buf_start,
		      temp_vf.width * 3,
		      temp_vf.height,
		      CANVAS_ADDR_NOWRAP,
		      CANVAS_BLKMODE_LINEAR);

	interlace_mode = vf->type & VIDTYPE_TYPEMASK;

	if (interlace_mode == VIDTYPE_INTERLACE_TOP ||
	    interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
		vf->height >>= 1;

	if (vf->canvas0Addr != (u32)-1) {
		canvas_copy(vf->canvas0Addr & 0xff,
			ppmgr_src_canvas[0]);
		canvas_copy((vf->canvas0Addr >> 8) & 0xff,
			ppmgr_src_canvas[1]);
		canvas_copy((vf->canvas0Addr >> 16) & 0xff,
			ppmgr_src_canvas[2]);
	} else if (vf->plane_num > 0) {
		canvas_config_config(ppmgr_src_canvas[0],
			&vf->canvas0_config[0]);
		if (vf->plane_num > 1)
			canvas_config_config(ppmgr_src_canvas[1],
				&vf->canvas0_config[1]);
		if (vf->plane_num > 2)
			canvas_config_config(ppmgr_src_canvas[2],
				&vf->canvas0_config[2]);
	}
	canvas_id = ppmgr_src_canvas[0]
		| (ppmgr_src_canvas[1] << 8)
		| (ppmgr_src_canvas[2] << 16);

	memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
	/* data operating. */
	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;

	if (pp_vf->dec_frame) {
		canvas_read(canvas_id & 0xff, &cs0);
		canvas_read((canvas_id >> 8) & 0xff, &cs1);
		canvas_read((canvas_id >> 16) & 0xff, &cs2);
		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;
		ge2d_config->src_para.format = get_input_format(vf);
	} else {
		canvas_read(canvas_id & 0xff, &cs0);
		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_para.format = get_input_format(vf);
	}

	canvas_read(temp_vf.canvas0Addr & 0xff, &cd);
	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->src_key.key_enable = 0;
	ge2d_config->src_key.key_mask = 0;
	ge2d_config->src_key.key_mode = 0;

	ge2d_config->src_para.canvas_index = canvas_id;
	ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;

	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 = 0;
	ge2d_config->src_para.left = 0;
	ge2d_config->src_para.width = vf->width;
	ge2d_config->src_para.height = vf->height;
	ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;

	ge2d_config->dst_para.canvas_index = temp_vf.canvas0Addr;
	ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
	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 = temp_vf.width;
	ge2d_config->dst_para.height = temp_vf.height;
	ge2d_config->mem_sec = vf->mem_sec;

	if (temp_angle == 1) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.x_rev = 1;
	} else if (temp_angle == 2) {
		ge2d_config->dst_para.x_rev = 1;
		ge2d_config->dst_para.y_rev = 1;
	} else if (temp_angle == 3) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.y_rev = 1;
	}
	if (ge2d_context_config_ex(context, ge2d_config) < 0) {
		PPMGRVPP_ERR("++ge2d configing error.\n");
		/*vfq_push(&q_free, new_vf);*/
		return;
	}

	stretchblt_noalpha(context, 0, 0, vf->width,
			   vf->height,
			   0, 0, temp_vf.width,
			   temp_vf.height);

	vf->type = VIDTYPE_VIU_444
			| VIDTYPE_VIU_SINGLE_PLANE
			| VIDTYPE_VIU_FIELD;

	vf->canvas0Addr = -1;
	vf->canvas1Addr = -1;
	vf->plane_num = 1;
	dst_canvas_id = ppmgr_dst_canvas[0];

	get_ppmgr_buf_info(&buf_start_ref, &buf_size);
	buf_start = buf_start_ref;
	buf_start += pp_vf->index * ppmgr_device.disp_width
			* ppmgr_device.disp_height * 3;
	buf_end = buf_start;
	buf_end += ppmgr_device.disp_width * ppmgr_device.disp_height * 3;
	if (buf_end > buf_start_ref + buf_size) {
		PPMGRVPP_INFO
			("%s dst overflow, pool:%x %x, buf:%x %x, id:%d\n",
			 __func__,
			 buf_start_ref, buf_size,
			 buf_start, buf_end - buf_start,
			 pp_vf->index);
		buf_start = buf_start_ref;
	}
	vf->canvas0_config[0].phy_addr = buf_start;
	vf->canvas0_config[0].width = temp_vf.width * 3;
	vf->canvas0_config[0].height = temp_vf.height;
	vf->canvas0_config[0].block_mode = 0;
	vf->canvas0_config[0].endian = 0;

	canvas_config(dst_canvas_id,
		      (ulong)buf_start,
		      temp_vf.width * 3,
		      temp_vf.height,
		      CANVAS_ADDR_NOWRAP,
		      CANVAS_BLKMODE_LINEAR);

	memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
	/* data operating. */
	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;

	canvas_read(temp_vf.canvas0Addr & 0xff, &cd);
	ge2d_config->src_planes[0].addr = cd.addr;
	ge2d_config->src_planes[0].w = cd.width;
	ge2d_config->src_planes[0].h = cd.height;
	ge2d_config->src_para.format = GE2D_FORMAT_S24_YUV444;

	canvas_read(dst_canvas_id & 0xff, &cd);
	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->src_key.key_enable = 0;
	ge2d_config->src_key.key_mask = 0;
	ge2d_config->src_key.key_mode = 0;

	ge2d_config->src_para.canvas_index = dst_canvas_id;
	ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;

	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 = 0;
	ge2d_config->src_para.left = 0;
	ge2d_config->src_para.width = temp_vf.width;
	ge2d_config->src_para.height = temp_vf.height;

	vf->width = temp_vf.width;
	vf->height = temp_vf.height;
	ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;

	ge2d_config->dst_para.canvas_index = vf->canvas0Addr;
	ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
	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 = vf->width;
	ge2d_config->dst_para.height = vf->height;
	ge2d_config->mem_sec = vf->mem_sec;

	if (ge2d_context_config_ex(context, ge2d_config) < 0) {
		PPMGRVPP_ERR("++ge2d configing error.\n");
		/*vfq_push(&q_free, new_vf);*/
		return;
	}
	stretchblt_noalpha(context, 0, 0, temp_vf.width, temp_vf.height, 0, 0,
			   vf->width, vf->height);
	/*vf->duration = 0 ;*/
change_done:
	if (pp_vf->dec_frame) {
		ppmgr_vf_put_dec(pp_vf->dec_frame);
		pp_vf->dec_frame = 0;
	}
	vf->ratio_control = 0;
}

static struct task_struct *task;

void ppmgr_vf_peek_dec_debug(void)
{
	struct vframe_s *vf;

	vf = ppmgr_vf_peek_dec();
	PPMGRVPP_INFO("peek vf=%p\n", vf);
}

static int ppmgr_task(void *data)
{
	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
	struct ge2d_context_s *context = create_ge2d_work_queue();
	struct config_para_ex_s ge2d_config;
#ifdef PPMGR_TB_DETECT
	bool first_frame = true;
	int first_frame_type = 0;
	unsigned int skip_picture = 0;
	u8 cur_invert = 0;
	u8 last_type = 0;
	u32 last_width = 0;
	u32 last_height = 0;
	u8 reset_tb = 0;
	u32 init_mute = 0;
#endif
	unsigned int newvideoangle = 0;
	memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
	sched_setscheduler(current, SCHED_FIFO, &param);
	allow_signal(SIGTERM);

	while (down_interruptible(&ppmgr_device.ppmgr_sem) == 0) {
		struct vframe_s *vf = NULL;

		if (ppmgr_device.debug_ppmgr_flag)
			PPMGRVPP_INFO("task_1, dec %p, free %d, avail %d\n",
				      ppmgr_vf_peek_dec(),
				      vfq_level(&q_free),
				      vfq_level(&q_ready));

		if (kthread_should_stop() || ppmgr_quit_flag) {
			PPMGRVPP_INFO("task: quit\n");
			break;
		}

		local_canvas_init();

		if (still_picture_notify) {
			still_picture_notify = 0;
			/* disableVideoLayer(); */
#ifdef CONFIG_AMLOGIC_MEDIA_VIDEO
			vf = get_cur_dispbuf();
#endif
			if (!is_valid_ppframe(to_ppframe(vf)))
				continue;
			if ((vf->type & VIDTYPE_COMPRESS) &&
			    vf->plane_num < 1 &&
			    vf->canvas0Addr == (u32)-1) {
				continue;
			}
			process_vf_change(vf, context, &ge2d_config);
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
			vf_notify_receiver(PROVIDER_NAME,
				   VFRAME_EVENT_PROVIDER_PROPERTY_CHANGED,
				   NULL);
#endif
			vfq_lookup_start(&q_ready);
			vf = vfq_peek(&q_ready);

			while (vf) {
				vf = vfq_pop(&q_ready);
				process_vf_change(vf, context, &ge2d_config);
				vf = vfq_peek(&q_ready);
			}
			vfq_lookup_end(&q_ready);
			/* enableVideoLayer(); */
			up(&ppmgr_device.ppmgr_sem);
			continue;
		}

		/* process when we have both input and output space */
		while (ppmgr_vf_peek_dec() &&
		       !vfq_empty(&q_free) && (!ppmgr_blocking)) {
			int ret = 0;

			vf = ppmgr_vf_get_dec();
			if (!vf)
				break;
			if (ppmgr_secure_debug)
				vf->mem_sec = ppmgr_secure_mode;
			if (vf && ppmgr_device.started) {
				ppmgr_device.videoangle =
					(ppmgr_device.angle +
					 ppmgr_device.orientation) % 4;
				set_property_change(1);
				ppmgr_device.started = 0;
			} else {
				newvideoangle = (ppmgr_device.angle + ppmgr_device.orientation) % 4;
				if (newvideoangle == ppmgr_device.videoangle)
					set_property_change(0);
			}
			vf->video_angle = (ppmgr_device.angle
					   + ppmgr_device.orientation
					   + vf->orientation) % 4;
#ifdef PPMGR_TB_DETECT
			if (vf->source_type !=
				VFRAME_SOURCE_TYPE_OTHERS)
				goto SKIP_DETECT;
			if ((vf->width * vf->height)
				> (1920 * 1088) || vf->mem_sec == 1) {
				/* greater than (1920 * 1088) or secure mode,
				 * do not detect
				 */
				goto SKIP_DETECT;
			}
			if (vf->type & VIDTYPE_V4L_EOS)
				goto SKIP_DETECT;
			if (first_frame) {
				last_type = vf->type & VIDTYPE_TYPEMASK;
				last_width = vf->width;
				last_height = vf->height;
				first_frame_type = last_type;
				tb_first_frame_type = last_type;
				first_frame = false;
				reset_tb = 0;
				skip_picture = 0;
				cur_invert = 0;
				init_mute = tb_init_mute;
				atomic_set(&tb_skip_flag, 1);
				atomic_set(&tb_reset_flag, 0);
				if (ppmgr_device.tb_detect & 0xe)
					PPMGRVPP_INFO("tb first type: %d\n",
						      last_type);
			} else if ((last_type ==
				    (vf->type & VIDTYPE_TYPEMASK)) &&
				    last_type) {
				/* interlace seq changed */
				first_frame_type =
					vf->type & VIDTYPE_TYPEMASK;
				tb_first_frame_type =
					first_frame_type;
				reset_tb = 1;
				/* keep old invert */
				if (ppmgr_device.tb_detect & 0xe)
					pr_info("PPMGRVPP: info: tb interlace seq change, old: %d, new: %d, invert: %d\n",
						last_type,
						first_frame_type,
						cur_invert);
			} else if (last_type == 0 &&
				   (vf->type & VIDTYPE_TYPEMASK) != 0) {
				/* prog -> interlace changed */
				first_frame_type =
					vf->type & VIDTYPE_TYPEMASK;
				tb_first_frame_type =
					first_frame_type;
				reset_tb = 1;
				if (ppmgr_device.tb_detect & 0xe)
					pr_info("PPMGRVPP: info: tb prog -> interlace, new type: %d, invert: %d\n",
						first_frame_type, cur_invert);
				/* not invert */
				cur_invert = 0;
			} else if ((last_width != vf->width ||
				    last_height != vf->height) &&
				    (vf->type & VIDTYPE_TYPEMASK) != 0) {
				/* size changed and next seq is interlace */
				first_frame_type =
					vf->type & VIDTYPE_TYPEMASK;
				tb_first_frame_type =
					first_frame_type;
				reset_tb = 1;
				/* keep old invert */
				if (ppmgr_device.tb_detect & 0xe)
					pr_info("PPMGRVPP: info: tb size change new type: %d, invert: %d\n",
						first_frame_type, cur_invert);
			} else if (last_type != 0 &&
				   (vf->type & VIDTYPE_TYPEMASK) == 0) {
				/* interlace -> prog changed */
				if (ppmgr_device.tb_detect & 0xe)
					pr_info("PPMGRVPP: info: tb interlace -> prog, invert: %d\n",
						cur_invert);
				/* not invert */
				cur_invert = 0;
			}
			last_type = vf->type & VIDTYPE_TYPEMASK;
			last_width = vf->width;
			last_height = vf->height;
			if (ppmgr_device.tb_detect) {
				ret = 0;
				if (tb_buffer_status < 0)
					goto SKIP_DETECT;
				if (tb_buffer_status == 0)
					if (tb_buffer_init() <= 0)
						goto SKIP_DETECT;
				ppmgr_device.tb_detect_buf_len = tb_buffer_len;
				vf->type = (vf->type & ~TB_DETECT_MASK);
				if (init_mute > 0) {
					init_mute--;
					atomic_set(&tb_skip_flag, 1);
					goto SKIP_DETECT;
				}
				if (last_type == 0) {/* cur type is prog */
					skip_picture++;
					cur_invert = 0;
					goto SKIP_DETECT;
				}
				vf->type |= cur_invert << TB_DETECT_MASK_BIT;
				if (reset_tb) {
					/* wait tb task done */
					while (tb_buff_wptr >= 5 &&
					       tb_buff_rptr <=
					       (tb_buff_wptr - 5) &&
					       atomic_read(&tb_run_flag) == 1)
						usleep_range(4000, 5000);
					atomic_set(&detect_status, tb_idle);
					tb_buff_wptr = 0;
					tb_buff_rptr = 0;
					atomic_set(&tb_detect_flag,
						   TB_DETECT_NC);
					atomic_set(&tb_reset_flag, 1);
					atomic_set(&tb_skip_flag, 1);
					skip_picture = 0;
					reset_tb  = 0;
					if (ppmgr_device.tb_detect & 0xc)
						PPMGRVPP_INFO("tb reset\n");
				}
				if (atomic_read(&detect_status) == tb_done &&
				    skip_picture >=
				    ppmgr_device.tb_detect_period &&
				    last_type == first_frame_type) {
					int tbf_flag =
						atomic_read(&tb_detect_flag);
					u8 old_invert = cur_invert;

					atomic_set(&detect_status, tb_idle);
					tb_buff_wptr = 0;
					tb_buff_rptr = 0;
					skip_picture = 0;
					if (tbf_flag == TB_DETECT_TBF &&
					    first_frame_type ==
						VIDTYPE_INTERLACE_TOP) {
						/* TBF sams as BFF */
						vf->type |= TB_DETECT_INVERT
							<< TB_DETECT_MASK_BIT;
						cur_invert = 1;
					} else if (tbf_flag == TB_DETECT_BFF &&
						   first_frame_type ==
						   VIDTYPE_INTERLACE_TOP) {
						vf->type |= TB_DETECT_INVERT
							<< TB_DETECT_MASK_BIT;
						cur_invert = 1;
					} else if (tbf_flag == TB_DETECT_TFF &&
						   first_frame_type ==
						   VIDTYPE_INTERLACE_BOTTOM) {
						vf->type |= TB_DETECT_INVERT
							<< TB_DETECT_MASK_BIT;
						cur_invert = 1;
					} else if (tbf_flag != TB_DETECT_NC) {
						cur_invert = 0;
					}
					vf->type = (vf->type & ~TB_DETECT_MASK);
					vf->type |= cur_invert <<
						TB_DETECT_MASK_BIT;
					if (old_invert != cur_invert &&
					    (ppmgr_device.tb_detect & 0xe))
						pr_info("PPMGRVPP: info: tb detect flag: %d->%d, invert: %d->%d\n",
							tb_last_flag,
							tbf_flag,
							old_invert,
							cur_invert);
					else if (tb_last_flag != tbf_flag &&
						 (ppmgr_device.tb_detect
						  & 0xc))
						pr_info("PPMGRVPP: info: tb detect flag %d->%d, invert: %d\n",
							tb_last_flag,
							tbf_flag,
							cur_invert);
					tb_last_flag = tbf_flag;
					atomic_set(&tb_detect_flag,
						   TB_DETECT_NC);
				}
				if (tb_buff_wptr == 0 &&
				    last_type != first_frame_type) {
					skip_picture++;
					atomic_set(&tb_skip_flag, 1);
					if (ppmgr_device.tb_detect & 0xc)
						pr_info("PPMGRVPP: info: tb detect skip case1\n");
					goto SKIP_DETECT;
				}
				if (tb_buff_wptr < tb_buffer_len &&
				    atomic_read(&tb_run_flag) == 1) {
					ret = process_vf_tb_detect(vf, context,
								   &ge2d_config
								   );
				} else {
					if (ppmgr_device.tb_detect & 0xc)
						pr_info("PPMGRVPP: info: tb detect skip case2\n");
					atomic_set(&tb_skip_flag, 1);
					skip_picture++;
				}
				if (ret > 0) {
					tb_buff_wptr++;
					if (tb_buff_wptr >= 5 &&
					    atomic_read(&detect_status)
					    == tb_idle)
						atomic_set(&detect_status,
							   tb_running);
					if (tb_buff_wptr >= 5)
						up(&ppmgr_device.tb_sem);
				}
			} else {
				reset_tb = 1;
				skip_picture++;
				cur_invert = 0;
				if (init_mute > 0)
					init_mute--;
			}
SKIP_DETECT:
			if (skip_picture > ppmgr_device.tb_detect_period)
				skip_picture = ppmgr_device.tb_detect_period;
#endif
			process_vf_rotate(vf, context, &ge2d_config);
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
			vf_notify_receiver(PROVIDER_NAME,
					   VFRAME_EVENT_PROVIDER_VFRAME_READY,
					   NULL);
#endif
		}

		if (ppmgr_blocking) {
			/***recycle buffer to decoder***/
			vf_local_init();
#ifdef CONFIG_AMLOGIC_MEDIA_VFM
			vf_light_unreg_provider(&ppmgr_vf_prov);
#endif
			PPMGRVPP_WARN("ppmgr rebuild light-unregister_1\n");
			vf_unreg_provider(&ppmgr_vf_prov);
			omx_cur_session = 0xffffffff;
			usleep_range(4000, 5000);
			vf_reg_provider(&ppmgr_vf_prov);
			vf_local_init();
			if (is_decontour_supported())
				decontour_buf_reset();
			ppmgr_blocking = false;
			up(&ppmgr_device.ppmgr_sem);
			PPMGRVPP_WARN("ppmgr rebuild light-unregister_2\n");
			PPMGRVPP_WARN("ppmgr, reset, free %d, avail %d\n",
				      vfq_level(&q_free),
				      vfq_level(&q_ready));
		}
		if (ppmgr_device.debug_ppmgr_flag)
			PPMGRVPP_WARN("ppmgr, dec %p, free %d, avail %d\n",
				      ppmgr_vf_peek_dec(),
				      vfq_level(&q_free),
				      vfq_level(&q_ready));

#ifdef DDD
		PPMGRVPP_WARN("process paused, dec %p, free %d, avail %d\n",
			      ppmgr_vf_peek_dec(),
			      vfq_level(&q_free),
			      vfq_level(&q_ready));
#endif
	}

	destroy_ge2d_work_queue(context);
	local_canvas_uninit();

	while (!kthread_should_stop()) {
		/* may not call stop, wait..
		 * it is killed by SIGTERM,eixt on down_interruptible
		 * if not call stop,this thread may on do_exit and
		 * kthread_stop may not work good;
		 */
		/* msleep(10); */
		usleep_range(9000, 10000);
	}
	return 0;
}

/************************************************
 *
 *   init functions.
 *
 *************************************************/
static int vout_notify_callback(struct notifier_block *block,
				unsigned long cmd, void *para)
{
	if (cmd == VOUT_EVENT_MODE_CHANGE)
		ppmgr_device.vinfo = get_current_vinfo();

	return 0;
}

static struct notifier_block vout_notifier = {.notifier_call =
		vout_notify_callback, };
int ppmgr_register(void)
{
	vf_ppmgr_init_provider();
	vf_ppmgr_init_receiver();
	vf_ppmgr_reg_receiver();
	vout_register_client(&vout_notifier);
	return 0;
}

int ppmgr_buffer_uninit(void)
{
	if (!ppmgr_device.use_reserved &&
	    ppmgr_device.buffer_start) {
		PPMGRVPP_INFO("cma free addr is %x , size is  %x\n",
			      (unsigned int)ppmgr_device.buffer_start,
			      (unsigned int)ppmgr_device.buffer_size);
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
		codec_mm_free_for_dma("ppmgr", ppmgr_device.buffer_start);
#endif
		ppmgr_device.buffer_start = 0;
		ppmgr_device.buffer_size = 0;
	}

	ppmgr_buffer_status = 0;
	return 0;
}

int ppmgr_buffer_init(int secure_mode)
{
	u32 canvas_width, canvas_height;
	int mem_sec_flag;
	int flags;
	struct vinfo_s vinfo = {.width = 1280, .height = 720, };

	mem_sec_flag = secure_mode == 1 ? CODEC_MM_FLAGS_TVP :
					  CODEC_MM_FLAGS_CMA_CLEAR;
	/* int flags = CODEC_MM_FLAGS_DMA; */
	flags = CODEC_MM_FLAGS_DMA | mem_sec_flag;

	switch (ppmgr_buffer_status) {
	case 0:/*not config*/
		break;
	case 1:/*config before , return ok*/
		return 0;
	case 2:/*config fail, won't retry , return failure*/
		return -1;
	default:
		return -1;
	}
	if (ppmgr_device.mirror_flag) {
		PPMGRVPP_INFO("CMA memory force config fail\n");
		ppmgr_buffer_status = 2;
		return -1;
	}
	if (!ppmgr_device.use_reserved &&
	    ppmgr_device.buffer_start == 0) {
		PPMGRVPP_INFO("reserved memory config fail,use CMA.\n");
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
		ppmgr_device.buffer_start =
			codec_mm_alloc_for_dma("ppmgr",
					       MM_ALLOC_SIZE / PAGE_SIZE,
					       0, flags);
		ppmgr_device.buffer_size = MM_ALLOC_SIZE;
		PPMGRVPP_INFO("cma memory is %x , size is  %x\n",
			      (unsigned int)ppmgr_device.buffer_start,
			      (unsigned int)ppmgr_device.buffer_size);
#endif
		if (ppmgr_device.buffer_start == 0) {
			ppmgr_buffer_status = 2;
			PPMGRVPP_ERR("cma memory config fail\n");
			return -1;
		}
	}

	ppmgr_buffer_status = 1;
	ppmgr_device.vinfo = get_current_vinfo();
	if (IS_ERR_OR_NULL(ppmgr_device.vinfo)) {
		pr_info("PPMGRVPP: info: failed to get_current_vinfo! Try to MAKE one!");
		ppmgr_device.vinfo = &vinfo;
	}

	if (ppmgr_device.disp_width == 0) {
		if (ppmgr_device.vinfo->width <= MAX_WIDTH)
			ppmgr_device.disp_width = ppmgr_device.vinfo->width;
		else
			ppmgr_device.disp_width = MAX_WIDTH;
	}

	if (ppmgr_device.disp_height == 0) {
		if (ppmgr_device.vinfo->height <= MAX_HEIGHT)
			ppmgr_device.disp_height = ppmgr_device.vinfo->height;
		else
			ppmgr_device.disp_height = MAX_HEIGHT;
	}
	if (get_platform_type() == PLATFORM_MID_VERTICAL) {
		int DISP_SIZE =
			ppmgr_device.disp_width >
			ppmgr_device.disp_height ?
			ppmgr_device.disp_width :
			ppmgr_device.disp_height;

		canvas_width = (DISP_SIZE + 0x1f) & ~0x1f;
		canvas_height = (DISP_SIZE + 0x1f) & ~0x1f;
	} else {
		canvas_width = (ppmgr_device.disp_width + 0x1f) & ~0x1f;
		canvas_height = (ppmgr_device.disp_height + 0x1f) & ~0x1f;
	}
	ppmgr_device.canvas_width = canvas_width;
	ppmgr_device.canvas_height = canvas_height;

	ppmgr_blocking = false;
	ppmgr_inited = true;
	//up(&ppmgr_device.ppmgr_sem);
	return 0;
}

int start_ppmgr_task(void)
{
	/*    if (get_cpu_type()>= MESON_CPU_TYPE_MESON6)*/
	/*	    switch_mod_gate_by_name("ge2d", 1);*/
	/*#endif*/
#ifdef PPMGR_TB_DETECT
	start_tb_task();
#endif
	if (!task) {
		vf_local_init();
		ppmgr_blocking = false;
		ppmgr_inited = true;
		ppmgr_buffer_status = 0;
		ppmgr_quit_flag = false;
		task = kthread_run(ppmgr_task, 0, "ppmgr");
	}
	if (!IS_ERR_OR_NULL(task))
		task_running = 1;
	else
		task = NULL;
	return 0;
}

void stop_ppmgr_task(void)
{
#ifdef PPMGR_TB_DETECT
	stop_tb_task();
#endif
	if (!IS_ERR_OR_NULL(task)) {
		/* send_sig(SIGTERM, task, 1); */
		ppmgr_quit_flag = true;
		up(&ppmgr_device.ppmgr_sem);
		kthread_stop(task);
		ppmgr_quit_flag = false;
		task = NULL;
	}
	task_running = 0;
	vf_local_init();
	/*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
	/*    switch_mod_gate_by_name("ge2d", 0);*/
	/*#endif*/
}

#ifdef PPMGR_TB_DETECT
static int tb_buffer_init(void)
{
	ulong i;
	//int flags = CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_CMA_CLEAR;
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
	int flags = 0;
#endif

	if (tb_buffer_status)
		return tb_buffer_status;

	if (ppmgr_src_canvas[0] < 0 || ppmgr_src_canvas[1] < 0 ||
	    ppmgr_src_canvas[2] < 0 ||
	    ppmgr_dst_canvas[0] < 0 || ppmgr_dst_canvas[1] < 0) {
		PPMGRVPP_INFO("%s canvas check fail\n", __func__);
		return -1;
	}

	if (tb_buffer_start == 0) {
		if (!ppmgr_device.tb_detect_buf_len)
			ppmgr_device.tb_detect_buf_len = 8;
		tb_buffer_len = ppmgr_device.tb_detect_buf_len;
		tb_buffer_size = TB_DETECT_H * TB_DETECT_W
			* tb_buffer_len;
		tb_buffer_size = PAGE_ALIGN(tb_buffer_size);
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
		tb_buffer_start = codec_mm_alloc_for_dma("tb_detect",
							 tb_buffer_size
							 / PAGE_SIZE,
							 0, flags);
		PPMGRVPP_INFO("tb cma memory %x, size %x, item %d\n",
			      (unsigned int)tb_buffer_start,
			      (unsigned int)tb_buffer_size,
			      tb_buffer_len);
#endif
		if (tb_buffer_start == 0) {
			PPMGRVPP_ERR("tb cma memory config fail\n");
			tb_buffer_status = -1;
			return -1;
		}
		for (i = 0; i < tb_buffer_len; i++) {
			detect_buf[i].paddr = tb_buffer_start +
				TB_DETECT_H * TB_DETECT_W * i;
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
			detect_buf[i].vaddr =
				(ulong)codec_mm_vmap(detect_buf[i].paddr,
						     TB_DETECT_H
						     * TB_DETECT_W);
			if (ppmgr_device.tb_detect & 0xc) {
				PPMGRVPP_INFO("detect buff(%ld)", i);
				PPMGRVPP_INFO(" paddr: %lx, vaddr: %lx\n",
					      detect_buf[i].paddr,
					      detect_buf[i].vaddr);
			}
#endif
		}
	}
	tb_buffer_status = 1;
	return 1;
}

static int tb_buffer_uninit(void)
{
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
	int i;
#endif

	if (tb_buffer_start) {
#ifdef CONFIG_AMLOGIC_MEDIA_CODEC_MM
		PPMGRVPP_INFO("tb cma free addr is %x, size is %x\n",
			      (unsigned int)tb_buffer_start,
			      (unsigned int)tb_buffer_size);
		for (i = 0; i < tb_buffer_len; i++) {
			if (detect_buf[i].vaddr) {
				codec_mm_unmap_phyaddr((u8 *)detect_buf[i]
						       .vaddr);
				detect_buf[i].vaddr = 0;
			}
		}
		codec_mm_free_for_dma("tb_detect", tb_buffer_start);
#endif
		tb_buffer_start = 0;
		tb_buffer_size = 0;
	}
	tb_buffer_status = 0;
	return 0;
}

static void tb_detect_init(void)
{
	memset(detect_buf, 0, sizeof(detect_buf));
	atomic_set(&detect_status, tb_idle);
	atomic_set(&tb_detect_flag, TB_DETECT_NC);
	atomic_set(&tb_reset_flag, 0);
	atomic_set(&tb_skip_flag, 0);
	atomic_set(&tb_run_flag, 1);
	tb_last_flag = TB_DETECT_NC;
	tb_buff_wptr = 0;
	tb_buff_rptr = 0;
	tb_buffer_status = 0;
	tb_buffer_start = 0;
	tb_buffer_size = 0;
	tb_first_frame_type = 0;
	tb_quit_flag = false;
	tb_init_mute =
		ppmgr_device.tb_detect_init_mute;
}

static int tb_task(void *data)
{
	int tbff_flag;
	struct tbff_stats *tb_reg = NULL;
	ulong y5fld[5];
	int is_top;
	int inited = 0;
	int i;
	int inter_flag;
	static const char * const detect_type[] = {"NC", "TFF", "BFF", "TBF"};
	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};

	sched_setscheduler(current, SCHED_FIFO, &param);

	inter_flag = 0;
	tb_reg = kmalloc(sizeof(*tb_reg), GFP_KERNEL);
	if (!tb_reg) {
		PPMGRVPP_INFO("tb_reg malloc fail\n");
		return 0;
	}
	memset(tb_reg, 0, sizeof(struct tbff_stats));

	if (gfunc)
		gfunc->stats_init(tb_reg, TB_DETECT_H, TB_DETECT_W);
	allow_signal(SIGTERM);
	while (down_interruptible(&ppmgr_device.tb_sem) == 0) {
		if (kthread_should_stop() || tb_quit_flag)
			break;
		if (tb_buff_rptr == 0) {
			if (atomic_read(&tb_reset_flag) != 0)
				inited = 0;
			atomic_set(&tb_reset_flag, 0);
			if (gfunc)
				gfunc->fwalg_init(inited);
		}
		inited = 1;
		is_top = (tb_buff_rptr & 1) ? 0 : 1;
		/* new -> old */
		y5fld[0] = detect_buf[tb_buff_rptr + 4].vaddr;
		y5fld[1] = detect_buf[tb_buff_rptr + 3].vaddr;
		y5fld[2] = detect_buf[tb_buff_rptr + 2].vaddr;
		y5fld[3] = detect_buf[tb_buff_rptr + 1].vaddr;
		y5fld[4] = detect_buf[tb_buff_rptr].vaddr;
		if (gfunc) {
			if (IS_ERR_OR_NULL(tb_reg)) {
				kfree(tb_reg);
				PPMGRVPP_INFO("tb_reg is NULL!\n");
				return 0;
			}
			for (i = 0; i < 5; i++) {
				if (IS_ERR_OR_NULL((void *)y5fld[i])) {
					PPMGRVPP_INFO("y5fld[%d] is NULL!\n",
						      i);
					inter_flag = 1;
					break;
				}
			}
			if (inter_flag) {
				inter_flag = 0;
				continue;
			}
			gfunc->stats_get(y5fld, tb_reg);
		}
		is_top = is_top ^ 1;
		tbff_flag = -1;
		if (gfunc)
			tbff_flag = gfunc->fwalg_get(tb_reg, is_top,
						    (tb_first_frame_type
						     == 3) ? 0 : 1,
						    tb_buff_rptr,
						    atomic_read(&tb_skip_flag),
						    (ppmgr_device.tb_detect
						     & 0x8) ? 1 : 0);

		if (tb_buff_rptr == 0)
			atomic_set(&tb_skip_flag, 0);

		if (tbff_flag < -1 || tbff_flag > 2) {
			PPMGRVPP_ERR("get tb detect flag error: %d\n",
				     tbff_flag);
		}

		if (tbff_flag == -1 && gfunc)
			tbff_flag = gfunc->majority_get();

		if (tbff_flag == -1)
			tbff_flag = TB_DETECT_NC;
		else if (tbff_flag == 0)
			tbff_flag = TB_DETECT_TFF;
		else if (tbff_flag == 1)
			tbff_flag = TB_DETECT_BFF;
		else if (tbff_flag == 2)
			tbff_flag = TB_DETECT_TBF;
		else
			tbff_flag = TB_DETECT_NC;
		tb_buff_rptr++;
		if (tb_buff_rptr > (tb_buffer_len - 5) &&
		    atomic_read(&detect_status) == tb_running) {
			atomic_set(&tb_detect_flag, tbff_flag);
			if (ppmgr_device.tb_detect & 0xc)
				PPMGRVPP_INFO("get tb detect final flag: %s\n",
					      detect_type[tbff_flag]);
			atomic_set(&detect_status, tb_done);
		}
	}
	atomic_set(&tb_run_flag, 0);
	kfree(tb_reg);
	while (!kthread_should_stop())
		usleep_range(9000, 10000);
	return 0;
}

int start_tb_task(void)
{
	if (!tb_detect_task) {
		tb_detect_init();
		tb_detect_task = kthread_run(tb_task, 0, "tb_detect");
	}
	if (!IS_ERR_OR_NULL(tb_detect_task))
		tb_task_running = 1;
	else
		tb_detect_task = NULL;
	return 0;
}

void stop_tb_task(void)
{
	if (!IS_ERR_OR_NULL(tb_detect_task)) {
		tb_quit_flag = true;
		up(&ppmgr_device.tb_sem);
		/* send_sig(SIGTERM, tb_detect_task, 1); */
		kthread_stop(tb_detect_task);
		tb_quit_flag = false;
		//sema_init(&tb_sem, val);
		tb_detect_task = NULL;
	}
	tb_task_running = 0;
}
#endif

void get_tb_detect_status(void)
{
#ifdef PPMGR_TB_DETECT
	static const char * const tb_type[] = {"Prog", "Top", "N/C", "Bottom"};
	static const char * const detect_type[] = {"NC", "TFF", "BFF", "TBF"};
	static const char * const status_str[] = {
		"Idle", "Run", "Done", "N/C"};
	u32 status = atomic_read(&detect_status);
	u32 flag = atomic_read(&tb_detect_flag);
	u32 reset_flag = atomic_read(&tb_reset_flag);

	PPMGRVPP_INFO("T/B detect buffer addr: 0x%x, size: 0x%x\n",
		      (unsigned int)tb_buffer_start,
		      (unsigned int)tb_buffer_size);
	PPMGRVPP_INFO("T/B detect canvas: %x, len: %d, buff status: %d\n",
		      (unsigned int)ppmgr_dst_canvas[0],
		      tb_buffer_len,
		      (unsigned int)tb_buffer_status);
	PPMGRVPP_INFO("T/B detect buffer wptr: %d, rptr: %d, reset: %d\n",
		      tb_buff_wptr, tb_buff_rptr, reset_flag);
	PPMGRVPP_INFO("T/B detect first frame type: %s, period: %d\n",
		      tb_type[tb_first_frame_type],
		      ppmgr_device.tb_detect_period);
	PPMGRVPP_INFO("T/B detect status: %s, cur flag: %s, last flag: %s\n",
		      status_str[status], detect_type[flag],
		      detect_type[tb_last_flag]);
	PPMGRVPP_INFO("T/B detect init mute is %d\n", tb_init_mute);
	PPMGRVPP_INFO("T/B detect tb_detect_task: %p, running: %d\n",
		      tb_detect_task, tb_task_running);
	PPMGRVPP_INFO("current T/B detect mode is %d\n",
		      ppmgr_device.tb_detect);
#endif
}

int RegisterTB_Function(struct TB_DetectFuncPtr *func, const char *ver)
{
	int ret = -1;
#ifdef PPMGR_TB_DETECT
	mutex_lock(&tb_mutex);
	PPMGRVPP_INFO("%s: gfunc %p, func: %p, ver:%s\n",
		      __func__, gfunc, func, ver);
	if (!gfunc && func) {
		gfunc = func;
		ret = 0;
	}
	mutex_unlock(&tb_mutex);
#endif
	return ret;
}
EXPORT_SYMBOL(RegisterTB_Function);

int UnRegisterTB_Function(struct TB_DetectFuncPtr *func)
{
	int ret = -1;
#ifdef PPMGR_TB_DETECT
	mutex_lock(&tb_mutex);
	PPMGRVPP_INFO("%s: gfunc %p, func: %p\n",
		      __func__, gfunc, func);
	if (func && func == gfunc) {
		gfunc = NULL;
		ret = 0;
	}
	mutex_unlock(&tb_mutex);
#endif
	return ret;
}
EXPORT_SYMBOL(UnRegisterTB_Function);

