// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#include <linux/types.h>
#include <linux/sync_file.h>
#include <linux/dma-fence.h>
#include <linux/dma-buf.h>
#include <uapi/linux/dma-buf.h>
#include <uapi/linux/magic.h>
#ifdef CONFIG_AMLOGIC_MEDIA_FB
#include <linux/amlogic/media/osd/osd_logo.h>
#endif

#include "meson_plane.h"
#include "meson_crtc.h"
#include "meson_vpu.h"
#include "meson_drv.h"
#include "meson_vpu_pipeline.h"
#include "meson_osd_afbc.h"
#include "meson_gem.h"
#include "meson_logo.h"


static u64 afbc_modifier[] = {
	/*
	 * - TOFIX Support AFBC modifiers for YUV formats (16x16 + TILED)
	 * - SPLIT is mandatory for performances reasons when in 16x16
	 *   block size
	 * - 32x8 block size + SPLIT is mandatory with 4K frame size
	 *   for performances reasons
	 */
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
				AFBC_FORMAT_MOD_YTR |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
				AFBC_FORMAT_MOD_TILED |
				AFBC_FORMAT_MOD_SC |
				AFBC_FORMAT_MOD_YTR |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
				AFBC_FORMAT_MOD_YTR |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
				AFBC_FORMAT_MOD_TILED |
				AFBC_FORMAT_MOD_SC |
				AFBC_FORMAT_MOD_YTR |
				AFBC_FORMAT_MOD_SPARSE |
				AFBC_FORMAT_MOD_SPLIT),
	DRM_FORMAT_MOD_LINEAR,
	DRM_FORMAT_MOD_INVALID
};

static const u32 supported_drm_formats[] = {
	DRM_FORMAT_ABGR2101010,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_RGBX8888,
	DRM_FORMAT_BGRX8888,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_RGBA8888,
	DRM_FORMAT_BGRA8888,
	DRM_FORMAT_RGB888,
	DRM_FORMAT_RGB565,
};

static const u32 supported_drm_formats_v2[] = {
	DRM_FORMAT_RGBA1010102,
	DRM_FORMAT_ARGB2101010,
	DRM_FORMAT_ABGR2101010,
	DRM_FORMAT_BGRA1010102,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_RGBX8888,
	DRM_FORMAT_BGRX8888,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_RGBA8888,
	DRM_FORMAT_BGRA8888,
	DRM_FORMAT_RGB888,
	DRM_FORMAT_RGB565,
};

static const u32 supported_drm_formats_v3[] = {
	DRM_FORMAT_RGBA1010102,
	DRM_FORMAT_ARGB2101010,
	DRM_FORMAT_ABGR2101010,
	DRM_FORMAT_BGRA1010102,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_RGBX8888,
	DRM_FORMAT_BGRX8888,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_RGBA8888,
	DRM_FORMAT_BGRA8888,
	DRM_FORMAT_RGB888,
	DRM_FORMAT_ABGR4444,
	DRM_FORMAT_ARGB4444,
	DRM_FORMAT_BGRA4444,
	DRM_FORMAT_RGBA4444,
	DRM_FORMAT_RGB565,
};

static const u32 supported_drm_formats_v4[] = {
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_RGBX8888,
	DRM_FORMAT_BGRX8888,
	DRM_FORMAT_RGB565,
	DRM_FORMAT_RGBA4444,
	DRM_FORMAT_ARGB4444,
	DRM_FORMAT_RGBA5551,
	DRM_FORMAT_ARGB1555,
	DRM_FORMAT_RGB888,
	DRM_FORMAT_BGR888,
	DRM_FORMAT_RGBA8888,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_BGRA8888,
	DRM_FORMAT_RGBA1010102,
	DRM_FORMAT_ABGR2101010,
	DRM_FORMAT_C8,
};

static u64 video_fbc_modifier[] = {
	DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, 0),
	DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC,
		AMLOGIC_FBC_OPTION_MEM_SAVING),
	DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, 0),
	DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER,
		AMLOGIC_FBC_OPTION_MEM_SAVING),
	DRM_FORMAT_MOD_LINEAR,
	DRM_FORMAT_MOD_INVALID
};

static const u32 video_supported_drm_formats[] = {
	DRM_FORMAT_NV12,
	DRM_FORMAT_NV21,
	DRM_FORMAT_UYVY,
	DRM_FORMAT_VUY888,
};

#if IS_ENABLED(CONFIG_SYNC_FILE)
int am_meson_dmabuf_export_sync_file_ioctl(struct drm_device *dev,
	void *data, struct drm_file *file_priv)
{
	struct drm_meson_dma_buf_export_sync_file *arg = data;
	struct dma_fence *fence = NULL;
	struct dma_buf *dmabuf = NULL;
	struct sync_file *sync_file;
	int fd, ret;

	DRM_DEBUG("dmabuf-%px fence-%px", dmabuf, fence);
	if (arg->flags & ~DMA_BUF_SYNC_RW)
		return -EINVAL;

	if ((arg->flags & DMA_BUF_SYNC_RW) == 0)
		return -EINVAL;

	fd = get_unused_fd_flags(O_CLOEXEC);
	if (fd < 0)
		return fd;
	if (arg->dmabuf_fd <= 0)
		return -EINVAL;

	dmabuf = dma_buf_get(arg->dmabuf_fd);
	if (IS_ERR_OR_NULL(dmabuf))
		return -EINVAL;

	if (arg->flags & DMA_BUF_SYNC_WRITE) {
		//fence = dma_resv_get_singleton_unlocked(dmabuf->resv);
		//if (IS_ERR(fence)) {
		//	ret = PTR_ERR(fence);
		//	goto err_put_fd;
		//}
	} else if (arg->flags & DMA_BUF_SYNC_READ) {
		fence = dma_resv_get_excl_unlocked(dmabuf->resv);
	}
	if (!fence)
		fence = dma_fence_get_stub();

	sync_file = sync_file_create(fence);

	dma_fence_put(fence);
	dma_buf_put(dmabuf);

	if (!sync_file) {
		ret = -ENOMEM;
		goto err_put_fd;
	}

	fd_install(fd, sync_file->file);

	arg->fd = fd;
	DRM_DEBUG("dmabuf-%px fence-%px fd-%d", dmabuf, fence, fd);
	return 0;

err_put_fd:
	put_unused_fd(fd);
	return ret;
}
#endif

static void
meson_plane_position_calc(struct meson_vpu_osd_layer_info *plane_info,
			  struct drm_plane_state *state,
			  struct meson_vpu_pipeline *pipeline)
{
	u32 dst_w, dst_h, src_w, src_h, scan_mode_out;
	struct am_osd_plane *amp;
	struct drm_atomic_state *atomic_state = state->state;
	struct drm_crtc *crtc = state->crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_display_mode *mode;

	if (crtc) {
		crtc_state = drm_atomic_get_crtc_state(atomic_state, crtc);
		mode = &crtc_state->mode;
	} else {
		DRM_DEBUG("Disabling plane %d, so skip postion calc",
			 plane_info->plane_index);
		return;
	}

	if (!crtc_state || !mode)
		mode = &pipeline->subs[crtc->index].mode;
	scan_mode_out = mode->flags & DRM_MODE_FLAG_INTERLACE;
	plane_info->src_x = state->src_x >> 16;
	plane_info->src_y = state->src_y >> 16;
	plane_info->src_w = (state->src_w >> 16) & 0xffff;
	plane_info->src_h = (state->src_h >> 16) & 0xffff;
	DRM_DEBUG("original source: src_x=%d, src_y=%d, src_w=%d, src_h=%d\n",
		plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h);

