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

#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/of_fdt.h>
#include <linux/console.h>
#include <linux/amlogic/ge2d/ge2d.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>

#include "osd_canvas.h"
#include "osd_fb.h"
#include "osd_hw.h"
#include "osd_io.h"
#include "osd_reg.h"

struct src_dst_info_s {
	struct rectangle_s  src_rect;
	struct rectangle_s  dst_rect;
	unsigned int		color;
};

struct osd_progress_bar_s {
	struct ge2d_context_s  *ge2d_context;
	const struct vinfo_s *vinfo;
	struct src_dst_info_s  op_info;
	u32 bar_border;
	u32 bar_width;
	u32 bar_height;
};

static struct osd_progress_bar_s progress_bar;
static struct config_para_s ge2d_config;
static struct ge2d_context_s *ge2d_context;

static int init_fb1_first(const struct vinfo_s *vinfo)
{
	struct osd_ctl_s  osd_ctl;
	const struct color_bit_define_s  *color;
	u32 reg = 0, data32 = 0;
	size_t osd_size;
	void __iomem *osd_vaddr;

	osd_ctl.index = 1;
	color = &default_color_format_array[31];

	osd_ctl.addr = get_fb_rmem_paddr(osd_ctl.index);
	osd_vaddr = get_fb_rmem_vaddr(osd_ctl.index);
	osd_size = get_fb_rmem_size(osd_ctl.index);

	osd_ctl.xres = vinfo->width;
	osd_ctl.yres = vinfo->height;
	osd_ctl.xres_virtual = osd_ctl.xres;
	osd_ctl.yres_virtual = osd_ctl.yres;
	osd_ctl.disp_start_x = 0;
	osd_ctl.disp_end_x = osd_ctl.xres - 1;
	osd_ctl.disp_start_y = (vinfo->height * 9) / 10;
	osd_ctl.disp_end_y = osd_ctl.yres - 1;

	reg = osd_ctl.index == 0 ? VIU_OSD1_BLK0_CFG_W0 : VIU_OSD2_BLK0_CFG_W0;
	data32 = VSYNCOSD_RD_MPEG_REG(reg) & (~(0xf << 8));
	data32 |=  color->hw_blkmode << 8; /* osd_blk_mode */
	VSYNCOSD_WR_MPEG_REG(reg, data32);

	memset(osd_vaddr, 0, osd_size);
	pr_debug("addr is 0x%08x, xres is %d, yres is %d\n",
		 osd_ctl.addr, osd_ctl.xres, osd_ctl.yres);
	osd_setup_hw(osd_ctl.index,
		     &osd_ctl,
		     0,
		     0,
		     osd_ctl.xres,
		     osd_ctl.yres,
		     osd_ctl.xres_virtual,
		     osd_ctl.yres_virtual,
		     osd_ctl.disp_start_x,
		     osd_ctl.disp_start_y,
		     osd_ctl.disp_end_x,
		     osd_ctl.disp_end_y,
		     osd_ctl.addr,
		     NULL,
		     color);

	return 0;
}

int osd_show_progress_bar(u32 percent)
{
	static u32 progress;
	u32 step = 1;
	/* wait_queue_head_t  wait_head; */
	struct osd_fb_dev_s *fb_dev;
	struct ge2d_context_s  *context = progress_bar.ge2d_context;
	struct src_dst_info_s  *op_info = &progress_bar.op_info;

	if (!context) {
		/* osd_init_progress_bar(); */
		pr_debug("context is NULL\n");
		return -1;
	}
	fb_dev = gp_fbdev_list[1];
	if (!fb_dev) {
		pr_debug("fb1 should exit!!!");
		return -EFAULT;
	}

	while (progress < percent) {
		pr_debug("progress is %d, x: [%d], y: [%d], w: [%d], h: [%d]\n",
			 progress, op_info->dst_rect.x, op_info->dst_rect.y,
			 op_info->dst_rect.w, op_info->dst_rect.h);

		fillrect(context, op_info->dst_rect.x,
			 op_info->dst_rect.y,
			 op_info->dst_rect.w,
			 op_info->dst_rect.h,
			 op_info->color);

		/* wait_event_interruptible_timeout(wait_head,0,4); */
		progress += step;
		op_info->dst_rect.x += op_info->dst_rect.w;
		op_info->color -= (0xff * step / 100) << 16;
	}

	if (percent == 100) {
		progress = 0;
		osd_blank(1, fb_dev->fb_info);
		destroy_ge2d_work_queue(progress_bar.ge2d_context);
	}

	return 0;
}
EXPORT_SYMBOL(osd_show_progress_bar);

