/* SPDX-License-Identifier: GPL-2.0-only OR MIT */

#include <console/console.h>
#include <edid.h>
#include <soc/addressmap.h>
#include <soc/ddp.h>
#include <soc/display.h>
#include <device/mmio.h>

static void disp_config_main_path_connection(enum disp_path_sel path)
{
	/* Main path: OVL0->RDMA0->DVO0 */
	write32(&mmsys_cfg->disp_ovl0_bgclr_mout_en, 0x1);
	write32(&mmsys_cfg->disp_ovl0_out0_mout_en, 0x2);
	write32(&mmsys_cfg->disp_rdma0_sel_in, 0x1);
	write32(&mmsys_cfg->disp_rdma0_rsz0_sout_sel, 0x0);
	write32(&mmsys_cfg->ovl_pq_out_cross1_mout_en, 0x10);
	write32(&mmsys_cfg->comp_out_cross4_mout_en,
		path == DISP_PATH_EDP ? 0x02 : 0x01);
	printk(BIOS_DEBUG,
	       "%s: bgclr_mout_en: %#x ovl0_mout_en: %#x sel_in: %#x sout_sel: %#x"
	       "cross1_mout_en: %#x cross4_mout_en: %#x\n",
	       __func__, read32(&mmsys_cfg->disp_ovl0_bgclr_mout_en),
	       read32(&mmsys_cfg->disp_ovl0_out0_mout_en),
	       read32(&mmsys_cfg->disp_rdma0_sel_in),
	       read32(&mmsys_cfg->disp_rdma0_rsz0_sout_sel),
	       read32(&mmsys_cfg->ovl_pq_out_cross1_mout_en),
	       read32(&mmsys_cfg->comp_out_cross4_mout_en));
}

static void disp_config_main_path_mutex(enum disp_path_sel path)
{
	u32 val;

	val = path == DISP_PATH_EDP ? MUTEX_MOD_MAIN_PATH : MUTEX_MOD_MAIN_DSI_PATH;
	write32(&disp_mutex->mutex[0].mod, val);

	/* Clock source from DVO0 */
	val = path == DISP_PATH_EDP ? (MUTEX_SOF_DVO | (MUTEX_SOF_DVO << 7)) :
		(MUTEX_SOF_DSI0 | (MUTEX_SOF_DSI0 << 7));
	write32(&disp_mutex->mutex[0].ctl, val);

	write32(&disp_mutex->mutex[0].en, BIT(0));
	printk(BIOS_DEBUG, "%s: mutex_mod: %#x ctl %#x\n",
	       __func__, read32(&disp_mutex->mutex[0].mod), read32(&disp_mutex->mutex[0].ctl));
}

static void ovl_layer_smi_id_en(u32 idx)
{
	setbits32(&disp_ovl[idx]->datapath_con, BIT(0));
}

static void ovl_layer_gclast_en(u32 idx)
{
	setbits32(&disp_ovl[idx]->datapath_con, BIT(24));
	setbits32(&disp_ovl[idx]->datapath_con, BIT(25));
}

static void ovl_layer_output_clamp_en(u32 idx)
{
	setbits32(&disp_ovl[idx]->datapath_con, BIT(26));
}

static void ovl_layer_en(u32 idx)
{
	setbits32(&disp_ovl[idx]->en, BIT(0));
}

static void main_disp_path_setup(u32 width, u32 height, u32 vrefresh, enum disp_path_sel path)
{
	u32 idx;
	const u32 pixel_clk = width * height * vrefresh;

	for (idx = 0; idx < MAIN_PATH_OVL_NR; idx++) {
		/* Set OVL background color to blue */
		ovl_set_roi(idx, width, height, idx ? 0 : 0xff0000ff);
		ovl_layer_smi_id_en(idx);
		ovl_layer_gclast_en(idx);
		ovl_layer_output_clamp_en(idx);
		ovl_layer_en(idx);
	}

	rdma_config(width, height, pixel_clk, 5 * KiB);
	disp_config_main_path_connection(path);
	disp_config_main_path_mutex(path);
}

static void disp_clock_on(void)
{
	clrbits32(&mmsys_cfg->mmsys_cg_con0, CG_CON0_ALL);
	clrbits32(&mmsys_cfg->mmsys_cg_con1, CG_CON0_ALL);
	printk(BIOS_DEBUG, "%s: disp_clock: [%#x %#x]\n",
	       __func__, read32(&mmsys_cfg->mmsys_cg_con0), read32(&mmsys_cfg->mmsys_cg_con1));
}

void mtk_display_disable_secure_mode(void)
{
	disp_clock_on();

	printk(BIOS_DEBUG, "%s: shadow: %#x %#x, secure before: [%#x %#x %#x]\n",
	       __func__,
	       read32(&mmsys_cfg->disp_bypass_mux_shadow),
	       read32(&mmsys_cfg->disp_crossbar_con),
	       read32(&mmsys_cfg->mmsys_security_disable),
	       read32(&mmsys_cfg->mmsys_security_disable1),
	       read32(&mmsys_cfg->mmsys_security_disable2));

	/* disable shadow */
	write32(&mmsys_cfg->disp_bypass_mux_shadow, 0x1);
	write32(&mmsys_cfg->disp_crossbar_con, 0x00FF0000);
	/* disable secure mode */
	write32(&mmsys_cfg->mmsys_security_disable, 0xFFFFFFFF);
	write32(&mmsys_cfg->mmsys_security_disable1, 0xFFFFFFFF);
	write32(&mmsys_cfg->mmsys_security_disable2, 0xFFFFFFFF);

	printk(BIOS_DEBUG, "%s: shadow: %#x %#x, secure: [%#x %#x %#x]n",
	       __func__,
	       read32(&mmsys_cfg->disp_bypass_mux_shadow),
	       read32(&mmsys_cfg->disp_crossbar_con),
	       read32(&mmsys_cfg->mmsys_security_disable),
	       read32(&mmsys_cfg->mmsys_security_disable1),
	       read32(&mmsys_cfg->mmsys_security_disable2));
}

void mtk_ddp_init(void)
{
	int i;

	mtk_display_disable_secure_mode();

	/* Turn off M4U port */
	for (i = 0; i < RDMA_PORT_NR; i++) {
		write32(&smi_larb0->port_l0_ovl_rdma[i], 0);
		write32(&smi_larb1->port_l0_ovl_rdma[i], 0);
	}

	printk(BIOS_DEBUG, "%s: larb: %#x\n",
	       __func__, read32(&smi_larb0->port_l0_ovl_rdma[0]));
}

void mtk_ddp_soc_mode_set(u32 fmt, u32 bpp, u32 width, u32 height, u32 vrefresh,
			  enum disp_path_sel path, struct dsc_config *dsc_config)
{
	main_disp_path_setup(width, height, vrefresh, path);
	rdma_start();
	ovl_layer_config(fmt, bpp, width, height);
}