	plane_info->dst_x = state->crtc_x;
	plane_info->dst_y = state->crtc_y;
	plane_info->dst_w = state->crtc_w;
	plane_info->dst_h = state->crtc_h;
	plane_info->rotation = state->rotation;
	DRM_DEBUG("original destination: dst_x=%d, dst_y=%d, dst_w=%d, dst_h=%d\n",
		plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h);
	if (state->plane) {
		amp = to_am_osd_plane(state->plane);
		if (plane_info->rotation != amp->osd_reverse)
			plane_info->rotation = amp->osd_reverse;
		if (plane_info->blend_bypass != amp->osd_blend_bypass)
			plane_info->blend_bypass = amp->osd_blend_bypass;
		if (plane_info->read_ports != amp->osd_read_ports)
			plane_info->read_ports = amp->osd_read_ports;
		else
			plane_info->read_ports = 2;
	}
	if (scan_mode_out) {
		plane_info->dst_y >>= 1;
		plane_info->dst_h >>= 1;
	}
	/*negative position process*/
	if (state->crtc_x < 0) {
		dst_w = state->crtc_w + state->crtc_x;
		if (dst_w > 0) {
			src_w = plane_info->src_w * dst_w / state->crtc_w;
			plane_info->src_x = plane_info->src_w - src_w;
			plane_info->src_w = src_w;
			plane_info->dst_w = dst_w;
			plane_info->dst_x = 0;
		} else {
			plane_info->enable = 0;
		}
		DRM_DEBUG("state->crtc_x < 0\n");
	}
	if (state->crtc_y < 0) {
		dst_h = state->crtc_h + state->crtc_y;
		if (dst_h > 0) {
			src_h = plane_info->src_h * dst_h / state->crtc_h;
			plane_info->src_y = plane_info->src_h - src_h;
			plane_info->src_h = src_h;
			plane_info->dst_h = dst_h;
			plane_info->dst_y = 0;
		} else {
			plane_info->enable = 0;
		}
		DRM_DEBUG("state->crtc_y < 0\n");
	}
	/*overdisplay process*/
	if ((plane_info->dst_x + plane_info->dst_w) > mode->hdisplay) {
		if (plane_info->dst_x >= mode->hdisplay) {
			plane_info->enable = 0;
		} else {
			dst_w = plane_info->dst_w;
			src_w = plane_info->src_w;
			plane_info->dst_w =
				mode->hdisplay - plane_info->dst_x;
			plane_info->src_w =
				(src_w * plane_info->dst_w) / dst_w;
		}
		DRM_DEBUG("(plane_info->dst_x + plane_info->dst_w) > mode->hdisplay\n");
	}
	if ((plane_info->dst_y + plane_info->dst_h) > mode->vdisplay) {
		if (plane_info->dst_y >= mode->vdisplay) {
			plane_info->enable = 0;
		} else {
			dst_h = plane_info->dst_h;
			src_h = plane_info->src_h;
			plane_info->dst_h =
				mode->vdisplay - plane_info->dst_y;
			plane_info->src_h =
				(src_h * plane_info->dst_h) / dst_h;
		}
		DRM_DEBUG("(plane_info->dst_y + plane_info->dst_h) > mode->vdisplay\n");
	}
	/*reverse process*/
	if (plane_info->rotation & DRM_MODE_REFLECT_X)
		plane_info->dst_x = mode->hdisplay - plane_info->dst_w -
			plane_info->dst_x;
	if (plane_info->rotation & DRM_MODE_REFLECT_Y)
		plane_info->dst_y = mode->vdisplay - plane_info->dst_h -
			plane_info->dst_y;
	DRM_DEBUG("fini source: src_x=%d, src_y=%d, src_w=%d, src_h=%d\n",
		plane_info->src_x, plane_info->src_y,
		plane_info->src_w, plane_info->src_h);
	DRM_DEBUG("fini destination: dst_x=%d, dst_y=%d, dst_w=%d, dst_h=%d\n",
		plane_info->dst_x, plane_info->dst_y,
		plane_info->dst_w, plane_info->dst_h);

}

static void
meson_video_plane_position_calc(struct meson_vpu_video_layer_info *plane_info,
				struct drm_plane_state *state,
				struct meson_vpu_pipeline *pipeline)
{
	u32 dst_w, dst_h, src_w, src_h;
	struct drm_atomic_state *atomic_state = state->state;
	struct drm_crtc *crtc = state->crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_display_mode *mode;

	if (crtc) {
		crtc_state = drm_atomic_get_crtc_state(atomic_state, crtc);
		mode = &crtc_state->mode;
	} else {
		DRM_DEBUG("Disabling video plane %d, so skip postion calc",
			 plane_info->plane_index);
		return;
	}

	if (!crtc_state || !mode)
		mode = &pipeline->subs[crtc->index].mode;
	plane_info->src_x = state->src_x >> 16;
	plane_info->src_y = state->src_y >> 16;
	plane_info->src_w = (state->src_w >> 16) & 0xffff;
	plane_info->src_h = (state->src_h >> 16) & 0xffff;

	plane_info->dst_x = state->crtc_x;
	plane_info->dst_y = state->crtc_y;
	plane_info->dst_w = state->crtc_w;
	plane_info->dst_h = state->crtc_h;
	/*negative position process*/
	if (state->crtc_x < 0) {
		dst_w = state->crtc_w + state->crtc_x;
		if (dst_w > 0) {
			src_w = plane_info->src_w * dst_w / state->crtc_w;
			plane_info->src_x = plane_info->src_w - src_w;
			plane_info->src_w = src_w;
			plane_info->dst_w = dst_w;
			plane_info->dst_x = 0;
		} else {
			plane_info->enable = 0;
		}
	}
	if (state->crtc_y < 0) {
		dst_h = state->crtc_h + state->crtc_y;
		if (dst_h > 0) {
			src_h = plane_info->src_h * dst_h / state->crtc_h;
			plane_info->src_y = plane_info->src_h - src_h;
			plane_info->src_h = src_h;
			plane_info->dst_h = dst_h;
			plane_info->dst_y = 0;
		} else {
			plane_info->enable = 0;
		}
	}
	/*overdisplay process*/
	if ((plane_info->dst_x + plane_info->dst_w) > mode->hdisplay) {
		if (plane_info->dst_x >= mode->hdisplay)
			plane_info->enable = 0;
		else
			plane_info->dst_w =
				mode->hdisplay - plane_info->dst_x;
	}
	if ((plane_info->dst_y + plane_info->dst_h) > mode->vdisplay) {
		if (plane_info->dst_y >= mode->vdisplay)
			plane_info->enable = 0;
		else
			plane_info->dst_h = mode->vdisplay - plane_info->dst_y;
	}
	DRM_DEBUG("mode->hdisplay=%d, mode->vdisplay=%d\n",
		  mode->hdisplay, mode->vdisplay);
}

static int
meson_plane_check_size_range(struct meson_vpu_osd_layer_info *plane_info)
{
	u32 dst_w, dst_h, src_w, src_h, ratio_x, ratio_y;
	int ret;

	src_w = plane_info->src_w;
	src_h = plane_info->src_h;
	dst_w = plane_info->dst_w;
	dst_h = plane_info->dst_h;
	ratio_x = 0;
	ratio_y = 0;
	ret = 0;

	if (src_w > dst_w)
		ratio_x = (src_w + dst_w - 1) / dst_w;
	if (src_h > dst_h)
		ratio_y = (src_h + dst_h - 1) / dst_h;
	if (ratio_x > MESON_OSD_SCALE_DOWN_LIMIT ||
	    ratio_y > MESON_OSD_SCALE_DOWN_LIMIT)
		ret = -EDOM;
	if (src_w < dst_w)
		ratio_x = (dst_w + src_w - 1) / src_w;
	if (src_h < dst_h)
		ratio_y = (dst_h + src_h - 1) / src_h;
	if (ratio_x > MESON_OSD_SCALE_UP_LIMIT ||
	    ratio_y > MESON_OSD_SCALE_UP_LIMIT)
		ret = -EDOM;
	return ret;
}

static int meson_plane_fb_check(struct drm_plane *plane,
				struct drm_plane_state *new_state,
				struct meson_vpu_osd_layer_info *plane_info)
{
	struct drm_framebuffer *fb = new_state->fb;
	#ifdef CONFIG_AMLOGIC_DRM_USE_ION
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct meson_drm *drv = osd_plane->drv;
	struct am_meson_fb *meson_fb;
	#else
	struct drm_gem_cma_object *gem;
	#endif
	size_t fb_size = 0;
	phys_addr_t phyaddr;

	#ifdef CONFIG_AMLOGIC_DRM_USE_ION
	meson_fb = container_of(fb, struct am_meson_fb, base);
	if (!meson_fb)
		return -EINVAL;

	DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%p\n",
		  meson_fb->base.base.id,
		  kref_read(&meson_fb->base.base.refcount), meson_fb);
	if (meson_fb->logo && meson_fb->logo->alloc_flag &&
	    meson_fb->logo->start) {
		phyaddr = meson_fb->logo->start;
		fb_size = meson_fb->logo->size;
		DRM_DEBUG("logo->phyaddr=0x%pa\n", &phyaddr);
	} else  if (meson_fb->bufp[0]) {
		phyaddr = am_meson_gem_object_get_phyaddr(drv,
							  meson_fb->bufp[0],
							  &fb_size);
	} else {
		phyaddr = 0;
		DRM_INFO("don't find phyaddr!\n");
		return -EINVAL;
	}
	#else
	if (!fb)
		return -EINVAL;

	/* Update Canvas with buffer address */
	gem = drm_fb_cma_get_gem_obj(fb, 0);
	if (!gem) {
		DRM_INFO("gem is NULL!\n");
		return -EINVAL;
	}
	phyaddr = gem->paddr;
	#endif
	plane_info->phy_addr = phyaddr;
	plane_info->fb_size = (u32)fb_size;
	return 0;
}

