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

/* Amlogic Headers */
#include <linux/amlogic/media/ge2d/ge2d.h>

static inline void _set_src1_format(struct ge2d_src1_data_s *src1_data_cfg,
				    struct ge2d_src1_gen_s *src1_gen_cfg,
				    struct ge2d_dp_gen_s *dp_gen_cfg,
				    unsigned int format_src,
				    unsigned int format_dst)
{
	src1_data_cfg->format_all  = format_src;

	src1_data_cfg->format      = (format_src >> 8) & 3;
	src1_data_cfg->mode_8b_sel = (format_src >> 6) & 3;
	src1_data_cfg->lut_en      = (format_src >> 5) & 1;
	src1_data_cfg->sep_en      = (format_src >> 2) & 1;

	src1_data_cfg->endian      = (format_src & GE2D_ENDIAN_MASK) >>
				     GE2D_ENDIAN_SHIFT;
	src1_data_cfg->color_map   = (format_src & GE2D_COLOR_MAP_MASK) >>
				     GE2D_COLOR_MAP_SHIFT;

	src1_gen_cfg->pic_struct   = (format_src >> 3) & 3;
	src1_data_cfg->x_yc_ratio  = (format_src >> 1) & 1;
	src1_data_cfg->y_yc_ratio  = (format_src >> 0) & 1;

	if (format_src & GE2D_FORMAT_DEEP_COLOR)
		src1_data_cfg->deep_color = 1;
	else
		src1_data_cfg->deep_color = 0;
}

static inline
void _set_src2_format(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
		      struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg,
		      unsigned int format)
{
	src2_dst_data_cfg->src2_format_all = format;

	src2_dst_data_cfg->src2_format = (format >> 8) & 3;
	src2_dst_data_cfg->src2_endian = (format & GE2D_ENDIAN_MASK) >>
					      GE2D_ENDIAN_SHIFT;
	src2_dst_data_cfg->src2_color_map = (format & GE2D_COLOR_MAP_MASK) >>
					      GE2D_COLOR_MAP_SHIFT;

	src2_dst_data_cfg->src2_mode_8b_sel = (format >> 6) & 3;

	src2_dst_gen_cfg->src2_pic_struct = (format >> 3) & 3;
}

static inline
void _set_dst_format(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
		     struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg,
		     struct ge2d_dp_gen_s *dp_gen_cfg,
		     unsigned int format_src,
		     unsigned int format_dst)
{
	unsigned int y_yc_ratio;

	src2_dst_data_cfg->dst_format_all = format_dst;
	src2_dst_data_cfg->dst_format = (format_dst >> 8) & 3;
	src2_dst_data_cfg->dst_endian = (format_dst & GE2D_ENDIAN_MASK) >>
					GE2D_ENDIAN_SHIFT;
	src2_dst_data_cfg->dst_color_map = (format_dst & GE2D_COLOR_MAP_MASK) >>
					   GE2D_COLOR_MAP_SHIFT;

	src2_dst_data_cfg->dst_mode_8b_sel = (format_dst >> 6) & 3;
	src2_dst_gen_cfg->dst_pic_struct   = (format_dst >> 3) & 3;

	y_yc_ratio = (format_dst >> 0) & 1;

	/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
	/* for dest is nv21 or nv12 in m6. */
	if ((format_dst & GE2D_FORMAT_YUV) &&
	    ((src2_dst_data_cfg->dst_color_map | 1) == 15)) {
		src2_dst_data_cfg->dst_format = 0;
		src2_dst_data_cfg->dst_mode_8b_sel = 0;
		src2_dst_data_cfg->dst2_pixel_byte_width = 1;
		if (y_yc_ratio == 0)
			src2_dst_data_cfg->dst2_discard_mode = 0xc;
		else
			src2_dst_data_cfg->dst2_discard_mode = 0xf;
		src2_dst_data_cfg->dst2_enable = 1;
		src2_dst_data_cfg->dst2_color_map =
			src2_dst_data_cfg->dst_color_map - 5;
	} else {
		src2_dst_data_cfg->dst2_enable = 0;
	}
	/* #endif */

	if (ge2d_meson_dev.dst_repeat)
		switch (format_dst & GE2D_EXT_MASK) {
		case GE2D_DST_REPEAT_2:
			src2_dst_data_cfg->dst_rpt = DST_RPT_2 |
						     DST_RPT_PATTERN_XX;
			break;
		case GE2D_DST_REPEAT_4:
			src2_dst_data_cfg->dst_rpt = DST_RPT_4 |
						     DST_RPT_PATTERN_XX;
			break;
		case GE2D_DST_REPEAT_8:
			src2_dst_data_cfg->dst_rpt = DST_RPT_8 |
						     DST_RPT_PATTERN_XX;
			break;
		default:
			src2_dst_data_cfg->dst_rpt = 0;
			break;
		}
}

