[PATCH v3 12/14] video: add nexell video driver (display/video driver)

Stefan Bosch stefan_b at posteo.net
Mon Jun 29 19:46:44 CEST 2020


Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- nexell_display.c: Changed to DM, CONFIG_FB_ADDR can not be used
  anymore because framebuffer is allocated by video_reserve() in
  video-uclass.c. Therefore code changed appropriately.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
  possible (and similar).
- livetree API (dev_read_...) is used instead of fdt one (fdt...).

Signed-off-by: Stefan Bosch <stefan_b at posteo.net>
---

Changes in v3:
- drivers/video/nexell/s5pxx18_dp_hdmi.c: '#include <log.h>' and
  '#include <linux/delay.h>' inserted because it has been removed from
  common.h.
- drivers/video/nexell_display.c: '#include <command.h>' inserted because
  '#include <image.h>' (includes command.h) has been removed from common.h.
- '#ifdef...' changed to 'if (IS_ENABLED(CONFIG...))' where possible
  because of appropriate warnings of patman.
- Changed to livetree API as proposed by patman:
  fdtdec_get_int() --> ofnode_read_s32_default()
  for (node = ...) --> ofnode_for_each_subnode()
  fdt_get_name() --> ofnode_get_name()
  fdt_getprop() --> dev_read_string()
- nx_display_probe() removed, nx_display_dev_probe_uclass() renamed to
  nx_display_probe().

 drivers/video/Kconfig                  |  10 +
 drivers/video/Makefile                 |   1 +
 drivers/video/nexell/Kconfig           |  27 ++
 drivers/video/nexell/Makefile          |  12 +
 drivers/video/nexell/s5pxx18_dp.c      | 341 +++++++++++++++++
 drivers/video/nexell/s5pxx18_dp_hdmi.c | 545 ++++++++++++++++++++++++++
 drivers/video/nexell/s5pxx18_dp_lvds.c | 274 +++++++++++++
 drivers/video/nexell/s5pxx18_dp_mipi.c | 677 +++++++++++++++++++++++++++++++++
 drivers/video/nexell/s5pxx18_dp_rgb.c  |  69 ++++
 drivers/video/nexell_display.c         | 651 +++++++++++++++++++++++++++++++
 10 files changed, 2607 insertions(+)
 create mode 100644 drivers/video/nexell/Kconfig
 create mode 100644 drivers/video/nexell/Makefile
 create mode 100644 drivers/video/nexell/s5pxx18_dp.c
 create mode 100644 drivers/video/nexell/s5pxx18_dp_hdmi.c
 create mode 100644 drivers/video/nexell/s5pxx18_dp_lvds.c
 create mode 100644 drivers/video/nexell/s5pxx18_dp_mipi.c
 create mode 100644 drivers/video/nexell/s5pxx18_dp_rgb.c
 create mode 100644 drivers/video/nexell_display.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3812354..fb145e5 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -572,6 +572,16 @@ source "drivers/video/bridge/Kconfig"
 
 source "drivers/video/imx/Kconfig"
 
+config VIDEO_NX
+	bool "Enable video support on Nexell SoC"
+	depends on ARCH_S5P6818 || ARCH_S5P4418
+	help
+	   Nexell SoC supports many video output options including eDP and
+	   HDMI. This option enables this support which can be used on devices
+	   which have an eDP display connected.
+
+source "drivers/video/nexell/Kconfig"
+
 config VIDEO
 	bool "Enable legacy video support"
 	depends on !DM_VIDEO
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index df7119d..3bacc64 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -61,6 +61,7 @@ obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o
 obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o
 obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
 obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o
+obj-$(CONFIG_VIDEO_NX) += nexell_display.o videomodes.o nexell/
 obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
 obj-$(CONFIG_VIDEO_DSI_HOST_SANDBOX) += sandbox_dsi_host.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