static int meson_video_plane_fb_check(struct drm_plane *plane,
				      struct drm_plane_state *new_state,
				struct meson_vpu_video_layer_info *plane_info)
{
	struct drm_framebuffer *fb = new_state->fb;
	#ifdef CONFIG_AMLOGIC_DRM_USE_ION
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct meson_drm *drv = video_plane->drv;
	struct am_meson_fb *meson_fb;
	struct uvm_buf_obj *ubo;
	#else
	struct drm_gem_cma_object *gem;
	#endif
	size_t fb_size[2] = {0};
	phys_addr_t phyaddr, phyaddr1 = 0;

	#ifdef CONFIG_AMLOGIC_DRM_USE_ION
	meson_fb = container_of(fb, struct am_meson_fb, base);
	if (!meson_fb)
		return -EINVAL;

	DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%p\n",
		  meson_fb->base.base.id,
		  kref_read(&meson_fb->base.base.refcount), meson_fb);
	if (meson_fb->bufp[0]) {
		phyaddr =
			am_meson_gem_object_get_phyaddr(drv,
							meson_fb->bufp[0],
							&fb_size[0]);
	} else {
		//phyaddr = 0;
		DRM_INFO("don't find phyaddr!\n");
		return -EINVAL;
	}
	if (meson_fb->bufp[1] && meson_fb->bufp[1] != meson_fb->bufp[0] &&
	    (fb->format->format == DRM_FORMAT_NV12 ||
				  fb->format->format == DRM_FORMAT_NV21))
		phyaddr1 = am_meson_gem_object_get_phyaddr(drv,
							   meson_fb->bufp[1],
							   &fb_size[1]);
	/* start to get vframe from uvm */
	if (meson_fb->bufp[0]->is_uvm) {
		ubo = &meson_fb->bufp[0]->ubo;
		plane_info->vf = dmabuf_get_vframe(ubo->dmabuf);
		dmabuf_put_vframe(ubo->dmabuf);
		plane_info->is_uvm = meson_fb->bufp[0]->is_uvm;
		plane_info->dmabuf = ubo->dmabuf;
	} else {
		plane_info->dmabuf = meson_fb->bufp[0]->base.dma_buf;
		if (!plane_info->dmabuf)
			plane_info->dmabuf = meson_fb->bufp[0]->dmabuf;
	}
	if (!plane_info->dmabuf)
		return -EINVAL;

	DRM_DEBUG("%s dmabuf %px\n", __func__, plane_info->dmabuf);
	#else
	if (!fb)
		return -EINVAL;

	/* Update Canvas with buffer address */
	gem = drm_fb_cma_get_gem_obj(fb, 0);
	if (!gem) {
		DRM_INFO("gem is NULL!\n");
		return -EINVAL;
	}
	phyaddr = gem->paddr;
	#endif
	plane_info->phy_addr[0] = phyaddr;
	plane_info->fb_size[0] = (u32)fb_size[0];
	plane_info->phy_addr[1] = phyaddr1;
	plane_info->fb_size[1] = (u32)fb_size[1];
	return 0;
}

static int meson_plane_get_fb_info(struct drm_plane *plane,
				   struct drm_plane_state *new_state,
				   struct meson_vpu_osd_layer_info *plane_info)
{
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct drm_framebuffer *fb = new_state->fb;
	struct meson_drm *drv = osd_plane->drv;

	if (!drv) {
		DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__);
		return -EINVAL;
	}
	if (osd_plane->plane_index >= MESON_MAX_OSDS) {
		DRM_INFO("%s invalid plane_index!\n", __func__);
		return -EINVAL;
	}
	plane_info->pixel_format = fb->format->format;
	plane_info->byte_stride = fb->pitches[0];
	plane_info->afbc_en = 0;
	plane_info->afbc_inter_format = 0;
	plane_info->fb_w = fb->width;
	plane_info->fb_h = fb->height;

	/*setup afbc info*/
	if (fb->modifier) {
		plane_info->afbc_en = 1;
		plane_info->afbc_inter_format = AFBC_EN;
	}

	if (fb->modifier & AFBC_FORMAT_MOD_YTR)
		plane_info->afbc_inter_format |= YUV_TRANSFORM;

	if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
		plane_info->afbc_inter_format |= BLOCK_SPLIT;

	if (fb->modifier & AFBC_FORMAT_MOD_TILED)
		plane_info->afbc_inter_format |= TILED_HEADER_EN;

	if ((fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) ==
	    AFBC_FORMAT_MOD_BLOCK_SIZE_32x8)
		plane_info->afbc_inter_format |= SUPER_BLOCK_ASPECT;

	DRM_DEBUG("flags:%d pixel_format:%d,modifier=%llu\n",
		  fb->flags, fb->format->format,
				fb->modifier);
	DRM_DEBUG("plane afbc_en=%u, afbc_inter_format=%x\n",
		  plane_info->afbc_en, plane_info->afbc_inter_format);

	DRM_DEBUG("phy_addr=0x%pa,byte_stride=%d,pixel_format=%d\n",
		  &plane_info->phy_addr, plane_info->byte_stride,
		  plane_info->pixel_format);
	DRM_DEBUG("plane_index %d, size %d.\n",
		  osd_plane->plane_index,
		  plane_info->fb_size);
	return 0;
}

static int meson_video_plane_get_fb_info(struct drm_plane *plane,
					 struct drm_plane_state *new_state,
			struct meson_vpu_video_layer_info *plane_info)
{
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct drm_framebuffer *fb = new_state->fb;
	struct meson_drm *drv = video_plane->drv;

	if (!drv) {
		DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__);
		return -EINVAL;
	}
	if (video_plane->plane_index >= MESON_MAX_VIDEO) {
		DRM_INFO("%s invalid plane_index!\n", __func__);
		return -EINVAL;
	}
	plane_info->pixel_format = fb->format->format;
	plane_info->byte_stride = fb->pitches[0];
	plane_info->fb_w = fb->width;
	plane_info->fb_h = fb->height;

	DRM_DEBUG("flags:%d pixel_format:%d,modifier=%llu\n",
		  fb->flags, fb->format->format,
				fb->modifier);
	DRM_DEBUG("phy_addr[0]=0x%pa,byte_stride=%d,pixel_format=%d\n",
		  &plane_info->phy_addr[0], plane_info->byte_stride,
		  plane_info->pixel_format);
	DRM_DEBUG("phy_addr[1]=0x%pa, plane_index %d, size-0 %d, size-1 %d.\n",
		  &plane_info->phy_addr[1],
		  video_plane->plane_index,
		  plane_info->fb_size[0],
		  plane_info->fb_size[1]);
	return 0;
}

static const char *am_meson_video_fence_get_driver_name(struct dma_fence *fence)
{
	return "meson";
}

static const char *
am_meson_video_fence_get_timeline_name(struct dma_fence *fence)
{
	return "meson_video_fence";
}

static const struct dma_fence_ops am_meson_video_plane_fence_ops = {
	.get_driver_name = am_meson_video_fence_get_driver_name,
	.get_timeline_name = am_meson_video_fence_get_timeline_name,
};

static struct dma_fence *am_meson_video_create_fence(spinlock_t *lock)
{
	struct dma_fence *fence;

	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
	if (!fence)
		return NULL;

	dma_fence_init(fence, &am_meson_video_plane_fence_ops,
		lock, 0, 0);

	return fence;
}

static int meson_video_prepare_fence(struct drm_plane *plane,
					  struct drm_plane_state *state,
				struct meson_vpu_video *mvv)
{
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct dma_fence *fence;

	/*creat implicit fence as out_fence, and next will directly
	 *export to video composer module
	 */
	fence = am_meson_video_create_fence(&video_plane->lock);
	if (!fence)
		return -ENOMEM;

	mvv->fence = fence;
	MESON_DRM_FENCE("creat fence %s fence(%px) plane_index%d\n",
		__func__, fence, video_plane->plane_index);
	return 0;
}

static int meson_plane_atomic_get_property(struct drm_plane *plane,
					   const struct drm_plane_state *state,
					struct drm_property *property,
					uint64_t *val)
{
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct am_meson_plane_state *plane_state;
	int ret = 0;

	plane_state = to_am_meson_plane_state(state);

	if (property == osd_plane->occupied_property) {
		*val = osd_plane->osd_occupied;
		return 0;
	} else if (property == osd_plane->prop_sec_en) {
		*val = plane_state->sec_en;
		ret = 0;
	} else if (property == osd_plane->palette) {
		*val = osd_plane->palette_id;
		ret = 0;
	}

	return ret;
}

