// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

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

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/canvas/canvas.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/vfm/vfm_ext.h>
#include <linux/amlogic/media/ge2d/ge2d_cmd.h>
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
#include <linux/sched/rt.h>
#include <linux/platform_device.h>
#include "vm_log.h"
#include "vm.h"
#include <linux/ctype.h>
#include <linux/videodev2.h>
#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
#include <media/videobuf-dma-contig.h>
#include <media/videobuf-vmalloc.h>
#include <media/videobuf-dma-sg.h>
#include <linux/amlogic/media/v4l_util/videobuf-res.h>

#include <linux/amlogic/media/utils/amlog.h>
#include <linux/amlogic/media/camera/vmapi.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin_v4l2.h>
#include <linux/ctype.h>
#include <linux/of.h>
#include <linux/cdev.h>

#include <linux/sizes.h>
#include <linux/dma-mapping.h>
#include <linux/of_fdt.h>
//#include <linux/dma-contiguous.h>
#include <linux/dma-map-ops.h>
#include <linux/cma.h>
#include <linux/module.h>
#include <linux/of_reserved_mem.h>
#include <linux/list.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <uapi/linux/sched/types.h>
extern struct vframe_provider_s *vf_get_provider(const char *receiver_name);

/*class property info.*/
//#include "vmcls.h"

/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */

#define GE2D_NV

#define MAX_VF_POOL_SIZE 8

#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
/*same as tvin pool*/
static int VM_POOL_SIZE = 6;
static int VF_POOL_SIZE = 6;
static int VM_CANVAS_ID = 24;
/*same as tvin pool*/
#endif

/*the counter of VM*/
#define VM_MAX_DEVS		2

static struct vm_device_s  *vm_device[VM_MAX_DEVS];

/* static bool isvmused; */

static void vm_cache_this_flush(unsigned int buf_start,
				unsigned int buf_size,
				struct vm_init_s *info);

static inline void vm_vf_put_from_provider(struct vframe_s *vf,
		unsigned int vdin_id);
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
#define INCPTR(p) ptr_atomic_wrap_inc(&(p))
#endif

#ifdef CONFIG_AMLOGIC_CAPTURE_FRAME_ROTATE
static int vmdecbuf_size[] = {
	0x13B3000,/* 5M */
	0xc00000,/* 3M */
	0x753000,/* 2M */
	0x4b0000,/* 1M3 */
	0x300000,/* 1M */
	0x12c000,/* VGA */
	0x4b000,/* QVGA */
};

static struct v4l2_frmsize_discrete canvas_config_wh[] = {
	{2624, 2624},
	{2048, 2048},
	{1600, 1600},
	{1280, 1280},
	{1024, 1024},
	{640, 640},
	{320, 320},
};
#else
static int vmdecbuf_size[] = {
	0xEE5000,/* 5M */
	0x900000,/* 3M */
	0x591000,/* 2M */
	0x384000,/* 1M3 */
	0x240000,/* 1M */
	0xF0000,/* VGA */
	0x3C000,/* QVGA */
};

static struct v4l2_frmsize_discrete canvas_config_wh[] = {
	{2624, 1984},
	{2048, 1536},
	{1600, 1216},
	{1280, 960},
	{1024, 768},
	{640, 512},
	{320, 256},
};
#endif
#define GE2D_ENDIAN_SHIFT        24
#define GE2D_ENDIAN_MASK            (0x1 << GE2D_ENDIAN_SHIFT)
#define GE2D_BIG_ENDIAN             (0 << GE2D_ENDIAN_SHIFT)
//#define GE2D_LITTLE_ENDIAN          (1 << GE2D_ENDIAN_SHIFT)

#define PROVIDER_NAME "vm"

static dev_t vm_devno;
static struct class *vm_clsp;

#define VM_DEV_NAME	   "vm"
#define RECEIVER_NAME  "vm"
#define VM_CLS_NAME	   "vm"

static DEFINE_SPINLOCK(lock);

#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static inline void ptr_atomic_wrap_inc(u32 *ptr)
{
	u32 i = *ptr;

	i++;
	if (i >= VM_POOL_SIZE)
		i = 0;
	*ptr = i;
}
#endif

#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static struct vframe_s vfpool[MAX_VF_POOL_SIZE];
static s32 vfbuf_use[MAX_VF_POOL_SIZE];
static s32 fill_ptr, get_ptr, putting_ptr, put_ptr;
#endif

atomic_t waiting_flag = ATOMIC_INIT(0);

static inline struct vframe_s *vm_vf_get_from_provider(unsigned int vdin_id);
static inline struct vframe_s *vm_vf_peek_from_provider(unsigned int vdin_id);
static inline void vm_vf_put_from_provider(struct vframe_s *vf,
		unsigned int vdin_id);
struct vframe_receiver_op_s *vf_vm_unreg_provider(struct vm_device_s *vdevp);
struct vframe_receiver_op_s *vf_vm_reg_provider(struct vm_device_s
		*vdevp);
static void stop_vm_task(struct vm_device_s *vdevp);
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static int prepare_vframe(struct vframe_s *vf);
#endif

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif

/*
 ***********************************************
 *
 *   buffer op for video sink.
 *
 ***********************************************
 */

#ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static struct vframe_s *local_vf_peek(unsigned int vdin_id)
{
	struct vframe_s *vf = NULL;

	vf = vm_vf_peek_from_provider(vdin_id);
	if (vf) {
		if (vm_device[vdin_id]->vm_skip_count > 0) {
			vm_device[vdin_id]->vm_skip_count--;
			vm_vf_get_from_provider(vdin_id);
			vm_vf_put_from_provider(vf, vdin_id);
			vf = NULL;
		}
	}
	return vf;
}

static struct vframe_s *local_vf_get(unsigned int vdin_id)
{
	return vm_vf_get_from_provider(vdin_id);
}

static void local_vf_put(struct vframe_s *vf, unsigned int vdin_id)
{
	if (vf)
		vm_vf_put_from_provider(vf, vdin_id);
}
#else

static inline u32 index2canvas(u32 index)
{
	int i;
	int start_canvas, count;
	u32 canvas_tab[6];
	struct vdin_v4l2_ops_s *vops = get_vdin_v4l2_ops();

	vops->get_tvin_canvas_info(&start_canvas, &count);
	VM_POOL_SIZE  = count;
	VF_POOL_SIZE  = count;
	VM_CANVAS_ID = start_canvas;
	for (i = 0; i < count; i++)
		canvas_tab[i] =  VM_CANVAS_INDEX + i;
	return canvas_tab[index];
}

static struct vframe_s *vm_vf_peek(void *op_arg, unsigned int vdin_id)
{
	struct vframe_s *vf = NULL;

	vf = vm_vf_peek_from_provider(vdin_id);
	if (vf) {
		if (vm_device[vdin_id]->vm_skip_count > 0) {
			vm_device[vdin_id]->vm_skip_count--;
			vm_vf_get_from_provider(vdin_id);
			vm_vf_put_from_provider(vf, vdin_id);
			vf = NULL;
		}
	}
	return vf;
}

static struct vframe_s *vm_vf_get(void *op_arg, unsigned int vdin_id)
{
	return vm_vf_get_from_provider(vdin_id);
}

static void vm_vf_put(struct vframe_s *vf, void *op_arg)
{
	prepare_vframe(vf);
}

static int vm_vf_states(struct vframe_states *states, void *op_arg)
{
	return 0;
}

static struct vframe_s *local_vf_peek(void)
{
	if (get_ptr == fill_ptr)
		return NULL;
	return &vfpool[get_ptr];
}

static struct vframe_s *local_vf_get(unsigned int vdin_id)
{
	struct vframe_s *vf;

	if (get_ptr == fill_ptr)
		return NULL;
	vf = &vfpool[get_ptr];
	INCPTR(get_ptr);
	return vf;
}

static void local_vf_put(struct vframe_s *vf, unsigned int vdin_id)
{
	int i;
	int  canvas_addr;

	if (!vf)
		return;
	INCPTR(putting_ptr);
	for (i = 0; i < VF_POOL_SIZE; i++) {
		canvas_addr = index2canvas(i);
		if (vf->canvas0Addr == canvas_addr) {
			vfbuf_use[i] = 0;
			vm_vf_put_from_provider(vf, vdin_id);
		}
	}
}
#endif