static bool is_src1_addr_update(struct ge2d_src1_data_s *src1_data_cfg,
				unsigned long *phy_addr,
				unsigned int *stride)
{
	int i;
	int update = 0;

	for (i = 0; i < MAX_PLANE; i++) {
		if (phy_addr[i]) {
			if (phy_addr[i] != src1_data_cfg->phy_addr[i] ||
			    stride[i] != src1_data_cfg->stride[i]) {
				update = 1;
				break;
			}
		}
	}
	return update;
}

static bool is_src2_addr_update(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
				unsigned long *phy_addr,
				unsigned int *stride)
{
	int i;
	int update = 0;

	for (i = 0; i < MAX_PLANE; i++) {
		if (phy_addr[i]) {
			if (phy_addr[i] != src2_dst_data_cfg->src2_phyaddr[i] ||
			    stride[i] != src2_dst_data_cfg->src2_stride[i]) {
				update = 1;
				break;
			}
		}
	}
	return update;
}

static bool is_dst_addr_update(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
			       unsigned long *phy_addr,
			       unsigned int *stride)
{
	int i;
	int update = 0;

	for (i = 0; i < MAX_PLANE; i++) {
		if (phy_addr[i]) {
			if (phy_addr[i] != src2_dst_data_cfg->dst_phyaddr[i] ||
			    stride[i] != src2_dst_data_cfg->dst_stride[i]) {
				update = 1;
				break;
			}
		}
	}
	return update;
}

void ge2dgen_src(struct ge2d_context_s *wq,
		 unsigned int canvas_addr,
		unsigned int format,
		unsigned long *phy_addr,
		unsigned int *stride)
{
	struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);
	struct ge2d_src1_gen_s *src1_gen_cfg = ge2d_wq_get_src_gen(wq);
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
	struct ge2d_src2_dst_data_s *src2_dst_data_cfg =
		ge2d_wq_get_dst_data(wq);
	int i;

	if (format != src1_data_cfg->format_all ||
	    canvas_addr != src1_data_cfg->canaddr ||
	    is_src1_addr_update(src1_data_cfg, phy_addr, stride)) {
		src1_data_cfg->canaddr = canvas_addr;

		_set_src1_format(src1_data_cfg, src1_gen_cfg, dp_gen_cfg,
				 format, src2_dst_data_cfg->dst_format_all);
		for (i = 0; i < MAX_PLANE; i++) {
			src1_data_cfg->phy_addr[i] = phy_addr[i];
			src1_data_cfg->stride[i] = stride[i];
		}
		wq->config.update_flag |= UPDATE_SRC_DATA;
		wq->config.update_flag |= UPDATE_SRC_GEN;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
}

void ge2dgen_antiflicker(struct ge2d_context_s *wq, unsigned long enable)
{
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

	enable = enable ? 1 : 0;

	if (dp_gen_cfg->antiflick_en != enable) {
		dp_gen_cfg->antiflick_en = enable;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
}

void ge2dgen_post_release_src1buf(struct ge2d_context_s *wq,
				  unsigned int buffer)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->src1_buffer = buffer;
	ge2d_cmd_cfg->release_flag |= RELEASE_SRC1_BUFFER;
}

void ge2dgen_post_release_src1canvas(struct ge2d_context_s *wq)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->release_flag |= RELEASE_SRC1_CANVAS;
}

void ge2dgen_post_release_src2buf(struct ge2d_context_s *wq,
				  unsigned int buffer)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->src2_buffer = buffer;
	ge2d_cmd_cfg->release_flag |= RELEASE_SRC2_BUFFER;
}

void ge2dgen_post_release_src2canvas(struct ge2d_context_s *wq)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->release_flag |= RELEASE_SRC2_CANVAS;
}

