/*
 * Copyright 2017-2018 NXP
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/mach-imx/sci/sci.h>
#include <i2c.h>
#include <asm/arch/sys_proto.h>

#include <imxdpuv1.h>
#include <imxdpuv1_registers.h>
#include <imxdpuv1_events.h>
#include <asm/arch/imx8_lvds.h>
#include <video_fb.h>
#include <asm/arch/imx8_mipi_dsi.h>
#include <asm/arch/video_common.h>
#include <power-domain.h>

DECLARE_GLOBAL_DATA_PTR;

static struct imxdpuv1_videomode gmode;
static int8_t gdisp, gdc;
static uint32_t gpixfmt;
static GraphicDevice panel;

static int hdmi_i2c_reg_write(struct udevice *dev, uint addr, uint mask, uint data)
{
	uint8_t valb;
	int err;

	if (mask != 0xff) {
		err = dm_i2c_read(dev, addr, &valb, 1);
		if (err)
			return err;

		valb &= ~mask;
		valb |= data;
	} else {
		valb = data;
	}

	err = dm_i2c_write(dev, addr, &valb, 1);
	return err;
}

static int hdmi_i2c_reg_read(struct udevice *dev, uint8_t addr, uint8_t *data)
{
	uint8_t valb;
	int err;

	err = dm_i2c_read(dev, addr, &valb, 1);
	if (err)
		return err;

	*data = (int)valb;
	return 0;
}

/* On 8QXP ARM2, the LVDS1 signals are connected to LVDS2HDMI card's LVDS2 channel,
 *  LVDS0 signals are connected to LVDS2HDMI card's LVDS4 channel.
 *  There totally 6 channels on the cards, from 0-5.
 */
int lvds2hdmi_setup(int i2c_bus)
{
	struct udevice *bus, *dev;
	uint8_t chip = 0x4c;
	uint8_t data;
	int ret;

	ret = uclass_get_device_by_seq(UCLASS_I2C, i2c_bus, &bus);
	if (ret) {
		printf("%s: No bus %d\n", __func__, i2c_bus);
		return ret;
	}

	ret = dm_i2c_probe(bus, chip, 0, &dev);
	if (ret) {
		printf("%s: Can't find device id=0x%x, on bus %d\n",
			__func__, chip, i2c_bus);
		return ret;
	}

	/* InitIT626X(): start */
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x3d);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x05, 0xff, 0x40);
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x15);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x1d, 0xff, 0x66);
	hdmi_i2c_reg_write(dev, 0x1e, 0xff, 0x01);

	hdmi_i2c_reg_write(dev, 0x61, 0xff, 0x30);
	hdmi_i2c_reg_read(dev, 0xf3, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0xf3, 0xff, data & ~0x30);
	hdmi_i2c_reg_read(dev, 0xf3, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0xf3, 0xff, data | 0x20);

	hdmi_i2c_reg_write(dev, 0x09, 0xff, 0x30);
	hdmi_i2c_reg_write(dev, 0x0a, 0xff, 0xf8);
	hdmi_i2c_reg_write(dev, 0x0b, 0xff, 0x37);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xc9, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xca, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xcb, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xcc, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xcd, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xce, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xcf, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xd0, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x01);

	hdmi_i2c_reg_read(dev, 0x58, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x58, 0xff, data & ~(3 << 5));

	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xe1, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x0c, 0xff, 0xff);
	hdmi_i2c_reg_write(dev, 0x0d, 0xff, 0xff);
	hdmi_i2c_reg_read(dev, 0x0e, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x0e, 0xff, (data | 0x3));
	hdmi_i2c_reg_write(dev, 0x0e, 0xff, (data & 0xfe));
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x01);
	hdmi_i2c_reg_write(dev, 0x33, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x34, 0xff, 0x18);
	hdmi_i2c_reg_write(dev, 0x35, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xc4, 0xff, 0xfe);
	hdmi_i2c_reg_read(dev, 0xc5, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0xc5, 0xff, data | 0x30);
	/* InitIT626X  end */

	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x3d);
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x15);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x1d, 0xff, 0x66);
	hdmi_i2c_reg_write(dev, 0x1e, 0xff, 0x01);

	hdmi_i2c_reg_read(dev, 0xc1, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x61, 0xff, 0x10);

	/* SetupAFE(): */
	hdmi_i2c_reg_write(dev, 0x62, 0xff, 0x88);
	hdmi_i2c_reg_write(dev, 0x63, 0xff, 0x10);
	hdmi_i2c_reg_write(dev, 0x64, 0xff, 0x84);
	/* SetupAFE(): end */

	hdmi_i2c_reg_read(dev, 0x04, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x1d);

	hdmi_i2c_reg_read(dev, 0x04, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x04, 0xff, 0x15);

	hdmi_i2c_reg_read(dev, 0x0e, &data); /* -> 0x00 */

	/*  Wait video stable */
	hdmi_i2c_reg_read(dev, 0x0e, &data); /* -> 0x00 */

	/* Reset Video */
	hdmi_i2c_reg_read(dev, 0x0d, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x0d, 0xff, 0x40);
	hdmi_i2c_reg_read(dev, 0x0e, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x0e, 0xff, 0x7d);
	hdmi_i2c_reg_write(dev, 0x0e, 0xff, 0x7c);
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0x61, 0xff, 0x00);
	hdmi_i2c_reg_read(dev, 0x61, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x62, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x63, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x64, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x65, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x66, &data); /* -> 0x00 */
	hdmi_i2c_reg_read(dev, 0x67, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0x0f, 0xff, 0x00);
	hdmi_i2c_reg_read(dev, 0xc1, &data); /* -> 0x00 */
	hdmi_i2c_reg_write(dev, 0xc1, 0xff, 0x00);
	hdmi_i2c_reg_write(dev, 0xc6, 0xff, 0x03);
	/* Clear AV mute */

	return 0;
}