static int vm_receiver_event_fun(int type, void *data, void *private_data)
{
	struct vm_device_s *vdevp = (struct vm_device_s *)private_data;

	switch (type) {
	case VFRAME_EVENT_PROVIDER_VFRAME_READY:
		/* if  (atomic_read(&waiting_flag))  { */
		wake_up_interruptible(&vdevp->frame_ready);
		/* atomic_set(&waiting_flag, 0); */
		/* } */
		/* up(&vb_start_sema); */
		/* printk("vdin %d frame ready !!!!!\n", vdevp->index); */
		break;
	case VFRAME_EVENT_PROVIDER_START:
		/* printk("vm register!!!!!\n"); */
		vf_vm_reg_provider(vdevp);
		vdevp->vm_skip_count = 0;
		vdevp->test_zoom = 0;
		break;
	case VFRAME_EVENT_PROVIDER_UNREG:
		/* printk("vm unregister!!!!!\n"); */
		vm_local_init();
		vf_vm_unreg_provider(vdevp);
		/* printk("vm unregister succeed!!!!!\n"); */
		break;
	default:
		break;
	}
	return 0;
}

static struct vframe_receiver_op_s vm_vf_receiver = {
	.event_cb = vm_receiver_event_fun
};

#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static const struct vframe_operations_s vm_vf_provider = {
	.peek = vm_vf_peek,
	.get  = vm_vf_get,
	.put  = vm_vf_put,
	.vf_states = vm_vf_states,
};

static struct vframe_provider_s vm_vf_prov;
#endif

#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
int get_unused_vm_index(void)
{
	int i;

	for (i = 0; i < VF_POOL_SIZE; i++) {
		if (vfbuf_use[i] == 0)
			return i;
	}
	return -1;
}

static int prepare_vframe(struct vframe_s *vf)
{
	struct vframe_s *new_vf;
	int index;

	index = get_unused_vm_index();
	if (index < 0)
		return -1;
	new_vf = &vfpool[fill_ptr];
	memcpy(new_vf, vf, sizeof(struct vframe_s));
	vfbuf_use[index]++;
	INCPTR(fill_ptr);
	return 0;
}
#endif

/*
 ************************************************
 *
 *   buffer op for decoder, camera, etc.
 *
 **************************************************
 */

/* static const vframe_provider_t *vfp = NULL; */

void vm_local_init(void)
{
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	int i;

	for (i = 0; i < MAX_VF_POOL_SIZE; i++)
		vfbuf_use[i] = 0;

	fill_ptr = 0;
	get_ptr = 0;
	putting_ptr = 0;
	put_ptr = 0;
#endif
}

struct vframe_receiver_op_s *vf_vm_unreg_provider(struct vm_device_s *vdevp)
{
	/* ulong flags; */
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	vf_unreg_provider(&vm_vf_prov);
#endif
	stop_vm_task(vdevp);
	/* spin_lock_irqsave(&lock, flags); */
	/* vfp = NULL; */
	/* spin_unlock_irqrestore(&lock, flags); */
	return (struct vframe_receiver_op_s *)NULL;
}
EXPORT_SYMBOL(vf_vm_unreg_provider);

struct vframe_receiver_op_s *vf_vm_reg_provider(struct vm_device_s
		*vdevp)
{
	ulong flags;
	/* int ret; */
	spin_lock_irqsave(&lock, flags);
	spin_unlock_irqrestore(&lock, flags);
	vm_buffer_init(vdevp);
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	vf_reg_provider(&vm_vf_prov);
#endif
	start_vm_task(vdevp);
//	start_simulate_task();
	return &vm_vf_receiver;
}
EXPORT_SYMBOL(vf_vm_reg_provider);

static inline struct vframe_s *vm_vf_peek_from_provider(unsigned int vdin_id)
{
	struct vframe_provider_s *vfp;
	struct vframe_s *vf;
	char name[20];

	sprintf(name, "%s%d", RECEIVER_NAME, vdin_id);
	vfp = vf_get_provider(name);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return NULL;
	vf  = vfp->ops->peek(vfp->op_arg);
	return vf;
}

static inline struct vframe_s *vm_vf_get_from_provider(unsigned int vdin_id)
{
	struct vframe_provider_s *vfp;
	char name[20];

	sprintf(name, "%s%d", RECEIVER_NAME, vdin_id);
	vfp = vf_get_provider(name);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return NULL;
	return vfp->ops->get(vfp->op_arg);
}

static inline void vm_vf_put_from_provider(struct vframe_s *vf,
		unsigned int vdin_id)
{
	struct vframe_provider_s *vfp;
	char name[20];

	sprintf(name, "%s%d", RECEIVER_NAME, vdin_id);
	vfp = vf_get_provider(name);
	if (!(vfp && vfp->ops && vfp->ops->peek))
		return;
	vfp->ops->put(vf, vfp->op_arg);
}

/*
 ***********************************************
 *
 *   main task functions.
 *
 ***********************************************
 */

static unsigned int print_ifmt;
/* module_param(print_ifmt, unsigned int, 0644); */
/* MODULE_PARM_DESC(print_ifmt, "print input format\n"); */

static int get_input_format(struct vframe_s *vf)
{
	int format = GE2D_FORMAT_M24_NV21;

	if (vf->type & VIDTYPE_VIU_422) {
		if (vf->type & VIDTYPE_INTERLACE_BOTTOM)
			format =  GE2D_FORMAT_S16_YUV422 |
				  (GE2D_FORMAT_S16_YUV422B & (3 << 3));
		else if (vf->type & VIDTYPE_INTERLACE_TOP)
			format =  GE2D_FORMAT_S16_YUV422 |
				  (GE2D_FORMAT_S16_YUV422T & (3 << 3));
		else
			format =  GE2D_FORMAT_S16_YUV422;
	} else if (vf->type & VIDTYPE_VIU_NV21) {
		if (vf->type & VIDTYPE_INTERLACE_BOTTOM)
			format =  GE2D_FORMAT_M24_NV21 |
				  (GE2D_FORMAT_M24_NV21B & (3 << 3));
		else if (vf->type & VIDTYPE_INTERLACE_TOP)
			format =  GE2D_FORMAT_M24_NV21 |
				  (GE2D_FORMAT_M24_NV21T & (3 << 3));
		else
			format =  GE2D_FORMAT_M24_NV21;
	} else {
		if (vf->type & VIDTYPE_INTERLACE_BOTTOM)
			format =  GE2D_FORMAT_M24_YUV420 |
				  (GE2D_FMT_M24_YUV420B & (3 << 3));
		else if (vf->type & VIDTYPE_INTERLACE_TOP)
			format =  GE2D_FORMAT_M24_YUV420 |
				  (GE2D_FORMAT_M24_YUV420T & (3 << 3));
		else
			format =  GE2D_FORMAT_M24_YUV420;
	}
	if (print_ifmt == 1) {
		pr_debug("VIDTYPE_VIU_NV21=%x, vf->type=%x\n",
			 VIDTYPE_VIU_NV21, vf->type);
		pr_debug("format=%x, w=%d, h=%d\n",
			 format, vf->width, vf->height);
		print_ifmt = 0;
	}
	return format;
}

#ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
static int calc_zoom(int *top, int *left, int *bottom, int *right, int zoom)
{
	u32 screen_width, screen_height;
	s32 start, end;
	s32 video_top, video_left, temp;
	u32 video_width, video_height;
	u32 ratio_x = 0;
	u32 ratio_y = 0;

	if (zoom < 100)
		zoom = 100;

	video_top = *top;
	video_left = *left;
	video_width = *right - *left + 1;
	video_height = *bottom - *top + 1;

	screen_width = video_width * zoom / 100;
	screen_height = video_height * zoom / 100;

	ratio_x = (video_width << 18) / screen_width;
	if (ratio_x * screen_width < (video_width << 18))
		ratio_x++;
	ratio_y = (video_height << 18) / screen_height;

	/* vertical */
	start = video_top + video_height / 2 - (video_height << 17) / ratio_y;
	end   = (video_height << 18) / ratio_y + start - 1;

	if (start < video_top) {
		temp = ((video_top - start) * ratio_y) >> 18;
		*top = temp;
	} else {
		*top = 0;
	}
	temp = *top + (video_height * ratio_y >> 18);
	*bottom = (temp <= (video_height - 1)) ? temp : (video_height - 1);

	/* horizontal */
	start = video_left + video_width / 2 - (video_width << 17) / ratio_x;
	end   = (video_width << 18) / ratio_x + start - 1;
	if (start < video_left) {
		temp = ((video_left - start) * ratio_x) >> 18;
		*left = temp;
	} else {
		*left = 0;
	}
	temp = *left + (video_width * ratio_x >> 18);
	*right = (temp <= (video_width - 1)) ? temp : (video_width - 1);
	return 0;
}
#endif