int osd_init_progress_bar(void)
{
	struct src_dst_info_s  *op_info = &progress_bar.op_info;
	const struct vinfo_s *vinfo = progress_bar.vinfo;
	struct osd_fb_dev_s *fb_dev;
#ifdef CONFIG_AMLOGIC_MEDIA_CANVAS
	struct canvas_s cs;
#endif
	u32 cs_addr, cs_width, cs_height;
	struct config_para_s *cfg = &ge2d_config;
	struct ge2d_context_s *context = ge2d_context;
	u32 step = 1;

	if (osd_hw.osd_meson_dev.has_ver == OSD_SIMPLE)
		return 0;

	memset(&progress_bar, 0, sizeof(struct osd_progress_bar_s));

	vinfo = get_current_vinfo();

	progress_bar.bar_border =
		(((vinfo->field_height ?
		vinfo->field_height :
		vinfo->height) * 4 / 720) >> 2) << 2;
	progress_bar.bar_width =
		(((vinfo->width * 200 / 1280) >> 2) << 2) +
		progress_bar.bar_border;
	progress_bar.bar_height =
		(((vinfo->field_height ?
		vinfo->field_height :
		vinfo->height) * 32 / 720) >> 2) << 2;

	if (!init_fb1_first(vinfo)) {
		fb_dev = gp_fbdev_list[1];
		if (!fb_dev) {
			pr_debug("fb1 should exit!!!");
			return -EFAULT;
		}
#ifdef CONFIG_AMLOGIC_MEDIA_CANVAS
		canvas_read(OSD2_CANVAS_INDEX, &cs);
		cs_addr = cs.addr;
		cs_width = cs.width / 4;
		cs_height = cs.height;
#else
		cs_addr = 0;
		cs_width = 0;
		cs_height = 0;
#endif
		context = create_ge2d_work_queue();
		if (!context) {
			pr_debug("create work queue error\n");
			return -EFAULT;
		}

		memset(cfg, 0, sizeof(struct config_para_s));
		cfg->src_dst_type = OSD1_OSD1;
		cfg->src_format = GE2D_FORMAT_S32_ARGB;
		cfg->src_planes[0].addr = cs_addr;
		cfg->src_planes[0].w = cs_width;
		cfg->src_planes[0].h = cs_height;
		cfg->dst_planes[0].addr = cs_addr;
		cfg->dst_planes[0].w = cs_width;
		cfg->dst_planes[0].h = cs_height;

		if (ge2d_context_config(context, cfg) < 0) {
			pr_debug("ge2d config error.\n");
			return -EFAULT;
		}

		if (!context) {
			pr_debug("ge2d_context is NULL!!!!!!\n");
			return -EFAULT;
		}
		progress_bar.ge2d_context = context;
		pr_debug("progress bar setup ge2d device OK\n");
		/* show fb1 */
		console_lock();
		osd_blank(0, fb_dev->fb_info);
		console_unlock();
		op_info->color = 0x555555ff;
		op_info->dst_rect.x =
			(vinfo->width / 2) - progress_bar.bar_width;
		op_info->dst_rect.y = 0;
		op_info->dst_rect.w = progress_bar.bar_width * 2;
		op_info->dst_rect.h = progress_bar.bar_height;

		pr_debug("fill==dst:%d-%d-%d-%d\n",
			 op_info->dst_rect.x, op_info->dst_rect.y,
			 op_info->dst_rect.w, op_info->dst_rect.h);

		fillrect(context, op_info->dst_rect.x,
			 op_info->dst_rect.y,
			 op_info->dst_rect.w,
			 op_info->dst_rect.h,
			 op_info->color);
	} else {
		pr_debug("fb1 init failed, exit!!!");
		return -EFAULT;
	}

	/* initial op info before draw actual */
	op_info->dst_rect.x += progress_bar.bar_border;
	op_info->dst_rect.y += progress_bar.bar_border;
	op_info->dst_rect.w =
		(progress_bar.bar_width - progress_bar.bar_border)
		* 2 * step / 100;
	op_info->dst_rect.h =
		progress_bar.bar_height - progress_bar.bar_border * 2;
	op_info->color = 0xffffff;

	return 0;
}
EXPORT_SYMBOL(osd_init_progress_bar);