int lvds_soc_setup(int lvds_id, sc_pm_clock_rate_t pixel_clock)
{
	sc_err_t err;
	sc_rsrc_t lvds_rsrc, mipi_rsrc;
	const char *pd_name;
	sc_ipc_t ipcHndl = gd->arch.ipc_channel_handle;

	struct power_domain pd;
	int ret;

	if (lvds_id == 0) {
		lvds_rsrc = SC_R_LVDS_0;
		mipi_rsrc = SC_R_MIPI_0;
		pd_name = "lvds0_power_domain";
	} else {
		lvds_rsrc = SC_R_LVDS_1;
		mipi_rsrc = SC_R_MIPI_1;
		pd_name = "lvds1_power_domain";
	}
	/* Power up LVDS */
	if (!power_domain_lookup_name(pd_name, &pd)) {
		ret = power_domain_on(&pd);
		if (ret) {
			printf("%s Power up failed! (error = %d)\n", pd_name, ret);
			return -EIO;
		}
	} else {
		printf("%s lookup failed!\n", pd_name);
		return -EIO;
	}

	/* Setup clocks */
	err = sc_pm_set_clock_rate(ipcHndl, lvds_rsrc, SC_PM_CLK_BYPASS, &pixel_clock);
	if (err != SC_ERR_NONE) {
		printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_set_clock_rate(ipcHndl, lvds_rsrc, SC_PM_CLK_PER, &pixel_clock);
	if (err != SC_ERR_NONE) {
		printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_set_clock_rate(ipcHndl, lvds_rsrc, SC_PM_CLK_PHY, &pixel_clock);
	if (err != SC_ERR_NONE) {
		printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err);
		return -EIO;
	}

	if (is_imx8qxp()) {
		/* For QXP, there is only one DC, and two pixel links to each LVDS with a mux provided.
		  * We connect LVDS0 to pixel link 0, lVDS1 to pixel link 1 from DC
		  */

		/* Configure to LVDS mode not MIPI DSI */
		err = sc_misc_set_control(ipcHndl, mipi_rsrc, SC_C_MODE, 1);
		if (err != SC_ERR_NONE) {
			printf("LVDS sc_misc_set_control SC_C_MODE failed! (error = %d)\n", err);
			return -EIO;
		}

		/* Configure to LVDS mode with single channel */
		err = sc_misc_set_control(ipcHndl, mipi_rsrc, SC_C_DUAL_MODE, 0);
		if (err != SC_ERR_NONE) {
			printf("LVDS sc_misc_set_control SC_C_DUAL_MODE failed! (error = %d)\n", err);
			return -EIO;
		}

		err = sc_misc_set_control(ipcHndl, mipi_rsrc, SC_C_PXL_LINK_SEL, lvds_id);
		if (err != SC_ERR_NONE) {
			printf("LVDS sc_misc_set_control SC_C_PXL_LINK_SEL failed! (error = %d)\n", err);
			return -EIO;
		}
	}

	err = sc_pm_clock_enable(ipcHndl, lvds_rsrc, SC_PM_CLK_BYPASS, true, false);
	if (err != SC_ERR_NONE) {
		printf("LVDS enable clock SC_PM_CLK_BYPASS failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, lvds_rsrc, SC_PM_CLK_PER, true, false);
	if (err != SC_ERR_NONE) {
		printf("LVDS enable clock SC_PM_CLK_PER failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, lvds_rsrc, SC_PM_CLK_PHY, true, false);
	if (err != SC_ERR_NONE) {
		printf("LVDS enable clock SC_PM_CLK_PHY failed! (error = %d)\n", err);
		return -EIO;
	}

	return 0;
}

void lvds_configure(int lvds_id)
{
	void __iomem *lvds_base;
	void __iomem *mipi_base;
	uint32_t phy_setting;
	uint32_t mode;

	if (lvds_id == 0) {
		lvds_base = (void __iomem *)LVDS0_PHYCTRL_BASE;
		mipi_base = (void __iomem *)MIPI0_SS_BASE;
	} else {
		lvds_base = (void __iomem *)LVDS1_PHYCTRL_BASE;
		mipi_base = (void __iomem *)MIPI1_SS_BASE;
	}

	if (is_imx8qm()) {
		mode =
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_MODE, LVDS_CTRL_CH0_MODE__DI0) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_DATA_WIDTH, LVDS_CTRL_CH0_DATA_WIDTH__24BIT) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_BIT_MAP, LVDS_CTRL_CH0_BIT_MAP__JEIDA) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_10BIT_ENABLE, LVDS_CTRL_CH0_10BIT_ENABLE__10BIT) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_DI0_DATA_WIDTH, LVDS_CTRL_DI0_DATA_WIDTH__USE_30BIT);

		writel(mode, lvds_base + LVDS_CTRL);

		phy_setting =
			LVDS_PHY_CTRL_RFB_MASK |
			LVDS_PHY_CTRL_CH0_EN_MASK |
			(0 << LVDS_PHY_CTRL_M_SHIFT) |
			(0x04 << LVDS_PHY_CTRL_CCM_SHIFT) |
			(0x04 << LVDS_PHY_CTRL_CA_SHIFT);
		writel(phy_setting, lvds_base + LVDS_PHY_CTRL);
	} else if (is_imx8qxp()) {
		mode =
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_MODE, LVDS_CTRL_CH0_MODE__DI0) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_DATA_WIDTH, LVDS_CTRL_CH0_DATA_WIDTH__24BIT) |
			IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_BIT_MAP, LVDS_CTRL_CH0_BIT_MAP__JEIDA);

		phy_setting = 0x4 << 5 | 0x4 << 2 | 1 << 1 | 0x1;
		writel(phy_setting, lvds_base + 0 /* PHY_CTRL*/);
		writel(mode, lvds_base + LVDS_CTRL);
		writel(0, lvds_base +  MIPIv2_CSR_TX_ULPS);
		writel(MIPI_CSR_PXL2DPI_24_BIT, lvds_base + MIPIv2_CSR_PXL2DPI);

		/* Power up PLL in MIPI DSI PHY */
		writel(0, mipi_base + MIPI_DSI_OFFSET + DPHY_PD_PLL);
		writel(0, mipi_base + MIPI_DSI_OFFSET + DPHY_PD_TX);
	}
}