static int  get_input_frame(struct display_frame_s *frame, struct vframe_s *vf,
			    int zoom)
{
	int ret = 0;
	int top, left, bottom, right;

	if (!vf)
		return -1;

	frame->frame_top = 0;
	frame->frame_left = 0;
	frame->frame_width = vf->width;
	frame->frame_height = vf->height;
	top = 0;
	left = 0;
	bottom = vf->height - 1;
	right = vf->width - 1;
#ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	ret = calc_zoom(&top, &left, &bottom, &right, zoom);
#else
	ret = get_curren_frame_para(&top, &left, &bottom, &right);
#endif
	if (ret >= 0) {
		frame->content_top     =  top & (~1);
		frame->content_left    =  left & (~1);
		frame->content_width   =  vf->width - 2 * frame->content_left;
		frame->content_height  =  vf->height - 2 * frame->content_top;
	} else {
		frame->content_top     = 0;
		frame->content_left    =  0;
		frame->content_width   = vf->width;
		frame->content_height  = vf->height;
	}
	return 0;
}

static int get_output_format(int v4l2_format)
{
	int format = GE2D_FORMAT_S24_YUV444;

	switch (v4l2_format) {
	case V4L2_PIX_FMT_RGB565X:
		format = GE2D_FORMAT_S16_RGB_565;
		break;
	case V4L2_PIX_FMT_YUV444:
		format = GE2D_FORMAT_S24_YUV444;
		break;
	case V4L2_PIX_FMT_VYUY:
		format = GE2D_FORMAT_S16_YUV422;
		break;
	case V4L2_PIX_FMT_BGR24:
		format = GE2D_FORMAT_S24_RGB;
		break;
	case V4L2_PIX_FMT_RGB24:
		format = GE2D_FORMAT_S24_BGR;
		break;
	case V4L2_PIX_FMT_NV12:
#ifdef GE2D_NV
		format = GE2D_FORMAT_M24_NV12;
		break;
#endif
	case V4L2_PIX_FMT_NV21:
#ifdef GE2D_NV
		format = GE2D_FORMAT_M24_NV21;
		break;
#endif
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
		format = GE2D_FORMAT_S8_Y;
		break;
	default:
		break;
	}
	return format;
}

struct vm_dma_contig_memory {
	u32 magic;
	void *vaddr;
	dma_addr_t dma_handle;
	unsigned long size;
	int is_userptr;
};

int is_need_ge2d_pre_process(struct vm_output_para output_para)
{
	int ret = 0;

	switch (output_para.v4l2_format) {
	case  V4L2_PIX_FMT_RGB565X:
	case  V4L2_PIX_FMT_YUV444:
	case  V4L2_PIX_FMT_VYUY:
	case  V4L2_PIX_FMT_BGR24:
	case  V4L2_PIX_FMT_RGB24:
	case  V4L2_PIX_FMT_YUV420:
	case  V4L2_PIX_FMT_YVU420:
	case  V4L2_PIX_FMT_NV12:
	case  V4L2_PIX_FMT_NV21:
		ret = 1;
		break;
	default:
		break;
	}
	return ret;
}

int is_need_sw_post_process(struct vm_output_para output_para)
{
	int ret = 0;

	switch (output_para.v4l2_memory) {
	case MAGIC_DC_MEM:
	case MAGIC_RE_MEM:
		goto exit;
	case MAGIC_SG_MEM:
	case MAGIC_VMAL_MEM:
	default:
		ret = 1;
		break;
	}
exit:
	return ret;
}

int get_canvas_index(int v4l2_format, int *depth)
{
	int canvas = vm_device[0]->vm_canvas[0];
	*depth = 16;
	switch (v4l2_format) {
	case V4L2_PIX_FMT_RGB565X:
	case V4L2_PIX_FMT_VYUY:
		canvas = vm_device[0]->vm_canvas[0];
		*depth = 16;
		break;
	case V4L2_PIX_FMT_YUV444:
	case V4L2_PIX_FMT_BGR24:
	case V4L2_PIX_FMT_RGB24:
		canvas = vm_device[0]->vm_canvas[1];
		*depth = 24;
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV21:
#ifdef GE2D_NV
		canvas = vm_device[0]->vm_canvas[2] |
		    (vm_device[0]->vm_canvas[3] << 8);
#else
		canvas = vm_device[0]->vm_canvas[2] |
		    (vm_device[0]->vm_canvas[3] << 8) |
		    (vm_device[0]->vm_canvas[4] << 16);
#endif
		*depth = 12;
		break;
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
		canvas = vm_device[0]->vm_canvas[5] |
		    (vm_device[0]->vm_canvas[6] << 8) |
		    (vm_device[0]->vm_canvas[7] << 16);
		*depth = 12;
		break;
	default:
		break;
	}
	return canvas;
}