static int meson_plane_atomic_set_property(struct drm_plane *plane,
					   struct drm_plane_state *state,
					 struct drm_property *property,
					 uint64_t val)
{
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct am_meson_plane_state *plane_state;
	struct drm_property_blob *new_blob = NULL;
	int ret = 0;

	plane_state = to_am_meson_plane_state(state);

	if (property == osd_plane->occupied_property) {
		osd_plane->osd_occupied = val;
		return 0;
	} else if (property == osd_plane->prop_sec_en) {
		plane_state->sec_en = val;
		ret = 0;
	} else if (property == osd_plane->palette) {
		osd_plane->palette_id = val;
		new_blob = drm_property_lookup_blob(osd_plane->drv->drm, val);
		if (!new_blob)
			return ret;
		osd_plane->receive_palette = new_blob->data;
		drm_property_blob_put(new_blob);
		ret = 0;
	}

	return ret;
}

static struct drm_plane_state *
meson_plane_duplicate_state(struct drm_plane *plane)
{
	struct am_meson_plane_state *meson_plane_state;

	if (WARN_ON(!plane->state))
		return NULL;

	DRM_DEBUG("%s (%s)\n", __func__, plane->name);

	meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL);
	if (!meson_plane_state)
		return NULL;

	__drm_atomic_helper_plane_duplicate_state(plane,
						  &meson_plane_state->base);
	return &meson_plane_state->base;
}

static void meson_plane_destroy_state(struct drm_plane *plane,
				      struct drm_plane_state *state)
{
	struct am_meson_plane_state *meson_plane_state;

	meson_plane_state = to_am_meson_plane_state(state);
	__drm_atomic_helper_plane_destroy_state(&meson_plane_state->base);
	kfree(meson_plane_state);
}

static void meson_plane_reset(struct drm_plane *plane)
{
	struct am_meson_plane_state *meson_plane_state;
	int min_zpos = OSD_PLANE_BEGIN_ZORDER;
	int zpos = 0;
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);

	zpos = osd_plane->plane_index + min_zpos;

	if (plane->state) {
		meson_plane_destroy_state(plane, plane->state);
		plane->state = NULL;
	}

	meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL);
	if (!meson_plane_state)
		return;

	__drm_atomic_helper_plane_reset(plane, &meson_plane_state->base);
	meson_plane_state->base.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE;
	/*reset zpos property unless it set by hwc*/
	if (meson_plane_state->base.zpos == 0)
		meson_plane_state->base.zpos = zpos;
}

bool am_meson_vpu_check_format_mod(struct drm_plane *plane,
				   u32 format, u64 modifier)
{
	DRM_DEBUG("modifier %llu", modifier);
	if (modifier == DRM_FORMAT_MOD_INVALID)
		return false;

	if (modifier == DRM_FORMAT_MOD_LINEAR)
		return true;

	switch (format) {
	case DRM_FORMAT_XRGB8888:
	case DRM_FORMAT_ARGB8888:
		/* YTR is forbidden for non XBGR formats */
		if (modifier & AFBC_FORMAT_MOD_YTR)
			return false;
		fallthrough;
	case DRM_FORMAT_XBGR8888:
	case DRM_FORMAT_ABGR8888:
	case DRM_FORMAT_ABGR2101010:
		return true;
	case DRM_FORMAT_RGB888:
		/* YTR is forbidden for non XBGR formats */
		if (modifier & AFBC_FORMAT_MOD_YTR)
			return false;
		return true;
	case DRM_FORMAT_RGB565:
		/* YTR is forbidden for non XBGR formats */
		if (modifier & AFBC_FORMAT_MOD_YTR)
			return false;
		return true;
	/* TOFIX support mode formats */
	default:
		DRM_DEBUG("unsupported afbc format[%08x]\n", format);
		return false;
	}
}

bool am_meson_vpu_check_video_format_mod(struct drm_plane *plane,
		u32 format, u64 modifier)
{
	if (modifier == DRM_FORMAT_MOD_LINEAR &&
	    format != DRM_FORMAT_YUV420_8BIT &&
	    format != DRM_FORMAT_YUV420_10BIT)
		return true;

	if ((modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) ==
			DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) {
		unsigned int layout = modifier &
			DRM_FORMAT_MOD_AMLOGIC_FBC(__fourcc_mod_amlogic_layout_mask, 0);
		unsigned int options =
			(modifier >> __fourcc_mod_amlogic_options_shift) &
			__fourcc_mod_amlogic_options_mask;

		if (format != DRM_FORMAT_YUV420_8BIT &&
		    format != DRM_FORMAT_YUV420_10BIT) {
			DRM_DEBUG_KMS("%llx invalid format 0x%08x\n",
				      modifier, format);
			return false;
		}

		if (layout != AMLOGIC_FBC_LAYOUT_BASIC &&
		    layout != AMLOGIC_FBC_LAYOUT_SCATTER) {
			DRM_DEBUG_KMS("%llx invalid layout %x\n",
				      modifier, layout);
			return false;
		}

		if (options &&
		    options != AMLOGIC_FBC_OPTION_MEM_SAVING) {
			DRM_DEBUG_KMS("%llx invalid layout %x\n",
				      modifier, layout);
			return false;
		}

		return true;
	}

	DRM_DEBUG_KMS("invalid modifier %llx for format 0x%08x\n",
		      modifier, format);

	return false;
}

static void meson_osd_plane_atomic_print_state(struct drm_printer *p,
		const struct drm_plane_state *state)
{
	struct drm_plane *plane;
	struct meson_vpu_osd_layer_info *plane_info;
	struct meson_vpu_pipeline_state *mvps;
	struct am_osd_plane *osd_plane;
	struct meson_drm *drv;
	struct drm_private_state *obj_state;

	if (!state) {
		DRM_INFO("%s state/meson_drm is NULL!\n", __func__);
		return;
	}

	plane = state->plane;
	if (!plane) {
		DRM_INFO("%s drm_plane is NULL!\n", __func__);
		return;
	}

	osd_plane = to_am_osd_plane(plane);
	DRM_DEBUG("%s [%d]\n", __func__, osd_plane->plane_index);

	drv = osd_plane->drv;
	if (!drv || !drv->pipeline) {
		DRM_INFO("%s private state or pipeline is NULL!\n", __func__);
		return;
	}

	obj_state = drv->pipeline->obj.state;
	if (!obj_state) {
		DRM_ERROR("null pipeline obj state!\n");
		return;
	}

	mvps = container_of(obj_state, struct meson_vpu_pipeline_state, obj);

	if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) {
		DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__);
		return;
	}

	plane_info = &mvps->plane_info[osd_plane->plane_index];

	drm_printf(p, "\tmeson osd plane %d info:\n", osd_plane->plane_index);
	drm_printf(p, "\t\tsrc_x=%u\n", plane_info->src_x);
	drm_printf(p, "\t\tsrc_y=%u\n", plane_info->src_y);
	drm_printf(p, "\t\tsrc_w=%u\n", plane_info->src_w);
	drm_printf(p, "\t\tsrc_h=%u\n", plane_info->src_h);
	drm_printf(p, "\t\tdst_w=%u\n", plane_info->dst_w);
	drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_h);
	drm_printf(p, "\t\tdst_x=%d\n", plane_info->dst_x);
	drm_printf(p, "\t\tdst_y=%d\n", plane_info->dst_y);
	drm_printf(p, "\t\tfb_w=%u\n", plane_info->fb_w);
	drm_printf(p, "\t\tfb_h=%u\n", plane_info->fb_h);
	drm_printf(p, "\t\tzorder=%u\n", plane_info->zorder);
	drm_printf(p, "\t\tbyte_stride=%u\n", plane_info->byte_stride);
	drm_printf(p, "\t\tpixel_format=%u\n", plane_info->pixel_format);
	drm_printf(p, "\t\tphy_addr=0x%llx\n", plane_info->phy_addr);
	drm_printf(p, "\t\tplane_index=%u\n", plane_info->plane_index);
	drm_printf(p, "\t\tuhd_plane_index=%u\n", plane_info->uhd_plane_index);
	drm_printf(p, "\t\tenable=%u\n", plane_info->enable);
	drm_printf(p, "\t\tratio_x=%u\n", plane_info->ratio_x);
	drm_printf(p, "\t\tafbc_inter_format=%u\n",
					plane_info->afbc_inter_format);
	drm_printf(p, "\t\tafbc_en=%u\n", plane_info->afbc_en);
	drm_printf(p, "\t\tfb_size=%u\n", plane_info->fb_size);
	drm_printf(p, "\t\tpixel_blend=%u\n", plane_info->pixel_blend);
	drm_printf(p, "\t\trotation=%u\n", plane_info->rotation);
	drm_printf(p, "\t\tblend_bypass=%u\n", plane_info->blend_bypass);
	drm_printf(p, "\t\tglobal_alpha=%u\n", plane_info->global_alpha);
	drm_printf(p, "\t\tscaling_filter=%u\n", plane_info->scaling_filter);
	drm_printf(p, "\t\tread_ports=%u\n", plane_info->read_ports);
}