int display_controller_setup(sc_pm_clock_rate_t pixel_clock)
{
	sc_err_t err;
	sc_rsrc_t dc_rsrc, pll0_rsrc, pll1_rsrc;
	sc_pm_clock_rate_t pll_clk;
	const char *pll1_pd_name;
	sc_ipc_t ipcHndl = gd->arch.ipc_channel_handle;

	int dc_id = gdc;

	struct power_domain pd;
	int ret;

	if (dc_id == 0) {
		dc_rsrc = SC_R_DC_0;
		pll0_rsrc = SC_R_DC_0_PLL_0;
		pll1_rsrc = SC_R_DC_0_PLL_1;
		pll1_pd_name = "dc0_pll1";
	} else {
		dc_rsrc = SC_R_DC_1;
		pll0_rsrc = SC_R_DC_1_PLL_0;
		pll1_rsrc = SC_R_DC_1_PLL_1;
		pll1_pd_name = "dc1_pll1";
	}

	if (!power_domain_lookup_name(pll1_pd_name, &pd)) {
		ret = power_domain_on(&pd);
		if (ret) {
			printf("%s Power up failed! (error = %d)\n", pll1_pd_name, ret);
			return -EIO;
		}
	} else {
		printf("%s lookup failed!\n", pll1_pd_name);
		return -EIO;
	}

	/* Setup the pll1/2 and DISP0/1 clock */
	if (pixel_clock >= 40000000)
		pll_clk = 1188000000;
	else
		pll_clk = 675000000;

	err = sc_pm_set_clock_rate(ipcHndl, pll0_rsrc, SC_PM_CLK_PLL, &pll_clk);
	if (err != SC_ERR_NONE) {
		printf("PLL0 set clock rate failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_set_clock_rate(ipcHndl, pll1_rsrc, SC_PM_CLK_PLL, &pll_clk);
	if (err != SC_ERR_NONE) {
		printf("PLL1 set clock rate failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_set_clock_rate(ipcHndl, dc_rsrc, SC_PM_CLK_MISC0, &pixel_clock);
	if (err != SC_ERR_NONE) {
		printf("DISP0 set clock rate failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_set_clock_rate(ipcHndl, dc_rsrc, SC_PM_CLK_MISC1, &pixel_clock);
	if (err != SC_ERR_NONE) {
		printf("DISP1 set clock rate failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, pll0_rsrc, SC_PM_CLK_PLL, true, false);
	if (err != SC_ERR_NONE) {
		printf("PLL0 clock enable failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, pll1_rsrc, SC_PM_CLK_PLL, true, false);
	if (err != SC_ERR_NONE) {
		printf("PLL1 clock enable failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, dc_rsrc, SC_PM_CLK_MISC0, true, false);
	if (err != SC_ERR_NONE) {
		printf("DISP0 clock enable failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_pm_clock_enable(ipcHndl, dc_rsrc, SC_PM_CLK_MISC1, true, false);
	if (err != SC_ERR_NONE) {
		printf("DISP1 clock enable failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST1_ADDR, 0);
	if (err != SC_ERR_NONE) {
		printf("DC Set control fSC_C_PXL_LINK_MST1_ADDR ailed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST1_ENB, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_PXL_LINK_MST1_ENB failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST1_VLD, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_PXL_LINK_MST1_VLD failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST2_ADDR, 0);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_PXL_LINK_MST2_ADDR ailed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST2_ENB, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_PXL_LINK_MST2_ENB failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_PXL_LINK_MST2_VLD, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_PXL_LINK_MST2_VLD failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_SYNC_CTRL0, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_SYNC_CTRL0 failed! (error = %d)\n", err);
		return -EIO;
	}

	err = sc_misc_set_control(ipcHndl, dc_rsrc, SC_C_SYNC_CTRL1, 1);
	if (err != SC_ERR_NONE) {
		printf("DC Set control SC_C_SYNC_CTRL1 failed! (error = %d)\n", err);
		return -EIO;
	}

	return 0;
}

void *video_hw_init(void)
{
	imxdpuv1_channel_params_t channel;
	imxdpuv1_layer_t layer;
	void *fb;

	int8_t imxdpuv1_id = gdc;

	if (imxdpuv1_id != 0 || (imxdpuv1_id == 1 && !is_imx8qm())) {
		printf("%s(): invalid imxdpuv1_id %d", __func__, imxdpuv1_id);
		return NULL;
	}

	panel.winSizeX = gmode.hlen;
	panel.winSizeY = gmode.vlen;
	panel.plnSizeX = gmode.hlen;
	panel.plnSizeY = gmode.vlen;

	panel.gdfBytesPP = 4;
	panel.gdfIndex = GDF_32BIT_X888RGB;

	panel.memSize = gmode.hlen * gmode.vlen * panel.gdfBytesPP;

	/* Allocate framebuffer */
	fb = memalign(0x1000,
		      roundup(panel.memSize, 0x1000));
	if (!fb) {
		printf("IMXDPUv1: Error allocating framebuffer!\n");
		return NULL;
	}

	/* Wipe framebuffer */
	memset(fb, 0, panel.memSize);

	panel.frameAdrs = (ulong)fb;

	imxdpuv1_init(imxdpuv1_id);
	imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, 0, IMXDPUV1_FALSE);
	imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, 1, IMXDPUV1_FALSE);

	imxdpuv1_disp_setup_frame_gen(imxdpuv1_id, gdisp,
		(const struct imxdpuv1_videomode *)&gmode,
		0x3ff, 0, 0, 1, IMXDPUV1_DISABLE);
	imxdpuv1_disp_init(imxdpuv1_id, gdisp);
	imxdpuv1_disp_setup_constframe(imxdpuv1_id,
		gdisp, 0, 0, 0xff, 0); /* blue */

	if (gdisp == 0)
		channel.common.chan = IMXDPUV1_CHAN_VIDEO_0;
	else
		channel.common.chan = IMXDPUV1_CHAN_VIDEO_1;
	channel.common.src_pixel_fmt = gpixfmt;
	channel.common.dest_pixel_fmt = gpixfmt;
	channel.common.src_width = gmode.hlen;
	channel.common.src_height = gmode.vlen;

	channel.common.clip_width = 0;
	channel.common.clip_height = 0;
	channel.common.clip_top = 0;
	channel.common.clip_left = 0;

	channel.common.dest_width = gmode.hlen;
	channel.common.dest_height = gmode.vlen;
	channel.common.dest_top = 0;
	channel.common.dest_left = 0;
	channel.common.stride =
		gmode.hlen * imxdpuv1_bytes_per_pixel(IMXDPUV1_PIX_FMT_BGRA32);
	channel.common.disp_id = gdisp;
	channel.common.const_color = 0;
	channel.common.use_global_alpha = 0;
	channel.common.use_local_alpha = 0;
	imxdpuv1_init_channel(imxdpuv1_id, &channel);

	imxdpuv1_init_channel_buffer(imxdpuv1_id,
		channel.common.chan,
		gmode.hlen * imxdpuv1_bytes_per_pixel(IMXDPUV1_PIX_FMT_RGB32),
		IMXDPUV1_ROTATE_NONE,
		(dma_addr_t)fb,
		0,
		0);

	layer.enable    = IMXDPUV1_TRUE;
	layer.secondary = get_channel_blk(channel.common.chan);

	if (gdisp == 0) {
		layer.stream    = IMXDPUV1_DISPLAY_STREAM_0;
		layer.primary   = IMXDPUV1_ID_CONSTFRAME0;
	} else {
		layer.stream    = IMXDPUV1_DISPLAY_STREAM_1;
		layer.primary   = IMXDPUV1_ID_CONSTFRAME1;
	}

	imxdpuv1_disp_setup_layer(
		imxdpuv1_id, &layer, IMXDPUV1_LAYER_0, 1);
	imxdpuv1_disp_set_layer_global_alpha(
		imxdpuv1_id, IMXDPUV1_LAYER_0, 0xff);

	imxdpuv1_disp_set_layer_position(
		imxdpuv1_id, IMXDPUV1_LAYER_0, 0, 0);
	imxdpuv1_disp_set_chan_position(
		imxdpuv1_id, channel.common.chan, 0, 0);

	imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, gdisp, IMXDPUV1_ENABLE);

	debug("IMXDPU display start ...\n");

	return &panel;
}

void imxdpuv1_fb_disable(void)
{
	/* Disable video only when video init is done */
	if (panel.frameAdrs)
		imxdpuv1_disp_enable_frame_gen(gdc, gdisp, IMXDPUV1_DISABLE);
}

int imxdpuv1_fb_init(struct fb_videomode const *mode,
		  uint8_t disp, uint32_t pixfmt)
{
	if (disp > 1) {
		printf("Invalid disp parameter %d for imxdpuv1_fb_init\n", disp);
		return -EINVAL;
	}

	memset(&gmode, 0, sizeof(struct imxdpuv1_videomode));
	gmode.pixelclock = PS2KHZ(mode->pixclock) * 1000;
	gmode.hlen = mode->xres;
	gmode.hbp = mode->left_margin;
	gmode.hfp = mode->right_margin;

	gmode.vlen = mode->yres;
	gmode.vbp = mode->upper_margin;
	gmode.vfp = mode->lower_margin;

	gmode.hsync = mode->hsync_len;
	gmode.vsync = mode->vsync_len;
	gmode.flags = IMXDPUV1_MODE_FLAGS_HSYNC_POL | IMXDPUV1_MODE_FLAGS_VSYNC_POL | IMXDPUV1_MODE_FLAGS_DE_POL;

	if (is_imx8qm()) { /* QM has two DCs each contains one LVDS as secondary display output */
		gdisp = 1;
		gdc = disp;
	} else if (is_imx8qxp()) { /* QXP has one DC which contains 2 LVDS/MIPI_DSI combo */
		gdisp = disp;
		gdc = 0;
	} else {
		printf("Unsupported SOC for imxdpuv1_fb_init\n");
		return -EPERM;
	}

	gpixfmt = pixfmt;

	debug("imxdpuv1_fb_init, dc=%d, disp=%d\n", gdc, gdisp);

	return 0;
}