int get_canvas_index_res(int ext_canvas, int v4l2_format, int *depth, int width,
			 int height, unsigned int buf)
{
	int canvas = ext_canvas;
	*depth = 16;
	switch (v4l2_format) {
	case V4L2_PIX_FMT_RGB565X:
	case V4L2_PIX_FMT_VYUY:
		canvas = ext_canvas & 0xff;
		*depth = 16;
		canvas_config(canvas,
			      (unsigned long)buf,
			      width * 2, height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		break;
	case V4L2_PIX_FMT_YUV444:
	case V4L2_PIX_FMT_BGR24:
	case V4L2_PIX_FMT_RGB24:
		canvas = ext_canvas & 0xff;
		*depth = 24;
		canvas_config(canvas,
			      (unsigned long)buf,
			      width * 3, height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV21:
		canvas_config(ext_canvas & 0xff,
			      (unsigned long)buf,
			      width, height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((ext_canvas & 0xff00) >> 8,
			      (unsigned long)(buf + width * height),
			      width, height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas = ext_canvas & 0xffff;
		*depth = 12;
		break;
	case V4L2_PIX_FMT_YVU420:
	case V4L2_PIX_FMT_YUV420:
		canvas_config(ext_canvas & 0xff,
			      (unsigned long)buf,
			      width, height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((ext_canvas & 0xff00) >> 8,
			      (unsigned long)(buf + width * height),
			      width / 2, height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((ext_canvas & 0xff0000) >> 16,
			      (unsigned long)(buf + width * height * 5 / 4),
			      width / 2, height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas = ext_canvas & 0xffffff;
		*depth = 12;
		break;
	default:
		break;
	}
	return canvas;
}

int vm_fill_this_buffer(struct videobuf_buffer *vb,
			struct vm_output_para *para, struct vm_init_s *info)

{
	int depth = 0;
	int ret = 0;
	int canvas_index = -1;
	int v4l2_format = V4L2_PIX_FMT_YUV444;
	int magic = 0;
	int ext_canvas;
	struct videobuf_buffer buf = {0};

	if (!info)
		return -1;
	if (info->vdin_id >= VM_MAX_DEVS) {
		pr_err("beyond the device array bound .\n");
		return -1;
	}
	/* if (info->isused == false) */
	/* return -2; */

//	if (!vb)
//		goto exit;

	if (!vb) {
		buf.width = 640;
		buf.height = 480;
		v4l2_format =  V4L2_PIX_FMT_YUV444;
		vb = &buf;
	}

	if (!vm_device[info->vdin_id]->task_running)
		return -1;

	v4l2_format = para->v4l2_format;
	magic = para->v4l2_memory;
	switch (magic) {
	case   MAGIC_DC_MEM:
		/* mem = vb->priv; */
		canvas_config(vm_device[0]->vm_canvas[11],
			      (dma_addr_t)para->vaddr,
			      vb->bytesperline, vb->height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_index =  vm_device[0]->vm_canvas[11];
		depth = (vb->bytesperline << 3) / vb->width;
		break;
	case  MAGIC_RE_MEM:
		if (para->ext_canvas != 0) {
			canvas_index = get_canvas_index_res(para->ext_canvas,
				   v4l2_format,
			       &depth, vb->width,
			       (para->height == 0) ? vb->height : para->height,
			       (unsigned int)para->vaddr);
		} else if (info->vdin_id == 0) {
			ext_canvas = ((vm_device[0]->vm_canvas[3]) |
			       (vm_device[0]->vm_canvas[4] << 8) |
			       (vm_device[0]->vm_canvas[5] << 16));
			canvas_index =
				get_canvas_index_res(ext_canvas,
				     v4l2_format, &depth, vb->width,
				     vb->height, (unsigned int)para->vaddr);
		} else {
			ext_canvas = ((vm_device[1]->vm_canvas[0]) |
			       (vm_device[1]->vm_canvas[1] << 8) |
			       (vm_device[1]->vm_canvas[2] << 16));
			canvas_index =
				get_canvas_index_res(ext_canvas,
				     v4l2_format, &depth, vb->width,
				     vb->height, (unsigned int)para->vaddr);
		}
		break;
	case  MAGIC_SG_MEM:
	case  MAGIC_VMAL_MEM:
		if (vm_device[info->vdin_id]->buffer_start &&
		    vm_device[info->vdin_id]->buffer_size)
			canvas_index = get_canvas_index(v4l2_format, &depth);
		break;
	default:
		canvas_index = vm_device[0]->vm_canvas[0];
		break;
	}
	vm_device[info->vdin_id]->output_para.width = vb->width;
	vm_device[info->vdin_id]->output_para.height = vb->height;
	vm_device[info->vdin_id]->output_para.bytesperline =
		(vb->width * depth) >> 3;
	vm_device[info->vdin_id]->output_para.index = canvas_index;
	vm_device[info->vdin_id]->output_para.v4l2_format = v4l2_format;
	vm_device[info->vdin_id]->output_para.v4l2_memory = magic;
	vm_device[info->vdin_id]->output_para.mirror = para->mirror;
	vm_device[info->vdin_id]->output_para.zoom = para->zoom;
	vm_device[info->vdin_id]->output_para.angle = para->angle;
	vm_device[info->vdin_id]->output_para.vaddr = para->vaddr;
	vm_device[info->vdin_id]->output_para.ext_canvas =
		(magic == MAGIC_RE_MEM) ? para->ext_canvas : 0;
	complete(&vm_device[info->vdin_id]->vb_start_sema);
	wait_for_completion(&vm_device[info->vdin_id]->vb_done_sema);
	if (magic == MAGIC_RE_MEM)
		vm_cache_this_flush((unsigned int)para->vaddr,
				    para->bytesperline *
				    para->height, info);

	return ret;
}

/*
 *for decoder input processing
 *  1. output window should 1:1 as source frame size
 *  2. keep the frame ratio
 *  3. input format should be YUV420 , output format should be YUV444
 */

int vm_ge2d_pre_process(struct vframe_s *vf,
					struct ge2d_context_s *context,
					struct config_para_ex_s *ge2d_config,
					struct vm_output_para output_para,
					unsigned int index)
{
	int ret;
	int src_top, src_left, src_width, src_height;
	struct canvas_s cs0, cs1, cs2, cd;
	int current_mirror = 0;
	int cur_angle = 0;
	struct display_frame_s input_frame = {0};

	ret = get_input_frame(&input_frame, vf, output_para.zoom);
	src_top = input_frame.content_top;
	src_left = input_frame.content_left;
	src_width = input_frame.content_width;
	src_height = input_frame.content_height;
	if (vm_device[index]->test_zoom) {
		vm_device[index]->test_zoom = 0;
		pr_debug("top is %d , left is %d\n",
			 input_frame.content_top,
			 input_frame.content_left);
		pr_debug("width is %d , height is %d\n",
			 input_frame.content_width,
			 input_frame.content_height);
	}
#ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	current_mirror = output_para.mirror;
	if (current_mirror < 0)
		current_mirror = 0;
#else
	current_mirror = camera_mirror_flag;
#endif

	cur_angle = output_para.angle;
	if (current_mirror == 1)
		cur_angle = (360 - cur_angle % 360);
	else
		cur_angle = cur_angle % 360;

	/* data operating. */
	ge2d_config->alu_const_color = 0; /* 0x000000ff; */
	ge2d_config->bitmask_en  = 0;
	ge2d_config->src1_gb_alpha = 0;/* 0xff; */
	ge2d_config->dst_xy_swap = 0;

	canvas_read(vf->canvas0Addr & 0xff, &cs0);
	canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1);
	canvas_read((vf->canvas0Addr >> 16) & 0xff, &cs2);
	ge2d_config->src_planes[0].addr = cs0.addr;
	ge2d_config->src_planes[0].w = cs0.width;
	ge2d_config->src_planes[0].h = cs0.height;
	ge2d_config->src_planes[1].addr = cs1.addr;
	ge2d_config->src_planes[1].w = cs1.width;
	ge2d_config->src_planes[1].h = cs1.height;
	ge2d_config->src_planes[2].addr = cs2.addr;
	ge2d_config->src_planes[2].w = cs2.width;
	ge2d_config->src_planes[2].h = cs2.height;
	canvas_read(output_para.index & 0xff, &cd);
	ge2d_config->dst_planes[0].addr = cd.addr;
	ge2d_config->dst_planes[0].w = cd.width;
	ge2d_config->dst_planes[0].h = cd.height;
	ge2d_config->src_key.key_enable = 0;
	ge2d_config->src_key.key_mask = 0;
	ge2d_config->src_key.key_mode = 0;
	ge2d_config->src_para.canvas_index = vf->canvas0Addr;
	ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->src_para.format = get_input_format(vf);
	ge2d_config->src_para.fill_color_en = 0;
	ge2d_config->src_para.fill_mode = 0;
	ge2d_config->src_para.x_rev = 0;
	ge2d_config->src_para.y_rev = 0;
	ge2d_config->src_para.color = 0xffffffff;
	ge2d_config->src_para.top = 0;
	ge2d_config->src_para.left = 0;
	ge2d_config->src_para.width = vf->width;
	ge2d_config->src_para.height = vf->height;
	ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.canvas_index = output_para.index & 0xff;
#ifdef GE2D_NV
	if (output_para.v4l2_format != V4L2_PIX_FMT_YUV420 &&
			output_para.v4l2_format != V4L2_PIX_FMT_YVU420)
		ge2d_config->dst_para.canvas_index = output_para.index;
#endif
	ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
	ge2d_config->dst_para.format =
		get_output_format(output_para.v4l2_format) |
		GE2D_LITTLE_ENDIAN;
	ge2d_config->dst_para.fill_color_en = 0;
	ge2d_config->dst_para.fill_mode = 0;
	ge2d_config->dst_para.x_rev = 0;
	ge2d_config->dst_para.y_rev = 0;
	ge2d_config->dst_para.color = 0;
	ge2d_config->dst_para.top = 0;
	ge2d_config->dst_para.left = 0;
	ge2d_config->dst_para.width = output_para.width;
	ge2d_config->dst_para.height = output_para.height;

	if (current_mirror == 1) {
		ge2d_config->dst_para.x_rev = 1;
		ge2d_config->dst_para.y_rev = 0;
	} else if (current_mirror == 2) {
		ge2d_config->dst_para.x_rev = 0;
		ge2d_config->dst_para.y_rev = 1;
	} else {
		ge2d_config->dst_para.x_rev = 0;
		ge2d_config->dst_para.y_rev = 0;
	}

	if (cur_angle == 90) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.x_rev ^= 1;
	} else if (cur_angle == 180) {
		ge2d_config->dst_para.x_rev ^= 1;
		ge2d_config->dst_para.y_rev ^= 1;
	} else if (cur_angle == 270) {
		ge2d_config->dst_xy_swap = 1;
		ge2d_config->dst_para.y_rev ^= 1;
	}

	if (ge2d_context_config_ex(context, ge2d_config) < 0) {
		pr_err("++ge2d configing error.\n");
		return -1;
	}
	stretchblt_noalpha(context, src_left, src_top,
			   src_width, src_height, 0, 0,
			   output_para.width, output_para.height);

	/* for cr of  yuv420p or yuv420sp. */
	if (output_para.v4l2_format == V4L2_PIX_FMT_YUV420 ||
			output_para.v4l2_format == V4L2_PIX_FMT_YVU420) {
		/* for cb. */
		canvas_read((output_para.index >> 8) & 0xff, &cd);
		ge2d_config->dst_planes[0].addr = cd.addr;
		ge2d_config->dst_planes[0].w = cd.width;
		ge2d_config->dst_planes[0].h = cd.height;
		ge2d_config->dst_para.canvas_index =
			(output_para.index >> 8) & 0xff;
#ifndef GE2D_NV
		ge2d_config->dst_para.format =
			GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN;
#else
		ge2d_config->dst_para.format =
			GE2D_FORMAT_S8_CR | GE2D_LITTLE_ENDIAN;
#endif
		ge2d_config->dst_para.width = output_para.width / 2;
		ge2d_config->dst_para.height = output_para.height / 2;
		ge2d_config->dst_xy_swap = 0;

		if (current_mirror == 1) {
			ge2d_config->dst_para.x_rev = 1;
			ge2d_config->dst_para.y_rev = 0;
		} else if (current_mirror == 2) {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 1;
		} else {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 0;
		}

		if (cur_angle == 90) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.x_rev ^= 1;
		} else if (cur_angle == 180) {
			ge2d_config->dst_para.x_rev ^= 1;
			ge2d_config->dst_para.y_rev ^= 1;
		} else if (cur_angle == 270) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.y_rev ^= 1;
		}

		if (ge2d_context_config_ex(context, ge2d_config) < 0) {
			pr_err("++ge2d configing error.\n");
			return -1;
		}
		stretchblt_noalpha(context, src_left, src_top,
				   src_width, src_height,
				   0, 0, ge2d_config->dst_para.width,
				   ge2d_config->dst_para.height);
	}
#ifndef GE2D_NV
	else if (output_para.v4l2_format == V4L2_PIX_FMT_NV12 ||
		 output_para.v4l2_format == V4L2_PIX_FMT_NV21) {
		canvas_read((output_para.index >> 8) & 0xff, &cd);
		ge2d_config->dst_planes[0].addr = cd.addr;
		ge2d_config->dst_planes[0].w = cd.width;
		ge2d_config->dst_planes[0].h = cd.height;
		ge2d_config->dst_para.canvas_index =
			(output_para.index >> 8) & 0xff;
		ge2d_config->dst_para.format =
			GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN;
		ge2d_config->dst_para.width = output_para.width / 2;
		ge2d_config->dst_para.height = output_para.height / 2;
		ge2d_config->dst_xy_swap = 0;

		if (current_mirror == 1) {
			ge2d_config->dst_para.x_rev = 1;
			ge2d_config->dst_para.y_rev = 0;
		} else if (current_mirror == 2) {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 1;
		} else {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 0;
		}

		if (cur_angle == 90) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.x_rev ^= 1;
		} else if (cur_angle == 180) {
			ge2d_config->dst_para.x_rev ^= 1;
			ge2d_config->dst_para.y_rev ^= 1;
		} else if (cur_angle == 270) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.y_rev ^= 1;
		}

		if (ge2d_context_config_ex(context, ge2d_config) < 0) {
			pr_err("++ge2d configing error.\n");
			return -1;
		}
		stretchblt_noalpha(context, src_left, src_top,
				   src_width, src_height, 0, 0,
				   ge2d_config->dst_para.width,
				   ge2d_config->dst_para.height);
	}
#endif

	/* for cb of yuv420p or yuv420sp. */
	if (output_para.v4l2_format == V4L2_PIX_FMT_YUV420 ||
#ifdef GE2D_NV
	    output_para.v4l2_format == V4L2_PIX_FMT_YVU420
#else
		output_para.v4l2_format == V4L2_PIX_FMT_YVU420 ||
		output_para.v4l2_format == V4L2_PIX_FMT_NV12 ||
		output_para.v4l2_format == V4L2_PIX_FMT_NV21
#endif
	   ) {
		canvas_read((output_para.index >> 16) & 0xff, &cd);
		ge2d_config->dst_planes[0].addr = cd.addr;
		ge2d_config->dst_planes[0].w = cd.width;
		ge2d_config->dst_planes[0].h = cd.height;
		ge2d_config->dst_para.canvas_index =
			(output_para.index >> 16) & 0xff;
#ifndef GE2D_NV
		ge2d_config->dst_para.format =
			GE2D_FORMAT_S8_CR | GE2D_LITTLE_ENDIAN;
#else
		ge2d_config->dst_para.format =
			GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN;
#endif
		ge2d_config->dst_para.width = output_para.width / 2;
		ge2d_config->dst_para.height = output_para.height / 2;
		ge2d_config->dst_xy_swap = 0;

		if (current_mirror == 1) {
			ge2d_config->dst_para.x_rev = 1;
			ge2d_config->dst_para.y_rev = 0;
		} else if (current_mirror == 2) {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 1;
		} else {
			ge2d_config->dst_para.x_rev = 0;
			ge2d_config->dst_para.y_rev = 0;
		}

		if (cur_angle == 90) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.x_rev ^= 1;
		} else if (cur_angle == 180) {
			ge2d_config->dst_para.x_rev ^= 1;
			ge2d_config->dst_para.y_rev ^= 1;
		} else if (cur_angle == 270) {
			ge2d_config->dst_xy_swap = 1;
			ge2d_config->dst_para.y_rev ^= 1;
		}

		if (ge2d_context_config_ex(context, ge2d_config) < 0) {
			pr_err("++ge2d configing error.\n");
			return -1;
		}
		stretchblt_noalpha(context, src_left,
				   src_top, src_width, src_height,
				   0, 0, ge2d_config->dst_para.width,
				   ge2d_config->dst_para.height);
	}
	return output_para.index;
}

int vm_sw_post_process(int canvas, uintptr_t addr, unsigned int index)
{
	int poss = 0, posd = 0;
	int i = 0;
	void __iomem *buffer_y_start;
	void __iomem *buffer_u_start;
	void __iomem *buffer_v_start = 0;
	struct io_mapping *mapping_wc;
	int offset = 0;
	struct canvas_s canvas_work_y;
	struct canvas_s canvas_work_u;
	struct canvas_s canvas_work_v;
	struct vm_output_para output_para = vm_device[index]->output_para;

	if (!addr)
		return -1;
	mapping_wc = io_mapping_create_wc(vm_device[index]->buffer_start,
					  vm_device[index]->buffer_size);
	if (!mapping_wc)
		return -1;
	canvas_read(canvas & 0xff, &canvas_work_y);
	offset = 0;
	buffer_y_start = io_mapping_map_atomic_wc(mapping_wc, offset);
	if (!buffer_y_start) {
		pr_err(" vm.postprocess:mapping buffer error\n");
		io_mapping_free(mapping_wc);
		return -1;
	}
	if (output_para.v4l2_format == V4L2_PIX_FMT_BGR24 ||
	    output_para.v4l2_format == V4L2_PIX_FMT_RGB24 ||
	    output_para.v4l2_format == V4L2_PIX_FMT_RGB565X) {
		for (i = 0; i < output_para.height; i++) {
			memcpy((void *)(addr + poss),
			       (void *)(buffer_y_start + posd),
			       output_para.bytesperline);
			poss += output_para.bytesperline;
			posd += canvas_work_y.width;
		}
		io_mapping_unmap_atomic(buffer_y_start);

	} else if (output_para.v4l2_format == V4L2_PIX_FMT_NV12 ||
		   output_para.v4l2_format == V4L2_PIX_FMT_NV21) {
#ifdef GE2D_NV
		unsigned int uv_width = output_para.width;
		unsigned int uv_height = output_para.height >> 1;

		posd = 0;
		for (i = output_para.height; i > 0; i--) { /* copy y */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_y_start + posd),
			       output_para.width);
			poss += output_para.width;
			posd += canvas_work_y.width;
		}
		io_mapping_unmap_atomic(buffer_y_start);

		posd = 0;
		canvas_read((canvas >> 8) & 0xff, &canvas_work_u);
		offset = canvas_work_u.addr - canvas_work_y.addr;
		buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset);
		for (i = uv_height; i > 0; i--) { /* copy uv */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_u_start + posd), uv_width);
			poss += uv_width;
			posd += canvas_work_u.width;
		}

		io_mapping_unmap_atomic(buffer_u_start);