diff --git a/drivers/video/nexell/Kconfig b/drivers/video/nexell/Kconfig
new file mode 100644
index 0000000..54b8ccb
--- /dev/null
+++ b/drivers/video/nexell/Kconfig
@@ -0,0 +1,27 @@
+if VIDEO_NX
+
+menu "LCD select"
+
+config VIDEO_NX_RGB
+	bool "RGB LCD"
+	help
+	  Support for RGB lcd output.
+
+config VIDEO_NX_LVDS
+	bool "LVDS LCD"
+	help
+	  Support for LVDS lcd output.
+
+config VIDEO_NX_MIPI
+	bool "MiPi"
+	help
+	  Support for MiPi lcd output.
+
+config VIDEO_NX_HDMI
+	bool "HDMI"
+	help
+	  Support for hdmi output.
+
+endmenu
+
+endif
diff --git a/drivers/video/nexell/Makefile b/drivers/video/nexell/Makefile
new file mode 100644
index 0000000..111ab45
--- /dev/null
+++ b/drivers/video/nexell/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Junghyun, kim<jhkim at nexell.co.kr>
+
+obj-$(CONFIG_VIDEO_NX) += s5pxx18_dp.o
+obj-$(CONFIG_VIDEO_NX) += soc/
+
+obj-$(CONFIG_VIDEO_NX_RGB)  += s5pxx18_dp_rgb.o
+obj-$(CONFIG_VIDEO_NX_LVDS) += s5pxx18_dp_lvds.o
+obj-$(CONFIG_VIDEO_NX_MIPI) += s5pxx18_dp_mipi.o
+obj-$(CONFIG_VIDEO_NX_HDMI) += s5pxx18_dp_hdmi.o
diff --git a/drivers/video/nexell/s5pxx18_dp.c b/drivers/video/nexell/s5pxx18_dp.c
new file mode 100644
index 0000000..2248f47
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/nexell.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_dpc.h"
+#include "soc/s5pxx18_soc_mlc.h"
+
+#define	MLC_LAYER_RGB_0		0	/* number of RGB layer 0 */
+#define	MLC_LAYER_RGB_1		1	/* number of RGB layer 1 */
+#define	MLC_LAYER_VIDEO		3	/* number of Video layer: 3 = VIDEO */
+
+#define	__io_address(a)	(void *)(uintptr_t)(a)
+
+void dp_control_init(int module)
+{
+	void *base;
+
+	/* top */
+	base = __io_address(nx_disp_top_get_physical_address());
+	nx_disp_top_set_base_address(base);
+
+	/* control */
+	base = __io_address(nx_dpc_get_physical_address(module));
+	nx_dpc_set_base_address(module, base);
+
+	/* top controller */
+	nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_NEGATE);
+
+	/* display controller */
+	nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_NEGATE);
+
+	nx_dpc_set_clock_pclk_mode(module, nx_pclkmode_always);
+}
+
+int dp_control_setup(int module,
+		     struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
+{
+	unsigned int out_format;
+	unsigned int delay_mask;
+	int rgb_pvd = 0, hsync_cp1 = 7, vsync_fram = 7, de_cp2 = 7;
+	int v_vso = 1, v_veo = 1, e_vso = 1, e_veo = 1;
+
+	int interlace = 0;
+	int invert_field;
+	int swap_rb;
+	unsigned int yc_order;
+	int vck_select;
+	int vclk_invert;
+	int emb_sync;
+
+	enum nx_dpc_dither r_dither, g_dither, b_dither;
+	int rgb_mode = 0;
+
+	if (NULL == sync || NULL == ctrl) {
+		debug("error, dp.%d not set sync or pad clock info !!!\n",
+		      module);
+		return -EINVAL;
+	}
+
+	out_format = ctrl->out_format;
+	delay_mask = ctrl->delay_mask;
+	interlace = sync->interlace;
+	invert_field = ctrl->invert_field;
+	swap_rb = ctrl->swap_RB;
+	yc_order = ctrl->yc_order;
+	vck_select = ctrl->vck_select;
+	vclk_invert = ctrl->clk_inv_lv0 | ctrl->clk_inv_lv1;
+	emb_sync = (out_format == DPC_FORMAT_CCIR656 ? 1 : 0);
+
+	/* set delay mask */
+	if (delay_mask & DP_SYNC_DELAY_RGB_PVD)
+		rgb_pvd = ctrl->d_rgb_pvd;
+	if (delay_mask & DP_SYNC_DELAY_HSYNC_CP1)
+		hsync_cp1 = ctrl->d_hsync_cp1;
+	if (delay_mask & DP_SYNC_DELAY_VSYNC_FRAM)
+		vsync_fram = ctrl->d_vsync_fram;
+	if (delay_mask & DP_SYNC_DELAY_DE_CP)
+		de_cp2 = ctrl->d_de_cp2;
+
+	if (ctrl->vs_start_offset != 0 ||
+	    ctrl->vs_end_offset != 0 ||
+	    ctrl->ev_start_offset != 0 || ctrl->ev_end_offset != 0) {
+		v_vso = ctrl->vs_start_offset;
+		v_veo = ctrl->vs_end_offset;
+		e_vso = ctrl->ev_start_offset;
+		e_veo = ctrl->ev_end_offset;
+	}
+
+	if (nx_dpc_format_rgb555 == out_format ||
+	    nx_dpc_format_mrgb555a == out_format ||
+	    nx_dpc_format_mrgb555b == out_format) {
+		r_dither = nx_dpc_dither_5bit;
+		g_dither = nx_dpc_dither_5bit;
+		b_dither = nx_dpc_dither_5bit;
+		rgb_mode = 1;
+	} else if (nx_dpc_format_rgb565 == out_format ||
+		       nx_dpc_format_mrgb565 == out_format) {
+		r_dither = nx_dpc_dither_5bit;
+		b_dither = nx_dpc_dither_5bit;
+		g_dither = nx_dpc_dither_6bit, rgb_mode = 1;
+	} else if ((nx_dpc_format_rgb666 == out_format) ||
+		   (nx_dpc_format_mrgb666 == out_format)) {
+		r_dither = nx_dpc_dither_6bit;
+		g_dither = nx_dpc_dither_6bit;
+		b_dither = nx_dpc_dither_6bit;
+		rgb_mode = 1;
+	} else {
+		r_dither = nx_dpc_dither_bypass;
+		g_dither = nx_dpc_dither_bypass;
+		b_dither = nx_dpc_dither_bypass;
+		rgb_mode = 1;
+	}
+
+	/* CLKGEN0/1 */
+	nx_dpc_set_clock_source(module, 0, ctrl->clk_src_lv0 == 3 ?
+				6 : ctrl->clk_src_lv0);
+	nx_dpc_set_clock_divisor(module, 0, ctrl->clk_div_lv0);
+	nx_dpc_set_clock_source(module, 1, ctrl->clk_src_lv1);
+	nx_dpc_set_clock_divisor(module, 1, ctrl->clk_div_lv1);
+	nx_dpc_set_clock_out_delay(module, 0, ctrl->clk_delay_lv0);
+	nx_dpc_set_clock_out_delay(module, 1, ctrl->clk_delay_lv1);
+
+	/* LCD out */
+	nx_dpc_set_mode(module, out_format, interlace, invert_field,
+			rgb_mode, swap_rb, yc_order, emb_sync, emb_sync,
+			vck_select, vclk_invert, 0);
+	nx_dpc_set_hsync(module, sync->h_active_len, sync->h_sync_width,
+			 sync->h_front_porch, sync->h_back_porch,
+			 sync->h_sync_invert);
+	nx_dpc_set_vsync(module, sync->v_active_len, sync->v_sync_width,
+			 sync->v_front_porch, sync->v_back_porch,
+			 sync->v_sync_invert, sync->v_active_len,
+			 sync->v_sync_width, sync->v_front_porch,
+			 sync->v_back_porch);
+	nx_dpc_set_vsync_offset(module, v_vso, v_veo, e_vso, e_veo);
+	nx_dpc_set_delay(module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2);
+	nx_dpc_set_dither(module, r_dither, g_dither, b_dither);
+
+	if (IS_ENABLED(CONFIG_MACH_S5P6818)) {
+		/* Set TFT_CLKCTRL (offset : 1030h)
+		 * Field name : DPC0_CLKCTRL, DPC1_CLKCRL
+		 * Default value : clk_inv_lv0/1 = 0 : PADCLK_InvCLK
+		 * Invert case   : clk_inv_lv0/1 = 1 : PADCLK_CLK
+		 */
+		if (module == 0 && ctrl->clk_inv_lv0)
+			nx_disp_top_set_padclock(padmux_primary_mlc,
+						 padclk_clk);
+		if (module == 1 && ctrl->clk_inv_lv1)
+			nx_disp_top_set_padclock(padmux_secondary_mlc,
+						 padclk_clk);
+	}
+
+	debug("%s: dp.%d x:%4d, hf:%3d, hb:%3d, hs:%3d, hi=%d\n",
+	      __func__, module, sync->h_active_len, sync->h_front_porch,
+	      sync->h_back_porch, sync->h_sync_width, sync->h_sync_invert);
+	debug("%s: dp.%d y:%4d, vf:%3d, vb:%3d, vs:%3d, vi=%d\n",
+	      __func__, module, sync->v_active_len, sync->v_front_porch,
+	      sync->v_back_porch, sync->v_sync_width, sync->h_sync_invert);
+	debug("%s: dp.%d ck.0:%d:%d:%d, ck.1:%d:%d:%d\n",
+	      __func__, module,
+	      ctrl->clk_src_lv0, ctrl->clk_div_lv0, ctrl->clk_inv_lv0,
+	      ctrl->clk_src_lv1, ctrl->clk_div_lv1, ctrl->clk_inv_lv1);
+	debug("%s: dp.%d vs:%d, ve:%d, es:%d, ee:%d\n",
+	      __func__, module, v_vso, v_veo, e_vso, e_veo);
+	debug("%s: dp.%d delay RGB:%d, hs:%d, vs:%d, de:%d, fmt:0x%x\n",
+	      __func__, module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2,
+	      out_format);
+
+	return 0;
+}
+
+void dp_control_enable(int module, int on)
+{
+	debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
+
+	nx_dpc_set_dpc_enable(module, on);
+	nx_dpc_set_clock_divisor_enable(module, on);
+}
+
+void dp_plane_init(int module)
+{
+	void *base = __io_address(nx_mlc_get_physical_address(module));
+
+	nx_mlc_set_base_address(module, base);
+	nx_mlc_set_clock_pclk_mode(module, nx_pclkmode_always);
+	nx_mlc_set_clock_bclk_mode(module, nx_bclkmode_always);
+}
+
+int dp_plane_screen_setup(int module, struct dp_plane_top *top)
+{
+	int width = top->screen_width;
+	int height = top->screen_height;
+	int interlace = top->interlace;
+	int video_prior = top->video_prior;
+	unsigned int bg_color = top->back_color;
+
+	/* MLC TOP layer */
+	nx_mlc_set_screen_size(module, width, height);
+	nx_mlc_set_layer_priority(module, video_prior);
+	nx_mlc_set_background(module, bg_color);
+	nx_mlc_set_field_enable(module, interlace);
+	nx_mlc_set_rgblayer_gama_table_power_mode(module, 0, 0, 0);
+	nx_mlc_set_rgblayer_gama_table_sleep_mode(module, 1, 1, 1);
+	nx_mlc_set_rgblayer_gamma_enable(module, 0);
+	nx_mlc_set_dither_enable_when_using_gamma(module, 0);
+	nx_mlc_set_gamma_priority(module, 0);
+	nx_mlc_set_top_power_mode(module, 1);
+	nx_mlc_set_top_sleep_mode(module, 0);
+
+	debug("%s: dp.%d screen %dx%d, %s, priority:%d, bg:0x%x\n",
+	      __func__, module, width, height,
+	      interlace ? "Interlace" : "Progressive",
+	      video_prior, bg_color);
+
+	return 0;
+}
+
+void dp_plane_screen_enable(int module, int on)
+{
+	/* enable top screen */
+	nx_mlc_set_mlc_enable(module, on);
+	nx_mlc_set_top_dirty_flag(module);
+	debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
+}
+
+int dp_plane_layer_setup(int module, struct dp_plane_info *plane)
+{
+	int sx = plane->left;
+	int sy = plane->top;
+	int ex = sx + plane->width - 1;
+	int ey = sy + plane->height - 1;
+	int pixel_byte = plane->pixel_byte;
+	int mem_lock_size = 16;	/* fix mem lock size */
+	int layer = plane->layer;
+	unsigned int format = plane->format;
+
+	if (!plane->enable)
+		return -EINVAL;
+
+	/* MLC layer */
+	nx_mlc_set_lock_size(module, layer, mem_lock_size);
+	nx_mlc_set_alpha_blending(module, layer, 0, 15);
+	nx_mlc_set_transparency(module, layer, 0, 0);
+	nx_mlc_set_color_inversion(module, layer, 0, 0);
+	nx_mlc_set_rgblayer_invalid_position(module, layer, 0, 0, 0, 0, 0, 0);
+	nx_mlc_set_rgblayer_invalid_position(module, layer, 1, 0, 0, 0, 0, 0);
+	nx_mlc_set_format_rgb(module, layer, format);
+	nx_mlc_set_position(module, layer, sx, sy, ex, ey);
+	nx_mlc_set_rgblayer_stride(module, layer, pixel_byte,
+				   plane->width * pixel_byte);
+	nx_mlc_set_rgblayer_address(module, layer, plane->fb_base);
+
+	debug("%s: dp.%d.%d %d * %d, %dbpp, fmt:0x%x\n",
+	      __func__, module, layer, plane->width, plane->height,
+	      pixel_byte * 8, format);
+	debug("%s: b:0x%x, l:%d, t:%d, r:%d, b:%d, hs:%d, vs:%d\n",
+	      __func__, plane->fb_base, sx, sy, ex, ey,
+	      plane->width * pixel_byte, pixel_byte);
+
+	return 0;
+}
+
+int dp_plane_set_enable(int module, int layer, int on)
+{
+	int hl, hc;
+	int vl, vc;
+
+	debug("%s: dp.%d.%d %s:%s\n",
+	      __func__, module, layer,
+	      layer == MLC_LAYER_VIDEO ? "Video" : "RGB",
+	      on ? "ON" : "OFF");
+
+	if (layer != MLC_LAYER_VIDEO) {
+		nx_mlc_set_layer_enable(module, layer, on);
+		nx_mlc_set_dirty_flag(module, layer);
+		return 0;
+	}
+
+	/* video layer */
+	if (on) {
+		nx_mlc_set_video_layer_line_buffer_power_mode(module, 1);
+		nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 0);
+		nx_mlc_set_layer_enable(module, layer, 1);
+		nx_mlc_set_dirty_flag(module, layer);
+	} else {
+		nx_mlc_set_layer_enable(module, layer, 0);
+		nx_mlc_set_dirty_flag(module, layer);
+		nx_mlc_get_video_layer_scale_filter(module,
+						    &hl, &hc, &vl, &vc);
+		if (hl || hc || vl || vc)
+			nx_mlc_set_video_layer_scale_filter(module, 0, 0, 0, 0);
+		nx_mlc_set_video_layer_line_buffer_power_mode(module, 0);
+		nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 1);
+		nx_mlc_set_dirty_flag(module, layer);
+	}
+
+	return 0;
+}
+
+void dp_plane_layer_enable(int module,
+			   struct dp_plane_info *plane, int on)
+{
+	dp_plane_set_enable(module, plane->layer, on);
+}
+
+int dp_plane_set_address(int module, int layer, unsigned int address)
+{
+	nx_mlc_set_rgblayer_address(module, layer, address);
+	nx_mlc_set_dirty_flag(module, layer);
+
+	return 0;
+}
+
+int dp_plane_wait_vsync(int module, int layer, int fps)
+{
+	int cnt = 0;
+
+	if (fps == 0)
+		return (int)nx_mlc_get_dirty_flag(module, layer);
+
+	while (fps > cnt++) {
+		while (nx_mlc_get_dirty_flag(module, layer))
+			;
+		nx_mlc_set_dirty_flag(module, layer);
+	}
+	return 0;
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_hdmi.c b/drivers/video/nexell/s5pxx18_dp_hdmi.c
new file mode 100644
index 0000000..3f1fb8a
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_hdmi.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/tieoff.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include <linux/delay.h>
+
+#include "soc/s5pxx18_soc_dpc.h"
+#include "soc/s5pxx18_soc_hdmi.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define	__io_address(a)	(void *)(uintptr_t)(a)
+
+static const u8 hdmiphy_preset74_25[32] = {
+	0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0xc8, 0x81,
+	0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
+	0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
+	0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x10, 0x80,
+};
+
+static const u8 hdmiphy_preset148_5[32] = {
+	0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0xc8, 0x81,
+	0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
+	0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
+	0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+};
+
+#define HDMIPHY_PRESET_TABLE_SIZE   (32)
+
+enum NXP_HDMI_PRESET {
+	NXP_HDMI_PRESET_720P = 0,	/* 1280 x 720 */
+	NXP_HDMI_PRESET_1080P,	/* 1920 x 1080 */
+	NXP_HDMI_PRESET_MAX
+};
+
+static void hdmi_reset(void)
+{
+	nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_NEGATE);
+}
+
+static int hdmi_phy_enable(int preset, int enable)
+{
+	const u8 *table = NULL;
+	int size = 0;
+	u32 addr, i = 0;
+
+	if (!enable)
+		return 0;
+
+	switch (preset) {
+	case NXP_HDMI_PRESET_720P:
+		table = hdmiphy_preset74_25;
+		size = 32;
+		break;
+	case NXP_HDMI_PRESET_1080P:
+		table = hdmiphy_preset148_5;
+		size = 31;
+		break;
+	default:
+		printf("hdmi: phy not support preset %d\n", preset);
+		return -EINVAL;
+	}
+
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
+
+	for (i = 0, addr = HDMI_PHY_REG04; size > i; i++, addr += 4) {
+		nx_hdmi_set_reg(0, addr, table[i]);
+		nx_hdmi_set_reg(0, addr, table[i]);
+	}
+
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
+	nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
+	debug("%s: preset = %d\n", __func__, preset);
+
+	return 0;
+}
+
+static inline int hdmi_wait_phy_ready(void)
+{
+	int count = 500;
+
+	do {
+		u32 val = nx_hdmi_get_reg(0, HDMI_LINK_PHY_STATUS_0);
+
+		if (val & 0x01) {
+			printf("HDMI:  phy ready...\n");
+			return 1;
+		}
+		mdelay(10);
+	} while (count--);
+
+	return 0;
+}
+
+static inline int hdmi_get_vsync(int preset,
+				 struct dp_sync_info *sync,
+				 struct dp_ctrl_info *ctrl)
+{
+	switch (preset) {
+	case NXP_HDMI_PRESET_720P:	/* 720p: 1280x720 */
+		sync->h_active_len = 1280;
+		sync->h_sync_width = 40;
+		sync->h_back_porch = 220;
+		sync->h_front_porch = 110;
+		sync->h_sync_invert = 0;
+		sync->v_active_len = 720;
+		sync->v_sync_width = 5;
+		sync->v_back_porch = 20;
+		sync->v_front_porch = 5;
+		sync->v_sync_invert = 0;
+		break;
+
+	case NXP_HDMI_PRESET_1080P:	/* 1080p: 1920x1080 */
+		sync->h_active_len = 1920;
+		sync->h_sync_width = 44;
+		sync->h_back_porch = 148;
+		sync->h_front_porch = 88;
+		sync->h_sync_invert = 0;
+		sync->v_active_len = 1080;
+		sync->v_sync_width = 5;
+		sync->v_back_porch = 36;
+		sync->v_front_porch = 4;
+		sync->v_sync_invert = 0;
+		break;
+	default:
+		printf("HDMI: not support preset sync %d\n", preset);
+		return -EINVAL;
+	}
+
+	ctrl->clk_src_lv0 = 4;
+	ctrl->clk_div_lv0 = 1;
+	ctrl->clk_src_lv1 = 7;
+	ctrl->clk_div_lv1 = 1;
+
+	ctrl->out_format = outputformat_rgb888;
+	ctrl->delay_mask = (DP_SYNC_DELAY_RGB_PVD | DP_SYNC_DELAY_HSYNC_CP1 |
+			    DP_SYNC_DELAY_VSYNC_FRAM | DP_SYNC_DELAY_DE_CP);
+	ctrl->d_rgb_pvd = 0;
+	ctrl->d_hsync_cp1 = 0;
+	ctrl->d_vsync_fram = 0;
+	ctrl->d_de_cp2 = 7;
+
+	/* HFP + HSW + HBP + AVWidth-VSCLRPIXEL- 1; */
+	ctrl->vs_start_offset = (sync->h_front_porch + sync->h_sync_width +
+				 sync->h_back_porch + sync->h_active_len - 1);
+	ctrl->vs_end_offset = 0;
+
+	/* HFP + HSW + HBP + AVWidth-EVENVSCLRPIXEL- 1 */
+	ctrl->ev_start_offset = (sync->h_front_porch + sync->h_sync_width +
+				 sync->h_back_porch + sync->h_active_len - 1);
+	ctrl->ev_end_offset = 0;
+	debug("%s: preset: %d\n", __func__, preset);
+
+	return 0;
+}
+
+static void hdmi_clock(void)
+{
+	void *base =
+	    __io_address(nx_disp_top_clkgen_get_physical_address
+			 (to_mipi_clkgen));
+
+	nx_disp_top_clkgen_set_base_address(to_mipi_clkgen, base);
+	nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 0);
+	nx_disp_top_clkgen_set_clock_pclk_mode(to_mipi_clkgen,
+					       nx_pclkmode_always);
+	nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
+					    2);
+	nx_disp_top_clkgen_set_clock_divisor(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
+					     2);
+	nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, 1, 7);
+	nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 1);
+
+	/* must initialize this !!! */
+	nx_disp_top_hdmi_set_vsync_hsstart_end(0, 0);
+	nx_disp_top_hdmi_set_vsync_start(0);
+	nx_disp_top_hdmi_set_hactive_start(0);
+	nx_disp_top_hdmi_set_hactive_end(0);
+}
+
+static void hdmi_vsync(struct dp_sync_info *sync)
+{
+	int width = sync->h_active_len;
+	int hsw = sync->h_sync_width;
+	int hbp = sync->h_back_porch;
+	int height = sync->v_active_len;
+	int vsw = sync->v_sync_width;
+	int vbp = sync->v_back_porch;
+
+	int v_sync_s = vsw + vbp + height - 1;
+	int h_active_s = hsw + hbp;
+	int h_active_e = width + hsw + hbp;
+	int v_sync_hs_se0 = hsw + hbp + 1;
+	int v_sync_hs_se1 = hsw + hbp + 2;
+
+	nx_disp_top_hdmi_set_vsync_start(v_sync_s);
+	nx_disp_top_hdmi_set_hactive_start(h_active_s);
+	nx_disp_top_hdmi_set_hactive_end(h_active_e);
+	nx_disp_top_hdmi_set_vsync_hsstart_end(v_sync_hs_se0, v_sync_hs_se1);
+}
+
+static int hdmi_prepare(struct dp_sync_info *sync)
+{
+	int width = sync->h_active_len;
+	int hsw = sync->h_sync_width;
+	int hfp = sync->h_front_porch;
+	int hbp = sync->h_back_porch;
+	int height = sync->v_active_len;
+	int vsw = sync->v_sync_width;
+	int vfp = sync->v_front_porch;
+	int vbp = sync->v_back_porch;
+
+	u32 h_blank, h_line, h_sync_start, h_sync_end;
+	u32 v_blank, v2_blank, v_line;
+	u32 v_sync_line_bef_1, v_sync_line_bef_2;
+
+	u32 fixed_ffff = 0xffff;
+
+	/* calculate sync variables */
+	h_blank = hfp + hsw + hbp;
+	v_blank = vfp + vsw + vbp;
+	v2_blank = height + vfp + vsw + vbp;
+	v_line = height + vfp + vsw + vbp;	/* total v */
+	h_line = width + hfp + hsw + hbp;	/* total h */
+	h_sync_start = hfp;
+	h_sync_end = hfp + hsw;
+	v_sync_line_bef_1 = vfp;
+	v_sync_line_bef_2 = vfp + vsw;
+
+	/* no blue screen mode, encoding order as it is */
+	nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0, (0 << 5) | (1 << 4));
+
+	/* set HDMI_LINK_BLUE_SCREEN_* to 0x0 */
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_0, 0x5555);
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_1, 0x5555);
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_0, 0x5555);
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_1, 0x5555);
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_0, 0x5555);
+	nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_1, 0x5555);
+
+	/* set HDMI_CON_1 to 0x0 */
+	nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_1, 0x0);
+	nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_2, 0x0);
+
+	/* set interrupt : enable hpd_plug, hpd_unplug */
+	nx_hdmi_set_reg(0, HDMI_LINK_INTC_CON_0,
+			(1 << 6) | (1 << 3) | (1 << 2));
+
+	/* set STATUS_EN to 0x17 */
+	nx_hdmi_set_reg(0, HDMI_LINK_STATUS_EN, 0x17);
+
+	/* TODO set HDP to 0x0 : later check hpd */
+	nx_hdmi_set_reg(0, HDMI_LINK_HPD, 0x0);
+
+	/* set MODE_SEL to 0x02 */
+	nx_hdmi_set_reg(0, HDMI_LINK_MODE_SEL, 0x2);
+
+	/* set H_BLANK_*, V1_BLANK_*, V2_BLANK_*, V_LINE_*,
+	 * H_LINE_*, H_SYNC_START_*, H_SYNC_END_ *
+	 * V_SYNC_LINE_BEF_1_*, V_SYNC_LINE_BEF_2_*
+	 */
+	nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_0, h_blank % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_1, h_blank >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_0, v_blank % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_1, v_blank >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_0, v2_blank % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_1, v2_blank >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_0, v_line % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_1, v_line >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_0, h_line % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_1, h_line >> 8);
+
+	if (width == 1280) {
+		nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x1);
+		nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x1);
+	} else {
+		nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x0);
+		nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x0);
+	}
+
+	nx_hdmi_set_reg(0, HDMI_LINK_INT_PRO_MODE, 0x0);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_0, (h_sync_start % 256) - 2);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_1, h_sync_start >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_0, (h_sync_end % 256) - 2);
+	nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_1, h_sync_end >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_0,
+			v_sync_line_bef_1 % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_1,
+			v_sync_line_bef_1 >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_0,
+			v_sync_line_bef_2 % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_1,
+			v_sync_line_bef_2 >> 8);
+
+	/* Set V_SYNC_LINE_AFT*, V_SYNC_LINE_AFT_PXL*, VACT_SPACE* */
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_1, fixed_ffff >> 8);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_1, fixed_ffff >> 8);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_1, fixed_ffff >> 8);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_0, fixed_ffff % 256);
+	nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_1, fixed_ffff >> 8);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_CSC_MUX, 0x0);
+	nx_hdmi_set_reg(0, HDMI_LINK_SYNC_GEN_MUX, 0x0);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_0, 0xfd);
+	nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_1, 0x01);
+	nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_0, 0x0d);
+	nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_1, 0x3a);
+	nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_2, 0x08);
+
+	/* Set DC_CONTROL to 0x00 */
+	nx_hdmi_set_reg(0, HDMI_LINK_DC_CONTROL, 0x0);
+
+	if (IS_ENABLED(CONFIG_HDMI_PATTERN))
+		nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x1);
+	else
+		nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x0);
+
+	nx_hdmi_set_reg(0, HDMI_LINK_GCP_CON, 0x0a);
+	return 0;
+}
+
+static void hdmi_init(void)
+{
+	void *base;
+   /**
+    * [SEQ 2] set the HDMI CLKGEN's PCLKMODE to always enabled
+    */
+	base =
+	    __io_address(nx_disp_top_clkgen_get_physical_address(hdmi_clkgen));
+	nx_disp_top_clkgen_set_base_address(hdmi_clkgen, base);
+	nx_disp_top_clkgen_set_clock_pclk_mode(hdmi_clkgen, nx_pclkmode_always);
+
+	base = __io_address(nx_hdmi_get_physical_address(0));
+	nx_hdmi_set_base_address(0, base);
+
+    /**
+     * [SEQ 3] set the 0xC001100C[0] to 1
+     */
+	nx_tieoff_set(NX_TIEOFF_DISPLAYTOP0_i_HDMI_PHY_REFCLK_SEL, 1);
+
+    /**
+     * [SEQ 4] release the resets of HDMI.i_PHY_nRST and HDMI.i_nRST
+     */
+	nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_NEGATE);
+}
+
+void hdmi_enable(int input, int preset, struct dp_sync_info *sync, int enable)
+{
+	if (enable) {
+		nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0,
+				(nx_hdmi_get_reg(0, HDMI_LINK_HDMI_CON_0) |
+				 0x1));
+		hdmi_vsync(sync);
+	} else {
+		hdmi_phy_enable(preset, 0);
+	}
+}
+
+static int hdmi_setup(int input, int preset,
+		      struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
+{
+	u32 HDMI_SEL = 0;
+	int ret;
+
+	switch (input) {
+	case DP_DEVICE_DP0:
+		HDMI_SEL = primary_mlc;
+		break;
+	case DP_DEVICE_DP1:
+		HDMI_SEL = secondary_mlc;
+		break;
+	case DP_DEVICE_RESCONV:
+		HDMI_SEL = resolution_conv;
+		break;
+	default:
+		printf("HDMI: not support source device %d\n", input);
+		return -EINVAL;
+	}
+
+	/**
+	 * [SEQ 5] set up the HDMI PHY to specific video clock.
+	 */
+	ret = hdmi_phy_enable(preset, 1);
+	if (ret < 0)
+		return ret;
+
+	/**
+	 * [SEQ 6] I2S (or SPDIFTX) configuration for the source audio data
+	 * this is done in another user app  - ex> Android Audio HAL
+	 */
+
+	/**
+	 * [SEQ 7] Wait for ECID ready
+	 */
+
+	/**
+	 * [SEQ 8] release the resets of HDMI.i_VIDEO_nRST and HDMI.i_SPDIF_nRST
+	 * and HDMI.i_TMDS_nRST
+	 */
+	hdmi_reset();
+
+	/**
+	 * [SEQ 9] Wait for HDMI PHY ready (wait until 0xC0200020.[0], 1)
+	 */
+	if (hdmi_wait_phy_ready() == 0) {
+		printf("%s: failed to wait for hdmiphy ready\n", __func__);
+		hdmi_phy_enable(preset, 0);
+		return -EIO;
+	}
+	/* set mux */
+	nx_disp_top_set_hdmimux(1, HDMI_SEL);
+
+	/**
+	 * [SEC 10] Set the DPC CLKGEN's Source Clock to HDMI_CLK &
+	 * Set Sync Parameter
+	 */
+	hdmi_clock();
+	/* set hdmi link clk to clkgen  vs default is hdmi phy clk */
+
+	/**
+	 * [SEQ 11] Set up the HDMI Converter parameters
+	 */
+	hdmi_get_vsync(preset, sync, ctrl);
+	hdmi_prepare(sync);
+
+	return 0;
+}
+
+void nx_hdmi_display(int module,
+		     struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		     struct dp_plane_top *top, struct dp_plane_info *planes,
+		     struct dp_hdmi_dev *dev)
+{
+	struct dp_plane_info *plane = planes;
+	int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+	int count = top->plane_num;
+	int preset = dev->preset;
+	int i = 0;
+
+	debug("HDMI:  display.%d\n", module);
+
+	switch (preset) {
+	case 0:
+		top->screen_width = 1280;
+		top->screen_height = 720;
+		sync->h_active_len = 1280;
+		sync->v_active_len = 720;
+		break;
+	case 1:
+		top->screen_width = 1920;
+		top->screen_height = 1080;
+		sync->h_active_len = 1920;
+		sync->v_active_len = 1080;
+		break;
+	default:
+		printf("hdmi not support preset %d\n", preset);
+		return;
+	}
+
+	printf("HDMI:  display.%d, preset %d (%4d * %4d)\n",
+	       module, preset, top->screen_width, top->screen_height);
+
+	dp_control_init(module);
+	dp_plane_init(module);
+
+	hdmi_init();
+	hdmi_setup(input, preset, sync, ctrl);
+
+	dp_plane_screen_setup(module, top);
+	for (i = 0; count > i; i++, plane++) {
+		if (!plane->enable)
+			continue;
+		dp_plane_layer_setup(module, plane);
+		dp_plane_layer_enable(module, plane, 1);
+	}
+	dp_plane_screen_enable(module, 1);
+
+	dp_control_setup(module, sync, ctrl);
+	dp_control_enable(module, 1);
+
+	hdmi_enable(input, preset, sync, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_lvds.c b/drivers/video/nexell/s5pxx18_dp_lvds.c
new file mode 100644
index 0000000..f8ea63f
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_lvds.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_lvds.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define	__io_address(a)	(void *)(uintptr_t)(a)
+
+static void lvds_phy_reset(void)
+{
+	nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_NEGATE);
+}
+
+static void lvds_init(void)
+{
+	int clkid = DP_CLOCK_LVDS;
+	int index = 0;
+	void *base;
+
+	base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
+	nx_disp_top_clkgen_set_base_address(clkid, base);
+
+	nx_lvds_initialize();
+
+	for (index = 0; nx_lvds_get_number_of_module() > index; index++)
+		nx_lvds_set_base_address(index,
+		  (void *)__io_address(nx_lvds_get_physical_address(index)));
+
+	nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
+}
+
+static void lvds_enable(int enable)
+{
+	int clkid = DP_CLOCK_LVDS;
+	int on = (enable ? 1 : 0);
+
+	nx_disp_top_clkgen_set_clock_divisor_enable(clkid, on);
+}
+
+static int lvds_setup(int module, int input,
+		      struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		      struct dp_lvds_dev *dev)
+{
+	unsigned int val;
+	int clkid = DP_CLOCK_LVDS;
+	enum dp_lvds_format format = DP_LVDS_FORMAT_JEIDA;
+	u32 voltage = DEF_VOLTAGE_LEVEL;
+
+	if (dev) {
+		format = dev->lvds_format;
+		voltage = dev->voltage_level;
+	}
+
+	printf("LVDS:  ");
+	printf("%s, ", format == DP_LVDS_FORMAT_VESA ? "VESA" :
+		format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC");
+	printf("voltage LV:0x%x\n", voltage);
+
+	/*
+	 *-------- predefined type.
+	 * only change iTA to iTE in VESA mode
+	 * wire [34:0] loc_VideoIn =
+	 * {4'hf, 4'h0, i_VDEN, i_VSYNC, i_HSYNC, i_VD[23:0] };
+	 */
+	u32 VSYNC = 25;
+	u32 HSYNC = 24;
+	u32 VDEN  = 26; /* bit position */
+	u32 ONE   = 34;
+	u32 ZERO  = 27;
+
+	/*====================================================
+	 * current not use location mode
+	 *====================================================
+	 */
+	u32 LOC_A[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
+	u32 LOC_B[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
+	u32 LOC_C[7] = {VDEN, VSYNC, HSYNC, ONE, HSYNC, VSYNC, VDEN};
+	u32 LOC_D[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
+	u32 LOC_E[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
+
+	switch (input) {
+	case DP_DEVICE_DP0:
+		input = 0;
+		break;
+	case DP_DEVICE_DP1:
+		input = 1;
+		break;
+	case DP_DEVICE_RESCONV:
+		input = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * select TOP MUX
+	 */
+	nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 0);
+	nx_disp_top_clkgen_set_clock_source(clkid, 0, ctrl->clk_src_lv0);
+	nx_disp_top_clkgen_set_clock_divisor(clkid, 0, ctrl->clk_div_lv0);
+	nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv1);
+	nx_disp_top_clkgen_set_clock_divisor(clkid, 1, ctrl->clk_div_lv1);
+
+	/*
+	 * LVDS Control Pin Setting
+	 */
+	val = (0 << 30) |      /* CPU_I_VBLK_FLAG_SEL */
+	      (0 << 29) |      /* CPU_I_BVLK_FLAG */
+	      (1 << 28) |      /* SKINI_BST  */
+	      (1 << 27) |      /* DLYS_BST  */
+	      (0 << 26) |      /* I_AUTO_SEL */
+	      (format << 19) | /* JEiDA data packing */
+	      (0x1B << 13)   | /* I_LOCK_PPM_SET, PPM setting for PLL lock */
+	      (0x638 << 1);    /* I_DESKEW_CNT_SEL, period of de-skew region */
+	nx_lvds_set_lvdsctrl0(0, val);
+
+	val = (0 << 28) |   /* I_ATE_MODE, function mode */
+	      (0 << 27) |   /* I_TEST_CON_MODE, DA (test ctrl mode) */
+	      (0 << 24) |   /* I_TX4010X_DUMMY */
+	      (0 << 15) |   /* SKCCK 0 */
+	      (0 << 12) |   /* SKC4 (TX output skew control pin at ODD ch4) */
+	      (0 << 9)  |   /* SKC3 (TX output skew control pin at ODD ch3) */
+	      (0 << 6)  |   /* SKC2 (TX output skew control pin at ODD ch2) */
+	      (0 << 3)  |   /* SKC1 (TX output skew control pin at ODD ch1) */
+	      (0 << 0);     /* SKC0 (TX output skew control pin at ODD ch0) */
+	nx_lvds_set_lvdsctrl1(0, val);
+
+	val = (0 << 15)   | /* CK_POL_SEL, Input clock, bypass */
+	      (0 << 14)   | /* VSEL, VCO Freq. range. 0: Low(40MHz~90MHz),
+			     *                        1: High(90MHz~160MHz) */
+	      (0x1 << 12) | /* S (Post-scaler) */
+	      (0xA << 6)  | /* M (Main divider) */
+	      (0xA << 0);   /* P (Pre-divider) */
+
+	nx_lvds_set_lvdsctrl2(0, val);
+	val = (0x03 << 6) | /* SK_BIAS, Bias current ctrl pin */
+	      (0 << 5)    | /* SKEWINI, skew selection pin, 0: bypass,
+			     *                              1: skew enable */
+	      (0 << 4)    | /* SKEW_EN_H, skew block power down, 0: power down,
+			     *                                   1: operating */
+	      (1 << 3)    | /* CNTB_TDLY, delay control pin */
+	      (0 << 2)    | /* SEL_DATABF, input clock 1/2 division cont. pin */
+	      (0x3 << 0);   /* SKEW_REG_CUR, regulator bias current selection
+			     *               in SKEW block */
+
+	nx_lvds_set_lvdsctrl3(0, val);
+	val = (0 << 28)   | /* FLT_CNT, filter control pin for PLL */
+	      (0 << 27)   | /* VOD_ONLY_CNT, the pre-emphasis's pre-diriver
+			     *               control pin (VOD only) */
+	      (0 << 26)   | /* CNNCT_MODE_SEL, connectivity mode selection,
+			     *                 0:TX operating, 1:con check */
+	      (0 << 24)   | /* CNNCT_CNT, connectivity ctrl pin,
+			     *            0: tx operating, 1: con check */
+	      (0 << 23)   | /* VOD_HIGH_S, VOD control pin, 1: Vod only */
+	      (0 << 22)   | /* SRC_TRH, source termination resistor sel. pin */
+	      (voltage << 14) |
+	      (0x01 << 6) | /* CNT_PEN_H, TX driver pre-emphasis level cont. */
+	      (0x4 << 3)  | /* FC_CODE, vos control pin */
+	      (0 << 2)    | /* OUTCON, TX Driver state selectioin pin, 0:Hi-z,
+			     *                                         1:Low */
+	      (0 << 1)    | /* LOCK_CNT, Lock signal selection pin, enable */
+	      (0 << 0);     /* AUTO_DSK_SEL, auto deskew sel. pin, normal */
+	nx_lvds_set_lvdsctrl4(0, val);
+
+	val = (0 << 24) |   /* I_BIST_RESETB */
+	      (0 << 23) |   /* I_BIST_EN */
+	      (0 << 21) |   /* I_BIST_PAT_SEL */
+	      (0 << 14) |   /* I_BIST_USER_PATTERN */
+	      (0 << 13) |   /* I_BIST_FORCE_ERROR */
+	      (0 << 7)  |   /* I_BIST_SKEW_CTRL */
+	      (0 << 5)  |   /* I_BIST_CLK_INV */
+	      (0 << 3)  |   /* I_BIST_DATA_INV */
+	      (0 << 0);     /* I_BIST_CH_SEL */
+	nx_lvds_set_lvdstmode0(0, val);
+
+	/* user do not need to modify this codes. */
+	val = (LOC_A[4] << 24) | (LOC_A[3] << 18) | (LOC_A[2] << 12) |
+	      (LOC_A[1] << 6)  | (LOC_A[0] << 0);
+	nx_lvds_set_lvdsloc0(0, val);
+
+	val = (LOC_B[2] << 24) | (LOC_B[1] << 18) | (LOC_B[0] << 12) |
+	      (LOC_A[6] << 6)  | (LOC_A[5] << 0);
+	nx_lvds_set_lvdsloc1(0, val);
+
+	val = (LOC_C[0] << 24) | (LOC_B[6] << 18) | (LOC_B[5] << 12) |
+	      (LOC_B[4] << 6)  | (LOC_B[3] << 0);
+	nx_lvds_set_lvdsloc2(0, val);
+
+	val = (LOC_C[5] << 24) | (LOC_C[4] << 18) | (LOC_C[3] << 12) |
+	      (LOC_C[2] << 6)  | (LOC_C[1] << 0);
+	nx_lvds_set_lvdsloc3(0, val);
+
+	val = (LOC_D[3] << 24) | (LOC_D[2] << 18) | (LOC_D[1] << 12) |
+	      (LOC_D[0] << 6)  | (LOC_C[6] << 0);
+	nx_lvds_set_lvdsloc4(0, val);
+
+	val = (LOC_E[1] << 24) | (LOC_E[0] << 18) | (LOC_D[6] << 12) |
+	      (LOC_D[5] << 6)  | (LOC_D[4] << 0);
+	nx_lvds_set_lvdsloc5(0, val);
+
+	val = (LOC_E[6] << 24) | (LOC_E[5] << 18) | (LOC_E[4] << 12) |
+	      (LOC_E[3] << 6)  | (LOC_E[2] << 0);
+	nx_lvds_set_lvdsloc6(0, val);
+
+	nx_lvds_set_lvdslocmask0(0, 0xffffffff);
+	nx_lvds_set_lvdslocmask1(0, 0xffffffff);
+
+	nx_lvds_set_lvdslocpol0(0, (0 << 19) | (0 << 18));
+
+	/*
+	 * select TOP MUX
+	 */
+	nx_disp_top_set_lvdsmux(1, input);
+
+	/*
+	 * LVDS PHY Reset, make sure last.
+	 */
+	lvds_phy_reset();
+
+	return 0;
+}
+
+void nx_lvds_display(int module,
+		     struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		     struct dp_plane_top *top, struct dp_plane_info *planes,
+		     struct dp_lvds_dev *dev)
+{
+	struct dp_plane_info *plane = planes;
+	int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+	int count = top->plane_num;
+	int i = 0;
+
+	printf("LVDS:  dp.%d\n", module);
+
+	dp_control_init(module);
+	dp_plane_init(module);
+
+	lvds_init();
+
+	/* set plane */
+	dp_plane_screen_setup(module, top);
+
+	for (i = 0; count > i; i++, plane++) {
+		if (!plane->enable)
+			continue;
+		dp_plane_layer_setup(module, plane);
+		dp_plane_layer_enable(module, plane, 1);
+	}
+
+	dp_plane_screen_enable(module, 1);
+
+	/* set lvds */
+	lvds_setup(module, input, sync, ctrl, dev);
+
+	lvds_enable(1);
+
+	/* set dp control */
+	dp_control_setup(module, sync, ctrl);
+	dp_control_enable(module, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_mipi.c b/drivers/video/nexell/s5pxx18_dp_mipi.c
new file mode 100644
index 0000000..670272b
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_mipi.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/tieoff.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_mipi.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define	PLLPMS_1000MHZ		0x33E8
+#define	BANDCTL_1000MHZ		0xF
+#define PLLPMS_960MHZ       0x2280
+#define BANDCTL_960MHZ      0xF
+#define	PLLPMS_900MHZ		0x2258
+#define	BANDCTL_900MHZ		0xE
+#define	PLLPMS_840MHZ		0x2230
+#define	BANDCTL_840MHZ		0xD
+#define	PLLPMS_750MHZ		0x43E8
+#define	BANDCTL_750MHZ		0xC
+#define	PLLPMS_660MHZ		0x21B8
+#define	BANDCTL_660MHZ		0xB
+#define	PLLPMS_600MHZ		0x2190
+#define	BANDCTL_600MHZ		0xA
+#define	PLLPMS_540MHZ		0x2168
+#define	BANDCTL_540MHZ		0x9
+#define	PLLPMS_512MHZ		0x03200
+#define	BANDCTL_512MHZ		0x9
+#define	PLLPMS_480MHZ		0x2281
+#define	BANDCTL_480MHZ		0x8
+#define	PLLPMS_420MHZ		0x2231
+#define	BANDCTL_420MHZ		0x7
+#define	PLLPMS_402MHZ		0x2219
+#define	BANDCTL_402MHZ		0x7
+#define	PLLPMS_330MHZ		0x21B9
+#define	BANDCTL_330MHZ		0x6
+#define	PLLPMS_300MHZ		0x2191
+#define	BANDCTL_300MHZ		0x5
+#define	PLLPMS_210MHZ		0x2232
+#define	BANDCTL_210MHZ		0x4
+#define	PLLPMS_180MHZ		0x21E2
+#define	BANDCTL_180MHZ		0x3
+#define	PLLPMS_150MHZ		0x2192
+#define	BANDCTL_150MHZ		0x2
+#define	PLLPMS_100MHZ		0x3323
+#define	BANDCTL_100MHZ		0x1
+#define	PLLPMS_80MHZ		0x3283
+#define	BANDCTL_80MHZ		0x0
+
+#define	MIPI_INDEX		0
+#define MIPI_EXC_PRE_VALUE      1
+#define MIPI_DSI_IRQ_MASK       29
+
+#define	__io_address(a)	(void *)(uintptr_t)(a)
+
+struct mipi_xfer_msg {
+	u8 id, data[2];
+	u16 flags;
+	const u8 *tx_buf;
+	u16 tx_len;
+	u8 *rx_buf;
+	u16 rx_len;
+};
+
+static void mipi_reset(void)
+{
+	/* tieoff */
+	nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA, 3);
+	nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB, 3);
+
+	/* reset */
+	nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_MIPI_CSI, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_ASSERT);
+	nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_ASSERT);
+
+	nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_NEGATE);
+	nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_NEGATE);
+}
+
+static void mipi_init(void)
+{
+	int clkid = DP_CLOCK_MIPI;
+	void *base;
+
+	/*
+	 * neet to reset before open
+	 */
+	mipi_reset();
+
+	base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
+	nx_disp_top_clkgen_set_base_address(clkid, base);
+	nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
+
+	base = __io_address(nx_mipi_get_physical_address(0));
+	nx_mipi_set_base_address(0, base);
+}
+
+static int mipi_get_phy_pll(int bitrate, unsigned int *pllpms,
+			    unsigned int *bandctl)
+{
+	unsigned int pms, ctl;
+
+	switch (bitrate) {
+	case 1000:
+		pms = PLLPMS_1000MHZ;
+		ctl = BANDCTL_1000MHZ;
+		break;
+	case 960:
+		pms = PLLPMS_960MHZ;
+		ctl = BANDCTL_960MHZ;
+		break;
+	case 900:
+		pms = PLLPMS_900MHZ;
+		ctl = BANDCTL_900MHZ;
+		break;
+	case 840:
+		pms = PLLPMS_840MHZ;
+		ctl = BANDCTL_840MHZ;
+		break;
+	case 750:
+		pms = PLLPMS_750MHZ;
+		ctl = BANDCTL_750MHZ;
+		break;
+	case 660:
+		pms = PLLPMS_660MHZ;
+		ctl = BANDCTL_660MHZ;
+		break;
+	case 600:
+		pms = PLLPMS_600MHZ;
+		ctl = BANDCTL_600MHZ;
+		break;
+	case 540:
+		pms = PLLPMS_540MHZ;
+		ctl = BANDCTL_540MHZ;
+		break;
+	case 512:
+		pms = PLLPMS_512MHZ;
+		ctl = BANDCTL_512MHZ;
+		break;
+	case 480:
+		pms = PLLPMS_480MHZ;
+		ctl = BANDCTL_480MHZ;
+		break;
+	case 420:
+		pms = PLLPMS_420MHZ;
+		ctl = BANDCTL_420MHZ;
+		break;
+	case 402:
+		pms = PLLPMS_402MHZ;
+		ctl = BANDCTL_402MHZ;
+		break;
+	case 330:
+		pms = PLLPMS_330MHZ;
+		ctl = BANDCTL_330MHZ;
+		break;
+	case 300:
+		pms = PLLPMS_300MHZ;
+		ctl = BANDCTL_300MHZ;
+		break;
+	case 210:
+		pms = PLLPMS_210MHZ;
+		ctl = BANDCTL_210MHZ;
+		break;
+	case 180:
+		pms = PLLPMS_180MHZ;
+		ctl = BANDCTL_180MHZ;
+		break;
+	case 150:
+		pms = PLLPMS_150MHZ;
+		ctl = BANDCTL_150MHZ;
+		break;
+	case 100:
+		pms = PLLPMS_100MHZ;
+		ctl = BANDCTL_100MHZ;
+		break;
+	case 80:
+		pms = PLLPMS_80MHZ;
+		ctl = BANDCTL_80MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*pllpms = pms;
+	*bandctl = ctl;
+
+	return 0;
+}
+
+static int mipi_prepare(int module, int input,
+			struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+			struct dp_mipi_dev *mipi)
+{
+	int index = MIPI_INDEX;
+	u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
+	int lpm = mipi->lpm_trans;
+	int ret = 0;
+
+	ret = mipi_get_phy_pll(mipi->hs_bitrate,
+			       &mipi->hs_pllpms, &mipi->hs_bandctl);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_get_phy_pll(mipi->lp_bitrate,
+			       &mipi->lp_pllpms, &mipi->lp_bandctl);
+	if (ret < 0)
+		return ret;
+
+	debug("%s: mipi lp:%dmhz:0x%x:0x%x, hs:%dmhz:0x%x:0x%x, %s trans\n",
+	      __func__, mipi->lp_bitrate, mipi->lp_pllpms, mipi->lp_bandctl,
+	      mipi->hs_bitrate, mipi->hs_pllpms, mipi->hs_bandctl,
+	      lpm ? "low" : "high");
+
+	if (lpm)
+		nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+				    mipi->lp_pllpms, mipi->lp_bandctl, 0, 0);
+	else
+		nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+				    mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
+
+#ifdef CONFIG_ARCH_S5P4418
+	/*
+	 * disable the escape clock generating prescaler
+	 * before soft reset.
+	 */
+	nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 0, 10);
+	mdelay(1);
+#endif
+
+	nx_mipi_dsi_software_reset(index);
+	nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value);
+	nx_mipi_dsi_set_phy(index, 0, 1, 1, 0, 0, 0, 0, 0);
+
+	if (lpm)
+		nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_lp,
+					  nx_mipi_dsi_lpmode_lp);
+	else
+		nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs,
+					  nx_mipi_dsi_lpmode_hs);
+	mdelay(20);
+
+	return 0;
+}
+
+static int mipi_enable(int module, int input,
+		       struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		       struct dp_mipi_dev *mipi)
+{
+	struct mipi_dsi_device *dsi = &mipi->dsi;
+	int clkid = DP_CLOCK_MIPI;
+	int index = MIPI_INDEX;
+	int width = sync->h_active_len;
+	int height = sync->v_active_len;
+	int HFP = sync->h_front_porch;
+	int HBP = sync->h_back_porch;
+	int HS = sync->h_sync_width;
+	int VFP = sync->v_front_porch;
+	int VBP = sync->v_back_porch;
+	int VS = sync->v_sync_width;
+	int en_prescaler = 1;
+	u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
+
+	int txhsclock = 1;
+	int lpm = mipi->lpm_trans;
+	bool command_mode = mipi->command_mode;
+
+	enum nx_mipi_dsi_format dsi_format;
+	int data_len = dsi->lanes - 1;
+	bool burst = dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST ? true : false;
+	bool eot_enable = dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET ?
+	    false : true;
+
+	/*
+	 * disable the escape clock generating prescaler
+	 * before soft reset.
+	 */
+#ifdef CONFIG_ARCH_S5P4418
+	en_prescaler = 0;
+#endif
+
+	debug("%s: mode:%s, lanes.%d\n", __func__,
+	      command_mode ? "command" : "video", data_len + 1);
+
+	if (lpm)
+		nx_mipi_dsi_set_escape_lp(index,
+					  nx_mipi_dsi_lpmode_hs,
+					  nx_mipi_dsi_lpmode_hs);
+
+	nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+			    mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
+	mdelay(1);
+
+	nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, en_prescaler, 10);
+	mdelay(1);
+
+	nx_mipi_dsi_software_reset(index);
+	nx_mipi_dsi_set_clock(index, txhsclock, 0, 1,
+			      1, 1, 0, 0, 0, 1, esc_pre_value);
+
+	switch (data_len) {
+	case 0:		/* 1 lane */
+		nx_mipi_dsi_set_phy(index, data_len, 1, 1, 0, 0, 0, 0, 0);
+		break;
+	case 1:		/* 2 lane */
+		nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 0, 0, 0, 0);
+		break;
+	case 2:		/* 3 lane */
+		nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 0, 0, 0);
+		break;
+	case 3:		/* 3 lane */
+		nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 1, 0, 0);
+		break;
+	default:
+		printf("%s: not support data lanes %d\n",
+		       __func__, data_len + 1);
+		return -EINVAL;
+	}
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB565:
+		dsi_format = nx_mipi_dsi_format_rgb565;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		dsi_format = nx_mipi_dsi_format_rgb666;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		dsi_format = nx_mipi_dsi_format_rgb666_packed;
+		break;
+	case MIPI_DSI_FMT_RGB888:
+		dsi_format = nx_mipi_dsi_format_rgb888;
+		break;
+	default:
+		printf("%s: not support format %d\n", __func__, dsi->format);
+		return -EINVAL;
+	}
+
+	nx_mipi_dsi_set_config_video_mode(index, 1, 0, burst,
+					  nx_mipi_dsi_syncmode_event,
+					  eot_enable, 1, 1, 1, 1, 0, dsi_format,
+					  HFP, HBP, HS, VFP, VBP, VS, 0);
+
+	nx_mipi_dsi_set_size(index, width, height);
+
+	/* set mux */
+	nx_disp_top_set_mipimux(1, module);
+
+	/*  0 is spdif, 1 is mipi vclk */
+	nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv0);
+	nx_disp_top_clkgen_set_clock_divisor(clkid, 1,
+					     ctrl->clk_div_lv1 *
+					     ctrl->clk_div_lv0);
+
+	/* SPDIF and MIPI */
+	nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 1);
+
+	/* START: CLKGEN, MIPI is started in setup function */
+	nx_disp_top_clkgen_set_clock_divisor_enable(clkid, true);
+	nx_mipi_dsi_set_enable(index, true);
+
+	return 0;
+}
+
+static int nx_mipi_transfer_tx(struct mipi_dsi_device *dsi,
+			       struct mipi_xfer_msg *xfer)
+{
+	const u8 *txb;
+	int size, index = 0;
+	u32 data;
+
+	if (xfer->tx_len > DSI_TX_FIFO_SIZE)
+		printf("warn: tx %d size over fifo %d\n",
+		       (int)xfer->tx_len, DSI_TX_FIFO_SIZE);
+
+	/* write payload */
+	size = xfer->tx_len;
+	txb = xfer->tx_buf;
+
+	while (size >= 4) {
+		data = (txb[3] << 24) | (txb[2] << 16) |
+		    (txb[1] << 8) | (txb[0]);
+		nx_mipi_dsi_write_payload(index, data);
+		txb += 4, size -= 4;
+		data = 0;
+	}
+
+	switch (size) {
+	case 3:
+		data |= txb[2] << 16;
+	case 2:
+		data |= txb[1] << 8;
+	case 1:
+		data |= txb[0];
+		nx_mipi_dsi_write_payload(index, data);
+		break;
+	case 0:
+		break;		/* no payload */
+	}
+
+	/* write packet hdr */
+	data = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->id;
+
+	nx_mipi_dsi_write_pkheader(index, data);
+
+	return 0;
+}
+
+static int nx_mipi_transfer_done(struct mipi_dsi_device *dsi)
+{
+	int index = 0, count = 100;
+	u32 value;
+
+	do {
+		mdelay(1);
+		value = nx_mipi_dsi_read_fifo_status(index);
+		if (((1 << 22) & value))
+			break;
+	} while (count-- > 0);
+
+	if (count < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int nx_mipi_transfer_rx(struct mipi_dsi_device *dsi,
+			       struct mipi_xfer_msg *xfer)
+{
+	u8 *rxb = xfer->rx_buf;
+	int index = 0, rx_len = 0;
+	u32 data, count = 0;
+	u16 size;
+	int err = -EINVAL;
+
+	nx_mipi_dsi_clear_interrupt_pending(index, 18);
+
+	while (1) {
+		/* Completes receiving data. */
+		if (nx_mipi_dsi_get_interrupt_pending(index, 18))
+			break;
+
+		mdelay(1);
+
+		if (count > 500) {
+			printf("%s: error recevice data\n", __func__);
+			err = -EINVAL;
+			goto clear_fifo;
+		} else {
+			count++;
+		}
+	}
+
+	data = nx_mipi_dsi_read_fifo(index);
+
+	switch (data & 0x3f) {
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+		if (xfer->rx_len >= 2) {
+			rxb[1] = data >> 16;
+			rx_len++;
+		}
+
+		/* Fall through */
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+		rxb[0] = data >> 8;
+		rx_len++;
+		xfer->rx_len = rx_len;
+		err = rx_len;
+		goto clear_fifo;
+
+	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+		printf("DSI Error Report: 0x%04x\n", (data >> 8) & 0xffff);
+		err = rx_len;
+		goto clear_fifo;
+	}
+
+	size = (data >> 8) & 0xffff;
+
+	if (size > xfer->rx_len)
+		size = xfer->rx_len;
+	else if (size < xfer->rx_len)
+		xfer->rx_len = size;
+
+	size = xfer->rx_len - rx_len;
+	rx_len += size;
+
+	/* Receive payload */
+	while (size >= 4) {
+		data = nx_mipi_dsi_read_fifo(index);
+		rxb[0] = (data >> 0) & 0xff;
+		rxb[1] = (data >> 8) & 0xff;
+		rxb[2] = (data >> 16) & 0xff;
+		rxb[3] = (data >> 24) & 0xff;
+		rxb += 4, size -= 4;
+	}
+
+	if (size) {
+		data = nx_mipi_dsi_read_fifo(index);
+		switch (size) {
+		case 3:
+			rxb[2] = (data >> 16) & 0xff;
+		case 2:
+			rxb[1] = (data >> 8) & 0xff;
+		case 1:
+			rxb[0] = data & 0xff;
+		}
+	}
+
+	if (rx_len == xfer->rx_len)
+		err = rx_len;
+
+clear_fifo:
+	size = DSI_RX_FIFO_SIZE / 4;
+	do {
+		data = nx_mipi_dsi_read_fifo(index);
+		if (data == DSI_RX_FIFO_EMPTY)
+			break;
+	} while (--size);
+
+	return err;
+}
+
+#define	IS_SHORT(t)	(9 > ((t) & 0x0f))
+
+static int nx_mipi_transfer(struct mipi_dsi_device *dsi,
+			    const struct mipi_dsi_msg *msg)
+{
+	struct mipi_xfer_msg xfer;
+	int err;
+
+	if (!msg->tx_len)
+		return -EINVAL;
+
+	/* set id */
+	xfer.id = msg->type | (msg->channel << 6);
+
+	/* short type msg */
+	if (IS_SHORT(msg->type)) {
+		const char *txb = msg->tx_buf;
+
+		if (msg->tx_len > 2)
+			return -EINVAL;
+
+		xfer.tx_len = 0;	/* no payload */
+		xfer.data[0] = txb[0];
+		xfer.data[1] = (msg->tx_len == 2) ? txb[1] : 0;
+		xfer.tx_buf = NULL;
+	} else {
+		xfer.tx_len = msg->tx_len;
+		xfer.data[0] = msg->tx_len & 0xff;
+		xfer.data[1] = msg->tx_len >> 8;
+		xfer.tx_buf = msg->tx_buf;
+	}
+
+	xfer.rx_len = msg->rx_len;
+	xfer.rx_buf = msg->rx_buf;
+	xfer.flags = msg->flags;
+
+	err = nx_mipi_transfer_tx(dsi, &xfer);
+
+	if (xfer.rx_len)
+		err = nx_mipi_transfer_rx(dsi, &xfer);
+
+	nx_mipi_transfer_done(dsi);
+
+	return err;
+}
+
+static ssize_t nx_mipi_write_buffer(struct mipi_dsi_device *dsi,
+				    const void *data, size_t len)
+{
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.tx_buf = data,
+		.tx_len = len
+	};
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		msg.type = MIPI_DSI_DCS_LONG_WRITE;
+		break;
+	}
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+		msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+	return nx_mipi_transfer(dsi, &msg);
+}
+
+__weak int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi)
+{
+	return 0;
+}
+
+/*
+ * disply
+ * MIPI DSI Setting
+ *		(1) Initiallize MIPI(DSIM,DPHY,PLL)
+ *		(2) Initiallize LCD
+ *		(3) ReInitiallize MIPI(DSIM only)
+ *		(4) Turn on display(MLC,DPC,...)
+ */
+void nx_mipi_display(int module,
+		     struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		     struct dp_plane_top *top, struct dp_plane_info *planes,
+		     struct dp_mipi_dev *dev)
+{
+	struct dp_plane_info *plane = planes;
+	struct mipi_dsi_device *dsi = &dev->dsi;
+	int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+	int count = top->plane_num;
+	int i = 0, ret;
+
+	printf("MIPI: dp.%d\n", module);
+
+	/* map mipi-dsi write callback func */
+	dsi->write_buffer = nx_mipi_write_buffer;
+
+	ret = nx_mipi_dsi_lcd_bind(dsi);
+	if (ret) {
+		printf("Error: bind mipi-dsi lcd driver !\n");
+		return;
+	}
+
+	dp_control_init(module);
+	dp_plane_init(module);
+
+	mipi_init();
+
+	/* set plane */
+	dp_plane_screen_setup(module, top);
+
+	for (i = 0; count > i; i++, plane++) {
+		if (!plane->enable)
+			continue;
+		dp_plane_layer_setup(module, plane);
+		dp_plane_layer_enable(module, plane, 1);
+	}
+	dp_plane_screen_enable(module, 1);
+
+	/* set mipi */
+	mipi_prepare(module, input, sync, ctrl, dev);
+
+	if (dsi->ops && dsi->ops->prepare)
+		dsi->ops->prepare(dsi);
+
+	if (dsi->ops && dsi->ops->enable)
+		dsi->ops->enable(dsi);
+
+	mipi_enable(module, input, sync, ctrl, dev);
+
+	/* set dp control */
+	dp_control_setup(module, sync, ctrl);
+	dp_control_enable(module, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_rgb.c b/drivers/video/nexell/s5pxx18_dp_rgb.c
new file mode 100644
index 0000000..44e8edb
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_rgb.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_disptop.h"
+
+static int rgb_switch(int module, int input, struct dp_sync_info *sync,
+		      struct dp_rgb_dev *dev)
+{
+	int mpu = dev->lcd_mpu_type;
+	int rsc = 0, sel = 0;
+
+	switch (module) {
+	case 0:
+		sel = mpu ? 1 : 0;
+		break;
+	case 1:
+		sel = rsc ? 3 : 2;
+		break;
+	default:
+		printf("Fail, %s nuknown module %d\n", __func__, module);
+		return -1;
+	}
+
+	nx_disp_top_set_primary_mux(sel);
+	return 0;
+}
+
+void nx_rgb_display(int module,
+		    struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+		    struct dp_plane_top *top, struct dp_plane_info *planes,
+		    struct dp_rgb_dev *dev)
+{
+	struct dp_plane_info *plane = planes;
+	int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+	int count = top->plane_num;
+	int i = 0;
+
+	printf("RGB:   dp.%d\n", module);
+
+	dp_control_init(module);
+	dp_plane_init(module);
+
+	/* set plane */
+	dp_plane_screen_setup(module, top);
+
+	for (i = 0; count > i; i++, plane++) {
+		if (!plane->enable)
+			continue;
+		dp_plane_layer_setup(module, plane);
+		dp_plane_layer_enable(module, plane, 1);
+	}
+
+	dp_plane_screen_enable(module, 1);
+
+	rgb_switch(module, input, sync, dev);
+
+	dp_control_setup(module, sync, ctrl);
+	dp_control_enable(module, 1);
+}
diff --git a/drivers/video/nexell_display.c b/drivers/video/nexell_display.c
new file mode 100644
index 0000000..4101e09
--- /dev/null
+++ b/drivers/video/nexell_display.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim at nexell.co.kr>
+ *
+ * Copyright (C) 2020  Stefan Bosch <stefan_b at posteo.net>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <malloc.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <video.h>		/* For struct video_uc_platdata */
+#include <video_fb.h>
+#include <lcd.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/arch/display.h>
+#include <asm/arch/display_dev.h>
+#include "videomodes.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if !defined(CONFIG_DM) && !defined(CONFIG_OF_CONTROL)
+static struct nx_display_dev *dp_dev;
+#endif
+
+static char *const dp_dev_str[] = {
+	[DP_DEVICE_RESCONV] = "RESCONV",
+	[DP_DEVICE_RGBLCD] = "LCD",
+	[DP_DEVICE_HDMI] = "HDMI",
+	[DP_DEVICE_MIPI] = "MiPi",
+	[DP_DEVICE_LVDS] = "LVDS",
+	[DP_DEVICE_CVBS] = "TVOUT",
+	[DP_DEVICE_DP0] = "DP0",
+	[DP_DEVICE_DP1] = "DP1",
+};
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static void nx_display_parse_dp_sync(ofnode node, struct dp_sync_info *sync)
+{
+	sync->h_active_len = ofnode_read_s32_default(node, "h_active_len", 0);
+	sync->h_sync_width = ofnode_read_s32_default(node, "h_sync_width", 0);
+	sync->h_back_porch = ofnode_read_s32_default(node, "h_back_porch", 0);
+	sync->h_front_porch = ofnode_read_s32_default(node, "h_front_porch", 0);
+	sync->h_sync_invert = ofnode_read_s32_default(node, "h_sync_invert", 0);
+	sync->v_active_len = ofnode_read_s32_default(node, "v_active_len", 0);
+	sync->v_sync_width = ofnode_read_s32_default(node, "v_sync_width", 0);
+	sync->v_back_porch = ofnode_read_s32_default(node, "v_back_porch", 0);
+	sync->v_front_porch = ofnode_read_s32_default(node, "v_front_porch", 0);
+	sync->v_sync_invert = ofnode_read_s32_default(node, "v_sync_invert", 0);
+	sync->pixel_clock_hz = ofnode_read_s32_default(node, "pixel_clock_hz", 0);
+
+	debug("DP: sync ->\n");
+	debug("ha:%d, hs:%d, hb:%d, hf:%d, hi:%d\n",
+	      sync->h_active_len, sync->h_sync_width,
+	      sync->h_back_porch, sync->h_front_porch, sync->h_sync_invert);
+	debug("va:%d, vs:%d, vb:%d, vf:%d, vi:%d\n",
+	      sync->v_active_len, sync->v_sync_width,
+	      sync->v_back_porch, sync->v_front_porch, sync->v_sync_invert);
+}
+
+static void nx_display_parse_dp_ctrl(ofnode node, struct dp_ctrl_info *ctrl)
+{
+	/* clock gen */
+	ctrl->clk_src_lv0 = ofnode_read_s32_default(node, "clk_src_lv0", 0);
+	ctrl->clk_div_lv0 = ofnode_read_s32_default(node, "clk_div_lv0", 0);
+	ctrl->clk_src_lv1 = ofnode_read_s32_default(node, "clk_src_lv1", 0);
+	ctrl->clk_div_lv1 = ofnode_read_s32_default(node, "clk_div_lv1", 0);
+
+	/* scan format */
+	ctrl->interlace = ofnode_read_s32_default(node, "interlace", 0);
+
+	/* syncgen format */
+	ctrl->out_format = ofnode_read_s32_default(node, "out_format", 0);
+	ctrl->invert_field = ofnode_read_s32_default(node, "invert_field", 0);
+	ctrl->swap_RB = ofnode_read_s32_default(node, "swap_RB", 0);
+	ctrl->yc_order = ofnode_read_s32_default(node, "yc_order", 0);
+
+	/* extern sync delay */
+	ctrl->delay_mask = ofnode_read_s32_default(node, "delay_mask", 0);
+	ctrl->d_rgb_pvd = ofnode_read_s32_default(node, "d_rgb_pvd", 0);
+	ctrl->d_hsync_cp1 = ofnode_read_s32_default(node, "d_hsync_cp1", 0);
+	ctrl->d_vsync_fram = ofnode_read_s32_default(node, "d_vsync_fram", 0);
+	ctrl->d_de_cp2 = ofnode_read_s32_default(node, "d_de_cp2", 0);
+
+	/* extern sync delay */
+	ctrl->vs_start_offset =
+	    ofnode_read_s32_default(node, "vs_start_offset", 0);
+	ctrl->vs_end_offset = ofnode_read_s32_default(node, "vs_end_offset", 0);
+	ctrl->ev_start_offset =
+	    ofnode_read_s32_default(node, "ev_start_offset", 0);
+	ctrl->ev_end_offset = ofnode_read_s32_default(node, "ev_end_offset", 0);
+
+	/* pad clock seletor */
+	ctrl->vck_select = ofnode_read_s32_default(node, "vck_select", 0);
+	ctrl->clk_inv_lv0 = ofnode_read_s32_default(node, "clk_inv_lv0", 0);
+	ctrl->clk_delay_lv0 = ofnode_read_s32_default(node, "clk_delay_lv0", 0);
+	ctrl->clk_inv_lv1 = ofnode_read_s32_default(node, "clk_inv_lv1", 0);
+	ctrl->clk_delay_lv1 = ofnode_read_s32_default(node, "clk_delay_lv1", 0);
+	ctrl->clk_sel_div1 = ofnode_read_s32_default(node, "clk_sel_div1", 0);
+
+	debug("DP: ctrl [%s] ->\n",
+	      ctrl->interlace ? "Interlace" : " Progressive");
+	debug("cs0:%d, cd0:%d, cs1:%d, cd1:%d\n",
+	      ctrl->clk_src_lv0, ctrl->clk_div_lv0,
+	      ctrl->clk_src_lv1, ctrl->clk_div_lv1);
+	debug("fmt:0x%x, inv:%d, swap:%d, yb:0x%x\n",
+	      ctrl->out_format, ctrl->invert_field,
+	      ctrl->swap_RB, ctrl->yc_order);
+	debug("dm:0x%x, drp:%d, dhs:%d, dvs:%d, dde:0x%x\n",
+	      ctrl->delay_mask, ctrl->d_rgb_pvd,
+	      ctrl->d_hsync_cp1, ctrl->d_vsync_fram, ctrl->d_de_cp2);
+	debug("vss:%d, vse:%d, evs:%d, eve:%d\n",
+	      ctrl->vs_start_offset, ctrl->vs_end_offset,
+	      ctrl->ev_start_offset, ctrl->ev_end_offset);
+	debug("sel:%d, i0:%d, d0:%d, i1:%d, d1:%d, s1:%d\n",
+	      ctrl->vck_select, ctrl->clk_inv_lv0, ctrl->clk_delay_lv0,
+	      ctrl->clk_inv_lv1, ctrl->clk_delay_lv1, ctrl->clk_sel_div1);
+}
+
+static void nx_display_parse_dp_top_layer(ofnode node, struct dp_plane_top *top)
+{
+	top->screen_width = ofnode_read_s32_default(node, "screen_width", 0);
+	top->screen_height = ofnode_read_s32_default(node, "screen_height", 0);
+	top->video_prior = ofnode_read_s32_default(node, "video_prior", 0);
+	top->interlace = ofnode_read_s32_default(node, "interlace", 0);
+	top->back_color = ofnode_read_s32_default(node, "back_color", 0);
+	top->plane_num = DP_PLANS_NUM;
+
+	debug("DP: top [%s] ->\n",
+	      top->interlace ? "Interlace" : " Progressive");
+	debug("w:%d, h:%d, prior:%d, bg:0x%x\n",
+	      top->screen_width, top->screen_height,
+	      top->video_prior, top->back_color);
+}
+
+static void nx_display_parse_dp_layer(ofnode node, struct dp_plane_info *plane)
+{
+	plane->left = ofnode_read_s32_default(node, "left", 0);
+	plane->width = ofnode_read_s32_default(node, "width", 0);
+	plane->top = ofnode_read_s32_default(node, "top", 0);
+	plane->height = ofnode_read_s32_default(node, "height", 0);
+	plane->pixel_byte = ofnode_read_s32_default(node, "pixel_byte", 0);
+	plane->format = ofnode_read_s32_default(node, "format", 0);
+	plane->alpha_on = ofnode_read_s32_default(node, "alpha_on", 0);
+	plane->alpha_depth = ofnode_read_s32_default(node, "alpha", 0);
+	plane->tp_on = ofnode_read_s32_default(node, "tp_on", 0);
+	plane->tp_color = ofnode_read_s32_default(node, "tp_color", 0);
+
+	/* enable layer */
+	if (plane->fb_base)
+		plane->enable = 1;
+	else
+		plane->enable = 0;
+
+	if (plane->fb_base == 0) {
+		printf("fail : dp plane.%d invalid fb base [0x%x] ->\n",
+		       plane->layer, plane->fb_base);
+		return;
+	}
+
+	debug("DP: plane.%d [0x%x] ->\n", plane->layer, plane->fb_base);
+	debug("f:0x%x, l:%d, t:%d, %d * %d, bpp:%d, a:%d(%d), t:%d(0x%x)\n",
+	      plane->format, plane->left, plane->top, plane->width,
+	      plane->height, plane->pixel_byte, plane->alpha_on,
+	      plane->alpha_depth, plane->tp_on, plane->tp_color);
+}
+
+static void nx_display_parse_dp_planes(ofnode node,
+				       struct nx_display_dev *dp,
+				       struct video_uc_platdata *plat)
+{
+	const char *name;
+	ofnode subnode;
+
+	ofnode_for_each_subnode(subnode, node) {
+		name = ofnode_get_name(subnode);
+
+		if (strcmp(name, "layer_top") == 0)
+			nx_display_parse_dp_top_layer(subnode, &dp->top);
+
+		/*
+		 * TODO: Is it sure that only one layer is used? Otherwise
+		 * fb_base must be different?
+		 */
+		if (strcmp(name, "layer_0") == 0) {
+			dp->planes[0].fb_base =
+			      (uint)map_sysmem(plat->base, plat->size);
+			debug("%s(): dp->planes[0].fb_base == 0x%x\n", __func__,
+			      (uint)dp->planes[0].fb_base);
+			nx_display_parse_dp_layer(subnode, &dp->planes[0]);
+		}
+
+		if (strcmp(name, "layer_1") == 0) {
+			dp->planes[1].fb_base =
+			      (uint)map_sysmem(plat->base, plat->size);
+			debug("%s(): dp->planes[1].fb_base == 0x%x\n", __func__,
+			      (uint)dp->planes[1].fb_base);
+			nx_display_parse_dp_layer(subnode, &dp->planes[1]);
+		}
+
+		if (strcmp(name, "layer_2") == 0) {
+			dp->planes[2].fb_base =
+			      (uint)map_sysmem(plat->base, plat->size);
+			debug("%s(): dp->planes[2].fb_base == 0x%x\n", __func__,
+			      (uint)dp->planes[2].fb_base);
+			nx_display_parse_dp_layer(subnode, &dp->planes[2]);
+		}
+	}
+}
+
+static int nx_display_parse_dp_lvds(ofnode node, struct nx_display_dev *dp)
+{
+	struct dp_lvds_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev) {
+		printf("failed to allocate display LVDS object.\n");
+		return -ENOMEM;
+	}
+
+	dp->device = dev;
+
+	dev->lvds_format = ofnode_read_s32_default(node, "format", 0);
+	dev->pol_inv_hs = ofnode_read_s32_default(node, "pol_inv_hs", 0);
+	dev->pol_inv_vs = ofnode_read_s32_default(node, "pol_inv_vs", 0);
+	dev->pol_inv_de = ofnode_read_s32_default(node, "pol_inv_de", 0);
+	dev->pol_inv_ck = ofnode_read_s32_default(node, "pol_inv_ck", 0);
+	dev->voltage_level = ofnode_read_s32_default(node, "voltage_level", 0);
+
+	if (!dev->voltage_level)
+		dev->voltage_level = DEF_VOLTAGE_LEVEL;
+
+	debug("DP: LVDS -> %s, voltage LV:0x%x\n",
+	      dev->lvds_format == DP_LVDS_FORMAT_VESA ? "VESA" :
+	      dev->lvds_format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC",
+	      dev->voltage_level);
+	debug("pol inv hs:%d, vs:%d, de:%d, ck:%d\n",
+	      dev->pol_inv_hs, dev->pol_inv_vs,
+	      dev->pol_inv_de, dev->pol_inv_ck);
+
+	return 0;
+}
+
+static int nx_display_parse_dp_rgb(ofnode node, struct nx_display_dev *dp)
+{
+	struct dp_rgb_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev) {
+		printf("failed to allocate display RGB LCD object.\n");
+		return -ENOMEM;
+	}
+	dp->device = dev;
+
+	dev->lcd_mpu_type = ofnode_read_s32_default(node, "lcd_mpu_type", 0);
+
+	debug("DP: RGB -> MPU[%s]\n", dev->lcd_mpu_type ? "O" : "X");
+	return 0;
+}
+
+static int nx_display_parse_dp_mipi(ofnode node, struct nx_display_dev *dp)
+{
+	struct dp_mipi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev) {
+		printf("failed to allocate display MiPi object.\n");
+		return -ENOMEM;
+	}
+	dp->device = dev;
+
+	dev->lp_bitrate = ofnode_read_s32_default(node, "lp_bitrate", 0);
+	dev->hs_bitrate = ofnode_read_s32_default(node, "hs_bitrate", 0);
+	dev->lpm_trans = 1;
+	dev->command_mode = 0;
+
+	debug("DP: MIPI ->\n");
+	debug("lp:%dmhz, hs:%dmhz\n", dev->lp_bitrate, dev->hs_bitrate);
+
+	return 0;
+}
+
+static int nx_display_parse_dp_hdmi(ofnode node, struct nx_display_dev *dp)
+{
+	struct dp_hdmi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev) {
+		printf("failed to allocate display HDMI object.\n");
+		return -ENOMEM;
+	}
+	dp->device = dev;
+
+	dev->preset = ofnode_read_s32_default(node, "preset", 0);
+
+	debug("DP: HDMI -> %d\n", dev->preset);
+
+	return 0;
+}
+
+static int nx_display_parse_dp_lcds(ofnode node, const char *type,
+				    struct nx_display_dev *dp)
+{
+	if (strcmp(type, "lvds") == 0) {
+		dp->dev_type = DP_DEVICE_LVDS;
+		return nx_display_parse_dp_lvds(node, dp);
+	} else if (strcmp(type, "rgb") == 0) {
+		dp->dev_type = DP_DEVICE_RGBLCD;
+		return nx_display_parse_dp_rgb(node, dp);
+	} else if (strcmp(type, "mipi") == 0) {
+		dp->dev_type = DP_DEVICE_MIPI;
+		return nx_display_parse_dp_mipi(node, dp);
+	} else if (strcmp(type, "hdmi") == 0) {
+		dp->dev_type = DP_DEVICE_HDMI;
+		return nx_display_parse_dp_hdmi(node, dp);
+	}
+
+	printf("%s: node %s unknown display type\n", __func__,
+	       ofnode_get_name(node));
+	return -EINVAL;
+
+	return 0;
+}
+
+#define	DT_SYNC		(1 << 0)
+#define	DT_CTRL		(1 << 1)
+#define	DT_PLANES	(1 << 2)
+#define	DT_DEVICE	(1 << 3)
+
+static int nx_display_parse_dt(struct udevice *dev,
+			       struct nx_display_dev *dp,
+			       struct video_uc_platdata *plat)
+{
+	const char *name, *dtype;
+	int ret = 0;
+	unsigned int dt_status = 0;
+	ofnode subnode;
+
+	if (!dev)
+		return -ENODEV;
+
+	dp->module = dev_read_s32_default(dev, "module", -1);
+	if (dp->module == -1)
+		dp->module = dev_read_s32_default(dev, "index", 0);
+
+	dtype = dev_read_string(dev, "lcd-type");
+
+	ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
+		name = ofnode_get_name(subnode);
+
+		if (strcmp("dp-sync", name) == 0) {
+			dt_status |= DT_SYNC;
+			nx_display_parse_dp_sync(subnode, &dp->sync);
+		}
+
+		if (strcmp("dp-ctrl", name) == 0) {
+			dt_status |= DT_CTRL;
+			nx_display_parse_dp_ctrl(subnode, &dp->ctrl);
+		}
+
+		if (strcmp("dp-planes", name) == 0) {
+			dt_status |= DT_PLANES;
+			nx_display_parse_dp_planes(subnode, dp, plat);
+		}
+
+		if (strcmp("dp-device", name) == 0) {
+			dt_status |= DT_DEVICE;
+			ret = nx_display_parse_dp_lcds(subnode, dtype, dp);
+		}
+	}
+
+	if (dt_status != (DT_SYNC | DT_CTRL | DT_PLANES | DT_DEVICE)) {
+		printf("Not enough DT config for display [0x%x]\n", dt_status);
+		return -ENODEV;
+	}
+
+	return ret;
+}
+#endif
+
+__weak int nx_display_fixup_dp(struct nx_display_dev *dp)
+{
+	return 0;
+}
+
+static struct nx_display_dev *nx_display_setup(void)
+{
+	struct nx_display_dev *dp;
+	int i, ret;
+	int node = 0;
+	struct video_uc_platdata *plat = NULL;
+
+	struct udevice *dev;
+
+	/* call driver probe */
+	debug("DT: uclass device call...\n");
+
+	ret = uclass_get_device(UCLASS_VIDEO, 0, &dev);
+	if (ret) {
+		debug("%s(): uclass_get_device(UCLASS_VIDEO, 0, &dev) != 0 --> return NULL\n",
+		      __func__);
+		return NULL;
+	}
+	plat = dev_get_uclass_platdata(dev);
+	if (!dev) {
+		debug("%s(): dev_get_uclass_platdata(dev) == NULL --> return NULL\n",
+		      __func__);
+		return NULL;
+	}
+	dp = dev_get_priv(dev);
+	if (!dp) {
+		debug("%s(): dev_get_priv(dev) == NULL --> return NULL\n",
+		      __func__);
+		return NULL;
+	}
+	node = dev->node.of_offset;
+
+	if (CONFIG_IS_ENABLED(OF_CONTROL)) {
+		ret = nx_display_parse_dt(dev, dp, plat);
+		if (ret)
+			goto err_setup;
+	}
+
+	nx_display_fixup_dp(dp);
+
+	for (i = 0; dp->top.plane_num > i; i++) {
+		dp->planes[i].layer = i;
+		if (dp->planes[i].enable && !dp->fb_plane) {
+			dp->fb_plane = &dp->planes[i];
+			dp->fb_addr = dp->fb_plane->fb_base;
+			dp->depth = dp->fb_plane->pixel_byte;
+		}
+	}
+
+	switch (dp->dev_type) {
+#ifdef CONFIG_VIDEO_NX_RGB
+	case DP_DEVICE_RGBLCD:
+		nx_rgb_display(dp->module,
+			       &dp->sync, &dp->ctrl, &dp->top,
+			       dp->planes, (struct dp_rgb_dev *)dp->device);
+		break;
+#endif
+#ifdef CONFIG_VIDEO_NX_LVDS
+	case DP_DEVICE_LVDS:
+		nx_lvds_display(dp->module,
+				&dp->sync, &dp->ctrl, &dp->top,
+				dp->planes, (struct dp_lvds_dev *)dp->device);
+		break;
+#endif
+#ifdef CONFIG_VIDEO_NX_MIPI
+	case DP_DEVICE_MIPI:
+		nx_mipi_display(dp->module,
+				&dp->sync, &dp->ctrl, &dp->top,
+				dp->planes, (struct dp_mipi_dev *)dp->device);
+		break;
+#endif
+#ifdef CONFIG_VIDEO_NX_HDMI
+	case DP_DEVICE_HDMI:
+		nx_hdmi_display(dp->module,
+				&dp->sync, &dp->ctrl, &dp->top,
+				dp->planes, (struct dp_hdmi_dev *)dp->device);
+		break;
+#endif
+	default:
+		printf("fail : not support lcd type %d !!!\n", dp->dev_type);
+		goto err_setup;
+	};
+
+	printf("LCD:   [%s] dp.%d.%d %dx%d %dbpp FB:0x%08x\n",
+	       dp_dev_str[dp->dev_type], dp->module, dp->fb_plane->layer,
+	       dp->fb_plane->width, dp->fb_plane->height, dp->depth * 8,
+	       dp->fb_addr);
+
+	return dp;
+
+err_setup:
+	kfree(dp);
+
+	return NULL;
+}
+
+#if defined CONFIG_LCD
+
+/* default lcd */
+struct vidinfo panel_info = {
+	.vl_col = 320, .vl_row = 240, .vl_bpix = 32,
+};
+
+void lcd_ctrl_init(void *lcdbase)
+{
+	vidinfo_t *pi = &panel_info;
+	struct nx_display_dev *dp;
+	int bpix;
+
+	dp = nx_display_setup();
+	if (!dp)
+		return NULL;
+
+	switch (dp->depth) {
+	case 2:
+		bpix = LCD_COLOR16;
+		break;
+	case 3:
+	case 4:
+		bpix = LCD_COLOR32;
+		break;
+	default:
+		printf("fail : not support LCD bit per pixel %d\n",
+		       dp->depth * 8);
+		return NULL;
+	}
+
+	dp->panel_info = pi;
+
+	/* set resolution with config */
+	pi->vl_bpix = bpix;
+	pi->vl_col = dp->fb_plane->width;
+	pi->vl_row = dp->fb_plane->height;
+	pi->priv = dp;
+	gd->fb_base = dp->fb_addr;
+}
+
+void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
+{
+}
+
+__weak void lcd_enable(void)
+{
+}
+#endif
+
+static int nx_display_probe(struct udevice *dev)
+{
+	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct nx_display_platdata *plat = dev_get_platdata(dev);
+	static GraphicDevice *graphic_device;
+	char addr[64];
+
+	debug("%s()\n", __func__);
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!uc_plat) {
+		debug("%s(): video_uc_platdata *plat == NULL --> return -EINVAL\n",
+		      __func__);
+		return -EINVAL;
+	}
+
+	if (!uc_priv) {
+		debug("%s(): video_priv *uc_priv == NULL --> return -EINVAL\n",
+		      __func__);
+		return -EINVAL;
+	}
+
+	if (!plat) {
+		debug("%s(): nx_display_platdata *plat == NULL --> return -EINVAL\n",
+		      __func__);
+		return -EINVAL;
+	}
+
+	struct nx_display_dev *dp;
+	unsigned int pp_index = 0;
+
+	dp = nx_display_setup();
+	if (!dp) {
+		debug("%s(): nx_display_setup() == 0 --> return -EINVAL\n",
+		      __func__);
+		return -EINVAL;
+	}
+
+	switch (dp->depth) {
+	case 2:
+		pp_index = GDF_16BIT_565RGB;
+		uc_priv->bpix = VIDEO_BPP16;
+		break;
+	case 3:
+		/* There is no VIDEO_BPP24 because these values are of
+		 * type video_log2_bpp
+		 */
+	case 4:
+		pp_index = GDF_32BIT_X888RGB;
+		uc_priv->bpix = VIDEO_BPP32;
+		break;
+	default:
+		printf("fail : not support LCD bit per pixel %d\n",
+		       dp->depth * 8);
+		return -EINVAL;
+	}
+
+	uc_priv->xsize = dp->fb_plane->width;
+	uc_priv->ysize = dp->fb_plane->height;
+	uc_priv->rot = 0;
+
+	graphic_device = &dp->graphic_device;
+	graphic_device->frameAdrs = dp->fb_addr;
+	graphic_device->gdfIndex = pp_index;
+	graphic_device->gdfBytesPP = dp->depth;
+	graphic_device->winSizeX = dp->fb_plane->width;
+	graphic_device->winSizeY = dp->fb_plane->height;
+	graphic_device->plnSizeX =
+	    graphic_device->winSizeX * graphic_device->gdfBytesPP;
+
+	/*
+	 * set environment variable "fb_addr" (frame buffer address), required
+	 * for splash image. Because drv_video_init() in common/stdio.c is only
+	 * called when CONFIG_VIDEO is set (and not if CONFIG_DM_VIDEO is set).
+	 */
+	sprintf(addr, "0x%x", dp->fb_addr);
+	debug("%s(): env_set(\"fb_addr\", %s) ...\n", __func__, addr);
+	env_set("fb_addr", addr);
+
+	return 0;
+}
+
+static int nx_display_bind(struct udevice *dev)
+{
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+
+	debug("%s()\n", __func__);
+
+	/* Datasheet S5p4418:
+	 *   Resolution up to 2048 x 1280, up to 12 Bit per color (HDMI)
+	 * Actual (max.) size is 0x1000000 because in U-Boot nanopi2-2016.01
+	 * "#define CONFIG_FB_ADDR  0x77000000" and next address is
+	 * "#define BMP_LOAD_ADDR  0x78000000"
+	 */
+	plat->size = 0x1000000;
+
+	return 0;
+}
+
+static const struct udevice_id nx_display_ids[] = {
+	{.compatible = "nexell,nexell-display", },
+	{}
+};
+
+U_BOOT_DRIVER(nexell_display) = {
+	.name = "nexell-display",
+	.id = UCLASS_VIDEO,
+	.of_match = nx_display_ids,
+	.platdata_auto_alloc_size =
+	    sizeof(struct nx_display_platdata),
+	.bind = nx_display_bind,
+	.probe = nx_display_probe,
+	.priv_auto_alloc_size = sizeof(struct nx_display_dev),
+};
-- 
1.9.1



More information about the U-Boot mailing list