void ge2dgen_cb(struct ge2d_context_s *wq,
		int (*cmd_cb)(unsigned int), unsigned int param)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->cmd_cb = cmd_cb;
	ge2d_cmd_cfg->cmd_cb_param = param;
	ge2d_cmd_cfg->release_flag |= RELEASE_CB;
}

void ge2dgen_src2(struct ge2d_context_s *wq,
		  unsigned int canvas_addr,
		unsigned int format,
		unsigned long *phy_addr,
		unsigned int *stride)
{
	struct ge2d_src2_dst_data_s *src2_dst_data_cfg =
		ge2d_wq_get_dst_data(wq);
	struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
	int i;

	if (format != src2_dst_data_cfg->src2_format_all ||
	    canvas_addr != src2_dst_data_cfg->src2_canaddr ||
	    is_src2_addr_update(src2_dst_data_cfg, phy_addr, stride)) {
		src2_dst_data_cfg->src2_canaddr = canvas_addr;

		_set_src2_format(src2_dst_data_cfg, src2_dst_gen_cfg, format);
		for (i = 0; i < MAX_PLANE; i++) {
			src2_dst_data_cfg->src2_phyaddr[i] = phy_addr[i];
			src2_dst_data_cfg->src2_stride[i] = stride[i];
		}
		wq->config.update_flag |= UPDATE_DST_DATA;
		wq->config.update_flag |= UPDATE_DST_GEN;
	}
}

void ge2dgen_dst(struct ge2d_context_s *wq,
		 unsigned int canvas_addr,
		 unsigned int format,
		 unsigned long *phy_addr,
		 unsigned int *stride)
{
	struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);
	struct ge2d_src2_dst_data_s *src2_dst_data_cfg =
		ge2d_wq_get_dst_data(wq);
	struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
	int i;

	if (format != src2_dst_data_cfg->dst_format_all ||
	    canvas_addr != src2_dst_data_cfg->dst_canaddr ||
	    is_dst_addr_update(src2_dst_data_cfg, phy_addr, stride)) {
		src2_dst_data_cfg->dst_canaddr = canvas_addr;

		_set_dst_format(src2_dst_data_cfg, src2_dst_gen_cfg, dp_gen_cfg,
				src1_data_cfg->format_all, format);
		for (i = 0; i < MAX_PLANE; i++) {
			src2_dst_data_cfg->dst_phyaddr[i] = phy_addr[i];
			src2_dst_data_cfg->dst_stride[i] = stride[i];
		}
		wq->config.update_flag |= UPDATE_DST_DATA;
		wq->config.update_flag |= UPDATE_DST_GEN;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
}

void ge2dgen_src_clip(struct ge2d_context_s *wq,
		      int x, int y, int w, int h)
{
	struct ge2d_src1_gen_s *src1_gen_cfg = ge2d_wq_get_src_gen(wq);
	/* adjust w->x_end h->y_end */
	w = x + w - 1;
	h = y + h - 1;
	if (src1_gen_cfg->clipx_start != x ||
	    src1_gen_cfg->clipx_end   != w ||
	    src1_gen_cfg->clipy_start != y ||
	    src1_gen_cfg->clipy_end   != h) {
		src1_gen_cfg->clipx_start = x;
		src1_gen_cfg->clipx_end   = w;
		src1_gen_cfg->clipy_start = y;
		src1_gen_cfg->clipy_end   = h;
		wq->config.update_flag |= UPDATE_SRC_GEN;
	}
}

void ge2dgen_src2_clip(struct ge2d_context_s *wq,
		       int x, int y, int w, int h)
{
	struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);

	/* adjust w->x_end h->y_end */
	w = x + w - 1;
	h = y + h - 1;
	if (src2_dst_gen_cfg->src2_clipx_start != x ||
	    src2_dst_gen_cfg->src2_clipx_end   != w ||
	    src2_dst_gen_cfg->src2_clipy_start != y ||
	    src2_dst_gen_cfg->src2_clipy_end   != h) {
		src2_dst_gen_cfg->src2_clipx_start = x;
		src2_dst_gen_cfg->src2_clipx_end   = w;
		src2_dst_gen_cfg->src2_clipy_start = y;
		src2_dst_gen_cfg->src2_clipy_end   = h;
		wq->config.update_flag |= UPDATE_DST_GEN;
	}
}