#else
		char *dst_buff = NULL;
		char *src_buff = NULL;
		char *src2_buff = NULL;

		canvas_read((canvas >> 8) & 0xff, &canvas_work_u);
		poss = 0;
		posd = 0;
		for (i = 0; i < output_para.height; i += 2) { /* copy y */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_y_start + posd),
			       output_para.width);
			poss += output_para.width;
			posd += canvas_work_y.width;
			memcpy((void *)(addr + poss),
			       (void *)(buffer_y_start + posd),
			       output_para.width);
			poss += output_para.width;
			posd += canvas_work_y.width;
		}
		io_mapping_unmap_atomic(buffer_y_start);

		posd = 0;
		canvas_read((canvas >> 16) & 0xff, &canvas_work_v);
		offset = canvas_work_u.addr - canvas_work_y.addr;
		buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset);
		offset = canvas_work_v.addr - canvas_work_y.addr;
		buffer_v_start = io_mapping_map_atomic_wc(mapping_wc, offset);

		dst_buff = (char *)addr +
			   output_para.width * output_para.height;
		src_buff = (char *)buffer_u_start;
		src2_buff = (char *)buffer_v_start;
		if (output_para.v4l2_format == V4L2_PIX_FMT_NV12) {
			for (i = 0 ; i < output_para.height / 2; i++) {
				interleave_uv(src_buff, src2_buff,
					      dst_buff, output_para.width / 2);
				src_buff +=  canvas_work_u.width;
				src2_buff +=    canvas_work_v.width;
				dst_buff += output_para.width;
			}
		} else {
			for (i = 0 ; i < output_para.height / 2; i++) {
				interleave_uv(src2_buff, src_buff,
					      dst_buff, output_para.width / 2);
				src_buff +=  canvas_work_u.width;
				src2_buff +=    canvas_work_v.width;
				dst_buff += output_para.width;
			}
		}

		io_mapping_unmap_atomic(buffer_u_start);
		io_mapping_unmap_atomic(buffer_v_start);