static void meson_video_plane_atomic_print_state(struct drm_printer *p,
		const struct drm_plane_state *state)
{
	struct drm_plane *plane;
	struct meson_vpu_video_layer_info *plane_info;
	struct meson_vpu_pipeline_state *mvps;
	struct am_video_plane *video_plane;
	struct meson_drm *drv;
	struct drm_private_state *obj_state;

	if (!state) {
		DRM_INFO("%s state/meson_drm is NULL!\n", __func__);
		return;
	}

	plane = state->plane;
	if (!plane) {
		DRM_INFO("%s drm_plane is NULL!\n", __func__);
		return;
	}

	video_plane = to_am_video_plane(plane);
	DRM_DEBUG("%s [%d]\n", __func__, video_plane->plane_index);

	drv = video_plane->drv;
	if (!drv || !drv->pipeline) {
		DRM_INFO("%s private state is NULL!\n", __func__);
		return;
	}

	obj_state = drv->pipeline->obj.state;
	if (!obj_state) {
		DRM_ERROR("null pipeline obj state!\n");
		return;
	}

	mvps = container_of(obj_state, struct meson_vpu_pipeline_state, obj);

	if (!mvps || video_plane->plane_index >= MESON_MAX_VIDEO) {
		DRM_INFO("%s mvps/video_plane is NULL!\n", __func__);
		return;
	}

	plane_info = &mvps->video_plane_info[video_plane->plane_index];

	drm_printf(p, "\tmeson video plane %d info:\n",
					video_plane->plane_index);
	drm_printf(p, "\t\tsrc_x=%u\n", plane_info->src_x);
	drm_printf(p, "\t\tsrc_y=%u\n", plane_info->src_y);
	drm_printf(p, "\t\tsrc_w=%u\n", plane_info->src_w);
	drm_printf(p, "\t\tsrc_h=%u\n", plane_info->src_h);
	drm_printf(p, "\t\tdst_w=%u\n", plane_info->dst_w);
	drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_h);
	drm_printf(p, "\t\tdst_x=%d\n", plane_info->dst_x);
	drm_printf(p, "\t\tdst_y=%d\n", plane_info->dst_y);
	drm_printf(p, "\t\tzorder=%u\n", plane_info->zorder);
	drm_printf(p, "\t\tbyte_stride=%u\n", plane_info->byte_stride);
	drm_printf(p, "\t\tpixel_format=%u\n", plane_info->pixel_format);
	drm_printf(p, "\t\tphy_addr[0]=0x%llx\n", plane_info->phy_addr[0]);
	drm_printf(p, "\t\tphy_addr[1]=0x%llx\n", plane_info->phy_addr[1]);
	drm_printf(p, "\t\tplane_index=%u\n", plane_info->plane_index);
	drm_printf(p, "\t\tenable=%u\n", plane_info->enable);
	drm_printf(p, "\t\tratio_x=%u\n", plane_info->ratio_x);
	drm_printf(p, "\t\tfb_size[0]=%u\n", plane_info->fb_size[0]);
	drm_printf(p, "\t\tfb_size[1]=%u\n", plane_info->fb_size[1]);
	drm_printf(p, "\t\tpixel_blend=%u\n", plane_info->pixel_blend);
	/*TODO: vframe_s */
	drm_printf(p, "\t\tis_uvm=%u\n", plane_info->is_uvm);
	drm_printf(p, "\t\tvfm_mode=%u\n", plane_info->vfm_mode);
}

static const struct drm_plane_funcs am_osd_plane_funs = {
	.update_plane		= drm_atomic_helper_update_plane,
	.disable_plane		= drm_atomic_helper_disable_plane,
	.destroy		= drm_plane_cleanup,
	.reset			= meson_plane_reset,
	.atomic_duplicate_state = meson_plane_duplicate_state,
	.atomic_destroy_state	= meson_plane_destroy_state,
	.atomic_set_property = meson_plane_atomic_set_property,
	.atomic_get_property = meson_plane_atomic_get_property,
	.format_mod_supported = am_meson_vpu_check_format_mod,
	.atomic_print_state = meson_osd_plane_atomic_print_state,
};

static const struct drm_plane_funcs am_video_plane_funs = {
	.update_plane		= drm_atomic_helper_update_plane,
	.disable_plane		= drm_atomic_helper_disable_plane,
	.destroy		= drm_plane_cleanup,
	.reset			= meson_plane_reset,
	.atomic_duplicate_state = meson_plane_duplicate_state,
	.atomic_destroy_state	= meson_plane_destroy_state,
	.atomic_set_property = meson_plane_atomic_set_property,
	.atomic_get_property = meson_plane_atomic_get_property,
	.format_mod_supported = am_meson_vpu_check_video_format_mod,
	.atomic_print_state = meson_video_plane_atomic_print_state,
};

static int meson_plane_prepare_fb(struct drm_plane *plane,
				  struct drm_plane_state *new_state)
{
	return 0;
}

static void meson_plane_cleanup_fb(struct drm_plane *plane,
				   struct drm_plane_state *old_state)
{
	//struct am_osd_plane *osd_plane = to_am_osd_plane(plane);

	//DRM_DEBUG("osd %d.\n", osd_plane->plane_index);
}

static void meson_plane_atomic_update(struct drm_plane *plane,
				      struct drm_atomic_state *state)
{
	DRM_DEBUG("osd plane atomic_update.\n");
}

static void meson_video_plane_atomic_update(struct drm_plane *plane,
				      struct drm_atomic_state *old_atomic_state)
{
	int video_index, crtc_index;
	struct meson_vpu_sub_pipeline *sub_pipe;
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct drm_crtc *crtc = plane->state->crtc;
	struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc);
	struct meson_vpu_pipeline *pipeline = amcrtc->pipeline;
	struct drm_plane_state *old_plane_state;
	struct meson_vpu_video *mvv = pipeline->video[video_plane->plane_index];

	crtc_index = crtc->index;
	video_index = video_plane->plane_index;
	sub_pipe = &pipeline->subs[crtc_index];
	old_plane_state = drm_atomic_get_old_plane_state(old_atomic_state, plane);

	DRM_DEBUG("video plane atomic_update.\n");
	meson_video_prepare_fence(plane, old_plane_state, mvv);
	vpu_video_plane_update(sub_pipe, old_atomic_state, video_index);
}

static int meson_plane_atomic_check(struct drm_plane *plane,
					struct drm_atomic_state *atomic_state)
{
	struct meson_vpu_osd_layer_info *plane_info;
	struct meson_vpu_pipeline_state *mvps;
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct meson_drm *drv = osd_plane->drv;
	struct drm_plane_state *state;
	struct am_meson_plane_state *plane_state;
	int ret;

	state = drm_atomic_get_new_plane_state(atomic_state, plane);
	if (!state || !drv) {
		DRM_INFO("%s state/meson_drm is NULL!\n", __func__);
		return -EINVAL;
	}

	DRM_DEBUG("%s [%d]\n", __func__, osd_plane->plane_index);

	mvps = meson_vpu_pipeline_get_state(drv->pipeline, atomic_state);
	if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) {
		DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__);
		return -EINVAL;
	}
	plane_info = &mvps->plane_info[osd_plane->plane_index];
	if ((plane_info->src_w != ((state->src_w >> 16) & 0xffff)) ||
		(plane_info->src_h != ((state->src_h >> 16) & 0xffff)) ||
		plane_info->dst_x != state->crtc_x ||
		plane_info->dst_y != state->crtc_y ||
		plane_info->dst_w != state->crtc_w ||
		plane_info->dst_h != state->crtc_h ||
		plane_info->zorder != state->zpos ||
		!plane_info->enable)
		plane_info->status_changed = 1;
	else
		plane_info->status_changed = 0;

	plane_info->plane_index = osd_plane->plane_index;
	/*get plane prop value*/
	plane_info->zorder = state->zpos;
	plane_info->pixel_blend = state->pixel_blend_mode;
	plane_info->global_alpha = state->alpha;
	plane_info->scaling_filter = (u32)state->scaling_filter;
	if (osd_plane->receive_palette)
		plane_info->palette_arry = osd_plane->receive_palette;

	mvps->plane_index[osd_plane->plane_index] = osd_plane->plane_index;
	meson_plane_position_calc(plane_info, state, mvps->pipeline);
	ret = meson_plane_check_size_range(plane_info);
	if (ret < 0) {
		plane_info->enable = 0;
		DRM_INFO("plane%d size check unsupport!!!\n",
			 plane_info->plane_index);
		return ret;
	}
	ret = meson_plane_fb_check(plane, state, plane_info);
	if (ret < 0) {
		plane_info->enable = 0;
		DRM_DEBUG("plane%d fb is NULL,disable the plane!\n",
			  plane_info->plane_index);
		return 0;
	}
	ret = meson_plane_get_fb_info(plane, state, plane_info);
	if (ret < 0 || plane_info->src_w > MESON_OSD_INPUT_W_LIMIT ||
	    plane_info->src_w == 0) {
		plane_info->enable = 0;
		DRM_DEBUG("fb is invalid, disable plane[%d].\n", plane_info->src_w);
		return ret;
	}

	plane_info->enable = 1;
	if (state->crtc)
		plane_info->crtc_index = state->crtc->index;

	plane_state = to_am_meson_plane_state(state);
	plane_info->sec_en = plane_state->sec_en;

	DRM_DEBUG("OSD PLANE index=%d, zorder=%d, premult= %d, alpha = %d, phy = %llx\n",
		  plane_info->plane_index, plane_info->zorder,
		  state->pixel_blend_mode, plane_info->global_alpha,
		  plane_info->phy_addr);
	DRM_DEBUG("w/h = %d/%d, src_x/y/w/h=%d/%d/%d/%d\n",
		  plane_info->fb_w, plane_info->fb_h,
		  plane_info->src_x, plane_info->src_y,
		plane_info->src_w, plane_info->src_h);
	DRM_DEBUG("status_changed = %d, dst_x/y/w/h=%d/%d/%d/%d\n",
		  plane_info->status_changed, plane_info->dst_x, plane_info->dst_y,
		plane_info->dst_w, plane_info->dst_h);
	return 0;
}

