summaryrefslogtreecommitdiff
path: root/drivers/video/nexell
diff options
context:
space:
mode:
authorStefan Bosch <stefan_b@posteo.net>2020-07-10 20:07:36 +0300
committerTom Rini <trini@konsulko.com>2020-07-29 15:43:40 +0300
commite1e96ba6a21a7988d5dc9e33f7f078799b0890b9 (patch)
treef473516a6e136546beeac0f522e8cf200b27fae2 /drivers/video/nexell
parent9c5d377583681e00a3f7d6d2cf6c33d01a1469a6 (diff)
downloadu-boot-e1e96ba6a21a7988d5dc9e33f7f078799b0890b9.tar.xz
video: add nexell video driver (display/video driver)
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>
Diffstat (limited to 'drivers/video/nexell')
-rw-r--r--drivers/video/nexell/Kconfig27
-rw-r--r--drivers/video/nexell/Makefile12
-rw-r--r--drivers/video/nexell/s5pxx18_dp.c341
-rw-r--r--drivers/video/nexell/s5pxx18_dp_hdmi.c545
-rw-r--r--drivers/video/nexell/s5pxx18_dp_lvds.c274
-rw-r--r--drivers/video/nexell/s5pxx18_dp_mipi.c677
-rw-r--r--drivers/video/nexell/s5pxx18_dp_rgb.c69
7 files changed, 1945 insertions, 0 deletions
diff --git a/drivers/video/nexell/Kconfig b/drivers/video/nexell/Kconfig
new file mode 100644
index 0000000000..54b8ccb56e
--- /dev/null
+++ b/drivers/video/nexell/Kconfig
@@ -0,0 +1,27 @@
+if VIDEO_NX
+
+menu "LCD select"
+
+config VIDEO_NX_RGB
+ bool "RGB LCD"
+ help
+ Support for RGB lcd output.
+
+config VIDEO_NX_LVDS
+ bool "LVDS LCD"
+ help
+ Support for LVDS lcd output.
+
+config VIDEO_NX_MIPI
+ bool "MiPi"
+ help
+ Support for MiPi lcd output.
+
+config VIDEO_NX_HDMI
+ bool "HDMI"
+ help
+ Support for hdmi output.
+
+endmenu
+
+endif
diff --git a/drivers/video/nexell/Makefile b/drivers/video/nexell/Makefile
new file mode 100644
index 0000000000..111ab4533c
--- /dev/null
+++ b/drivers/video/nexell/Makefile
@@ -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
index 0000000000..2248f47905
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/nexell.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_dpc.h"
+#include "soc/s5pxx18_soc_mlc.h"
+
+#define MLC_LAYER_RGB_0 0 /* number of RGB layer 0 */
+#define MLC_LAYER_RGB_1 1 /* number of RGB layer 1 */
+#define MLC_LAYER_VIDEO 3 /* number of Video layer: 3 = VIDEO */
+
+#define __io_address(a) (void *)(uintptr_t)(a)
+
+void dp_control_init(int module)
+{
+ void *base;
+
+ /* top */
+ base = __io_address(nx_disp_top_get_physical_address());
+ nx_disp_top_set_base_address(base);
+
+ /* control */
+ base = __io_address(nx_dpc_get_physical_address(module));
+ nx_dpc_set_base_address(module, base);
+
+ /* top controller */
+ nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_NEGATE);
+
+ /* display controller */
+ nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_NEGATE);
+
+ nx_dpc_set_clock_pclk_mode(module, nx_pclkmode_always);
+}
+
+int dp_control_setup(int module,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
+{
+ unsigned int out_format;
+ unsigned int delay_mask;
+ int rgb_pvd = 0, hsync_cp1 = 7, vsync_fram = 7, de_cp2 = 7;
+ int v_vso = 1, v_veo = 1, e_vso = 1, e_veo = 1;
+
+ int interlace = 0;
+ int invert_field;
+ int swap_rb;
+ unsigned int yc_order;
+ int vck_select;
+ int vclk_invert;
+ int emb_sync;
+
+ enum nx_dpc_dither r_dither, g_dither, b_dither;
+ int rgb_mode = 0;
+
+ if (NULL == sync || NULL == ctrl) {
+ debug("error, dp.%d not set sync or pad clock info !!!\n",
+ module);
+ return -EINVAL;
+ }
+
+ out_format = ctrl->out_format;
+ delay_mask = ctrl->delay_mask;
+ interlace = sync->interlace;
+ invert_field = ctrl->invert_field;
+ swap_rb = ctrl->swap_RB;
+ yc_order = ctrl->yc_order;
+ vck_select = ctrl->vck_select;
+ vclk_invert = ctrl->clk_inv_lv0 | ctrl->clk_inv_lv1;
+ emb_sync = (out_format == DPC_FORMAT_CCIR656 ? 1 : 0);
+
+ /* set delay mask */
+ if (delay_mask & DP_SYNC_DELAY_RGB_PVD)
+ rgb_pvd = ctrl->d_rgb_pvd;
+ if (delay_mask & DP_SYNC_DELAY_HSYNC_CP1)
+ hsync_cp1 = ctrl->d_hsync_cp1;
+ if (delay_mask & DP_SYNC_DELAY_VSYNC_FRAM)
+ vsync_fram = ctrl->d_vsync_fram;
+ if (delay_mask & DP_SYNC_DELAY_DE_CP)
+ de_cp2 = ctrl->d_de_cp2;
+
+ if (ctrl->vs_start_offset != 0 ||
+ ctrl->vs_end_offset != 0 ||
+ ctrl->ev_start_offset != 0 || ctrl->ev_end_offset != 0) {
+ v_vso = ctrl->vs_start_offset;
+ v_veo = ctrl->vs_end_offset;
+ e_vso = ctrl->ev_start_offset;
+ e_veo = ctrl->ev_end_offset;
+ }
+
+ if (nx_dpc_format_rgb555 == out_format ||
+ nx_dpc_format_mrgb555a == out_format ||
+ nx_dpc_format_mrgb555b == out_format) {
+ r_dither = nx_dpc_dither_5bit;
+ g_dither = nx_dpc_dither_5bit;
+ b_dither = nx_dpc_dither_5bit;
+ rgb_mode = 1;
+ } else if (nx_dpc_format_rgb565 == out_format ||
+ nx_dpc_format_mrgb565 == out_format) {
+ r_dither = nx_dpc_dither_5bit;
+ b_dither = nx_dpc_dither_5bit;
+ g_dither = nx_dpc_dither_6bit, rgb_mode = 1;
+ } else if ((nx_dpc_format_rgb666 == out_format) ||
+ (nx_dpc_format_mrgb666 == out_format)) {
+ r_dither = nx_dpc_dither_6bit;
+ g_dither = nx_dpc_dither_6bit;
+ b_dither = nx_dpc_dither_6bit;
+ rgb_mode = 1;
+ } else {
+ r_dither = nx_dpc_dither_bypass;
+ g_dither = nx_dpc_dither_bypass;
+ b_dither = nx_dpc_dither_bypass;
+ rgb_mode = 1;
+ }
+
+ /* CLKGEN0/1 */
+ nx_dpc_set_clock_source(module, 0, ctrl->clk_src_lv0 == 3 ?
+ 6 : ctrl->clk_src_lv0);
+ nx_dpc_set_clock_divisor(module, 0, ctrl->clk_div_lv0);
+ nx_dpc_set_clock_source(module, 1, ctrl->clk_src_lv1);
+ nx_dpc_set_clock_divisor(module, 1, ctrl->clk_div_lv1);
+ nx_dpc_set_clock_out_delay(module, 0, ctrl->clk_delay_lv0);
+ nx_dpc_set_clock_out_delay(module, 1, ctrl->clk_delay_lv1);
+
+ /* LCD out */
+ nx_dpc_set_mode(module, out_format, interlace, invert_field,
+ rgb_mode, swap_rb, yc_order, emb_sync, emb_sync,
+ vck_select, vclk_invert, 0);
+ nx_dpc_set_hsync(module, sync->h_active_len, sync->h_sync_width,
+ sync->h_front_porch, sync->h_back_porch,
+ sync->h_sync_invert);
+ nx_dpc_set_vsync(module, sync->v_active_len, sync->v_sync_width,
+ sync->v_front_porch, sync->v_back_porch,
+ sync->v_sync_invert, sync->v_active_len,
+ sync->v_sync_width, sync->v_front_porch,
+ sync->v_back_porch);
+ nx_dpc_set_vsync_offset(module, v_vso, v_veo, e_vso, e_veo);
+ nx_dpc_set_delay(module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2);
+ nx_dpc_set_dither(module, r_dither, g_dither, b_dither);
+
+ if (IS_ENABLED(CONFIG_MACH_S5P6818)) {
+ /* Set TFT_CLKCTRL (offset : 1030h)
+ * Field name : DPC0_CLKCTRL, DPC1_CLKCRL
+ * Default value : clk_inv_lv0/1 = 0 : PADCLK_InvCLK
+ * Invert case : clk_inv_lv0/1 = 1 : PADCLK_CLK
+ */
+ if (module == 0 && ctrl->clk_inv_lv0)
+ nx_disp_top_set_padclock(padmux_primary_mlc,
+ padclk_clk);
+ if (module == 1 && ctrl->clk_inv_lv1)
+ nx_disp_top_set_padclock(padmux_secondary_mlc,
+ padclk_clk);
+ }
+
+ debug("%s: dp.%d x:%4d, hf:%3d, hb:%3d, hs:%3d, hi=%d\n",
+ __func__, module, sync->h_active_len, sync->h_front_porch,
+ sync->h_back_porch, sync->h_sync_width, sync->h_sync_invert);
+ debug("%s: dp.%d y:%4d, vf:%3d, vb:%3d, vs:%3d, vi=%d\n",
+ __func__, module, sync->v_active_len, sync->v_front_porch,
+ sync->v_back_porch, sync->v_sync_width, sync->h_sync_invert);
+ debug("%s: dp.%d ck.0:%d:%d:%d, ck.1:%d:%d:%d\n",
+ __func__, module,
+ ctrl->clk_src_lv0, ctrl->clk_div_lv0, ctrl->clk_inv_lv0,
+ ctrl->clk_src_lv1, ctrl->clk_div_lv1, ctrl->clk_inv_lv1);
+ debug("%s: dp.%d vs:%d, ve:%d, es:%d, ee:%d\n",
+ __func__, module, v_vso, v_veo, e_vso, e_veo);
+ debug("%s: dp.%d delay RGB:%d, hs:%d, vs:%d, de:%d, fmt:0x%x\n",
+ __func__, module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2,
+ out_format);
+
+ return 0;
+}
+
+void dp_control_enable(int module, int on)
+{
+ debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
+
+ nx_dpc_set_dpc_enable(module, on);
+ nx_dpc_set_clock_divisor_enable(module, on);
+}
+
+void dp_plane_init(int module)
+{
+ void *base = __io_address(nx_mlc_get_physical_address(module));
+
+ nx_mlc_set_base_address(module, base);
+ nx_mlc_set_clock_pclk_mode(module, nx_pclkmode_always);
+ nx_mlc_set_clock_bclk_mode(module, nx_bclkmode_always);
+}
+
+int dp_plane_screen_setup(int module, struct dp_plane_top *top)
+{
+ int width = top->screen_width;
+ int height = top->screen_height;
+ int interlace = top->interlace;
+ int video_prior = top->video_prior;
+ unsigned int bg_color = top->back_color;
+
+ /* MLC TOP layer */
+ nx_mlc_set_screen_size(module, width, height);
+ nx_mlc_set_layer_priority(module, video_prior);
+ nx_mlc_set_background(module, bg_color);
+ nx_mlc_set_field_enable(module, interlace);
+ nx_mlc_set_rgblayer_gama_table_power_mode(module, 0, 0, 0);
+ nx_mlc_set_rgblayer_gama_table_sleep_mode(module, 1, 1, 1);
+ nx_mlc_set_rgblayer_gamma_enable(module, 0);
+ nx_mlc_set_dither_enable_when_using_gamma(module, 0);
+ nx_mlc_set_gamma_priority(module, 0);
+ nx_mlc_set_top_power_mode(module, 1);
+ nx_mlc_set_top_sleep_mode(module, 0);
+
+ debug("%s: dp.%d screen %dx%d, %s, priority:%d, bg:0x%x\n",
+ __func__, module, width, height,
+ interlace ? "Interlace" : "Progressive",
+ video_prior, bg_color);
+
+ return 0;
+}
+
+void dp_plane_screen_enable(int module, int on)
+{
+ /* enable top screen */
+ nx_mlc_set_mlc_enable(module, on);
+ nx_mlc_set_top_dirty_flag(module);
+ debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
+}
+
+int dp_plane_layer_setup(int module, struct dp_plane_info *plane)
+{
+ int sx = plane->left;
+ int sy = plane->top;
+ int ex = sx + plane->width - 1;
+ int ey = sy + plane->height - 1;
+ int pixel_byte = plane->pixel_byte;
+ int mem_lock_size = 16; /* fix mem lock size */
+ int layer = plane->layer;
+ unsigned int format = plane->format;
+
+ if (!plane->enable)
+ return -EINVAL;
+
+ /* MLC layer */
+ nx_mlc_set_lock_size(module, layer, mem_lock_size);
+ nx_mlc_set_alpha_blending(module, layer, 0, 15);
+ nx_mlc_set_transparency(module, layer, 0, 0);
+ nx_mlc_set_color_inversion(module, layer, 0, 0);
+ nx_mlc_set_rgblayer_invalid_position(module, layer, 0, 0, 0, 0, 0, 0);
+ nx_mlc_set_rgblayer_invalid_position(module, layer, 1, 0, 0, 0, 0, 0);
+ nx_mlc_set_format_rgb(module, layer, format);
+ nx_mlc_set_position(module, layer, sx, sy, ex, ey);
+ nx_mlc_set_rgblayer_stride(module, layer, pixel_byte,
+ plane->width * pixel_byte);
+ nx_mlc_set_rgblayer_address(module, layer, plane->fb_base);
+
+ debug("%s: dp.%d.%d %d * %d, %dbpp, fmt:0x%x\n",
+ __func__, module, layer, plane->width, plane->height,
+ pixel_byte * 8, format);
+ debug("%s: b:0x%x, l:%d, t:%d, r:%d, b:%d, hs:%d, vs:%d\n",
+ __func__, plane->fb_base, sx, sy, ex, ey,
+ plane->width * pixel_byte, pixel_byte);
+
+ return 0;
+}
+
+int dp_plane_set_enable(int module, int layer, int on)
+{
+ int hl, hc;
+ int vl, vc;
+
+ debug("%s: dp.%d.%d %s:%s\n",
+ __func__, module, layer,
+ layer == MLC_LAYER_VIDEO ? "Video" : "RGB",
+ on ? "ON" : "OFF");
+
+ if (layer != MLC_LAYER_VIDEO) {
+ nx_mlc_set_layer_enable(module, layer, on);
+ nx_mlc_set_dirty_flag(module, layer);
+ return 0;
+ }
+
+ /* video layer */
+ if (on) {
+ nx_mlc_set_video_layer_line_buffer_power_mode(module, 1);
+ nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 0);
+ nx_mlc_set_layer_enable(module, layer, 1);
+ nx_mlc_set_dirty_flag(module, layer);
+ } else {
+ nx_mlc_set_layer_enable(module, layer, 0);
+ nx_mlc_set_dirty_flag(module, layer);
+ nx_mlc_get_video_layer_scale_filter(module,
+ &hl, &hc, &vl, &vc);
+ if (hl || hc || vl || vc)
+ nx_mlc_set_video_layer_scale_filter(module, 0, 0, 0, 0);
+ nx_mlc_set_video_layer_line_buffer_power_mode(module, 0);
+ nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 1);
+ nx_mlc_set_dirty_flag(module, layer);
+ }
+
+ return 0;
+}
+
+void dp_plane_layer_enable(int module,
+ struct dp_plane_info *plane, int on)
+{
+ dp_plane_set_enable(module, plane->layer, on);
+}
+
+int dp_plane_set_address(int module, int layer, unsigned int address)
+{
+ nx_mlc_set_rgblayer_address(module, layer, address);
+ nx_mlc_set_dirty_flag(module, layer);
+
+ return 0;
+}
+
+int dp_plane_wait_vsync(int module, int layer, int fps)
+{
+ int cnt = 0;
+
+ if (fps == 0)
+ return (int)nx_mlc_get_dirty_flag(module, layer);
+
+ while (fps > cnt++) {
+ while (nx_mlc_get_dirty_flag(module, layer))
+ ;
+ nx_mlc_set_dirty_flag(module, layer);
+ }
+ return 0;
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_hdmi.c b/drivers/video/nexell/s5pxx18_dp_hdmi.c
new file mode 100644
index 0000000000..3f1fb8a575
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_hdmi.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/tieoff.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include <linux/delay.h>
+
+#include "soc/s5pxx18_soc_dpc.h"
+#include "soc/s5pxx18_soc_hdmi.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define __io_address(a) (void *)(uintptr_t)(a)
+
+static const u8 hdmiphy_preset74_25[32] = {
+ 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0xc8, 0x81,
+ 0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
+ 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
+ 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x10, 0x80,
+};
+
+static const u8 hdmiphy_preset148_5[32] = {
+ 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0xc8, 0x81,
+ 0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
+ 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
+ 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+};
+
+#define HDMIPHY_PRESET_TABLE_SIZE (32)
+
+enum NXP_HDMI_PRESET {
+ NXP_HDMI_PRESET_720P = 0, /* 1280 x 720 */
+ NXP_HDMI_PRESET_1080P, /* 1920 x 1080 */
+ NXP_HDMI_PRESET_MAX
+};
+
+static void hdmi_reset(void)
+{
+ nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_NEGATE);
+}
+
+static int hdmi_phy_enable(int preset, int enable)
+{
+ const u8 *table = NULL;
+ int size = 0;
+ u32 addr, i = 0;
+
+ if (!enable)
+ return 0;
+
+ switch (preset) {
+ case NXP_HDMI_PRESET_720P:
+ table = hdmiphy_preset74_25;
+ size = 32;
+ break;
+ case NXP_HDMI_PRESET_1080P:
+ table = hdmiphy_preset148_5;
+ size = 31;
+ break;
+ default:
+ printf("hdmi: phy not support preset %d\n", preset);
+ return -EINVAL;
+ }
+
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
+
+ for (i = 0, addr = HDMI_PHY_REG04; size > i; i++, addr += 4) {
+ nx_hdmi_set_reg(0, addr, table[i]);
+ nx_hdmi_set_reg(0, addr, table[i]);
+ }
+
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
+ nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
+ debug("%s: preset = %d\n", __func__, preset);
+
+ return 0;
+}
+
+static inline int hdmi_wait_phy_ready(void)
+{
+ int count = 500;
+
+ do {
+ u32 val = nx_hdmi_get_reg(0, HDMI_LINK_PHY_STATUS_0);
+
+ if (val & 0x01) {
+ printf("HDMI: phy ready...\n");
+ return 1;
+ }
+ mdelay(10);
+ } while (count--);
+
+ return 0;
+}
+
+static inline int hdmi_get_vsync(int preset,
+ struct dp_sync_info *sync,
+ struct dp_ctrl_info *ctrl)
+{
+ switch (preset) {
+ case NXP_HDMI_PRESET_720P: /* 720p: 1280x720 */
+ sync->h_active_len = 1280;
+ sync->h_sync_width = 40;
+ sync->h_back_porch = 220;
+ sync->h_front_porch = 110;
+ sync->h_sync_invert = 0;
+ sync->v_active_len = 720;
+ sync->v_sync_width = 5;
+ sync->v_back_porch = 20;
+ sync->v_front_porch = 5;
+ sync->v_sync_invert = 0;
+ break;
+
+ case NXP_HDMI_PRESET_1080P: /* 1080p: 1920x1080 */
+ sync->h_active_len = 1920;
+ sync->h_sync_width = 44;
+ sync->h_back_porch = 148;
+ sync->h_front_porch = 88;
+ sync->h_sync_invert = 0;
+ sync->v_active_len = 1080;
+ sync->v_sync_width = 5;
+ sync->v_back_porch = 36;
+ sync->v_front_porch = 4;
+ sync->v_sync_invert = 0;
+ break;
+ default:
+ printf("HDMI: not support preset sync %d\n", preset);
+ return -EINVAL;
+ }
+
+ ctrl->clk_src_lv0 = 4;
+ ctrl->clk_div_lv0 = 1;
+ ctrl->clk_src_lv1 = 7;
+ ctrl->clk_div_lv1 = 1;
+
+ ctrl->out_format = outputformat_rgb888;
+ ctrl->delay_mask = (DP_SYNC_DELAY_RGB_PVD | DP_SYNC_DELAY_HSYNC_CP1 |
+ DP_SYNC_DELAY_VSYNC_FRAM | DP_SYNC_DELAY_DE_CP);
+ ctrl->d_rgb_pvd = 0;
+ ctrl->d_hsync_cp1 = 0;
+ ctrl->d_vsync_fram = 0;
+ ctrl->d_de_cp2 = 7;
+
+ /* HFP + HSW + HBP + AVWidth-VSCLRPIXEL- 1; */
+ ctrl->vs_start_offset = (sync->h_front_porch + sync->h_sync_width +
+ sync->h_back_porch + sync->h_active_len - 1);
+ ctrl->vs_end_offset = 0;
+
+ /* HFP + HSW + HBP + AVWidth-EVENVSCLRPIXEL- 1 */
+ ctrl->ev_start_offset = (sync->h_front_porch + sync->h_sync_width +
+ sync->h_back_porch + sync->h_active_len - 1);
+ ctrl->ev_end_offset = 0;
+ debug("%s: preset: %d\n", __func__, preset);
+
+ return 0;
+}
+
+static void hdmi_clock(void)
+{
+ void *base =
+ __io_address(nx_disp_top_clkgen_get_physical_address
+ (to_mipi_clkgen));
+
+ nx_disp_top_clkgen_set_base_address(to_mipi_clkgen, base);
+ nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 0);
+ nx_disp_top_clkgen_set_clock_pclk_mode(to_mipi_clkgen,
+ nx_pclkmode_always);
+ nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
+ 2);
+ nx_disp_top_clkgen_set_clock_divisor(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
+ 2);
+ nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, 1, 7);
+ nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 1);
+
+ /* must initialize this !!! */
+ nx_disp_top_hdmi_set_vsync_hsstart_end(0, 0);
+ nx_disp_top_hdmi_set_vsync_start(0);
+ nx_disp_top_hdmi_set_hactive_start(0);
+ nx_disp_top_hdmi_set_hactive_end(0);
+}
+
+static void hdmi_vsync(struct dp_sync_info *sync)
+{
+ int width = sync->h_active_len;
+ int hsw = sync->h_sync_width;
+ int hbp = sync->h_back_porch;
+ int height = sync->v_active_len;
+ int vsw = sync->v_sync_width;
+ int vbp = sync->v_back_porch;
+
+ int v_sync_s = vsw + vbp + height - 1;
+ int h_active_s = hsw + hbp;
+ int h_active_e = width + hsw + hbp;
+ int v_sync_hs_se0 = hsw + hbp + 1;
+ int v_sync_hs_se1 = hsw + hbp + 2;
+
+ nx_disp_top_hdmi_set_vsync_start(v_sync_s);
+ nx_disp_top_hdmi_set_hactive_start(h_active_s);
+ nx_disp_top_hdmi_set_hactive_end(h_active_e);
+ nx_disp_top_hdmi_set_vsync_hsstart_end(v_sync_hs_se0, v_sync_hs_se1);
+}
+
+static int hdmi_prepare(struct dp_sync_info *sync)
+{
+ int width = sync->h_active_len;
+ int hsw = sync->h_sync_width;
+ int hfp = sync->h_front_porch;
+ int hbp = sync->h_back_porch;
+ int height = sync->v_active_len;
+ int vsw = sync->v_sync_width;
+ int vfp = sync->v_front_porch;
+ int vbp = sync->v_back_porch;
+
+ u32 h_blank, h_line, h_sync_start, h_sync_end;
+ u32 v_blank, v2_blank, v_line;
+ u32 v_sync_line_bef_1, v_sync_line_bef_2;
+
+ u32 fixed_ffff = 0xffff;
+
+ /* calculate sync variables */
+ h_blank = hfp + hsw + hbp;
+ v_blank = vfp + vsw + vbp;
+ v2_blank = height + vfp + vsw + vbp;
+ v_line = height + vfp + vsw + vbp; /* total v */
+ h_line = width + hfp + hsw + hbp; /* total h */
+ h_sync_start = hfp;
+ h_sync_end = hfp + hsw;
+ v_sync_line_bef_1 = vfp;
+ v_sync_line_bef_2 = vfp + vsw;
+
+ /* no blue screen mode, encoding order as it is */
+ nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0, (0 << 5) | (1 << 4));
+
+ /* set HDMI_LINK_BLUE_SCREEN_* to 0x0 */
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_0, 0x5555);
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_1, 0x5555);
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_0, 0x5555);
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_1, 0x5555);
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_0, 0x5555);
+ nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_1, 0x5555);
+
+ /* set HDMI_CON_1 to 0x0 */
+ nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_1, 0x0);
+ nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_2, 0x0);
+
+ /* set interrupt : enable hpd_plug, hpd_unplug */
+ nx_hdmi_set_reg(0, HDMI_LINK_INTC_CON_0,
+ (1 << 6) | (1 << 3) | (1 << 2));
+
+ /* set STATUS_EN to 0x17 */
+ nx_hdmi_set_reg(0, HDMI_LINK_STATUS_EN, 0x17);
+
+ /* TODO set HDP to 0x0 : later check hpd */
+ nx_hdmi_set_reg(0, HDMI_LINK_HPD, 0x0);
+
+ /* set MODE_SEL to 0x02 */
+ nx_hdmi_set_reg(0, HDMI_LINK_MODE_SEL, 0x2);
+
+ /* set H_BLANK_*, V1_BLANK_*, V2_BLANK_*, V_LINE_*,
+ * H_LINE_*, H_SYNC_START_*, H_SYNC_END_ *
+ * V_SYNC_LINE_BEF_1_*, V_SYNC_LINE_BEF_2_*
+ */
+ nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_0, h_blank % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_1, h_blank >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_0, v_blank % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_1, v_blank >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_0, v2_blank % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_1, v2_blank >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_0, v_line % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_1, v_line >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_0, h_line % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_1, h_line >> 8);
+
+ if (width == 1280) {
+ nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x1);
+ nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x1);
+ } else {
+ nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x0);
+ nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x0);
+ }
+
+ nx_hdmi_set_reg(0, HDMI_LINK_INT_PRO_MODE, 0x0);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_0, (h_sync_start % 256) - 2);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_1, h_sync_start >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_0, (h_sync_end % 256) - 2);
+ nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_1, h_sync_end >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_0,
+ v_sync_line_bef_1 % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_1,
+ v_sync_line_bef_1 >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_0,
+ v_sync_line_bef_2 % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_1,
+ v_sync_line_bef_2 >> 8);
+
+ /* Set V_SYNC_LINE_AFT*, V_SYNC_LINE_AFT_PXL*, VACT_SPACE* */
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_1, fixed_ffff >> 8);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_1, fixed_ffff >> 8);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_1, fixed_ffff >> 8);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_0, fixed_ffff % 256);
+ nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_1, fixed_ffff >> 8);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_CSC_MUX, 0x0);
+ nx_hdmi_set_reg(0, HDMI_LINK_SYNC_GEN_MUX, 0x0);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_0, 0xfd);
+ nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_1, 0x01);
+ nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_0, 0x0d);
+ nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_1, 0x3a);
+ nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_2, 0x08);
+
+ /* Set DC_CONTROL to 0x00 */
+ nx_hdmi_set_reg(0, HDMI_LINK_DC_CONTROL, 0x0);
+
+ if (IS_ENABLED(CONFIG_HDMI_PATTERN))
+ nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x1);
+ else
+ nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x0);
+
+ nx_hdmi_set_reg(0, HDMI_LINK_GCP_CON, 0x0a);
+ return 0;
+}
+
+static void hdmi_init(void)
+{
+ void *base;
+ /**
+ * [SEQ 2] set the HDMI CLKGEN's PCLKMODE to always enabled
+ */
+ base =
+ __io_address(nx_disp_top_clkgen_get_physical_address(hdmi_clkgen));
+ nx_disp_top_clkgen_set_base_address(hdmi_clkgen, base);
+ nx_disp_top_clkgen_set_clock_pclk_mode(hdmi_clkgen, nx_pclkmode_always);
+
+ base = __io_address(nx_hdmi_get_physical_address(0));
+ nx_hdmi_set_base_address(0, base);
+
+ /**
+ * [SEQ 3] set the 0xC001100C[0] to 1
+ */
+ nx_tieoff_set(NX_TIEOFF_DISPLAYTOP0_i_HDMI_PHY_REFCLK_SEL, 1);
+
+ /**
+ * [SEQ 4] release the resets of HDMI.i_PHY_nRST and HDMI.i_nRST
+ */
+ nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_NEGATE);
+}
+
+void hdmi_enable(int input, int preset, struct dp_sync_info *sync, int enable)
+{
+ if (enable) {
+ nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0,
+ (nx_hdmi_get_reg(0, HDMI_LINK_HDMI_CON_0) |
+ 0x1));
+ hdmi_vsync(sync);
+ } else {
+ hdmi_phy_enable(preset, 0);
+ }
+}
+
+static int hdmi_setup(int input, int preset,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
+{
+ u32 HDMI_SEL = 0;
+ int ret;
+
+ switch (input) {
+ case DP_DEVICE_DP0:
+ HDMI_SEL = primary_mlc;
+ break;
+ case DP_DEVICE_DP1:
+ HDMI_SEL = secondary_mlc;
+ break;
+ case DP_DEVICE_RESCONV:
+ HDMI_SEL = resolution_conv;
+ break;
+ default:
+ printf("HDMI: not support source device %d\n", input);
+ return -EINVAL;
+ }
+
+ /**
+ * [SEQ 5] set up the HDMI PHY to specific video clock.
+ */
+ ret = hdmi_phy_enable(preset, 1);
+ if (ret < 0)
+ return ret;
+
+ /**
+ * [SEQ 6] I2S (or SPDIFTX) configuration for the source audio data
+ * this is done in another user app - ex> Android Audio HAL
+ */
+
+ /**
+ * [SEQ 7] Wait for ECID ready
+ */
+
+ /**
+ * [SEQ 8] release the resets of HDMI.i_VIDEO_nRST and HDMI.i_SPDIF_nRST
+ * and HDMI.i_TMDS_nRST
+ */
+ hdmi_reset();
+
+ /**
+ * [SEQ 9] Wait for HDMI PHY ready (wait until 0xC0200020.[0], 1)
+ */
+ if (hdmi_wait_phy_ready() == 0) {
+ printf("%s: failed to wait for hdmiphy ready\n", __func__);
+ hdmi_phy_enable(preset, 0);
+ return -EIO;
+ }
+ /* set mux */
+ nx_disp_top_set_hdmimux(1, HDMI_SEL);
+
+ /**
+ * [SEC 10] Set the DPC CLKGEN's Source Clock to HDMI_CLK &
+ * Set Sync Parameter
+ */
+ hdmi_clock();
+ /* set hdmi link clk to clkgen vs default is hdmi phy clk */
+
+ /**
+ * [SEQ 11] Set up the HDMI Converter parameters
+ */
+ hdmi_get_vsync(preset, sync, ctrl);
+ hdmi_prepare(sync);
+
+ return 0;
+}
+
+void nx_hdmi_display(int module,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_plane_top *top, struct dp_plane_info *planes,
+ struct dp_hdmi_dev *dev)
+{
+ struct dp_plane_info *plane = planes;
+ int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+ int count = top->plane_num;
+ int preset = dev->preset;
+ int i = 0;
+
+ debug("HDMI: display.%d\n", module);
+
+ switch (preset) {
+ case 0:
+ top->screen_width = 1280;
+ top->screen_height = 720;
+ sync->h_active_len = 1280;
+ sync->v_active_len = 720;
+ break;
+ case 1:
+ top->screen_width = 1920;
+ top->screen_height = 1080;
+ sync->h_active_len = 1920;
+ sync->v_active_len = 1080;
+ break;
+ default:
+ printf("hdmi not support preset %d\n", preset);
+ return;
+ }
+
+ printf("HDMI: display.%d, preset %d (%4d * %4d)\n",
+ module, preset, top->screen_width, top->screen_height);
+
+ dp_control_init(module);
+ dp_plane_init(module);
+
+ hdmi_init();
+ hdmi_setup(input, preset, sync, ctrl);
+
+ dp_plane_screen_setup(module, top);
+ for (i = 0; count > i; i++, plane++) {
+ if (!plane->enable)
+ continue;
+ dp_plane_layer_setup(module, plane);
+ dp_plane_layer_enable(module, plane, 1);
+ }
+ dp_plane_screen_enable(module, 1);
+
+ dp_control_setup(module, sync, ctrl);
+ dp_control_enable(module, 1);
+
+ hdmi_enable(input, preset, sync, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_lvds.c b/drivers/video/nexell/s5pxx18_dp_lvds.c
new file mode 100644
index 0000000000..f8ea63fdf1
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_lvds.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_lvds.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define __io_address(a) (void *)(uintptr_t)(a)
+
+static void lvds_phy_reset(void)
+{
+ nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_NEGATE);
+}
+
+static void lvds_init(void)
+{
+ int clkid = DP_CLOCK_LVDS;
+ int index = 0;
+ void *base;
+
+ base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
+ nx_disp_top_clkgen_set_base_address(clkid, base);
+
+ nx_lvds_initialize();
+
+ for (index = 0; nx_lvds_get_number_of_module() > index; index++)
+ nx_lvds_set_base_address(index,
+ (void *)__io_address(nx_lvds_get_physical_address(index)));
+
+ nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
+}
+
+static void lvds_enable(int enable)
+{
+ int clkid = DP_CLOCK_LVDS;
+ int on = (enable ? 1 : 0);
+
+ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, on);
+}
+
+static int lvds_setup(int module, int input,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_lvds_dev *dev)
+{
+ unsigned int val;
+ int clkid = DP_CLOCK_LVDS;
+ enum dp_lvds_format format = DP_LVDS_FORMAT_JEIDA;
+ u32 voltage = DEF_VOLTAGE_LEVEL;
+
+ if (dev) {
+ format = dev->lvds_format;
+ voltage = dev->voltage_level;
+ }
+
+ printf("LVDS: ");
+ printf("%s, ", format == DP_LVDS_FORMAT_VESA ? "VESA" :
+ format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC");
+ printf("voltage LV:0x%x\n", voltage);
+
+ /*
+ *-------- predefined type.
+ * only change iTA to iTE in VESA mode
+ * wire [34:0] loc_VideoIn =
+ * {4'hf, 4'h0, i_VDEN, i_VSYNC, i_HSYNC, i_VD[23:0] };
+ */
+ u32 VSYNC = 25;
+ u32 HSYNC = 24;
+ u32 VDEN = 26; /* bit position */
+ u32 ONE = 34;
+ u32 ZERO = 27;
+
+ /*====================================================
+ * current not use location mode
+ *====================================================
+ */
+ u32 LOC_A[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
+ u32 LOC_B[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
+ u32 LOC_C[7] = {VDEN, VSYNC, HSYNC, ONE, HSYNC, VSYNC, VDEN};
+ u32 LOC_D[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
+ u32 LOC_E[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
+
+ switch (input) {
+ case DP_DEVICE_DP0:
+ input = 0;
+ break;
+ case DP_DEVICE_DP1:
+ input = 1;
+ break;
+ case DP_DEVICE_RESCONV:
+ input = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * select TOP MUX
+ */
+ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 0);
+ nx_disp_top_clkgen_set_clock_source(clkid, 0, ctrl->clk_src_lv0);
+ nx_disp_top_clkgen_set_clock_divisor(clkid, 0, ctrl->clk_div_lv0);
+ nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv1);
+ nx_disp_top_clkgen_set_clock_divisor(clkid, 1, ctrl->clk_div_lv1);
+
+ /*
+ * LVDS Control Pin Setting
+ */
+ val = (0 << 30) | /* CPU_I_VBLK_FLAG_SEL */
+ (0 << 29) | /* CPU_I_BVLK_FLAG */
+ (1 << 28) | /* SKINI_BST */
+ (1 << 27) | /* DLYS_BST */
+ (0 << 26) | /* I_AUTO_SEL */
+ (format << 19) | /* JEiDA data packing */
+ (0x1B << 13) | /* I_LOCK_PPM_SET, PPM setting for PLL lock */
+ (0x638 << 1); /* I_DESKEW_CNT_SEL, period of de-skew region */
+ nx_lvds_set_lvdsctrl0(0, val);
+
+ val = (0 << 28) | /* I_ATE_MODE, function mode */
+ (0 << 27) | /* I_TEST_CON_MODE, DA (test ctrl mode) */
+ (0 << 24) | /* I_TX4010X_DUMMY */
+ (0 << 15) | /* SKCCK 0 */
+ (0 << 12) | /* SKC4 (TX output skew control pin at ODD ch4) */
+ (0 << 9) | /* SKC3 (TX output skew control pin at ODD ch3) */
+ (0 << 6) | /* SKC2 (TX output skew control pin at ODD ch2) */
+ (0 << 3) | /* SKC1 (TX output skew control pin at ODD ch1) */
+ (0 << 0); /* SKC0 (TX output skew control pin at ODD ch0) */
+ nx_lvds_set_lvdsctrl1(0, val);
+
+ val = (0 << 15) | /* CK_POL_SEL, Input clock, bypass */
+ (0 << 14) | /* VSEL, VCO Freq. range. 0: Low(40MHz~90MHz),
+ * 1: High(90MHz~160MHz) */
+ (0x1 << 12) | /* S (Post-scaler) */
+ (0xA << 6) | /* M (Main divider) */
+ (0xA << 0); /* P (Pre-divider) */
+
+ nx_lvds_set_lvdsctrl2(0, val);
+ val = (0x03 << 6) | /* SK_BIAS, Bias current ctrl pin */
+ (0 << 5) | /* SKEWINI, skew selection pin, 0: bypass,
+ * 1: skew enable */
+ (0 << 4) | /* SKEW_EN_H, skew block power down, 0: power down,
+ * 1: operating */
+ (1 << 3) | /* CNTB_TDLY, delay control pin */
+ (0 << 2) | /* SEL_DATABF, input clock 1/2 division cont. pin */
+ (0x3 << 0); /* SKEW_REG_CUR, regulator bias current selection
+ * in SKEW block */
+
+ nx_lvds_set_lvdsctrl3(0, val);
+ val = (0 << 28) | /* FLT_CNT, filter control pin for PLL */
+ (0 << 27) | /* VOD_ONLY_CNT, the pre-emphasis's pre-diriver
+ * control pin (VOD only) */
+ (0 << 26) | /* CNNCT_MODE_SEL, connectivity mode selection,
+ * 0:TX operating, 1:con check */
+ (0 << 24) | /* CNNCT_CNT, connectivity ctrl pin,
+ * 0: tx operating, 1: con check */
+ (0 << 23) | /* VOD_HIGH_S, VOD control pin, 1: Vod only */
+ (0 << 22) | /* SRC_TRH, source termination resistor sel. pin */
+ (voltage << 14) |
+ (0x01 << 6) | /* CNT_PEN_H, TX driver pre-emphasis level cont. */
+ (0x4 << 3) | /* FC_CODE, vos control pin */
+ (0 << 2) | /* OUTCON, TX Driver state selectioin pin, 0:Hi-z,
+ * 1:Low */
+ (0 << 1) | /* LOCK_CNT, Lock signal selection pin, enable */
+ (0 << 0); /* AUTO_DSK_SEL, auto deskew sel. pin, normal */
+ nx_lvds_set_lvdsctrl4(0, val);
+
+ val = (0 << 24) | /* I_BIST_RESETB */
+ (0 << 23) | /* I_BIST_EN */
+ (0 << 21) | /* I_BIST_PAT_SEL */
+ (0 << 14) | /* I_BIST_USER_PATTERN */
+ (0 << 13) | /* I_BIST_FORCE_ERROR */
+ (0 << 7) | /* I_BIST_SKEW_CTRL */
+ (0 << 5) | /* I_BIST_CLK_INV */
+ (0 << 3) | /* I_BIST_DATA_INV */
+ (0 << 0); /* I_BIST_CH_SEL */
+ nx_lvds_set_lvdstmode0(0, val);
+
+ /* user do not need to modify this codes. */
+ val = (LOC_A[4] << 24) | (LOC_A[3] << 18) | (LOC_A[2] << 12) |
+ (LOC_A[1] << 6) | (LOC_A[0] << 0);
+ nx_lvds_set_lvdsloc0(0, val);
+
+ val = (LOC_B[2] << 24) | (LOC_B[1] << 18) | (LOC_B[0] << 12) |
+ (LOC_A[6] << 6) | (LOC_A[5] << 0);
+ nx_lvds_set_lvdsloc1(0, val);
+
+ val = (LOC_C[0] << 24) | (LOC_B[6] << 18) | (LOC_B[5] << 12) |
+ (LOC_B[4] << 6) | (LOC_B[3] << 0);
+ nx_lvds_set_lvdsloc2(0, val);
+
+ val = (LOC_C[5] << 24) | (LOC_C[4] << 18) | (LOC_C[3] << 12) |
+ (LOC_C[2] << 6) | (LOC_C[1] << 0);
+ nx_lvds_set_lvdsloc3(0, val);
+
+ val = (LOC_D[3] << 24) | (LOC_D[2] << 18) | (LOC_D[1] << 12) |
+ (LOC_D[0] << 6) | (LOC_C[6] << 0);
+ nx_lvds_set_lvdsloc4(0, val);
+
+ val = (LOC_E[1] << 24) | (LOC_E[0] << 18) | (LOC_D[6] << 12) |
+ (LOC_D[5] << 6) | (LOC_D[4] << 0);
+ nx_lvds_set_lvdsloc5(0, val);
+
+ val = (LOC_E[6] << 24) | (LOC_E[5] << 18) | (LOC_E[4] << 12) |
+ (LOC_E[3] << 6) | (LOC_E[2] << 0);
+ nx_lvds_set_lvdsloc6(0, val);
+
+ nx_lvds_set_lvdslocmask0(0, 0xffffffff);
+ nx_lvds_set_lvdslocmask1(0, 0xffffffff);
+
+ nx_lvds_set_lvdslocpol0(0, (0 << 19) | (0 << 18));
+
+ /*
+ * select TOP MUX
+ */
+ nx_disp_top_set_lvdsmux(1, input);
+
+ /*
+ * LVDS PHY Reset, make sure last.
+ */
+ lvds_phy_reset();
+
+ return 0;
+}
+
+void nx_lvds_display(int module,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_plane_top *top, struct dp_plane_info *planes,
+ struct dp_lvds_dev *dev)
+{
+ struct dp_plane_info *plane = planes;
+ int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+ int count = top->plane_num;
+ int i = 0;
+
+ printf("LVDS: dp.%d\n", module);
+
+ dp_control_init(module);
+ dp_plane_init(module);
+
+ lvds_init();
+
+ /* set plane */
+ dp_plane_screen_setup(module, top);
+
+ for (i = 0; count > i; i++, plane++) {
+ if (!plane->enable)
+ continue;
+ dp_plane_layer_setup(module, plane);
+ dp_plane_layer_enable(module, plane, 1);
+ }
+
+ dp_plane_screen_enable(module, 1);
+
+ /* set lvds */
+ lvds_setup(module, input, sync, ctrl, dev);
+
+ lvds_enable(1);
+
+ /* set dp control */
+ dp_control_setup(module, sync, ctrl);
+ dp_control_enable(module, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_mipi.c b/drivers/video/nexell/s5pxx18_dp_mipi.c
new file mode 100644
index 0000000000..670272b268
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_mipi.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@nexell.co.kr>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/tieoff.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/display.h>
+
+#include "soc/s5pxx18_soc_mipi.h"
+#include "soc/s5pxx18_soc_disptop.h"
+#include "soc/s5pxx18_soc_disptop_clk.h"
+
+#define PLLPMS_1000MHZ 0x33E8
+#define BANDCTL_1000MHZ 0xF
+#define PLLPMS_960MHZ 0x2280
+#define BANDCTL_960MHZ 0xF
+#define PLLPMS_900MHZ 0x2258
+#define BANDCTL_900MHZ 0xE
+#define PLLPMS_840MHZ 0x2230
+#define BANDCTL_840MHZ 0xD
+#define PLLPMS_750MHZ 0x43E8
+#define BANDCTL_750MHZ 0xC
+#define PLLPMS_660MHZ 0x21B8
+#define BANDCTL_660MHZ 0xB
+#define PLLPMS_600MHZ 0x2190
+#define BANDCTL_600MHZ 0xA
+#define PLLPMS_540MHZ 0x2168
+#define BANDCTL_540MHZ 0x9
+#define PLLPMS_512MHZ 0x03200
+#define BANDCTL_512MHZ 0x9
+#define PLLPMS_480MHZ 0x2281
+#define BANDCTL_480MHZ 0x8
+#define PLLPMS_420MHZ 0x2231
+#define BANDCTL_420MHZ 0x7
+#define PLLPMS_402MHZ 0x2219
+#define BANDCTL_402MHZ 0x7
+#define PLLPMS_330MHZ 0x21B9
+#define BANDCTL_330MHZ 0x6
+#define PLLPMS_300MHZ 0x2191
+#define BANDCTL_300MHZ 0x5
+#define PLLPMS_210MHZ 0x2232
+#define BANDCTL_210MHZ 0x4
+#define PLLPMS_180MHZ 0x21E2
+#define BANDCTL_180MHZ 0x3
+#define PLLPMS_150MHZ 0x2192
+#define BANDCTL_150MHZ 0x2
+#define PLLPMS_100MHZ 0x3323
+#define BANDCTL_100MHZ 0x1
+#define PLLPMS_80MHZ 0x3283
+#define BANDCTL_80MHZ 0x0
+
+#define MIPI_INDEX 0
+#define MIPI_EXC_PRE_VALUE 1
+#define MIPI_DSI_IRQ_MASK 29
+
+#define __io_address(a) (void *)(uintptr_t)(a)
+
+struct mipi_xfer_msg {
+ u8 id, data[2];
+ u16 flags;
+ const u8 *tx_buf;
+ u16 tx_len;
+ u8 *rx_buf;
+ u16 rx_len;
+};
+
+static void mipi_reset(void)
+{
+ /* tieoff */
+ nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA, 3);
+ nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB, 3);
+
+ /* reset */
+ nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_MIPI_CSI, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_ASSERT);
+ nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_ASSERT);
+
+ nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_NEGATE);
+ nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_NEGATE);
+}
+
+static void mipi_init(void)
+{
+ int clkid = DP_CLOCK_MIPI;
+ void *base;
+
+ /*
+ * neet to reset before open
+ */
+ mipi_reset();
+
+ base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
+ nx_disp_top_clkgen_set_base_address(clkid, base);
+ nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
+
+ base = __io_address(nx_mipi_get_physical_address(0));
+ nx_mipi_set_base_address(0, base);
+}
+
+static int mipi_get_phy_pll(int bitrate, unsigned int *pllpms,
+ unsigned int *bandctl)
+{
+ unsigned int pms, ctl;
+
+ switch (bitrate) {
+ case 1000:
+ pms = PLLPMS_1000MHZ;
+ ctl = BANDCTL_1000MHZ;
+ break;
+ case 960:
+ pms = PLLPMS_960MHZ;
+ ctl = BANDCTL_960MHZ;
+ break;
+ case 900:
+ pms = PLLPMS_900MHZ;
+ ctl = BANDCTL_900MHZ;
+ break;
+ case 840:
+ pms = PLLPMS_840MHZ;
+ ctl = BANDCTL_840MHZ;
+ break;
+ case 750:
+ pms = PLLPMS_750MHZ;
+ ctl = BANDCTL_750MHZ;
+ break;
+ case 660:
+ pms = PLLPMS_660MHZ;
+ ctl = BANDCTL_660MHZ;
+ break;
+ case 600:
+ pms = PLLPMS_600MHZ;
+ ctl = BANDCTL_600MHZ;
+ break;
+ case 540:
+ pms = PLLPMS_540MHZ;
+ ctl = BANDCTL_540MHZ;
+ break;
+ case 512:
+ pms = PLLPMS_512MHZ;
+ ctl = BANDCTL_512MHZ;
+ break;
+ case 480:
+ pms = PLLPMS_480MHZ;
+ ctl = BANDCTL_480MHZ;
+ break;
+ case 420:
+ pms = PLLPMS_420MHZ;
+ ctl = BANDCTL_420MHZ;
+ break;
+ case 402:
+ pms = PLLPMS_402MHZ;
+ ctl = BANDCTL_402MHZ;
+ break;
+ case 330:
+ pms = PLLPMS_330MHZ;
+ ctl = BANDCTL_330MHZ;
+ break;
+ case 300:
+ pms = PLLPMS_300MHZ;
+ ctl = BANDCTL_300MHZ;
+ break;
+ case 210:
+ pms = PLLPMS_210MHZ;
+ ctl = BANDCTL_210MHZ;
+ break;
+ case 180:
+ pms = PLLPMS_180MHZ;
+ ctl = BANDCTL_180MHZ;
+ break;
+ case 150:
+ pms = PLLPMS_150MHZ;
+ ctl = BANDCTL_150MHZ;
+ break;
+ case 100:
+ pms = PLLPMS_100MHZ;
+ ctl = BANDCTL_100MHZ;
+ break;
+ case 80:
+ pms = PLLPMS_80MHZ;
+ ctl = BANDCTL_80MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *pllpms = pms;
+ *bandctl = ctl;
+
+ return 0;
+}
+
+static int mipi_prepare(int module, int input,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_mipi_dev *mipi)
+{
+ int index = MIPI_INDEX;
+ u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
+ int lpm = mipi->lpm_trans;
+ int ret = 0;
+
+ ret = mipi_get_phy_pll(mipi->hs_bitrate,
+ &mipi->hs_pllpms, &mipi->hs_bandctl);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_get_phy_pll(mipi->lp_bitrate,
+ &mipi->lp_pllpms, &mipi->lp_bandctl);
+ if (ret < 0)
+ return ret;
+
+ debug("%s: mipi lp:%dmhz:0x%x:0x%x, hs:%dmhz:0x%x:0x%x, %s trans\n",
+ __func__, mipi->lp_bitrate, mipi->lp_pllpms, mipi->lp_bandctl,
+ mipi->hs_bitrate, mipi->hs_pllpms, mipi->hs_bandctl,
+ lpm ? "low" : "high");
+
+ if (lpm)
+ nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+ mipi->lp_pllpms, mipi->lp_bandctl, 0, 0);
+ else
+ nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+ mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
+
+#ifdef CONFIG_ARCH_S5P4418
+ /*
+ * disable the escape clock generating prescaler
+ * before soft reset.
+ */
+ nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 0, 10);
+ mdelay(1);
+#endif
+
+ nx_mipi_dsi_software_reset(index);
+ nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value);
+ nx_mipi_dsi_set_phy(index, 0, 1, 1, 0, 0, 0, 0, 0);
+
+ if (lpm)
+ nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_lp,
+ nx_mipi_dsi_lpmode_lp);
+ else
+ nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs,
+ nx_mipi_dsi_lpmode_hs);
+ mdelay(20);
+
+ return 0;
+}
+
+static int mipi_enable(int module, int input,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_mipi_dev *mipi)
+{
+ struct mipi_dsi_device *dsi = &mipi->dsi;
+ int clkid = DP_CLOCK_MIPI;
+ int index = MIPI_INDEX;
+ int width = sync->h_active_len;
+ int height = sync->v_active_len;
+ int HFP = sync->h_front_porch;
+ int HBP = sync->h_back_porch;
+ int HS = sync->h_sync_width;
+ int VFP = sync->v_front_porch;
+ int VBP = sync->v_back_porch;
+ int VS = sync->v_sync_width;
+ int en_prescaler = 1;
+ u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
+
+ int txhsclock = 1;
+ int lpm = mipi->lpm_trans;
+ bool command_mode = mipi->command_mode;
+
+ enum nx_mipi_dsi_format dsi_format;
+ int data_len = dsi->lanes - 1;
+ bool burst = dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST ? true : false;
+ bool eot_enable = dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET ?
+ false : true;
+
+ /*
+ * disable the escape clock generating prescaler
+ * before soft reset.
+ */
+#ifdef CONFIG_ARCH_S5P4418
+ en_prescaler = 0;
+#endif
+
+ debug("%s: mode:%s, lanes.%d\n", __func__,
+ command_mode ? "command" : "video", data_len + 1);
+
+ if (lpm)
+ nx_mipi_dsi_set_escape_lp(index,
+ nx_mipi_dsi_lpmode_hs,
+ nx_mipi_dsi_lpmode_hs);
+
+ nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
+ mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
+ mdelay(1);
+
+ nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, en_prescaler, 10);
+ mdelay(1);
+
+ nx_mipi_dsi_software_reset(index);
+ nx_mipi_dsi_set_clock(index, txhsclock, 0, 1,
+ 1, 1, 0, 0, 0, 1, esc_pre_value);
+
+ switch (data_len) {
+ case 0: /* 1 lane */
+ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 0, 0, 0, 0, 0);
+ break;
+ case 1: /* 2 lane */
+ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 0, 0, 0, 0);
+ break;
+ case 2: /* 3 lane */
+ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 0, 0, 0);
+ break;
+ case 3: /* 3 lane */
+ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 1, 0, 0);
+ break;
+ default:
+ printf("%s: not support data lanes %d\n",
+ __func__, data_len + 1);
+ return -EINVAL;
+ }
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB565:
+ dsi_format = nx_mipi_dsi_format_rgb565;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ dsi_format = nx_mipi_dsi_format_rgb666;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ dsi_format = nx_mipi_dsi_format_rgb666_packed;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ dsi_format = nx_mipi_dsi_format_rgb888;
+ break;
+ default:
+ printf("%s: not support format %d\n", __func__, dsi->format);
+ return -EINVAL;
+ }
+
+ nx_mipi_dsi_set_config_video_mode(index, 1, 0, burst,
+ nx_mipi_dsi_syncmode_event,
+ eot_enable, 1, 1, 1, 1, 0, dsi_format,
+ HFP, HBP, HS, VFP, VBP, VS, 0);
+
+ nx_mipi_dsi_set_size(index, width, height);
+
+ /* set mux */
+ nx_disp_top_set_mipimux(1, module);
+
+ /* 0 is spdif, 1 is mipi vclk */
+ nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv0);
+ nx_disp_top_clkgen_set_clock_divisor(clkid, 1,
+ ctrl->clk_div_lv1 *
+ ctrl->clk_div_lv0);
+
+ /* SPDIF and MIPI */
+ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 1);
+
+ /* START: CLKGEN, MIPI is started in setup function */
+ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, true);
+ nx_mipi_dsi_set_enable(index, true);
+
+ return 0;
+}
+
+static int nx_mipi_transfer_tx(struct mipi_dsi_device *dsi,
+ struct mipi_xfer_msg *xfer)
+{
+ const u8 *txb;
+ int size, index = 0;
+ u32 data;
+
+ if (xfer->tx_len > DSI_TX_FIFO_SIZE)
+ printf("warn: tx %d size over fifo %d\n",
+ (int)xfer->tx_len, DSI_TX_FIFO_SIZE);
+
+ /* write payload */
+ size = xfer->tx_len;
+ txb = xfer->tx_buf;
+
+ while (size >= 4) {
+ data = (txb[3] << 24) | (txb[2] << 16) |
+ (txb[1] << 8) | (txb[0]);
+ nx_mipi_dsi_write_payload(index, data);
+ txb += 4, size -= 4;
+ data = 0;
+ }
+
+ switch (size) {
+ case 3:
+ data |= txb[2] << 16;
+ case 2:
+ data |= txb[1] << 8;
+ case 1:
+ data |= txb[0];
+ nx_mipi_dsi_write_payload(index, data);
+ break;
+ case 0:
+ break; /* no payload */
+ }
+
+ /* write packet hdr */
+ data = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->id;
+
+ nx_mipi_dsi_write_pkheader(index, data);
+
+ return 0;
+}
+
+static int nx_mipi_transfer_done(struct mipi_dsi_device *dsi)
+{
+ int index = 0, count = 100;
+ u32 value;
+
+ do {
+ mdelay(1);
+ value = nx_mipi_dsi_read_fifo_status(index);
+ if (((1 << 22) & value))
+ break;
+ } while (count-- > 0);
+
+ if (count < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int nx_mipi_transfer_rx(struct mipi_dsi_device *dsi,
+ struct mipi_xfer_msg *xfer)
+{
+ u8 *rxb = xfer->rx_buf;
+ int index = 0, rx_len = 0;
+ u32 data, count = 0;
+ u16 size;
+ int err = -EINVAL;
+
+ nx_mipi_dsi_clear_interrupt_pending(index, 18);
+
+ while (1) {
+ /* Completes receiving data. */
+ if (nx_mipi_dsi_get_interrupt_pending(index, 18))
+ break;
+
+ mdelay(1);
+
+ if (count > 500) {
+ printf("%s: error recevice data\n", __func__);
+ err = -EINVAL;
+ goto clear_fifo;
+ } else {
+ count++;
+ }
+ }
+
+ data = nx_mipi_dsi_read_fifo(index);
+
+ switch (data & 0x3f) {
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+ if (xfer->rx_len >= 2) {
+ rxb[1] = data >> 16;
+ rx_len++;
+ }
+
+ /* Fall through */
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+ rxb[0] = data >> 8;
+ rx_len++;
+ xfer->rx_len = rx_len;
+ err = rx_len;
+ goto clear_fifo;
+
+ case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+ printf("DSI Error Report: 0x%04x\n", (data >> 8) & 0xffff);
+ err = rx_len;
+ goto clear_fifo;
+ }
+
+ size = (data >> 8) & 0xffff;
+
+ if (size > xfer->rx_len)
+ size = xfer->rx_len;
+ else if (size < xfer->rx_len)
+ xfer->rx_len = size;
+
+ size = xfer->rx_len - rx_len;
+ rx_len += size;
+
+ /* Receive payload */
+ while (size >= 4) {
+ data = nx_mipi_dsi_read_fifo(index);
+ rxb[0] = (data >> 0) & 0xff;
+ rxb[1] = (data >> 8) & 0xff;
+ rxb[2] = (data >> 16) & 0xff;
+ rxb[3] = (data >> 24) & 0xff;
+ rxb += 4, size -= 4;
+ }
+
+ if (size) {
+ data = nx_mipi_dsi_read_fifo(index);
+ switch (size) {
+ case 3:
+ rxb[2] = (data >> 16) & 0xff;
+ case 2:
+ rxb[1] = (data >> 8) & 0xff;
+ case 1:
+ rxb[0] = data & 0xff;
+ }
+ }
+
+ if (rx_len == xfer->rx_len)
+ err = rx_len;
+
+clear_fifo:
+ size = DSI_RX_FIFO_SIZE / 4;
+ do {
+ data = nx_mipi_dsi_read_fifo(index);
+ if (data == DSI_RX_FIFO_EMPTY)
+ break;
+ } while (--size);
+
+ return err;
+}
+
+#define IS_SHORT(t) (9 > ((t) & 0x0f))
+
+static int nx_mipi_transfer(struct mipi_dsi_device *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ struct mipi_xfer_msg xfer;
+ int err;
+
+ if (!msg->tx_len)
+ return -EINVAL;
+
+ /* set id */
+ xfer.id = msg->type | (msg->channel << 6);
+
+ /* short type msg */
+ if (IS_SHORT(msg->type)) {
+ const char *txb = msg->tx_buf;
+
+ if (msg->tx_len > 2)
+ return -EINVAL;
+
+ xfer.tx_len = 0; /* no payload */
+ xfer.data[0] = txb[0];
+ xfer.data[1] = (msg->tx_len == 2) ? txb[1] : 0;
+ xfer.tx_buf = NULL;
+ } else {
+ xfer.tx_len = msg->tx_len;
+ xfer.data[0] = msg->tx_len & 0xff;
+ xfer.data[1] = msg->tx_len >> 8;
+ xfer.tx_buf = msg->tx_buf;
+ }
+
+ xfer.rx_len = msg->rx_len;
+ xfer.rx_buf = msg->rx_buf;
+ xfer.flags = msg->flags;
+
+ err = nx_mipi_transfer_tx(dsi, &xfer);
+
+ if (xfer.rx_len)
+ err = nx_mipi_transfer_rx(dsi, &xfer);
+
+ nx_mipi_transfer_done(dsi);
+
+ return err;
+}
+
+static ssize_t nx_mipi_write_buffer(struct mipi_dsi_device *dsi,
+ const void *data, size_t len)
+{
+ struct mipi_dsi_msg msg = {
+ .channel = dsi->channel,
+ .tx_buf = data,
+ .tx_len = len
+ };
+
+ switch (len) {
+ case 0:
+ return -EINVAL;
+ case 1:
+ msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+ break;
+ case 2:
+ msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+ break;
+ default:
+ msg.type = MIPI_DSI_DCS_LONG_WRITE;
+ break;
+ }
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+ msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+ return nx_mipi_transfer(dsi, &msg);
+}
+
+__weak int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi)
+{
+ return 0;
+}
+
+/*
+ * disply
+ * MIPI DSI Setting
+ * (1) Initiallize MIPI(DSIM,DPHY,PLL)
+ * (2) Initiallize LCD
+ * (3) ReInitiallize MIPI(DSIM only)
+ * (4) Turn on display(MLC,DPC,...)
+ */
+void nx_mipi_display(int module,
+ struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
+ struct dp_plane_top *top, struct dp_plane_info *planes,
+ struct dp_mipi_dev *dev)
+{
+ struct dp_plane_info *plane = planes;
+ struct mipi_dsi_device *dsi = &dev->dsi;
+ int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
+ int count = top->plane_num;
+ int i = 0, ret;
+
+ printf("MIPI: dp.%d\n", module);
+
+ /* map mipi-dsi write callback func */
+ dsi->write_buffer = nx_mipi_write_buffer;
+
+ ret = nx_mipi_dsi_lcd_bind(dsi);
+ if (ret) {
+ printf("Error: bind mipi-dsi lcd driver !\n");
+ return;
+ }
+
+ dp_control_init(module);
+ dp_plane_init(module);
+
+ mipi_init();
+
+ /* set plane */
+ dp_plane_screen_setup(module, top);
+
+ for (i = 0; count > i; i++, plane++) {
+ if (!plane->enable)
+ continue;
+ dp_plane_layer_setup(module, plane);
+ dp_plane_layer_enable(module, plane, 1);
+ }
+ dp_plane_screen_enable(module, 1);
+
+ /* set mipi */
+ mipi_prepare(module, input, sync, ctrl, dev);
+
+ if (dsi->ops && dsi->ops->prepare)
+ dsi->ops->prepare(dsi);
+
+ if (dsi->ops && dsi->ops->enable)
+ dsi->ops->enable(dsi);
+
+ mipi_enable(module, input, sync, ctrl, dev);
+
+ /* set dp control */
+ dp_control_setup(module, sync, ctrl);
+ dp_control_enable(module, 1);
+}
diff --git a/drivers/video/nexell/s5pxx18_dp_rgb.c b/drivers/video/nexell/s5pxx18_dp_rgb.c
new file mode 100644
index 0000000000..44e8edb02a
--- /dev/null
+++ b/drivers/video/nexell/s5pxx18_dp_rgb.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ *
+ * Author: junghyun, kim <jhkim@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);
+}