#endif
	} else if ((output_para.v4l2_format == V4L2_PIX_FMT_YUV420) ||
			(output_para.v4l2_format == V4L2_PIX_FMT_YVU420)) {
		int uv_width = output_para.width >> 1;
		int uv_height = output_para.height >> 1;

		posd = 0;
		for (i = output_para.height; i > 0; i--) { /* copy y */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_y_start + posd),
			       output_para.width);
			poss += output_para.width;
			posd += canvas_work_y.width;
		}
		io_mapping_unmap_atomic(buffer_y_start);

		posd = 0;
		canvas_read((canvas >> 8) & 0xff, &canvas_work_u);
		offset = canvas_work_u.addr - canvas_work_y.addr;
		buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset);

		canvas_read((canvas >> 16) & 0xff, &canvas_work_v);
		offset = canvas_work_v.addr - canvas_work_y.addr;
		buffer_v_start = io_mapping_map_atomic_wc(mapping_wc, offset);

#ifndef GE2D_NV
		if (output_para.v4l2_format == V4L2_PIX_FMT_YUV420)
#else
		if (output_para.v4l2_format == V4L2_PIX_FMT_YVU420) {
#endif
			for (i = uv_height; i > 0; i--) { /* copy y */
				memcpy((void *)(addr + poss),
				       (void *)(buffer_u_start + posd),
				       uv_width);
				poss += uv_width;
				posd += canvas_work_u.width;
			}
		posd = 0;
		for (i = uv_height; i > 0; i--) { /* copy y */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_v_start + posd),
			       uv_width);
			poss += uv_width;
			posd += canvas_work_v.width;
		}
	} else {
		for (i = uv_height; i > 0; i--) { /* copy v */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_v_start + posd),
			       uv_width);
			poss += uv_width;
			posd += canvas_work_v.width;
		}
		posd = 0;
		for (i = uv_height; i > 0; i--) { /* copy u */
			memcpy((void *)(addr + poss),
			       (void *)(buffer_u_start + posd),
			       uv_width);
			poss += uv_width;
			posd += canvas_work_u.width;
		}
	}
	io_mapping_unmap_atomic(buffer_u_start);
	io_mapping_unmap_atomic(buffer_v_start);
}

	if (mapping_wc)
		io_mapping_free(mapping_wc);

	return 0;
}

static struct task_struct *simulate_task_fd;

static bool is_vf_available(unsigned int vdin_id)
{
	bool ret = (local_vf_peek(vdin_id) ||
			(!vm_device[vdin_id]->task_running));
	return ret;
}

/* static int reset_frame = 1; */
static int vm_task(void *data)
{
	int ret = 0;
	struct vframe_s *vf;
	int src_canvas;
	struct vm_device_s *devp = (struct vm_device_s *)data;
	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 };
	struct ge2d_context_s *context = create_ge2d_work_queue();
	struct config_para_ex_s ge2d_config;

#ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES
	struct timeval start;
	struct timeval end;
	unsigned long time_use = 0;
#endif
	memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
	sched_setscheduler(current, SCHED_FIFO, &param);
	allow_signal(SIGTERM);
	while (1) {
		ret = wait_for_completion_interruptible(&devp->vb_start_sema);

		if (kthread_should_stop()) {
			complete(&devp->vb_done_sema);
			break;
		}

		/* wait for frame from 656 provider until 500ms runs out */
		wait_event_interruptible_timeout(devp->frame_ready,
						is_vf_available(devp->index),
						msecs_to_jiffies(2000));

		if (!devp->task_running) {
			ret = -1;
			goto vm_exit;
		}

		/* start to convert frame. */
#ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES
		do_gettimeofday(&start);
#endif
		vf = local_vf_get(devp->index);

		if (vf) {
			src_canvas = vf->canvas0Addr;

			/* step1 convert 422 format to other format.*/
			if (is_need_ge2d_pre_process(devp->output_para))
				src_canvas = vm_ge2d_pre_process(vf, context,
					 &ge2d_config, devp->output_para,
					 devp->index);

//			if (devp->dump == 2) {
//				vm_dump_mem(devp->dump_path,
//			    (void *)output_para.vaddr, &output_para);
//				devp->dump = 0;
//			}

			local_vf_put(vf, devp->index);
#ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES
			do_gettimeofday(&end);
			time_use = (end.tv_sec - start.tv_sec) * 1000 +
				   (end.tv_usec - start.tv_usec) / 1000;
			pr_debug("step 1, ge2d use: %ldms\n", time_use);
			do_gettimeofday(&start);
#endif

			/* step2 copy to user memory. */

			if (is_need_sw_post_process(devp->output_para))
				vm_sw_post_process(src_canvas,
				   devp->output_para.vaddr, devp->index);

#ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES
			do_gettimeofday(&end);
			time_use = (end.tv_sec - start.tv_sec) * 1000 +
				   (end.tv_usec - start.tv_usec) / 1000;
			pr_debug("step 2, memcpy use: %ldms\n", time_use);
#endif
		}
		if (kthread_should_stop()) {
			complete(&devp->vb_done_sema);
			break;
		}
		complete(&devp->vb_done_sema);
	}
vm_exit:
	destroy_ge2d_work_queue(context);
	while (!kthread_should_stop()) {
		/*may not call stop, wait..
		 *it is killed by SIGTERM,eixt on
		 *down_interruptible.
		 *if not call stop,this thread
		 *may on do_exit and
		 *kthread_stop may not work good;
		 */
		msleep(20);/*default 10ms*/
	}
	return ret;
}

/*simulate v4l2 device to request*/
/*filling buffer,only for test use*/
static int simulate_task(void *data)
{
	while (1) {
		msleep(50);
		pr_debug("simulate succeed\n");
	}
	return 0;
}

/*
 ***********************************************
 *
 *   init functions.
 *
 ***********************************************
 */

int alloc_vm_canvas(struct vm_device_s *vdevp)
{
	int j;

	if (vdevp->index == 0) {
		for (j = 0; j < MAX_CANVAS_INDEX; j++) {
			memset(&vdevp->vm_canvas[j], -1, sizeof(int));
			vdevp->vm_canvas[j] =
				canvas_pool_map_alloc_canvas("vm0");
			if (vdevp->vm_canvas[j] < 0) {
				pr_err("alloc vm0_canvas %d failed\n", j);
				return -1;
			}
		}
	} else {
		for (j = 0; j < MAX_CANVAS_INDEX; j++) {
			memset(&vdevp->vm_canvas[j], -1, sizeof(int));
			vdevp->vm_canvas[j] =
				canvas_pool_map_alloc_canvas("vm1");
			if (vdevp->vm_canvas[j] < 0) {
				pr_err("alloc vm1_canvas %d failed\n", j);
				return -1;
			}
		}
	}

	return 0;
}