static int meson_video_plane_atomic_check(struct drm_plane *plane,
					  struct drm_atomic_state *atomic_state)
{
	struct meson_vpu_video_layer_info *plane_info;
	struct meson_vpu_pipeline_state *mvps;
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct meson_drm *drv = video_plane->drv;
	struct drm_plane_state *state;
	int ret;

	state = drm_atomic_get_new_plane_state(atomic_state, plane);
	if (!state || !drv) {
		DRM_INFO("%s state/meson_drm is NULL!\n", __func__);
		return -EINVAL;
	}

	DRM_DEBUG("planeindex [%d]\n", video_plane->plane_index);

	mvps = meson_vpu_pipeline_get_state(drv->pipeline, atomic_state);
	if (!mvps || video_plane->plane_index >= MESON_MAX_VIDEO) {
		DRM_INFO("%s mvps/video_plane is NULL!\n", __func__);
		return -EINVAL;
	}

	plane_info = &mvps->video_plane_info[video_plane->plane_index];
	if ((plane_info->src_w != ((state->src_w >> 16) & 0xffff)) ||
		(plane_info->src_h != ((state->src_h >> 16) & 0xffff)) ||
		plane_info->dst_x != state->crtc_x ||
		plane_info->dst_y != state->crtc_y ||
		plane_info->dst_w != state->crtc_w ||
		plane_info->dst_h != state->crtc_h ||
		plane_info->zorder != state->zpos ||
		!plane_info->enable)
		plane_info->status_changed = 1;
	else
		plane_info->status_changed = 0;

	plane_info->plane_index = video_plane->plane_index;
	plane_info->vfm_mode = video_plane->vfm_mode;
	plane_info->zorder = state->zpos + plane_info->plane_index;

	mvps->plane_index[video_plane->plane_index] = video_plane->plane_index;
	meson_video_plane_position_calc(plane_info, state,
					mvps->pipeline);
	ret = meson_video_plane_fb_check(plane, state, plane_info);
	if (ret < 0) {
		plane_info->enable = 0;
		DRM_DEBUG("plane%d fb is NULL,disable the plane!\n",
			  plane_info->plane_index);
		return 0;
	}
	ret = meson_video_plane_get_fb_info(plane, state, plane_info);
	if (ret < 0 ||
	    plane_info->src_w == 0) {
		plane_info->enable = 0;
		return ret;
	}

	plane_info->enable = 1;
	if (state->crtc)
		plane_info->crtc_index = state->crtc->index;
	DRM_DEBUG("VIDOE PLANE index=%d, zorder=%d\n",
		  plane_info->plane_index, plane_info->zorder);
	DRM_DEBUG("src_x/y/w/h=%d/%d/%d/%d\n",
		  plane_info->src_x, plane_info->src_y,
		plane_info->src_w, plane_info->src_h);
	DRM_DEBUG("dst_x/y/w/h=%d/%d/%d/%d\n",
		  plane_info->dst_x, plane_info->dst_y,
		plane_info->dst_w, plane_info->dst_h);
	return 0;
}

static void meson_plane_atomic_disable(struct drm_plane *plane,
				       struct drm_atomic_state *old_state)
{
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);

	DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->plane_index);
}

static void meson_video_plane_atomic_disable(struct drm_plane *plane,
					     struct drm_atomic_state *old_atomic_state)
{
	int video_index, crtc_index;
	struct meson_vpu_sub_pipeline *sub_pipe;
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct meson_vpu_pipeline *pipeline = video_plane->pipeline;
	unsigned long pos_crtcs = plane->possible_crtcs;

	crtc_index = find_first_bit(&pos_crtcs, 32);
	if (crtc_index >= MESON_MAX_CRTC)
		crtc_index = 0;

	video_index = video_plane->plane_index;
	sub_pipe = &pipeline->subs[crtc_index];
	DRM_DEBUG("%s video %d, crtc %d.\n", __func__, video_index, crtc_index);
	vpu_video_plane_update(sub_pipe, old_atomic_state, video_index);
}

/*add async check & atomic funs*/
int meson_osd_plane_async_check(struct drm_plane *plane,
	struct drm_atomic_state *atomic_state)
{
	int ret = 0;
	struct meson_vpu_pipeline_state *mvps;
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct meson_drm *drv = osd_plane->drv;
	struct meson_vpu_osd_layer_info *plane_info;
	struct drm_plane_state *new_state;

	DRM_DEBUG("plane_index-%d\n", osd_plane->plane_index);

	new_state = drm_atomic_get_new_plane_state(atomic_state, plane);
	if (!new_state) {
		DRM_ERROR("atomic state is NULL!\n");
		return -EINVAL;
	}

	mvps = meson_vpu_pipeline_get_state(drv->pipeline, atomic_state);
	if (!mvps) {
		DRM_ERROR("mvps is NULL.\n");
		return -EINVAL;
	}

	plane_info = &mvps->plane_info[osd_plane->plane_index];
	if ((plane_info->enable && !new_state->fb) || !plane_info->enable) {
		DRM_ERROR("plane visible state changed.\n");
		return -EINVAL;
	}

	if (plane_info->status_changed) {
		DRM_ERROR("osd%d plane info changed\n", osd_plane->plane_index);
		return -EINVAL;
	}

	return ret;
}

int meson_video_plane_async_check(struct drm_plane *plane,
	struct drm_atomic_state *atomic_state)
{

	struct meson_vpu_video_layer_info *plane_info;
	struct meson_vpu_pipeline_state *mvps;
	struct drm_plane_state *new_state;
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct meson_drm *drv = video_plane->drv;

	DRM_DEBUG("plane_index-%d\n", video_plane->plane_index);

	new_state = drm_atomic_get_new_plane_state(atomic_state, plane);
	if (!new_state) {
		DRM_ERROR("atomic state is NULL!\n");
		return -EINVAL;
	}

	mvps = meson_vpu_pipeline_get_state(drv->pipeline, atomic_state);
	if (!mvps) {
		DRM_ERROR("mvps is NULL.\n");
		return -EINVAL;
	}

	plane_info = &mvps->video_plane_info[video_plane->plane_index];
	if ((plane_info->enable && !new_state->fb) || !plane_info->enable) {
		DRM_ERROR("plane visible state changed.\n");
		return -EINVAL;
	}

	if (plane_info->status_changed) {
		DRM_ERROR("video%d plane info changed\n", video_plane->plane_index);
		return -EINVAL;
	}

	return 0;
}

void meson_osd_plane_async_update(struct drm_plane *plane,
	struct drm_atomic_state *state)
{
	struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
	struct drm_plane_state *old_plane_state;
	struct drm_plane_state *new_state;
	struct meson_vpu_sub_pipeline *sub_pipe;
	struct am_meson_crtc *amcrtc;
	struct meson_vpu_pipeline *pipeline;
	int crtc_index;

	old_plane_state = drm_atomic_get_old_plane_state(state, plane);
	if (!old_plane_state) {
		DRM_ERROR("plane state is null.\n");
		return;
	}

	new_state = drm_atomic_get_new_plane_state(state, plane);
	if (!new_state || !new_state->state) {
		DRM_ERROR("atomic state is NULL!\n");
		return;
	}

	amcrtc = to_am_meson_crtc(new_state->crtc);
	pipeline = amcrtc->pipeline;
	crtc_index = amcrtc->crtc_index;
	sub_pipe = &pipeline->subs[crtc_index];

	DRM_DEBUG("plane_index-%d\n", osd_plane->plane_index);
	plane->state->fb = new_state->fb;
	plane->state->src_x = new_state->src_x;
	plane->state->src_y = new_state->src_y;
	plane->state->crtc_x = new_state->crtc_x;
	plane->state->crtc_y = new_state->crtc_y;

