video: add nexell video driver (display/video driver)
authorStefan Bosch <stefan_b@posteo.net>
Fri, 10 Jul 2020 17:07:36 +0000 (19:07 +0200)
committerTom Rini <trini@konsulko.com>
Wed, 29 Jul 2020 12:43:40 +0000 (08:43 -0400)
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@posteo.net>
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/nexell/Kconfig [new file with mode: 0644]
drivers/video/nexell/Makefile [new file with mode: 0644]
drivers/video/nexell/s5pxx18_dp.c [new file with mode: 0644]
drivers/video/nexell/s5pxx18_dp_hdmi.c [new file with mode: 0644]
drivers/video/nexell/s5pxx18_dp_lvds.c [new file with mode: 0644]
drivers/video/nexell/s5pxx18_dp_mipi.c [new file with mode: 0644]
drivers/video/nexell/s5pxx18_dp_rgb.c [new file with mode: 0644]
drivers/video/nexell_display.c [new file with mode: 0644]

index 89ad603..55f4fa4 100644 (file)
@@ -644,6 +644,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
index 1dbd09a..67a492a 100644 (file)
@@ -62,6 +62,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 (file)
index 0000000..54b8ccb
--- /dev/null
@@ -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 (file)
index 0000000..111ab45
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Junghyun, kim<jhkim@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 (file)
index 0000000..2248f47
--- /dev/null
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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 (file)
index 0000000..3f1fb8a
--- /dev/null
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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 (file)
index 0000000..f8ea63f
--- /dev/null
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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 (file)
index 0000000..670272b
--- /dev/null
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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 (file)
index 0000000..44e8edb
--- /dev/null
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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 (file)
index 0000000..4101e09
--- /dev/null
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016  Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@nexell.co.kr>
+ *
+ * Copyright (C) 2020  Stefan Bosch <stefan_b@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),
+};