int vm_buffer_init(struct vm_device_s *vdevp)
{
	int i;
	u32 canvas_width, canvas_height;
	u32 decbuf_size;
	resource_size_t buf_start;
	unsigned int buf_size;
	int buf_num = 0;
	int local_pool_size = 0;

	init_completion(&vdevp->vb_start_sema);
	init_completion(&vdevp->vb_done_sema);

	buf_start = vdevp->buffer_start;
	buf_size = vdevp->buffer_size;

	if (!buf_start || !buf_size)
		goto exit;

	for (i = 0; i < ARRAY_SIZE(vmdecbuf_size); i++) {
		if (buf_size >= vmdecbuf_size[i])
			break;
	}
	if (i == ARRAY_SIZE(vmdecbuf_size)) {
		pr_debug("vmbuf size=%d less than the smallest vmbuf size%d\n",
			 buf_size, vmdecbuf_size[i - 1]);
		return -1;
	}

	canvas_width = canvas_config_wh[i].width;/* 1920; */
	canvas_height = canvas_config_wh[i].height;/* 1200; */
	decbuf_size = vmdecbuf_size[i];/* 0x700000; */
	buf_num  = buf_size / decbuf_size;

	if (buf_num > 0) {
		local_pool_size   = 1;
	} else {
		local_pool_size = 0;
		pr_debug("need at least one buffer to handle 1920*1080 data.\n");
	}

	for (i = 0; i < local_pool_size; i++) {
		canvas_config((vdevp->vm_canvas[0] + i),
			      (unsigned long)(buf_start + i * decbuf_size),
			      canvas_width * 2, canvas_height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((vdevp->vm_canvas[2] + i),
			      (unsigned long)(buf_start + i * decbuf_size),
			      canvas_width * 3, canvas_height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((vdevp->vm_canvas[4] + i),
			      (unsigned long)(buf_start + i * decbuf_size / 2),
			      canvas_width, canvas_height,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config(vdevp->vm_canvas[6] + i,
			      (unsigned long)(buf_start +
					      (i + 1) * decbuf_size / 2),
			      canvas_width, canvas_height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);

		canvas_config((vdevp->vm_canvas[8] + i),
			      (unsigned long)(buf_start +
					      (i + 1) * decbuf_size / 2),
			      canvas_width / 2, canvas_height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
		canvas_config((vdevp->vm_canvas[10] + i),
			      (unsigned long)(buf_start +
					      (i + 3) * decbuf_size / 4),
			      canvas_width / 2, canvas_height / 2,
			      CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
		vfbuf_use[i] = 0;
#endif
	}

exit:
	return 0;
}

int start_vm_task(struct vm_device_s *vdevp)
{
	/* init the device. */
	vm_local_init();
	if (!vdevp->task) {
		vdevp->task = kthread_create(vm_task, vdevp, vdevp->name);
		if (IS_ERR(vdevp->task)) {
			pr_err("thread creating error.\n");
			return -1;
		}
		init_waitqueue_head(&vdevp->frame_ready);
		wake_up_process(vdevp->task);
	}
	vdevp->task_running = 1;
	return 0;
}

int start_simulate_task(void)
{
	if (!simulate_task_fd) {
		simulate_task_fd = kthread_create(simulate_task, 0, "vm");
		if (IS_ERR(simulate_task_fd)) {
			pr_err("thread creating error.\n");
			return -1;
		}
		wake_up_process(simulate_task_fd);
	}
	return 0;
}

void stop_vm_task(struct vm_device_s *vdevp)
{
	if (vdevp->task) {
		vdevp->task_running = 0;
		send_sig(SIGTERM, vdevp->task, 1);
		complete(&vdevp->vb_start_sema);
		wake_up_interruptible(&vdevp->frame_ready);
		kthread_stop(vdevp->task);
		vdevp->task = NULL;
	}
	vm_local_init();
}

/*
 **********************************************************************
 *
 * global status.
 *
 **********************************************************************
 */

static int vm_enable_flag;

int get_vm_status(void)
{
	return vm_enable_flag;
}

void set_vm_status(int flag)
{
	if (flag >= 0)
		vm_enable_flag = flag;
	else
		vm_enable_flag = 0;
}

/*
 **********************************************************************
 *
 * file op section.
 *
 **********************************************************************
 */

void unset_vm_buf_res(struct vm_init_s *info)
{
	info->buffer_start = 0;
}

static void vm_cache_this_flush(unsigned int buf_start, unsigned int buf_size,
				struct vm_init_s *info)
{
	struct vm_device_s *devp = vm_device[info->vdin_id];

	if (devp->dev) {
		if (buf_start >= info->buffer_start &&
		    ((buf_start + buf_size) <= (info->buffer_start +
						info->vm_buf_size)))
			dma_sync_single_for_cpu(devp->dev,
				buf_start, buf_size, DMA_FROM_DEVICE);
	}
}

static int vm_open(struct inode *inode, struct file *file)
{
	struct ge2d_context_s *context = NULL;

	pr_info("open one vm device\n");
	file->private_data = context;
	/* vm_device.open_count++; */
	return 0;
}

static long vm_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
	int  ret = 0;
	struct ge2d_context_s *context;
	void  __user *argp;

	context = (struct ge2d_context_s *)filp->private_data;
	argp = (void __user *)args;
	switch (cmd) {
	case VM_IOC_2OSD0:
		break;
	case VM_IOC_ENABLE_PP:
		break;
	case VM_IOC_CONFIG_FRAME:
		break;
	default:
		return -ENOIOCTLCMD;
	}
	return ret;
}

static int vm_release(struct inode *inode, struct file *file)
{
	struct ge2d_context_s *context =
		(struct ge2d_context_s *)file->private_data;

	if (context && (destroy_ge2d_work_queue(context) == 0)) {
		/* vm_device.open_count--; */
		return 0;
	}
	pr_info("release one vm device\n");
	return -1;
}

/*
 **********************************************************************
 *
 * file op init section.
 *
 **********************************************************************
 */

static const struct file_operations vm_fops = {
	.owner = THIS_MODULE,
	.open = vm_open,
	.unlocked_ioctl = vm_ioctl,
	.release = vm_release,
};

static ssize_t vm_attr_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	ssize_t len = 0;
	struct vm_device_s *devp;

	devp = dev_get_drvdata(dev);
	if (devp->task_running == 0) {
		len += sprintf(buf + len, "vm does not start\n");
		return len;
	}

	len += sprintf((char *)buf + len, "vm parameters below\n");

	return len;
}

static ssize_t vm_attr_store(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t len)
{
	struct vm_device_s *devp;
	unsigned int n = 0;
	char *buf_orig, *ps, *token;
	char *parm[6] = {NULL};

	if (!buf)
		return len;

	buf_orig = kstrdup(buf, GFP_KERNEL);
	/* printk(KERN_INFO "input cmd : %s",buf_orig); */
	devp = dev_get_drvdata(dev);
	if (devp->task_running == 0) {
		len += sprintf((char *)buf + len, "vm does not start\n");
		return len;
	}

	ps = buf_orig;
	while (1) {
		if (n >= ARRAY_SIZE(parm)) {
			pr_err("parm array overflow.\n");

//			pr_err("n=%d,ARRAY_SIZE(parm)=%d\n",
//			       n, ARRAY_SIZE(parm));
			return len;
		}
		token = strsep(&ps, "\n");
		if (!token)
			break;
		if (*token == '\0')
			continue;
		parm[n++] = token;
	}

	if (strcmp(parm[0], "before") == 0) {
		devp->dump = 1;
		devp->dump_path = parm[1];
		pr_debug("this not support\n");
	} else if (strcmp(parm[0], "after") == 0) {
		devp->dump = 2;
		devp->dump_path = parm[1];
		pr_debug("after ge2d processed, store to %s\n", parm[1]);
	}

	kfree(buf_orig);
	return len;
}

static DEVICE_ATTR_RW(vm_attr);

static int vm_add_cdev(struct cdev *cdevp,
		       const struct file_operations *fops,
		       int minor)
{
	int ret;
	dev_t devno = MKDEV(MAJOR(vm_devno), minor);

	cdev_init(cdevp, fops);
	cdevp->owner = THIS_MODULE;
	ret = cdev_add(cdevp, devno, 1);
	return ret;
}

int init_vm_device(struct vm_device_s *vdevp, struct platform_device *pdev)
{
	int  ret = 0;

	ret = vm_add_cdev(&vdevp->cdev, &vm_fops, vdevp->index);
	if (ret) {
		pr_err("%s: failed to add cdev. !\n", __func__);
		goto fail_add_cdev;
	}
	vdevp->dev = device_create(vm_clsp, &pdev->dev, MKDEV(MAJOR(vm_devno),
				   vdevp->index),
				   NULL, "%s%d", VM_DEV_NAME, vdevp->index);
	if (IS_ERR(vdevp->dev)) {
		pr_err("%s: failed to create device. !\n", __func__);
		ret = PTR_ERR(vdevp->dev);
		goto fail_create_device;
	}
//	ret = device_create_file(vdevp->dev, &dev_attr_dump);
	ret = device_create_file(vdevp->dev, &dev_attr_vm_attr);

	if (ret < 0) {
		pr_err("%s: fail to create vdin attribute files.\n", __func__);
		goto fail_create_dev_file;
	}

	dev_set_drvdata(vdevp->dev, vdevp);
	platform_set_drvdata(pdev, vdevp);

	/* if (vm_buffer_init(vdevp) < 0) */
	/* goto unregister_dev; */
#ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER
	vf_provider_init(&vm_vf_prov, PROVIDER_NAME, &vm_vf_provider, NULL);
#endif
	/* vf_reg_provider(&vm_vf_prov); */
	vdevp->task = NULL;
	vdevp->task_running = 0;
	memset(&vdevp->output_para, 0, sizeof(struct vm_output_para));
	sprintf(vdevp->name, "%s%d", RECEIVER_NAME, vdevp->index);
	sprintf(vdevp->vf_provider_name, "%s%d", "vdin", vdevp->index);

	snprintf(vdevp->vfm_map_chain, VM_MAP_NAME_SIZE,
				"%s %s", vdevp->vf_provider_name,
				vdevp->name);
	snprintf(vdevp->vfm_map_id, VM_MAP_NAME_SIZE,
				"vm-map-%d", vdevp->index);
	if (vfm_map_add(vdevp->vfm_map_id,
		vdevp->vfm_map_chain) < 0) {
		pr_err("vm pipeline map creation failed %s.\n",
			vdevp->vfm_map_id);
		goto fail_create_dev_file;
	}

	vf_receiver_init(&vdevp->vm_vf_recv, vdevp->name,
			&vm_vf_receiver, vdevp);

	vf_reg_receiver(&vdevp->vm_vf_recv);
	return 0;

	/* unregister_dev: */
fail_create_dev_file:
	device_destroy(vm_clsp, MKDEV(MAJOR(vm_devno), vdevp->index));
fail_create_device:
	cdev_del(&vdevp->cdev);
fail_add_cdev:
	kfree(vdevp);
	return ret;
}

int uninit_vm_device(struct platform_device *plat_dev)
{
	struct vm_device_s *vdevp;

	vdevp = platform_get_drvdata(plat_dev);
	/* stop_vm_task(vdevp); */
	device_remove_file(vdevp->dev, &dev_attr_vm_attr);
	device_destroy(vm_clsp, MKDEV(MAJOR(vm_devno), vdevp->index));
	cdev_del(&vdevp->cdev);
	vm_device[vdevp->index] = NULL;

	/* free drvdata */
	dev_set_drvdata(vdevp->dev, NULL);
	platform_set_drvdata(plat_dev, NULL);
	kfree(vdevp);
	return 0;
}

#if defined(CONFIG_CMA) && defined(MODULE)
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
				       unsigned int align, bool no_warn)
{
	if (align > CONFIG_CMA_ALIGNMENT)
		align = CONFIG_CMA_ALIGNMENT;

	return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
}

bool dma_release_from_contiguous(struct device *dev, struct page *pages,
				 int count)
{
	return cma_release(dev_get_cma_area(dev), pages, count);
}
#endif

int vm_init_resource(size_t size, struct vm_init_s *info)
{
	struct vm_device_s *devp;
#ifdef CONFIG_CMA
	devp = vm_device[info->vdin_id];
	if (size == 0)
		return -1;
	if (info->vm_pages && info->vm_buf_size != 0) {
		dma_release_from_contiguous(&devp->pdev->dev,
					    info->vm_pages,
					    info->vm_buf_size / PAGE_SIZE);
	}
	info->vm_pages = dma_alloc_from_contiguous(&devp->pdev->dev,
			 size / PAGE_SIZE, 0, 0);
	if (info->vm_pages) {
		dma_addr_t phys;

		phys = page_to_phys(info->vm_pages);
		info->buffer_start = phys;
		info->vm_buf_size = size;
		if (info->bt_path_count == 1) {
			if (info->vdin_id == 0)
				info->isused = true;
			else
				info->isused = false;
		} else {
			info->isused = true;
		}
		info->mem_alloc_succeed = true;
		return 0;
	}

	info->mem_alloc_succeed = false;
	pr_err("CMA failed to allocate dma buffer\n");
	return -ENOMEM;
#else
	if (size == 0)
		return -1;
	info->buffer_start = vm_device[info->vdin_id]->buffer_start;
	info->vm_buf_size = vm_device[info->vdin_id]->buffer_size;
	info->mem_alloc_succeed = true;
	info->isused = true;
	return 0;
#endif
}
EXPORT_SYMBOL(vm_init_resource);

void vm_deinit_resource(struct vm_init_s *info)
{
#ifdef CONFIG_CMA
	struct vm_device_s *devp;

	devp = vm_device[info->vdin_id];
	if (info->vm_buf_size == 0) {
		pr_warn("vm buf size equals 0\n");
		return;
	}
	unset_vm_buf_res(info);

	if (info->vm_pages) {
		dma_release_from_contiguous(&devp->pdev->dev,
					    info->vm_pages,
					    info->vm_buf_size / PAGE_SIZE);
		info->vm_buf_size = 0;
		info->vm_pages = NULL;
		info->isused = false;
		info->mem_alloc_succeed = false;
	}
#else
	if (info->vm_buf_size) {
		info->buffer_start = 0;
		info->vm_buf_size = 0;
		info->isused = false;
		info->mem_alloc_succeed = false;
	}
#endif
}
EXPORT_SYMBOL(vm_deinit_resource);

/*******************************************************************
 *
 * interface for Linux driver
 *
 * ******************************************************************/

static int vm_mem_device_init(struct reserved_mem *rmem, struct device *dev)
{
	struct platform_device *pdev = container_of(dev,
					struct platform_device, dev);
	struct vm_device_s *vdevp = platform_get_drvdata(pdev);
	unsigned long mem_start, mem_end;

	vdevp->buffer_start = rmem->base;
	vdevp->buffer_size = rmem->size;
	mem_start = rmem->base;
	mem_end = rmem->base + rmem->size - 1;
	pr_info("init vm memsource %lx->%lx\n", mem_start, mem_end);
	return 0;
}

static const struct reserved_mem_ops rmem_vm_ops = {
	.device_init = vm_mem_device_init,
};

static int __init vm_mem_setup(struct reserved_mem *rmem)
{
	rmem->ops = &rmem_vm_ops;
	pr_info("vm share mem setup\n");
	return 0;
}

/* MODULE_AMLOG(AMLOG_DEFAULT_LEVEL, 0xff, LOG_LEVEL_DESC, LOG_MASK_DESC); */

/* for driver. */
static int vm_driver_probe(struct platform_device *pdev)
{
	int idx;
	int ret;
	struct vm_device_s *vdevp;

	pr_info("vm memory init\n");
	/* malloc vdev */
	vdevp = kmalloc(sizeof(*vdevp), GFP_KERNEL);
	if (!vdevp)
		return -ENOMEM;

	memset(vdevp, 0, sizeof(*vdevp));

	if (pdev->dev.of_node) {
		ret = of_property_read_u32(pdev->dev.of_node,
					   "vm_id", &vdevp->index);
		if (ret)
			pr_err("don't find vm id.\n");
	}
	vm_device[vdevp->index] = vdevp;
	vdevp->pdev = pdev;
	platform_set_drvdata(pdev, vdevp);
	ret = alloc_vm_canvas(vdevp);
	if (ret != 0) {
		pr_err("alloc vm canvas failed\n");
		return ret;
	}
	vm_buffer_init(vdevp);
	ret = init_vm_device(vdevp, pdev);
	if (ret != 0)
		return ret;
	idx = of_reserved_mem_device_init(&pdev->dev);
	if (idx == 0)
		pr_info("vm probe done\n");
	else
		pr_info("malloc reserved memory failed !\n");

	/* init_vm_device(vdevp, pdev); */
	return 0;
}

static int vm_drv_remove(struct platform_device *plat_dev)
{
	int i;
	struct vm_device_s *vdevp;

	vdevp = platform_get_drvdata(plat_dev);

	for (i = 0; i < MAX_CANVAS_INDEX; i++) {
		if (vdevp &&
			vdevp->vm_canvas[i] >= 0) {
			canvas_pool_map_free_canvas(vdevp->vm_canvas[i]);
			memset(&vdevp->vm_canvas[i],
				-1, sizeof(int));
		}
	}

	if (vdevp->vfm_map_id[0]) {
		vfm_map_remove(vdevp->vfm_map_id);
		vdevp->vfm_map_id[0] = 0;
	}

	uninit_vm_device(plat_dev);
	return 0;
}

static const struct of_device_id amlogic_vm_dt_match[] = {
	{
		.compatible = "amlogic, vm",
	},
	{},
};

/* general interface for a linux driver .*/
static struct platform_driver vm_drv = {
	.probe  = vm_driver_probe,
	.remove = vm_drv_remove,
	.driver = {
		.name = "vm",
		.owner = THIS_MODULE,
		.of_match_table = amlogic_vm_dt_match,
	}
};

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

	pr_info("vm_init .\n");
	ret = alloc_chrdev_region(&vm_devno, 0, VM_MAX_DEVS, VM_DEV_NAME);
	if (ret < 0) {
		pr_err("%s: failed to allocate major number\n", __func__);
		goto fail_alloc_cdev_region;
	}
	vm_clsp = class_create(THIS_MODULE, VM_CLS_NAME);
	if (IS_ERR(vm_clsp)) {
		ret = PTR_ERR(vm_clsp);
		pr_err("%s: failed to create class\n", __func__);
		goto fail_class_create;
	}
	ret = platform_driver_register(&vm_drv);
	if (ret) {
		pr_err("Failed to register vm driver (error=%d\n",
		       ret);
		goto fail_pdrv_register;
	}
	return 0;

fail_pdrv_register:
	class_destroy(vm_clsp);
fail_class_create:
	unregister_chrdev_region(vm_devno, VM_MAX_DEVS);
fail_alloc_cdev_region:
	return ret;
}

void __exit
vm_remove_module(void)
{
	/* class_remove_file(vm_clsp, &class_attr_dump); */
	class_destroy(vm_clsp);
	unregister_chrdev_region(vm_devno, VM_MAX_DEVS);
	platform_driver_unregister(&vm_drv);
	pr_info("vm module removed.\n");
}

//module_init(vm_init_module);
//module_exit(vm_remove_module);
RESERVEDMEM_OF_DECLARE(vm, "amlogic, vm_memory", vm_mem_setup);

//MODULE_DESCRIPTION("Amlogic Video Input Manager");
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Simon Zheng <simon.zheng@amlogic.com>");