	vpu_pipeline_prepare_update(pipeline, new_state->crtc->mode.vdisplay,
			drm_mode_vrefresh(&new_state->crtc->mode), crtc_index);
	vpu_pipeline_osd_update(sub_pipe, state);
	vpu_pipeline_finish_update(pipeline, crtc_index);
}

void meson_video_plane_async_update(struct drm_plane *plane,
	struct drm_atomic_state *state)
{
	struct am_video_plane *video_plane = to_am_video_plane(plane);
	struct meson_vpu_sub_pipeline *sub_pipe;
	struct am_meson_crtc *amcrtc;
	struct meson_vpu_pipeline *pipeline;
	struct meson_vpu_video *mvv;
	int crtc_index;
	struct drm_plane_state *old_plane_state;
	struct drm_plane_state *new_state;

	old_plane_state = drm_atomic_get_old_plane_state(state, plane);
	if (!old_plane_state) {
		DRM_ERROR("plane state is null.\n");
		return;
	}

	new_state = drm_atomic_get_new_plane_state(state, plane);
	if (!new_state || !new_state->state) {
		DRM_ERROR("atomic state is NULL!\n");
		return;
	}

	DRM_DEBUG("%s plane_index-%d, crtc-%p\n", __func__,
		video_plane->plane_index, new_state->crtc);

	amcrtc = to_am_meson_crtc(new_state->crtc);
	pipeline = amcrtc->pipeline;
	crtc_index = amcrtc->crtc_index;
	sub_pipe = &pipeline->subs[crtc_index];
	mvv = pipeline->video[video_plane->plane_index];

	plane->state->fb = new_state->fb;
	plane->state->src_x = new_state->src_x;
	plane->state->src_y = new_state->src_y;
	plane->state->crtc_x = new_state->crtc_x;
	plane->state->crtc_y = new_state->crtc_y;

	meson_video_prepare_fence(plane, new_state, mvv);
	vpu_pipeline_video_update(sub_pipe, new_state->state);
}

static const struct drm_plane_helper_funcs am_osd_helper_funcs = {
	.prepare_fb = meson_plane_prepare_fb,
	.cleanup_fb = meson_plane_cleanup_fb,
	.atomic_update	= meson_plane_atomic_update,
	.atomic_check	= meson_plane_atomic_check,
	.atomic_disable	= meson_plane_atomic_disable,
	.atomic_async_check = meson_osd_plane_async_check,
	.atomic_async_update = meson_osd_plane_async_update,
};

static const struct drm_plane_helper_funcs am_video_helper_funcs = {
	.prepare_fb = meson_plane_prepare_fb,
	.cleanup_fb = meson_plane_cleanup_fb,
	.atomic_update	= meson_video_plane_atomic_update,
	.atomic_check	= meson_video_plane_atomic_check,
	.atomic_disable	= meson_video_plane_atomic_disable,
	.atomic_async_check = meson_video_plane_async_check,
	.atomic_async_update = meson_video_plane_async_update,
};

static void meson_plane_create_security_en_property(struct drm_device *drm_dev,
						  struct am_osd_plane *osd_plane)
{
	struct drm_property *prop;

	prop = drm_property_create_bool(drm_dev, 0, "SEC_EN");
	if (prop) {
		osd_plane->prop_sec_en = prop;
		drm_object_attach_property(&osd_plane->base.base, prop, 0);
	} else {
		DRM_ERROR("Failed to SEC_EN property\n");
	}
}

struct drm_property *
meson_create_scaling_filter_prop(struct drm_device *dev,
			       unsigned int supported_filters)
{
	struct drm_property *prop;
	static const struct drm_prop_enum_list props[] = {
	{ DRM_SCALING_FILTER_DEFAULT, "Default" },
	{ DRM_SCALING_FILTER_NEAREST_NEIGHBOR, "Nearest Neighbor" },
	{ DRM_SCALING_FILTER_BICUBIC_SHARP, "Bicubic_Sharp" },
	{ DRM_SCALING_FILTER_BICUBIC, "Bicubic" },
	{ DRM_SCALING_FILTER_BILINEAR, "Bilinear" },
	{ DRM_SCALING_FILTER_2POINT_BILINEAR, "2Point_Bilinear" },
	{ DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP, "3Point_Triangle_Sharp" },
	{ DRM_SCALING_FILTER_3POINT_TRIANGLE, "3Point_Triangle" },
	{ DRM_SCALING_FILTER_4POINT_TRIANGLE, "4Point_Triangle" },
	{ DRM_SCALING_FILTER_4POINT_BSPLINE, "4Point_BSPline" },
	{ DRM_SCALING_FILTER_3POINT_BSPLINE, "3Point_BSPline" },
	{ DRM_SCALING_FILTER_REPEATE, "Repeate" },
	};
	unsigned int valid_mode_mask = BIT(DRM_SCALING_FILTER_DEFAULT) |
				BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR) |
				BIT(DRM_SCALING_FILTER_BICUBIC_SHARP) |
				BIT(DRM_SCALING_FILTER_BICUBIC) |
				BIT(DRM_SCALING_FILTER_BILINEAR) |
				BIT(DRM_SCALING_FILTER_2POINT_BILINEAR)  |
				BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP) |
				BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE) |
				BIT(DRM_SCALING_FILTER_4POINT_TRIANGLE) |
				BIT(DRM_SCALING_FILTER_4POINT_BSPLINE) |
				BIT(DRM_SCALING_FILTER_3POINT_BSPLINE) |
				BIT(DRM_SCALING_FILTER_REPEATE);

	int i;

	if (WARN_ON((supported_filters & ~valid_mode_mask) ||
		((supported_filters & BIT(DRM_SCALING_FILTER_DEFAULT)) == 0)))
		return ERR_PTR(-EINVAL);

	prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
				   "SCALING_FILTER",
				   hweight32(supported_filters));
	if (!prop)
		return ERR_PTR(-ENOMEM);

	for (i = 0; i < ARRAY_SIZE(props); i++) {
		int ret;

		if (!(BIT(props[i].type) & supported_filters))
			continue;

		ret = drm_property_add_enum(prop, props[i].type,
					    props[i].name);

		if (ret) {
			drm_property_destroy(dev, prop);

			return ERR_PTR(ret);
		}
	}

	return prop;
}

int meson_plane_create_scaling_filter_property(struct drm_plane *plane,
					     unsigned int supported_filters)
{
	struct drm_property *prop =
		meson_create_scaling_filter_prop(plane->dev, supported_filters);

	if (IS_ERR(prop))
		return PTR_ERR(prop);

	drm_object_attach_property(&plane->base, prop,
				   DRM_SCALING_FILTER_DEFAULT);
	plane->scaling_filter_property = prop;

	return 0;
}

static void meson_plane_add_occupied_property(struct drm_device *drm_dev,
						  struct am_osd_plane *osd_plane)
{
	struct drm_property *prop;

	prop = drm_property_create_bool(drm_dev, 0, "meson.plane.occupied");
	if (prop) {
		osd_plane->occupied_property = prop;
		drm_object_attach_property(&osd_plane->base.base, prop, 0);
	} else {
		DRM_ERROR("Failed to occupied property\n");
	}
}

static void meson_plane_add_palette_property(struct drm_device *drm_dev,
						  struct am_osd_plane *osd_plane)
{
	struct drm_property *prop;

	prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB, "palette", 1);
	if (prop) {
		osd_plane->palette = prop;
		drm_object_attach_property(&osd_plane->base.base, prop, 12);
	} else {
		DRM_ERROR("Failed to palette property\n");
	}
}

static const u32 meson_plane_fb_size_list[] = {
	1080 << 16 | 1920,
	2160 << 16 | 3840,
};

static void meson_plane_add_max_fb_property(struct drm_device *drm_dev,
					    struct am_osd_plane *osd_plane)
{
	int ret;
	u32 size_index, max_fb_size;
	struct drm_property *prop;

	ret = of_property_read_u32(drm_dev->dev->of_node, "max_fb_size", &size_index);
	if (ret)
		max_fb_size = meson_plane_fb_size_list[0];
	else
		max_fb_size = meson_plane_fb_size_list[size_index];

	prop = drm_property_create_range(drm_dev, DRM_MODE_PROP_IMMUTABLE,
					"max_fb_size", 0, UINT_MAX);
	if (prop) {
		osd_plane->max_fb_property = prop;
		drm_object_attach_property(&osd_plane->base.base, prop, max_fb_size);
	} else {
		DRM_ERROR("Failed to create max_fb property\n");
	}
}

static void meson_plane_get_primary_plane(struct meson_drm *priv,
			enum drm_plane_type *type)
{
	int i, j, first_plane = -1;