void ge2dgen_src_key(struct ge2d_context_s *wq,
		     int en, int key, int keymask, int keymode)
{
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

	if (dp_gen_cfg->src1_key_en != en ||
	    dp_gen_cfg->src1_key != key ||
	    dp_gen_cfg->src1_key_mask != keymask ||
	    dp_gen_cfg->src1_key_mode != keymode) {
		dp_gen_cfg->src1_key_en = en & 0x1;
		dp_gen_cfg->src1_key = key;
		dp_gen_cfg->src1_key_mask = keymask;
		dp_gen_cfg->src1_key_mode = keymode & 0x1;

		dp_gen_cfg->src1_vsc_bank_length = 4;
		dp_gen_cfg->src1_hsc_bank_length = 4;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
}
EXPORT_SYMBOL(ge2dgen_src_key);

void ge2dgent_src_gbalpha(struct ge2d_context_s *wq,
			  unsigned char alpha1, unsigned char alpha2)
{
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
#ifdef CONFIG_GE2D_SRC2
	if (dp_gen_cfg->src1_gb_alpha != alpha1 ||
	    dp_gen_cfg->src2_gb_alpha != alpha2) {
		dp_gen_cfg->src1_gb_alpha = alpha1;
		dp_gen_cfg->src2_gb_alpha = alpha2;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
#else
	if (dp_gen_cfg->src1_gb_alpha != alpha1) {
		dp_gen_cfg->src1_gb_alpha = alpha1;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
#endif
}

void ge2dgen_src_color(struct ge2d_context_s *wq, unsigned int color)
{
	struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);

	if (src1_data_cfg->def_color != color) {
		src1_data_cfg->def_color = color;
		wq->config.update_flag |= UPDATE_SRC_DATA;
	}
}

void ge2dgen_rendering_dir(struct ge2d_context_s *wq,
			   int src_x_dir, int src_y_dir,
			   int dst_x_dir, int dst_y_dir,
			   int dst_xy_swap)
{
	struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

	ge2d_cmd_cfg->src1_x_rev = src_x_dir;
	ge2d_cmd_cfg->src1_y_rev = src_y_dir;
	ge2d_cmd_cfg->dst_x_rev = dst_x_dir;
	ge2d_cmd_cfg->dst_y_rev = dst_y_dir;
	ge2d_cmd_cfg->dst_xy_swap =  dst_xy_swap;
}

void ge2dgen_dst_clip(struct ge2d_context_s *wq,
		      int x, int y, int w, int h, int mode)
{
	struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
	/* adjust w->x_end h->y_end */
	w = x + w - 1;
	h = y + h - 1;
	if (src2_dst_gen_cfg->dst_clipx_start != x ||
	    src2_dst_gen_cfg->dst_clipx_end   != w ||
	    src2_dst_gen_cfg->dst_clipy_start != y ||
	    src2_dst_gen_cfg->dst_clipy_end   != h ||
	    src2_dst_gen_cfg->dst_clip_mode   != mode) {
		src2_dst_gen_cfg->dst_clipx_start = x;
		src2_dst_gen_cfg->dst_clipx_end   = w;
		src2_dst_gen_cfg->dst_clipy_start = y;
		src2_dst_gen_cfg->dst_clipy_end   = h;
		src2_dst_gen_cfg->dst_clip_mode   = mode;
		wq->config.update_flag |= UPDATE_DST_GEN;
	}
}

void ge2dgent_src2_clip(struct ge2d_context_s *wq,
			int x, int y, int w, int h)
{
	struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
	/* adjust w->x_end h->y_end */
	w = x + w - 1;
	h = y + h - 1;
	if (src2_dst_gen_cfg->src2_clipx_start != x ||
	    src2_dst_gen_cfg->src2_clipx_end   != w ||
	    src2_dst_gen_cfg->src2_clipy_start != y ||
	    src2_dst_gen_cfg->src2_clipy_end   != h) {
		src2_dst_gen_cfg->src2_clipx_start = x;
		src2_dst_gen_cfg->src2_clipx_end   = w;
		src2_dst_gen_cfg->src2_clipy_start = y;
		src2_dst_gen_cfg->src2_clipy_end   = h;
		wq->config.update_flag |= UPDATE_DST_GEN;
	}
}

void ge2dgen_const_color(struct ge2d_context_s *wq,
			 unsigned int color)
{
	struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

	if (dp_gen_cfg->alu_const_color != color) {
		dp_gen_cfg->alu_const_color = color;
		wq->config.update_flag |= UPDATE_DP_GEN;
	}
}
