// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * drivers/amlogic/media/deinterlace/deinterlace.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/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_irq.h>
#include <linux/uaccess.h>
#include <linux/of_fdt.h>
#include <linux/cma.h>
#include <linux/dma-map-ops.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
/* media module used media/registers/cpu_version.h since kernel 5.4 */
#include <linux/amlogic/media/registers/cpu_version.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin_v4l2.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vpu/vpu.h>
#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
#include <linux/amlogic/media/rdma/rdma_mgr.h>
#endif
#include <linux/amlogic/media/video_sink/video.h>
#include "register.h"
#include "register_nr4.h"
#include "deinterlace.h"
#include "deinterlace_dbg.h"
#include "nr_downscale.h"
#include "di_pps.h"
#include "di_pqa.h"

/*2018-07-18 add debugfs*/
#include <linux/seq_file.h>
#include <linux/debugfs.h>
/*2018-07-18 -----------*/
/*
#undef TRACE_INCLUDE_PATH
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE deinterlace_trace
#include <trace/define_trace.h>
*/

#define CREATE_TRACE_POINTS
#include "deinterlace_trace.h"

#ifdef DET3D
#include "detect3d.h"
#endif
#define ENABLE_SPIN_LOCK_ALWAYS

#include <linux/amlogic/gki_module.h>

#define DEVICE_NAME		"deinterlace"
#define CLASS_NAME		"deinterlace"

static struct di_pre_stru_s di_pre_stru;

static struct di_post_stru_s di_post_stru;

static DEFINE_SPINLOCK(di_lock2);

#define di_lock_irqfiq_save(irq_flag) \
	spin_lock_irqsave(&di_lock2, irq_flag)

#define di_unlock_irqfiq_restore(irq_flag) \
	spin_unlock_irqrestore(&di_lock2, irq_flag)

#ifdef SUPPORT_MPEG_TO_VDIN
static int mpeg2vdin_flag;
static int mpeg2vdin_en;
#endif

static int queue_print_flag = -1;
static int di_reg_unreg_cnt = 1000;
static bool overturn;
static bool check_start_drop_prog;
static bool mcpre_en = true;

static bool mc_mem_alloc;

static unsigned int di_pre_rdma_enable;
static struct mutex di_event_mutex;
static atomic_t di_flag_unreg;	//ary 2019-05-27
static atomic_t di_trig_free_mem;

static unsigned int di_force_bit_mode = 10;
module_param(di_force_bit_mode, uint, 0664);
MODULE_PARM_DESC(di_force_bit_mode, "force DI bit mode to 8 or 10 bit");

static bool full_422_pack;
/* destroy unnecessary frames before display */
static unsigned int hold_video;

static DEFINE_SPINLOCK(plist_lock);

static di_dev_t *de_devp;
static dev_t di_devno;
static struct class *di_clsp;

static const char version_s[] = "2019-06-20a: afbc switch from vpp";

static int bypass_state = 1;
static int bypass_all;
/*1:enable bypass pre,ei only;
 * 2:debug force bypass pre,ei for post
 */
static int bypass_pre;
static int bypass_trick_mode = 1;
static int bypass_3d = 1;
static int invert_top_bot;
static int skip_top_bot;/*1or2: may affect atv when bypass di*/

static int bypass_post;
static bool post_wr_en;
static unsigned int post_wr_support;
static int bypass_post_state;
/* 0, use di_wr_buf still;
 * 1, call pre_de_done_buf_clear to clear di_wr_buf;
 * 2, do nothing
 */

static int force_width;
static int force_height;
/* add avoid vframe put/get error */
static int di_blocking;
/*
 * bit[2]: enable bypass all when skip
 * bit[1:0]: enable bypass post when skip
 */
static int di_vscale_skip_enable;

/* 0: not support nr10bit, 1: support nr10bit */
static unsigned int nr10bit_support;

#ifdef RUN_DI_PROCESS_IN_IRQ
/*
 * di_process() run in irq,
 * di_reg_process(), di_unreg_process() run in kernel thread
 * di_reg_process_irq(), di_unreg_process_irq() run in irq
 * di_vf_put(), di_vf_peek(), di_vf_get() run in irq
 * di_receiver_event_fun() run in task or irq
 */
/*
 * important:
 * to set input2pre, VFRAME_EVENT_PROVIDER_VFRAME_READY of
 * vdin should be sent in irq
 */

static int input2pre;
/*false:process progress by field;
 * true: process progress by frame with 2 interlace buffer
 */
static int input2pre_buf_miss_count;
static int input2pre_proc_miss_count;
static int input2pre_throw_count;
static int input2pre_miss_policy;
/* 0, do not force pre_de_busy to 0, use di_wr_buf after de_irq happen;
 * 1, force pre_de_busy to 0 and call pre_de_done_buf_clear to clear di_wr_buf
 */
#endif
/*false:process progress by field;
 * bit0: process progress by frame with 2 interlace buffer
 * bit1: temp add debug for 3d process FA,1:bit0 force to 1;
 */
static int use_2_interlace_buff;
/* prog_proc_config,
 * bit[2:1]: when two field buffers are used,
 * 0 use vpp for blending ,
 * 1 use post_di module for blending
 * 2 debug mode, bob with top field
 * 3 debug mode, bot with bot field
 * bit[0]:
 * 0 "prog vdin" use two field buffers,
 * 1 "prog vdin" use single frame buffer
 * bit[4]:
 * 0 "prog frame from decoder/vdin" use two field buffers,
 * 1 use single frame buffer
 * bit[5]:
 * when two field buffers are used for decoder (bit[4] is 0):
 * 1,handle prog frame as two interlace frames
 * bit[6]:(bit[4] is 0,bit[5] is 0,use_2_interlace_buff is 0): 0,
 * process progress frame as field,blend by post;
 * 1, process progress frame as field,process by normal di
 */
static int prog_proc_config = (1 << 5) | (1 << 1) | 1;
/*
 * for source include both progressive and interlace pictures,
 * always use post_di module for blending
 */
#define is_handle_prog_frame_as_interlace(vframe) \
	(((prog_proc_config & 0x30) == 0x20) && \
	 ((vframe->type & VIDTYPE_VIU_422) == 0))

static int frame_count;
static int disp_frame_count;
static int start_frame_drop_count = 2;
/* static int start_frame_hold_count = 0; */

static int video_peek_cnt;
static unsigned long reg_unreg_timeout_cnt;
static int di_vscale_skip_count;
static int di_vscale_skip_count_real;
static int vpp_3d_mode;
static bool det3d_en;

#ifdef DET3D
static unsigned int det3d_mode;
static void set3d_view(enum tvin_trans_fmt trans_fmt, struct vframe_s *vf);
#endif
int get_current_vscale_skip_count(struct vframe_s *vf);

static void di_pq_parm_destroy(struct di_pq_parm_s *pq_ptr);
static struct di_pq_parm_s *di_pq_parm_create(struct am_pq_parm_s *);

static uint init_flag;/*flag for di buferr*/
static unsigned int reg_flag;/*flag for vframe reg/unreg*/
static unsigned int unreg_cnt;/*cnt for vframe unreg*/
static unsigned int reg_cnt;/*cnt for vframe reg*/
static unsigned char active_flag;
static unsigned char recovery_flag;
static unsigned int force_recovery = 1;
static unsigned int force_recovery_count;
static unsigned int recovery_log_reason;
static unsigned int recovery_log_queue_idx;
static struct di_buf_s *recovery_log_di_buf;

static long same_field_top_count;
static long same_field_bot_count;
/* bit 0:
 * 0, keep 3 buffers in pre_ready_list for checking;
 * 1, keep 4 buffers in pre_ready_list for checking;
 */

static int post_hold_line = 8; /*2019-01-10: from VLSI feijun from 17 to 8*/
static int post_urgent = 1;

/*pre process speed debug */
static int pre_process_time;
static int di_receiver_event_fun(int type, void *data, void *arg);
static void di_uninit_buf(unsigned int disable_mirror);
static void log_buffer_state(unsigned char *tag);
/* static void put_get_disp_buf(void); */
static unsigned int isbypass_flag = true;
static unsigned int needbypass_flag = true;

static const
struct vframe_receiver_op_s di_vf_receiver = {
	.event_cb	= di_receiver_event_fun
};

static struct vframe_receiver_s di_vf_recv;

static vframe_t *di_vf_peek(void *arg);
static vframe_t *di_vf_get(void *arg);
static void di_vf_put(vframe_t *vf, void *arg);
static int di_event_cb(int type, void *data, void *private_data);
static int di_vf_states(struct vframe_states *states, void *arg);
static void di_process(void);
static void di_reg_process(void);
static void di_reg_process_irq(void);
static void di_unreg_process(void);
static void di_unreg_process_irq(void);
static struct queue_s *get_queue_by_idx(int idx);
static void dump_state(void);
static void recycle_keep_buffer(void);

static const
struct vframe_operations_s deinterlace_vf_provider = {
	.peek		= di_vf_peek,
	.get		= di_vf_get,
	.put		= di_vf_put,
	.event_cb	= di_event_cb,
	.vf_states	= di_vf_states,
};

static struct vframe_provider_s di_vf_prov;

static int di_sema_init_flag;
static struct semaphore di_sema;
static struct tasklet_struct di_pre_tasklet;
extern unsigned long long notrace sched_clock(void);

void trigger_pre_di_process(unsigned char idx)
{
	if (di_sema_init_flag == 0)
		return;
	if (!active_flag)
		return;
	log_buffer_state((idx == 'i') ? "irq" : ((idx == 'p') ?
		"put" : ((idx == 'r') ? "rdy" : "oth")));

	/* tasklet_hi_schedule(&di_pre_tasklet); */
	tasklet_schedule(&di_pre_tasklet);
	de_devp->jiffy = jiffies_64;
	/* trigger di_reg_process and di_unreg_process */
	if ((idx != 'p') && (idx != 'i'))
		up(&di_sema);
}

static unsigned int di_printk_flag;
#define DI_PRE_INTERVAL         (HZ / 100)

/*
 * progressive frame process type config:
 * 0, process by field;
 * 1, process by frame (only valid for vdin source whose
 * width/height does not change)
 */
static vframe_t *vframe_in[MAX_IN_BUF_NUM];
static vframe_t vframe_in_dup[MAX_IN_BUF_NUM];
static vframe_t vframe_local[MAX_LOCAL_BUF_NUM * 2];
static vframe_t vframe_post[MAX_POST_BUF_NUM];
static struct di_buf_s *cur_post_ready_di_buf;

static struct di_buf_s di_buf_local[MAX_LOCAL_BUF_NUM * 2];
static struct di_buf_s di_buf_in[MAX_IN_BUF_NUM];
static struct di_buf_s di_buf_post[MAX_POST_BUF_NUM];

/************For Write register**********************/
static unsigned int di_stop_reg_flag;
static unsigned int num_di_stop_reg_addr = 4;
static unsigned int di_stop_reg_addr[4] = {0};
static unsigned int di_dbg_mask;

unsigned int is_need_stop_reg(unsigned int addr)
{
	int idx = 0;

	if (di_stop_reg_flag) {
		for (idx = 0; idx < num_di_stop_reg_addr; idx++) {
			if (addr == di_stop_reg_addr[idx]) {
				pr_dbg("stop write addr: %x\n", addr);
				return 1;
			}
		}
	}

	return 0;
}

void DI_Wr(unsigned int addr, unsigned int val)
{
	if (is_need_stop_reg(addr))
		return;

	Wr(addr, val);
}

void DI_Wr_reg_bits(unsigned int adr, unsigned int val,
		unsigned int start, unsigned int len)
{
	if (is_need_stop_reg(adr))
		return;

	Wr_reg_bits(adr, val, start, len);
}

void DI_VSYNC_WR_MPEG_REG(unsigned int addr, unsigned int val)
{
	if (is_need_stop_reg(addr))
		return;
	if (post_wr_en && post_wr_support)
		DI_Wr(addr, val);
	else
		VSYNC_WR_MPEG_REG(addr, val);
}

void DI_VSYNC_WR_MPEG_REG_BITS(unsigned int addr, unsigned int val,
	unsigned int start, unsigned int len)
{
	if (is_need_stop_reg(addr))
		return;
	if (post_wr_en && post_wr_support)
		DI_Wr_reg_bits(addr, val, start, len);
	else
		VSYNC_WR_MPEG_REG_BITS(addr, val, start, len);
}

#if 0
unsigned int DI_POST_REG_RD(unsigned int addr)
{
	if (IS_ERR_OR_NULL(de_devp))
		return 0;
	if (de_devp->flags & DI_SUSPEND_FLAG) {
		pr_err("[DI] REG 0x%x access prohibited.\n", addr);
		return 0;
	}
	return VSYNC_RD_MPEG_REG(addr);
}
EXPORT_SYMBOL(DI_POST_REG_RD);

int DI_POST_WR_REG_BITS(u32 adr, u32 val, u32 start, u32 len)
{
	if (IS_ERR_OR_NULL(de_devp))
		return 0;
	if (de_devp->flags & DI_SUSPEND_FLAG) {
		pr_err("[DI] REG 0x%x access prohibited.\n", adr);
		return -1;
	}
	return VSYNC_WR_MPEG_REG_BITS(adr, val, start, len);
}
EXPORT_SYMBOL(DI_POST_WR_REG_BITS);
#else
static unsigned int lDI_POST_REG_RD(unsigned int addr)
{
	if (IS_ERR_OR_NULL(de_devp))
		return 0;
	if (de_devp->flags & DI_SUSPEND_FLAG) {
		pr_err("[DI] REG 0x%x access prohibited.\n", addr);
		return 0;
	}
	return VSYNC_RD_MPEG_REG(addr);
}

static int lDI_POST_WR_REG_BITS(u32 adr, u32 val, u32 start, u32 len)
{
	if (IS_ERR_OR_NULL(de_devp))
		return 0;
	if (de_devp->flags & DI_SUSPEND_FLAG) {
		pr_err("[DI] REG 0x%x access prohibited.\n", adr);
		return -1;
	}
	return VSYNC_WR_MPEG_REG_BITS(adr, val, start, len);
}

static const struct di_ext_ops di_ext = {
	.di_post_reg_rd             = lDI_POST_REG_RD,
	.di_post_wr_reg_bits        = lDI_POST_WR_REG_BITS,
	.post_update_mc		    = di_patch_post_update_mc,
};

#endif
/**********************************/

/*****************************
 *	 di attr management :
 *	 enable
 *	 mode
 *	 reg
 ******************************/
/*config attr*/
static ssize_t
show_config(struct device *dev,
	    struct device_attribute *attr, char *buf)
{
	int pos = 0;

	return pos;
}

static ssize_t
store_config(struct device *dev, struct device_attribute *attr, const char *buf,
	     size_t count);

static int run_flag = DI_RUN_FLAG_RUN;
static int pre_run_flag = DI_RUN_FLAG_RUN;
static int dump_state_flag;

/*2018-08-17 add debugfs*/
/*add debugfs: get dump_state parameter*/
struct di_pre_stru_s *get_di_pre_stru(void)
{
	return &di_pre_stru;
}

struct di_post_stru_s *get_di_post_stru(void)
{
	return &di_post_stru;
}

struct di_dev_s *get_di_de_devp(void)
{
	return de_devp;
}

const char *get_di_version_s(void)
{
	return version_s;
}

int get_di_di_blocking(void)
{
	return di_blocking;
}

int get_di_video_peek_cnt(void)
{
	return video_peek_cnt;
}

unsigned long get_di_reg_unreg_timeout_cnt(void)
{
	return reg_unreg_timeout_cnt;
}

uint get_di_init_flag(void)
{
	return init_flag;
}

unsigned char get_di_recovery_flag(void)
{
	return recovery_flag;
}

unsigned int get_di_recovery_log_reason(void)
{
	return recovery_log_reason;
}

unsigned int get_di_recovery_log_queue_idx(void)
{
	return recovery_log_queue_idx;
}

struct di_buf_s *get_di_recovery_log_di_buf(void)
{
	return recovery_log_di_buf;
}

struct vframe_s **get_di_vframe_in(void)
{
	return &vframe_in[0];
}

int get_di_dump_state_flag(void)
{
	return dump_state_flag;
}
/********************************************
 * function: for releas mirror mem
 *	need call this after unreg di
 ********************************************/
void di_trig_free_mirror_mem(void)
{
	if (dil_get_diff_ver_flag())
		return;

	if (atomic_read(&di_flag_unreg)
		&& de_devp->flag_cma != 2
		&& de_devp->flag_cma != 0
		&& active_flag) {
		atomic_set(&di_trig_free_mem, 1);
		up(&di_sema);
		di_pr_info("%s\n", __func__);
	}

	if (atomic_read(&di_flag_unreg)) {
		diwr_set_power_control_pst(0);
		di_pr_info("disable pst pd\n");
	}
}
EXPORT_SYMBOL(di_trig_free_mirror_mem);

u32 di_api_get_instance_id(void)
{
	u32 ret = 0;

	if (dil_get_diff_ver_flag())
		return ret;

	if (de_devp)
		ret = de_devp->instance_id;
	return ret;
}
EXPORT_SYMBOL(di_api_get_instance_id);

static bool di_free_mem_pre(void)
{

	if (!atomic_read(&di_trig_free_mem))
		return false;
	if (di_pre_stru.cma_alloc_done)
		return false;

	if (!di_post_stru.keep_buf
		|| !atomic_read(&di_flag_unreg)) {
		atomic_set(&di_trig_free_mem, 0);
		return false;
	}

	/*free mirror memory*/
	di_post_stru.keep_buf = NULL;

	return true;
}

/*--------------------------*/

static void parse_param_di(char *buf_orig, char **parm)
{
	char *ps, *token;
	unsigned int n = 0;
	char delim1[3] = " ";
	char delim2[2] = "\n";

	ps = buf_orig;
	strcat(delim1, delim2);
	while (1) {
		token = strsep(&ps, delim1);
		if (token == NULL)
			break;
		if (*token == '\0')
			continue;
		parm[n++] = token;
	}
}

static ssize_t
store_dbg(struct device *dev,
	  struct device_attribute *attr,
	  const char *buf, size_t count)
{
	u32 val;
	char *buf_orig, *parm[8] = {NULL};

	buf_orig = kstrdup(buf, GFP_KERNEL);
	parse_param_di(buf_orig, (char **)&parm);
	if (strncmp(buf, "buf", 3) == 0) {
		struct di_buf_s *di_buf_tmp = 0;

		if (kstrtoul(buf + 3, 16, (unsigned long *)&di_buf_tmp)) {
			kfree(buf_orig);
			return count;
		}
		dump_di_buf(di_buf_tmp);
	} else if (strncmp(buf, "vframe", 6) == 0) {
		vframe_t *vf = 0;

		if (kstrtoul(buf + 6, 16, (unsigned long *)&vf)) {
			kfree(buf_orig);
			return count;
		}
		dump_vframe(vf);
	} else if (strncmp(buf, "pool", 4) == 0) {
		unsigned long idx = 0;
		if (kstrtoul(buf + 4, 10, &idx)) {
			kfree(buf_orig);
			return count;
		}
		dump_pool(get_queue_by_idx(idx));
	} else if (strncmp(buf, "state", 4) == 0) {
		dump_state();
		pr_info("add new debugfs: cat /sys/kernel/debug/di/state\n");
	} else if (strncmp(buf, "prog_proc_config", 16) == 0) {
		if (buf[16] == '1')
			prog_proc_config = 1;
		else
			prog_proc_config = 0;
	} else if (strncmp(buf, "init_flag", 9) == 0) {
		if (buf[9] == '1')
			init_flag = 1;
		else
			init_flag = 0;
	} else if (strncmp(buf, "run", 3) == 0) {
		/* timestamp_pcrscr_enable(1); */
		run_flag = DI_RUN_FLAG_RUN;
	} else if (strncmp(buf, "pause", 5) == 0) {
		/* timestamp_pcrscr_enable(0); */
		run_flag = DI_RUN_FLAG_PAUSE;
	} else if (strncmp(buf, "step", 4) == 0) {
		run_flag = DI_RUN_FLAG_STEP;
	} else if (strncmp(buf, "prun", 4) == 0) {
		pre_run_flag = DI_RUN_FLAG_RUN;
	} else if (strncmp(buf, "ppause", 6) == 0) {
		pre_run_flag = DI_RUN_FLAG_PAUSE;
	} else if (strncmp(buf, "pstep", 5) == 0) {
		pre_run_flag = DI_RUN_FLAG_STEP;
	} else if (strncmp(buf, "dumpreg", 7) == 0) {
		if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) {
			dump_di_reg_g12();
			dump_afbcd_reg();
		} else
			pr_info("add new debugfs: cat /sys/kernel/debug/di/dumpreg\n");
	} else if (strncmp(buf, "dumpafbc", 8) == 0) {
		dump_afbcd_reg();
	} else if (strncmp(buf, "dumpmif", 7) == 0) {
		dump_mif_size_state(&di_pre_stru, &di_post_stru);
	} else if (strncmp(buf, "dumppostmif", 11) == 0) {
		dump_post_mif_reg();
	} else if (strncmp(buf, "recycle_buf", 11) == 0) {
		recycle_keep_buffer();
	} else if (strncmp(buf, "free_mirror", 11) == 0) {
		di_trig_free_mirror_mem();
	} else if (strncmp(buf, "recycle_post", 12) == 0) {
		if (di_vf_peek(NULL))
			di_vf_put(di_vf_get(NULL), NULL);
	} else if (strncmp(buf, "mem_map", 7) == 0) {
		dump_buf_addr(di_buf_local, MAX_LOCAL_BUF_NUM * 2);
	} else if (strncmp(buf, "afbc_on", 7) == 0) {
		if (kstrtoint(parm[1], 10, &val) < 0) {
			kfree(buf_orig);
			return count;
		}
		if (!val)
			afbc_sw(false);
		afbc_disable_flag = val > 0 ? 0:1;
		pr_info("afbc_disable_flag:%d\n", afbc_disable_flag);
	} else if (strncmp(buf, "reqafbc", 7) == 0) {
		val = di_request_afbc(true);
		di_pre_stru.wait_afbc = false;
		pr_info("request_afbc(%d)\n", val);
	} else if (strncmp(buf, "rlsafbc", 7) == 0) {
		val = di_request_afbc(false);
		di_pre_stru.wait_afbc = false;
		pr_info("rlease_afbc(%d)\n", val);
	} else {
		pr_info("DI no support cmd %s\n", buf);
		pr_info("supported cmd list:\n");
		pr_info("\t vframe\n");
		pr_info("\t state\n");
		pr_info("\t prog_proc_config 0/1\n");
		pr_info("\t init_flag 0/1\n");
		pr_info("\t run\n");
		pr_info("\t pause\n");
		pr_info("\t step\n");
		pr_info("\t prun\n");
		pr_info("\t ppause\n");
		pr_info("\t pstep\n");
		pr_info("\t dumpreg\n");
		pr_info("\t dumpmif\n");
		pr_info("\t dumppostmif\n");
		pr_info("\t recycle_buf\n");
		pr_info("\t recycle_post\n");
		pr_info("\t mem_map\n");
		pr_info("\t reqafbc\n");
		pr_info("\t rlsafbc\n");
		pr_info("\n trigger val\n");
	}

	kfree(buf_orig);
	return count;
}

static int di_read_canvas_reverse(char *str)
{
	unsigned char *ptr = str;

	pr_dbg("%s: bootargs is %s.\n", __func__, str);
	if (strstr(ptr, "1")) {
		invert_top_bot |= 0x1;
		overturn = true;
	} else {
		invert_top_bot &= (~0x1);
		overturn = false;
	}

	return 0;
}
__setup("video_reverse=", di_read_canvas_reverse);

static unsigned int di_debug_flag;/* enable rdma even di bypassed */
static unsigned char *di_log_buf;
static unsigned int di_log_wr_pos;
static unsigned int di_log_rd_pos;
static unsigned int di_log_buf_size;
static unsigned int di_printk_flag;
static unsigned int di_log_flag;
static unsigned int buf_state_log_threshold = 16;
static unsigned int buf_state_log_start;
/*  set to 1 by condition of "post_ready count < buf_state_log_threshold",
 * reset to 0 by set buf_state_log_threshold as 0
 */

static DEFINE_SPINLOCK(di_print_lock);

#define PRINT_TEMP_BUF_SIZE 128

int di_print_buf(char *buf, int len)
{
	unsigned long flags;
	int pos;
	int di_log_rd_pos_;

	if (di_log_buf_size == 0)
		return 0;

	spin_lock_irqsave(&di_print_lock, flags);
	di_log_rd_pos_ = di_log_rd_pos;
	if (di_log_wr_pos >= di_log_rd_pos)
		di_log_rd_pos_ += di_log_buf_size;

	for (pos = 0; pos < len && di_log_wr_pos < (di_log_rd_pos_ - 1);
	     pos++, di_log_wr_pos++) {
		if (di_log_wr_pos >= di_log_buf_size)
			di_log_buf[di_log_wr_pos - di_log_buf_size] = buf[pos];
		else
			di_log_buf[di_log_wr_pos] = buf[pos];
	}
	if (di_log_wr_pos >= di_log_buf_size)
		di_log_wr_pos -= di_log_buf_size;
	spin_unlock_irqrestore(&di_print_lock, flags);
	return pos;
}

static u64 cur_to_msecs(void)
{
	u64 cur = sched_clock();

	do_div(cur, NSEC_PER_MSEC);
	return cur;
}

/* static int log_seq = 0; */
int di_print(const char *fmt, ...)
{
	va_list args;
	int avail = PRINT_TEMP_BUF_SIZE;
	char buf[PRINT_TEMP_BUF_SIZE];
	int pos, len = 0;

	if (di_printk_flag & 1) {
		if (di_log_flag & DI_LOG_PRECISE_TIMESTAMP)
			pr_dbg("%llums:", cur_to_msecs());
		va_start(args, fmt);
		vprintk(fmt, args);
		va_end(args);
		return 0;
	}

	if (di_log_buf_size == 0)
		return 0;

/* len += snprintf(buf+len, avail-len, "%d:",log_seq++); */
	if (di_log_flag & DI_LOG_TIMESTAMP)
		len += snprintf(buf + len, avail - len, "%u:",
			jiffies_to_msecs(jiffies_64));

	va_start(args, fmt);
	len += vsnprintf(buf + len, avail - len, fmt, args);
	va_end(args);

	if ((avail - len) <= 0)
		buf[PRINT_TEMP_BUF_SIZE - 1] = '\0';

	pos = di_print_buf(buf, len);
/* pr_dbg("di_print:%d %d\n", di_log_wr_pos, di_log_rd_pos); */
	return pos;
}

static ssize_t read_log(char *buf)
{
	unsigned long flags;
	ssize_t read_size = 0;

	if (di_log_buf_size == 0)
		return 0;
/* pr_dbg("show_log:%d %d\n", di_log_wr_pos, di_log_rd_pos); */
	spin_lock_irqsave(&di_print_lock, flags);
	if (di_log_rd_pos < di_log_wr_pos)
		read_size = di_log_wr_pos - di_log_rd_pos;

	else if (di_log_rd_pos > di_log_wr_pos)
		read_size = di_log_buf_size - di_log_rd_pos;

	if (read_size > PAGE_SIZE)
		read_size = PAGE_SIZE;
	if (read_size > 0)
		memcpy(buf, di_log_buf + di_log_rd_pos, read_size);

	di_log_rd_pos += read_size;
	if (di_log_rd_pos >= di_log_buf_size)
		di_log_rd_pos = 0;
	spin_unlock_irqrestore(&di_print_lock, flags);
	return read_size;
}

static ssize_t
show_log(struct device *dev, struct device_attribute *attr, char *buf)
{
	return read_log(buf);
}

static ssize_t
store_log(struct device *dev,
	  struct device_attribute *attr,
	  const char *buf, size_t count)
{
	unsigned long flags, tmp;

	if (strncmp(buf, "bufsize", 7) == 0) {
		if (kstrtoul(buf + 7, 10, &tmp))
			return count;
		spin_lock_irqsave(&di_print_lock, flags);
		kfree(di_log_buf);
		di_log_buf = NULL;
		di_log_buf_size = 0;
		di_log_rd_pos = 0;
		di_log_wr_pos = 0;
		if (tmp >= 1024) {
			di_log_buf_size = 0;
			di_log_rd_pos = 0;
			di_log_wr_pos = 0;
			di_log_buf = kmalloc(tmp, GFP_KERNEL);
			if (di_log_buf)
				di_log_buf_size = tmp;
		}
		spin_unlock_irqrestore(&di_print_lock, flags);
		pr_dbg("di_store:set bufsize tmp %lu %u\n",
			tmp, di_log_buf_size);
	} else if (strncmp(buf, "printk", 6) == 0) {
		if (kstrtoul(buf + 6, 10, &tmp))
			return count;
		di_printk_flag = tmp;
	} else {
		di_print("%s", buf);
	}
	return 16;
}

static ssize_t
show_vframe_status(struct device *dev,
		   struct device_attribute *attr,
		   char *buf)
{
	int ret = 0;
	struct vframe_states states;

	ret = vf_get_states_by_name(VFM_NAME, &states);

	if (ret == 0) {
		ret += sprintf(buf + ret, "vframe_pool_size=%d\n",
			states.vf_pool_size);
		ret += sprintf(buf + ret, "vframe buf_free_num=%d\n",
			states.buf_free_num);
		ret += sprintf(buf + ret, "vframe buf_recycle_num=%d\n",
			states.buf_recycle_num);
		ret += sprintf(buf + ret, "vframe buf_avail_num=%d\n",
			states.buf_avail_num);
	} else {
		ret = 0;
		ret += sprintf(buf + ret, "vframe no states\n");
	}

	return ret;
}

static ssize_t show_tvp_region(struct device *dev,
			     struct device_attribute *attr, char *buff)
{
	ssize_t len = 0;

	len = sprintf(buff, "segment DI:%lx - %lx (size:0x%x)\n",
			de_devp->mem_start,
			de_devp->mem_start + de_devp->mem_size - 1,
			de_devp->mem_size);
	return len;
}

static ssize_t store_dump_mem(struct device *dev, struct device_attribute *attr,
			      const char *buf, size_t len);
static DEVICE_ATTR(config, 0640, show_config, store_config);
static DEVICE_ATTR(debug, 0200, NULL, store_dbg);
static DEVICE_ATTR(dump_pic, 0200, NULL, store_dump_mem);
static DEVICE_ATTR(log, 0640, show_log, store_log);
static DEVICE_ATTR(provider_vframe_status, 0444, show_vframe_status, NULL);
static DEVICE_ATTR(tvp_region, 0444, show_tvp_region, NULL);
/***************************
 * di buffer management
 ***************************/

static char *vframe_type_name[] = {
	"", "di_buf_in", "di_buf_loc", "di_buf_post"
};

static unsigned int default_width = 1920;
static unsigned int default_height = 1080;

/*
 * all buffers are in
 * 1) list of local_free_list,in_free_list,pre_ready_list,recycle_list
 * 2) di_pre_stru.di_inp_buf
 * 3) di_pre_stru.di_wr_buf
 * 4) cur_post_ready_di_buf
 * 5) (struct di_buf_s*)(vframe->private_data)->di_buf[]
 *
 * 6) post_free_list_head
 * 8) (struct di_buf_s*)(vframe->private_data)
 */

#define queue_t struct queue_s
static queue_t queue[QUEUE_NUM];
static struct di_buf_pool_s di_buf_pool[VFRAME_TYPE_NUM];
static struct queue_s *get_queue_by_idx(int idx)
{
	if (idx < QUEUE_NUM)
		return &(queue[idx]);
	else
		return NULL;
}

static void queue_init(int local_buffer_num)
{
	int i, j;

	for (i = 0; i < QUEUE_NUM; i++) {
		queue_t *q = &queue[i];

		for (j = 0; j < MAX_QUEUE_POOL_SIZE; j++)
			q->pool[j] = 0;

		q->in_idx = 0;
		q->out_idx = 0;
		q->num = 0;
		q->type = 0;
		if ((i == QUEUE_RECYCLE) ||
			(i == QUEUE_DISPLAY) ||
			(i == QUEUE_TMP)     ||
			(i == QUEUE_POST_DOING))
			q->type = 1;

		if ((i == QUEUE_LOCAL_FREE) && use_2_interlace_buff)
			q->type = 2;
	}
	if (local_buffer_num > 0) {
		di_buf_pool[VFRAME_TYPE_IN - 1].di_buf_ptr = &di_buf_in[0];
		di_buf_pool[VFRAME_TYPE_IN - 1].size = MAX_IN_BUF_NUM;

		di_buf_pool[VFRAME_TYPE_LOCAL-1].di_buf_ptr = &di_buf_local[0];
		di_buf_pool[VFRAME_TYPE_LOCAL - 1].size = local_buffer_num;

		di_buf_pool[VFRAME_TYPE_POST - 1].di_buf_ptr = &di_buf_post[0];
		di_buf_pool[VFRAME_TYPE_POST - 1].size = MAX_POST_BUF_NUM;
	}
}

struct di_buf_s *get_di_buf(int queue_idx, int *start_pos)
{
	queue_t *q = &(queue[queue_idx]);
	int idx = 0;
	unsigned int pool_idx, di_buf_idx;
	struct di_buf_s *di_buf = NULL;
	int start_pos_init = *start_pos;

	if (di_log_flag & DI_LOG_QUEUE)
		di_print("%s:<%d:%d,%d,%d> %d\n", __func__, queue_idx,
			q->num, q->in_idx, q->out_idx, *start_pos);

	if (q->type == 0) {
		if ((*start_pos) < q->num) {
			idx = q->out_idx + (*start_pos);
			if (idx >= MAX_QUEUE_POOL_SIZE)
				idx -= MAX_QUEUE_POOL_SIZE;

			(*start_pos)++;
		} else {
			idx = MAX_QUEUE_POOL_SIZE;
		}
	} else if ((q->type == 1) || (q->type == 2)) {
		for (idx = (*start_pos); idx < MAX_QUEUE_POOL_SIZE; idx++) {
			if (q->pool[idx] != 0) {
				*start_pos = idx + 1;
				break;
			}
		}
	}
	if (idx < MAX_QUEUE_POOL_SIZE) {
		pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1;
		di_buf_idx = q->pool[idx] & 0xff;
		if (pool_idx < VFRAME_TYPE_NUM) {
			if (di_buf_idx < di_buf_pool[pool_idx].size)
				di_buf =
					&(di_buf_pool[pool_idx].di_buf_ptr[
						  di_buf_idx]);
		}
	}

	if ((di_buf) && ((((pool_idx + 1) << 8) | di_buf_idx)
			 != ((di_buf->type << 8) | (di_buf->index)))) {
		pr_dbg("%s: Error (%x,%x)\n", __func__,
			(((pool_idx + 1) << 8) | di_buf_idx),
			((di_buf->type << 8) | (di_buf->index)));
		if (recovery_flag == 0) {
			recovery_log_reason = 1;
			recovery_log_queue_idx = (start_pos_init<<8)|queue_idx;
			recovery_log_di_buf = di_buf;
		}
		recovery_flag++;
		di_buf = NULL;
	}

	if (di_log_flag & DI_LOG_QUEUE) {
		if (di_buf)
			di_print("%s: 0x%p(%d,%d)\n", __func__, di_buf,
				 pool_idx, di_buf_idx);
		else
			di_print("%s: 0x%p\n", __func__, di_buf);
	}

	return di_buf;
}

static struct di_buf_s *get_di_buf_head(int queue_idx)
{
	queue_t *q = &(queue[queue_idx]);
	int idx;
	unsigned int pool_idx, di_buf_idx;
	struct di_buf_s *di_buf = NULL;

	if (di_log_flag & DI_LOG_QUEUE)
		di_print("%s:<%d:%d,%d,%d>\n", __func__, queue_idx,
			q->num, q->in_idx, q->out_idx);

	if (q->num > 0) {
		if (q->type == 0) {
			idx = q->out_idx;
		} else {
			for (idx = 0; idx < MAX_QUEUE_POOL_SIZE; idx++)
				if (q->pool[idx] != 0)
					break;
		}
		if (idx < MAX_QUEUE_POOL_SIZE) {
			pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1;
			di_buf_idx = q->pool[idx] & 0xff;
			if (pool_idx < VFRAME_TYPE_NUM) {
				if (di_buf_idx < di_buf_pool[pool_idx].size)
					di_buf = &(di_buf_pool[pool_idx].
						di_buf_ptr[di_buf_idx]);
			}
		}
	}

	if ((di_buf) && ((((pool_idx + 1) << 8) | di_buf_idx) !=
			 ((di_buf->type << 8) | (di_buf->index)))) {

		pr_dbg("%s: Error (%x,%x)\n", __func__,
			(((pool_idx + 1) << 8) | di_buf_idx),
			((di_buf->type << 8) | (di_buf->index)));

		if (recovery_flag == 0) {
			recovery_log_reason = 2;
			recovery_log_queue_idx = queue_idx;
			recovery_log_di_buf = di_buf;
		}
		recovery_flag++;
		di_buf = NULL;
	}

	if (di_log_flag & DI_LOG_QUEUE) {
		if (di_buf)
			di_print("%s: 0x%p(%d,%d)\n", __func__, di_buf,
				 pool_idx, di_buf_idx);
		else
			di_print("%s: 0x%p\n", __func__, di_buf);
	}

	return di_buf;
}

static void queue_out(struct di_buf_s *di_buf)
{
	int i;
	queue_t *q;

	if (di_buf == NULL) {

		pr_dbg("%s:Error\n", __func__);

		if (recovery_flag == 0)
			recovery_log_reason = 3;

		recovery_flag++;
		return;
	}
	if (di_buf->queue_index >= 0 && di_buf->queue_index < QUEUE_NUM) {
		q = &(queue[di_buf->queue_index]);

		if (di_log_flag & DI_LOG_QUEUE)
			di_print("%s:<%d:%d,%d,%d> 0x%p\n", __func__,
				 di_buf->queue_index, q->num, q->in_idx,
				 q->out_idx, di_buf);

		if (q->num > 0) {
			if (q->type == 0) {
				if (q->pool[q->out_idx] ==
				    ((di_buf->type << 8) | (di_buf->index))) {
					q->num--;
					q->pool[q->out_idx] = 0;
					q->out_idx++;
					if (q->out_idx >= MAX_QUEUE_POOL_SIZE)
						q->out_idx = 0;
					di_buf->queue_index = -1;
				} else {

					pr_dbg(
						"%s: Error (%d, %x,%x)\n",
						__func__,
						di_buf->queue_index,
						q->pool[q->out_idx],
						((di_buf->type << 8) |
						(di_buf->index)));

					if (recovery_flag == 0) {
						recovery_log_reason = 4;
						recovery_log_queue_idx =
							di_buf->queue_index;
						recovery_log_di_buf = di_buf;
					}
					recovery_flag++;
				}
			} else if (q->type == 1) {
				int pool_val =
					(di_buf->type << 8) | (di_buf->index);
				for (i = 0; i < MAX_QUEUE_POOL_SIZE; i++) {
					if (q->pool[i] == pool_val) {
						q->num--;
						q->pool[i] = 0;
						di_buf->queue_index = -1;
						break;
					}
				}
				if (i == MAX_QUEUE_POOL_SIZE) {

					pr_dbg("%s: Error\n", __func__);

					if (recovery_flag == 0) {
						recovery_log_reason = 5;
						recovery_log_queue_idx =
							di_buf->queue_index;
						recovery_log_di_buf = di_buf;
					}
					recovery_flag++;
				}
			} else if (q->type == 2) {
				int pool_val =
					(di_buf->type << 8) | (di_buf->index);
				if ((di_buf->index < MAX_QUEUE_POOL_SIZE) &&
				    (q->pool[di_buf->index] == pool_val)) {
					q->num--;
					q->pool[di_buf->index] = 0;
					di_buf->queue_index = -1;
				} else {

					pr_dbg("%s: Error\n", __func__);

					if (recovery_flag == 0) {
						recovery_log_reason = 5;
						recovery_log_queue_idx =
							di_buf->queue_index;
						recovery_log_di_buf = di_buf;
					}
					recovery_flag++;
				}
			}
		}
	} else {

		pr_dbg("%s: Error, queue_index %d is not right\n",
			__func__, di_buf->queue_index);

		if (recovery_flag == 0) {
			recovery_log_reason = 6;
			recovery_log_queue_idx = 0;
			recovery_log_di_buf = di_buf;
		}
		recovery_flag++;
	}

	if (di_log_flag & DI_LOG_QUEUE)
		di_print("%s done\n", __func__);

}

static void queue_in(struct di_buf_s *di_buf, int queue_idx)
{
	queue_t *q = NULL;

	if (di_buf == NULL) {
		pr_dbg("%s:Error\n", __func__);
		if (recovery_flag == 0) {
			recovery_log_reason = 7;
			recovery_log_queue_idx = queue_idx;
			recovery_log_di_buf = di_buf;
		}
		recovery_flag++;
		return;
	}
	if (di_buf->queue_index != -1) {
		pr_dbg("%s:%s[%d] Error, queue_index(%d) is not -1\n",
			__func__, vframe_type_name[di_buf->type],
			di_buf->index, di_buf->queue_index);
		if (recovery_flag == 0) {
			recovery_log_reason = 8;
			recovery_log_queue_idx = queue_idx;
			recovery_log_di_buf = di_buf;
		}
		recovery_flag++;
		return;
	}
	q = &(queue[queue_idx]);
	if (di_log_flag & DI_LOG_QUEUE)
		di_print("%s:<%d:%d,%d,%d> 0x%p\n", __func__, queue_idx,
			 q->num, q->in_idx, q->out_idx, di_buf);

	if (q->type == 0) {
		q->pool[q->in_idx] = (di_buf->type << 8) | (di_buf->index);
		di_buf->queue_index = queue_idx;
		q->in_idx++;
		if (q->in_idx >= MAX_QUEUE_POOL_SIZE)
			q->in_idx = 0;

		q->num++;
	} else if (q->type == 1) {
		int i;

		for (i = 0; i < MAX_QUEUE_POOL_SIZE; i++) {
			if (q->pool[i] == 0) {
				q->pool[i] = (di_buf->type<<8)|(di_buf->index);
				di_buf->queue_index = queue_idx;
				q->num++;
				break;
			}
		}
		if (i == MAX_QUEUE_POOL_SIZE) {
			pr_dbg("%s: Error\n", __func__);
			if (recovery_flag == 0) {
				recovery_log_reason = 9;
				recovery_log_queue_idx = queue_idx;
			}
			recovery_flag++;
		}
	} else if (q->type == 2) {
		if ((di_buf->index < MAX_QUEUE_POOL_SIZE) &&
		    (q->pool[di_buf->index] == 0)) {
			q->pool[di_buf->index] =
				(di_buf->type << 8) | (di_buf->index);
			di_buf->queue_index = queue_idx;
			q->num++;
		} else {
			pr_dbg("%s: Error\n", __func__);
			if (recovery_flag == 0) {
				recovery_log_reason = 9;
				recovery_log_queue_idx = queue_idx;
			}
			recovery_flag++;
		}
	}

	if (di_log_flag & DI_LOG_QUEUE)
		di_print("%s done\n", __func__);
}

static int list_count(int queue_idx)
{
	return queue[queue_idx].num;
}

static bool queue_empty(int queue_idx)
{
	bool ret = (queue[queue_idx].num == 0);

	return ret;
}

static bool is_in_queue(struct di_buf_s *di_buf, int queue_idx)
{
	bool ret = 0;
	struct di_buf_s *p = NULL;
	int itmp;
	unsigned int overflow_cnt;

	overflow_cnt = 0;
	if ((di_buf == NULL) || (queue_idx < 0) || (queue_idx >= QUEUE_NUM)) {
		ret = 0;
		di_print("%s: not in queue:%d!!!\n", __func__, queue_idx);
		return ret;
	}
	queue_for_each_entry(p, ptmp, queue_idx, list) {
		if (p == di_buf) {
			ret = 1;
			break;
		}
		if (overflow_cnt++ > MAX_QUEUE_POOL_SIZE) {
			ret = 0;
			di_print("%s: overflow_cnt!!!\n", __func__);
			break;
		}
	}
	return ret;
}
//---------------------------
u8 *di_vmap(ulong addr, u32 size, bool *bflg)
{
	u8 *vaddr = NULL;
	ulong phys = addr;
	u32 offset = phys & ~PAGE_MASK;
	u32 npages = PAGE_ALIGN(size) / PAGE_SIZE;
	struct page **pages = NULL;
	pgprot_t pgprot;
	int i;

	if (!PageHighMem(phys_to_page(phys)))
		return phys_to_virt(phys);

	if (offset)
		npages++;

	pages = vmalloc(sizeof(struct page *) * npages);
	if (!pages)
		return NULL;

	for (i = 0; i < npages; i++) {
		pages[i] = phys_to_page(phys);
		phys += PAGE_SIZE;
	}

	/*nocache*/
	pgprot = pgprot_writecombine(PAGE_KERNEL);

	vaddr = vmap(pages, npages, VM_MAP, pgprot);
	if (!vaddr) {
		pr_err("the phy(%lx) vmaped fail, size: %d\n",
			addr - offset, npages << PAGE_SHIFT);
		vfree(pages);
		return NULL;
	}

	vfree(pages);

//	if (debug_mode & 0x20) {
//		di_print("[HIGH-MEM-MAP] %s, pa(%lx) to va(%p), size: %d\n",
//			__func__, addr, vaddr + offset, npages << PAGE_SHIFT);
//	}
	*bflg = true;

	return vaddr + offset;
}

void di_unmap_phyaddr(u8 *vaddr)
{
	void *addr = (void *)(PAGE_MASK & (ulong)vaddr);

	vunmap(addr);
}
//-------------------------
static ssize_t
store_dump_mem(struct device *dev, struct device_attribute *attr,
	       const char *buf, size_t len)
{
#ifdef CONFIG_AMLOGIC_ENABLE_MEDIA_FILE
	unsigned int n = 0, canvas_w = 0, canvas_h = 0;
	unsigned long nr_size = 0, dump_adr = 0;
	struct di_buf_s *di_buf = NULL;
	struct vframe_s *post_vf = NULL;
	char *buf_orig, *ps, *token;
	char *parm[3] = { NULL };
	char delim1[3] = " ";
	char delim2[2] = "\n";
	struct file *filp = NULL;
	loff_t pos = 0;
	void *buff = NULL;
	mm_segment_t old_fs;
	bool bflg_vmap = false;

	buf_orig = kstrdup(buf, GFP_KERNEL);
	ps = buf_orig;
	strcat(delim1, delim2);
	while (1) {
		token = strsep(&ps, delim1);
		if (token == NULL)
			break;
		if (*token == '\0')
			continue;
		parm[n++] = token;
	}
	if (strcmp(parm[0], "capture") == 0)
		di_buf = di_pre_stru.di_mem_buf_dup_p;
	else if (strcmp(parm[0], "capture_post") == 0) {
		if (di_vf_peek(NULL)) {
			post_vf = di_vf_get(NULL);
			if (!IS_ERR_OR_NULL(post_vf)) {
				di_buf = post_vf->private_data;
				di_vf_put(post_vf, NULL);
			}
			post_vf = NULL;
		}
	} else if (strcmp(parm[0], "capture_nrds") == 0)
		get_nr_ds_buf(&dump_adr, &nr_size);
	else {
		pr_err("wrong dump cmd\n");
		return len;
	}
	if (nr_size == 0) {
		if (unlikely(di_buf == NULL))
			return len;
		canvas_w = di_buf->canvas_width[NR_CANVAS];
		canvas_h = di_buf->canvas_height;
		nr_size = canvas_w * canvas_h * 2;
		dump_adr = di_buf->nr_adr;
	}
	old_fs = get_fs();
	set_fs(KERNEL_DS);
/* pr_dbg("dump path =%s\n",dump_path); */
		filp = filp_open(parm[1], O_RDWR | O_CREAT, 0666);
		if (IS_ERR(filp)) {
			pr_err("create %s error.\n", parm[1]);
			return len;
		}
		dump_state_flag = 1;
		if (de_devp->flags & DI_MAP_FLAG) {
			//buff = (void *)phys_to_virt(dump_adr);
			buff = di_vmap(dump_adr, nr_size, &bflg_vmap);
			if (buff == NULL) {
				pr_info("di_vap err\n");
				filp_close(filp, NULL);
				return len;

			}
		} else {
			buff = ioremap(dump_adr, nr_size);
		}
		if (IS_ERR_OR_NULL(buff))
			pr_err("%s: ioremap error.\n", __func__);
		vfs_write(filp, buff, nr_size, &pos);
/*	pr_dbg("di_chan2_buf_dup_p:\n	nr:%u,mtn:%u,cnt:%u\n",
 * di_pre_stru.di_chan2_buf_dup_p->nr_adr,
 * di_pre_stru.di_chan2_buf_dup_p->mtn_adr,
 * di_pre_stru.di_chan2_buf_dup_p->cnt_adr);
 * pr_dbg("di_inp_buf:\n	nr:%u,mtn:%u,cnt:%u\n",
 * di_pre_stru.di_inp_buf->nr_adr,
 * di_pre_stru.di_inp_buf->mtn_adr,
 * di_pre_stru.di_inp_buf->cnt_adr);
 * pr_dbg("di_wr_buf:\n	nr:%u,mtn:%u,cnt:%u\n",
 * di_pre_stru.di_wr_buf->nr_adr,
 * di_pre_stru.di_wr_buf->mtn_adr,
 * di_pre_stru.di_wr_buf->cnt_adr);
 * pr_dbg("di_mem_buf_dup_p:\n  nr:%u,mtn:%u,cnt:%u\n",
 * di_pre_stru.di_mem_buf_dup_p->nr_adr,
 * di_pre_stru.di_mem_buf_dup_p->mtn_adr,
 * di_pre_stru.di_mem_buf_dup_p->cnt_adr);
 * pr_dbg("di_mem_start=%u\n",di_mem_start);
 */
		vfs_fsync(filp, 0);
		pr_info("write buffer 0x%lx  to %s.\n", dump_adr, parm[1]);
		if (bflg_vmap)
			di_unmap_phyaddr(buff);

		if (!(de_devp->flags & DI_MAP_FLAG))
			iounmap(buff);
		dump_state_flag = 0;
		filp_close(filp, NULL);
		set_fs(old_fs);
#endif
	return len;
}

#define is_from_vdin(vframe) (vframe->type & VIDTYPE_VIU_422)
static void recycle_vframe_type_pre(struct di_buf_s *di_buf);
static void recycle_vframe_type_post(struct di_buf_s *di_buf);
static void add_dummy_vframe_type_pre(struct di_buf_s *src_buf);
#ifdef DI_BUFFER_DEBUG
static void
recycle_vframe_type_post_print(struct di_buf_s *di_buf,
				const char *func,
				const int line);
#endif

static void dis2_di(void)
{
	ulong flags = 0, irq_flag2 = 0;

	init_flag = 0;
	di_lock_irqfiq_save(irq_flag2);
/* vf_unreg_provider(&di_vf_prov); */
	vf_light_unreg_provider(&di_vf_prov);
	di_unlock_irqfiq_restore(irq_flag2);
	reg_flag = 0;
	spin_lock_irqsave(&plist_lock, flags);
	di_lock_irqfiq_save(irq_flag2);
	if (di_pre_stru.di_inp_buf) {
		if (vframe_in[di_pre_stru.di_inp_buf->index]) {
			vf_put(
				vframe_in[di_pre_stru.di_inp_buf->index],
				VFM_NAME);
			vframe_in[di_pre_stru.di_inp_buf->index] = NULL;
			vf_notify_provider(
				VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL);
		}
		di_pre_stru.di_inp_buf->invert_top_bot_flag = 0;
		queue_in(di_pre_stru.di_inp_buf, QUEUE_IN_FREE);
		di_pre_stru.di_inp_buf = NULL;
	}
	di_uninit_buf(0);
	if (get_blackout_policy()) {
		DI_Wr(DI_CLKG_CTRL, 0x2);
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
		if (is_meson_txlx_cpu() || is_meson_txhd_cpu()) {
			enable_di_post_mif(GATE_OFF);
			di_post_gate_control(false);
			di_top_gate_control(false, false);
		}
#endif
	}

	if (post_wr_en && post_wr_support)
		diwr_set_power_control(0);

	di_unlock_irqfiq_restore(irq_flag2);
	spin_unlock_irqrestore(&plist_lock, flags);
}

static ssize_t
store_config(struct device *dev,
	     struct device_attribute *attr,
	     const char *buf, size_t count)
{
	int rc = 0;
	char *parm[2] = { NULL }, *buf_orig;

	buf_orig = kstrdup(buf, GFP_KERNEL);
	parse_cmd_params(buf_orig, (char **)(&parm));

	if (strncmp(buf, "disable", 7) == 0) {

		di_print("%s: disable\n", __func__);

		if (init_flag) {
			di_pre_stru.disable_req_flag = 1;

			trigger_pre_di_process(TRIGGER_PRE_BY_DEBUG_DISABLE);
			while (di_pre_stru.disable_req_flag)
				usleep_range(1000, 1001);
		}
	} else if (strncmp(buf, "dis2", 4) == 0) {
		dis2_di();
	} else if (strcmp(parm[0], "hold_video") == 0) {
		pr_info("%s(%s %s)\n", __func__, parm[0], parm[1]);
		rc = kstrtouint(parm[1], 10, &hold_video);
	}
	kfree(buf_orig);
	return count;
}

static unsigned char is_progressive(vframe_t *vframe)
{
	unsigned char ret = 0;

	ret = ((vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_PROGRESSIVE);
	return ret;
}

static unsigned char is_source_change(vframe_t *vframe)
{
#define VFRAME_FORMAT_MASK      \
	(VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_444 | \
	 VIDTYPE_MVC)
	if ((di_pre_stru.cur_width != vframe->width) ||
	(di_pre_stru.cur_height != vframe->height) ||
	(((di_pre_stru.cur_inp_type & VFRAME_FORMAT_MASK) !=
	(vframe->type & VFRAME_FORMAT_MASK)) &&
	(!is_handle_prog_frame_as_interlace(vframe))) ||
	(di_pre_stru.cur_source_type != vframe->source_type)) {
		/* video format changed */
		return 1;
	} else if (
	((di_pre_stru.cur_prog_flag != is_progressive(vframe)) &&
	(!is_handle_prog_frame_as_interlace(vframe))) ||
	((di_pre_stru.cur_inp_type & VIDTYPE_VIU_FIELD) !=
	(vframe->type & VIDTYPE_VIU_FIELD))
	) {
		/* just scan mode changed */
		if (!di_pre_stru.force_interlace)
			pr_dbg("DI I<->P.\n");
		return 2;
	}
	return 0;
}
/*
 * static unsigned char is_vframe_type_change(vframe_t* vframe)
 * {
 * if(
 * (di_pre_stru.cur_prog_flag!=is_progressive(vframe))||
 * ((di_pre_stru.cur_inp_type&VFRAME_FORMAT_MASK)!=
 * (vframe->type&VFRAME_FORMAT_MASK))
 * )
 * return 1;
 *
 * return 0;
 * }
 */
static int trick_mode;

unsigned char is_bypass(vframe_t *vf_in)
{
	unsigned int vtype = 0;
	int ret = 0;
	static vframe_t vf_tmp;

	isbypass_flag = true;
	if (di_debug_flag & 0x10000) /* for debugging */
		return (di_debug_flag >> 17) & 0x1;

	if (bypass_all)
		return 1;
	if (di_pre_stru.cur_prog_flag &&
	    (
	     (di_pre_stru.cur_width > 1920) || (di_pre_stru.cur_height > 1080)
	     || (di_pre_stru.cur_inp_type & VIDTYPE_VIU_444))
	    )
		return 1;

	if ((di_pre_stru.cur_width < 16) || (di_pre_stru.cur_height < 16))
		return 1;

	if (di_pre_stru.cur_inp_type & VIDTYPE_MVC)
		return 1;

	if (di_pre_stru.cur_source_type == VFRAME_SOURCE_TYPE_PPMGR)
		return 1;

	if (bypass_trick_mode) {
		int trick_mode_fffb = 0;
		int trick_mode_i = 0;

		if (bypass_trick_mode&0x1)
			query_video_status(0, &trick_mode_fffb);
		if (bypass_trick_mode&0x2)
			query_video_status(1, &trick_mode_i);
		trick_mode = trick_mode_fffb | (trick_mode_i << 1);
		if (trick_mode)
			return 1;
	}

	if (bypass_3d &&
	    (di_pre_stru.source_trans_fmt != 0))
		return 1;

/*prot is conflict with di post*/
	if (vf_in && vf_in->video_angle)
		return 1;
	if (vf_in && (vf_in->type & VIDTYPE_PIC))
		return 1;
#if 0
	if (vf_in && (vf_in->type & VIDTYPE_COMPRESS))
		return 1;
#endif
	if ((di_vscale_skip_enable & 0x4) &&
		vf_in && !post_wr_en) {
		//--------------------------------------
		if (vf_in->type & VIDTYPE_COMPRESS) {
			vf_tmp.width = vf_in->compWidth;
			vf_tmp.height = vf_in->compHeight;
			if (vf_tmp.width > 1920 || vf_tmp.height > 1088)
				return 1;

		}
		//--------------------------------------
		/*backup vtype,set type as progressive*/
		vtype = vf_in->type;
		vf_in->type &= (~VIDTYPE_TYPEMASK);
		vf_in->type &= (~VIDTYPE_VIU_NV21);
		vf_in->type |= VIDTYPE_VIU_SINGLE_PLANE;
		vf_in->type |= VIDTYPE_VIU_FIELD;
		vf_in->type |= VIDTYPE_PRE_INTERLACE;
		vf_in->type |= VIDTYPE_VIU_422;
		ret = get_current_vscale_skip_count(vf_in);
		di_vscale_skip_count = (ret&0xff);
		vpp_3d_mode = ((ret>>8)&0xff);
		vf_in->type = vtype;
		if (di_vscale_skip_count > 0 ||
			(vpp_3d_mode
			#ifdef DET3D
			&& (!det3d_en)
			#endif
			)
			)
			return 1;
	}
	isbypass_flag = false;
	return 0;
}

static unsigned char is_bypass_post(void)
{
	if (di_debug_flag & 0x40000) /* for debugging */
		return (di_debug_flag >> 19) & 0x1;

	/*prot is conflict with di post*/
	if (di_pre_stru.orientation)
		return 1;
	if (bypass_post)
		return 1;

#ifdef DET3D
	if (di_pre_stru.vframe_interleave_flag != 0)
		return 1;

#endif
	return 0;
}

#ifdef RUN_DI_PROCESS_IN_IRQ
static unsigned char is_input2pre(void)
{
	if (input2pre
	    && di_pre_stru.cur_prog_flag
	    && di_pre_stru.vdin_source
	    && (bypass_state == 0))
		return 1;

	return 0;
}
#endif

#ifdef DI_USE_FIXED_CANVAS_IDX
static int di_post_idx[2][6];
static int di_pre_idx[2][10];
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
static unsigned int di_inp_idx[3];
#else
static int di_wr_idx;
#endif
static int di_get_canvas(void)
{
	unsigned int pre_num = 7, post_num = 6, i = 0;

	if (mcpre_en) {
		/* mem/chan2/nr/mtn/contrd/contrd2/
		 * contw/mcinfrd/mcinfow/mcvecw
		 */
		pre_num = 10;
		/* buf0/buf1/buf2/mtnp/mcvec */
		post_num = 6;
	}
	if (canvas_pool_alloc_canvas_table("di_pre",
	&di_pre_idx[0][0], pre_num, CANVAS_MAP_TYPE_1)) {
		pr_err("%s allocate di pre canvas error.\n", __func__);
		return 1;
	}
	if (di_pre_rdma_enable) {
		if (canvas_pool_alloc_canvas_table("di_pre",
		&di_pre_idx[1][0], pre_num, CANVAS_MAP_TYPE_1)) {
			pr_err("%s allocate di pre canvas error.\n", __func__);
			return 1;
		}
	} else {
		for (i = 0; i < pre_num; i++)
			di_pre_idx[1][i] = di_pre_idx[0][i];
	}
	if (canvas_pool_alloc_canvas_table("di_post",
		    &di_post_idx[0][0], post_num, CANVAS_MAP_TYPE_1)) {
		pr_err("%s allocate di post canvas error.\n", __func__);
		return 1;
	}

#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
	if (canvas_pool_alloc_canvas_table("di_post",
		    &di_post_idx[1][0], post_num, CANVAS_MAP_TYPE_1)) {
		pr_err("%s allocate di post canvas error.\n", __func__);
		return 1;
	}
#else
	for (i = 0; i < post_num; i++)
		di_post_idx[1][i] = di_post_idx[0][i];
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
	if (canvas_pool_alloc_canvas_table("di_inp", &di_inp_idx[0], 3,
			CANVAS_MAP_TYPE_1)) {
		pr_err("%s allocat di inp canvas error.\n", __func__);
		return 1;
	}
	pr_info("DI: support multi decoding %u~%u~%u.\n",
		di_inp_idx[0], di_inp_idx[1], di_inp_idx[2]);
#endif
	if (de_devp->post_wr_support == 0)
		return 0;

#ifndef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
	if (canvas_pool_alloc_canvas_table("di_wr",
			&di_wr_idx, 1, CANVAS_MAP_TYPE_1)) {
		pr_err("%s allocat di write back canvas error.\n",
			__func__);
			return 1;
	}
	pr_info("DI: support post write back %u.\n", di_wr_idx);
#endif
	return 0;
}

static void config_canvas_idx(struct di_buf_s *di_buf, int nr_canvas_idx,
	int mtn_canvas_idx)
{
	unsigned int height = 0;

	if (!di_buf)
		return;
	if (di_buf->canvas_config_flag == 1) {
		if (nr_canvas_idx >= 0) {
			/* linked two interlace buffer should double height*/
			if (di_buf->di_wr_linked_buf)
				height = (di_buf->canvas_height << 1);
			else
				height =  di_buf->canvas_height;
			di_buf->nr_canvas_idx = nr_canvas_idx;
			canvas_config(nr_canvas_idx, di_buf->nr_adr,
				di_buf->canvas_width[NR_CANVAS], height, 0, 0);
		}
	} else if (di_buf->canvas_config_flag == 2) {
		if (nr_canvas_idx >= 0) {
			di_buf->nr_canvas_idx = nr_canvas_idx;
			canvas_config(nr_canvas_idx, di_buf->nr_adr,
				di_buf->canvas_width[NR_CANVAS],
				di_buf->canvas_height, 0, 0);
		}
		if (mtn_canvas_idx >= 0) {
			di_buf->mtn_canvas_idx = mtn_canvas_idx;
			canvas_config(mtn_canvas_idx, di_buf->mtn_adr,
				di_buf->canvas_width[MTN_CANVAS],
				di_buf->canvas_height, 0, 0);
		}
	}
	if (nr_canvas_idx >= 0) {
		di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx;
		di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx;
	}
}

static void config_cnt_canvas_idx(struct di_buf_s *di_buf,
	unsigned int cnt_canvas_idx)
{

	if (!di_buf)
		return;

	di_buf->cnt_canvas_idx = cnt_canvas_idx;
	canvas_config(
		cnt_canvas_idx, di_buf->cnt_adr,
		di_buf->canvas_width[MTN_CANVAS],
		di_buf->canvas_height, 0, 0);
}

static void config_mcinfo_canvas_idx(struct di_buf_s *di_buf,
	int mcinfo_canvas_idx)
{

	if (!di_buf)
		return;

	di_buf->mcinfo_canvas_idx = mcinfo_canvas_idx;
	canvas_config(
		mcinfo_canvas_idx, di_buf->mcinfo_adr,
		di_buf->canvas_height, 2, 0, 0);
}
static void config_mcvec_canvas_idx(struct di_buf_s *di_buf,
	int mcvec_canvas_idx)
{

	if (!di_buf)
		return;

	di_buf->mcvec_canvas_idx = mcvec_canvas_idx;
	canvas_config(
		mcvec_canvas_idx, di_buf->mcvec_adr,
		di_buf->canvas_width[MV_CANVAS],
		di_buf->canvas_height, 0, 0);
}

#else

static void config_canvas(struct di_buf_s *di_buf)
{
	unsigned int height = 0;

	if (!di_buf)
		return;

	if (di_buf->canvas_config_flag == 1) {
		/* linked two interlace buffer should double height*/
		if (di_buf->di_wr_linked_buf)
			height = (di_buf->canvas_height << 1);
		else
			height =  di_buf->canvas_height;
		canvas_config(di_buf->nr_canvas_idx, di_buf->nr_adr,
			di_buf->canvas_width[NR_CANVAS], height, 0, 0);
		di_buf->canvas_config_flag = 0;
	} else if (di_buf->canvas_config_flag == 2) {
		canvas_config(di_buf->nr_canvas_idx, di_buf->nr_adr,
			di_buf->canvas_width[MV_CANVAS],
			di_buf->canvas_height, 0, 0);
		canvas_config(di_buf->mtn_canvas_idx, di_buf->mtn_adr,
			di_buf->canvas_width[MTN_CANVAS],
			di_buf->canvas_height, 0, 0);
		di_buf->canvas_config_flag = 0;
	}
}

#endif
#ifdef CONFIG_CMA
/**********************************************************
 * ./include/linux/amlogic/media/codec_mm/codec_mm.h:
 *	unsigned long codec_mm_alloc_for_dma(const char *owner,
 *					int page_cnt,
 *					int align2n,
 *					int memflags);
 *	int codec_mm_free_for_dma(const char *owner,
 *				unsigned long phy_addr);
 *	void *codec_mm_phys_to_virt(unsigned long phy_addr);
 ***********************************************************/

#define TVP_MEM_PAGES	0xffff
/**********************************************************
 * alloc mm from codec mm
 * o: out:
 * return:
 *	true: seccuss
 *	false: failed
 ***********************************************************/
static bool mm_codec_alloc(const char *owner, size_t count,
			int cma_mode,
			struct di_mm_s *o)
{
	int flags = 0;
	bool istvp = false;

	if (codec_mm_video_tvp_enabled()) {
		istvp = true;
		flags |= CODEC_MM_FLAGS_TVP;
	} else {
		flags |= CODEC_MM_FLAGS_RESERVED | CODEC_MM_FLAGS_CPU;
	}

	if (cma_mode == 4 && !istvp)
		flags = CODEC_MM_FLAGS_CMA_FIRST |
			CODEC_MM_FLAGS_CPU;

	o->addr = codec_mm_alloc_for_dma(owner,
					count,
					0,
					flags);

	if (o->addr == 0) {
		/*failed*/
		pr_err("di:%s: failed\n", __func__);
		return false;
	}

	if (istvp)
		o->ppage = (struct page *) TVP_MEM_PAGES;
	else
		o->ppage = codec_mm_phys_to_virt(o->addr);

	//pr_info("%s:page:0x%p,add:0x%lx\n", __func__, o->ppage, o->addr);
	return true;
}

/**********************************************************
 *	./include/linux/dma-contiguous.h:
 * struct page *dma_alloc_from_contiguous(struct device *dev,
 *					size_t count,
 *					unsigned int order);
 * bool dma_release_from_contiguous(struct device *dev,
 *					struct page *pages,
 *					int count);
 *
 ***********************************************************/

/**********************************************************
 * alloc mm by cma
 * o: out:
 * return:
 *	true: seccuss
 *	false: failed
 ***********************************************************/
static bool mm_cma_alloc(struct device *dev, size_t count,
			struct di_mm_s *o)
{
	o->ppage = dma_alloc_from_contiguous(dev, count, 0, 0);
	if (o->ppage) {
		o->addr = page_to_phys(o->ppage);
		return true;
	}
	pr_err("di:%s: failed\n", __func__);
	return false;
}

bool di_mm_alloc(int cma_mode, size_t count, struct di_mm_s *o)
{
	bool ret;

	if (cma_mode == 3 || cma_mode == 4)
		ret = mm_codec_alloc(DEVICE_NAME,
			count,
			cma_mode,
			o);
	else
		ret = mm_cma_alloc(&(de_devp->pdev->dev), count, o);

	return ret;
}

bool di_mm_release(int cma_mode,
			struct page *pages,
			int count,
			unsigned long addr)
{
	bool ret = true;

	if (cma_mode == 3 || cma_mode == 4)
		codec_mm_free_for_dma(DEVICE_NAME, addr);
	else
		ret = dma_release_from_contiguous(&(de_devp->pdev->dev),
			pages,
			count);
	return ret;
}
/***********************************************************/
static unsigned int di_cma_alloc_total(struct di_dev_s *de_devp)
{
	#if 0
	de_devp->total_pages =
		dma_alloc_from_contiguous(&(de_devp->pdev->dev),
		de_devp->mem_size >> PAGE_SHIFT, 0);
	if (de_devp->total_pages) {
		de_devp->mem_start =
			page_to_phys(de_devp->total_pages);
		pr_dbg("%s:DI CMA allocate addr:0x%lx ok.\n",
			__func__, de_devp->mem_start);
		return 1;
	}
	pr_err("%s:xxxxxx DI CMA allocate fail.\n", __func__);
	if (de_devp->flag_cma != 0 && de_devp->nrds_enable) {
		nr_ds_buf_init(de_devp->flag_cma, 0,
			&(de_devp->pdev->dev));
	}

	return 0;
	#else
	/*****************************************************/
	struct di_mm_s omm;
	bool ret;

	ret = di_mm_alloc(de_devp->flag_cma,
		de_devp->mem_size >> PAGE_SHIFT,
		&omm);

	if (!ret) /*failed*/
		return 0;

	de_devp->total_pages = omm.ppage;
	de_devp->mem_start = omm.addr;

	if (de_devp->flag_cma != 0 && de_devp->nrds_enable) {
		nr_ds_buf_init(de_devp->flag_cma, 0,
			&(de_devp->pdev->dev));
	}

	return 1;
	#endif
}

static bool cma_print;
static unsigned int di_cma_alloc(struct di_dev_s *devp)
{
	unsigned int start_time, end_time, delta_time;
	struct di_buf_s *buf_p = NULL;
	int itmp, alloc_cnt = 0;
	u8 *tmp;
	bool aret;
	struct di_mm_s omm;

	start_time = jiffies_to_msecs(jiffies);
	queue_for_each_entry(buf_p, ptmp, QUEUE_LOCAL_FREE, list) {
#if 0
		if (buf_p->pages == NULL) {
			buf_p->pages =
			dma_alloc_from_contiguous(&(devp->pdev->dev),
				devp->buffer_size >> PAGE_SHIFT, 0);
			if (IS_ERR_OR_NULL(buf_p->pages)) {
				buf_p->pages = NULL;
				pr_err("xxxxxxxxx DI CMA  allocate %d fail.\n",
					buf_p->index);
				return 0;
			} else {
				alloc_cnt++;
				if (cma_print)
					pr_info("DI CMA  allocate buf[%d]page:0x%p\n",
						buf_p->index, buf_p->pages);
			}
		} else {
			if (cma_print) {
				pr_err("DI buf[%d] page:0x%p cma alloced skip\n",
					buf_p->index, buf_p->pages);
			}
		}
		buf_p->nr_adr = page_to_phys(buf_p->pages);
#else
		if (buf_p->pages != NULL) {
			pr_err("di:err1:%s:buf[%d] page:0x%p alloced skip\n",
				__func__, buf_p->index, buf_p->pages);
			continue;
		}

		aret = di_mm_alloc(devp->flag_cma,
			devp->buffer_size >> PAGE_SHIFT,
			&omm);

		if (!aret) {
			buf_p->pages = NULL;
			pr_err("di:err2:%s: alloc failed %d fail.\n",
				__func__,
				buf_p->index);
			return 0;
		}

		buf_p->pages = omm.ppage;
		buf_p->nr_adr = omm.addr;
		alloc_cnt++;
		if (cma_print)
			pr_info("DI CMA  allocate buf[%d]page:0x%p\n",
				buf_p->index, buf_p->pages);

#endif
		if (cma_print)
			pr_info(" addr 0x%lx ok.\n", buf_p->nr_adr);
		if (di_pre_stru.buf_alloc_mode == 0) {
			buf_p->mtn_adr = buf_p->nr_adr +
				di_pre_stru.nr_size;
			buf_p->cnt_adr = buf_p->nr_adr +
				di_pre_stru.nr_size +
				di_pre_stru.mtn_size;
			if (mc_mem_alloc) {
				buf_p->mcvec_adr = buf_p->nr_adr +
					di_pre_stru.nr_size +
					di_pre_stru.mtn_size +
					di_pre_stru.count_size;
				buf_p->mcinfo_adr =
					buf_p->nr_adr +
					di_pre_stru.nr_size +
					di_pre_stru.mtn_size +
					di_pre_stru.count_size +
					di_pre_stru.mv_size;
					tmp = di_vmap(buf_p->mcinfo_adr,
						di_pre_stru.mcinfo_size,
						&buf_p->bflg_vmap);

					if (buf_p->bflg_vmap == true)
						buf_p->mcinfo_vaddr =
							(unsigned short *)tmp;
					else {
						buf_p->mcinfo_vaddr = NULL;
						pr_err("DI: %s vmap fail\n",
							__func__);
					}
			}
		}
	}
	if (post_wr_en && post_wr_support) {
		queue_for_each_entry(buf_p, ptmp, QUEUE_POST_FREE, list) {
#if 0
			if (buf_p->pages == NULL) {
				buf_p->pages =
					dma_alloc_from_contiguous(
					&(devp->pdev->dev),
					devp->post_buffer_size>>PAGE_SHIFT,
					0);
				if (IS_ERR_OR_NULL(buf_p->pages)) {
					buf_p->pages = NULL;
					pr_err("xxxxxxxxx DI CMA allocate %d fail.\n",
						buf_p->index);
					return 0;
				}
				alloc_cnt++;
				if (cma_print)
					pr_info("DI CMA allocate buf[%d]page:0x%p\n",
						buf_p->index,
						buf_p->pages);
			} else {
				pr_err("DI buf[%d] page:0x%p cma alloced skip\n",
					buf_p->index, buf_p->pages);
			}
			buf_p->nr_adr = page_to_phys(buf_p->pages);

#else
			if (buf_p->pages != NULL) {
				pr_err("di:err3:%s:buf[%d] page:0x%p skip\n",
					__func__,
					buf_p->index, buf_p->pages);
				continue;
			}

			aret = di_mm_alloc(devp->flag_cma,
				devp->post_buffer_size>>PAGE_SHIFT,
				&omm);

			if (!aret) {
				buf_p->pages = NULL;
				pr_err("di:err4:%s: alloc failed %d fail.\n",
					__func__,
					buf_p->index);
				return 0;
			}

			buf_p->pages = omm.ppage;
			buf_p->nr_adr = omm.addr;
			alloc_cnt++;
			if (cma_print)
				pr_info("di:%s:pbuf[%d]page:0x%p\n",
					__func__,
					buf_p->index, buf_p->pages);
#endif
			if (cma_print)
				pr_info(" addr 0x%lx ok.\n", buf_p->nr_adr);
		}
	}
	if (de_devp->flag_cma != 0 && de_devp->nrds_enable) {
		nr_ds_buf_init(de_devp->flag_cma, 0,
			&(de_devp->pdev->dev));
	}

	end_time = jiffies_to_msecs(jiffies);
	delta_time = end_time - start_time;
	pr_info("%s:alloc %u buffer use %u ms(%u~%u)\n",
			__func__, alloc_cnt, delta_time, start_time, end_time);
	return 1;
}

static void di_cma_release(struct di_dev_s *devp)
{
	unsigned int i, ii, rels_cnt = 0, start_time, end_time, delta_time;
	struct di_buf_s *buf_p, *keep_buf;
	bool ret;

	keep_buf = di_post_stru.keep_buf;
	start_time = jiffies_to_msecs(jiffies);
	for (i = 0; (i < devp->buf_num_avail); i++) {
		buf_p = &(di_buf_local[i]);
		ii = USED_LOCAL_BUF_MAX;
		if (!IS_ERR_OR_NULL(keep_buf)) {
			for (ii = 0; ii < USED_LOCAL_BUF_MAX; ii++) {
				if (buf_p == keep_buf->di_buf_dup_p[ii]) {
					pr_dbg("%s skip buf[%d].\n\n",
						__func__, i);
					break;
				}
			}
		}
		if ((ii >= USED_LOCAL_BUF_MAX) &&
			(buf_p->pages != NULL)) {

			if (buf_p->bflg_vmap == true) {
				di_unmap_phyaddr((u8 *)buf_p->mcinfo_vaddr);
				buf_p->bflg_vmap = false;
			}
#if 0
			if (dma_release_from_contiguous(&(devp->pdev->dev),
					buf_p->pages,
					devp->buffer_size >> PAGE_SHIFT)) {
				buf_p->pages = NULL;
				rels_cnt++;
				if (cma_print)
					pr_info(
					"DI CMA  release buf[%d] ok.\n", i);
			} else {
				pr_err("DI CMA  release buf[%d] fail.\n", i);
			}
#else

			ret = di_mm_release(devp->flag_cma, buf_p->pages,
				devp->buffer_size >> PAGE_SHIFT,
				buf_p->nr_adr);
			if (ret) {
				buf_p->pages = NULL;
				rels_cnt++;
				if (cma_print)
					pr_info(
					"DI release buf[%d] ok.\n", i);
			} else {
				pr_err("di:err:%s:release buf[%d] fail.\n",
					__func__, i);
			}
#endif
		} else {
			if (!IS_ERR_OR_NULL(buf_p->pages) && cma_print) {
				pr_info("DI buf[%d] page:0x%p no release.\n",
					buf_p->index, buf_p->pages);
			}
		}
	}
	if (post_wr_en && post_wr_support) {
		for (i = 0; i < di_post_stru.di_post_num; i++) {
			buf_p = &(di_buf_post[i]);
#if 0
			if (buf_p->pages != NULL) {
				if (dma_release_from_contiguous(
						&(devp->pdev->dev),
						buf_p->pages,
						devp->post_buffer_size>>
						PAGE_SHIFT)) {
					buf_p->pages = NULL;
					rels_cnt++;
					if (cma_print)
						pr_info(
					"DI CMA release post buf[%d] ok.\n", i);
				} else {
					pr_err("DI CMA release post buf[%d] fail.\n",
						i);
				}
			}
#else
			if (buf_p->pages == NULL) {
				pr_err("di:err2:%s:post buf[%d] is null\n",
					__func__, i);
				continue;
			}

			ret = di_mm_release(devp->flag_cma,
					buf_p->pages,
					devp->post_buffer_size >> PAGE_SHIFT,
					buf_p->nr_adr);
			if (ret) {
				buf_p->pages = NULL;
				rels_cnt++;
				if (cma_print)
					pr_info(
					"DI release post buf[%d] ok.\n", i);
			} else {
				pr_err("di:err:%s:release post buf[%d] fail\n",
					__func__, i);
			}
#endif
		}
	}
	if (de_devp->nrds_enable) {
		nr_ds_buf_uninit(de_devp->flag_cma,
			&(de_devp->pdev->dev));
	}

	end_time = jiffies_to_msecs(jiffies);
	delta_time = end_time - start_time;
	pr_info("%s:release %u buffer use %u ms(%u~%u)\n",
			__func__, rels_cnt, delta_time, start_time, end_time);
}
#endif

static void di_patch_mov_ini(void)
{
	struct di_patch_mov_s *pmov;

	if (!de_devp)
		return;

	pmov = &de_devp->mov;
	if (!cpu_after_eq(MESON_CPU_MAJOR_ID_TL1)) {
		pmov->en_support = false;
		return;
	}
	pmov->en_support = true;
	pmov->mode = -1;
	pmov->nub = 3;
	pmov->reg_addr[0] = DI_NR_CTRL1;
	pmov->reg_addr[1] = DI_NR_CTRL2;
	pmov->reg_addr[2] = NR4_TOP_CTRL;
}

bool di_patch_mov_db(unsigned int addr, unsigned int val)
{
	bool ret = false;
	int i;
	struct di_patch_mov_s *pmov;
	struct di_patch_mov_d_s *pmv;

	if (!de_devp)
		return ret;

	pmov = &de_devp->mov;

	if (!pmov->en_support || !pmov->nub)
		return ret;

	for (i = 0; i < pmov->nub; i++) {
		if (addr == pmov->reg_addr[i]) {
			pmv = &pmov->val_db[i];
			pmv->val = val;
			pmv->en = true;
			pmv->mask = 0xffffffff;
			ret = true;
		}
	}
	return ret;
}

void di_patch_mov_setreg(unsigned int nub, unsigned int *preg)
{
	struct di_patch_mov_s *pmov;
	unsigned int i;

	if (!de_devp)
		return;

	pmov = &de_devp->mov;

	if (!pmov->en_support)
		return;

	pmov->nub = nub;
	if (nub > DI_PATCH_MOV_MAX_NUB) {
		pr_error("err: %s:nub is overflow %d\n",
			 __func__, nub);
		pmov->nub = DI_PATCH_MOV_MAX_NUB;
	}

	pmov->mode = -1;
	for (i = 0; i < pmov->nub; i++) {
		pmov->reg_addr[i] = preg[i];
		di_pr_info("reg:0x%x\n", preg[i]);
	}
}
EXPORT_SYMBOL(di_patch_mov_setreg);

/**************************************************
 * pdate:
 *	value / mask
 *	value / mask
 *	need keep same order with di_patch_mov_setreg
 **************************************************/
bool di_api_mov_sel(unsigned int mode, unsigned int *pdate)
{
	struct di_patch_mov_s *pmov;
	int i;
	struct di_patch_mov_d_s *pmv;
	bool ret = true;

	if (!de_devp)
		return false;

	pmov = &de_devp->mov;

	if (!pmov		||
	    !pmov->en_support	||
	    !init_flag)
		return false;

	switch (mode) {
	case 0:/*setting from db*/
		pmov->mode = 0;
		pmov->update = 1;
		break;
	case 1:/*setting from pq*/
		pmov->update = 0;
		for (i = 0; i < pmov->nub; i++) {
			pmv = &pmov->val_pq[i];
			pmv->val = pdate[i * 2];
			pmv->mask = pdate[i * 2 + 1];
			pmv->en = true;
		}
		pmov->mode = 1;
		pmov->update = true;

		break;
	default:
		ret = false;
		break;
	}
	return ret;
}
EXPORT_SYMBOL(di_api_mov_sel);

static void di_patch_mov_setting(void)
{
	struct di_patch_mov_s *pmov;
	int i;
	struct di_patch_mov_d_s *pmv;
	unsigned int val;

	if (!de_devp)
		return;

	pmov = &de_devp->mov;

	if (!pmov		||
	    !pmov->en_support	||
	    pmov->mode < 0	||
	    pmov->mode > 1	||
	    !pmov->update)
		return;

	if (pmov->mode == 0)
		pmv = &pmov->val_db[0];
	else
		pmv = &pmov->val_pq[0];

	for (i = 0; i < pmov->nub; i++) {
		if (pmv->en) {
			if (pmv->mask != 0xffffffff) {
				val = ((RDMA_RD(pmov->reg_addr[i]) &
					(~(pmv->mask))) |
					(pmv->val & pmv->mask));
			} else {
				val = pmv->val;
			}
			DI_Wr(pmov->reg_addr[i], val);
		}
		pmv++;
	}
	pmov->update = 0;
}

void di_set_comb_mode(unsigned int mode)
{
	di_pre_stru.comb_mode = mode;
}
EXPORT_SYMBOL(di_set_comb_mode);

static int di_init_buf(int width, int height, unsigned char prog_flag)
{
	int i;
	int canvas_height = height + 8;
	struct page *tmp_page = NULL;
	unsigned int di_buf_size = 0, di_post_buf_size = 0, mtn_size = 0;
	unsigned int nr_size = 0, count_size = 0, mv_size = 0, mc_size = 0;
	unsigned int nr_width = width, mtn_width = width, mv_width = width;
	unsigned int nr_canvas_width = width, mtn_canvas_width = width;
	unsigned int mv_canvas_width = width, canvas_align_width = 32;
	unsigned long di_post_mem = 0, nrds_mem = 0;
	struct di_buf_s *keep_buf = di_post_stru.keep_buf;
	u8 *tmp;

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
		canvas_align_width = 64;
	pr_info("di: %s -S\n", __func__);
	frame_count = 0;
	disp_frame_count = 0;
	cur_post_ready_di_buf = NULL;
	/* decoder'buffer had been release no need put */
	for (i = 0; i < MAX_IN_BUF_NUM; i++)
		vframe_in[i] = NULL;
	memset(&di_pre_stru, 0, sizeof(di_pre_stru));
	if (nr10bit_support) {
		if (full_422_pack)
			nr_width = (width * 5) / 4;
		else
			nr_width = (width * 3) / 2;
	} else {
		nr_width = width;
	}
	/* make sure canvas width must be divided by 256bit|32byte align */
	nr_canvas_width = nr_width << 1;
	mtn_canvas_width = mtn_width >> 1;
	mv_canvas_width = (mv_width << 1) / 5;
	nr_canvas_width = roundup(nr_canvas_width, canvas_align_width);
	mtn_canvas_width = roundup(mtn_canvas_width, canvas_align_width);
	mv_canvas_width = roundup(mv_canvas_width, canvas_align_width);
	nr_width = nr_canvas_width >> 1;
	mtn_width = mtn_canvas_width << 1;
	mv_width = (mv_canvas_width * 5) >> 1;

	if (prog_flag) {
		di_pre_stru.prog_proc_type = 1;
		di_pre_stru.buf_alloc_mode = 1;
		di_buf_size = nr_width * canvas_height * 2;
		di_buf_size = roundup(di_buf_size, PAGE_SIZE);
		de_devp->buf_num_avail = de_devp->mem_size / di_buf_size;
		if (de_devp->buf_num_avail > (2 * MAX_LOCAL_BUF_NUM))
			de_devp->buf_num_avail = 2 * MAX_LOCAL_BUF_NUM;

	} else {
		di_pre_stru.prog_proc_type = 0;
		di_pre_stru.buf_alloc_mode = 0;
		/*nr_size(bits)=w*active_h*8*2(yuv422)
		 * mtn(bits)=w*active_h*4
		 * cont(bits)=w*active_h*4 mv(bits)=w*active_h/5*16
		 * mcinfo(bits)=active_h*16
		 */
		nr_size = (nr_width * canvas_height)*8*2/16;
		mtn_size = (mtn_width * canvas_height)*4/16;
		count_size = (mtn_width * canvas_height)*4/16;
		mv_size = (mv_width * canvas_height)/5;
		mc_size = roundup(canvas_height >> 1, canvas_align_width) << 1;

		if (mc_mem_alloc) {
			di_buf_size = nr_size + mtn_size + count_size +
				mv_size + mc_size;
		} else {
			di_buf_size = nr_size + mtn_size + count_size;
		}
		di_buf_size = roundup(di_buf_size, PAGE_SIZE);
		de_devp->buf_num_avail = de_devp->mem_size / di_buf_size;

		if (post_wr_en && post_wr_support) {
			de_devp->buf_num_avail = (de_devp->mem_size +
				(nr_width*canvas_height<<2)) /
				(di_buf_size + (nr_width*canvas_height<<1));
		}

		if (de_devp->buf_num_avail > MAX_LOCAL_BUF_NUM)
			de_devp->buf_num_avail = MAX_LOCAL_BUF_NUM;

	}
	de_devp->buffer_size = di_buf_size;
	di_pre_stru.nr_size = nr_size;
	di_pre_stru.count_size = count_size;
	di_pre_stru.mtn_size = mtn_size;
	di_pre_stru.mv_size = mv_size;
	di_pre_stru.mcinfo_size = mc_size;
	same_field_top_count = 0;
	same_field_bot_count = 0;

	queue_init(de_devp->buf_num_avail);
	for (i = 0; i < de_devp->buf_num_avail; i++) {
		struct di_buf_s *di_buf = &(di_buf_local[i]);
		int ii = USED_LOCAL_BUF_MAX;
		if (!IS_ERR_OR_NULL(keep_buf)) {
			for (ii = 0; ii < USED_LOCAL_BUF_MAX; ii++) {
				if (di_buf == keep_buf->di_buf_dup_p[ii]) {
					di_print("%s skip %d\n", __func__, i);
					break;
				}
			}
		}

		if (ii >= USED_LOCAL_BUF_MAX) {
			/* backup cma pages */
			tmp_page = di_buf->pages;
			memset(di_buf, 0, sizeof(struct di_buf_s));
			di_buf->pages = tmp_page;
			di_buf->type = VFRAME_TYPE_LOCAL;
			di_buf->pre_ref_count = 0;
			di_buf->post_ref_count = 0;
			di_buf->canvas_width[NR_CANVAS] = nr_canvas_width;
			di_buf->canvas_width[MTN_CANVAS] = mtn_canvas_width;
			di_buf->canvas_width[MV_CANVAS] = mv_canvas_width;

			if (prog_flag) {
				di_buf->canvas_height = canvas_height;

				//use reserved memory
				if (de_devp->flag_cma == 0)
					di_buf->nr_adr = de_devp->mem_start +
						di_buf_size * i;

				di_buf->canvas_config_flag = 1;
			} else {
				di_buf->canvas_height = (canvas_height>>1);
				di_buf->canvas_height =
					roundup(di_buf->canvas_height,
					canvas_align_width);

				//use reserved memory
				if (de_devp->flag_cma == 0) {
					di_buf->nr_adr = de_devp->mem_start +
						di_buf_size * i;
					di_buf->mtn_adr = de_devp->mem_start +
						di_buf_size * i +
						nr_size;
					di_buf->cnt_adr = de_devp->mem_start +
						di_buf_size * i +
						nr_size + mtn_size;

					if (mc_mem_alloc) {
						di_buf->mcvec_adr =
							de_devp->mem_start +
							di_buf_size * i +
							nr_size + mtn_size +
							count_size;
						di_buf->mcinfo_adr =
							de_devp->mem_start +
							di_buf_size * i +
							nr_size +
							mtn_size +
							count_size + mv_size;
						tmp = di_vmap(
							di_buf->mcinfo_adr,
							di_pre_stru.
							mcinfo_size,
							&di_buf->bflg_vmap);

					if (di_buf->bflg_vmap == true)
						di_buf->mcinfo_vaddr =
						(unsigned short *)tmp;
					else {
						di_buf->mcinfo_vaddr = NULL;
						pr_err("DI: %s vmap fail\n",
							__func__);
					}
					}
				}

				di_buf->canvas_config_flag = 2;
			}

			di_buf->index = i;
			di_buf->vframe = &(vframe_local[i]);
			di_buf->vframe->private_data = di_buf;
			di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx;
			di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx;
			di_buf->queue_index = -1;
			di_buf->invert_top_bot_flag = 0;
			queue_in(di_buf, QUEUE_LOCAL_FREE);
		}
	}
#ifdef CONFIG_CMA
	if (de_devp->flag_cma == 1 || (de_devp->flag_cma == 4) ||
		(de_devp->flag_cma == 3)) {
		pr_dbg("%s:cma alloc req time: %u ms\n",
			__func__, jiffies_to_msecs(jiffies));
		atomic_set(&de_devp->mem_flag, 0);
		di_pre_stru.cma_alloc_req = 1;
		up(&di_sema);
	}
#endif

	//use reserved memory
	if (de_devp->flag_cma == 0)
		di_post_mem = de_devp->mem_start +
			di_buf_size*de_devp->buf_num_avail;
	if (post_wr_en && post_wr_support) {
		di_post_buf_size = nr_width * canvas_height * 2;
		/* pre buffer must 2 more than post buffer */
		if ((de_devp->buf_num_avail - 2) > MAX_POST_BUF_NUM)
			di_post_stru.di_post_num = MAX_POST_BUF_NUM;
		else
			di_post_stru.di_post_num = (de_devp->buf_num_avail - 2);
		pr_info("DI: di post buffer size %u byte.\n", di_post_buf_size);
	} else {
		di_post_stru.di_post_num = MAX_POST_BUF_NUM;
		di_post_buf_size = 0;
	}
	de_devp->post_buffer_size = di_post_buf_size;
	for (i = 0; i < MAX_IN_BUF_NUM; i++) {
		struct di_buf_s *di_buf = &(di_buf_in[i]);

		if (di_buf) {
			memset(di_buf, 0, sizeof(struct di_buf_s));
			di_buf->type = VFRAME_TYPE_IN;
			di_buf->pre_ref_count = 0;
			di_buf->post_ref_count = 0;
			di_buf->vframe = &(vframe_in_dup[i]);
			di_buf->vframe->private_data = di_buf;
			di_buf->index = i;
			di_buf->queue_index = -1;
			di_buf->invert_top_bot_flag = 0;
			queue_in(di_buf, QUEUE_IN_FREE);
		}
	}

	for (i = 0; i < di_post_stru.di_post_num; i++) {
		struct di_buf_s *di_buf = &(di_buf_post[i]);

		if (di_buf) {
			if (di_buf != keep_buf) {
				memset(di_buf, 0, sizeof(struct di_buf_s));
				di_buf->type = VFRAME_TYPE_POST;
				di_buf->index = i;
				di_buf->vframe = &(vframe_post[i]);
				di_buf->vframe->private_data = di_buf;
				di_buf->queue_index = -1;
				di_buf->invert_top_bot_flag = 0;
				if (post_wr_en && post_wr_support) {
					di_buf->canvas_width[NR_CANVAS] =
						(nr_width << 1);
					di_buf->canvas_height = canvas_height;
					di_buf->canvas_config_flag = 1;

					//use reserved memory
					if (de_devp->flag_cma == 0)
						di_buf->nr_adr = di_post_mem +
							di_post_buf_size*i;
				}
				queue_in(di_buf, QUEUE_POST_FREE);
			}
		}
	}
	if (de_devp->flag_cma == 0 && de_devp->nrds_enable) {
		nrds_mem = di_post_mem +
			di_post_stru.di_post_num * di_post_buf_size;
		nr_ds_buf_init(de_devp->flag_cma, nrds_mem,
			&(de_devp->pdev->dev));
	}
	pr_info("di: %s -E\n", __func__);
	return 0;
}

static void keep_mirror_buffer(void)
{
	struct di_buf_s *p = NULL;
	int i = 0, ii = 0, itmp;

	queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) {
	if (p->di_buf[0]->type != VFRAME_TYPE_IN &&
		(p->process_fun_index != PROCESS_FUN_NULL) &&
		(ii < USED_LOCAL_BUF_MAX) &&
		(p->index == di_post_stru.cur_disp_index)) {
		di_post_stru.keep_buf = p;
		for (i = 0; i < USED_LOCAL_BUF_MAX; i++) {
			if (IS_ERR_OR_NULL(p->di_buf_dup_p[i]))
				continue;
			/* prepare for recycle
			 * the keep buffer
			 */
			p->di_buf_dup_p[i]->pre_ref_count = 0;
			p->di_buf_dup_p[i]->post_ref_count = 0;
			if ((p->di_buf_dup_p[i]->queue_index >= 0) &&
			(p->di_buf_dup_p[i]->queue_index < QUEUE_NUM)) {
				if (is_in_queue(p->di_buf_dup_p[i],
				p->di_buf_dup_p[i]->queue_index))
					queue_out(p->di_buf_dup_p[i]);
			}
			ii++;
			if (p->di_buf_dup_p[i]->di_wr_linked_buf)
				p->di_buf_dup_p[i+1] =
					p->di_buf_dup_p[i]->di_wr_linked_buf;
		}
		queue_out(p);
		break;
	}
	}

}
static void di_uninit_buf(unsigned int disable_mirror)
{
	struct di_buf_s *keep_buf = NULL;
	int i = 0;

	if (!queue_empty(QUEUE_DISPLAY) || disable_mirror)
		di_post_stru.keep_buf = NULL;

	if (disable_mirror != 1)
		keep_mirror_buffer();

	if (!IS_ERR_OR_NULL(di_post_stru.keep_buf)) {
		keep_buf = di_post_stru.keep_buf;
		pr_dbg("%s keep cur di_buf %d (",
			__func__, keep_buf->index);
		for (i = 0; i < USED_LOCAL_BUF_MAX; i++) {
			/*coverity[var_deref_op] null pointer has been judged*/
			if (!IS_ERR_OR_NULL(keep_buf->di_buf_dup_p[i]))
				pr_dbg("%d\t",
					keep_buf->di_buf_dup_p[i]->index);
		}
		pr_dbg(")\n");
	}
	queue_init(0);
	/* decoder'buffer had been release no need put */
	for (i = 0; i < MAX_IN_BUF_NUM; i++)
		vframe_in[i] = NULL;
	di_pre_stru.pre_de_process_done = 0;
	di_pre_stru.pre_de_process_flag = 0;
	if (post_wr_en && post_wr_support) {
		di_post_stru.cur_post_buf = NULL;
		di_post_stru.post_de_busy = 0;
		di_post_stru.de_post_process_done = 0;
		di_post_stru.post_wr_cnt = 0;
	}
	if (de_devp->flag_cma == 0 && de_devp->nrds_enable) {
		nr_ds_buf_uninit(de_devp->flag_cma,
			&(de_devp->pdev->dev));
	}
}

static void log_buffer_state(unsigned char *tag)
{
	if (di_log_flag & DI_LOG_BUFFER_STATE) {
		struct di_buf_s *p = NULL;/* , *ptmp; */
		int itmp;
		int in_free = 0;
		int local_free = 0;
		int pre_ready = 0;
		int post_free = 0;
		int post_ready = 0;
		int post_ready_ext = 0;
		int display = 0;
		int display_ext = 0;
		int recycle = 0;
		int di_inp = 0;
		int di_wr = 0;
		ulong irq_flag2 = 0;

		di_lock_irqfiq_save(irq_flag2);
		in_free = list_count(QUEUE_IN_FREE);
		local_free = list_count(QUEUE_LOCAL_FREE);
		pre_ready = list_count(QUEUE_PRE_READY);
		post_free = list_count(QUEUE_POST_FREE);
		post_ready = list_count(QUEUE_POST_READY);
		queue_for_each_entry(p, ptmp, QUEUE_POST_READY, list) {
			if (p->di_buf[0])
				post_ready_ext++;

			if (p->di_buf[1])
				post_ready_ext++;
		}
		queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) {
			display++;
			if (p->di_buf[0])
				display_ext++;

			if (p->di_buf[1])
				display_ext++;
		}
		recycle = list_count(QUEUE_RECYCLE);

		if (di_pre_stru.di_inp_buf)
			di_inp++;
		if (di_pre_stru.di_wr_buf)
			di_wr++;

		if (buf_state_log_threshold == 0)
			buf_state_log_start = 0;
		else if (post_ready < buf_state_log_threshold)
			buf_state_log_start = 1;

		if (buf_state_log_start) {
			di_print(
		"[%s]i i_f %d/%d, l_f %d/%d, pre_r %d, post_f %d/%d,",
				tag,
				in_free, MAX_IN_BUF_NUM,
				local_free, de_devp->buf_num_avail,
				pre_ready,
				post_free, MAX_POST_BUF_NUM);
			di_print(
		"post_r (%d:%d), disp (%d:%d),rec %d, di_i %d, di_w %d\n",
				post_ready, post_ready_ext,
				display, display_ext,
				recycle,
				di_inp, di_wr
				);
		}
		di_unlock_irqfiq_restore(irq_flag2);
	}
}

static void dump_state(void)
{
	struct di_buf_s *p = NULL, *keep_buf;
	int itmp, i;

	dump_state_flag = 1;
	pr_info("version %s, init_flag %d, is_bypass %d\n",
			version_s, init_flag, is_bypass(NULL));
	pr_info("isbypass_flag %d, needbypass_flag %d\n",
		isbypass_flag, needbypass_flag);
	pr_info("di_pre_stru.bypass_flag=%d\n",
		di_pre_stru.bypass_flag);
	pr_info("afbcd support %d\n", afbc_is_supported());
	pr_info("recovery_flag = %d, recovery_log_reason=%d, di_blocking=%d",
		recovery_flag, recovery_log_reason, di_blocking);
	pr_info("recovery_log_queue_idx=%d, recovery_log_di_buf=0x%p\n",
		recovery_log_queue_idx, recovery_log_di_buf);
	pr_info("buffer_size=%d, mem_flag=%d, cma_flag=%d\n",
		de_devp->buffer_size, atomic_read(&de_devp->mem_flag),
		de_devp->flag_cma);
	keep_buf = di_post_stru.keep_buf;
	pr_info("used_post_buf_index %d(0x%p),",
		IS_ERR_OR_NULL(keep_buf) ?
		-1 : keep_buf->index, keep_buf);
	if (!IS_ERR_OR_NULL(keep_buf)) {
		pr_info("used_local_buf_index:\n");
		for (i = 0; i < USED_LOCAL_BUF_MAX; i++) {
			p = keep_buf->di_buf_dup_p[i];
			pr_info("%d(0x%p) ",
			IS_ERR_OR_NULL(p) ? -1 : p->index, p);
		}
	}
	pr_info("\nin_free_list (max %d):\n", MAX_IN_BUF_NUM);
	queue_for_each_entry(p, ptmp, QUEUE_IN_FREE, list) {
		pr_info("index %2d, 0x%p, type %d\n",
			p->index, p, p->type);
	}
	pr_info("local_free_list (max %d):\n", de_devp->buf_num_avail);
	queue_for_each_entry(p, ptmp, QUEUE_LOCAL_FREE, list) {
		pr_info("index %2d, 0x%p, type %d\n", p->index, p, p->type);
	}

	pr_info("post_doing_list:\n");
	queue_for_each_entry(p, ptmp, QUEUE_POST_DOING, list) {
		print_di_buf(p, 2);
	}
	pr_info("pre_ready_list:\n");
	queue_for_each_entry(p, ptmp, QUEUE_PRE_READY, list) {
		print_di_buf(p, 2);
	}
	pr_info("post_free_list (max %d):\n", di_post_stru.di_post_num);
	queue_for_each_entry(p, ptmp, QUEUE_POST_FREE, list) {
		pr_info("index %2d, 0x%p, type %d, vframetype 0x%x\n",
			p->index, p, p->type, p->vframe->type);
	}
	pr_info("post_ready_list:\n");
	queue_for_each_entry(p, ptmp, QUEUE_POST_READY, list) {
		print_di_buf(p, 2);
		print_di_buf(p->di_buf[0], 1);
		print_di_buf(p->di_buf[1], 1);
	}
	pr_info("display_list:\n");
	queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) {
		print_di_buf(p, 2);
		print_di_buf(p->di_buf[0], 1);
		print_di_buf(p->di_buf[1], 1);
	}
	pr_info("recycle_list:\n");
	queue_for_each_entry(p, ptmp, QUEUE_RECYCLE, list) {
		pr_info(
"index %d, 0x%p, type %d, vframetype 0x%x pre_ref_count %d post_ref_count %d\n",
			p->index, p, p->type,
			p->vframe->type,
			p->pre_ref_count,
			p->post_ref_count);
		if (p->di_wr_linked_buf) {
			pr_info(
	"linked index %2d, 0x%p, type %d pre_ref_count %d post_ref_count %d\n",
				p->di_wr_linked_buf->index,
				p->di_wr_linked_buf,
				p->di_wr_linked_buf->type,
				p->di_wr_linked_buf->pre_ref_count,
				p->di_wr_linked_buf->post_ref_count);
		}
	}
	if (di_pre_stru.di_inp_buf) {
		pr_info("di_inp_buf:index %d, 0x%p, type %d\n",
			di_pre_stru.di_inp_buf->index,
			di_pre_stru.di_inp_buf,
			di_pre_stru.di_inp_buf->type);
	} else {
		pr_info("di_inp_buf: NULL\n");
	}
	if (di_pre_stru.di_wr_buf) {
		pr_info("di_wr_buf:index %d, 0x%p, type %d\n",
			di_pre_stru.di_wr_buf->index,
			di_pre_stru.di_wr_buf,
			di_pre_stru.di_wr_buf->type);
	} else {
		pr_info("di_wr_buf: NULL\n");
	}
	dump_di_pre_stru(&di_pre_stru);
	dump_di_post_stru(&di_post_stru);
	pr_info("vframe_in[]:");

	for (i = 0; i < MAX_IN_BUF_NUM; i++)
		pr_info("0x%p ", vframe_in[i]);

	pr_info("\n");
	pr_info("vf_peek()=>0x%p, video_peek_cnt = %d\n",
		vf_peek(VFM_NAME), video_peek_cnt);
	pr_info("reg_unreg_timerout = %lu\n", reg_unreg_timeout_cnt);
	dump_state_flag = 0;
}

static unsigned char check_di_buf(struct di_buf_s *di_buf, int reason)
{
	int error = 0;

	if (di_buf == NULL) {
		pr_dbg("%s: Error %d, di_buf is NULL\n", __func__, reason);
		return 1;
	}

	if (di_buf->type == VFRAME_TYPE_IN) {
		if (di_buf->vframe != &vframe_in_dup[di_buf->index])
			error = 1;
	} else if (di_buf->type == VFRAME_TYPE_LOCAL) {
		if (di_buf->vframe !=
		    &vframe_local[di_buf->index])
			error = 1;
	} else if (di_buf->type == VFRAME_TYPE_POST) {
		if (di_buf->vframe != &vframe_post[di_buf->index])
			error = 1;
	} else {
		error = 1;
	}

	if (error) {
		pr_dbg(
			"%s: Error %d, di_buf wrong\n", __func__,
			reason);
		if (recovery_flag == 0)
			recovery_log_reason = reason;
		recovery_flag++;
		dump_di_buf(di_buf);
		return 1;
	}

	return 0;
}
/*
 *  di pre process
 */
static void
config_di_mcinford_mif(struct DI_MC_MIF_s *di_mcinford_mif,
		       struct di_buf_s *di_buf)
{
	if (di_buf) {
		di_mcinford_mif->size_x = (di_buf->vframe->height + 2) / 4 - 1;
		di_mcinford_mif->size_y = 1;
		di_mcinford_mif->canvas_num = di_buf->mcinfo_canvas_idx;
	}
}
static void
config_di_pre_mc_mif(struct DI_MC_MIF_s *di_mcinfo_mif,
		     struct DI_MC_MIF_s *di_mcvec_mif,
		     struct di_buf_s *di_buf)
{
	unsigned int pre_size_w = 0, pre_size_h = 0;

	if (di_buf) {
		pre_size_w = di_buf->vframe->width;
		pre_size_h = (di_buf->vframe->height + 1) / 2;
		di_mcinfo_mif->size_x = (pre_size_h + 1) / 2 - 1;
		di_mcinfo_mif->size_y = 1;
		di_mcinfo_mif->canvas_num = di_buf->mcinfo_canvas_idx;

		di_mcvec_mif->size_x = (pre_size_w + 4) / 5 - 1;
		di_mcvec_mif->size_y = pre_size_h - 1;
		di_mcvec_mif->canvas_num = di_buf->mcvec_canvas_idx;
	}
}

static void config_di_cnt_mif(struct DI_SIM_MIF_s *di_cnt_mif,
	struct di_buf_s *di_buf)
{
	if (di_buf) {
		di_cnt_mif->start_x = 0;
		di_cnt_mif->end_x = di_buf->vframe->width - 1;
		di_cnt_mif->start_y = 0;
		di_cnt_mif->end_y = di_buf->vframe->height / 2 - 1;
		di_cnt_mif->canvas_num = di_buf->cnt_canvas_idx;
	}
}

static void
config_di_wr_mif(struct DI_SIM_MIF_s *di_nrwr_mif,
		 struct DI_SIM_MIF_s *di_mtnwr_mif,
		 struct di_buf_s *di_buf)
{
	vframe_t *vf = di_buf->vframe;
	di_nrwr_mif->canvas_num = di_buf->nr_canvas_idx;
	di_nrwr_mif->start_x = 0;
	di_nrwr_mif->end_x = vf->width - 1;
	di_nrwr_mif->start_y = 0;
	if (di_buf->vframe->bitdepth & BITDEPTH_Y10)
		di_nrwr_mif->bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
	else
		di_nrwr_mif->bit_mode = 0;
	if (di_pre_stru.prog_proc_type == 0)
		di_nrwr_mif->end_y = vf->height / 2 - 1;
	else
		di_nrwr_mif->end_y = vf->height - 1;
	if (di_pre_stru.prog_proc_type == 0) {
		di_mtnwr_mif->start_x = 0;
		di_mtnwr_mif->end_x = vf->width - 1;
		di_mtnwr_mif->start_y = 0;
		di_mtnwr_mif->end_y = vf->height / 2 - 1;
		di_mtnwr_mif->canvas_num = di_buf->mtn_canvas_idx;
	}
}
static bool force_prog = true;
module_param_named(force_prog, force_prog, bool, 0664);
static void config_di_mif(struct DI_MIF_s *di_mif, struct di_buf_s *di_buf)
{
	if (di_buf == NULL)
		return;
	di_mif->canvas0_addr0 =
		di_buf->vframe->canvas0Addr & 0xff;
	di_mif->canvas0_addr1 =
		(di_buf->vframe->canvas0Addr >> 8) & 0xff;
	di_mif->canvas0_addr2 =
		(di_buf->vframe->canvas0Addr >> 16) & 0xff;

	di_mif->nocompress = (di_buf->vframe->type & VIDTYPE_COMPRESS)?0:1;

	if (di_buf->vframe->bitdepth & BITDEPTH_Y10) {
		if (di_buf->vframe->type & VIDTYPE_VIU_444)
			di_mif->bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2;
		else if (di_buf->vframe->type & VIDTYPE_VIU_422)
			di_mif->bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
	} else {
		di_mif->bit_mode = 0;
	}
	if (di_buf->vframe->type & VIDTYPE_VIU_422) {
		/* from vdin or local vframe */
		if ((!is_progressive(di_buf->vframe))
		    || (di_pre_stru.prog_proc_type)
		    ) {
			di_mif->video_mode = 0;
			di_mif->set_separate_en = 0;
			di_mif->src_field_mode = 0;
			di_mif->output_field_num = 0;
			di_mif->luma_x_start0 = 0;
			di_mif->luma_x_end0 =
				di_buf->vframe->width - 1;
			di_mif->luma_y_start0 = 0;
			if (di_pre_stru.prog_proc_type)
				di_mif->luma_y_end0 =
					di_buf->vframe->height - 1;
			else
				di_mif->luma_y_end0 =
					di_buf->vframe->height / 2 - 1;
			di_mif->chroma_x_start0 = 0;
			di_mif->chroma_x_end0 = 0;
			di_mif->chroma_y_start0 = 0;
			di_mif->chroma_y_end0 = 0;
			di_mif->canvas0_addr0 =
				di_buf->vframe->canvas0Addr & 0xff;
			di_mif->canvas0_addr1 =
				(di_buf->vframe->canvas0Addr >> 8) & 0xff;
			di_mif->canvas0_addr2 =
				(di_buf->vframe->canvas0Addr >> 16) & 0xff;
		}
	} else {
		if (di_buf->vframe->type & VIDTYPE_VIU_444)
			di_mif->video_mode = 1;
		else
			di_mif->video_mode = 0;
		if (di_buf->vframe->type & VIDTYPE_VIU_NV21)
			di_mif->set_separate_en = 2;
		else
			di_mif->set_separate_en = 1;

		if (is_progressive(di_buf->vframe) &&
		    (di_pre_stru.prog_proc_type)) {
			di_mif->src_field_mode = 0;
			di_mif->output_field_num = 0; /* top */
			di_mif->luma_x_start0 = 0;
			di_mif->luma_x_end0 =
				di_buf->vframe->width - 1;
			di_mif->luma_y_start0 = 0;
			di_mif->luma_y_end0 =
				di_buf->vframe->height - 1;
			di_mif->chroma_x_start0 = 0;
			di_mif->chroma_x_end0 =
				di_buf->vframe->width / 2 - 1;
			di_mif->chroma_y_start0 = 0;
			di_mif->chroma_y_end0 =
				di_buf->vframe->height / 2 - 1;
		} else {
			if (di_pre_stru.cur_inp_type  & VIDTYPE_INTERLACE)
				di_mif->src_prog = 0;
			else
				di_mif->src_prog = force_prog?1:0;
			di_mif->src_field_mode = 1;
			if (
				(di_buf->vframe->type & VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_TOP) {
				di_mif->output_field_num = 0; /* top */
				di_mif->luma_x_start0 = 0;
				di_mif->luma_x_end0 =
					di_buf->vframe->width - 1;
				di_mif->luma_y_start0 = 0;
				di_mif->luma_y_end0 =
					di_buf->vframe->height - 2;
				di_mif->chroma_x_start0 = 0;
				di_mif->chroma_x_end0 =
					di_buf->vframe->width / 2 - 1;
				di_mif->chroma_y_start0 = 0;
				di_mif->chroma_y_end0 =
					di_buf->vframe->height / 2
						- (di_mif->src_prog?1:2);
			} else {
				di_mif->output_field_num = 1;
				/* bottom */
				di_mif->luma_x_start0 = 0;
				di_mif->luma_x_end0 =
					di_buf->vframe->width - 1;
				di_mif->luma_y_start0 = 1;
				di_mif->luma_y_end0 =
					di_buf->vframe->height - 1;
				di_mif->chroma_x_start0 = 0;
				di_mif->chroma_x_end0 =
					di_buf->vframe->width / 2 - 1;
				di_mif->chroma_y_start0 =
					(di_mif->src_prog?0:1);
				di_mif->chroma_y_end0 =
					di_buf->vframe->height / 2 - 1;
			}
		}
	}
}

static void di_pre_size_change(unsigned short width,
	unsigned short height, unsigned short vf_type);

#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
static void pre_inp_canvas_config(struct vframe_s *vf);
#endif

static void pre_de_process(void)
{
	ulong irq_flag2 = 0;
	unsigned short pre_width = 0, pre_height = 0;
	unsigned char chan2_field_num = 1;
	int canvases_idex = di_pre_stru.field_count_for_cont % 2;
	unsigned short cur_inp_field_type = VIDTYPE_TYPEMASK;
	unsigned short int_mask = 0x7f;

	di_pre_stru.pre_de_process_flag = 1;
	di_pre_stru.pre_de_busy_timer_count = 0;
	ddbg_mod_save(eDI_DBG_MOD_PRE_SETB, 0, di_pre_stru.in_seq);/*dbg*/
	#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
	pre_inp_canvas_config(di_pre_stru.di_inp_buf->vframe);
	#endif

	config_di_mif(&di_pre_stru.di_inp_mif, di_pre_stru.di_inp_buf);
	/* pr_dbg("set_separate_en=%d vframe->type %d\n",
	 * di_pre_stru.di_inp_mif.set_separate_en,
	 * di_pre_stru.di_inp_buf->vframe->type);
	 */
#ifdef DI_USE_FIXED_CANVAS_IDX
	if (di_pre_stru.di_mem_buf_dup_p != NULL &&
		di_pre_stru.di_mem_buf_dup_p != di_pre_stru.di_inp_buf) {
		config_canvas_idx(di_pre_stru.di_mem_buf_dup_p,
			di_pre_idx[canvases_idex][0], -1);
		config_cnt_canvas_idx(di_pre_stru.di_mem_buf_dup_p,
			di_pre_idx[canvases_idex][1]);
	} else {
		config_cnt_canvas_idx(di_pre_stru.di_wr_buf,
				di_pre_idx[canvases_idex][1]);
		config_di_cnt_mif(&di_pre_stru.di_contp2rd_mif,
			di_pre_stru.di_wr_buf);

	}
	if (di_pre_stru.di_chan2_buf_dup_p != NULL) {
		config_canvas_idx(di_pre_stru.di_chan2_buf_dup_p,
			di_pre_idx[canvases_idex][2], -1);
		config_cnt_canvas_idx(di_pre_stru.di_chan2_buf_dup_p,
			di_pre_idx[canvases_idex][3]);
	} else {
		config_cnt_canvas_idx(di_pre_stru.di_wr_buf,
			di_pre_idx[canvases_idex][3]);
	}
	config_canvas_idx(di_pre_stru.di_wr_buf,
		di_pre_idx[canvases_idex][4], di_pre_idx[canvases_idex][5]);
	config_cnt_canvas_idx(di_pre_stru.di_wr_buf,
		di_pre_idx[canvases_idex][6]);
	if (mcpre_en) {
		if (di_pre_stru.di_chan2_buf_dup_p != NULL)
			config_mcinfo_canvas_idx(di_pre_stru.di_chan2_buf_dup_p,
				di_pre_idx[canvases_idex][7]);
		else
			config_mcinfo_canvas_idx(di_pre_stru.di_wr_buf,
				di_pre_idx[canvases_idex][7]);

		config_mcinfo_canvas_idx(di_pre_stru.di_wr_buf,
			di_pre_idx[canvases_idex][8]);
		config_mcvec_canvas_idx(di_pre_stru.di_wr_buf,
			di_pre_idx[canvases_idex][9]);
	}
#endif
	config_di_mif(&di_pre_stru.di_mem_mif, di_pre_stru.di_mem_buf_dup_p);
	if (di_pre_stru.di_chan2_buf_dup_p == NULL) {
		config_di_mif(&di_pre_stru.di_chan2_mif,
			di_pre_stru.di_inp_buf);
	} else
		config_di_mif(&di_pre_stru.di_chan2_mif,
			di_pre_stru.di_chan2_buf_dup_p);

	config_di_wr_mif(&di_pre_stru.di_nrwr_mif, &di_pre_stru.di_mtnwr_mif,
		di_pre_stru.di_wr_buf);

	if (di_pre_stru.di_chan2_buf_dup_p)
		config_di_cnt_mif(&di_pre_stru.di_contprd_mif,
			di_pre_stru.di_chan2_buf_dup_p);
	else
		config_di_cnt_mif(&di_pre_stru.di_contprd_mif,
			di_pre_stru.di_wr_buf);

	config_di_cnt_mif(&di_pre_stru.di_contwr_mif, di_pre_stru.di_wr_buf);
	if (mcpre_en) {
		if (di_pre_stru.di_chan2_buf_dup_p)
			config_di_mcinford_mif(&di_pre_stru.di_mcinford_mif,
				di_pre_stru.di_chan2_buf_dup_p);
		else
			config_di_mcinford_mif(&di_pre_stru.di_mcinford_mif,
				di_pre_stru.di_wr_buf);

		config_di_pre_mc_mif(&di_pre_stru.di_mcinfowr_mif,
			&di_pre_stru.di_mcvecwr_mif, di_pre_stru.di_wr_buf);
	}

	if ((di_pre_stru.di_chan2_buf_dup_p) &&
	    ((di_pre_stru.di_chan2_buf_dup_p->vframe->type & VIDTYPE_TYPEMASK)
	     == VIDTYPE_INTERLACE_TOP))
		chan2_field_num = 0;
	pre_width = di_pre_stru.di_nrwr_mif.end_x + 1;
	pre_height = di_pre_stru.di_nrwr_mif.end_y + 1;
	if (di_pre_stru.input_size_change_flag) {
		cur_inp_field_type =
		(di_pre_stru.di_inp_buf->vframe->type & VIDTYPE_TYPEMASK);
		cur_inp_field_type =
	di_pre_stru.cur_prog_flag ? VIDTYPE_PROGRESSIVE : cur_inp_field_type;
		di_pre_size_change(pre_width, pre_height,
				cur_inp_field_type);
		di_pre_stru.input_size_change_flag = false;
	}

	di_patch_mov_setting();
	if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) {
		if (de_devp->nrds_enable) {
			nr_ds_mif_config();
			nr_ds_hw_ctrl(true);
			int_mask = 0x3f;
		} else {
			nr_ds_hw_ctrl(false);
		}
	}

#ifdef CONFIG_AMLOGIC_MEDIA_TVIN
	/*patch for SECAM signal format from vlsi-feijun for all IC*/
	di_nr_opl()->secam_cfr_adjust(di_pre_stru.di_inp_buf->vframe->sig_fmt,
			 di_pre_stru.di_inp_buf->vframe->type);
#endif

	/* set interrupt mask for pre module.
	 * we need to only leave one mask open
	 * to prevent multiple entry for de_irq
	*/
	enable_di_pre_aml(&di_pre_stru.di_inp_mif,
			&di_pre_stru.di_mem_mif,
			&di_pre_stru.di_chan2_mif,
			&di_pre_stru.di_nrwr_mif,
			&di_pre_stru.di_mtnwr_mif,
			&di_pre_stru.di_contp2rd_mif,
			&di_pre_stru.di_contprd_mif,
			&di_pre_stru.di_contwr_mif,
			di_pre_stru.madi_enable,
			chan2_field_num,
			di_pre_stru.vdin2nr);

	enable_afbc_input(di_pre_stru.di_inp_buf->vframe);

	if (mcpre_en) {
		if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
			enable_mc_di_pre_g12(
				&di_pre_stru.di_mcinford_mif,
				&di_pre_stru.di_mcinfowr_mif,
				&di_pre_stru.di_mcvecwr_mif,
				di_pre_stru.mcdi_enable);
		else
			enable_mc_di_pre(
				&di_pre_stru.di_mcinford_mif,
				&di_pre_stru.di_mcinfowr_mif,
				&di_pre_stru.di_mcvecwr_mif,
				di_pre_stru.mcdi_enable);
	}

	di_pre_stru.field_count_for_cont++;
	if (di_pre_stru.field_count_for_cont >= 5)
		DI_Wr_reg_bits(DI_MTN_CTRL, 0, 30, 1);

	di_txl_patch_prog(di_pre_stru.cur_prog_flag,
		di_pre_stru.field_count_for_cont, mcpre_en);

#ifdef SUPPORT_MPEG_TO_VDIN
	if (mpeg2vdin_flag) {
		struct vdin_arg_s vdin_arg;
		struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops();

		vdin_arg.cmd = VDIN_CMD_FORCE_GO_FIELD;
		if (vdin_ops->tvin_vdin_func)
			vdin_ops->tvin_vdin_func(0, &vdin_arg);
	}
#endif
	/* must make sure follow part issue without interrupts,
	 * otherwise may cause watch dog reboot
	 */
	di_lock_irqfiq_save(irq_flag2);
	if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
		pre_frame_reset_g12(di_pre_stru.madi_enable,
			di_pre_stru.mcdi_enable);
	else
		pre_frame_reset();

	/* enable mc pre mif*/
	enable_di_pre_mif(true, mcpre_en);
	di_unlock_irqfiq_restore(irq_flag2);
	/*reinit pre busy flag*/
	di_pre_stru.pre_de_busy_timer_count = 0;
	di_pre_stru.pre_de_busy = 1;
	#ifdef SUPPORT_MPEG_TO_VDIN
	if (mpeg2vdin_flag)
		RDMA_WR_BITS(DI_PRE_CTRL, 1, 13, 1);
	#endif
	di_pre_stru.irq_time[0] = cur_to_msecs();
	di_pre_stru.irq_time[1] = cur_to_msecs();
	ddbg_mod_save(eDI_DBG_MOD_PRE_SETE, 0, di_pre_stru.in_seq);/*dbg*/
#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
	if (di_pre_rdma_enable & 0x2)
		rdma_config(de_devp->rdma_handle, RDMA_TRIGGER_MANUAL);
	else if (di_pre_rdma_enable & 1)
		rdma_config(de_devp->rdma_handle, RDMA_DEINT_IRQ);
#endif
	di_pre_stru.pre_de_process_flag = 0;
}

static void pre_de_done_buf_clear(void)
{
	struct di_buf_s *wr_buf = NULL;

	if (di_pre_stru.di_wr_buf) {
		wr_buf = di_pre_stru.di_wr_buf;
		if ((di_pre_stru.prog_proc_type == 2) &&
		    wr_buf->di_wr_linked_buf) {
			wr_buf->di_wr_linked_buf->pre_ref_count = 0;
			wr_buf->di_wr_linked_buf->post_ref_count = 0;
			queue_in(wr_buf->di_wr_linked_buf, QUEUE_RECYCLE);
			wr_buf->di_wr_linked_buf = NULL;
		}
		wr_buf->pre_ref_count = 0;
		wr_buf->post_ref_count = 0;
		queue_in(wr_buf, QUEUE_RECYCLE);
		di_pre_stru.di_wr_buf = NULL;
	}
	if (di_pre_stru.di_inp_buf) {
		if (di_pre_stru.di_mem_buf_dup_p == di_pre_stru.di_inp_buf)
			di_pre_stru.di_mem_buf_dup_p = NULL;

		queue_in(di_pre_stru.di_inp_buf, QUEUE_RECYCLE);
		di_pre_stru.di_inp_buf = NULL;
	}
}

static void top_bot_config(struct di_buf_s *di_buf)
{
	vframe_t *vframe = di_buf->vframe;

	if (((invert_top_bot & 0x1) != 0) && (!is_progressive(vframe))) {
		if (di_buf->invert_top_bot_flag == 0) {
			if (
				(vframe->type & VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_TOP) {
				vframe->type &= (~VIDTYPE_TYPEMASK);
				vframe->type |= VIDTYPE_INTERLACE_BOTTOM;
			} else {
				vframe->type &= (~VIDTYPE_TYPEMASK);
				vframe->type |= VIDTYPE_INTERLACE_TOP;
			}
			di_buf->invert_top_bot_flag = 1;
		}
	}
}

static bool pulldown_enable = true;

static bool combing_fix_en = true;
module_param_named(combing_fix_en, combing_fix_en, bool, 0664);

static int cur_lev = 2;
module_param_named(combing_cur_lev, cur_lev, int, 0444);

static void pre_de_done_buf_config(void)
{
	ulong irq_flag2 = 0;
	struct di_buf_s *post_wr_buf = NULL;
	unsigned int glb_frame_mot_num = 0;
	unsigned int glb_field_mot_num = 0;
	unsigned int pull_down_info = 0;

	ddbg_mod_save(eDI_DBG_MOD_PRE_DONEB, 0, di_pre_stru.in_seq);/*dbg*/
	if (di_pre_stru.di_wr_buf) {
		if (di_pre_stru.pre_throw_flag > 0) {
			di_pre_stru.di_wr_buf->throw_flag = 1;
			di_pre_stru.pre_throw_flag--;
		} else {
			di_pre_stru.di_wr_buf->throw_flag = 0;
		}
#ifdef DET3D
		if (di_pre_stru.di_wr_buf->vframe->trans_fmt == 0 &&
				di_pre_stru.det3d_trans_fmt != 0 && det3d_en) {
			di_pre_stru.di_wr_buf->vframe->trans_fmt =
			di_pre_stru.det3d_trans_fmt;
			set3d_view(di_pre_stru.det3d_trans_fmt,
				di_pre_stru.di_wr_buf->vframe);
		}
#endif
		if (!di_pre_rdma_enable)
			di_pre_stru.di_post_wr_buf = di_pre_stru.di_wr_buf;
		post_wr_buf = di_pre_stru.di_post_wr_buf;

		if (post_wr_buf) {
			post_wr_buf->vframe->di_pulldown = 0;
			post_wr_buf->vframe->di_gmv = 0;
			post_wr_buf->vframe->di_cm_cnt = 0;
		}
		if (IS_ERR_OR_NULL(post_wr_buf))
			return;
		if (post_wr_buf && !di_pre_stru.cur_prog_flag) {
			read_pulldown_info(&glb_frame_mot_num,
				&glb_field_mot_num);
			if (pulldown_enable) {
				pull_down_info = pulldown_detection(
					&post_wr_buf->pd_config,
					di_pre_stru.mtn_status, overturn,
					di_pre_stru.di_inp_buf->vframe);
				post_wr_buf->vframe->di_pulldown
					= pull_down_info;

			}
			post_wr_buf->vframe->di_pulldown |= 0x08;

			post_wr_buf->vframe->di_gmv = glb_frame_mot_num;
			post_wr_buf->vframe->di_cm_cnt = di_rd_mcdi_fldcnt();
			if (di_pre_stru.combing_fix_en)
				cur_lev = adaptive_combing_fixing(
				di_pre_stru.mtn_status,
					glb_field_mot_num,
					glb_frame_mot_num,
					di_force_bit_mode);
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX))
				di_nr_opl()->adaptive_cue_adjust(glb_frame_mot_num,
					glb_field_mot_num);
			pulldown_info_clear_g12a();
		}

		if (di_pre_stru.cur_prog_flag) {
			if (di_pre_stru.prog_proc_type == 0) {
				/* di_mem_buf_dup->vfrme
				 * is either local vframe,
				 * or bot field of vframe from in_list
				 */
				di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0;
				di_pre_stru.di_mem_buf_dup_p
					= di_pre_stru.di_chan2_buf_dup_p;
				di_pre_stru.di_chan2_buf_dup_p
					= di_pre_stru.di_wr_buf;
#ifdef DI_BUFFER_DEBUG
				di_print("%s:set di_mem to di_chan2,",
					__func__);
				di_print("%s:set di_chan2 to di_wr_buf\n",
					__func__);
#endif
			} else {
				di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0;
				/*recycle the progress throw buffer*/
				if (di_pre_stru.di_wr_buf->throw_flag) {
					di_pre_stru.di_wr_buf->
						pre_ref_count = 0;
					di_pre_stru.di_mem_buf_dup_p = NULL;
#ifdef DI_BUFFER_DEBUG
				di_print(
				"%s set throw %s[%d] pre_ref_count to 0.\n",
				__func__,
				vframe_type_name[di_pre_stru.di_wr_buf->type],
				di_pre_stru.di_wr_buf->index);
#endif
				} else {
					di_pre_stru.di_mem_buf_dup_p
						= di_pre_stru.di_wr_buf;
				}
#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s: set di_mem_buf_dup_p to di_wr_buf\n",
					__func__);
#endif
			}

			di_pre_stru.di_wr_buf->seq
				= di_pre_stru.pre_ready_seq++;
			di_pre_stru.di_wr_buf->post_ref_count = 0;
			di_pre_stru.di_wr_buf->left_right
				= di_pre_stru.left_right;
			if (di_pre_stru.source_change_flag) {
				di_pre_stru.di_wr_buf->new_format_flag = 1;
				di_pre_stru.source_change_flag = 0;
			} else {
				di_pre_stru.di_wr_buf->new_format_flag = 0;
			}
			if (bypass_state == 1) {
				di_pre_stru.di_wr_buf->new_format_flag = 1;
				bypass_state = 0;
#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s:bypass_state->0, is_bypass() %d\n",
					__func__, is_bypass(NULL));
				di_print(
					"trick_mode %d bypass_all %d\n",
					trick_mode, bypass_all);
#endif
			}
			if (di_pre_stru.di_post_wr_buf)
				queue_in(di_pre_stru.di_post_wr_buf,
					QUEUE_PRE_READY);
#ifdef DI_BUFFER_DEBUG
			di_print(
				"%s: %s[%d] => pre_ready_list\n", __func__,
				vframe_type_name[di_pre_stru.di_wr_buf->type],
				di_pre_stru.di_wr_buf->index);
#endif
			if (di_pre_stru.di_wr_buf) {
				if (di_pre_rdma_enable)
					di_pre_stru.di_post_wr_buf =
				di_pre_stru.di_wr_buf;
				else
					di_pre_stru.di_post_wr_buf = NULL;
				di_pre_stru.di_wr_buf = NULL;
			}
		} else {
			di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0;
			di_pre_stru.di_mem_buf_dup_p = NULL;
			if (di_pre_stru.di_chan2_buf_dup_p) {
				di_pre_stru.di_mem_buf_dup_p =
					di_pre_stru.di_chan2_buf_dup_p;
#ifdef DI_BUFFER_DEBUG
				di_print(
				"%s: di_mem_buf_dup_p = di_chan2_buf_dup_p\n",
				__func__);
#endif
			}
			di_pre_stru.di_chan2_buf_dup_p = di_pre_stru.di_wr_buf;

			if (di_pre_stru.source_change_flag) {
				/* add dummy buf, will not be displayed */
				/*coverity[var_deref_model] post_wr_buf has been judged*/
				add_dummy_vframe_type_pre(post_wr_buf);
			}
			di_pre_stru.di_wr_buf->seq =
				di_pre_stru.pre_ready_seq++;
			di_pre_stru.di_wr_buf->left_right =
				di_pre_stru.left_right;
			di_pre_stru.di_wr_buf->post_ref_count = 0;
			if (di_pre_stru.source_change_flag) {
				di_pre_stru.di_wr_buf->new_format_flag = 1;
				di_pre_stru.source_change_flag = 0;
			} else {
				di_pre_stru.di_wr_buf->new_format_flag = 0;
			}
			if (bypass_state == 1) {
				di_pre_stru.di_wr_buf->new_format_flag = 1;
				bypass_state = 0;

#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s:bypass_state->0, is_bypass() %d\n",
					__func__, is_bypass(NULL));
				di_print(
					"trick_mode %d bypass_all %d\n",
					trick_mode, bypass_all);
#endif
			}

			if (di_pre_stru.di_post_wr_buf)
				queue_in(di_pre_stru.di_post_wr_buf,
					QUEUE_PRE_READY);

			di_print("%s: %s[%d] => pre_ready_list\n", __func__,
				vframe_type_name[di_pre_stru.di_wr_buf->type],
				di_pre_stru.di_wr_buf->index);

			if (di_pre_stru.di_wr_buf) {
				if (di_pre_rdma_enable)
					di_pre_stru.di_post_wr_buf =
				di_pre_stru.di_wr_buf;
				else
					di_pre_stru.di_post_wr_buf = NULL;
				di_pre_stru.di_wr_buf = NULL;
			}
		}
	}
	if (di_pre_stru.di_post_inp_buf && di_pre_rdma_enable) {
#ifdef DI_BUFFER_DEBUG
		di_print("%s: %s[%d] => recycle_list\n", __func__,
			vframe_type_name[di_pre_stru.di_post_inp_buf->type],
			di_pre_stru.di_post_inp_buf->index);
#endif
		di_lock_irqfiq_save(irq_flag2);
		queue_in(di_pre_stru.di_post_inp_buf, QUEUE_RECYCLE);
		di_pre_stru.di_post_inp_buf = NULL;
		di_unlock_irqfiq_restore(irq_flag2);
	}
	if (di_pre_stru.di_inp_buf) {
		if (!di_pre_rdma_enable) {
#ifdef DI_BUFFER_DEBUG
			di_print("%s: %s[%d] => recycle_list\n", __func__,
			vframe_type_name[di_pre_stru.di_inp_buf->type],
			di_pre_stru.di_inp_buf->index);
#endif
			di_lock_irqfiq_save(irq_flag2);
			queue_in(di_pre_stru.di_inp_buf, QUEUE_RECYCLE);
			di_pre_stru.di_inp_buf = NULL;
			di_unlock_irqfiq_restore(irq_flag2);
		} else {
			di_pre_stru.di_post_inp_buf = di_pre_stru.di_inp_buf;
			di_pre_stru.di_inp_buf = NULL;
		}
	}
	ddbg_mod_save(eDI_DBG_MOD_PRE_DONEE, 0, di_pre_stru.in_seq);/*dbg*/
}

static void recycle_vframe_type_pre(struct di_buf_s *di_buf)
{
	ulong irq_flag2 = 0;

	di_lock_irqfiq_save(irq_flag2);

	queue_in(di_buf, QUEUE_RECYCLE);

	di_unlock_irqfiq_restore(irq_flag2);
}
/*
 * add dummy buffer to pre ready queue
 */
static void add_dummy_vframe_type_pre(struct di_buf_s *src_buf)
{
	struct di_buf_s *di_buf_tmp = NULL;

	if (!queue_empty(QUEUE_LOCAL_FREE)) {
		di_buf_tmp = get_di_buf_head(QUEUE_LOCAL_FREE);
		if (di_buf_tmp) {
			queue_out(di_buf_tmp);
			di_buf_tmp->pre_ref_count = 0;
			di_buf_tmp->post_ref_count = 0;
			di_buf_tmp->post_proc_flag = 3;
			di_buf_tmp->new_format_flag = 0;
			if (!IS_ERR_OR_NULL(src_buf))
				memcpy(di_buf_tmp->vframe, src_buf->vframe,
					sizeof(vframe_t));
			queue_in(di_buf_tmp, QUEUE_PRE_READY);
			#ifdef DI_BUFFER_DEBUG
			di_print("%s: dummy %s[%d] => pre_ready_list\n",
				__func__,
				vframe_type_name[di_buf_tmp->type],
				di_buf_tmp->index);
			#endif
		}
	}
}
/*
 * it depend on local buffer queue type is 2
 */
static int peek_free_linked_buf(void)
{
	struct di_buf_s *p = NULL;
	int itmp, p_index = -2;

	if (list_count(QUEUE_LOCAL_FREE) < 2)
		return -1;

	queue_for_each_entry(p, ptmp, QUEUE_LOCAL_FREE, list) {
		if (abs(p->index - p_index) == 1)
			return min(p->index, p_index);
		p_index = p->index;
	}
	return -1;
}
/*
 * it depend on local buffer queue type is 2
 */
static struct di_buf_s *get_free_linked_buf(int idx)
{
	struct di_buf_s *di_buf = NULL, *di_buf_linked = NULL;
	int pool_idx = 0, di_buf_idx = 0;

	queue_t *q = &(queue[QUEUE_LOCAL_FREE]);

	if (list_count(QUEUE_LOCAL_FREE) < 2)
		return NULL;
	if (q->pool[idx] != 0 && q->pool[idx + 1] != 0) {
		pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1;
		di_buf_idx = q->pool[idx] & 0xff;
		if (pool_idx < VFRAME_TYPE_NUM) {
			if (di_buf_idx < di_buf_pool[pool_idx].size) {
				di_buf = &(di_buf_pool[pool_idx].
					di_buf_ptr[di_buf_idx]);
				queue_out(di_buf);
			}
		}
		pool_idx = ((q->pool[idx + 1] >> 8) & 0xff) - 1;
		di_buf_idx = q->pool[idx + 1] & 0xff;
		if (pool_idx < VFRAME_TYPE_NUM) {
			if (di_buf_idx < di_buf_pool[pool_idx].size) {
				di_buf_linked =	&(di_buf_pool[pool_idx].
					di_buf_ptr[di_buf_idx]);
				queue_out(di_buf_linked);
			}
		}
		/*coverity[FORWARD_NULL] di_buf has been judged empty*/
		if (IS_ERR_OR_NULL(di_buf))
			return NULL;
		di_buf->di_wr_linked_buf = di_buf_linked;
	}
	return di_buf;
}

#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
static void pre_inp_canvas_config(struct vframe_s *vf)
{
	if (vf->canvas0Addr == (u32)-1) {
		canvas_config_config(di_inp_idx[0],
			&vf->canvas0_config[0]);
		canvas_config_config(di_inp_idx[1],
			&vf->canvas0_config[1]);
		vf->canvas0Addr = (di_inp_idx[1]<<8)|(di_inp_idx[0]);
		if (vf->plane_num == 2) {
			vf->canvas0Addr |= (di_inp_idx[1]<<16);
		} else if (vf->plane_num == 3) {
			canvas_config_config(di_inp_idx[2],
			&vf->canvas0_config[2]);
			vf->canvas0Addr |= (di_inp_idx[2]<<16);
		}
		vf->canvas1Addr = vf->canvas0Addr;
	}
}
#endif
static int pps_dstw;
module_param_named(pps_dstw, pps_dstw, int, 0644);
static int pps_dsth;
module_param_named(pps_dsth, pps_dsth, int, 0644);
static bool pps_en;
module_param_named(pps_en, pps_en, bool, 0644);
static unsigned int pps_position = 1;
module_param_named(pps_position, pps_position, uint, 0644);
static unsigned int pre_enable_mask = 3;/*bit0:ma bit1:mc*/
module_param_named(pre_enable_mask, pre_enable_mask, uint, 0644);

static bool pre_hsc_down_en;
module_param_named(pre_hsc_down_en, pre_hsc_down_en, bool, 0644);
static int pre_hsc_down_width = 480;
module_param_named(pre_hsc_down_width, pre_hsc_down_width, int, 0644);

u32 di_request_afbc(u32 onoff)
{
	u32 afbc_busy;

	if (onoff)
		afbc_busy = di_request_afbc_hw(afbc_get_decnub(), true);
	else
		afbc_busy = di_request_afbc_hw(afbc_get_decnub(), false);

	return afbc_busy;
}

static unsigned char pre_de_buf_config(void)
{
	struct di_buf_s *di_buf = NULL;
	vframe_t *vframe;
	int i, di_linked_buf_idx = -1;
	unsigned char change_type = 0;
	bool bit10_pack_patch = false;
	unsigned int width_roundup = 2;
	u32 rls_timeout;
	u32 afbc_busy;
	u32 is_afbc_mode = 0;
	bool flg_1080i = false;
	bool flg_480i = false;

	if (di_blocking || !atomic_read(&de_devp->mem_flag))
		return 0;
	if ((list_count(QUEUE_IN_FREE) < 2 && (!di_pre_stru.di_inp_buf_next)) ||
	    (queue_empty(QUEUE_LOCAL_FREE)))
		return 0;

	if (is_bypass(NULL)) {
		/* some provider has problem if receiver
		 * get all buffers of provider
		 */
		int in_buf_num = 0;
		cur_lev = 0;
		for (i = 0; i < MAX_IN_BUF_NUM; i++)
			if (vframe_in[i] != NULL)
				in_buf_num++;
		if (in_buf_num > BYPASS_GET_MAX_BUF_NUM
#ifdef DET3D
			&& (di_pre_stru.vframe_interleave_flag == 0)
#endif
		    )
			return 0;

		di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false);
	} else if (di_pre_stru.prog_proc_type == 2) {
		di_linked_buf_idx = peek_free_linked_buf();
		if (di_linked_buf_idx == -1 &&
			!IS_ERR_OR_NULL(di_post_stru.keep_buf)) {
			recycle_keep_buffer();
			pr_info("%s: recycle keep buffer for peek null linked buf\n",
				__func__);
			return 0;
		}
	}

	if (di_pre_stru.di_inp_buf_next) {
		di_pre_stru.di_inp_buf = di_pre_stru.di_inp_buf_next;
		di_pre_stru.di_inp_buf_next = NULL;
#ifdef DI_BUFFER_DEBUG
		di_print("%s: di_inp_buf_next %s[%d] => di_inp_buf\n",
			__func__,
			vframe_type_name[di_pre_stru.di_inp_buf->type],
			di_pre_stru.di_inp_buf->index);
#endif
		if (di_pre_stru.di_mem_buf_dup_p == NULL) {/* use n */
			di_pre_stru.di_mem_buf_dup_p = di_pre_stru.di_inp_buf;
#ifdef DI_BUFFER_DEBUG
			di_print(
				"%s: set di_mem_buf_dup_p to be di_inp_buf\n",
				__func__);
#endif
		}
	} else {
		/* check if source change */
		vframe = vf_peek(VFM_NAME);

		if (vframe && is_from_vdin(vframe)) {
#ifdef RUN_DI_PROCESS_IN_IRQ
			di_pre_stru.vdin2nr = is_input2pre();
#endif
		}

		vframe = vf_get(VFM_NAME);

		if (vframe == NULL)
			return 0;

		/*for support compress from dec*/
		if (IS_COMP_MODE(vframe->type) &&
			!is_bypass(vframe)) {
			is_afbc_mode = true;
			if (IS_VDIN_SRC(vframe->source_type)
				&& IS_I_SRC(vframe->type)) {
				vframe->width = vframe->compWidth;
				vframe->height = vframe->compHeight*2;
			} else {
				vframe->width = vframe->compWidth;
				vframe->height = vframe->compHeight;
			}
		}

		di_print("DI: get %dth vf[0x%p] from frontend %u ms.\n",
			di_pre_stru.in_seq, vframe,
jiffies_to_msecs(jiffies_64 - vframe->ready_jiffies64));
		vframe->prog_proc_config = (prog_proc_config&0x20) >> 5;

		if (vframe->width > 10000 || vframe->height > 10000 ||
		    hold_video || di_pre_stru.bad_frame_throw_count > 0) {
			if (vframe->width > 10000 || vframe->height > 10000)
				di_pre_stru.bad_frame_throw_count = 10;
			di_pre_stru.bad_frame_throw_count--;

			vf_put(vframe, VFM_NAME);
			vf_notify_provider(
				VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL);
			return 0;
		}

		/*
		 * for afbc used by vpp and di, when di use it,
		 * vpp need release afbc, waitting vpp release
		 */
		if (di_pre_stru.wait_afbc) {
			/*check time out and afbc release status*/
			rls_timeout =
				jiffies_to_msecs(jiffies_64 -
					di_pre_stru.afbc_rls_time);
			afbc_busy = di_request_afbc(true);
			if (afbc_busy && (rls_timeout < 80)) {
				vf_put(vframe, VFM_NAME);
				vf_notify_provider(
				VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL);
				pr_info("di: drop vframe (%d) t:%d\n",
					afbc_busy, rls_timeout);
				return 0;
			} else if (!afbc_busy) {
				/*afbc_busy = di_request_afbc(false);*/
				di_pre_stru.wait_afbc = false;
				pr_info("di: afbc hw free\n");
			} else {
				di_pre_stru.wait_afbc = false;
				pr_info("di: afbc wait timeout\n");
			}
		}

		bit10_pack_patch =  (is_meson_gxtvbb_cpu() ||
							is_meson_gxl_cpu() ||
							is_meson_gxm_cpu());
		width_roundup = bit10_pack_patch ? 16 : width_roundup;
		if (di_force_bit_mode == 10)
			force_width = roundup(vframe->width, width_roundup);
		else
			force_width = 0;
		di_pre_stru.source_trans_fmt = vframe->trans_fmt;
		di_pre_stru.left_right = di_pre_stru.left_right ? 0 : 1;
		di_pre_stru.invert_flag =
			(vframe->type &  TB_DETECT_MASK) ? true : false;
		vframe->type &= ~TB_DETECT_MASK;

		if ((((invert_top_bot & 0x2) != 0) ||
				di_pre_stru.invert_flag) &&
		    (!is_progressive(vframe))) {
			if (
				(vframe->type & VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_TOP) {
				vframe->type &= (~VIDTYPE_TYPEMASK);
				vframe->type |= VIDTYPE_INTERLACE_BOTTOM;
			} else {
				vframe->type &= (~VIDTYPE_TYPEMASK);
				vframe->type |= VIDTYPE_INTERLACE_TOP;
			}
		}
		/*di_pre_stru.width_bk = vframe->width;*/
		if (force_width)
			vframe->width = force_width;
		if (force_height)
			vframe->height = force_height;

		/* backup frame motion info */
		vframe->combing_cur_lev = cur_lev;

		di_print("%s: vf_get => 0x%p\n", __func__, vframe);

		di_buf = get_di_buf_head(QUEUE_IN_FREE);

		if (check_di_buf(di_buf, 10))
			return 0;

		if (di_log_flag & DI_LOG_VFRAME)
			dump_vframe(vframe);

#ifdef SUPPORT_MPEG_TO_VDIN
		if (
			(!is_from_vdin(vframe)) &&
			(vframe->sig_fmt == TVIN_SIG_FMT_NULL) &&
			mpeg2vdin_flag) {
			struct vdin_arg_s vdin_arg;
			struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops();

			vdin_arg.cmd = VDIN_CMD_GET_HISTGRAM;
			vdin_arg.private = (unsigned int)vframe;
			if (vdin_ops->tvin_vdin_func)
				vdin_ops->tvin_vdin_func(0, &vdin_arg);
		}
#endif
		memcpy(di_buf->vframe, vframe, sizeof(vframe_t));
		di_buf->width_bk = vframe->width;
		di_buf->vframe->private_data = di_buf;
		vframe_in[di_buf->index] = vframe;
		di_buf->seq = di_pre_stru.in_seq;
		di_pre_stru.in_seq++;
		queue_out(di_buf);
		change_type = is_source_change(vframe);
		/* source change, when i mix p,force p as i*/
		if (change_type == 1 || (change_type == 2 &&
					 di_pre_stru.cur_prog_flag == 1)) {

			di_pre_stru.field_count_for_cont = 0;

			if (di_pre_stru.di_mem_buf_dup_p) {
				/*avoid only 2 i field then p field*/
				if (
					(di_pre_stru.cur_prog_flag == 0) &&
					use_2_interlace_buff)
					di_pre_stru.di_mem_buf_dup_p->
					post_proc_flag = -1;
				di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0;
				di_pre_stru.di_mem_buf_dup_p = NULL;
			}
			if (di_pre_stru.di_chan2_buf_dup_p) {
				/*avoid only 1 i field then p field*/
				if (
					(di_pre_stru.cur_prog_flag == 0) &&
					use_2_interlace_buff)
					di_pre_stru.di_chan2_buf_dup_p->
					post_proc_flag = -1;
				di_pre_stru.di_chan2_buf_dup_p->pre_ref_count =
					0;
				di_pre_stru.di_chan2_buf_dup_p = NULL;
			}
			#if 0
			/* channel change will occur between atv and dtv,
			 * that need mirror
			 */
			if (!IS_ERR_OR_NULL(di_post_stru.keep_buf)) {
				if (di_post_stru.keep_buf->vframe
					->source_type !=
					di_buf->vframe->source_type) {
					recycle_keep_buffer();
					pr_info("%s: source type changed recycle buffer!!!\n",
						__func__);
				}
			}
			#endif
			pr_info(
			"%s:%ums %dth source change: 0x%x/%d/%d/%d=>0x%x/%d/%d/%d\n",
				__func__,
				jiffies_to_msecs(jiffies_64),
				di_pre_stru.in_seq,
				di_pre_stru.cur_inp_type,
				di_pre_stru.cur_width,
				di_pre_stru.cur_height,
				di_pre_stru.cur_source_type,
				di_buf->vframe->type,
				di_buf->vframe->width,
				di_buf->vframe->height,
				di_buf->vframe->source_type);

			if (IS_COMP_MODE(di_buf->vframe->type) &&
				!is_bypass(vframe)) {
				if (IS_VDIN_SRC(di_buf->vframe->source_type) &&
					IS_I_SRC(di_buf->vframe->type)) {
					di_pre_stru.cur_width =
						di_buf->vframe->compWidth;
					di_pre_stru.cur_height =
						di_buf->vframe->compHeight*2;
				} else {
					di_pre_stru.cur_width =
						di_buf->vframe->compWidth;
					di_pre_stru.cur_height =
						di_buf->vframe->compHeight;
				}
			} else {
				di_pre_stru.cur_width = di_buf->vframe->width;
				di_pre_stru.cur_height = di_buf->vframe->height;
			}

			di_pre_stru.cur_prog_flag =
				is_progressive(di_buf->vframe);
			if (di_pre_stru.cur_prog_flag) {
				if ((use_2_interlace_buff) &&
				    !(prog_proc_config & 0x10))
					di_pre_stru.prog_proc_type = 2;
				else
					di_pre_stru.prog_proc_type
						= prog_proc_config & 0x10;
			} else
				di_pre_stru.prog_proc_type = 0;
			di_pre_stru.cur_inp_type = di_buf->vframe->type;
			di_pre_stru.cur_source_type =
				di_buf->vframe->source_type;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN
			di_pre_stru.cur_sig_fmt = di_buf->vframe->sig_fmt;
#endif
			di_pre_stru.orientation = di_buf->vframe->video_angle;
			di_pre_stru.source_change_flag = 1;
			di_pre_stru.input_size_change_flag = true;
#ifdef SUPPORT_MPEG_TO_VDIN
			if ((!is_from_vdin(vframe)) &&
			    (vframe->sig_fmt == TVIN_SIG_FMT_NULL) &&
			    (mpeg2vdin_en)) {
				struct vdin_arg_s vdin_arg;
				struct vdin_v4l2_ops_s *vdin_ops =
					get_vdin_v4l2_ops();
				vdin_arg.cmd = VDIN_CMD_MPEGIN_START;
				vdin_arg.h_active = di_pre_stru.cur_width;
				vdin_arg.v_active = di_pre_stru.cur_height;
				if (vdin_ops->tvin_vdin_func)
					vdin_ops->tvin_vdin_func(0, &vdin_arg);
				mpeg2vdin_flag = 1;
			}
#endif

			/*
			 * for afbc used by vpp and di, when di use it,
			 * vpp need release afbc, waitting vpp release
			 */
			if (is_meson_tl1_cpu() || is_meson_sm1_cpu()) {
				/*compress mode and format changed*/
				if (!is_bypass(di_buf->vframe) &&
					di_pre_stru.source_change_flag &&
					IS_COMP_MODE(di_pre_stru.cur_inp_type)
					&& !afbc_is_free()
					&& !di_pre_stru.wait_afbc) {
					afbc_busy = di_request_afbc(true);
					vf_put(vframe, VFM_NAME);
					vf_notify_provider(VFM_NAME,
					VFRAME_EVENT_RECEIVER_PUT, NULL);
					recycle_vframe_type_pre(di_buf);
					di_pre_stru.afbc_rls_time = jiffies_64;
					di_pre_stru.wait_afbc = true;
					pr_info("di req afbc:%d\n", afbc_busy);
					return 0;
				}
			}
			/*di_pre_stru.field_count_for_cont = 0;*/
		} else if (di_pre_stru.cur_prog_flag == 0) {
			/* check if top/bot interleaved */
			if (change_type == 2)
				/* source is i interleaves p fields */
				di_pre_stru.force_interlace = true;
			if ((di_pre_stru.cur_inp_type &
			VIDTYPE_TYPEMASK) == (di_buf->vframe->type &
			VIDTYPE_TYPEMASK)) {
				if ((di_buf->vframe->type &
				VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_TOP)
					same_field_top_count++;
				else
					same_field_bot_count++;
			}
			di_pre_stru.cur_inp_type = di_buf->vframe->type;
		} else {
			di_pre_stru.cur_inp_type = di_buf->vframe->type;
		}

		if (is_bypass(di_buf->vframe)) {
			/* bypass progressive */
			di_buf->seq = di_pre_stru.pre_ready_seq++;
			di_buf->post_ref_count = 0;
			cur_lev = 0;
			if (di_pre_stru.source_change_flag) {
				di_buf->new_format_flag = 1;
				di_pre_stru.source_change_flag = 0;
			} else {
				di_buf->new_format_flag = 0;
			}

			if (bypass_state == 0) {
				if (di_pre_stru.di_mem_buf_dup_p) {
					di_pre_stru.di_mem_buf_dup_p->
					pre_ref_count = 0;
					di_pre_stru.di_mem_buf_dup_p = NULL;
				}
				if (di_pre_stru.di_chan2_buf_dup_p) {
					di_pre_stru.di_chan2_buf_dup_p->
					pre_ref_count = 0;
					di_pre_stru.di_chan2_buf_dup_p = NULL;
				}

				if (di_pre_stru.di_wr_buf) {
					di_pre_stru.di_wr_buf->pre_ref_count =
						0;
					di_pre_stru.di_wr_buf->post_ref_count =
						0;
					recycle_vframe_type_pre(
						di_pre_stru.di_wr_buf);
#ifdef DI_BUFFER_DEBUG
					di_print(
						"%s: %s[%d] => recycle_list\n",
						__func__,
						vframe_type_name[di_pre_stru.
							di_wr_buf->type],
						di_pre_stru.di_wr_buf->index);
#endif
					di_pre_stru.di_wr_buf = NULL;
				}

				di_buf->new_format_flag = 1;
				bypass_state = 1;
#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s:bypass_state = 1, is_bypass() %d\n",
					__func__, is_bypass(NULL));
				di_print(
					"trick_mode %d bypass_all %d\n",
					trick_mode, bypass_all);
#endif
			}

			top_bot_config(di_buf);
			queue_in(di_buf, QUEUE_PRE_READY);
			/*if previous isn't bypass post_wr_buf not recycled */
			if (di_pre_stru.di_post_wr_buf && di_pre_rdma_enable) {
				queue_in(
					di_pre_stru.di_post_inp_buf,
					QUEUE_RECYCLE);
				di_pre_stru.di_post_inp_buf = NULL;
			}

			if (
				(bypass_pre & 0x2) &&
				!di_pre_stru.cur_prog_flag)
				di_buf->post_proc_flag = -2;
			else
				di_buf->post_proc_flag = 0;
#ifdef DI_BUFFER_DEBUG
			di_print(
				"%s: %s[%d] => pre_ready_list\n", __func__,
				vframe_type_name[di_buf->type], di_buf->index);
#endif
			return 0;
		} else if (is_progressive(di_buf->vframe)) {
			if (
				is_handle_prog_frame_as_interlace(vframe) &&
				(is_progressive(vframe))) {
				struct di_buf_s *di_buf_tmp = NULL;

				vframe_in[di_buf->index] = NULL;
				di_buf->vframe->type &=
					(~VIDTYPE_TYPEMASK);
				di_buf->vframe->type |=
					VIDTYPE_INTERLACE_TOP;
				di_buf->post_proc_flag = 0;

				di_buf_tmp =
					get_di_buf_head(QUEUE_IN_FREE);
				if (check_di_buf(di_buf_tmp, 10)) {
					recycle_vframe_type_pre(di_buf);
					pr_err("DI:no free in_buffer for progressive skip.\n");
					return 0;
				}
				queue_out(di_buf_tmp);
				di_buf_tmp->vframe->private_data
					= di_buf_tmp;
				di_buf_tmp->seq = di_pre_stru.in_seq;
				di_pre_stru.in_seq++;
				vframe_in[di_buf_tmp->index] = vframe;
				memcpy(
					di_buf_tmp->vframe, vframe,
					sizeof(vframe_t));
				di_pre_stru.di_inp_buf_next
					= di_buf_tmp;
				di_buf_tmp->vframe->type
					&= (~VIDTYPE_TYPEMASK);
				di_buf_tmp->vframe->type
					|= VIDTYPE_INTERLACE_BOTTOM;
				di_buf_tmp->post_proc_flag = 0;

				di_pre_stru.di_inp_buf = di_buf;
#ifdef DI_BUFFER_DEBUG
				di_print(
			"%s: %s[%d] => di_inp_buf; %s[%d] => di_inp_buf_next\n",
					__func__,
					vframe_type_name[di_buf->type],
					di_buf->index,
					vframe_type_name[di_buf_tmp->type],
					di_buf_tmp->index);
#endif
				if (di_pre_stru.di_mem_buf_dup_p == NULL) {
					di_pre_stru.di_mem_buf_dup_p = di_buf;
#ifdef DI_BUFFER_DEBUG
					di_print(
				"%s: set di_mem_buf_dup_p to be di_inp_buf\n",
						__func__);
#endif
				}
			} else {
				di_buf->post_proc_flag = 0;
				if ((prog_proc_config & 0x40) ||
				    di_pre_stru.force_interlace)
					di_buf->post_proc_flag = 1;
				di_pre_stru.di_inp_buf = di_buf;
#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s: %s[%d] => di_inp_buf\n",
					__func__,
					vframe_type_name[di_buf->type],
					di_buf->index);
#endif
				if (
					di_pre_stru.di_mem_buf_dup_p == NULL) {
					/* use n */
					di_pre_stru.di_mem_buf_dup_p
						= di_buf;
#ifdef DI_BUFFER_DEBUG
					di_print(
				"%s: set di_mem_buf_dup_p to be di_inp_buf\n",
						__func__);
#endif
				}
			}
		} else {
		/*********************************/
			if ((di_buf->vframe->width >= 1920) &&
				(di_buf->vframe->height >= 1080))
				flg_1080i = true;
			else if ((di_buf->vframe->width == 720) &&
				 (di_buf->vframe->height == 480))
				flg_480i = true;

		/*********************************/
			if (di_pre_stru.di_chan2_buf_dup_p == NULL) {
				di_pre_stru.field_count_for_cont = 0;
				di_pre_stru.source_change_flag = 1;
				di_pre_stru.input_size_change_flag = true;
				//di_buf->new_format_flag = 1;
				di_pr_info("set cnt 0;");
				/* ignore contp2rd and contprd */
			}
			di_buf->post_proc_flag = 1;
			di_pre_stru.di_inp_buf = di_buf;
			di_print("%s: %s[%d] => di_inp_buf\n", __func__,
				vframe_type_name[di_buf->type], di_buf->index);

			if (di_pre_stru.di_mem_buf_dup_p == NULL) {/* use n */
				di_pre_stru.di_mem_buf_dup_p = di_buf;
#ifdef DI_BUFFER_DEBUG
				di_print(
				"%s: set di_mem_buf_dup_p to be di_inp_buf\n",
					__func__);
#endif
			}
		}
	}

	/* di_wr_buf */
	if (di_pre_stru.prog_proc_type == 2) {
		di_linked_buf_idx = peek_free_linked_buf();
		if (di_linked_buf_idx != -1)
			di_buf = get_free_linked_buf(di_linked_buf_idx);
		else
			di_buf = NULL;
		if (di_buf == NULL) {
			/* recycle_vframe_type_pre(di_pre_stru.di_inp_buf);
			 *save for next process
			 */
			recycle_keep_buffer();
			di_pre_stru.di_inp_buf_next = di_pre_stru.di_inp_buf;
			return 0;
		}
		di_buf->post_proc_flag = 0;
		di_buf->di_wr_linked_buf->pre_ref_count = 0;
		di_buf->di_wr_linked_buf->post_ref_count = 0;
		di_buf->canvas_config_flag = 1;
	} else {
		di_buf = get_di_buf_head(QUEUE_LOCAL_FREE);
		if (check_di_buf(di_buf, 11)) {
			/* recycle_keep_buffer();
			 *  pr_dbg("%s:recycle keep buffer\n", __func__);
			 */
			recycle_vframe_type_pre(di_pre_stru.di_inp_buf);
			return 0;
		}
		queue_out(di_buf);
		if (di_pre_stru.prog_proc_type & 0x10)
			di_buf->canvas_config_flag = 1;
		else
			di_buf->canvas_config_flag = 2;
		di_buf->di_wr_linked_buf = NULL;
	}

	di_pre_stru.di_wr_buf = di_buf;
	di_pre_stru.di_wr_buf->pre_ref_count = 1;

#ifdef DI_BUFFER_DEBUG
	di_print("%s: %s[%d] => di_wr_buf\n", __func__,
		vframe_type_name[di_buf->type], di_buf->index);
	if (di_buf->di_wr_linked_buf)
		di_print("%s: linked %s[%d] => di_wr_buf\n", __func__,
			vframe_type_name[di_buf->di_wr_linked_buf->type],
			di_buf->di_wr_linked_buf->index);
#endif

	/*for support compress from dec, not from vdin*/
	if (IS_COMP_MODE(di_pre_stru.cur_inp_type) &&
		(!(di_pre_stru.cur_inp_type & VIDTYPE_VIU_422))) {
		/*compress type and not from vdin*/
		di_pre_stru.di_inp_buf->vframe->width =
			di_pre_stru.di_inp_buf->vframe->compWidth;
		di_pre_stru.di_inp_buf->vframe->height =
			di_pre_stru.di_inp_buf->vframe->compHeight;
	}

	memcpy(di_buf->vframe,
		di_pre_stru.di_inp_buf->vframe, sizeof(vframe_t));
	di_buf->vframe->private_data = di_buf;
	di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx;
	di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx;
	/* set vframe bit info */
	di_buf->vframe->bitdepth &= ~(BITDEPTH_YMASK);
	di_buf->vframe->bitdepth &= ~(FULL_PACK_422_MODE);
	di_buf->width_bk = di_buf->vframe->width;
	if (de_devp->pps_enable && pps_position) {
		if (pps_dstw != di_buf->vframe->width) {
			di_buf->vframe->width = pps_dstw;
			/*di_pre_stru.width_bk = pps_dstw;*/
			di_buf->width_bk = pps_dstw;
		}
		if (pps_dsth != di_buf->vframe->height)
			di_buf->vframe->height = pps_dsth;
	} else if (de_devp->h_sc_down_en) {
		if (pre_hsc_down_width != di_buf->vframe->width) {
			pr_info("di: hscd %d to %d\n", di_buf->vframe->width,
				pre_hsc_down_width);
			di_buf->vframe->width = pre_hsc_down_width;
			/*di_pre_stru.width_bk = pre_hsc_down_width;*/
			di_buf->width_bk = pre_hsc_down_width;
		}
	}
	if (di_force_bit_mode == 10) {
		di_buf->vframe->bitdepth |= (BITDEPTH_Y10);
		if (full_422_pack)
			di_buf->vframe->bitdepth |= (FULL_PACK_422_MODE);
	} else
		di_buf->vframe->bitdepth |= (BITDEPTH_Y8);

	if (di_pre_stru.prog_proc_type) {
		di_buf->vframe->type = VIDTYPE_PROGRESSIVE |
				       VIDTYPE_VIU_422 |
				       VIDTYPE_VIU_SINGLE_PLANE |
				       VIDTYPE_VIU_FIELD;
		if (di_pre_stru.cur_inp_type & VIDTYPE_PRE_INTERLACE)
			di_buf->vframe->type |= VIDTYPE_PRE_INTERLACE;
	} else {
		if (
			((di_pre_stru.di_inp_buf->vframe->type &
			  VIDTYPE_TYPEMASK) ==
			 VIDTYPE_INTERLACE_TOP))
			di_buf->vframe->type = VIDTYPE_INTERLACE_TOP |
					       VIDTYPE_VIU_422 |
					       VIDTYPE_VIU_SINGLE_PLANE |
					       VIDTYPE_VIU_FIELD;
		else
			di_buf->vframe->type = VIDTYPE_INTERLACE_BOTTOM |
					       VIDTYPE_VIU_422 |
					       VIDTYPE_VIU_SINGLE_PLANE |
					       VIDTYPE_VIU_FIELD;
		/*add for vpp skip line ref*/
		if (bypass_state == 0)
			di_buf->vframe->type |= VIDTYPE_PRE_INTERLACE;
	}

	if (is_afbc_mode) {
		di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC;
		/*pr_info("vf type:0x%x\n", di_buf->vframe->type);*/
	}

	if (is_bypass_post()) {
		if (bypass_post_state == 0)
			di_pre_stru.source_change_flag = 1;

		bypass_post_state = 1;
	} else {
		if (bypass_post_state)
			di_pre_stru.source_change_flag = 1;

		bypass_post_state = 0;
	}

	if (di_pre_stru.di_inp_buf->post_proc_flag == 0) {
		di_pre_stru.madi_enable = 0;
		di_pre_stru.mcdi_enable = 0;
		di_buf->post_proc_flag = 0;
		di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false);
	} else if (bypass_post_state) {
		di_pre_stru.madi_enable = 0;
		di_pre_stru.mcdi_enable = 0;
		di_buf->post_proc_flag = 0;
		di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false);
	} else {
		di_pre_stru.madi_enable = (pre_enable_mask&1);
		di_pre_stru.mcdi_enable = ((pre_enable_mask>>1)&1);
		di_buf->post_proc_flag = 1;
		di_patch_post_update_mc_sw(DI_MC_SW_OTHER, mcpre_en);//en
	}

	if ((di_pre_stru.di_mem_buf_dup_p == di_pre_stru.di_wr_buf) ||
	    (di_pre_stru.di_chan2_buf_dup_p == di_pre_stru.di_wr_buf)) {
		pr_dbg("+++++++++++++++++++++++\n");
		if (recovery_flag == 0)
			recovery_log_reason = 12;

		recovery_flag++;
		return 0;
	}
	if (is_meson_tl1_cpu()	&&
	    di_pre_stru.comb_mode &&
	    flg_1080i) {
		di_pre_stru.combing_fix_en = false;
		fix_tl1_1080i_patch_sel(di_pre_stru.comb_mode);
	} else {
		di_pre_stru.combing_fix_en = combing_fix_en;
	}

	if (di_pre_stru.combing_fix_en) {
		if (flg_1080i)
			com_patch_pre_sw_set(1);
		else if (flg_480i)
			com_patch_pre_sw_set(2);
		else
			com_patch_pre_sw_set(0);
	}
	return 1;
}

static int check_recycle_buf(void)
{
	struct di_buf_s *di_buf = NULL;/* , *ptmp; */
	int itmp;
	int ret = 0;
	bool blk_flg = 0;

#ifdef DI_KEEP_HIS
	if (di_blocking)
		return ret;
#endif
	queue_for_each_entry(di_buf, ptmp, QUEUE_RECYCLE, list) {
		if (di_blocking)
			blk_flg = 1;

		if ((di_buf->pre_ref_count == 0) &&
		    (di_buf->post_ref_count == 0)) {
			if (di_buf->type == VFRAME_TYPE_IN) {
				queue_out(di_buf);
				if (vframe_in[di_buf->index]) {
					vf_put(
						vframe_in[di_buf->index],
						VFM_NAME);
					vf_notify_provider(VFM_NAME,
						VFRAME_EVENT_RECEIVER_PUT,
						NULL);
					di_print(
						"%s: vf_put(%d) 0x%p, %u ms\n",
						__func__,
						di_pre_stru.recycle_seq,
						vframe_in[di_buf->index],
						jiffies_to_msecs(jiffies_64 -
				vframe_in[di_buf->index]->ready_jiffies64));
					vframe_in[di_buf->index] = NULL;
				}
				di_buf->invert_top_bot_flag = 0;
				queue_in(di_buf, QUEUE_IN_FREE);
				di_pre_stru.recycle_seq++;
				ret |= 1;
			} else {
				queue_out(di_buf);
				di_buf->invert_top_bot_flag = 0;
				queue_in(di_buf, QUEUE_LOCAL_FREE);
				if (di_buf->di_wr_linked_buf) {
					queue_in(
						di_buf->di_wr_linked_buf,
						QUEUE_LOCAL_FREE);
#ifdef DI_BUFFER_DEBUG
					di_print(
					"%s: linked %s[%d]=>recycle_list\n",
						__func__,
						vframe_type_name[
						di_buf->di_wr_linked_buf->type],
						di_buf->di_wr_linked_buf->index
					);
#endif
					di_buf->di_wr_linked_buf = NULL;
				}
				ret |= 2;
			}
#ifdef DI_BUFFER_DEBUG
			di_print("%s: recycle %s[%d]\n", __func__,
				vframe_type_name[di_buf->type], di_buf->index);
#endif
		}
	}

	if (blk_flg)
		pr_info("di:blk:recycle\n");

	return ret;
}

#ifdef DET3D
static void set3d_view(enum tvin_trans_fmt trans_fmt, struct vframe_s *vf)
{
	struct vframe_view_s *left_eye, *right_eye;

	left_eye = &vf->left_eye;
	right_eye = &vf->right_eye;

	switch (trans_fmt) {
	case TVIN_TFMT_3D_DET_LR:
	case TVIN_TFMT_3D_LRH_OLOR:
		left_eye->start_x = 0;
		left_eye->start_y = 0;
		left_eye->width = vf->width >> 1;
		left_eye->height = vf->height;
		right_eye->start_x = vf->width >> 1;
		right_eye->start_y = 0;
		right_eye->width = vf->width >> 1;
		right_eye->height = vf->height;
		break;
	case TVIN_TFMT_3D_DET_TB:
	case TVIN_TFMT_3D_TB:
		left_eye->start_x = 0;
		left_eye->start_y = 0;
		left_eye->width = vf->width;
		left_eye->height = vf->height >> 1;
		right_eye->start_x = 0;
		right_eye->start_y = vf->height >> 1;
		right_eye->width = vf->width;
		right_eye->height = vf->height >> 1;
		break;
	case TVIN_TFMT_3D_DET_INTERLACE:
		left_eye->start_x = 0;
		left_eye->start_y = 0;
		left_eye->width = vf->width;
		left_eye->height = vf->height >> 1;
		right_eye->start_x = 0;
		right_eye->start_y = 0;
		right_eye->width = vf->width;
		right_eye->height = vf->height >> 1;
		break;
	case TVIN_TFMT_3D_DET_CHESSBOARD:
/***
 * LRLRLR	  LRLRLR
 * LRLRLR  or RLRLRL
 * LRLRLR	  LRLRLR
 * LRLRLR	  RLRLRL
 */
		break;
	default: /* 2D */
		left_eye->start_x = 0;
		left_eye->start_y = 0;
		left_eye->width = 0;
		left_eye->height = 0;
		right_eye->start_x = 0;
		right_eye->start_y = 0;
		right_eye->width = 0;
		right_eye->height = 0;
		break;
	}
}

/*
 * static int get_3d_info(struct vframe_s *vf)
 * {
 * int ret = 0;
 *
 * vf->trans_fmt = det3d_fmt_detect();
 * pr_dbg("[det3d..]new 3d fmt: %d\n", vf->trans_fmt);
 *
 * vdin_set_view(vf->trans_fmt, vf);
 *
 * return ret;
 * }
 */
static unsigned int det3d_frame_cnt = 50;
module_param_named(det3d_frame_cnt, det3d_frame_cnt, uint, 0644);
static void det3d_irq(void)
{
	unsigned int data32 = 0, likely_val = 0;
	unsigned long frame_sum = 0;

	if (!det3d_en)
		return;

	data32 = det3d_fmt_detect();
	switch (data32) {
	case TVIN_TFMT_3D_DET_LR:
	case TVIN_TFMT_3D_LRH_OLOR:
		di_pre_stru.det_lr++;
		break;
	case TVIN_TFMT_3D_DET_TB:
	case TVIN_TFMT_3D_TB:
		di_pre_stru.det_tp++;
		break;
	case TVIN_TFMT_3D_DET_INTERLACE:
		di_pre_stru.det_la++;
		break;
	default:
		di_pre_stru.det_null++;
		break;
	}

	if (det3d_mode != data32) {
		det3d_mode = data32;
		di_print("[det3d..]new 3d fmt: %d\n", det3d_mode);
	}
	if (frame_count > 20) {
		frame_sum = di_pre_stru.det_lr + di_pre_stru.det_tp
					+ di_pre_stru.det_la
					+ di_pre_stru.det_null;
		if ((frame_count%det3d_frame_cnt) || (frame_sum > UINT_MAX))
			return;
		likely_val = max3(di_pre_stru.det_lr,
						di_pre_stru.det_tp,
						di_pre_stru.det_la);
		if (di_pre_stru.det_null >= likely_val)
			det3d_mode = 0;
		else if (likely_val == di_pre_stru.det_lr)
			det3d_mode = TVIN_TFMT_3D_LRH_OLOR;
		else if (likely_val == di_pre_stru.det_tp)
			det3d_mode = TVIN_TFMT_3D_TB;
		else
			det3d_mode = TVIN_TFMT_3D_DET_INTERLACE;
		di_pre_stru.det3d_trans_fmt = det3d_mode;
	} else {
		di_pre_stru.det3d_trans_fmt = 0;
	}
}
#endif

static bool calc_mcinfo_en = 1;
module_param(calc_mcinfo_en, bool, 0664);
MODULE_PARM_DESC(calc_mcinfo_en, "/n get mcinfo for post /n");

static unsigned int colcfd_thr = 128;
module_param(colcfd_thr, uint, 0664);
MODULE_PARM_DESC(colcfd_thr, "/n threshold for cfd/n");

unsigned int ro_mcdi_col_cfd[26];
static void get_mcinfo_from_reg_in_irq(void)
{
	unsigned int i = 0, ncolcrefsum = 0, blkcount = 0, *reg = NULL;

/*get info for current field process by post*/
	di_pre_stru.di_wr_buf->curr_field_mcinfo.highvertfrqflg =
		(Rd(MCDI_RO_HIGH_VERT_FRQ_FLG) & 0x1);
/* post:MCDI_MC_REL_GAIN_OFFST_0 */
	di_pre_stru.di_wr_buf->curr_field_mcinfo.motionparadoxflg =
		(Rd(MCDI_RO_MOTION_PARADOX_FLG) & 0x1);
/* post:MCDI_MC_REL_GAIN_OFFST_0 */
	reg = di_pre_stru.di_wr_buf->curr_field_mcinfo.regs;
	for (i = 0; i < 26; i++) {
		ro_mcdi_col_cfd[i] = Rd(0x2fb0 + i);
		di_pre_stru.di_wr_buf->curr_field_mcinfo.regs[i] = 0;
		if (!calc_mcinfo_en)
			*(reg + i) = ro_mcdi_col_cfd[i];
	}
	if (calc_mcinfo_en) {
		blkcount = (di_pre_stru.cur_width + 4) / 5;
		for (i = 0; i < blkcount; i++) {
			ncolcrefsum +=
				((ro_mcdi_col_cfd[i / 32] >> (i % 32)) & 0x1);
			if (
				((ncolcrefsum + (blkcount >> 1)) << 8) /
				blkcount > colcfd_thr)
				for (i = 0; i < blkcount; i++)
					*(reg + i / 32) += (1 << (i % 32));
		}
	}
}

static unsigned int bit_reverse(unsigned int val)
{
	unsigned int i = 0, res = 0;

	for (i = 0; i < 16; i++) {
		res |= (((val&(1<<i))>>i)<<(31-i));
		res |= (((val&(1<<(31-i)))<<i)>>(31-i));
	}
	return res;
}

static void set_post_mcinfo(struct mcinfo_pre_s *curr_field_mcinfo)
{
	unsigned int i = 0, value = 0;

	DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_REL_GAIN_OFFST_0,
		curr_field_mcinfo->highvertfrqflg, 24, 1);
	DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_REL_GAIN_OFFST_0,
		curr_field_mcinfo->motionparadoxflg, 25, 1);
	for (i = 0; i < 26; i++) {
		if (overturn)
			value = bit_reverse(curr_field_mcinfo->regs[i]);
		else
			value = curr_field_mcinfo->regs[i];
		DI_VSYNC_WR_MPEG_REG(0x2f78 + i, value);
	}
}
static unsigned char intr_mode;
static irqreturn_t de_irq(int irq, void *dev_instance)
{
#ifndef CHECK_DI_DONE
	unsigned int data32 = Rd(DI_INTR_CTRL);
	unsigned int mask32 = (data32 >> 16) & 0x3ff;
	unsigned int flag = 0;

	data32 &= 0x3fffffff;
	if ((data32 & 1) == 0 && di_dbg_mask&8)
		pr_info("irq[%d]pre|post=0 write done.\n", irq);
	if (di_pre_stru.pre_de_busy) {
		/* only one interrupt mask should be enable */
		if ((data32 & 2) && !(mask32 & 2)) {
			di_print("== MTNWR ==\n");
			flag = 1;
		} else if ((data32 & 1) && !(mask32 & 1)) {
			di_print("== NRWR ==\n");
			flag = 1;
		} else {
			di_print("== DI IRQ 0x%x ==\n", data32);
			flag = 0;
		}
	}

#endif
	ddbg_mod_save(eDI_DBG_MOD_PRE_IRQB, 0, di_pre_stru.in_seq);
#ifdef DET3D
	if (det3d_en) {
		if ((data32 & 0x100) && !(mask32 & 0x100) && flag) {
			DI_Wr(DI_INTR_CTRL, data32);
			det3d_irq();
		} else {
			goto end;
		}
	} else {
		DI_Wr(DI_INTR_CTRL, data32);
	}
#else
	if (flag)
		DI_Wr(DI_INTR_CTRL, (data32&0xfffffffb)|(intr_mode<<30));
#endif

	if (di_pre_stru.pre_de_busy == 0) {
		pr_err("di:err:%s: enter %x\n", __func__, Rd(DI_INTR_CTRL));
		return IRQ_HANDLED;
	}

	if (flag) {
		di_pre_stru.irq_time[0] =
			(cur_to_msecs() - di_pre_stru.irq_time[0]);
		trace_di_pre("PRE-IRQ-0",
			di_pre_stru.field_count_for_cont,
			di_pre_stru.irq_time[0]);
		/*add from valsi wang.feng*/
		di_arb_sw(false);
		di_arb_sw(true);

		if (mcpre_en) {
			get_mcinfo_from_reg_in_irq();
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
			if ((is_meson_gxlx_cpu() &&
				di_pre_stru.field_count_for_cont >= 4) ||
				is_meson_txhd_cpu())
				mc_pre_mv_irq();
#endif
			calc_lmv_base_mcinfo((di_pre_stru.cur_height>>1),
				di_pre_stru.di_wr_buf->mcinfo_vaddr);
		}
		di_nr_opl()->nr_process_in_irq();
		if ((data32&0x200) && de_devp->nrds_enable)
			nr_ds_irq();
		/* disable mif */
		enable_di_pre_mif(false, mcpre_en);
		di_pre_stru.pre_de_process_done = 1;
		di_pre_stru.pre_de_busy = 0;

		if (init_flag)
			/* pr_dbg("%s:up di sema\n", __func__); */
			trigger_pre_di_process(TRIGGER_PRE_BY_DE_IRQ);
	}
	ddbg_mod_save(eDI_DBG_MOD_PRE_IRQE, 0, di_pre_stru.in_seq);
	return IRQ_HANDLED;
}

static irqreturn_t post_irq(int irq, void *dev_instance)
{
	unsigned int data32 = Rd(DI_INTR_CTRL);

	data32 &= 0x3fffffff;
	if ((data32 & 4) == 0) {
	if (di_dbg_mask&8)
		pr_info("irq[%d]post write undone.\n", irq);
	return IRQ_HANDLED;
	}
	ddbg_mod_save(eDI_DBG_MOD_POST_IRQB, 0, 0);
	if ((post_wr_en && post_wr_support) && (data32&0x4)) {
		di_post_stru.de_post_process_done = 1;
		di_post_stru.post_de_busy = 0;
		di_post_stru.irq_time =
			(cur_to_msecs() - di_post_stru.irq_time);
		trace_di_post("POST-IRQ-1",
				di_post_stru.post_wr_cnt,
				di_post_stru.irq_time);
		DI_Wr(DI_INTR_CTRL, (data32&0xffff0004)|(intr_mode<<30));
		/* disable wr back avoid pps read in g12a */
		DI_Wr_reg_bits(DI_POST_CTRL, 0, 7, 1);
	}
	ddbg_mod_save(eDI_DBG_MOD_POST_IRQE, 0, 0);

	if (init_flag)
		trigger_pre_di_process(TRIGGER_PRE_BY_DE_IRQ);

	return IRQ_HANDLED;
}
/*
 * di post process
 */
static void inc_post_ref_count(struct di_buf_s *di_buf)
{
/* int post_blend_mode; */

	if (IS_ERR_OR_NULL(di_buf)) {
		pr_dbg("%s: Error\n", __func__);
		if (recovery_flag == 0)
			recovery_log_reason = 13;

		recovery_flag++;
		return;
	}

	if (di_buf->di_buf_dup_p[1])
		di_buf->di_buf_dup_p[1]->post_ref_count++;

	if (di_buf->di_buf_dup_p[0])
		di_buf->di_buf_dup_p[0]->post_ref_count++;

	if (di_buf->di_buf_dup_p[2])
		di_buf->di_buf_dup_p[2]->post_ref_count++;
}

static void dec_post_ref_count(struct di_buf_s *di_buf)
{
	if (IS_ERR_OR_NULL(di_buf)) {
		pr_dbg("%s: Error\n", __func__);
		if (recovery_flag == 0)
			recovery_log_reason = 14;

		recovery_flag++;
		return;
	}
	if (di_buf->pd_config.global_mode == PULL_DOWN_BUF1)
		return;
	if (di_buf->di_buf_dup_p[1])
		di_buf->di_buf_dup_p[1]->post_ref_count--;

	if (di_buf->di_buf_dup_p[0] &&
	    di_buf->di_buf_dup_p[0]->post_proc_flag != -2)
		di_buf->di_buf_dup_p[0]->post_ref_count--;

	if (di_buf->di_buf_dup_p[2])
		di_buf->di_buf_dup_p[2]->post_ref_count--;
}

static void vscale_skip_disable_post(struct di_buf_s *di_buf, vframe_t *disp_vf)
{
	struct di_buf_s *di_buf_i = NULL;
	int canvas_height = di_buf->di_buf[0]->canvas_height;

	if (di_vscale_skip_enable & 0x2) {/* drop the bottom field */
		if ((di_buf->di_buf_dup_p[0]) && (di_buf->di_buf_dup_p[1]))
			di_buf_i =
				(di_buf->di_buf_dup_p[1]->vframe->type &
				 VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_TOP ? di_buf->di_buf_dup_p[1]
				: di_buf->di_buf_dup_p[0];
		else
			di_buf_i = di_buf->di_buf[0];
	} else {
		if ((di_buf->di_buf[0]->post_proc_flag > 0)
		    && (di_buf->di_buf_dup_p[1]))
			di_buf_i = di_buf->di_buf_dup_p[1];
		else
			di_buf_i = di_buf->di_buf[0];
	}
	disp_vf->type = di_buf_i->vframe->type;
	/* pr_dbg("%s (%x %x) (%x %x)\n", __func__,
	 * disp_vf, disp_vf->type, di_buf_i->vframe,
	 * di_buf_i->vframe->type);
	 */
	disp_vf->width = di_buf_i->vframe->width;
	disp_vf->height = di_buf_i->vframe->height;
	disp_vf->duration = di_buf_i->vframe->duration;
	disp_vf->pts = di_buf_i->vframe->pts;
	disp_vf->flag = di_buf_i->vframe->flag;
	disp_vf->canvas0Addr = di_post_idx[di_post_stru.canvas_id][0];
	disp_vf->canvas1Addr = di_post_idx[di_post_stru.canvas_id][0];
	canvas_config(
		di_post_idx[di_post_stru.canvas_id][0],
		di_buf_i->nr_adr, di_buf_i->canvas_width[NR_CANVAS],
		canvas_height, 0, 0);
	disable_post_deinterlace_2();
	di_post_stru.vscale_skip_flag = true;
}
static void process_vscale_skip(struct di_buf_s *di_buf, vframe_t *disp_vf)
{
	int ret = 0, vpp_3d_mode = 0;

	if ((di_buf->di_buf[0] != NULL) && (di_vscale_skip_enable & 0x5) &&
	    (di_buf->process_fun_index != PROCESS_FUN_NULL)) {
		ret = get_current_vscale_skip_count(disp_vf);
		di_vscale_skip_count = (ret & 0xff);
		vpp_3d_mode = ((ret >> 8) & 0xff);
		if (((di_vscale_skip_count > 0) &&
		     (di_vscale_skip_enable & 0x5))
		    || (di_vscale_skip_enable >> 16)
		    ) {
			if ((di_vscale_skip_enable & 0x8) || vpp_3d_mode) {
				vscale_skip_disable_post(di_buf, disp_vf);
			} else {
				if (di_buf->di_buf_dup_p[1] &&
					di_buf->pd_config.global_mode !=
						PULL_DOWN_BUF1)
					di_buf->pd_config.global_mode =
						PULL_DOWN_EI;
			}
		}
	}
}
static int do_post_wr_fun(void *arg, vframe_t *disp_vf)
{
	di_post_stru.toggle_flag = true;
	return 1;
}
static int de_post_disable_fun(void *arg, vframe_t *disp_vf)
{
	struct di_buf_s *di_buf = (struct di_buf_s *)arg;

	di_post_stru.vscale_skip_flag = false;
	di_post_stru.toggle_flag = true;

	process_vscale_skip(di_buf, disp_vf);
/* for atv static image flickering */
	if (di_buf->process_fun_index == PROCESS_FUN_NULL)
		disable_post_deinterlace_2();

	return 1;
/* called for new_format_flag, make
 * video set video_property_changed
 */
}

void di_api_post_disable(void)
{
	if (dil_get_diff_ver_flag())
		return;

	di_print("%s:\n", __func__);
	disable_post_deinterlace_2();
}
EXPORT_SYMBOL(di_api_post_disable);

static int do_nothing_fun(void *arg, vframe_t *disp_vf)
{
	struct di_buf_s *di_buf = (struct di_buf_s *)arg;

	di_post_stru.vscale_skip_flag = false;
	di_post_stru.toggle_flag = true;

	process_vscale_skip(di_buf, disp_vf);

	if (di_buf->process_fun_index == PROCESS_FUN_NULL) {
		if (Rd(DI_IF1_GEN_REG) & 0x1 || Rd(DI_POST_CTRL) & 0xf)
			disable_post_deinterlace_2();
	/*if(di_buf->pulldown_mode == PULL_DOWN_EI && Rd(DI_IF1_GEN_REG)&0x1)
	 * DI_VSYNC_WR_MPEG_REG(DI_IF1_GEN_REG, 0x3 << 30);
	 */
	}
	return 0;
}

static int do_pre_only_fun(void *arg, vframe_t *disp_vf)
{
	di_post_stru.vscale_skip_flag = false;
	di_post_stru.toggle_flag = true;

#ifdef DI_USE_FIXED_CANVAS_IDX
	if (arg) {
		struct di_buf_s *di_buf = (struct di_buf_s *)arg;
		vframe_t *vf = di_buf->vframe;
		int width, canvas_height;

		if ((vf == NULL) || (di_buf->di_buf[0] == NULL)) {
			di_print("error:%s,NULL point!!\n", __func__);
			return 0;
		}
		width = di_buf->di_buf[0]->canvas_width[NR_CANVAS];
		/* linked two interlace buffer should double height*/
		if (di_buf->di_buf[0]->di_wr_linked_buf)
			canvas_height =
			(di_buf->di_buf[0]->canvas_height << 1);
		else
			canvas_height =
			di_buf->di_buf[0]->canvas_height;
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
		if (is_vsync_rdma_enable()) {
			di_post_stru.canvas_id = di_post_stru.next_canvas_id;
		} else {
			di_post_stru.canvas_id = 0;
			di_post_stru.next_canvas_id = 1;
			if (post_wr_en && post_wr_support)
				di_post_stru.canvas_id =
					di_post_stru.next_canvas_id;
		}
#endif

		canvas_config(
			di_post_idx[di_post_stru.canvas_id][0],
			di_buf->di_buf[0]->nr_adr,
			di_buf->di_buf[0]->canvas_width[NR_CANVAS],
			canvas_height, 0, 0);

		vf->canvas0Addr =
			di_post_idx[di_post_stru.canvas_id][0];
		vf->canvas1Addr =
			di_post_idx[di_post_stru.canvas_id][0];
#ifdef DET3D
		if (di_pre_stru.vframe_interleave_flag && di_buf->di_buf[1]) {
			canvas_config(
				di_post_idx[di_post_stru.canvas_id][1],
				di_buf->di_buf[1]->nr_adr,
				di_buf->di_buf[1]->canvas_width[NR_CANVAS],
				canvas_height, 0, 0);
			vf->canvas1Addr =
				di_post_idx[di_post_stru.canvas_id][1];
			vf->duration <<= 1;
		}
#endif
		di_post_stru.next_canvas_id = di_post_stru.canvas_id ? 0 : 1;

		if (di_buf->process_fun_index == PROCESS_FUN_NULL) {
			if (Rd(DI_IF1_GEN_REG) & 0x1 ||
				Rd(DI_POST_CTRL) & 0x10f)
				disable_post_deinterlace_2();
		}

	}
#endif

	return 0;
}

static void get_vscale_skip_count(unsigned int par)
{
	di_vscale_skip_count_real = (par >> 24) & 0xff;
}

#define get_vpp_reg_update_flag(par) ((par >> 16) & 0x1)

static unsigned int pldn_dly = 1;

static unsigned int post_blend;
module_param(post_blend, uint, 0664);
MODULE_PARM_DESC(post_blend, "/n show blend mode/n");
static unsigned int post_ei;
module_param(post_ei, uint, 0664);
MODULE_PARM_DESC(post_ei, "/n show blend mode/n");

static unsigned int post_cnt;
module_param(post_cnt, uint, 0664);
MODULE_PARM_DESC(post_cnt, "/n show blend mode/n");
static bool post_refresh;
module_param_named(post_refresh, post_refresh, bool, 0644);
unsigned int di_last_display;

static int
de_post_process(void *arg, unsigned int zoom_start_x_lines,
		unsigned int zoom_end_x_lines, unsigned int zoom_start_y_lines,
		unsigned int zoom_end_y_lines, vframe_t *disp_vf)
{
	struct di_buf_s *di_buf = (struct di_buf_s *)arg;
	struct di_buf_s *di_pldn_buf = NULL;
	unsigned int di_width, di_height, di_start_x, di_end_x, mv_offset;
	unsigned int di_start_y, di_end_y, hold_line = post_hold_line;
	unsigned int post_blend_en = 0, post_blend_mode = 0,
		     blend_mtn_en = 0, ei_en = 0, post_field_num = 0;
	int di_vpp_en, di_ddr_en;
	unsigned char mc_pre_flag = 0;
	bool invert_mv = false;
	static int post_index = -1;
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	unsigned char tmp_idx = 0;
#endif
	post_cnt++;
	if (di_post_stru.vscale_skip_flag)
		return 0;

	get_vscale_skip_count(zoom_start_x_lines);

	if (IS_ERR_OR_NULL(di_buf))
		return 0;
	else if (IS_ERR_OR_NULL(di_buf->di_buf_dup_p[0]))
		return 0;
	di_pldn_buf = di_buf->di_buf_dup_p[pldn_dly];

	if (is_in_queue(di_buf, QUEUE_POST_FREE) &&
			post_index != di_buf->index) {
		post_index = di_buf->index;
		pr_info("%s post_buf[%d] is in post free list.\n",
				__func__, di_buf->index);
		return 0;
	}

	if (di_post_stru.toggle_flag && di_buf->di_buf_dup_p[1])
		top_bot_config(di_buf->di_buf_dup_p[1]);

	di_post_stru.toggle_flag = false;

	di_post_stru.cur_disp_index = di_buf->index;

	di_last_display = di_buf->index;/*tmp for keep buf*/
	if (get_vpp_reg_update_flag(zoom_start_x_lines) || post_refresh)
		di_post_stru.update_post_reg_flag = 1;

	zoom_start_x_lines = zoom_start_x_lines & 0xffff;
	zoom_end_x_lines = zoom_end_x_lines & 0xffff;
	zoom_start_y_lines = zoom_start_y_lines & 0xffff;
	zoom_end_y_lines = zoom_end_y_lines & 0xffff;

	if (init_flag == 0 && IS_ERR_OR_NULL(di_post_stru.keep_buf))
		return 0;

	ddbg_mod_save(eDI_DBG_MOD_POST_SETB, 0, frame_count);/*dbg*/
	di_start_x = zoom_start_x_lines;
	di_end_x = zoom_end_x_lines;
	di_width = di_end_x - di_start_x + 1;
	di_start_y = zoom_start_y_lines;
	di_end_y = zoom_end_y_lines;
	di_height = di_end_y - di_start_y + 1;
	di_height = di_height / (di_vscale_skip_count_real + 1);

	/* make sure the height is even number */
	if (di_height%2) {
		/*for skip mode,post only half line-1*/
		if (((di_height > 150) && (di_height < 1080)) &&
			di_vscale_skip_count_real)
			di_height = di_height - 3;
		else
			di_height++;
	}

	if (Rd(DI_POST_SIZE) != ((di_width - 1) | ((di_height - 1) << 16)) ||
	    di_post_stru.buf_type != di_buf->di_buf_dup_p[0]->type ||
	    (di_post_stru.di_buf0_mif.luma_x_start0 != di_start_x) ||
	    (di_post_stru.di_buf0_mif.luma_y_start0 != di_start_y / 2)) {
		ddbg_mod_save(eDI_DBG_MOD_POST_RESIZE, 0,
			frame_count);/*dbg*/
		di_post_stru.buf_type = di_buf->di_buf_dup_p[0]->type;

		initial_di_post_2(di_width, di_height,
			hold_line,
			(post_wr_en && post_wr_support));

		if ((di_buf->di_buf_dup_p[0]->vframe == NULL) ||
			(di_buf->vframe == NULL))
			return 0;
		/* bit mode config */
		if (di_buf->vframe->bitdepth & BITDEPTH_Y10) {
			if (di_buf->vframe->type & VIDTYPE_VIU_444) {
				di_post_stru.di_buf0_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2;
				di_post_stru.di_buf1_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2;
				di_post_stru.di_buf2_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2;
				di_post_stru.di_diwr_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2;

			} else {
				di_post_stru.di_buf0_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
				di_post_stru.di_buf1_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
				di_post_stru.di_buf2_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
				di_post_stru.di_diwr_mif.bit_mode =
			(di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1;
			}
		} else {
			di_post_stru.di_buf0_mif.bit_mode = 0;
			di_post_stru.di_buf1_mif.bit_mode = 0;
			di_post_stru.di_buf2_mif.bit_mode = 0;
			di_post_stru.di_diwr_mif.bit_mode = 0;
		}
		if (di_buf->vframe->type & VIDTYPE_VIU_444) {
			di_post_stru.di_buf0_mif.video_mode = 1;
			di_post_stru.di_buf1_mif.video_mode = 1;
			di_post_stru.di_buf2_mif.video_mode = 1;
		} else {
			di_post_stru.di_buf0_mif.video_mode = 0;
			di_post_stru.di_buf1_mif.video_mode = 0;
			di_post_stru.di_buf2_mif.video_mode = 0;
		}
		if (di_post_stru.buf_type == VFRAME_TYPE_IN &&
		    !(di_buf->di_buf_dup_p[0]->vframe->type &
		      VIDTYPE_VIU_FIELD)) {
			if (di_buf->vframe->type & VIDTYPE_VIU_NV21) {
				di_post_stru.di_buf0_mif.set_separate_en = 1;
				di_post_stru.di_buf1_mif.set_separate_en = 1;
				di_post_stru.di_buf2_mif.set_separate_en = 1;
			} else {
				di_post_stru.di_buf0_mif.set_separate_en = 0;
				di_post_stru.di_buf1_mif.set_separate_en = 0;
				di_post_stru.di_buf2_mif.set_separate_en = 0;
			}
			di_post_stru.di_buf0_mif.luma_y_start0 = di_start_y;
			di_post_stru.di_buf0_mif.luma_y_end0 = di_end_y;
		} else { /* from vdin or local vframe process by di pre */
			di_post_stru.di_buf0_mif.set_separate_en = 0;
			di_post_stru.di_buf0_mif.luma_y_start0 =
				di_start_y >> 1;
			di_post_stru.di_buf0_mif.luma_y_end0 = di_end_y >> 1;
			di_post_stru.di_buf1_mif.set_separate_en = 0;
			di_post_stru.di_buf1_mif.luma_y_start0 =
				di_start_y >> 1;
			di_post_stru.di_buf1_mif.luma_y_end0 = di_end_y >> 1;
			di_post_stru.di_buf2_mif.set_separate_en = 0;
			di_post_stru.di_buf2_mif.luma_y_end0 = di_end_y >> 1;
			di_post_stru.di_buf2_mif.luma_y_start0 =
				di_start_y >> 1;
		}
		di_post_stru.di_buf0_mif.luma_x_start0 = di_start_x;
		di_post_stru.di_buf0_mif.luma_x_end0 = di_end_x;
		di_post_stru.di_buf1_mif.luma_x_start0 = di_start_x;
		di_post_stru.di_buf1_mif.luma_x_end0 = di_end_x;
		di_post_stru.di_buf2_mif.luma_x_start0 = di_start_x;
		di_post_stru.di_buf2_mif.luma_x_end0 = di_end_x;

		if (post_wr_en && post_wr_support) {
			if (de_devp->pps_enable && pps_position == 0) {
				di_pps_config(0, di_width, di_height,
					pps_dstw, pps_dsth);
				di_post_stru.di_diwr_mif.start_x = 0;
				di_post_stru.di_diwr_mif.end_x   = pps_dstw - 1;
				di_post_stru.di_diwr_mif.start_y = 0;
				di_post_stru.di_diwr_mif.end_y   = pps_dsth - 1;
			} else {
				di_post_stru.di_diwr_mif.start_x = di_start_x;
				di_post_stru.di_diwr_mif.end_x   = di_end_x;
				di_post_stru.di_diwr_mif.start_y = di_start_y;
				di_post_stru.di_diwr_mif.end_y   = di_end_y;
			}
		}

		di_post_stru.di_mtnprd_mif.start_x = di_start_x;
		di_post_stru.di_mtnprd_mif.end_x = di_end_x;
		di_post_stru.di_mtnprd_mif.start_y = di_start_y >> 1;
		di_post_stru.di_mtnprd_mif.end_y = di_end_y >> 1;
		if (mcpre_en) {
			di_post_stru.di_mcvecrd_mif.start_x = di_start_x / 5;
			mv_offset = (di_start_x % 5) ? (5 - di_start_x % 5) : 0;
			di_post_stru.di_mcvecrd_mif.vecrd_offset =
				overturn ? (di_end_x + 1) % 5 : mv_offset;
			di_post_stru.di_mcvecrd_mif.start_y =
				(di_start_y >> 1);
			di_post_stru.di_mcvecrd_mif.size_x =
				(di_end_x + 1 + 4) / 5 - 1 - di_start_x / 5;
			di_post_stru.di_mcvecrd_mif.end_y =
				(di_end_y >> 1);
		}
		di_post_stru.update_post_reg_flag = 1;
		/* if height decrease, mtn will not enough */
		if ((di_buf->pd_config.global_mode
			!= PULL_DOWN_BUF1) &&
			!di_buf->di_buf_dup_p[2] &&
			!post_wr_en)
			di_buf->pd_config.global_mode = PULL_DOWN_EI;
	}

#ifdef DI_USE_FIXED_CANVAS_IDX
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
	if (is_vsync_rdma_enable()) {
		di_post_stru.canvas_id = di_post_stru.next_canvas_id;
	} else {
		di_post_stru.canvas_id = 0;
		di_post_stru.next_canvas_id = 1;
		if (post_wr_en && post_wr_support)
			di_post_stru.canvas_id =
				di_post_stru.next_canvas_id;
	}
#endif
	post_blend = di_buf->pd_config.global_mode;
	switch (post_blend) {
	case PULL_DOWN_BLEND_0:
	case PULL_DOWN_NORMAL:
		config_canvas_idx(
			di_buf->di_buf_dup_p[1],
			di_post_idx[di_post_stru.canvas_id][0], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[2], -1,
			di_post_idx[di_post_stru.canvas_id][2]);
		config_canvas_idx(
			di_buf->di_buf_dup_p[0],
			di_post_idx[di_post_stru.canvas_id][1], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[2],
			di_post_idx[di_post_stru.canvas_id][3], -1);
		if (mcpre_en)
			config_mcvec_canvas_idx(
				di_buf->di_buf_dup_p[2],
				di_post_idx[di_post_stru.canvas_id][4]);
		break;
	case PULL_DOWN_BLEND_2:
	case PULL_DOWN_NORMAL_2:
		config_canvas_idx(
			di_buf->di_buf_dup_p[0],
			di_post_idx[di_post_stru.canvas_id][3], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[1],
			di_post_idx[di_post_stru.canvas_id][0], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[2], -1,
			di_post_idx[di_post_stru.canvas_id][2]);
		config_canvas_idx(
			di_buf->di_buf_dup_p[2],
			di_post_idx[di_post_stru.canvas_id][1], -1);
		if (mcpre_en)
			config_mcvec_canvas_idx(
				di_buf->di_buf_dup_p[2],
				di_post_idx[di_post_stru.canvas_id][4]);
		break;
	case PULL_DOWN_MTN:
		config_canvas_idx(
			di_buf->di_buf_dup_p[1],
			di_post_idx[di_post_stru.canvas_id][0], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[2], -1,
			di_post_idx[di_post_stru.canvas_id][2]);
		config_canvas_idx(
			di_buf->di_buf_dup_p[0],
			di_post_idx[di_post_stru.canvas_id][1], -1);
		break;
	case PULL_DOWN_BUF1:/* wave with buf1 */
		config_canvas_idx(
			di_buf->di_buf_dup_p[1],
			di_post_idx[di_post_stru.canvas_id][0], -1);
		config_canvas_idx(
			di_buf->di_buf_dup_p[1], -1,
			di_post_idx[di_post_stru.canvas_id][2]);
		config_canvas_idx(
			di_buf->di_buf_dup_p[0],
			di_post_idx[di_post_stru.canvas_id][1], -1);
		break;
	case PULL_DOWN_EI:
		if (di_buf->di_buf_dup_p[1])
			config_canvas_idx(
				di_buf->di_buf_dup_p[1],
				di_post_idx[di_post_stru.canvas_id][0], -1);
		break;
	default:
		break;
	}
	di_post_stru.next_canvas_id = di_post_stru.canvas_id ? 0 : 1;
#endif
	if (di_buf->di_buf_dup_p[1] == NULL)
		return 0;
	if ((di_buf->di_buf_dup_p[1]->vframe == NULL) ||
		di_buf->di_buf_dup_p[0]->vframe == NULL)
		return 0;

#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	if (is_meson_txl_cpu() && overturn && di_buf->di_buf_dup_p[2]) {
		/*sync from kernel 3.14 txl*/
		if (post_blend == PULL_DOWN_BLEND_2)
			post_blend = PULL_DOWN_BLEND_0;
		else if (post_blend == PULL_DOWN_BLEND_0)
			post_blend = PULL_DOWN_BLEND_2;
	}
#endif

	switch (post_blend) {
	case PULL_DOWN_BLEND_0:
	case PULL_DOWN_NORMAL:
		post_field_num =
		(di_buf->di_buf_dup_p[1]->vframe->type &
		 VIDTYPE_TYPEMASK)
		== VIDTYPE_INTERLACE_TOP ? 0 : 1;
		di_post_stru.di_buf0_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[1]->nr_canvas_idx;
		di_post_stru.di_buf1_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[0]->nr_canvas_idx;
		di_post_stru.di_buf2_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[2]->nr_canvas_idx;
		di_post_stru.di_mtnprd_mif.canvas_num =
			di_buf->di_buf_dup_p[2]->mtn_canvas_idx;
		//mc_pre_flag = is_meson_txl_cpu()?2:(overturn?0:1);
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
		if (is_meson_txl_cpu() && overturn) {
			/* swap if1&if2 mean negation of mv for normal di*/
			tmp_idx = di_post_stru.di_buf1_mif.canvas0_addr0;
			di_post_stru.di_buf1_mif.canvas0_addr0 =
				di_post_stru.di_buf2_mif.canvas0_addr0;
			di_post_stru.di_buf2_mif.canvas0_addr0 = tmp_idx;
		}
#endif
		mc_pre_flag = overturn?0:1;
		if (di_buf->pd_config.global_mode == PULL_DOWN_NORMAL) {
			post_blend_mode = 3;
			/*if pulldown, mcdi_mcpreflag is 1,*/
			/*it means use previous field for MC*/
			/*else not pulldown,mcdi_mcpreflag is 2*/
			/*it means use forward & previous field for MC*/
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD))
				mc_pre_flag = 2;
		} else {
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD))
				mc_pre_flag = 1;
			post_blend_mode = 1;
		}
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
		if (is_meson_txl_cpu() && overturn)
			mc_pre_flag = 1;
#endif
		if (mcpre_en) {
			di_post_stru.di_mcvecrd_mif.canvas_num =
				di_buf->di_buf_dup_p[2]->mcvec_canvas_idx;
		}
		blend_mtn_en = 1;
		post_ei = ei_en = 1;
		post_blend_en = 1;
		break;
	case PULL_DOWN_BLEND_2:
	case PULL_DOWN_NORMAL_2:
		post_field_num =
			(di_buf->di_buf_dup_p[1]->vframe->type &
			 VIDTYPE_TYPEMASK)
			== VIDTYPE_INTERLACE_TOP ? 0 : 1;
		di_post_stru.di_buf0_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[1]->nr_canvas_idx;
		di_post_stru.di_buf1_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[2]->nr_canvas_idx;
		di_post_stru.di_buf2_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[0]->nr_canvas_idx;
		di_post_stru.di_mtnprd_mif.canvas_num =
			di_buf->di_buf_dup_p[2]->mtn_canvas_idx;
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
		if (is_meson_txl_cpu() && overturn) {
			di_post_stru.di_buf1_mif.canvas0_addr0 =
			di_post_stru.di_buf2_mif.canvas0_addr0;
		}
#endif
		if (mcpre_en) {
			di_post_stru.di_mcvecrd_mif.canvas_num =
				di_buf->di_buf_dup_p[2]->mcvec_canvas_idx;
			mc_pre_flag = is_meson_txl_cpu()?0:(overturn?1:0);
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX))
				invert_mv = true;
			else if (!overturn)
				di_post_stru.di_buf2_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[2]->nr_canvas_idx;
		}
		if (di_buf->pd_config.global_mode == PULL_DOWN_NORMAL_2) {
			post_blend_mode = 3;
			/*if pulldown, mcdi_mcpreflag is 1,*/
			/*it means use previous field for MC*/
			/*else not pulldown,mcdi_mcpreflag is 2*/
			/*it means use forward & previous field for MC*/
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD))
				mc_pre_flag = 2;
		} else {
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD))
				mc_pre_flag = 1;
			post_blend_mode = 1;
		}
		blend_mtn_en = 1;
		post_ei = ei_en = 1;
		post_blend_en = 1;
		break;
	case PULL_DOWN_MTN:
		post_field_num =
			(di_buf->di_buf_dup_p[1]->vframe->type &
			 VIDTYPE_TYPEMASK)
			== VIDTYPE_INTERLACE_TOP ? 0 : 1;
		di_post_stru.di_buf0_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[1]->nr_canvas_idx;
		di_post_stru.di_buf1_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[0]->nr_canvas_idx;
		di_post_stru.di_mtnprd_mif.canvas_num =
			di_buf->di_buf_dup_p[2]->mtn_canvas_idx;
		post_blend_mode = 0;
		blend_mtn_en = 1;
		post_ei = ei_en = 1;
		post_blend_en = 1;
		break;
	case PULL_DOWN_BUF1:
		post_field_num =
			(di_buf->di_buf_dup_p[1]->vframe->type &
			 VIDTYPE_TYPEMASK)
			== VIDTYPE_INTERLACE_TOP ? 0 : 1;
		di_post_stru.di_buf0_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[1]->nr_canvas_idx;
		di_post_stru.di_mtnprd_mif.canvas_num =
			di_buf->di_buf_dup_p[1]->mtn_canvas_idx;
		di_post_stru.di_buf1_mif.canvas0_addr0 =
			di_buf->di_buf_dup_p[0]->nr_canvas_idx;
		post_blend_mode = 1;
		blend_mtn_en = 0;
		post_ei = ei_en = 0;
		post_blend_en = 0;
		break;
	case PULL_DOWN_EI:
		if (di_buf->di_buf_dup_p[1]) {
			di_post_stru.di_buf0_mif.canvas0_addr0 =
				di_buf->di_buf_dup_p[1]->nr_canvas_idx;
			post_field_num =
				(di_buf->di_buf_dup_p[1]->vframe->type &
				 VIDTYPE_TYPEMASK)
				== VIDTYPE_INTERLACE_TOP ? 0 : 1;
		} else {
			post_field_num =
				(di_buf->di_buf_dup_p[0]->vframe->type &
				 VIDTYPE_TYPEMASK)
				== VIDTYPE_INTERLACE_TOP ? 0 : 1;
			di_post_stru.di_buf0_mif.src_field_mode
				= post_field_num;
		}
		post_blend_mode = 2;
		blend_mtn_en = 0;
		post_ei = ei_en = 1;
		post_blend_en = 0;
		break;
	default:
		break;
	}

	if (post_wr_en && post_wr_support) {
		config_canvas_idx(di_buf,
			di_post_idx[di_post_stru.canvas_id][5], -1);
		di_post_stru.di_diwr_mif.canvas_num = di_buf->nr_canvas_idx;
		di_vpp_en = 0;
		di_ddr_en = 1;
	} else {
		di_vpp_en = 1;
		di_ddr_en = 0;
	}

	/* if post size < MIN_POST_WIDTH, force ei */
	if ((di_width < MIN_BLEND_WIDTH) &&
		(di_buf->pd_config.global_mode == PULL_DOWN_BLEND_0 ||
		di_buf->pd_config.global_mode == PULL_DOWN_BLEND_2 ||
		di_buf->pd_config.global_mode == PULL_DOWN_NORMAL
		)) {
		post_blend_mode = 1;
		blend_mtn_en = 0;
		post_ei = ei_en = 0;
		post_blend_en = 0;
	}

	if (mcpre_en)
		di_post_stru.di_mcvecrd_mif.blend_en = post_blend_en;
	invert_mv = overturn ? (!invert_mv) : invert_mv;
	if (di_post_stru.update_post_reg_flag) {
		enable_di_post_2(
			&di_post_stru.di_buf0_mif,
			&di_post_stru.di_buf1_mif,
			&di_post_stru.di_buf2_mif,
			&di_post_stru.di_diwr_mif,
			&di_post_stru.di_mtnprd_mif,
			ei_en,                  /* ei enable */
			post_blend_en,          /* blend enable */
			blend_mtn_en,           /* blend mtn enable */
			post_blend_mode,        /* blend mode. */
			di_vpp_en,		/* di_vpp_en. */
			di_ddr_en,		/* di_ddr_en. */
			post_field_num,         /* 1 bottom generate top */
			hold_line,
			post_urgent,
			(invert_mv?1:0),
			di_vscale_skip_count_real
			);
		if (mcpre_en) {
			if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
				enable_mc_di_post_g12(
					&di_post_stru.di_mcvecrd_mif,
					post_urgent,
					overturn, (invert_mv?1:0));
			else
				enable_mc_di_post(
					&di_post_stru.di_mcvecrd_mif,
					post_urgent,
					overturn, (invert_mv?1:0));
		} else if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX))
			DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2);
	} else {
		di_post_switch_buffer(
			&di_post_stru.di_buf0_mif,
			&di_post_stru.di_buf1_mif,
			&di_post_stru.di_buf2_mif,
			&di_post_stru.di_diwr_mif,
			&di_post_stru.di_mtnprd_mif,
			&di_post_stru.di_mcvecrd_mif,
			ei_en,                  /* ei enable */
			post_blend_en,          /* blend enable */
			blend_mtn_en,           /* blend mtn enable */
			post_blend_mode,        /* blend mode. */
			di_vpp_en,	/* di_vpp_en. */
			di_ddr_en,	/* di_ddr_en. */
			post_field_num,         /* 1 bottom generate top */
			hold_line,
			post_urgent,
			(invert_mv?1:0),
			pulldown_enable,
			mcpre_en,
			di_vscale_skip_count_real
			);
	}

		if (is_meson_gxtvbb_cpu()   ||
			is_meson_txl_cpu()  ||
			is_meson_txlx_cpu() ||
			is_meson_gxlx_cpu() ||
			is_meson_txhd_cpu() ||
			is_meson_g12a_cpu() ||
			is_meson_g12b_cpu() ||
			is_meson_tl1_cpu() ||
			is_meson_tm2_cpu() ||
			is_meson_sm1_cpu()) {
		di_post_read_reverse_irq(overturn, mc_pre_flag,
			post_blend_en ? mcpre_en : false);
		/* disable mc for first 2 fieldes mv unreliable */
		if (di_buf->seq < 2)
			DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2);
	}
	if (mcpre_en) {
		if (di_buf->di_buf_dup_p[2])
			set_post_mcinfo(&di_buf->di_buf_dup_p[2]
				->curr_field_mcinfo);
	} else if (is_meson_gxlx_cpu()
		|| is_meson_txl_cpu()
		|| is_meson_txlx_cpu())
		DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2);

/* set pull down region (f(t-1) */

	if (di_pldn_buf && pulldown_enable &&
		!di_pre_stru.cur_prog_flag) {
		unsigned short offset = (di_start_y>>1);

		if (overturn)
			offset = ((di_buf->vframe->height - di_end_y)>>1);
		else
			offset = 0;
		pulldown_vof_win_vshift(&di_pldn_buf->pd_config, offset);
		pulldown_vof_win_config(&di_pldn_buf->pd_config);
	}

	if (di_post_stru.update_post_reg_flag > 0)
		di_post_stru.update_post_reg_flag--;
	ddbg_mod_save(eDI_DBG_MOD_POST_SETE, 0, frame_count);/*dbg*/
	return 0;
}

static void post_de_done_buf_config(void)
{
	ulong irq_flag2 = 0;
	struct di_buf_s *di_buf = NULL;

	if (di_post_stru.cur_post_buf == NULL)
		return;
	ddbg_mod_save(eDI_DBG_MOD_POST_DB, 0, frame_count);/*dbg*/

	di_lock_irqfiq_save(irq_flag2);
	queue_out(di_post_stru.cur_post_buf);
	di_buf = di_post_stru.cur_post_buf;
	if (de_devp->pps_enable && pps_position == 0) {
		di_buf->vframe->width = pps_dstw;
		di_buf->vframe->height = pps_dsth;
	}
	queue_in(di_post_stru.cur_post_buf, QUEUE_POST_READY);
	di_unlock_irqfiq_restore(irq_flag2);
	vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
	di_post_stru.cur_post_buf = NULL;
	ddbg_mod_save(eDI_DBG_MOD_POST_DE, 0, frame_count);/*dbg*/

}

static void di_post_process(void)
{
	struct di_buf_s *di_buf = NULL;
	vframe_t *vf_p = NULL;

	if (di_post_stru.post_de_busy)
		return;
	if (queue_empty(QUEUE_POST_DOING)) {
		di_post_stru.post_peek_underflow++;
		return;
	}

	di_buf = get_di_buf_head(QUEUE_POST_DOING);
	if (check_di_buf(di_buf, 20))
		return;
	vf_p = di_buf->vframe;
	if (di_post_stru.run_early_proc_fun_flag) {
		if (vf_p->early_process_fun)
			vf_p->early_process_fun = do_post_wr_fun;
	}
	if (di_buf->process_fun_index) {

		di_post_stru.post_wr_cnt++;
		de_post_process(
			di_buf, 0, vf_p->width-1,
			0, vf_p->height-1, vf_p);
		di_post_stru.post_de_busy = 1;
		di_post_stru.irq_time = cur_to_msecs();
	} else {
		di_post_stru.de_post_process_done = 1;
	}
	di_post_stru.cur_post_buf = di_buf;
}

static void recycle_vframe_type_post(struct di_buf_s *di_buf)
{
	int i;

	if (di_buf == NULL) {
		pr_dbg("%s:Error\n", __func__);
		if (recovery_flag == 0)
			recovery_log_reason = 15;

		recovery_flag++;
		return;
	}
	if (di_buf->process_fun_index == PROCESS_FUN_DI)
		dec_post_ref_count(di_buf);

	for (i = 0; i < 2; i++) {
		if (di_buf->di_buf[i])
			queue_in(di_buf->di_buf[i], QUEUE_RECYCLE);
	}
	queue_out(di_buf); /* remove it from display_list_head */
	di_buf->invert_top_bot_flag = 0;
	queue_in(di_buf, QUEUE_POST_FREE);
}

#ifdef DI_BUFFER_DEBUG
static void
recycle_vframe_type_post_print(struct di_buf_s *di_buf,
			       const char *func,
			       const int	line)
{
	int i;

	di_print("%s:%d ", func, line);
	for (i = 0; i < 2; i++) {
		if (di_buf->di_buf[i])
			di_print("%s[%d]<%d>=>recycle_list; ",
				vframe_type_name[di_buf->di_buf[i]->type],
				di_buf->di_buf[i]->index, i);
	}
	di_print("%s[%d] =>post_free_list\n",
		vframe_type_name[di_buf->type], di_buf->index);
}
#endif
static int debug_blend_mode = -1;
static unsigned int pldn_dly1 = 1;
static void set_pulldown_mode(struct di_buf_s *di_buf)
{
	struct di_buf_s *pre_buf_p = di_buf->di_buf_dup_p[pldn_dly1];

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXBB)) {
		if (pulldown_enable && !di_pre_stru.cur_prog_flag) {
			if (pre_buf_p) {
				di_buf->pd_config.global_mode =
					pre_buf_p->pd_config.global_mode;
			} else {
				pr_err("DI[%s]: index out of range.\n",
					__func__);
			}
		} else {
			di_buf->pd_config.global_mode
				= PULL_DOWN_NORMAL;
		}
	}
}

void drop_frame(int check_drop, int throw_flag, struct di_buf_s *di_buf)
{
	ulong irq_flag2 = 0;
	int i = 0, drop_flag = 0;

	di_lock_irqfiq_save(irq_flag2);
	if ((frame_count == 0) && check_drop)
		di_post_stru.start_pts = di_buf->vframe->pts;
	if ((check_drop && (frame_count < start_frame_drop_count))
	|| throw_flag) {
		drop_flag = 1;
	} else {
		if (check_drop && (frame_count == start_frame_drop_count)) {
			if ((di_post_stru.start_pts)
			&& (di_buf->vframe->pts == 0))
				di_buf->vframe->pts = di_post_stru.start_pts;
			di_post_stru.start_pts = 0;
		}
		for (i = 0; i < 3; i++) {
			if (di_buf->di_buf_dup_p[i]) {
				if (di_buf->di_buf_dup_p[i]->vframe->bitdepth !=
					di_buf->vframe->bitdepth) {
					pr_info("%s buf[%d] not match bit mode\n",
						__func__, i);
					drop_flag = 1;
					break;
				}
			}
		}
	}
	if (drop_flag) {
		queue_in(di_buf, QUEUE_TMP);
		recycle_vframe_type_post(di_buf);
#ifdef DI_BUFFER_DEBUG
		recycle_vframe_type_post_print(
				di_buf, __func__,
				__LINE__);
#endif
	} else {
		if (post_wr_en && post_wr_support)
			queue_in(di_buf, QUEUE_POST_DOING);
		else
			queue_in(di_buf, QUEUE_POST_READY);
		di_print("DI:%dth %s[%d] => post ready %u ms.\n",
		frame_count, vframe_type_name[di_buf->type], di_buf->index,
		jiffies_to_msecs(jiffies_64 - di_buf->vframe->ready_jiffies64));

	}
	di_unlock_irqfiq_restore(irq_flag2);
}

static int process_post_vframe(void)
{
/*
 * 1) get buf from post_free_list, config it according to buf
 * in pre_ready_list, send it to post_ready_list
 * (it will be send to post_free_list in di_vf_put())
 * 2) get buf from pre_ready_list, attach it to buf from post_free_list
 * (it will be send to recycle_list in di_vf_put() )
 */
	ulong irq_flag2 = 0;
	int i = 0;
	int ret = 0;
	int buffer_keep_count = 3;
	struct di_buf_s *di_buf = NULL;
	struct di_buf_s *ready_di_buf;
	struct di_buf_s *p = NULL;/* , *ptmp; */
	int itmp;
	int ready_count = list_count(QUEUE_PRE_READY);
	bool check_drop = false;
	bool di_afbc = false;

	if (queue_empty(QUEUE_POST_FREE))
		return 0;

	if (ready_count == 0)
		return 0;

	ready_di_buf = get_di_buf_head(QUEUE_PRE_READY);
	if ((ready_di_buf == NULL) || (ready_di_buf->vframe == NULL)) {

		pr_dbg("%s:Error1\n", __func__);

		if (recovery_flag == 0)
			recovery_log_reason = 16;

		recovery_flag++;
		return 0;
	}

	if (ready_di_buf->vframe->type & VIDTYPE_PRE_DI_AFBC) {
		di_afbc = true;
		/*pr_info("di afbc mode 0x%x\n", ready_di_buf->vframe->type);*/
	}

	if ((ready_di_buf->post_proc_flag) &&
	    (ready_count >= buffer_keep_count)) {
		i = 0;
		queue_for_each_entry(p, ptmp, QUEUE_PRE_READY, list) {
			/* if(p->post_proc_flag == 0){ */
			if (p->type == VFRAME_TYPE_IN) {
				ready_di_buf->post_proc_flag = -1;
				ready_di_buf->new_format_flag = 1;
			}
			i++;
			if (i > 2)
				break;
		}
	}
	if (ready_di_buf->post_proc_flag > 0) {
		if (ready_count >= buffer_keep_count) {
			di_lock_irqfiq_save(irq_flag2);
			di_buf = get_di_buf_head(QUEUE_POST_FREE);
			if (check_di_buf(di_buf, 17)) {
				di_unlock_irqfiq_restore(irq_flag2);
				return 0;
			}

			queue_out(di_buf);
			di_unlock_irqfiq_restore(irq_flag2);

			i = 0;
			queue_for_each_entry(
				p, ptmp, QUEUE_PRE_READY, list) {
				di_buf->di_buf_dup_p[i++] = p;
				if (i >= buffer_keep_count)
					break;
			}
			if (i < buffer_keep_count) {

				pr_dbg("%s:Error3\n", __func__);

				if (recovery_flag == 0)
					recovery_log_reason = 18;
				recovery_flag++;
				return 0;
			}

			memcpy(di_buf->vframe,
				di_buf->di_buf_dup_p[1]->vframe,
				sizeof(vframe_t));
			di_buf->vframe->private_data = di_buf;
			if (di_buf->di_buf_dup_p[1]->post_proc_flag == 3) {
				/* dummy, not for display */
				inc_post_ref_count(di_buf);
				di_buf->di_buf[0] = di_buf->di_buf_dup_p[0];
				di_buf->di_buf[1] = NULL;
				queue_out(di_buf->di_buf[0]);
				di_lock_irqfiq_save(irq_flag2);
				queue_in(di_buf, QUEUE_TMP);
				recycle_vframe_type_post(di_buf);

				di_unlock_irqfiq_restore(irq_flag2);
#ifdef DI_BUFFER_DEBUG
				di_print("%s <dummy>: ", __func__);
#endif
			} else {
				if (di_buf->di_buf_dup_p[1]->
				post_proc_flag == 2){
					di_buf->pd_config.global_mode
						= PULL_DOWN_BLEND_2;
					/* blend with di_buf->di_buf_dup_p[2] */
				} else {
					set_pulldown_mode(di_buf);
				}
				di_buf->vframe->type =
					VIDTYPE_PROGRESSIVE |
					VIDTYPE_VIU_422 |
					VIDTYPE_VIU_SINGLE_PLANE |
					VIDTYPE_VIU_FIELD |
					VIDTYPE_PRE_INTERLACE;
				if (di_afbc)
					di_buf->vframe->type |=
					VIDTYPE_PRE_DI_AFBC;
				di_buf->vframe->width =
					di_buf->di_buf_dup_p[1]->width_bk;
				if (
					di_buf->di_buf_dup_p[1]->
					new_format_flag) {
					/* if (di_buf->di_buf_dup_p[1]
					 * ->post_proc_flag == 2) {
					 */
					di_buf->vframe->
					early_process_fun
						= de_post_disable_fun;
				} else {
					di_buf->vframe->
					early_process_fun
						= do_nothing_fun;
				}

				if (di_buf->di_buf_dup_p[1]->type
				    == VFRAME_TYPE_IN) {
					/* next will be bypass */
					di_buf->vframe->type
						= VIDTYPE_PROGRESSIVE |
						  VIDTYPE_VIU_422 |
						  VIDTYPE_VIU_SINGLE_PLANE |
							VIDTYPE_VIU_FIELD |
							VIDTYPE_PRE_INTERLACE;
					if (di_afbc)
						di_buf->vframe->type |=
						VIDTYPE_PRE_DI_AFBC;

					di_buf->vframe->height >>= 1;
					di_buf->vframe->canvas0Addr =
						di_buf->di_buf_dup_p[0]
						->nr_canvas_idx; /* top */
					di_buf->vframe->canvas1Addr =
						di_buf->di_buf_dup_p[0]
						->nr_canvas_idx;
					di_buf->vframe->process_fun =
						NULL;
					di_buf->process_fun_index =
						PROCESS_FUN_NULL;
				} else {
					/*for debug*/
					if (debug_blend_mode != -1)
						di_buf->pd_config.global_mode
							= debug_blend_mode;

					di_buf->vframe->process_fun =
((post_wr_en && post_wr_support) ? NULL : de_post_process);
					di_buf->process_fun_index =
						PROCESS_FUN_DI;
					inc_post_ref_count(di_buf);
				}
				di_buf->di_buf[0]
					= di_buf->di_buf_dup_p[0];
				di_buf->di_buf[1] = NULL;
				queue_out(di_buf->di_buf[0]);

				drop_frame(true,
					(di_buf->di_buf_dup_p[0]->throw_flag) ||
					(di_buf->di_buf_dup_p[1]->throw_flag) ||
					(di_buf->di_buf_dup_p[2]->throw_flag),
					di_buf);

				frame_count++;
#ifdef DI_BUFFER_DEBUG
				di_print("%s <interlace>: ", __func__);
#endif
				if (!(post_wr_en && post_wr_support))
					vf_notify_receiver(VFM_NAME,
VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
			}
			ret = 1;
		}
	} else {
		if (is_progressive(ready_di_buf->vframe) ||
		    ready_di_buf->type == VFRAME_TYPE_IN ||
		    ready_di_buf->post_proc_flag < 0 ||
		    bypass_post_state
		    ){
			int vframe_process_count = 1;
#ifdef DET3D
			int dual_vframe_flag = 0;

			if ((di_pre_stru.vframe_interleave_flag &&
			     ready_di_buf->left_right) ||
			    (bypass_post & 0x100)) {
				dual_vframe_flag = 1;
				vframe_process_count = 2;
			}
#endif
			if (skip_top_bot &&
			    (!is_progressive(ready_di_buf->vframe)))
				vframe_process_count = 2;

			if (ready_count >= vframe_process_count) {
				struct di_buf_s *di_buf_i;

				di_lock_irqfiq_save(irq_flag2);
				di_buf = get_di_buf_head(QUEUE_POST_FREE);
				if (check_di_buf(di_buf, 19)) {
					di_unlock_irqfiq_restore(irq_flag2);
					return 0;
				}

				queue_out(di_buf);
				di_unlock_irqfiq_restore(irq_flag2);

				i = 0;
				queue_for_each_entry(
					p, ptmp, QUEUE_PRE_READY, list){
					di_buf->di_buf_dup_p[i++] = p;
					if (i >= vframe_process_count) {
						di_buf->di_buf_dup_p[i] = NULL;
						di_buf->di_buf_dup_p[i+1] =
							NULL;
						break;
					}
				}
				if (i < vframe_process_count) {
					pr_dbg("%s:Error6\n", __func__);
					if (recovery_flag == 0)
						recovery_log_reason = 22;

					recovery_flag++;
					return 0;
				}

				di_buf_i = di_buf->di_buf_dup_p[0];
				if (!is_progressive(ready_di_buf->vframe)
					&& ((skip_top_bot == 1)
					|| (skip_top_bot == 2))) {
					unsigned int frame_type =
						di_buf->di_buf_dup_p[1]->
						vframe->type & VIDTYPE_TYPEMASK;
					if (skip_top_bot == 1) {
						di_buf_i = (frame_type ==
						VIDTYPE_INTERLACE_TOP)
						? di_buf->di_buf_dup_p[1]
						: di_buf->di_buf_dup_p[0];
					} else if (skip_top_bot == 2) {
						di_buf_i = (frame_type ==
						VIDTYPE_INTERLACE_BOTTOM)
						? di_buf->di_buf_dup_p[1]
						: di_buf->di_buf_dup_p[0];
					}
				}

				memcpy(di_buf->vframe,
					di_buf_i->vframe,
					sizeof(vframe_t));
				di_buf->vframe->width = di_buf_i->width_bk;
				di_buf->vframe->private_data = di_buf;

				if (ready_di_buf->new_format_flag &&
				(ready_di_buf->type == VFRAME_TYPE_IN)) {
					pr_info("DI:%d disable post.\n",
						__LINE__);
					di_buf->vframe->early_process_fun
						= de_post_disable_fun;
				} else {
					if (ready_di_buf->type ==
						VFRAME_TYPE_IN)
						di_buf->vframe->
						early_process_fun
						 = do_nothing_fun;

					else
						di_buf->vframe->
						early_process_fun
						 = do_pre_only_fun;
				}
				if (ready_di_buf->post_proc_flag == -2) {
					di_buf->vframe->type
						|= VIDTYPE_VIU_FIELD;
					di_buf->vframe->type
						&= ~(VIDTYPE_TYPEMASK);
					if (di_afbc)
						di_buf->vframe->type |=
						VIDTYPE_PRE_DI_AFBC;

					di_buf->vframe->process_fun
= (post_wr_en && post_wr_support)?NULL:de_post_process;
					di_buf->process_fun_index
						= PROCESS_FUN_DI;
					di_buf->pd_config.global_mode
						= PULL_DOWN_EI;
				} else {
					di_buf->vframe->process_fun =
						NULL;
					di_buf->process_fun_index =
						PROCESS_FUN_NULL;
					di_buf->pd_config.global_mode =
						PULL_DOWN_NORMAL;
				}
				di_buf->di_buf[0] = ready_di_buf;
				di_buf->di_buf[1] = NULL;
				queue_out(ready_di_buf);

#ifdef DET3D
				if (dual_vframe_flag) {
					di_buf->di_buf[1] =
						di_buf->di_buf_dup_p[1];
					queue_out(di_buf->di_buf[1]);
				}
#endif
				drop_frame(check_drop,
					di_buf->di_buf[0]->throw_flag, di_buf);

				frame_count++;
#ifdef DI_BUFFER_DEBUG
				di_print(
					"%s <prog by frame>: ",
					__func__);
#endif
				ret = 1;
				vf_notify_receiver(VFM_NAME,
					VFRAME_EVENT_PROVIDER_VFRAME_READY,
					NULL);
			}
		} else if (ready_count >= 2) {
			/*for progressive input,type
			 * 1:separate tow fields,type
			 * 2:bypass post as frame
			 */
			unsigned char prog_tb_field_proc_type =
				(prog_proc_config >> 1) & 0x3;
			di_lock_irqfiq_save(irq_flag2);
			di_buf = get_di_buf_head(QUEUE_POST_FREE);

			if (check_di_buf(di_buf, 20)) {
				di_unlock_irqfiq_restore(irq_flag2);
				return 0;
			}

			queue_out(di_buf);
			di_unlock_irqfiq_restore(irq_flag2);

			i = 0;
			queue_for_each_entry(
				p, ptmp, QUEUE_PRE_READY, list) {
				di_buf->di_buf_dup_p[i++] = p;
				if (i >= 2) {
					di_buf->di_buf_dup_p[i] = NULL;
					break;
				}
			}
			if (i < 2) {
				pr_dbg("%s:Error6\n", __func__);

				if (recovery_flag == 0)
					recovery_log_reason = 21;

				recovery_flag++;
				return 0;
			}

			memcpy(di_buf->vframe,
				di_buf->di_buf_dup_p[0]->vframe,
				sizeof(vframe_t));
			di_buf->vframe->private_data = di_buf;

			/*separate one progressive frame
			 * as two interlace fields
			 */
			if (prog_tb_field_proc_type == 1) {
				/* do weave by di post */
				di_buf->vframe->type =
					VIDTYPE_PROGRESSIVE |
					VIDTYPE_VIU_422 |
					VIDTYPE_VIU_SINGLE_PLANE |
					VIDTYPE_VIU_FIELD |
					VIDTYPE_PRE_INTERLACE;
				if (di_afbc)
					di_buf->vframe->type |=
					VIDTYPE_PRE_DI_AFBC;

				if (
					di_buf->di_buf_dup_p[0]->
					new_format_flag)
					di_buf->vframe->
					early_process_fun =
						de_post_disable_fun;
				else
					di_buf->vframe->
					early_process_fun =
						do_nothing_fun;

				di_buf->pd_config.global_mode =
					PULL_DOWN_BUF1;
				di_buf->vframe->process_fun =
(post_wr_en && post_wr_support)?NULL:de_post_process;
				di_buf->process_fun_index =
					PROCESS_FUN_DI;
			} else if (prog_tb_field_proc_type == 0) {
				/* to do: need change for
				 * DI_USE_FIXED_CANVAS_IDX
				 */
				/* do weave by vpp */
				di_buf->vframe->type =
					VIDTYPE_PROGRESSIVE |
					VIDTYPE_VIU_422 |
					VIDTYPE_VIU_SINGLE_PLANE;
				if (di_afbc)
					di_buf->vframe->type |=
					VIDTYPE_PRE_DI_AFBC;

				if (
					(di_buf->di_buf_dup_p[0]->
					 new_format_flag) ||
					(Rd(DI_IF1_GEN_REG) & 1))
					di_buf->vframe->
					early_process_fun =
						de_post_disable_fun;
				else
					di_buf->vframe->
					early_process_fun =
						do_nothing_fun;
				di_buf->vframe->process_fun = NULL;
				di_buf->process_fun_index =
					PROCESS_FUN_NULL;
				di_buf->vframe->canvas0Addr =
					di_buf->di_buf_dup_p[0]->
					nr_canvas_idx;
				di_buf->vframe->canvas1Addr =
					di_buf->di_buf_dup_p[1]->
					nr_canvas_idx;
			} else {
				/* to do: need change for
				 * DI_USE_FIXED_CANVAS_IDX
				 */
				di_buf->vframe->type =
					VIDTYPE_PROGRESSIVE |
					VIDTYPE_VIU_422 |
					VIDTYPE_VIU_SINGLE_PLANE |
					VIDTYPE_VIU_FIELD |
					VIDTYPE_PRE_INTERLACE;
				if (di_afbc)
					di_buf->vframe->type |=
					VIDTYPE_PRE_DI_AFBC;

				di_buf->vframe->height >>= 1;
				di_buf->vframe->width =
					di_buf->di_buf_dup_p[0]->width_bk;
				if (
					(di_buf->di_buf_dup_p[0]->
					 new_format_flag) ||
					(Rd(DI_IF1_GEN_REG) & 1))
					di_buf->vframe->
					early_process_fun =
						de_post_disable_fun;
				else
					di_buf->vframe->
					early_process_fun =
						do_nothing_fun;
				if (prog_tb_field_proc_type == 2) {
					di_buf->vframe->canvas0Addr =
						di_buf->di_buf_dup_p[0]
						->nr_canvas_idx;
/* top */
					di_buf->vframe->canvas1Addr =
						di_buf->di_buf_dup_p[0]
						->nr_canvas_idx;
				} else {
					di_buf->vframe->canvas0Addr =
						di_buf->di_buf_dup_p[1]
						->nr_canvas_idx; /* top */
					di_buf->vframe->canvas1Addr =
						di_buf->di_buf_dup_p[1]
						->nr_canvas_idx;
				}
			}

			di_buf->di_buf[0] = di_buf->di_buf_dup_p[0];
			queue_out(di_buf->di_buf[0]);
			/*check if the field is error,then drop*/
			if (
				(di_buf->di_buf_dup_p[0]->vframe->type &
				 VIDTYPE_TYPEMASK) ==
				VIDTYPE_INTERLACE_BOTTOM) {
				di_buf->di_buf[1] =
					di_buf->di_buf_dup_p[1] = NULL;
				queue_in(di_buf, QUEUE_TMP);
				recycle_vframe_type_post(di_buf);
				pr_dbg("%s drop field %d.\n", __func__,
					di_buf->di_buf_dup_p[0]->seq);
			} else {
				di_buf->di_buf[1] =
					di_buf->di_buf_dup_p[1];
				queue_out(di_buf->di_buf[1]);

				drop_frame(check_start_drop_prog,
					(di_buf->di_buf_dup_p[0]->throw_flag) ||
					(di_buf->di_buf_dup_p[1]->throw_flag),
					di_buf);
			}
			frame_count++;
#ifdef DI_BUFFER_DEBUG
			di_print("%s <prog by field>: ", __func__);
#endif
			ret = 1;
			vf_notify_receiver(VFM_NAME,
				VFRAME_EVENT_PROVIDER_VFRAME_READY,
				NULL);
		}
	}

#ifdef DI_BUFFER_DEBUG
	if (di_buf) {
		di_print("%s[%d](",
			vframe_type_name[di_buf->type], di_buf->index);
		for (i = 0; i < 2; i++) {
			if (di_buf->di_buf[i])
				di_print("%s[%d],",
				 vframe_type_name[di_buf->di_buf[i]->type],
				 di_buf->di_buf[i]->index);
		}
		di_print(")(vframe type %x dur %d)",
			di_buf->vframe->type, di_buf->vframe->duration);
		if (di_buf->di_buf_dup_p[1] &&
		    (di_buf->di_buf_dup_p[1]->post_proc_flag == 3))
			di_print("=> recycle_list\n");
		else
			di_print("=> post_ready_list\n");
	}
#endif
	return ret;
}

void di_vpu_dev_register(struct di_dev_s *vdevp)
{
	vdevp->di_vpu_clk_gate_dev = vpu_dev_register(VPU_VPU_CLKB,
						      DEVICE_NAME);
	vdevp->di_vpu_pd_dec = vpu_dev_register(VPU_AFBC_DEC,
						      DEVICE_NAME);
	vdevp->di_vpu_pd_dec1 = vpu_dev_register(VPU_AFBC_DEC1,
						      DEVICE_NAME);
	vdevp->di_vpu_pd_vd1 = vpu_dev_register(VPU_VIU_VD1,
						      DEVICE_NAME);
	vdevp->di_vpu_pd_post = vpu_dev_register(VPU_DI_POST,
						      DEVICE_NAME);
}
/*
 * di task
 */
static void di_unreg_process(void)
{
	unsigned long start_jiffes = 0;
	if (reg_flag) {
		pr_dbg("%s unreg start %d.\n", __func__, reg_flag);
		start_jiffes = jiffies_64;
		vf_unreg_provider(&di_vf_prov);
		pr_dbg("%s vf unreg cost %u ms.\n", __func__,
			jiffies_to_msecs(jiffies_64 - start_jiffes));
		unreg_cnt++;
		if (unreg_cnt > 0x3fffffff)
			unreg_cnt = 0;
		pr_dbg("%s unreg stop %d.\n", __func__, reg_flag);
		di_pre_stru.unreg_req_flag_irq = 1;
		reg_flag = 0;
		trigger_pre_di_process(TRIGGER_PRE_BY_UNREG);
	} else {
		di_pre_stru.force_unreg_req_flag = 0;
		di_pre_stru.disable_req_flag = 0;
		recovery_flag = 0;
		di_pre_stru.unreg_req_flag = 0;
		trigger_pre_di_process(TRIGGER_PRE_BY_UNREG);
	}
}

static void di_unreg_process_irq(void)
{
	ulong irq_flag2 = 0;
	unsigned int mirror_disable = 0;
#if (defined ENABLE_SPIN_LOCK_ALWAYS)
	ulong flags = 0;
	spin_lock_irqsave(&plist_lock, flags);
#endif
	init_flag = 0;
	mirror_disable = get_blackout_policy();
	di_lock_irqfiq_save(irq_flag2);
	di_print("%s: di_uninit_buf\n", __func__);
	di_uninit_buf(mirror_disable);
#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
	if (di_pre_rdma_enable)
		rdma_clear(de_devp->rdma_handle);
#endif
	adpative_combing_exit();
	enable_di_pre_mif(false, mcpre_en);
	/*disable afbc module when afbc working in DI*/
	afbc_reg_unreg_flag = 0;
	#if 0
	if (IS_COMP_MODE(di_pre_stru.cur_inp_type) &&
		(!needbypass_flag && !isbypass_flag)) {
		pr_info("DI: disable afbc\n");
		afbc_reg_sw(false);
		afbc_input_sw(false);
	}
	#endif
	di_hw_uninit();
	if (is_meson_txlx_cpu() || is_meson_txhd_cpu()
		|| is_meson_g12a_cpu() || is_meson_g12b_cpu()
		|| is_meson_tl1_cpu() || is_meson_sm1_cpu() ||
		is_meson_tm2_cpu()) {
		di_pre_gate_control(false, mcpre_en);
		di_nr_opl()->nr_gate_control(false);
	} else if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXTVBB)) {
		DI_Wr(DI_CLKG_CTRL, 0x80f60000);
		DI_Wr(DI_PRE_CTRL, 0);
	}
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	else
		DI_Wr(DI_CLKG_CTRL, 0xf60000);
#endif
/* nr/blend0/ei0/mtn0 clock gate */
	if (mirror_disable) {
		di_hw_disable(mcpre_en);
		if (is_meson_txlx_cpu() || is_meson_txhd_cpu()
			|| is_meson_g12a_cpu() || is_meson_g12b_cpu()
			|| is_meson_tl1_cpu() || is_meson_sm1_cpu() ||
			is_meson_tm2_cpu()) {
			enable_di_post_mif(GATE_OFF);
			di_post_gate_control(false);
			di_top_gate_control(false, false);
		} else {
			DI_Wr(DI_CLKG_CTRL, 0x80000000);
		}
		if (!is_meson_gxl_cpu() && !is_meson_gxm_cpu() &&
			!is_meson_gxbb_cpu() && !is_meson_txlx_cpu())
			vpu_dev_clk_gate_off(de_devp->di_vpu_clk_gate_dev);
		pr_info("%s disable di mirror image.\n", __func__);
	}
	if (post_wr_en && post_wr_support)
		diwr_set_power_control(0);
	di_unlock_irqfiq_restore(irq_flag2);

#if (defined ENABLE_SPIN_LOCK_ALWAYS)
	spin_unlock_irqrestore(&plist_lock, flags);
#endif
	di_patch_post_update_mc_sw(DI_MC_SW_REG, false);
	di_pre_stru.force_unreg_req_flag = 0;
	di_pre_stru.disable_req_flag = 0;
	recovery_flag = 0;
	di_pre_stru.cur_prog_flag = 0;
	di_pre_stru.unreg_req_flag = 0;
	di_pre_stru.unreg_req_flag_irq = 0;
#ifdef CONFIG_CMA
	if (de_devp->flag_cma == 1 ||
		(de_devp->flag_cma == 3) ||
		(de_devp->flag_cma == 4)) {
		pr_dbg("%s:cma release req time: %d ms\n",
			__func__, jiffies_to_msecs(jiffies));
		di_pre_stru.cma_release_req = 1;
		up(&di_sema);
	}
#endif
	/*dbg*/
	pr_info("di:retry cnt=%d\n", di_pre_stru.retry_cnt);
}

static void di_reg_process(void)
{
/*get vout information first time*/
	if (reg_flag == 1)
		return;
	vf_reg_provider(&di_vf_prov);
	vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_START, NULL);
	reg_flag = 1;
	di_pre_stru.bypass_flag = false;
	reg_cnt++;
	if (reg_cnt > 0x3fffffff)
		reg_cnt = 0;
	di_print("########%s\n", __func__);
}
#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
/* di pre rdma operation */
static void di_rdma_irq(void *arg)
{
	struct di_dev_s *di_devp = (struct di_dev_s *)arg;

	if (IS_ERR_OR_NULL(di_devp))
		return;
	if (di_devp->rdma_handle <= 0) {
		pr_err("%s rdma handle %d error.\n", __func__,
			di_devp->rdma_handle);
		return;
	}
	if (di_printk_flag)
		pr_dbg("%s...%d.\n", __func__,
			di_pre_stru.field_count_for_cont);
}

static struct rdma_op_s di_rdma_op = {
	di_rdma_irq,
	NULL
};
#endif

static void di_load_pq_table(void)
{
	struct di_pq_parm_s *pos = NULL, *tmp = NULL;

	if (atomic_read(&de_devp->pq_flag) == 0 &&
		(de_devp->flags & DI_LOAD_REG_FLAG)) {
		atomic_set(&de_devp->pq_flag, 1);
		list_for_each_entry_safe(pos, tmp,
			&de_devp->pq_table_list, list) {
			di_load_regs(pos);
			list_del(&pos->list);
			di_pq_parm_destroy(pos);
		}
		de_devp->flags &= ~DI_LOAD_REG_FLAG;
		atomic_set(&de_devp->pq_flag, 0);
	}
}

static void di_pre_size_change(unsigned short width,
	unsigned short height, unsigned short vf_type)
{
	unsigned int blkhsize = 0;
	int pps_w = 0, pps_h = 0;
	struct nr_cfg_s cfg_data;
	struct nr_cfg_s *cfg = &cfg_data;

	cfg->width = width;
	cfg->height = height;
	cfg->linkflag = 0;
	di_nr_opl()->nr_all_config(vf_type, cfg);
	#ifdef DET3D
	det3d_config(det3d_en ? 1 : 0);
	#endif
	if (pulldown_enable) {
		pulldown_init(width, height);
		init_field_mode(height);

		if (is_meson_txl_cpu() ||
			is_meson_txlx_cpu() ||
			is_meson_gxlx_cpu() ||
			is_meson_txhd_cpu() ||
			is_meson_g12a_cpu() ||
			is_meson_g12b_cpu() ||
			is_meson_tl1_cpu() || is_meson_tm2_cpu() ||
			is_meson_sm1_cpu())
			film_mode_win_config(width, height);
	}
	if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL))
		combing_pd22_window_config(width, height);
	RDMA_WR(DI_PRE_SIZE, (width - 1) |
		((height - 1) << 16));

	if (mcpre_en) {
		blkhsize = (width + 4) / 5;
		RDMA_WR(MCDI_HV_SIZEIN, height
			| (width << 16));
		RDMA_WR(MCDI_HV_BLKSIZEIN, (overturn ? 3 : 0) << 30
			| blkhsize << 16 | height);
		RDMA_WR(MCDI_BLKTOTAL, blkhsize * height);
		if (is_meson_gxlx_cpu()) {
			RDMA_WR(MCDI_PD_22_CHK_FLG_CNT, 0);
			RDMA_WR(MCDI_FIELD_MV, 0);
		}
	}

	di_load_pq_table();

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
		RDMA_WR(DI_PRE_GL_CTRL, 0x80000005);
	if (de_devp->nrds_enable)
		nr_ds_init(width, height);
	if (de_devp->pps_enable && pps_position) {
		pps_w = di_pre_stru.cur_width;
		pps_h = di_pre_stru.cur_height>>1;
		di_pps_config(1, pps_w, pps_h, pps_dstw, (pps_dsth>>1));
	}

	if (de_devp->h_sc_down_en) {
		pps_w = di_pre_stru.cur_width;
		di_inp_hsc_setting(pps_w, pre_hsc_down_width);
	} else {
		di_inp_hsc_setting(di_pre_stru.cur_width,
			di_pre_stru.cur_width);
	}

	di_interrupt_ctrl(di_pre_stru.madi_enable,
		det3d_en?1:0,
		de_devp->nrds_enable,
		post_wr_en,
		di_pre_stru.mcdi_enable);
}

void di_vf_x_y(struct vframe_s *vf, unsigned int *x, unsigned int *y)
{
	*x = 0;
	*y = 0;

	if (!vf)
		return;
	*x = vf->width;
	*y = vf->height;

	if (vf->type & VIDTYPE_COMPRESS) {
		*x = vf->compWidth;
		*y = vf->compHeight;
	}
}

static bool need_bypass(struct vframe_s *vf)
{
	unsigned int x, y;

	needbypass_flag = true;

	di_vf_x_y(vf, &x, &y);

	if (vf->type & VIDTYPE_MVC)
		return true;

	if (vf->source_type == VFRAME_SOURCE_TYPE_PPMGR)
		return true;

	if (vf->type & VIDTYPE_VIU_444)
		return true;

	if (vf->type & VIDTYPE_PIC)
		return true;
#if 0
	if (vf->type & VIDTYPE_COMPRESS)
		return true;
#else
	/*support G12A and TXLX platform*/
	if (vf->type & VIDTYPE_COMPRESS) {
		if (!afbc_is_supported())
			return true;
		if ((y > (default_height + 8)) ||
			x > default_width)
			return true;
	}
#endif
	if ((vf->width > default_width) ||
			(vf->height > (default_height + 8)))
		return true;

	/*true bypass for game mode*/
	if (vf->flag & VFRAME_FLAG_GAME_MODE)
		return true;

	needbypass_flag = false;
	return false;
}

static bool nrds_en;
module_param_named(nrds_en, nrds_en, bool, 0644);

static void di_reg_process_irq(void)
{
	ulong irq_flag2 = 0;
	#ifndef	RUN_DI_PROCESS_IN_IRQ
	ulong flags = 0;
	#endif
	vframe_t *vframe;
	unsigned short nr_height = 0, first_field_type;

	if ((pre_run_flag != DI_RUN_FLAG_RUN) &&
	    (pre_run_flag != DI_RUN_FLAG_STEP))
		return;
	if (pre_run_flag == DI_RUN_FLAG_STEP)
		pre_run_flag = DI_RUN_FLAG_STEP_DONE;

	di_pre_stru.reg_irq_busy = true;

	vframe = vf_peek(VFM_NAME);

	if (vframe) {
		if (need_bypass(vframe) || ((di_debug_flag>>20) & 0x1)) {
			if (!di_pre_stru.bypass_flag) {
				pr_info("DI bypass all %ux%u-0x%x.",
				vframe->width, vframe->height, vframe->type);
			}
			di_pre_stru.bypass_flag = true;
			di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false);
			di_pre_stru.reg_irq_busy = false;
			return;
		} else {
			di_pre_stru.bypass_flag = false;
		}
		/* patch for vdin progressive input */
		if ((is_from_vdin(vframe) &&
		    is_progressive(vframe))
			#ifdef DET3D
			|| det3d_en
			#endif
			|| (use_2_interlace_buff & 0x2)
			) {
			use_2_interlace_buff = 1;
			nr_height = vframe->height;
		} else {
			use_2_interlace_buff = 0;
			nr_height = (vframe->height>>1);
		}
		de_devp->nrds_enable = nrds_en;
		de_devp->pps_enable = pps_en;
		/*di pre h scaling down: sm1 tm2*/
		de_devp->h_sc_down_en = pre_hsc_down_en;
		vpu_dev_clk_gate_on(de_devp->di_vpu_clk_gate_dev);
		diwr_set_power_control_pst(1);
		if (post_wr_en && post_wr_support)
			diwr_set_power_control(1);
		/* up for vpu clkb rate change */
		up(&di_sema);
		if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) {
			if (!use_2_interlace_buff) {
				di_top_gate_control(true, true);
				di_post_gate_control(true);
				/* freerun for reg configuration */
				enable_di_post_mif(GATE_AUTO);
			} else {
				di_top_gate_control(true, false);
			}
			de_devp->flags |= DI_VPU_CLKB_SET;
			enable_di_pre_mif(false, mcpre_en);
			di_pre_gate_control(true, mcpre_en);
			di_rst_protect(true);/*2019-01-22 by VLSI feng.wang*/
			di_pre_nr_wr_done_sel(true);
			di_nr_opl()->nr_gate_control(true);
		} else {
			/* if mcdi enable DI_CLKG_CTRL should be 0xfef60000 */
			DI_Wr(DI_CLKG_CTRL, 0xfef60001);
			/* nr/blend0/ei0/mtn0 clock gate */
		}
		di_hdr2_hist_init();
		if (di_printk_flag & 2)
			di_printk_flag = 1;

		di_print("%s: vframe come => di_init_buf\n", __func__);
		if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX))
			di_wr_cue_int();

		if (is_progressive(vframe) && (prog_proc_config & 0x10)) {
#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
			spin_lock_irqsave(&plist_lock, flags);
#endif
			di_lock_irqfiq_save(irq_flag2);
			/*
			 * 10 bit mode need 1.5 times buffer size of
			 * 8 bit mode, init the buffer size as 10 bit
			 * mode size, to make sure can switch bit mode
			 * smoothly.
			 */
			di_init_buf(default_width, default_height, 1);

			di_unlock_irqfiq_restore(irq_flag2);

#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
			spin_unlock_irqrestore(&plist_lock, flags);
			nr_height = vframe->height;
#endif
		} else {
#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
			spin_lock_irqsave(&plist_lock, flags);
#endif
			di_lock_irqfiq_save(irq_flag2);
			/*
			 * 10 bit mode need 1.5 times buffer size of
			 * 8 bit mode, init the buffer size as 10 bit
			 * mode size, to make sure can switch bit mode
			 * smoothly.
			 */
			di_init_buf(default_width, default_height, 0);

			di_unlock_irqfiq_restore(irq_flag2);

#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
			spin_unlock_irqrestore(&plist_lock, flags);
#endif
		}
		if (!reg_flag)
			pr_err("di: warning unreg in reg irq\n");

		calc_lmv_init();
		first_field_type = (vframe->type & VIDTYPE_TYPEMASK);

		//pr_info("%s , %d\n", __func__, __LINE__);
		//pr_info("filed type:0x%x, in H=%d, V=%d\n",
		//	first_field_type, vframe->width, nr_height);
		di_pre_size_change(vframe->width, nr_height,
				first_field_type);

#ifdef CONFIG_AMLOGIC_MEDIA_TVIN
		di_pre_stru.mtn_status =
			adpative_combing_config(vframe->width,
					(vframe->height>>1),
					(vframe->source_type),
					is_progressive(vframe),
					vframe->sig_fmt);
#endif

		di_patch_post_update_mc_sw(DI_MC_SW_REG, true);
		di_nr_opl()->cue_int(vframe);
		if (de_devp->flags & DI_LOAD_REG_FLAG)
			up(&di_sema);

		di_pre_stru.retry_en = false;
		di_pre_stru.retry_cnt = 0;
		di_pre_stru.retry_index = 0;

		init_flag = 1;
		/*di_pre_stru.reg_req_flag_irq = 1;*/
	}
	di_pre_stru.reg_irq_busy = false;
}

static void di_process(void)
{
	ulong irq_flag2 = 0;
	#ifndef	RUN_DI_PROCESS_IN_IRQ
	ulong flags = 0;
	#endif

	if (init_flag && (recovery_flag == 0) &&
		(dump_state_flag == 0)) {
#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
		spin_lock_irqsave(&plist_lock, flags);
#endif
		if (di_pre_stru.pre_de_busy == 0) {
			if (di_pre_stru.pre_de_process_done) {
#if 0/*def CHECK_DI_DONE*/
				/* also for NEW_DI ? 7/15/2013 */
				unsigned int data32 = Rd(DI_INTR_CTRL);
				/*DI_INTR_CTRL[bit 0], NRWR_done, set by
				 * hardware when NRWR is done,clear by write 1
				 * by code;[bit 1]
				 * MTNWR_done, set by hardware when MTNWR
				 * is done, clear by write 1 by code;these two
				 * bits have nothing to do with
				 * DI_INTR_CTRL[16](NRW irq mask, 0 to enable
				 * irq) and DI_INTR_CTRL[17]
				 * (MTN irq mask, 0 to enable irq).two
				 * interrupts are raised if both
				 * DI_INTR_CTRL[16] and DI_INTR_CTRL[17] are 0
				 */
				if (
					((data32 & 0x1) &&
					 ((di_pre_stru.enable_mtnwr == 0)
					  || (data32 &
					      0x2))) ||
					(di_pre_stru.pre_de_clear_flag == 2)) {
					RDMA_WR(DI_INTR_CTRL, data32);
#endif
				if (di_pre_stru.pre_de_clear_flag == 2) {
					di_pre_stru.retry_en = true;
				} else {
					pre_process_time =
					di_pre_stru.pre_de_busy_timer_count;
					pre_de_done_buf_config();
				}
				di_pre_stru.pre_de_process_done = 0;
				di_pre_stru.pre_de_clear_flag = 0;
#ifdef CHECK_DI_DONE
			}
#endif
			} else if (di_pre_stru.pre_de_clear_flag == 1) {
				di_lock_irqfiq_save(
					irq_flag2);
				pre_de_done_buf_clear();
				di_unlock_irqfiq_restore(
					irq_flag2);
				di_pre_stru.pre_de_process_done = 0;
				di_pre_stru.pre_de_clear_flag = 0;
			}
		}

		di_lock_irqfiq_save(irq_flag2);
		di_post_stru.check_recycle_buf_cnt = 0;
		while (check_recycle_buf() & 1) {
			if (di_post_stru.check_recycle_buf_cnt++ >
				MAX_IN_BUF_NUM) {
				di_pr_info("%s: check_recycle_buf time out!!\n",
					__func__);
				break;
			}
		}
		di_unlock_irqfiq_restore(irq_flag2);

		/************/
		if (di_pre_stru.retry_en &&
		    (di_pre_stru.pre_de_busy == 0) &&
		    (di_pre_stru.pre_de_process_done == 0) &&
		    !atomic_read(&di_flag_unreg) &&
		    (di_pre_stru.pre_de_process_flag == 0)) {
			di_pre_stru.retry_index =
				di_pre_stru.field_count_for_cont;
			di_pre_stru.field_count_for_cont--;
			di_print("di:retry set%d\n", di_pre_stru.retry_index);
			pre_de_process();
			di_pre_stru.retry_en = false;
			di_pre_stru.retry_cnt++;
		}

		if ((di_pre_stru.pre_de_busy == 0) &&
			(di_pre_stru.pre_de_process_done == 0)) {
			if ((pre_run_flag == DI_RUN_FLAG_RUN) ||
				(pre_run_flag == DI_RUN_FLAG_STEP)) {
				if (pre_run_flag == DI_RUN_FLAG_STEP)
					pre_run_flag = DI_RUN_FLAG_STEP_DONE;
				if ((!atomic_read(&di_flag_unreg))
				&& (di_pre_stru.pre_de_process_flag == 0)
				&& pre_de_buf_config())
					pre_de_process();
			}
		}
		di_post_stru.di_post_process_cnt = 0;
		while (process_post_vframe()) {
			if (di_post_stru.di_post_process_cnt++ >
				MAX_POST_BUF_NUM) {
				di_pr_info("%s: process_post_vframe time out!!\n",
					__func__);
				break;
			}
		}
		if (post_wr_en && post_wr_support) {
			if (di_post_stru.post_de_busy == 0 &&
			di_post_stru.de_post_process_done) {
				post_de_done_buf_config();
				di_post_stru.de_post_process_done = 0;
			}
			di_post_process();
		}

#if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS)
		spin_unlock_irqrestore(&plist_lock, flags);
#endif
	}
}
static unsigned int nr_done_check_cnt = 5;
static void di_pre_trigger_work(struct di_pre_stru_s *pre_stru_p)
{

	if (pre_stru_p->pre_de_busy && init_flag) {
		pre_stru_p->pre_de_busy_timer_count++;
		if (pre_stru_p->pre_de_busy_timer_count >= nr_done_check_cnt &&
		((cur_to_msecs() - di_pre_stru.irq_time[1]) >
		(10*nr_done_check_cnt))) {
			if (di_dbg_mask & 4) {
				dump_mif_size_state(&di_pre_stru,
					&di_post_stru);
			}
			ddbg_mod_save(eDI_DBG_MOD_PRE_TIMEOUT, 0, 0);
			enable_di_pre_mif(false, mcpre_en);
			if (de_devp->nrds_enable)
				nr_ds_hw_ctrl(false);
			pre_stru_p->pre_de_busy_timer_count = 0;
			pre_stru_p->pre_de_irq_timeout_count++;
			pre_stru_p->pre_de_process_done = 1;
			pre_stru_p->pre_de_busy = 0;
			pre_stru_p->pre_de_clear_flag = 2;
			if ((pre_stru_p->field_count_for_cont < 10) ||
				(di_dbg_mask&0x2)) {
				pr_info("DI*****wait %d timeout 0x%x(%d ms)*****\n",
					pre_stru_p->field_count_for_cont,
					Rd(DI_INTR_CTRL),
					(unsigned int)(cur_to_msecs() -
					di_pre_stru.irq_time[1]));
				pr_info("AFBCD0_MISC_CTRL=0x%x\n",
					RDMA_RD(VD1_AFBCD0_MISC_CTRL));
			}
		}
	} else {
		pre_stru_p->pre_de_busy_timer_count = 0;
	}

	/* if(force_trig){ */
	trigger_pre_di_process(TRIGGER_PRE_BY_TIMER);
	/* } */

	if (force_recovery) {
		if (recovery_flag || (force_recovery & 0x2)) {
			force_recovery_count++;
			if (init_flag) {
				pr_err("====== DI force recovery %u-%u =========\n",
					recovery_log_reason,
					recovery_log_queue_idx);
				print_di_buf(recovery_log_di_buf, 2);
				force_recovery &= (~0x2);
				recovery_flag = 0;
			}
		}
	}
}

static int di_task_handle(void *data)
{
	struct di_dev_s *devp;
	int ret = 0;
	devp = (struct di_dev_s *)data;
	if (!devp)
		return -1;
	while (1) {
		ret = down_interruptible(&di_sema);
		if (ret != 0)
			continue;

		if (!de_devp)
			continue;

		if (!active_flag)
			continue;

		if ((di_pre_stru.unreg_req_flag		||
		     di_pre_stru.force_unreg_req_flag	||
		     di_pre_stru.disable_req_flag)	&&
		    (di_pre_stru.pre_de_busy == 0)) {
			di_unreg_process();
				#if 0
			/* if mirror mode, can't speed down the clk*/
			/* set min rate for power saving */
			if (de_devp->vpu_clkb) {
				clk_set_rate(de_devp->vpu_clkb,
					     de_devp->clkb_min_rate);
			}
				#endif
		}
		if (/*di_pre_stru.reg_req_flag_irq ||*/
		    di_pre_stru.reg_req_flag) {
			di_reg_process();
			di_pre_stru.reg_req_flag = 0;
			/*di_pre_stru.reg_req_flag_irq = 0;*/
		}
		#ifdef CONFIG_CMA
		/* mutex_lock(&de_devp->cma_mutex);*/
		if (di_pre_stru.cma_release_req) {
			atomic_set(&devp->mem_flag, 0);
			di_cma_release(devp);
			di_pre_stru.cma_release_req = 0;
			di_pre_stru.cma_alloc_done = 0;
		}
		if (di_pre_stru.cma_alloc_req) {
			if (di_cma_alloc(devp))
				atomic_set(&devp->mem_flag, 1);
			else
				atomic_set(&devp->mem_flag, 0);
			di_pre_stru.cma_alloc_req = 0;
			di_pre_stru.cma_alloc_done = 1;
		}
		if (di_free_mem_pre()) {
			di_cma_release(devp);
			di_pr_info("release mirror\n");
			atomic_set(&di_trig_free_mem, 0);
		}
		/* mutex_unlock(&de_devp->cma_mutex); */
		#endif

		if (de_devp->flags & DI_VPU_CLKB_SET) {
			if (is_meson_txlx_cpu()) {
				if (!use_2_interlace_buff) {
					#ifdef CLK_TREE_SUPPORT
					clk_set_rate(de_devp->vpu_clkb,
						     de_devp->clkb_min_rate);
					#endif
				} else {
					#ifdef CLK_TREE_SUPPORT
					clk_set_rate(de_devp->vpu_clkb,
						     de_devp->clkb_max_rate);
					#endif
				}
			}
			if (is_meson_g12a_cpu()	||
			    is_meson_g12b_cpu()	||
			    is_meson_tl1_cpu()	||
			    is_meson_tm2_cpu()	||
			    is_meson_sm1_cpu()) {
				#ifdef CLK_TREE_SUPPORT
				clk_set_rate(de_devp->vpu_clkb,
					     de_devp->clkb_max_rate);
				#endif
			}
			de_devp->flags &= (~DI_VPU_CLKB_SET);
		}
	}

	return 0;
}

static void di_pre_process_irq(struct di_pre_stru_s *pre_stru_p)
{
	int i;

	if (active_flag) {
		/* must wait pre de done or time out to clear the de_busy
		 * otherwise may appear watch dog reboot probability
		 * caused by disable mif in unreg_process_irq
		 */
		if (pre_stru_p->unreg_req_flag_irq &&
			(di_pre_stru.pre_de_busy == 0))
			di_unreg_process_irq();
		if (init_flag == 0 /*&& pre_stru_p->reg_req_flag_irq == 0*/
			&& (!atomic_read(&di_flag_unreg)))
			di_reg_process_irq();
	}

	for (i = 0; i < 2; i++) {
		if (active_flag)
			di_process();
	}
	log_buffer_state("pro");
}
static struct hrtimer di_pre_hrtimer;

static void pre_tasklet(unsigned long arg)
{
	unsigned int hrtimer_time = 0;

	hrtimer_time = jiffies_to_msecs(jiffies_64 - de_devp->jiffy);
	if (hrtimer_time > 10)
		pr_dbg("DI: tasklet schedule cost %ums.\n", hrtimer_time);
	di_pre_process_irq((struct di_pre_stru_s *)arg);
}

static enum hrtimer_restart di_pre_hrtimer_func(struct hrtimer *timer)
{
	if (!di_pre_stru.bypass_flag)
		di_pre_trigger_work(&di_pre_stru);
	hrtimer_forward_now(&di_pre_hrtimer, ms_to_ktime(10));
	/*di_patch_post_update_mc();*/
	return HRTIMER_RESTART;
}

static void post_display_buf_clear(void)
{
	struct di_buf_s *p = NULL;
	int itmp;

	pr_info("%s:\n", __func__);
	queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) {
		pr_info("\t%s,%d\n", vframe_type_name[p->type], p->index);
		if (p->type == VFRAME_TYPE_POST) {
			if (!atomic_dec_and_test(&p->di_cnt))
				di_print("%s,di_cnt > 0\n", __func__);
			recycle_vframe_type_post(p);
		} else {
			queue_in(p, QUEUE_RECYCLE);
			di_print("%s: %s[%d] =>recycle_list\n", __func__,
				 vframe_type_name[p->type], p->index);
		}
	}
}

static void dbg_check_list(void)
{
	unsigned int post_display;

	post_display = list_count(QUEUE_DISPLAY);
	di_pr_info("display:%d\n", post_display);
}

/*
 * provider/receiver interface
 */
char *vf_get_receiver_name(const char *provider_name);
static int di_receiver_event_fun(int type, void *data, void *arg)
{
	int i;
	ulong flags;
	char *provider_name = (char *)data;

	if (type == VFRAME_EVENT_PROVIDER_QUREY_VDIN2NR) {
		return di_pre_stru.vdin2nr;
	} else if (type == VFRAME_EVENT_PROVIDER_UNREG) {
		mutex_lock(&di_event_mutex);
		atomic_set(&di_flag_unreg, 1);	//ary 2019-05-27
		pr_dbg("%s , is_bypass() %d trick_mode %d bypass_all %d\n",
			__func__, is_bypass(NULL), trick_mode, bypass_all);
		di_pre_stru.vdin_source = false;
		pr_info("DI: %s: unreg\n", __func__);
		pr_info("DI: provider name:%s\n", provider_name);
		ddbg_mod_save(eDI_DBG_MOD_UNREGB, 0, 0);
		di_pre_stru.unreg_req_flag = 1;
		di_pre_stru.vdin_source = false;

		/*di_request_afbc(false);*/
		trigger_pre_di_process(TRIGGER_PRE_BY_PROVIDER_UNREG);
		di_pre_stru.unreg_req_flag_cnt = 0;
		//wait 10ms:
		if (di_pre_stru.pre_de_process_flag
			|| di_pre_stru.pre_de_busy) {
			pr_info("di:w10\n");
			usleep_range(10000, 10001);
		} else {
			pr_info("di:no w\n");
		}
		di_pre_stru.wait_afbc = false;
		while (di_pre_stru.unreg_req_flag ||
			di_pre_stru.reg_irq_busy) {
			usleep_range(1000, 1001);
			if (di_pre_stru.unreg_req_flag_cnt++ >
				di_reg_unreg_cnt) {
				reg_unreg_timeout_cnt++;
				pr_err("%s:unreg_reg_flag timeout!!!\n",
					__func__);
				di_unreg_process();
				break;
			}
		}
#ifdef SUPPORT_MPEG_TO_VDIN
		if (mpeg2vdin_flag) {
			struct vdin_arg_s vdin_arg;
			struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops();

			vdin_arg.cmd = VDIN_CMD_MPEGIN_STOP;
			if (vdin_ops->tvin_vdin_func)
				vdin_ops->tvin_vdin_func(0, &vdin_arg);

			mpeg2vdin_flag = 0;
		}
#endif
		bypass_state = 1;
#ifdef RUN_DI_PROCESS_IN_IRQ
		if (di_pre_stru.vdin_source)
			DI_Wr_reg_bits(VDIN_WR_CTRL, 0x3, 24, 3);
#endif
		ddbg_mod_save(eDI_DBG_MOD_UNREGE, 0, 0);
		mutex_unlock(&di_event_mutex);
		pr_info("DI: unreg f\n");
	} else if (type == VFRAME_EVENT_PROVIDER_RESET) {
		/*di_blocking = 1;*/
		mutex_lock(&di_event_mutex);
		pr_info("%s: VFRAME_EVENT_PROVIDER_RESET\n", __func__);
		if (is_bypass(NULL)
			|| bypass_state
			|| di_pre_stru.bypass_flag) {
			/* only if di is bypassed, then we send the message of
			 * VFRAME_EVENT_PROVIDER_RESET to video and notify
			 * it to keep the last canvas buffer which was
			 * alloced by codec not by di.
			 */
			vf_notify_receiver(VFM_NAME,
				VFRAME_EVENT_PROVIDER_RESET,
				NULL);
			di_blocking = 1;
			spin_lock_irqsave(&plist_lock, flags);
			post_display_buf_clear();
			spin_unlock_irqrestore(&plist_lock, flags);
		}
		di_blocking = 1;
		/*-----------------------------------*/
		spin_lock_irqsave(&plist_lock, flags);
		for (i = 0; i < MAX_IN_BUF_NUM; i++) {
			if (vframe_in[i])
				pr_info("DI:clear vframe_in[%d]\n", i);

			vframe_in[i] = NULL;
		}
		spin_unlock_irqrestore(&plist_lock, flags);
		di_blocking = 0;
		mutex_unlock(&di_event_mutex);
		pr_info("\treset:end\n");
		/*goto light_unreg;*/
	} else if (type == VFRAME_EVENT_PROVIDER_LIGHT_UNREG) {
		mutex_lock(&di_event_mutex);
		di_blocking = 1;

		pr_info("%s: LIGHT_UNREG\n", __func__);

/*light_unreg:*/
#if 1
		spin_lock_irqsave(&plist_lock, flags);
		for (i = 0; i < MAX_IN_BUF_NUM; i++) {

			if (vframe_in[i])
				pr_info("DI:clear vframe_in[%d]\n", i);

			vframe_in[i] = NULL;
		}
		spin_unlock_irqrestore(&plist_lock, flags);
#endif
		di_blocking = 0;
		mutex_unlock(&di_event_mutex);
		pr_info("\tlight unreg:end\n");
	} else if (type == VFRAME_EVENT_PROVIDER_LIGHT_UNREG_RETURN_VFRAME) {
		unsigned char vf_put_flag = 0;

		pr_info("%s:LIGHT_UNREG_RETURN_VFRAME\n", __func__);
/*
 * do not display garbage when 2d->3d or 3d->2d
 */
		spin_lock_irqsave(&plist_lock, flags);
		for (i = 0; i < MAX_IN_BUF_NUM; i++) {
			if (vframe_in[i]) {
				vf_put(vframe_in[i], VFM_NAME);
				pr_dbg("DI:clear vframe_in[%d]\n", i);
				vf_put_flag = 1;
			}
			vframe_in[i] = NULL;
		}
		if (vf_put_flag)
			vf_notify_provider(VFM_NAME,
				VFRAME_EVENT_RECEIVER_PUT, NULL);

		spin_unlock_irqrestore(&plist_lock, flags);
	} else if (type == VFRAME_EVENT_PROVIDER_VFRAME_READY) {
		if (di_pre_stru.bypass_flag)
			vf_notify_receiver(VFM_NAME,
				VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
		trigger_pre_di_process(TRIGGER_PRE_BY_VFRAME_READY);

#ifdef RUN_DI_PROCESS_IN_IRQ
#define INPUT2PRE_2_BYPASS_SKIP_COUNT   4
		if (active_flag && di_pre_stru.vdin_source) {
			if (is_bypass(NULL)) {
				if (di_pre_stru.pre_de_busy == 0) {
					DI_Wr_reg_bits(VDIN_WR_CTRL,
						0x3, 24, 3);
					di_pre_stru.vdin2nr = 0;
				}
				if (di_pre_stru.bypass_start_count <
				    INPUT2PRE_2_BYPASS_SKIP_COUNT) {
					vframe_t *vframe_tmp = vf_get(VFM_NAME);

					if (vframe_tmp != NULL) {
						vf_put(vframe_tmp, VFM_NAME);
						vf_notify_provider(VFM_NAME,
						VFRAME_EVENT_RECEIVER_PUT,
						NULL);
					}
					di_pre_stru.bypass_start_count++;
				}
			} else if (is_input2pre()) {
				di_pre_stru.bypass_start_count = 0;
				if ((di_pre_stru.pre_de_busy != 0) &&
				    (input2pre_miss_policy == 1 &&
				     frame_count < 30)) {
					di_pre_stru.pre_de_clear_flag = 1;
					di_pre_stru.pre_de_busy = 0;
					input2pre_buf_miss_count++;
				}

				if (di_pre_stru.pre_de_busy == 0) {
					DI_Wr_reg_bits(VDIN_WR_CTRL,
						0x5, 24, 3);
					di_pre_stru.vdin2nr = 1;
					di_process();
					log_buffer_state("pr_");
					if (di_pre_stru.pre_de_busy == 0)
						input2pre_proc_miss_count++;
				} else {
					vframe_t *vframe_tmp = vf_get(VFM_NAME);

					if (vframe_tmp != NULL) {
						vf_put(vframe_tmp, VFM_NAME);
						vf_notify_provider(VFM_NAME,
						VFRAME_EVENT_RECEIVER_PUT,
						NULL);
					}
					input2pre_buf_miss_count++;
					if ((di_pre_stru.cur_width > 720 &&
					     di_pre_stru.cur_height > 576) ||
					    (input2pre_throw_count & 0x10000))
						di_pre_stru.pre_throw_flag =
							input2pre_throw_count &
							0xffff;
				}
			} else {
				if (di_pre_stru.pre_de_busy == 0) {
					DI_Wr_reg_bits(VDIN_WR_CTRL,
						0x3, 24, 3);
					di_pre_stru.vdin2nr = 0;
				}
				di_pre_stru.bypass_start_count =
					INPUT2PRE_2_BYPASS_SKIP_COUNT;
			}
		}
#endif
	} else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) {
		/*int in_buf_num = 0;*/
		struct vframe_states states;

		if (recovery_flag)
			return RECEIVER_INACTIVE;
#if 1/*fix for ucode reset method be break by di.20151230*/
		di_vf_states(&states, NULL);
		if (states.buf_avail_num > 0)
			return RECEIVER_ACTIVE;

		if (vf_notify_receiver(
			VFM_NAME,
			VFRAME_EVENT_PROVIDER_QUREY_STATE,
			NULL) == RECEIVER_ACTIVE)
			return RECEIVER_ACTIVE;
		return RECEIVER_INACTIVE;

#else
		for (i = 0; i < MAX_IN_BUF_NUM; i++) {
			if (vframe_in[i] != NULL)
				in_buf_num++;
		if (bypass_state == 1) {
			if (in_buf_num > 1)
				return RECEIVER_ACTIVE;
			else
				return RECEIVER_INACTIVE;
		} else {
			if (in_buf_num > 0)
				return RECEIVER_ACTIVE;
			else
				return RECEIVER_INACTIVE;
		}
#endif
	} else if (type == VFRAME_EVENT_PROVIDER_REG) {
		char *receiver_name = NULL;

		mutex_lock(&di_event_mutex);
		if (de_devp->flags & DI_SUSPEND_FLAG) {
			pr_err("[DI] reg event device hasn't resumed\n");
			mutex_unlock(&di_event_mutex);
			return -1;
		}
		if (reg_flag) {
			pr_err("[DI] no muti instance.\n");
			mutex_unlock(&di_event_mutex);
			dbg_check_list();
			return -1;
		}
		pr_info("%s: vframe provider reg %s\n", __func__,
			provider_name);
		ddbg_mod_save(eDI_DBG_MOD_REGB, 0, 0);
		di_pre_stru.wait_afbc = false;
		atomic_set(&di_flag_unreg, 0); //ary
		bypass_state = 0;
		di_pre_stru.reg_req_flag = 1;
		trigger_pre_di_process(TRIGGER_PRE_BY_PROVIDER_REG);
		/*check unreg process*/
		di_pre_stru.reg_req_flag_cnt = 0;
		while (di_pre_stru.reg_req_flag) {
			usleep_range(1000, 1001);
			if (di_pre_stru.reg_req_flag_cnt++ > di_reg_unreg_cnt) {
				reg_unreg_timeout_cnt++;
				pr_dbg("%s:reg_req_flag timeout!!!\n",
					__func__);
				break;
			}
		}

		if (strncmp(provider_name, "vdin0", 4) == 0) {
			di_pre_stru.vdin_source = true;
		} else {
			di_pre_stru.vdin_source = false;
		}
		receiver_name = vf_get_receiver_name(VFM_NAME);
		if (receiver_name) {
			if (!strcmp(receiver_name, "amvideo")) {
				di_post_stru.run_early_proc_fun_flag = 0;
				if (post_wr_en && post_wr_support)
					di_post_stru.run_early_proc_fun_flag
						= 1;
			} else {
				di_post_stru.run_early_proc_fun_flag = 1;
				pr_info("set run_early_proc_fun_flag to 1\n");
			}
		} else {
			pr_info("%s error receiver is null.\n", __func__);
		}
		ddbg_mod_save(eDI_DBG_MOD_REGE, 0, 0);
		mutex_unlock(&di_event_mutex);
		pr_info("DI: reg f\n");
	}
#ifdef DET3D
	else if (type == VFRAME_EVENT_PROVIDER_SET_3D_VFRAME_INTERLEAVE) {
		int flag = (long)data;

		di_pre_stru.vframe_interleave_flag = flag;
	}
#endif
	else if (type == VFRAME_EVENT_PROVIDER_FR_HINT) {
		vf_notify_receiver(VFM_NAME,
			VFRAME_EVENT_PROVIDER_FR_HINT, data);
	} else if (type == VFRAME_EVENT_PROVIDER_FR_END_HINT) {
		vf_notify_receiver(VFM_NAME,
			VFRAME_EVENT_PROVIDER_FR_END_HINT, data);
	}

	return 0;
}
#if 0
static void fast_process(void)
{
	int i;
	ulong flags = 0, irq_flag2 = 0;

	if (active_flag && is_bypass(NULL) && (BYPASS_GET_MAX_BUF_NUM <= 1) &&
		init_flag && (recovery_flag == 0) &&
		(dump_state_flag == 0)) {
		if (vf_peek(VFM_NAME) == NULL)
			return;

		for (i = 0; i < 2; i++) {
			spin_lock_irqsave(&plist_lock, flags);
			if (di_pre_stru.pre_de_process_done) {
				pre_de_done_buf_config();
				di_pre_stru.pre_de_process_done = 0;
			}

			di_lock_irqfiq_save(irq_flag2);
			di_post_stru.check_recycle_buf_cnt = 0;
			while (check_recycle_buf() & 1) {
				if (di_post_stru.check_recycle_buf_cnt++ >
					MAX_IN_BUF_NUM) {
					di_pr_info("%s: check_recycle_buf time out!!\n",
						__func__);
					break;
				}
			}
			di_unlock_irqfiq_restore(irq_flag2);

			if ((di_pre_stru.pre_de_busy == 0) &&
			    (di_pre_stru.pre_de_process_done == 0)) {
				if ((pre_run_flag == DI_RUN_FLAG_RUN) ||
				    (pre_run_flag == DI_RUN_FLAG_STEP)) {
					if (pre_run_flag == DI_RUN_FLAG_STEP)
						pre_run_flag =
							DI_RUN_FLAG_STEP_DONE;
					if (pre_de_buf_config() &&
					(di_pre_stru.pre_de_process_flag == 0))
						pre_de_process();
				}
			}
			di_post_stru.di_post_process_cnt = 0;
			while (process_post_vframe()) {
				if (di_post_stru.di_post_process_cnt++ >
					MAX_POST_BUF_NUM) {
					pr_info("%s: process_post_vframe time out!!\n",
						__func__);
					break;
				}
			}

			spin_unlock_irqrestore(&plist_lock, flags);
		}
	}
}
#endif
static vframe_t *di_vf_peek(void *arg)
{
	vframe_t *vframe_ret = NULL;
	struct di_buf_s *di_buf = NULL;

	video_peek_cnt++;
	if (di_pre_stru.bypass_flag)
		return vf_peek(VFM_NAME);
	if ((init_flag == 0) || recovery_flag ||
		di_blocking || di_pre_stru.unreg_req_flag || dump_state_flag)
		return NULL;
	if ((run_flag == DI_RUN_FLAG_PAUSE) ||
	    (run_flag == DI_RUN_FLAG_STEP_DONE))
		return NULL;

	log_buffer_state("pek");

	/*fast_process();*/
#ifdef SUPPORT_START_FRAME_HOLD
	if ((disp_frame_count == 0) && (is_bypass(NULL) == 0)) {
		int ready_count = list_count(QUEUE_POST_READY);

		if (ready_count > start_frame_hold_count) {
			di_buf = get_di_buf_head(QUEUE_POST_READY);
			if (di_buf)
				vframe_ret = di_buf->vframe;
		}
	} else
#endif
	{
		if (!queue_empty(QUEUE_POST_READY)) {
			di_buf = get_di_buf_head(QUEUE_POST_READY);
			if (di_buf)
				vframe_ret = di_buf->vframe;
		}
	}
#ifdef DI_BUFFER_DEBUG
	if (vframe_ret)
		di_print("%s: %s[%d]:%x\n", __func__,
			vframe_type_name[di_buf->type],
			di_buf->index, vframe_ret);
#endif

	return vframe_ret;
}
/*recycle the buffer for keeping buffer*/
static void recycle_keep_buffer(void)
{
	ulong irq_flag2 = 0;
	int i = 0;
	struct di_buf_s *keep_buf;

	keep_buf = di_post_stru.keep_buf;
	if (!IS_ERR_OR_NULL(keep_buf)) {
		if (keep_buf->type == VFRAME_TYPE_POST) {
			pr_dbg("%s recycle keep cur di_buf %d (",
				__func__, keep_buf->index);
			di_lock_irqfiq_save(irq_flag2);
			for (i = 0; i < USED_LOCAL_BUF_MAX; i++) {
				if (keep_buf->di_buf_dup_p[i]) {
					queue_in(
						keep_buf->di_buf_dup_p[i],
						QUEUE_RECYCLE);
					pr_dbg(" %d ",
					keep_buf->di_buf_dup_p[i]->index);
				}
			}
			queue_in(keep_buf, QUEUE_POST_FREE);
			di_unlock_irqfiq_restore(irq_flag2);
			pr_dbg(")\n");
		}
		di_post_stru.keep_buf = NULL;
	}
}
static bool show_nrwr;
static vframe_t *di_vf_get(void *arg)
{
	vframe_t *vframe_ret = NULL;
	struct di_buf_s *di_buf = NULL, *nr_buf = NULL;
	ulong irq_flag2 = 0;

	if (di_pre_stru.bypass_flag)
		return vf_get(VFM_NAME);

	if ((init_flag == 0) || recovery_flag ||
		di_blocking || di_pre_stru.unreg_req_flag || dump_state_flag)
		return NULL;

	if ((run_flag == DI_RUN_FLAG_PAUSE) ||
	    (run_flag == DI_RUN_FLAG_STEP_DONE))
		return NULL;

#ifdef SUPPORT_START_FRAME_HOLD
	if ((disp_frame_count == 0) && (is_bypass(NULL) == 0)) {
		int ready_count = list_count(QUEUE_POST_READY);

		if (ready_count > start_frame_hold_count)
			goto get_vframe;
	} else
#endif
	if (!queue_empty(QUEUE_POST_READY)) {
#ifdef SUPPORT_START_FRAME_HOLD
get_vframe:
#endif
		log_buffer_state("ge_");
		di_lock_irqfiq_save(irq_flag2);

		di_buf = get_di_buf_head(QUEUE_POST_READY);
		if (check_di_buf(di_buf, 21)) {
			di_unlock_irqfiq_restore(irq_flag2);
			return NULL;
		}
		queue_out(di_buf);
		queue_in(di_buf, QUEUE_DISPLAY); /* add it into display_list */

		di_unlock_irqfiq_restore(irq_flag2);

		if (di_buf) {
			vframe_ret = di_buf->vframe;
			nr_buf = di_buf->di_buf_dup_p[1];
			if ((post_wr_en && post_wr_support) &&
			(di_buf->process_fun_index != PROCESS_FUN_NULL)) {
				#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
				vframe_ret->canvas0_config[0].phy_addr =
					di_buf->nr_adr;
				vframe_ret->canvas0_config[0].width =
					di_buf->canvas_width[NR_CANVAS],
				vframe_ret->canvas0_config[0].height =
					di_buf->canvas_height;
				vframe_ret->canvas0_config[0].block_mode = 0;
				vframe_ret->plane_num = 1;
				vframe_ret->canvas0Addr = -1;
				vframe_ret->canvas1Addr = -1;
				if (show_nrwr) {
					vframe_ret->canvas0_config[0].phy_addr =
						nr_buf->nr_adr;
					vframe_ret->canvas0_config[0].width =
						nr_buf->canvas_width[NR_CANVAS];
					vframe_ret->canvas0_config[0].height =
						nr_buf->canvas_height;
				}
				#else
				config_canvas_idx(di_buf, di_wr_idx, -1);
				vframe_ret->canvas0Addr = di_buf->nr_canvas_idx;
				vframe_ret->canvas1Addr = di_buf->nr_canvas_idx;
				if (show_nrwr) {
					config_canvas_idx(nr_buf,
						di_wr_idx, -1);
					vframe_ret->canvas0Addr = di_wr_idx;
					vframe_ret->canvas1Addr = di_wr_idx;
				}
				#endif
				vframe_ret->early_process_fun = do_post_wr_fun;
				vframe_ret->process_fun = NULL;
			}
			di_buf->seq = disp_frame_count;
			atomic_set(&di_buf->di_cnt, 1);
		}
		disp_frame_count++;
		if (run_flag == DI_RUN_FLAG_STEP)
			run_flag = DI_RUN_FLAG_STEP_DONE;

		log_buffer_state("get");
	}
	if (vframe_ret) {
		di_print("%s: %s[%d]:0x%p %u ms\n", __func__,
			 vframe_type_name[di_buf->type],
			 di_buf->index, vframe_ret,
			 jiffies_to_msecs
			 (jiffies_64 - vframe_ret->ready_jiffies64));
	}

	if (!post_wr_en && di_post_stru.run_early_proc_fun_flag && vframe_ret) {
		if (vframe_ret->early_process_fun == do_pre_only_fun)
			vframe_ret->early_process_fun(
				vframe_ret->private_data, vframe_ret);
	}

	return vframe_ret;
}

static void di_vf_put(vframe_t *vf, void *arg)
{
	struct di_buf_s *di_buf = NULL;
	ulong irq_flag2 = 0;

	if (di_pre_stru.bypass_flag) {
		vf_put(vf, VFM_NAME);
		vf_notify_provider(VFM_NAME,
			VFRAME_EVENT_RECEIVER_PUT, NULL);
		#if 0
		if (!IS_ERR_OR_NULL(di_post_stru.keep_buf))
			recycle_keep_buffer();
		#endif
		return;
	}
/* struct di_buf_s *p = NULL; */
/* int itmp = 0; */
	if ((init_flag == 0) || recovery_flag ||
		IS_ERR_OR_NULL(vf)) {
		di_print("%s: 0x%p\n", __func__, vf);
		return;
	}
	#ifdef DI_KEEP_HIS
	if (di_blocking)
		return;
	#endif
	log_buffer_state("pu_");
	di_buf = (struct di_buf_s *)vf->private_data;
	if (IS_ERR_OR_NULL(di_buf)) {
		pr_info("%s: get vframe %p without di buf\n",
			__func__, vf);
		return;
	}
	if (di_post_stru.keep_buf	&&
	    (di_post_stru.keep_buf == di_buf	||
	     di_last_display != di_post_stru.keep_buf->index)) {
	/*if (di_post_stru.keep_buf == di_buf) {*/
		pr_info("[DI]recycle buffer %d, get cnt %d.\n",
			di_post_stru.keep_buf->index, disp_frame_count);
		recycle_keep_buffer();
	}

	if (di_buf->type == VFRAME_TYPE_POST) {
		di_lock_irqfiq_save(irq_flag2);

		if (is_in_queue(di_buf, QUEUE_DISPLAY)) {
			if (!atomic_dec_and_test(&di_buf->di_cnt))
				di_print("%s,di_cnt > 0\n", __func__);
			recycle_vframe_type_post(di_buf);
	} else {
			di_print("%s: %s[%d] not in display list\n", __func__,
			vframe_type_name[di_buf->type], di_buf->index);
	}
		di_unlock_irqfiq_restore(irq_flag2);
#ifdef DI_BUFFER_DEBUG
		recycle_vframe_type_post_print(di_buf, __func__, __LINE__);
#endif
	} else {
		di_lock_irqfiq_save(irq_flag2);
		queue_in(di_buf, QUEUE_RECYCLE);
		di_unlock_irqfiq_restore(irq_flag2);

		di_print("%s: %s[%d] =>recycle_list\n", __func__,
			vframe_type_name[di_buf->type], di_buf->index);
	}

	trigger_pre_di_process(TRIGGER_PRE_BY_PUT);
}

static int di_event_cb(int type, void *data, void *private_data)
{
	if (type == VFRAME_EVENT_RECEIVER_FORCE_UNREG) {
		pr_info("%s: RECEIVER_FORCE_UNREG return\n",
			__func__);
		return 0;
	}
	return 0;
}

static int di_vf_states(struct vframe_states *states, void *arg)
{
	if (!states)
		return -1;
	states->vf_pool_size = de_devp->buf_num_avail;
	states->buf_free_num = list_count(QUEUE_LOCAL_FREE);
	states->buf_avail_num = list_count(QUEUE_POST_READY);
	states->buf_recycle_num = list_count(QUEUE_RECYCLE);
	if (di_dbg_mask&0x1) {
		di_pr_info("di-pre-ready-num:%d\n",
			list_count(QUEUE_PRE_READY));
		di_pr_info("di-display-num:%d\n", list_count(QUEUE_DISPLAY));
	}
	return 0;
}

/*****************************
 *	 di driver file_operations
 *
 ******************************/
static int di_open(struct inode *node, struct file *file)
{
	di_dev_t *di_in_devp;

/* Get the per-device structure that contains this cdev */
	di_in_devp = container_of(node->i_cdev, di_dev_t, cdev);
	file->private_data = di_in_devp;

	return 0;
}

static int di_release(struct inode *node, struct file *file)
{
/* di_dev_t *di_in_devp = file->private_data; */

/* Reset file pointer */

/* Release some other fields */
	file->private_data = NULL;
	return 0;
}
static struct di_pq_parm_s *di_pq_parm_create(struct am_pq_parm_s *pq_parm_p)
{
	struct di_pq_parm_s *pq_ptr = NULL;
	struct am_reg_s *am_reg_p = NULL;
	size_t mem_size = 0;

	pq_ptr = vzalloc(sizeof(struct di_pq_parm_s));
	mem_size = sizeof(struct am_pq_parm_s);
	memcpy(&pq_ptr->pq_parm, pq_parm_p, mem_size);
	mem_size = sizeof(struct am_reg_s)*pq_parm_p->table_len;
	am_reg_p = vzalloc(mem_size);
	if (!am_reg_p) {
		vfree(pq_ptr);
		pr_err("[DI] alloc pq table memory errors\n");
		return NULL;
	}
	pq_ptr->regs = am_reg_p;

	return pq_ptr;
}

static void di_pq_parm_destroy(struct di_pq_parm_s *pq_ptr)
{
	if (!pq_ptr) {
		pr_err("[DI] %s pq parm pointer null.\n", __func__);
		return;
	}
	vfree(pq_ptr->regs);
	vfree(pq_ptr);
}
static long di_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	long ret = 0, tab_flag = 0;
	di_dev_t *di_devp;
	void __user *argp = (void __user *)arg;
	size_t mm_size = 0;
	struct am_pq_parm_s tmp_pq_s = {0};
	struct di_pq_parm_s *di_pq_ptr = NULL;

	if (_IOC_TYPE(cmd) != _DI_) {
		pr_err("%s invalid command: %u\n", __func__, cmd);
		return -EFAULT;
	}
	di_devp = file->private_data;
	if (!di_devp || (di_devp->flags & DI_SUSPEND_FLAG))
		return  -EFAULT;

	switch (cmd) {
	case AMDI_IOC_SET_PQ_PARM:
		mm_size = sizeof(struct am_pq_parm_s);
	/*coverity[TAINTED_SCALAR] not a tainted data*/
		if (copy_from_user(&tmp_pq_s, argp, mm_size)) {
			pr_err("[DI] set pq parm errors\n");
			return -EFAULT;
		}
		if (tmp_pq_s.table_len >= TABLE_LEN_MAX) {
			pr_err("[DI] load 0x%x wrong pq table_len.\n",
				tmp_pq_s.table_len);
			return -EFAULT;
		}
		tab_flag = TABLE_NAME_DI | TABLE_NAME_NR | TABLE_NAME_MCDI |
			TABLE_NAME_DEBLOCK | TABLE_NAME_DEMOSQUITO;
		if (tmp_pq_s.table_name & tab_flag) {
			pr_info("[DI] load 0x%x pq table len %u %s.\n",
				tmp_pq_s.table_name, tmp_pq_s.table_len,
				init_flag?"directly":"later");
		} else {
			pr_err("[DI] load 0x%x wrong pq table.\n",
				tmp_pq_s.table_name);
			return -EFAULT;
		}
		di_pq_ptr = di_pq_parm_create(&tmp_pq_s);
		if (!di_pq_ptr) {
			pr_err("[DI] allocat pq parm struct error.\n");
			return -EFAULT;
		}
		argp = (void __user *)tmp_pq_s.table_ptr;
		mm_size = tmp_pq_s.table_len * sizeof(struct am_reg_s);
		if (copy_from_user(di_pq_ptr->regs, argp, mm_size)) {
			pr_err("[DI] user copy pq table errors\n");
			return -EFAULT;
		}
		if (init_flag) {
			di_load_regs(di_pq_ptr);
			di_pq_parm_destroy(di_pq_ptr);
			break;
		}
		if (atomic_read(&de_devp->pq_flag) == 0) {
			atomic_set(&de_devp->pq_flag, 1);
			if (di_devp->flags & DI_LOAD_REG_FLAG) {
				struct di_pq_parm_s *pos = NULL, *tmp = NULL;
				list_for_each_entry_safe(pos, tmp,
					&di_devp->pq_table_list, list) {
					if (di_pq_ptr->pq_parm.table_name ==
						pos->pq_parm.table_name) {
						pr_info("[DI] remove 0x%x table.\n",
						pos->pq_parm.table_name);
						list_del(&pos->list);
						di_pq_parm_destroy(pos);
					}
				}
			}
			list_add_tail(&di_pq_ptr->list,
				&di_devp->pq_table_list);
			di_devp->flags |= DI_LOAD_REG_FLAG;
			atomic_set(&de_devp->pq_flag, 0);
		} else {
			pr_err("[DI] please retry table name 0x%x.\n",
			di_pq_ptr->pq_parm.table_name);
			di_pq_parm_destroy(di_pq_ptr);
			ret = -EFAULT;
		}
		break;
	default:
		break;
	}
	return ret;
}

#ifdef CONFIG_COMPAT
static long di_compat_ioctl(struct file *file, unsigned int cmd,
	unsigned long arg)
{
	unsigned long ret;

	arg = (unsigned long)compat_ptr(arg);
	ret = di_ioctl(file, cmd, arg);
	return ret;
}
#endif

static const struct file_operations di_fops = {
	.owner		= THIS_MODULE,
	.open		= di_open,
	.release	= di_release,
	.unlocked_ioctl  = di_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = di_compat_ioctl,
#endif
};

static ssize_t
show_frame_format(struct device *dev,
		  struct device_attribute *attr, char *buf)
{
	int ret = 0;

	if (init_flag)
		ret += sprintf(buf + ret, "%s\n",
			di_pre_stru.cur_prog_flag
			? "progressive" : "interlace");

	else
		ret += sprintf(buf + ret, "%s\n", "null");

	return ret;
}
static DEVICE_ATTR(frame_format, 0444, show_frame_format, NULL);

#if 0	/*move to di_local.c*/
static int __init rmem_di_device_init(struct reserved_mem *rmem,
	struct device *dev)
{
	di_dev_t *di_devp = dev_get_drvdata(dev);

	if (di_devp) {
		di_devp->mem_start = rmem->base;
		di_devp->mem_size = rmem->size;
		#ifdef MARK_HIS//mark for ko can't use the reserved mem
		if (!of_get_flat_dt_prop(rmem->fdt_node, "no-map", NULL))
			di_devp->flags |= DI_MAP_FLAG;
		#endif
	pr_dbg("di reverse memory 0x%lx, size %uMB.\n",
			di_devp->mem_start, (di_devp->mem_size >> 20));
		return 0;
	}
/* pr_dbg("di reverse memory 0x%x, size %u B.\n",
 * rmem->base, rmem->size);
 */
	return 1;
}

static void rmem_di_device_release(struct reserved_mem *rmem,
				   struct device *dev)
{
	di_dev_t *di_devp = dev_get_drvdata(dev);

	if (di_devp) {
		di_devp->mem_start = 0;
		di_devp->mem_size = 0;
	}
}
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
unsigned int RDMA_RD_BITS(unsigned int adr, unsigned int start,
			  unsigned int len)
{
	if (de_devp->rdma_handle && di_pre_rdma_enable)
		return rdma_read_reg(de_devp->rdma_handle, adr) &
		       (((1 << len) - 1) << start);
	else
		return Rd_reg_bits(adr, start, len);
}

unsigned int RDMA_WR(unsigned int adr, unsigned int val)
{
	if (is_need_stop_reg(adr))
		return 0;

	if (de_devp->rdma_handle > 0 && di_pre_rdma_enable) {
		if (di_pre_stru.field_count_for_cont < 1)
			DI_Wr(adr, val);
		else
			rdma_write_reg(de_devp->rdma_handle, adr, val);
		return 0;
	}

	DI_Wr(adr, val);
	return 1;
}

unsigned int RDMA_RD(unsigned int adr)
{
	if (de_devp->rdma_handle > 0 && di_pre_rdma_enable)
		return rdma_read_reg(de_devp->rdma_handle, adr);
	else
		return Rd(adr);
}

unsigned int RDMA_WR_BITS(unsigned int adr, unsigned int val,
			  unsigned int start, unsigned int len)
{
	if (is_need_stop_reg(adr))
		return 0;

	if (de_devp->rdma_handle > 0 && di_pre_rdma_enable) {
		if (di_pre_stru.field_count_for_cont < 1)
			DI_Wr_reg_bits(adr, val, start, len);
		else
			rdma_write_reg_bits(de_devp->rdma_handle,
				adr, val, start, len);
		return 0;
	}
	DI_Wr_reg_bits(adr, val, start, len);
	return 1;
}
#else
unsigned int RDMA_RD_BITS(unsigned int adr, unsigned int start,
			  unsigned int len)
{
	return Rd_reg_bits(adr, start, len);
}
unsigned int RDMA_WR(unsigned int adr, unsigned int val)
{
	DI_Wr(adr, val);
	return 1;
}

unsigned int RDMA_RD(unsigned int adr)
{
	return Rd(adr);
}

unsigned int RDMA_WR_BITS(unsigned int adr, unsigned int val,
			  unsigned int start, unsigned int len)
{
	DI_Wr_reg_bits(adr, val, start, len);
	return 1;
}
#endif

static void set_di_flag(void)
{
	if (is_meson_txl_cpu() ||
		is_meson_txlx_cpu() ||
		is_meson_gxlx_cpu() ||
		is_meson_txhd_cpu() ||
		is_meson_g12a_cpu() ||
		is_meson_g12b_cpu() ||
		is_meson_tl1_cpu() || is_meson_tm2_cpu() ||
		is_meson_sm1_cpu()) {
		mcpre_en = true;
		mc_mem_alloc = true;
		pulldown_enable = false;
		di_pre_rdma_enable = false;
		/*
		 * txlx atsc 1080i ei only will cause flicker
		 * when full to small win in home screen
		 */
		di_vscale_skip_enable = (is_meson_txlx_cpu()
				|| is_meson_txhd_cpu())?12:4;
		use_2_interlace_buff = is_meson_gxlx_cpu()?0:1;
		if (is_meson_txl_cpu() ||
			is_meson_txlx_cpu() ||
			is_meson_gxlx_cpu() ||
			is_meson_txhd_cpu() ||
			is_meson_g12a_cpu() ||
			is_meson_g12b_cpu() ||
			is_meson_tl1_cpu() || is_meson_tm2_cpu() ||
			is_meson_sm1_cpu()) {
			full_422_pack = true;
		}

		if (nr10bit_support)
			di_force_bit_mode = 10;
		else {
			di_force_bit_mode = 8;
			full_422_pack = false;
		}
		post_hold_line =
			(is_meson_g12a_cpu() || is_meson_g12b_cpu()
				|| is_meson_tl1_cpu() || is_meson_tm2_cpu() ||
				is_meson_sm1_cpu())?10:17;
	} else {
		post_hold_line = 8;	/*2019-01-10: from VLSI feijun*/
		mcpre_en = false;
		pulldown_enable = false;
		di_pre_rdma_enable = false;
		di_vscale_skip_enable = 4;
		use_2_interlace_buff = 0;
		di_force_bit_mode = 8;
	}
	if (is_meson_tl1_cpu() || is_meson_tm2_cpu()
		|| is_meson_g12a_cpu()
		|| is_meson_g12b_cpu()
		|| is_meson_sm1_cpu())
		pulldown_enable = true;

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
		intr_mode = 3;
	if (di_pre_rdma_enable) {
		pldn_dly = 1;
		pldn_dly1 = 1;
	} else {
		pldn_dly = 2;
		pldn_dly1 = 2;
	}

	mtn_int_combing_glbmot();
}

#if 0	/*move to di_local.c*/
static const struct reserved_mem_ops rmem_di_ops = {
	.device_init	= rmem_di_device_init,
	.device_release = rmem_di_device_release,
};

static int __init rmem_di_setup(struct reserved_mem *rmem)
{
	rmem->ops = &rmem_di_ops;
/* rmem->priv = cma; */

	di_pr_info(
	"DI reserved memory: created CMA memory pool at %pa, size %ld MiB\n",
		&rmem->base, (unsigned long)rmem->size / SZ_1M);

	return 0;
}
RESERVEDMEM_OF_DECLARE(di, "amlogic, di-mem", rmem_di_setup);
#endif
static void di_get_vpu_clkb(struct device *dev, struct di_dev_s *pdev)
{

	int ret = 0;
	unsigned int tmp_clk[2] = {0, 0};
	struct clk *vpu_clk = NULL;
	struct clk *clkb_tmp_comp = NULL;

	vpu_clk = clk_get(dev, "vpu_mux");
	if (IS_ERR(vpu_clk))
		pr_err("%s: get clk vpu error.\n", __func__);
	else
		clk_prepare_enable(vpu_clk);

	ret = of_property_read_u32_array(dev->of_node, "clock-range",
				tmp_clk, 2);
	if (ret) {
		pdev->clkb_min_rate = 250000000;
		pdev->clkb_max_rate = 500000000;
	} else {
		pdev->clkb_min_rate = tmp_clk[0]*1000000;
		pdev->clkb_max_rate = tmp_clk[1]*1000000;
	}
	pr_info("DI: vpu clkb <%lu, %lu>\n", pdev->clkb_min_rate,
		pdev->clkb_max_rate);
	#ifdef CLK_TREE_SUPPORT
	pdev->vpu_clkb = clk_get(dev, "vpu_clkb");
	if (is_meson_tl1_cpu()) {
		clkb_tmp_comp = clk_get(dev, "vpu_clkb_tmp");
		if (IS_ERR(clkb_tmp_comp))
			pr_err("clkb_tmp_comp error\n");
		else {
			if (!IS_ERR(vpu_clk))
				/*coverity[var_deref_mode]*/
				clk_set_parent(clkb_tmp_comp, vpu_clk);
		}
	}

	if (IS_ERR(pdev->vpu_clkb))
		pr_err("%s: get vpu clkb gate error.\n", __func__);
	else {
		clk_set_rate(pdev->vpu_clkb, pdev->clkb_min_rate);
		pr_info("get clkb rate:%ld\n", clk_get_rate(pdev->vpu_clkb));
	}
	#endif
}

static int di_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct di_dev_s *di_devp = NULL;

	di_pr_info("%s:\n", __func__);

#if 1	/*move from init*/
	ret = alloc_chrdev_region(&di_devno, 0, DI_COUNT, DEVICE_NAME);
	if (ret < 0) {
		pr_err("%s: failed to allocate major number\n", __func__);
		goto fail_alloc_cdev_region;
	}
	di_pr_info("%s: major %d\n", __func__, MAJOR(di_devno));
	di_clsp = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(di_clsp)) {
		ret = PTR_ERR(di_clsp);
		pr_err("%s: failed to create class\n", __func__);
		goto fail_class_create;
	}
#endif

	di_devp = kmalloc(sizeof(struct di_dev_s), GFP_KERNEL);
	if (!di_devp) {
		pr_err("%s fail to allocate memory.\n", __func__);
		goto fail_kmalloc_dev;
	}
	de_devp = di_devp;
	memset(di_devp, 0, sizeof(struct di_dev_s));
	di_devp->flags |= DI_SUSPEND_FLAG;
	cdev_init(&(di_devp->cdev), &di_fops);
	di_devp->cdev.owner = THIS_MODULE;
	ret = cdev_add(&(di_devp->cdev), di_devno, DI_COUNT);
	if (ret)
		goto fail_cdev_add;

	di_devp->devt = MKDEV(MAJOR(di_devno), 0);
	di_devp->dev = device_create(di_clsp, &pdev->dev,
		di_devp->devt, di_devp, "di%d", 0);

	if (di_devp->dev == NULL) {
		pr_error("device_create create error\n");
		ret = -EEXIST;
		return ret;
	}
	dev_set_drvdata(di_devp->dev, di_devp);
	platform_set_drvdata(pdev, di_devp);
	ret = of_reserved_mem_device_init(&pdev->dev);
	if (ret != 0)
		pr_info("DI no reserved mem.\n");
	ret = of_property_read_u32(pdev->dev.of_node,
		"flag_cma", &(di_devp->flag_cma));
	if (ret)
		pr_err("DI-%s: get flag_cma error.\n", __func__);
	ret = of_property_read_u32(pdev->dev.of_node,
		"nrds-enable", &(di_devp->nrds_enable));
	ret = of_property_read_u32(pdev->dev.of_node,
		"pps-enable", &(di_devp->pps_enable));
	/*di pre h scaling down :sm1 tm2*/
	di_devp->h_sc_down_en = pre_hsc_down_en;

	pr_info("di:flag_cma=%d\n", di_devp->flag_cma);
	if (di_devp->flag_cma >= 1) {
#ifdef CONFIG_CMA
		di_devp->pdev = pdev;
		di_devp->flags |= DI_MAP_FLAG;
		if (di_devp->flag_cma == 1
			|| di_devp->flag_cma == 2)
			di_devp->mem_size
			= dma_get_cma_size_int_byte(&pdev->dev);

		if (di_devp->mem_size <= 0x800000) {
			di_devp->mem_size = 0x2800000;
			//(flag_cma ? 3) reserved in
			//codec mm : cma in codec mm
			if (di_devp->flag_cma != 3) {
				//no di cma, try use
				//cma from codec mm
				di_devp->flag_cma = 4;
			}
		}

		pr_info("DI: CMA size 0x%x.\n", di_devp->mem_size);
		if (di_devp->flag_cma == 2) {
			if (di_cma_alloc_total(di_devp))
				atomic_set(&di_devp->mem_flag, 1);
		} else
#endif
			atomic_set(&di_devp->mem_flag, 0);
	} else {
			atomic_set(&di_devp->mem_flag, 1);
	}
	/* mutex_init(&di_devp->cma_mutex); */
	INIT_LIST_HEAD(&di_devp->pq_table_list);

	atomic_set(&di_devp->pq_flag, 0);

	di_devp->pre_irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
	pr_info("pre_irq:%d\n",
		di_devp->pre_irq);
	di_devp->post_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
	pr_info("post_irq:%d\n",
		di_devp->post_irq);

#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
/* rdma handle */
	if (di_pre_rdma_enable) {
		di_rdma_op.arg = di_devp;
		di_devp->rdma_handle = rdma_register(&di_rdma_op,
			di_devp, RDMA_TABLE_SIZE);
	}
#endif
	di_pr_info("%s allocate rdma channel %d.\n", __func__,
		di_devp->rdma_handle);
	if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL)) {
		di_get_vpu_clkb(&pdev->dev, di_devp);
		#ifdef CLK_TREE_SUPPORT
		clk_prepare_enable(di_devp->vpu_clkb);
		pr_info("DI:enable vpu clkb.\n");
		#else
		aml_write_hiubus(HHI_VPU_CLKB_CNTL, 0x1000100);
		#endif
	}
	di_devp->flags &= (~DI_SUSPEND_FLAG);
	ret = of_property_read_u32(pdev->dev.of_node,
		"buffer-size", &(di_devp->buffer_size));
	if (ret)
		pr_err("DI-%s: get buffer size error.\n", __func__);

	/* set flag to indicate that post_wr is supportted */
	ret = of_property_read_u32(pdev->dev.of_node,
				"post-wr-support",
				&(di_devp->post_wr_support));
	if (ret)
		post_wr_support = 0;
	else
		post_wr_support = di_devp->post_wr_support;

	ret = of_property_read_u32(pdev->dev.of_node,
		"nr10bit-support",
		&(di_devp->nr10bit_support));
	if (ret)
		nr10bit_support = 0;
	else
		nr10bit_support = di_devp->nr10bit_support;

	memset(&di_post_stru, 0, sizeof(di_post_stru));
	di_post_stru.next_canvas_id = 1;
#ifdef DI_USE_FIXED_CANVAS_IDX
	if (di_get_canvas()) {
		pr_dbg("DI get canvas error.\n");
		ret = -EEXIST;
		return ret;
	}
#endif

	device_create_file(di_devp->dev, &dev_attr_config);
	device_create_file(di_devp->dev, &dev_attr_debug);
	device_create_file(di_devp->dev, &dev_attr_dump_pic);
	device_create_file(di_devp->dev, &dev_attr_log);
	device_create_file(di_devp->dev, &dev_attr_provider_vframe_status);
	device_create_file(di_devp->dev, &dev_attr_frame_format);
	device_create_file(di_devp->dev, &dev_attr_tvp_region);
	pd_device_files_add(di_devp->dev);
	di_nr_opl()->nr_drv_init(di_devp->dev);
	mutex_init(&di_event_mutex);
	di_vpu_dev_register(di_devp);

	init_flag = 0;
	reg_flag = 0;

	di_devp->buf_num_avail = di_devp->mem_size / di_devp->buffer_size;
	if (di_devp->buf_num_avail > MAX_LOCAL_BUF_NUM)
		di_devp->buf_num_avail = MAX_LOCAL_BUF_NUM;

	vf_receiver_init(&di_vf_recv, VFM_NAME, &di_vf_receiver, NULL);
	vf_reg_receiver(&di_vf_recv);
	vf_provider_init(&di_vf_prov, VFM_NAME, &deinterlace_vf_provider, NULL);
	/*active_flag = 1;*/
	sema_init(&di_sema, 1);
	ret = request_irq(di_devp->pre_irq, &de_irq, IRQF_SHARED,
		"pre_di", (void *)"pre_di");
	if (di_devp->post_wr_support) {
		ret = request_irq(di_devp->post_irq, &post_irq,
			IRQF_SHARED, "post_di", (void *)"post_di");
	}
	//sema_init(&di_sema, 1);
	di_sema_init_flag = 1;
	di_hw_init(pulldown_enable, mcpre_en);
	set_di_flag();
	atomic_set(&di_flag_unreg, 0);
/* Disable MCDI when code does not support MCDI */
	if (!mcpre_en)
		DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 1);
	tasklet_init(&di_pre_tasklet, pre_tasklet,
		(unsigned long)(&di_pre_stru));
	tasklet_disable(&di_pre_tasklet);
	hrtimer_init(&di_pre_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	di_pre_hrtimer.function = di_pre_hrtimer_func;
	hrtimer_start(&di_pre_hrtimer, ms_to_ktime(10), HRTIMER_MODE_REL);
	tasklet_enable(&di_pre_tasklet);
	di_pr_info("%s:Di use HRTIMER\n", __func__);
	di_devp->task = kthread_run(di_task_handle, di_devp, "kthread_di");
	if (IS_ERR(di_devp->task))
		pr_err("%s create kthread error.\n", __func__);
	active_flag = 1;
	di_debugfs_init();	/*2018-07-18 add debugfs*/
	di_patch_post_update_mc_sw(DI_MC_SW_IC, true);

	dil_attach_ext_api(&di_ext);
	di_patch_mov_ini();

	dil_set_diff_ver_flag(0);

	di_pr_info("%s:ok\n", __func__);
	return ret;

fail_cdev_add:
	pr_info("%s:fail_cdev_add\n", __func__);
	kfree(di_devp);

fail_kmalloc_dev:
#if 1	/*move from init*/
	class_destroy(di_clsp);
fail_class_create:
	unregister_chrdev_region(di_devno, DI_COUNT);
fail_alloc_cdev_region:
	return ret;
#endif
}

static int di_remove(struct platform_device *pdev)
{
	struct di_dev_s *di_devp = NULL;

	di_pr_info("%s:\n", __func__);
	di_devp = platform_get_drvdata(pdev);

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX))
		clk_disable_unprepare(di_devp->vpu_clkb);
	di_hw_uninit();
	di_devp->di_event = 0xff;
	kthread_stop(di_devp->task);
	hrtimer_cancel(&di_pre_hrtimer);
	tasklet_kill(&di_pre_tasklet);	//ary.sui
	tasklet_disable(&di_pre_tasklet);

#ifdef CONFIG_AMLOGIC_MEDIA_RDMA
/* rdma handle */
	if (di_devp->rdma_handle > 0)
		rdma_unregister(di_devp->rdma_handle);
#endif

	vf_unreg_provider(&di_vf_prov);
	vf_unreg_receiver(&di_vf_recv);

	di_uninit_buf(1);
/* Remove the cdev */
	device_remove_file(di_devp->dev, &dev_attr_config);
	device_remove_file(di_devp->dev, &dev_attr_debug);
	device_remove_file(di_devp->dev, &dev_attr_log);
	device_remove_file(di_devp->dev, &dev_attr_dump_pic);
	device_remove_file(di_devp->dev, &dev_attr_provider_vframe_status);
	device_remove_file(di_devp->dev, &dev_attr_frame_format);
	device_remove_file(di_devp->dev, &dev_attr_tvp_region);
	pd_device_files_del(di_devp->dev);
	di_nr_opl()->nr_drv_uninit(di_devp->dev);
	cdev_del(&di_devp->cdev);

	if (di_devp->flag_cma == 2) {
		if (dma_release_from_contiguous(&(pdev->dev),
			di_devp->total_pages,
			di_devp->mem_size >> PAGE_SHIFT)) {
			di_devp->total_pages = NULL;
			di_devp->mem_start = 0;
			pr_dbg("DI CMA total release ok.\n");
		} else {
			pr_dbg("DI CMA total release fail.\n");
		}
		if (de_devp->nrds_enable) {
			nr_ds_buf_uninit(de_devp->flag_cma,
				&(pdev->dev));
		}

	}
	device_destroy(di_clsp, di_devno);
#if 1	/*move from exit*/
	class_destroy(di_clsp);
	di_debugfs_exit();
	unregister_chrdev_region(di_devno, DI_COUNT);
#endif
	kfree(di_devp);
/* free drvdata */
	dev_set_drvdata(&pdev->dev, NULL);
	platform_set_drvdata(pdev, NULL);

	di_pr_info("%s:ok\n", __func__);
	return 0;
}

static void di_shutdown(struct platform_device *pdev)
{
	struct di_dev_s *di_devp = NULL;
	int ret = 0;

	active_flag = 0;

	di_devp = platform_get_drvdata(pdev);
	ret = hrtimer_cancel(&di_pre_hrtimer);
	pr_info("di pre hrtimer cancel %d.\n", ret);
	tasklet_kill(&di_pre_tasklet);
	tasklet_disable(&di_pre_tasklet);

	init_flag = 0;
	if (is_meson_txlx_cpu())
		di_top_gate_control(true, true);
	else
		DI_Wr(DI_CLKG_CTRL, 0x2);

	if (!is_meson_txlx_cpu())
		vpu_dev_clk_gate_off(di_devp->di_vpu_clk_gate_dev);
	kfree(di_devp);
	di_devp = NULL;
	pr_info("[DI] shutdown done.\n");

}

#ifdef CONFIG_PM

static void di_clear_for_suspend(struct di_dev_s *di_devp)
{
	pr_info("%s\n", __func__);

	di_unreg_process();/*have flag*/
	if (di_pre_stru.unreg_req_flag_irq)
		di_unreg_process_irq();

#ifdef CONFIG_CMA
	if (di_pre_stru.cma_release_req) {
		pr_info("\tcma_release\n");
		atomic_set(&di_devp->mem_flag, 0);
		di_cma_release(di_devp);
		di_pre_stru.cma_release_req = 0;
		di_pre_stru.cma_alloc_done = 0;
	}
#endif
	#ifdef DI_KEEP_HIS
	hrtimer_cancel(&di_pre_hrtimer);
	tasklet_kill(&di_pre_tasklet);	//ary.sui
	tasklet_disable(&di_pre_tasklet);
	#endif
	pr_info("%s end\n", __func__);
}
static int save_init_flag;
/* must called after lcd */
static int di_suspend(struct device *dev)
{

	struct di_dev_s *di_devp = NULL;

	di_devp = dev_get_drvdata(dev);
	di_devp->flags |= DI_SUSPEND_FLAG;

	di_clear_for_suspend(di_devp);//add
	active_flag = 0;

	/* fix suspend/resume crash problem */
	save_init_flag = init_flag;
	init_flag = 0;

	hrtimer_cancel(&di_pre_hrtimer);
	tasklet_kill(&di_pre_tasklet);
	tasklet_disable(&di_pre_tasklet);

#if 0	/*2019-01-18*/
	if (di_pre_stru.di_inp_buf) {
		if (vframe_in[di_pre_stru.di_inp_buf->index]) {
			vf_put(vframe_in[di_pre_stru.di_inp_buf->index],
				VFM_NAME);
			vframe_in[di_pre_stru.di_inp_buf->index] = NULL;
			vf_notify_provider(VFM_NAME,
				VFRAME_EVENT_RECEIVER_PUT, NULL);
		}
	}
#endif

	if (!is_meson_txlx_cpu())
		vpu_dev_clk_gate_off(di_devp->di_vpu_clk_gate_dev);
	if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD))
		clk_disable_unprepare(di_devp->vpu_clkb);
	pr_info("di: di_suspend\n");
	return 0;
}
/* must called before lcd */
static int di_resume(struct device *dev)
{
	struct di_dev_s *di_devp = NULL;

	di_devp = dev_get_drvdata(dev);

	if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL))
		clk_prepare_enable(di_devp->vpu_clkb);
	init_flag = save_init_flag;
	di_devp->flags &= ~DI_SUSPEND_FLAG;
	/*2018-01-18*/
	pr_info("%s\n", __func__);
	tasklet_init(&di_pre_tasklet, pre_tasklet,
		(unsigned long)(&di_pre_stru));
	tasklet_disable(&di_pre_tasklet);
	hrtimer_init(&di_pre_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	di_pre_hrtimer.function = di_pre_hrtimer_func;
	hrtimer_start(&di_pre_hrtimer, ms_to_ktime(10), HRTIMER_MODE_REL);
	tasklet_enable(&di_pre_tasklet);
	active_flag = 1;
	/************/
	pr_info("di: resume module\n");
	return 0;
}

static const struct dev_pm_ops di_pm_ops = {
	.suspend_late = di_suspend,
	.resume_early = di_resume,
};
#endif

/* #ifdef CONFIG_USE_OF */
static const struct of_device_id amlogic_deinterlace_dt_match[] = {
	{ .compatible = "amlogic, deinterlace", },
	{},
};
/* #else */
/* #define amlogic_deinterlace_dt_match NULL */
/* #endif */

static struct platform_driver di_driver = {
	.probe			= di_probe,
	.remove			= di_remove,
	.shutdown		= di_shutdown,
	.driver			= {
		.name		= DEVICE_NAME,
		.owner		= THIS_MODULE,
		.of_match_table = amlogic_deinterlace_dt_match,
#ifdef CONFIG_PM
		.pm			= &di_pm_ops,
#endif
	}
};

int __init di_module_init(void)
{
	int ret = 0;

	pr_dbg("%s ok.\n", __func__);
#if 0	/*move to prob*/
	ret = alloc_chrdev_region(&di_devno, 0, DI_COUNT, DEVICE_NAME);
	if (ret < 0) {
		pr_err("%s: failed to allocate major number\n", __func__);
		goto fail_alloc_cdev_region;
	}
	di_pr_info("%s: major %d\n", __func__, MAJOR(di_devno));
	di_clsp = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(di_clsp)) {
		ret = PTR_ERR(di_clsp);
		pr_err("%s: failed to create class\n", __func__);
		goto fail_class_create;
	}
#endif
	ret = platform_driver_register(&di_driver);
	if (ret != 0) {
		pr_err("%s: failed to register driver\n", __func__);
		return -ENODEV;//goto fail_pdrv_register;
	}
	return 0;
#if 0	/*move to prob*/
fail_pdrv_register:
	class_destroy(di_clsp);
fail_class_create:
	unregister_chrdev_region(di_devno, DI_COUNT);
fail_alloc_cdev_region:
	return ret;
#endif
}

void __exit di_module_exit(void)
{
#if 0	/*move to remove*/
	class_destroy(di_clsp);
	di_debugfs_exit();
	unregister_chrdev_region(di_devno, DI_COUNT);
#endif
	platform_driver_unregister(&di_driver);
}

module_param_named(bypass_all, bypass_all, int, 0664);
module_param_named(bypass_3d, bypass_3d, int, 0664);
module_param_named(bypass_trick_mode, bypass_trick_mode, int, 0664);
module_param_named(di_invert_top_bot, invert_top_bot, int, 0664);
module_param_named(skip_top_bot, skip_top_bot, int, 0664);
module_param_named(force_width, force_width, int, 0664);
module_param_named(force_height, force_height, int, 0664);
module_param_named(prog_proc_config, prog_proc_config, int, 0664);
module_param_named(start_frame_drop_count, start_frame_drop_count, int, 0664);
#ifdef SUPPORT_START_FRAME_HOLD
module_param_named(start_frame_hold_count, start_frame_hold_count, int, 0664);
#endif
module_param_named(same_field_top_count, same_field_top_count,
	long, 0664);
module_param_named(same_field_bot_count, same_field_bot_count,
	long, 0664);
MODULE_PARM_DESC(di_log_flag, "\n di log flag\n");
module_param(di_log_flag, int, 0664);

MODULE_PARM_DESC(di_debug_flag, "\n di debug flag\n");
module_param(di_debug_flag, int, 0664);

MODULE_PARM_DESC(buf_state_log_threshold, "\n buf_state_log_threshold\n");
module_param(buf_state_log_threshold, int, 0664);

MODULE_PARM_DESC(bypass_state, "\n bypass_state\n");
module_param(bypass_state, uint, 0664);

MODULE_PARM_DESC(di_vscale_skip_enable, "\n di_vscale_skip_enable\n");
module_param(di_vscale_skip_enable, uint, 0664);

MODULE_PARM_DESC(di_vscale_skip_count, "\n di_vscale_skip_count\n");
module_param(di_vscale_skip_count, int, 0664);

MODULE_PARM_DESC(di_vscale_skip_count_real, "\n di_vscale_skip_count_real\n");
module_param(di_vscale_skip_count_real, int, 0664);

module_param_named(vpp_3d_mode, vpp_3d_mode, int, 0664);
#ifdef DET3D
MODULE_PARM_DESC(det3d_en, "\n det3d_enable\n");
module_param(det3d_en, bool, 0664);
MODULE_PARM_DESC(det3d_mode, "\n det3d_mode\n");
module_param(det3d_mode, uint, 0664);
#endif

MODULE_PARM_DESC(post_hold_line, "\n post_hold_line\n");
module_param(post_hold_line, uint, 0664);

MODULE_PARM_DESC(post_urgent, "\n post_urgent\n");
module_param(post_urgent, uint, 0664);

MODULE_PARM_DESC(di_printk_flag, "\n di_printk_flag\n");
module_param(di_printk_flag, uint, 0664);

MODULE_PARM_DESC(force_recovery, "\n force_recovery\n");
module_param(force_recovery, uint, 0664);

module_param_named(force_recovery_count, force_recovery_count, uint, 0664);
module_param_named(pre_process_time, pre_process_time, uint, 0664);
module_param_named(bypass_post, bypass_post, uint, 0664);
module_param_named(post_wr_en, post_wr_en, bool, 0664);
module_param_named(post_wr_support, post_wr_support, uint, 0664);
module_param_named(bypass_post_state, bypass_post_state, uint, 0664);
/* n debug for progress interlace mixed source */
module_param_named(use_2_interlace_buff, use_2_interlace_buff, int, 0664);
module_param_named(debug_blend_mode, debug_blend_mode, int, 0664);
MODULE_PARM_DESC(debug_blend_mode, "\n force post blend mode\n");
module_param_named(nr10bit_support, nr10bit_support, uint, 0664);
module_param_named(di_stop_reg_flag, di_stop_reg_flag, uint, 0664);
module_param(di_dbg_mask, uint, 0664);
MODULE_PARM_DESC(di_dbg_mask, "\n di_dbg_mask\n");
module_param(nr_done_check_cnt, uint, 0664);
MODULE_PARM_DESC(nr_done_check_cnt, "\n nr_done_check_cnt\n");
module_param_array(di_stop_reg_addr, uint, &num_di_stop_reg_addr,
	0664);
module_param_named(mcpre_en, mcpre_en, bool, 0664);
module_param_named(check_start_drop_prog, check_start_drop_prog, bool, 0664);
module_param_named(overturn, overturn, bool, 0664);
module_param_named(queue_print_flag, queue_print_flag, int, 0664);
module_param_named(full_422_pack, full_422_pack, bool, 0644);
module_param_named(cma_print, cma_print, bool, 0644);
module_param_named(pulldown_enable, pulldown_enable, bool, 0644);
#ifdef DEBUG_SUPPORT
#ifdef RUN_DI_PROCESS_IN_IRQ
module_param_named(input2pre, input2pre, uint, 0664);
module_param_named(input2pre_buf_miss_count, input2pre_buf_miss_count,
	uint, 0664);
module_param_named(input2pre_proc_miss_count, input2pre_proc_miss_count,
	uint, 0664);
module_param_named(input2pre_miss_policy, input2pre_miss_policy, uint, 0664);
module_param_named(input2pre_throw_count, input2pre_throw_count, uint, 0664);
#endif
#ifdef SUPPORT_MPEG_TO_VDIN
module_param_named(mpeg2vdin_en, mpeg2vdin_en, int, 0664);
module_param_named(mpeg2vdin_flag, mpeg2vdin_flag, int, 0664);
#endif
module_param_named(di_pre_rdma_enable, di_pre_rdma_enable, uint, 0664);
module_param_named(pldn_dly, pldn_dly, uint, 0644);
module_param_named(pldn_dly1, pldn_dly1, uint, 0644);
module_param_named(di_reg_unreg_cnt, di_reg_unreg_cnt, int, 0664);
module_param_named(bypass_pre, bypass_pre, int, 0664);
module_param_named(di_frame_count, frame_count, int, 0664);
#endif
//MODULE_DESCRIPTION("AMLOGIC DEINTERLACE driver");
//MODULE_LICENSE("GPL");
//MODULE_VERSION("4.0.0");