	for (j = 0; j < MESON_MAX_OSDS; j++)
		type[j] = DRM_PLANE_TYPE_OVERLAY;

	for (i = 0; i < MESON_MAX_CRTC; i++) {
		for (j = 0; j < MESON_MAX_OSDS; j++) {
			if (i == priv->of_conf.crtcmask_osd[j] &&
				priv->osd_occupied_index != j) {
				first_plane = (first_plane != -1) ? first_plane : j;

				if (first_plane != -1 && first_plane < MESON_MAX_OSDS) {
					type[j] = DRM_PLANE_TYPE_PRIMARY;
					priv->primary_plane_index[i] = j;
					break;
				}
			}
		}
		first_plane = -1;
	}
}

static struct am_osd_plane *am_osd_plane_create(struct meson_drm *priv,
		int i, u32 crtc_mask, enum drm_plane_type type)
{
	struct am_osd_plane *osd_plane;
	struct meson_of_conf *conf;
	struct drm_plane *plane;
	u32  zpos, min_zpos, max_zpos, osd_index;
	char plane_name[8];
	const u32 *formats_group;
	int num_formats;
	const char *const_plane_name;

	osd_plane = devm_kzalloc(priv->drm->dev, sizeof(*osd_plane),
				 GFP_KERNEL);
	if (!osd_plane)
		return 0;

	min_zpos = OSD_PLANE_BEGIN_ZORDER;
	max_zpos = OSD_PLANE_END_ZORDER;

	osd_plane->drv = priv;
	osd_plane->plane_index = i;
	osd_plane->plane_type = OSD_PLANE;
	conf = &priv->of_conf;

#ifdef CONFIG_AMLOGIC_MEDIA_FB
	get_logo_osd_reverse(&osd_index, &logo.osd_reverse);
#else
	drm_logo_get_osd_reverse(&osd_index, &logo.osd_reverse);
#endif
	switch (logo.osd_reverse) {
	case 1:
		osd_plane->osd_reverse = DRM_MODE_REFLECT_MASK;
		break;
	case 2:
		osd_plane->osd_reverse = DRM_MODE_REFLECT_X;
		break;
	case 3:
		osd_plane->osd_reverse = DRM_MODE_REFLECT_Y;
		break;
	default:
		osd_plane->osd_reverse = DRM_MODE_ROTATE_0;
		break;
	}

	zpos = osd_plane->plane_index + min_zpos;

	plane = &osd_plane->base;
	sprintf(plane_name, "osd%d", i);
	const_plane_name = plane_name;

	if (i == priv->osd_occupied_index)
		osd_plane->osd_occupied = true;
	else
		osd_plane->osd_occupied = false;

	if (conf->osd_formats_group) {
		if (conf->osd_formats_group == 1) {
			formats_group = supported_drm_formats_v2;
			num_formats = ARRAY_SIZE(supported_drm_formats_v2);
		} else if (conf->osd_formats_group == 2) {
			formats_group = supported_drm_formats_v3;
			num_formats = ARRAY_SIZE(supported_drm_formats_v3);
		} else if (conf->osd_formats_group == 3) {
			formats_group = supported_drm_formats_v4;
			num_formats = ARRAY_SIZE(supported_drm_formats_v4);
		}
	} else {
		formats_group = supported_drm_formats;
		num_formats = ARRAY_SIZE(supported_drm_formats);
	}

	if (conf->osd_afbc_mask & BIT(i)) {
		drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask,
					 &am_osd_plane_funs,
					 formats_group,
					 num_formats,
					 afbc_modifier,
					 type, const_plane_name);
	} else {
		priv->drm->mode_config.allow_fb_modifiers = false;
		drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask,
					 &am_osd_plane_funs,
					 formats_group,
					 num_formats,
					 NULL,
					 type, const_plane_name);
		priv->drm->mode_config.allow_fb_modifiers = true;
	}
	drm_plane_create_blend_mode_property(plane,
				BIT(DRM_MODE_BLEND_PIXEL_NONE) |
				BIT(DRM_MODE_BLEND_PREMULTI)   |
				BIT(DRM_MODE_BLEND_COVERAGE));
	drm_plane_create_alpha_property(plane);
	drm_plane_create_rotation_property(plane,
				DRM_MODE_ROTATE_0,
				DRM_MODE_ROTATE_0 |
				DRM_MODE_REFLECT_MASK);
	drm_plane_create_zpos_property(plane, zpos, min_zpos, max_zpos);
	drm_plane_helper_add(plane, &am_osd_helper_funcs);
	meson_plane_create_scaling_filter_property(plane,
				BIT(DRM_SCALING_FILTER_DEFAULT) |
				BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR) |
				BIT(DRM_SCALING_FILTER_BICUBIC_SHARP) |
				BIT(DRM_SCALING_FILTER_BICUBIC) |
				BIT(DRM_SCALING_FILTER_BILINEAR) |
				BIT(DRM_SCALING_FILTER_2POINT_BILINEAR) |
				BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP) |
				BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE) |
				BIT(DRM_SCALING_FILTER_4POINT_TRIANGLE) |
				BIT(DRM_SCALING_FILTER_4POINT_BSPLINE) |
				BIT(DRM_SCALING_FILTER_3POINT_BSPLINE) |
				BIT(DRM_SCALING_FILTER_REPEATE));
	meson_plane_add_occupied_property(priv->drm, osd_plane);
	meson_plane_add_max_fb_property(priv->drm, osd_plane);
	DRM_INFO("osd plane %d create done, occupied-%d crtc_mask-%d type-%d\n",
		i, osd_plane->osd_occupied, crtc_mask, type);
	meson_plane_create_security_en_property(priv->drm, osd_plane);
	meson_plane_add_palette_property(priv->drm, osd_plane);
	return osd_plane;
}

static struct am_video_plane *am_video_plane_create(struct meson_drm *priv,
						    int i, u32 crtc_mask)
{
	struct am_video_plane *video_plane;
	struct drm_plane *plane;
	char plane_name[8];
	u32 zpos, min_zpos, max_zpos;
	const char *const_plane_name;

	video_plane = devm_kzalloc(priv->drm->dev, sizeof(*video_plane),
				   GFP_KERNEL);
	if (!video_plane) {
		DRM_INFO("no memory to alloc video plane\n");
		return 0;
	}

	min_zpos = 0;
	max_zpos = 255;

	video_plane->drv = priv;
	video_plane->plane_index = i;

	video_plane->plane_type = VIDEO_PLANE;
	video_plane->pipeline = priv->pipeline;
	zpos = video_plane->plane_index + min_zpos;

	plane = &video_plane->base;
	sprintf(plane_name, "video%d", i);
	const_plane_name = plane_name;
	spin_lock_init(&video_plane->lock);

	drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask,
				 &am_video_plane_funs,
				 video_supported_drm_formats,
				 ARRAY_SIZE(video_supported_drm_formats),
				 video_fbc_modifier,
				 DRM_PLANE_TYPE_OVERLAY, const_plane_name);

	drm_plane_create_zpos_property(plane, zpos, min_zpos, max_zpos);
	drm_plane_helper_add(plane, &am_video_helper_funcs);
	DRM_INFO("video plane %d create done\n", i);
	return video_plane;
}

int am_meson_plane_create(struct meson_drm *priv)
{
	struct am_osd_plane *plane;
	struct am_video_plane *video_plane;
	struct meson_vpu_pipeline *pipeline = priv->pipeline;
	struct meson_of_conf *conf = &priv->of_conf;
	enum drm_plane_type type[MESON_MAX_OSD];
	int i, osd_index, video_index;

	memset(priv->osd_planes, 0, sizeof(struct am_osd_plane *) * MESON_MAX_OSD);
	memset(priv->video_planes, 0, sizeof(struct am_video_plane *) * MESON_MAX_VIDEO);

	/*calculate primary plane*/
	meson_plane_get_primary_plane(priv, type);

	/*osd plane*/
	for (i = 0; i < MESON_MAX_OSD; i++) {
		if (!pipeline->osds[i])
			continue;

		osd_index = pipeline->osds[i]->base.index;
		plane = am_osd_plane_create(priv, osd_index,
					    conf->crtcmask_osd[i],
					    type[i]);

		if (!plane)
			return -ENOMEM;

		if (i == 0)
			priv->primary_plane = &plane->base;

		priv->osd_planes[i] = plane;
		priv->num_planes++;
	}

	/*video plane: init after osd to provide osd id at first.*/
	for (i = 0; i < pipeline->num_video; i++) {
		video_index = pipeline->video[i]->base.index;
		video_plane = am_video_plane_create(priv, video_index,
						    conf->crtcmask_video[i]);
		if (!video_plane)
			return -ENOMEM;

		video_plane->vfm_mode = conf->vfm_mode;
		priv->video_planes[i] = video_plane;
		priv->num_planes++;
	}

	return 0;
}
