arm: add support for SoC s5p4418 (cpu) / nanopi2 board

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- SPL not supported yet --> no spl-dir in arch/arm/cpu/armv7/s5p4418/.
  Appropriate line in Makefile removed.
- cpu.c: '#include <cpu_func.h>' added.
- arch/arm/cpu/armv7/s5p4418/u-boot.lds removed, is not required
  anylonger.
- "obj-$(CONFIG_ARCH_NEXELL) += s5p-common/" added to
  arch/arm/cpu/armv7/Makefile since s5p-common/pwm.c is used instead
  of drivers/pwm/pwm-nexell.c.
- s5p4418.dtsi: '#include "../../../include/generated/autoconf.h"'
  removed, is not necessary, error at out-of-tree building.
  '#ifdef CONFIG_CPU_NXP4330'-blocks (2x) removed. Some minor changes
  regarding mmc. 'u-boot,dm-pre-reloc' added to dp0 because of added
  DM_VIDEO support.
- board/s5p4418/ renamed to board/friendlyarm/
- All s5p4418-boards except nanopi2 removed because there is no
  possibility to test the other boards.
- Kconfig: Changes to have a structure like mach-bcm283x (RaspberryPi),
  e.g. "config ..." entries moved from/to other Kconfig.
- "CONFIG_" removed from several s5p4418/nanopi2 specific defines
  because the appropriate values do not need to be configurable.
- nanopi2/board.c: All getenv(), getenv_ulong(), setenv() and saveenv()
  renamed to env_get(), env_get_ulong(), env_set() and env_save(),
  respectively. MACH_TYPE_S5P4418 is not defined anymore, therefore
  appropriate code removed (not necessary for DT-kernels).
- nanopi2/onewire.c: All crc8() renamed to crc8_ow() because crc8() is
  already defined in lib/crc8.c (with different parameters).
- dts: "nexell,s5pxx18-i2c" used instead of "i2c-gpio", i2c0 and
  i2c1 added. gmac-, ehci- and dwc2otg-entries removed because the
  appropriate functionality is not supported yet. New mmc-property
  "mmcboost" added.
  s5p4418-pinctrl.dtsi: gmac-entries removed, mmc- and i2c-entries
  added.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
  possible (and similar).

Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
diff --git a/board/friendlyarm/nanopi2/Kconfig b/board/friendlyarm/nanopi2/Kconfig
new file mode 100644
index 0000000..0f68422
--- /dev/null
+++ b/board/friendlyarm/nanopi2/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_NANOPI2
+
+config SYS_BOARD
+	default "nanopi2"
+
+config SYS_VENDOR
+	default "friendlyarm"
+
+config SYS_CONFIG_NAME
+	default "s5p4418_nanopi2"
+
+endif
diff --git a/board/friendlyarm/nanopi2/MAINTAINERS b/board/friendlyarm/nanopi2/MAINTAINERS
new file mode 100644
index 0000000..c8e2ce7
--- /dev/null
+++ b/board/friendlyarm/nanopi2/MAINTAINERS
@@ -0,0 +1,7 @@
+NANOPI2 BOARD
+NANOPC-T2 BOARD
+M:  Stefan Bosch <stefan_b@posteo.net>
+S:  Maintained
+F:  board/s5p4418/nanopi2/
+F:  include/configs/s5p4418_nanopi2.h
+F:  configs/s5p4418_nanopi2_defconfig
diff --git a/board/friendlyarm/nanopi2/Makefile b/board/friendlyarm/nanopi2/Makefile
new file mode 100644
index 0000000..5c8b3b7
--- /dev/null
+++ b/board/friendlyarm/nanopi2/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Hyunseok, Jung <hsjung@nexell.co.kr>
+
+obj-y	:= board.o hwrev.o lcds.o
+obj-$(CONFIG_S5P4418_ONEWIRE) += onewire.o
diff --git a/board/friendlyarm/nanopi2/board.c b/board/friendlyarm/nanopi2/board.c
new file mode 100644
index 0000000..6898053
--- /dev/null
+++ b/board/friendlyarm/nanopi2/board.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <fdt_support.h>
+#include <log.h>
+#ifdef CONFIG_PWM_NX
+#include <pwm.h>
+#endif
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+#include <asm/arch/display.h>
+#include <asm/arch/display_dev.h>
+
+#include <u-boot/md5.h>
+
+#include <linux/stringify.h>
+
+#include "hwrev.h"
+#include "onewire.h"
+#include "nxp-fb.h"
+
+#include <env_internal.h>	/* for env_save() */
+#include <asm/mach-types.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum gpio_group {
+	gpio_a, gpio_b, gpio_c, gpio_d, gpio_e,
+};
+
+#ifdef CONFIG_PWM_NX
+struct pwm_device {
+	int grp;
+	int bit;
+	int io_fn;
+};
+
+static inline void bd_pwm_config_gpio(int ch)
+{
+	struct pwm_device pwm_dev[] = {
+		[0] = { .grp = gpio_d, .bit = 1,  .io_fn = 0 },
+		[1] = { .grp = gpio_c, .bit = 13, .io_fn = 1 },
+		[2] = { .grp = gpio_c, .bit = 14, .io_fn = 1 },
+		[3] = { .grp = gpio_d, .bit = 0,  .io_fn = 0 },
+	};
+
+	int gp = pwm_dev[ch].grp;
+	int io = pwm_dev[ch].bit;
+
+	/* pwm backlight OFF: HIGH, ON: LOW */
+	nx_gpio_set_pad_function(gp, io, pwm_dev[ch].io_fn);
+	nx_gpio_set_output_value(gp, io, 1);
+	nx_gpio_set_output_enable(gp, io, 1);
+}
+#endif
+
+static void bd_backlight_off(void)
+{
+#ifdef CONFIG_S5P4418_ONEWIRE
+	onewire_set_backlight(0);
+
+#elif defined(BACKLIGHT_CH)
+	bd_pwm_config_gpio(BACKLIGHT_CH);
+#endif
+}
+
+static void bd_backlight_on(void)
+{
+#ifdef CONFIG_S5P4418_ONEWIRE
+	onewire_set_backlight(127);
+
+#elif defined(BACKLIGHT_CH)
+	/* pwm backlight ON: HIGH, ON: LOW */
+	pwm_init(BACKLIGHT_CH,
+		 BACKLIGHT_DIV, BACKLIGHT_INV);
+	pwm_config(BACKLIGHT_CH,
+		   TO_DUTY_NS(BACKLIGHT_DUTY, BACKLIGHT_HZ),
+		   TO_PERIOD_NS(BACKLIGHT_HZ));
+#endif
+}
+
+static void bd_lcd_config_gpio(void)
+{
+	int i;
+
+	for (i = 0; i < 28; i++) {
+		nx_gpio_set_pad_function(gpio_a, i, 1);
+		nx_gpio_set_drive_strength(gpio_a, i, 0);
+		nx_gpio_set_pull_mode(gpio_a, i, 2);
+	}
+
+	nx_gpio_set_drive_strength(gpio_a, 0, 1);
+}
+
+/* DEFAULT mmc dev for eMMC boot (dwmmc.2) */
+static int mmc_boot_dev;
+
+int board_mmc_bootdev(void)
+{
+	return mmc_boot_dev;
+}
+
+/* call from common/env_mmc.c */
+int mmc_get_env_dev(void)
+{
+	return mmc_boot_dev;
+}
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+int checkboard(void)
+{
+	printf("Board: %s\n", get_board_name());
+
+	return 0;
+}
+#endif
+
+int nx_display_fixup_dp(struct nx_display_dev *dp)
+{
+	struct nxp_lcd *lcd = bd_get_lcd();
+	enum lcd_format fmt = bd_get_lcd_format();
+	struct nxp_lcd_timing *timing = &lcd->timing;
+	struct dp_sync_info *sync = &dp->sync;
+	struct dp_plane_info *plane = &dp->planes[0];
+	int i;
+	u32 clk = 800000000;
+	u32 div;
+
+	sync->h_active_len = lcd->width;
+	sync->h_sync_width = timing->h_sw;
+	sync->h_back_porch = timing->h_bp;
+	sync->h_front_porch = timing->h_fp;
+	sync->h_sync_invert = !lcd->polarity.inv_hsync;
+
+	sync->v_active_len = lcd->height;
+	sync->v_sync_width = timing->v_sw;
+	sync->v_back_porch = timing->v_bp;
+	sync->v_front_porch = timing->v_fp;
+	sync->v_sync_invert = !lcd->polarity.inv_vsync;
+
+	/* calculates pixel clock */
+	div  = timing->h_sw + timing->h_bp + timing->h_fp + lcd->width;
+	div *= timing->v_sw + timing->v_bp + timing->v_fp + lcd->height;
+	div *= lcd->freq ? : 60;
+	clk /= div;
+
+	dp->ctrl.clk_div_lv0 = clk;
+	dp->ctrl.clk_inv_lv0 = lcd->polarity.rise_vclk;
+
+	dp->top.screen_width = lcd->width;
+	dp->top.screen_height = lcd->height;
+
+	for (i = 0; i < dp->top.plane_num; i++, plane++) {
+		if (plane->enable) {
+			plane->width = lcd->width;
+			plane->height = lcd->height;
+		}
+	}
+
+	/* initialize display device type */
+	if (fmt == LCD_RGB) {
+		dp->dev_type = DP_DEVICE_RGBLCD;
+
+	} else if (fmt == LCD_HDMI) {
+		struct dp_hdmi_dev *dev = (struct dp_hdmi_dev *)dp->device;
+
+		dp->dev_type = DP_DEVICE_HDMI;
+		if (lcd->width == 1920 && lcd->height == 1080)
+			dev->preset = 1;
+		else
+			dev->preset = 0;
+
+	} else {
+		struct dp_lvds_dev *dev = (struct dp_lvds_dev *)dp->device;
+
+		dp->dev_type = DP_DEVICE_LVDS;
+		dev->lvds_format = (fmt & 0x3);
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * initialize board status.
+ */
+
+#define	MMC_BOOT_CH0		(0)
+#define	MMC_BOOT_CH1		(1 <<  3)
+#define	MMC_BOOT_CH2		(1 << 19)
+
+static void bd_bootdev_init(void)
+{
+	unsigned int rst = readl(PHY_BASEADDR_CLKPWR + SYSRSTCONFIG);
+
+	rst &= (1 << 19) | (1 << 3);
+	if (rst == MMC_BOOT_CH0) {
+		/* mmc dev 1 for SD boot */
+		mmc_boot_dev = 1;
+	}
+}
+
+#ifdef CONFIG_S5P4418_ONEWIRE
+static void bd_onewire_init(void)
+{
+	unsigned char lcd;
+	unsigned short fw_ver;
+
+	onewire_init();
+	onewire_get_info(&lcd, &fw_ver);
+}
+#endif
+
+static void bd_lcd_init(void)
+{
+	struct nxp_lcd *cfg;
+	int id = -1;
+	int ret;
+
+#ifdef CONFIG_S5P4418_ONEWIRE
+	id = onewire_get_lcd_id();
+	/* -1: onwire probe failed
+	 *  0: bad
+	 * >0: identified
+	 */
+#endif
+	ret = bd_setup_lcd_by_id(id);
+	if (id <= 0 || ret != id) {
+		printf("Panel: N/A (%d)\n", id);
+		bd_setup_lcd_by_name("HDMI720P60");
+
+	} else {
+		printf("Panel: %s\n", bd_get_lcd_name());
+
+		cfg = bd_get_lcd();
+		if (cfg->gpio_init)
+			cfg->gpio_init();
+	}
+}
+
+static int mac_read_from_generic_eeprom(u8 *addr)
+{
+	return -1;
+}
+
+static void make_ether_addr(u8 *addr)
+{
+	u32 hash[20];
+
+#define ETHER_MAC_TAG  "ethmac"
+	memset(hash, 0, sizeof(hash));
+	memcpy(hash + 12, ETHER_MAC_TAG, sizeof(ETHER_MAC_TAG));
+
+	hash[4] = readl(PHY_BASEADDR_ECID + 0x00);
+	hash[5] = readl(PHY_BASEADDR_ECID + 0x04);
+	hash[6] = readl(PHY_BASEADDR_ECID + 0x08);
+	hash[7] = readl(PHY_BASEADDR_ECID + 0x0c);
+
+	md5((unsigned char *)&hash[4], 64, (unsigned char *)hash);
+
+	hash[0] ^= hash[2];
+	hash[1] ^= hash[3];
+
+	memcpy(addr, (char *)hash, 6);
+	addr[0] &= 0xfe;	/* clear multicast bit */
+	addr[0] |= 0x02;
+}
+
+static void set_ether_addr(void)
+{
+	unsigned char mac[6];
+	char ethaddr[20];
+	int ret;
+
+	if (env_get("ethaddr"))
+		return;
+
+	ret = mac_read_from_generic_eeprom(mac);
+	if (ret < 0)
+		make_ether_addr(mac);
+
+	sprintf(ethaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	if (!ret)
+		printf("MAC:  [%s]\n", ethaddr);
+
+	env_set("ethaddr", ethaddr);
+}
+
+#ifdef CONFIG_REVISION_TAG
+static void set_board_rev(void)
+{
+	char info[64] = {0, };
+
+	snprintf(info, ARRAY_SIZE(info), "%02x", get_board_rev());
+	env_set("board_rev", info);
+}
+#endif
+
+static void set_dtb_name(void)
+{
+	char info[64] = {0, };
+
+	snprintf(info, ARRAY_SIZE(info),
+		 "s5p4418-nanopi2-rev%02x.dtb", get_board_rev());
+	env_set("dtb_name", info);
+}
+
+static void bd_update_env(void)
+{
+	char *lcdtype = env_get("lcdtype");
+	char *lcddpi = env_get("lcddpi");
+	char *bootargs = env_get("bootargs");
+	const char *name;
+	char *p = NULL;
+	int rootdev = board_mmc_bootdev();
+	int need_save = 0;
+
+#define CMDLINE_LCD		" lcd="
+	char cmdline[CONFIG_SYS_CBSIZE];
+	int n = 1;
+
+	if (rootdev != CONFIG_ROOT_DEV && !env_get("firstboot")) {
+		env_set_ulong("rootdev", rootdev);
+		env_set("firstboot", "0");
+		need_save = 1;
+	}
+
+	if (lcdtype) {
+		/* Setup again as user specified LCD in env */
+		bd_setup_lcd_by_name(lcdtype);
+	}
+
+	name = bd_get_lcd_name();
+
+	if (bootargs)
+		n = strlen(bootargs);	/* isn't 0 for NULL */
+	else
+		cmdline[0] = '\0';
+
+	if ((n + strlen(name) + sizeof(CMDLINE_LCD)) > sizeof(cmdline)) {
+		printf("Error: `bootargs' is too large (%d)\n", n);
+		goto __exit;
+	}
+
+	if (bootargs) {
+		p = strstr(bootargs, CMDLINE_LCD);
+		if (p) {
+			n = (p - bootargs);
+			p += strlen(CMDLINE_LCD);
+		}
+		strncpy(cmdline, bootargs, n);
+	}
+
+	/* add `lcd=NAME,NUMdpi' */
+	strncpy(cmdline + n, CMDLINE_LCD, strlen(CMDLINE_LCD));
+	n += strlen(CMDLINE_LCD);
+
+	strcpy(cmdline + n, name);
+	n += strlen(name);
+
+	if (lcddpi) {
+		n += sprintf(cmdline + n, ",%sdpi", lcddpi);
+	} else {
+		int dpi = bd_get_lcd_density();
+
+		if (dpi > 0 && dpi < 600)
+			n += sprintf(cmdline + n, ",%ddpi", dpi);
+	}
+
+	/* copy remaining of bootargs */
+	if (p) {
+		p = strstr(p, " ");
+		if (p) {
+			strcpy(cmdline + n, p);
+			n += strlen(p);
+		}
+	}
+
+	/* append `bootdev=2' */
+#define CMDLINE_BDEV	" bootdev="
+	if (rootdev > 0 && !strstr(cmdline, CMDLINE_BDEV))
+		n += sprintf(cmdline + n, "%s2", CMDLINE_BDEV);
+
+	/* finally, let's update uboot env & save it */
+	if (bootargs && strncmp(cmdline, bootargs, sizeof(cmdline))) {
+		env_set("bootargs", cmdline);
+		need_save = 1;
+	}
+
+__exit:
+	if (need_save)
+		env_save();
+}
+
+/* --------------------------------------------------------------------------
+ * call from u-boot
+ */
+
+int board_early_init_f(void)
+{
+	return 0;
+}
+
+int board_init(void)
+{
+	bd_hwrev_init();
+	bd_base_rev_init();
+
+	bd_bootdev_init();
+#ifdef CONFIG_S5P4418_ONEWIRE
+	bd_onewire_init();
+#endif
+
+	bd_backlight_off();
+
+	bd_lcd_config_gpio();
+	bd_lcd_init();
+
+	if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
+		gd->flags |= GD_FLG_SILENT;
+
+	return 0;
+}
+
+#ifdef CONFIG_BOARD_LATE_INIT
+int board_late_init(void)
+{
+	bd_update_env();
+
+#ifdef CONFIG_REVISION_TAG
+	set_board_rev();
+#endif
+	set_dtb_name();
+
+	set_ether_addr();
+
+	if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
+		gd->flags &= ~GD_FLG_SILENT;
+
+	bd_backlight_on();
+	printf("\n");
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SPLASH_SOURCE
+#include <splash.h>
+static struct splash_location splash_locations[] = {
+	{
+	.name = "mmc_fs",
+	.storage = SPLASH_STORAGE_MMC,
+	.flags = SPLASH_STORAGE_FS,
+	.devpart = __stringify(CONFIG_ROOT_DEV) ":"
+		   __stringify(CONFIG_BOOT_PART),
+	},
+};
+
+int splash_screen_prepare(void)
+{
+	int err;
+	char *env_cmd = env_get("load_splash");
+
+	debug("%s()\n", __func__);
+
+	if (env_cmd) {
+		err = run_command(env_cmd, 0);
+
+	} else {
+		char devpart[64] = { 0, };
+		int bootpart = env_get_ulong("bootpart", 0, CONFIG_BOOT_PART);
+		int rootdev;
+
+		if (env_get("firstboot"))
+			rootdev = env_get_ulong("rootdev", 0, CONFIG_ROOT_DEV);
+		else
+			rootdev = board_mmc_bootdev();
+
+		snprintf(devpart, ARRAY_SIZE(devpart), "%d:%d", rootdev,
+			 bootpart);
+		splash_locations[0].devpart = devpart;
+
+		err = splash_source_load(splash_locations,
+					 ARRAY_SIZE(splash_locations));
+	}
+
+	if (!err) {
+		char addr[64];
+
+		sprintf(addr, "0x%lx", gd->fb_base);
+		env_set("fb_addr", addr);
+	}
+
+	return err;
+}
+#endif
+
+/* u-boot dram initialize */
+int dram_init(void)
+{
+	gd->ram_size = CONFIG_SYS_SDRAM_SIZE;
+	return 0;
+}
+
+/* u-boot dram board specific */
+int dram_init_banksize(void)
+{
+#define SCR_USER_SIG6_READ		(SCR_ALIVE_BASE + 0x0F0)
+	unsigned int reg_val = readl(SCR_USER_SIG6_READ);
+
+	/* set global data memory */
+	gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x00000100;
+
+	gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
+	gd->bd->bi_dram[0].size  = CONFIG_SYS_SDRAM_SIZE;
+
+	/* Number of Row: 14 bits */
+	if ((reg_val >> 28) == 14)
+		gd->bd->bi_dram[0].size -= 0x20000000;
+
+	/* Number of Memory Chips */
+	if ((reg_val & 0x3) > 1) {
+		gd->bd->bi_dram[1].start = 0x80000000;
+		gd->bd->bi_dram[1].size  = 0x40000000;
+	}
+	return 0;
+}
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+int ft_board_setup(void *blob, struct bd_info *bd)
+{
+	int nodeoff;
+	unsigned int rootdev;
+	unsigned int fb_addr;
+
+	if (board_mmc_bootdev() > 0) {
+		rootdev = fdt_getprop_u32_default(blob, "/board", "sdidx", 2);
+		if (rootdev) {
+			/* find or create "/chosen" node. */
+			nodeoff = fdt_find_or_add_subnode(blob, 0, "chosen");
+			if (nodeoff >= 0)
+				fdt_setprop_u32(blob, nodeoff, "linux,rootdev",
+						rootdev);
+		}
+	}
+
+	fb_addr = env_get_ulong("fb_addr", 0, 0);
+	if (fb_addr) {
+		nodeoff = fdt_path_offset(blob, "/reserved-memory");
+		if (nodeoff < 0)
+			return nodeoff;
+
+		nodeoff = fdt_add_subnode(blob, nodeoff, "display_reserved");
+		if (nodeoff >= 0) {
+			fdt32_t cells[2];
+
+			cells[0] = cpu_to_fdt32(fb_addr);
+			cells[1] = cpu_to_fdt32(0x800000);
+
+			fdt_setprop(blob, nodeoff, "reg", cells,
+				    sizeof(cells[0]) * 2);
+		}
+	}
+
+	return 0;
+}
+#endif
diff --git a/board/friendlyarm/nanopi2/hwrev.c b/board/friendlyarm/nanopi2/hwrev.c
new file mode 100644
index 0000000..b1e23a4
--- /dev/null
+++ b/board/friendlyarm/nanopi2/hwrev.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <i2c.h>
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+
+/* Board revision list: <PCB3 | PCB2 | PCB1>
+ *  0b000 - NanoPi 2
+ *  0b001 - NanoPC-T2
+ *  0b010 - NanoPi S2
+ *  0b011 - Smart4418
+ *  0b100 - NanoPi Fire 2A
+ *  0b111 - NanoPi M2A
+ *
+ * Extented revision:
+ *  0b001 - Smart4418-SDK
+ */
+#define __IO_GRP		2	/* GPIO_C */
+#define __IO_PCB1		26
+#define __IO_PCB2		27
+#define __IO_PCB3		25
+
+static int pcb_rev = -1;
+static int base_rev;
+
+static void bd_hwrev_config_gpio(void)
+{
+	int gpios[3][2] = {
+		{ __IO_PCB1, 1 },
+		{ __IO_PCB2, 1 },
+		{ __IO_PCB3, 1 },
+	};
+	int i;
+
+	/* gpio input mode, pull-down */
+	for (i = 0; i < 3; i++) {
+		nx_gpio_set_pad_function(__IO_GRP, gpios[i][0], gpios[i][1]);
+		nx_gpio_set_output_enable(__IO_GRP, gpios[i][0], 0);
+		nx_gpio_set_pull_mode(__IO_GRP, gpios[i][0], 0);
+	}
+}
+
+void bd_hwrev_init(void)
+{
+	if (pcb_rev >= 0)
+		return;
+
+	bd_hwrev_config_gpio();
+
+	pcb_rev  = nx_gpio_get_input_value(__IO_GRP, __IO_PCB1);
+	pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB2) << 1;
+	pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB3) << 2;
+}
+
+/* Get extended revision for SmartXX18 */
+void bd_base_rev_init(void)
+{
+	struct udevice *dev;
+	u8 val = 0;
+
+	if (pcb_rev != 0x3)
+		return;
+
+#define PCA9536_I2C_BUS     2
+#define PCA9636_I2C_ADDR    0x41
+	if (i2c_get_chip_for_busnum
+				(PCA9536_I2C_BUS, PCA9636_I2C_ADDR, 1, &dev))
+		return;
+
+	if (!dm_i2c_read(dev, 0, &val, 1))
+		base_rev = (val & 0xf);
+}
+
+/* To override __weak symbols */
+u32 get_board_rev(void)
+{
+	return (base_rev << 8) | pcb_rev;
+}
+
+const char *get_board_name(void)
+{
+	bd_hwrev_init();
+
+	switch (pcb_rev) {
+	case 0:
+		return "NanoPi 2";
+	case 1:
+		return "NanoPC-T2";
+	case 2:
+		return "NanoPi S2";
+	case 3:
+		return "Smart4418";
+	case 4:
+		return "NanoPi Fire 2A";
+	case 7:
+		return "NanoPi M2A";
+	default:
+		return "s5p4418-X";
+	}
+}
diff --git a/board/friendlyarm/nanopi2/hwrev.h b/board/friendlyarm/nanopi2/hwrev.h
new file mode 100644
index 0000000..1b1a828
--- /dev/null
+++ b/board/friendlyarm/nanopi2/hwrev.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#ifndef __BD_HW_REV_H__
+#define __BD_HW_REV_H__
+
+extern void bd_hwrev_init(void);
+extern void bd_base_rev_init(void);
+extern u32 get_board_rev(void);
+extern const char *get_board_name(void);
+
+#endif /* __BD_HW_REV_H__ */
diff --git a/board/friendlyarm/nanopi2/lcds.c b/board/friendlyarm/nanopi2/lcds.c
new file mode 100644
index 0000000..7303e53
--- /dev/null
+++ b/board/friendlyarm/nanopi2/lcds.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 FriendlyARM (www.arm9.net)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <fdt_support.h>
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/display.h>
+#include <asm/arch/nx_gpio.h>
+
+#include "nxp-fb.h"
+
+/*
+ * param @module_index for nx_gpio APIs and will be removed
+ * after support pinctrl
+ */
+#ifndef PAD_GPIO_A
+#define PAD_GPIO_A	0
+#endif
+
+static inline void common_gpio_init(void)
+{
+	/* PVCLK */
+	nx_gpio_set_fast_slew(PAD_GPIO_A, 0, 1);
+}
+
+static void s70_gpio_init(void)
+{
+	int i;
+
+	/* PVCLK */
+	nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 1);
+
+	/* RGB24 */
+	for (i = 1; i < 25; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 2);
+
+	/* HS/VS/DE */
+	for (; i < 28; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void s702_gpio_init(void)
+{
+	int i;
+
+	common_gpio_init();
+
+	nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2);
+
+	for (i = 1; i < 25; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 0);
+
+	for (; i < 28; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void s430_gpio_init(void)
+{
+	int  i;
+
+	for (i = 0; i < 28; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void hd101_gpio_init(void)
+{
+	int i;
+
+	common_gpio_init();
+
+	nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2);
+
+	for (i = 1; i < 25; i++)
+		nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+
+	nx_gpio_set_drive_strength(PAD_GPIO_A, 27, 1);
+}
+
+static void hd700_gpio_init(void)
+{
+	hd101_gpio_init();
+}
+
+/* NXP display configs for supported LCD */
+
+static struct nxp_lcd wxga_hd700 = {
+	.width = 800,
+	.height = 1280,
+	.p_width = 94,
+	.p_height = 151,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 20,
+		.h_bp = 20,
+		.h_sw = 24,
+		.v_fp =  4,
+		.v_fpe = 1,
+		.v_bp =  4,
+		.v_bpe = 1,
+		.v_sw =  8,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 0,
+		.inv_vsync = 0,
+		.inv_vden = 0,
+	},
+	.gpio_init = hd700_gpio_init,
+};
+
+static struct nxp_lcd wvga_s70 = {
+	.width = 800,
+	.height = 480,
+	.p_width = 155,
+	.p_height = 93,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 48,
+		.h_bp = 36,
+		.h_sw = 10,
+		.v_fp = 22,
+		.v_fpe = 1,
+		.v_bp = 15,
+		.v_bpe = 1,
+		.v_sw = 8,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = s70_gpio_init,
+};
+
+static struct nxp_lcd wvga_s702 = {
+	.width = 800,
+	.height = 480,
+	.p_width = 155,
+	.p_height = 93,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 44,
+		.h_bp = 26,
+		.h_sw = 20,
+		.v_fp = 22,
+		.v_fpe = 1,
+		.v_bp = 15,
+		.v_bpe = 1,
+		.v_sw = 8,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = s702_gpio_init,
+};
+
+static struct nxp_lcd wvga_s70d = {
+	.width = 800,
+	.height = 480,
+	.p_width = 155,
+	.p_height = 93,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 80,
+		.h_bp = 78,
+		.h_sw = 10,
+		.v_fp = 22,
+		.v_fpe = 1,
+		.v_bp = 24,
+		.v_bpe = 1,
+		.v_sw = 8,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = s702_gpio_init,
+};
+
+static struct nxp_lcd wvga_w50 = {
+	.width = 800,
+	.height = 480,
+	.p_width = 108,
+	.p_height = 64,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 40,
+		.h_bp = 40,
+		.h_sw = 48,
+		.v_fp = 20,
+		.v_fpe = 1,
+		.v_bp = 20,
+		.v_bpe = 1,
+		.v_sw = 12,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = s70_gpio_init,
+};
+
+static struct nxp_lcd wvga_s430 = {
+	.width = 480,
+	.height = 800,
+	.p_width = 108,
+	.p_height = 64,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 64,
+		.h_bp = 0,
+		.h_sw = 16,
+		.v_fp = 32,
+		.v_fpe = 1,
+		.v_bp = 0,
+		.v_bpe = 1,
+		.v_sw = 16,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = s430_gpio_init,
+};
+
+static struct nxp_lcd wsvga_w101 = {
+	.width = 1024,
+	.height = 600,
+	.p_width = 204,
+	.p_height = 120,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 40,
+		.h_bp = 40,
+		.h_sw = 200,
+		.v_fp =  8,
+		.v_fpe = 1,
+		.v_bp =  8,
+		.v_bpe = 1,
+		.v_sw = 16,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd wsvga_x710 = {
+	.width = 1024,
+	.height = 600,
+	.p_width = 154,
+	.p_height = 90,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 84,
+		.h_bp = 84,
+		.h_sw = 88,
+		.v_fp = 10,
+		.v_fpe = 1,
+		.v_bp = 10,
+		.v_bpe = 1,
+		.v_sw = 20,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+	.gpio_init = hd101_gpio_init,
+};
+
+static struct nxp_lcd xga_a97 = {
+	.width = 1024,
+	.height = 768,
+	.p_width = 200,
+	.p_height = 150,
+	.bpp = 24,
+	.freq = 61,
+
+	.timing = {
+		.h_fp = 12,
+		.h_bp = 12,
+		.h_sw = 4,
+		.v_fp = 8,
+		.v_fpe = 1,
+		.v_bp = 8,
+		.v_bpe = 1,
+		.v_sw =  4,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd xga_lq150 = {
+	.width = 1024,
+	.height = 768,
+	.p_width = 304,
+	.p_height = 228,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 12,
+		.h_bp = 12,
+		.h_sw = 40,
+		.v_fp = 8,
+		.v_fpe = 1,
+		.v_bp = 8,
+		.v_bpe = 1,
+		.v_sw = 40,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd vga_l80 = {
+	.width = 640,
+	.height = 480,
+	.p_width = 160,
+	.p_height = 120,
+	.bpp = 32,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 35,
+		.h_bp = 53,
+		.h_sw = 73,
+		.v_fp = 3,
+		.v_fpe = 1,
+		.v_bp = 29,
+		.v_bpe = 1,
+		.v_sw = 6,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd wxga_bp101 = {
+	.width = 1280,
+	.height = 800,
+	.p_width = 218,
+	.p_height = 136,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 20,
+		.h_bp = 20,
+		.h_sw = 24,
+		.v_fp =  4,
+		.v_fpe = 1,
+		.v_bp =  4,
+		.v_bpe = 1,
+		.v_sw =  8,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd wxga_hd101 = {
+	.width = 1280,
+	.height = 800,
+	.p_width = 218,
+	.p_height = 136,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 16,
+		.h_bp = 16,
+		.h_sw = 30,
+		.v_fp =  8,
+		.v_fpe = 1,
+		.v_bp =  8,
+		.v_bpe = 1,
+		.v_sw = 12,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 0,
+		.inv_vsync = 0,
+		.inv_vden = 0,
+	},
+	.gpio_init = hd101_gpio_init,
+};
+
+static struct nxp_lcd hvga_h43 = {
+	.width = 480,
+	.height = 272,
+	.p_width = 96,
+	.p_height = 54,
+	.bpp = 32,
+	.freq = 65,
+
+	.timing = {
+		.h_fp =  5,
+		.h_bp = 40,
+		.h_sw =  2,
+		.v_fp =  8,
+		.v_fpe = 1,
+		.v_bp =  8,
+		.v_bpe = 1,
+		.v_sw =  2,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd hvga_p43 = {
+	.width = 480,
+	.height = 272,
+	.p_width = 96,
+	.p_height = 54,
+	.bpp = 32,
+	.freq = 65,
+
+	.timing = {
+		.h_fp =  5,
+		.h_bp = 40,
+		.h_sw =  2,
+		.v_fp =  8,
+		.v_fpe = 1,
+		.v_bp =  9,
+		.v_bpe = 1,
+		.v_sw =  2,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct nxp_lcd qvga_w35 = {
+	.width = 320,
+	.height = 240,
+	.p_width = 70,
+	.p_height = 52,
+	.bpp = 16,
+	.freq = 65,
+
+	.timing = {
+		.h_fp =  4,
+		.h_bp = 70,
+		.h_sw =  4,
+		.v_fp =  4,
+		.v_fpe = 1,
+		.v_bp = 12,
+		.v_bpe = 1,
+		.v_sw =  4,
+	},
+	.polarity = {
+		.rise_vclk = 1,
+		.inv_hsync = 0,
+		.inv_vsync = 0,
+		.inv_vden = 0,
+	},
+};
+
+/* HDMI */
+static struct nxp_lcd hdmi_def = {
+	.width = 1920,
+	.height = 1080,
+	.p_width = 480,
+	.p_height = 320,
+	.bpp = 24,
+	.freq = 60,
+
+	.timing = {
+		.h_fp = 12,
+		.h_bp = 12,
+		.h_sw = 4,
+		.v_fp = 8,
+		.v_fpe = 1,
+		.v_bp = 8,
+		.v_bpe = 1,
+		.v_sw =  4,
+	},
+	.polarity = {
+		.rise_vclk = 0,
+		.inv_hsync = 1,
+		.inv_vsync = 1,
+		.inv_vden = 0,
+	},
+};
+
+static struct hdmi_config {
+	char *name;
+	int width;
+	int height;
+} bd_hdmi_config[] = {
+	{ "HDMI1080P60",	1920, 1080 },
+	{ "HDMI1080I60",	1920, 1080 },
+	{ "HDMI1080P30",	1920, 1080 },
+	{ "HDMI1080P50",	1920, 1080 },
+	{ "HDMI1080I50",	1920, 1080 },
+
+	{ "HDMI1080P60D",	 960,  536 },
+	{ "HDMI1080I60D",	 960,  536 },
+	{ "HDMI1080P30D",	 960,  536 },
+	{ "HDMI1080P50D",	 960,  536 },
+	{ "HDMI1080I50D",	 960,  536 },
+
+	{ "HDMI720P60",		1280,  720 },
+	{ "HDMI720P60D",	 640,  360 },
+	{ "HDMI720P50",		1280,  720 },
+	{ "HDMI720P50D",	 640,  360 },
+
+	{ "HDMI576P16X9",	 720,  576 },
+	{ "HDMI576P16X9D",	 720,  576 },
+	{ "HDMI576P4X3",	 720,  576 },
+	{ "HDMI576P4X3D",	 720,  576 },
+
+	{ "HDMI480P16X9",	 720,  480 },
+	{ "HDMI480P16X9D",	 720,  480 },
+	{ "HDMI480P4X3",	 720,  480 },
+	{ "HDMI480P4X3D",	 720,  480 },
+};
+
+/* Try to guess LCD panel by kernel command line, or
+ * using *HD101* as default
+ */
+static struct {
+	int id;
+	char *name;
+	struct nxp_lcd *lcd;
+	int dpi;
+	int ctp;
+	enum lcd_format fmt;
+} bd_lcd_config[] = {
+	{  25, "HD101",	 &wxga_hd101,   0, 1, LCD_RGB  },
+	{  32, "HD101B", &wxga_hd101,   0, 1, LCD_RGB  },
+	{  18, "HD700",	 &wxga_hd700, 213, 1, LCD_RGB  },
+	{  30, "HD702",	 &wxga_hd700, 213, 1, LCD_RGB  },
+	{  33, "H70",	 &wxga_hd700, 213, 0, LCD_VESA },
+	{   3, "S70",	 &wvga_s70,   128, 1, LCD_RGB  },
+	{  36, "S701",	 &wvga_s70,   128, 1, LCD_RGB  },
+	{  24, "S702",	 &wvga_s702,  128, 3, LCD_RGB  },
+	{  26, "S70D",	 &wvga_s70d,  128, 0, LCD_RGB  },
+	{  14, "H43",	 &hvga_h43,     0, 0, LCD_RGB  },
+	{  19, "P43",	 &hvga_p43,     0, 0, LCD_RGB  },
+	{   8, "W35",	 &qvga_w35,     0, 0, LCD_RGB  },
+	{  28, "X710",	 &wsvga_x710,   0, 1, LCD_RGB  },
+	{  31, "S430",	 &wvga_s430,  180, 1, LCD_RGB  },
+	{   4, "W50",	 &wvga_w50,     0, 0, LCD_RGB  },
+
+	/* TODO: Testing */
+	{  15, "W101",	 &wsvga_w101,   0, 1, LCD_RGB  },
+	{   5, "L80",	 &vga_l80,      0, 1, LCD_RGB  },
+	{  -1, "A97",	 &xga_a97,      0, 0, LCD_RGB  },
+	{  -1, "LQ150",	 &xga_lq150,    0, 1, LCD_RGB  },
+	{  -1, "BP101",	 &wxga_bp101,   0, 1, LCD_RGB  },
+	/* Pls keep it at last */
+	{ 128, "HDMI",	 &hdmi_def,     0, 0, LCD_HDMI },
+};
+
+static int lcd_idx;
+
+int bd_setup_lcd_by_id(int id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) {
+		if (bd_lcd_config[i].id == id) {
+			lcd_idx = i;
+			break;
+		}
+	}
+
+	if (i >= ARRAY_SIZE(bd_lcd_config)) {
+		/* NOT found */
+		return -19;
+	}
+
+	return bd_lcd_config[i].id;
+}
+
+int bd_setup_lcd_by_name(char *str)
+{
+	char *delim;
+	int i;
+
+	delim = strchr(str, ',');
+	if (delim)
+		*delim++ = '\0';
+
+	if (!strncasecmp("HDMI", str, 4)) {
+		struct hdmi_config *cfg = &bd_hdmi_config[0];
+		struct nxp_lcd *lcd;
+
+		lcd_idx = ARRAY_SIZE(bd_lcd_config) - 1;
+		lcd = bd_lcd_config[lcd_idx].lcd;
+
+		for (i = 0; i < ARRAY_SIZE(bd_hdmi_config); i++, cfg++) {
+			if (!strcasecmp(cfg->name, str)) {
+				lcd->width = cfg->width;
+				lcd->height = cfg->height;
+				bd_lcd_config[lcd_idx].name = cfg->name;
+				goto __ret;
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) {
+		if (!strcasecmp(bd_lcd_config[i].name, str)) {
+			lcd_idx = i;
+			break;
+		}
+	}
+
+__ret:
+	return 0;
+}
+
+struct nxp_lcd *bd_get_lcd(void)
+{
+	return bd_lcd_config[lcd_idx].lcd;
+}
+
+const char *bd_get_lcd_name(void)
+{
+	return bd_lcd_config[lcd_idx].name;
+}
+
+enum lcd_format bd_get_lcd_format(void)
+{
+	return bd_lcd_config[lcd_idx].fmt;
+}
+
+int bd_get_lcd_density(void)
+{
+	return bd_lcd_config[lcd_idx].dpi;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *lcd)
+{
+	return 0;
+}
+#endif
diff --git a/board/friendlyarm/nanopi2/nxp-fb.h b/board/friendlyarm/nanopi2/nxp-fb.h
new file mode 100644
index 0000000..d31a03d
--- /dev/null
+++ b/board/friendlyarm/nanopi2/nxp-fb.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (c) 2017 FriendlyARM (www.arm9.net)
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com/
+ *
+ * Header file for NXP Display Driver
+ */
+
+#ifndef __MACH_NXP_FB_H__
+#define __MACH_NXP_FB_H__
+
+/*
+ * struct nxp_lcd_polarity
+ * @rise_vclk:	if 1, video data is fetched at rising edge
+ * @inv_hsync:	if HSYNC polarity is inversed
+ * @inv_vsync:	if VSYNC polarity is inversed
+ * @inv_vden:	if VDEN polarity is inversed
+ */
+struct nxp_lcd_polarity {
+	int	rise_vclk;
+	int	inv_hsync;
+	int	inv_vsync;
+	int	inv_vden;
+};
+
+/*
+ * struct nxp_lcd_timing
+ * @h_fp:	horizontal front porch
+ * @h_bp:	horizontal back porch
+ * @h_sw:	horizontal sync width
+ * @v_fp:	vertical front porch
+ * @v_fpe:	vertical front porch for even field
+ * @v_bp:	vertical back porch
+ * @v_bpe:	vertical back porch for even field
+ */
+struct nxp_lcd_timing {
+	int	h_fp;
+	int	h_bp;
+	int	h_sw;
+	int	v_fp;
+	int	v_fpe;
+	int	v_bp;
+	int	v_bpe;
+	int	v_sw;
+};
+
+/*
+ * struct nxp_lcd
+ * @width:		horizontal resolution
+ * @height:		vertical resolution
+ * @p_width:	width of lcd in mm
+ * @p_height:	height of lcd in mm
+ * @bpp:		bits per pixel
+ * @freq:		vframe frequency
+ * @timing:		timing values
+ * @polarity:	polarity settings
+ * @gpio_init:	pointer to GPIO init function
+ *
+ */
+struct nxp_lcd {
+	int	width;
+	int	height;
+	int	p_width;
+	int	p_height;
+	int	bpp;
+	int	freq;
+	struct	nxp_lcd_timing timing;
+	struct	nxp_lcd_polarity polarity;
+	void	(*gpio_init)(void);
+};
+
+/**
+ * Public interfaces
+ */
+enum lcd_format {
+	LCD_VESA	= 0,
+	LCD_JEIDA	= 1,
+	LCD_LOC		= 2,
+
+	LCD_RGB		= 4,
+	LCD_HDMI	= 5,
+};
+
+extern int bd_setup_lcd_by_id(int id);
+extern int bd_setup_lcd_by_name(char *name);
+extern struct nxp_lcd *bd_get_lcd(void);
+extern const char *bd_get_lcd_name(void);
+extern int bd_get_lcd_density(void);
+extern enum lcd_format bd_get_lcd_format(void);
+extern int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *cfg);
+
+#endif /* __MACH_NXP_FB_H__ */
diff --git a/board/friendlyarm/nanopi2/onewire.c b/board/friendlyarm/nanopi2/onewire.c
new file mode 100644
index 0000000..994befb
--- /dev/null
+++ b/board/friendlyarm/nanopi2/onewire.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include <i2c.h>
+#include <pwm.h>
+
+#include <irq_func.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC	1000000000L
+#endif
+
+#define SAMPLE_BPS		9600
+#define SAMPLE_IN_US	101		/* (1000000 / BPS) */
+
+#define REQ_INFO		0x60U
+#define REQ_BL			0x80U
+
+#define BUS_I2C			0x18
+#define ONEWIRE_I2C_BUS		2
+#define ONEWIRE_I2C_ADDR	0x2f
+
+static int bus_type = -1;
+static int lcd_id = -1;
+static unsigned short lcd_fwrev;
+static int current_brightness = -1;
+#ifdef CONFIG_DM_I2C
+static struct udevice *i2c_dev;
+#endif
+
+/* debug */
+#if (0)
+#define DBGOUT(msg...)	do { printf("onewire: " msg); } while (0)
+#else
+#define DBGOUT(msg...)	do {} while (0)
+#endif
+
+/* based on web page from http://lfh1986.blogspot.com */
+static unsigned char crc8_ow(unsigned int v, unsigned int len)
+{
+	unsigned char crc = 0xACU;
+
+	while (len--) {
+		if ((crc & 0x80U) != 0) {
+			crc <<= 1;
+			crc ^= 0x7U;
+		} else {
+			crc <<= 1;
+		}
+		if ((v & (1U << 31)) != 0)
+			crc ^= 0x7U;
+		v <<= 1;
+	}
+	return crc;
+}
+
+/* GPIO helpers */
+#define __IO_GRP		2	/* GPIOC15 */
+#define __IO_IDX		15
+
+static inline void set_pin_as_input(void)
+{
+	nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 0);
+}
+
+static inline void set_pin_as_output(void)
+{
+	nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 1);
+}
+
+static inline void set_pin_value(int v)
+{
+	nx_gpio_set_output_value(__IO_GRP, __IO_IDX, !!v);
+}
+
+static inline int get_pin_value(void)
+{
+	return nx_gpio_get_input_value(__IO_GRP, __IO_IDX);
+}
+
+/* Timer helpers */
+#define PWM_CH				3
+#define PWM_TCON			(PHY_BASEADDR_PWM + 0x08)
+#define PWM_TCON_START		(1 << 16)
+#define PWM_TINT_CSTAT		(PHY_BASEADDR_PWM + 0x44)
+
+static int onewire_init_timer(void)
+{
+	int period_ns = NSEC_PER_SEC / SAMPLE_BPS;
+
+	/* range: 1080~1970 */
+	period_ns -= 1525;
+
+	return pwm_config(PWM_CH, period_ns >> 1, period_ns);
+}
+
+static void wait_one_tick(void)
+{
+	unsigned int tcon;
+
+	tcon = readl(PWM_TCON);
+	tcon |= PWM_TCON_START;
+	writel(tcon, PWM_TCON);
+
+	while (1) {
+		if (readl(PWM_TINT_CSTAT) & (1 << (5 + PWM_CH)))
+			break;
+	}
+
+	writel((1 << (5 + PWM_CH)), PWM_TINT_CSTAT);
+
+	tcon &= ~PWM_TCON_START;
+	writel(tcon, PWM_TCON);
+}
+
+/* Session handler */
+static int onewire_session(unsigned char req, unsigned char res[])
+{
+	unsigned int Req;
+	unsigned int *Res;
+	int ints = disable_interrupts();
+	int i;
+	int ret;
+
+	Req = (req << 24) | (crc8_ow(req << 24, 8) << 16);
+	Res = (unsigned int *)res;
+
+	set_pin_value(1);
+	set_pin_as_output();
+	for (i = 0; i < 60; i++)
+		wait_one_tick();
+
+	set_pin_value(0);
+	for (i = 0; i < 2; i++)
+		wait_one_tick();
+
+	for (i = 0; i < 16; i++) {
+		int v = !!(Req & (1U << 31));
+
+		Req <<= 1;
+		set_pin_value(v);
+		wait_one_tick();
+	}
+
+	wait_one_tick();
+	set_pin_as_input();
+	wait_one_tick();
+	for (i = 0; i < 32; i++) {
+		(*Res) <<= 1;
+		(*Res) |= get_pin_value();
+		wait_one_tick();
+	}
+	set_pin_value(1);
+	set_pin_as_output();
+
+	if (ints)
+		enable_interrupts();
+
+	ret = crc8_ow(*Res, 24) == res[0];
+	DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %d\n",
+	       req, res[3], res[2], res[1], res[0], ret);
+
+	return ret;
+}
+
+static int onewire_i2c_do_request(unsigned char req, unsigned char *buf)
+{
+	unsigned char tx[4];
+	int ret;
+
+	tx[0] = req;
+	tx[1] = crc8_ow(req << 24, 8);
+
+#ifdef CONFIG_DM_I2C
+	if (dm_i2c_write(i2c_dev, 0, tx, 2))
+		return -EIO;
+
+	if (!buf)
+		return 0;
+
+	if (dm_i2c_read(i2c_dev, 0, buf, 4))
+		return -EIO;
+#else
+	if (i2c_write(ONEWIRE_I2C_ADDR, 0, 0, tx, 2))
+		return -EIO;
+
+	if (!buf) /* NO READ */
+		return 0;
+
+	if (i2c_read(ONEWIRE_I2C_ADDR, 0, 0, buf, 4))
+		return -EIO;
+#endif
+
+	ret = crc8_ow((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8), 24);
+	DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %02x\n",
+	       req, buf[0], buf[1], buf[2], buf[3], ret);
+
+	return (ret == buf[3]) ? 0 : -EIO;
+}
+
+static void onewire_i2c_init(void)
+{
+	unsigned char buf[4];
+	int ret;
+
+#ifdef CONFIG_DM_I2C
+	ret = i2c_get_chip_for_busnum(ONEWIRE_I2C_BUS,
+				      ONEWIRE_I2C_ADDR, 0, &i2c_dev);
+#else
+	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+	i2c_set_bus_num(ONEWIRE_I2C_BUS);
+
+	ret = i2c_probe(ONEWIRE_I2C_ADDR);
+#endif
+	if (ret)
+		return;
+
+	ret = onewire_i2c_do_request(REQ_INFO, buf);
+	if (!ret) {
+		lcd_id = buf[0];
+		lcd_fwrev = buf[1] * 0x100 + buf[2];
+		bus_type = BUS_I2C;
+	}
+}
+
+void onewire_init(void)
+{
+	/* GPIO, Pull-off */
+	nx_gpio_set_pad_function(__IO_GRP, __IO_IDX, 1);
+	nx_gpio_set_pull_mode(__IO_GRP, __IO_IDX, 2);
+
+	onewire_init_timer();
+	onewire_i2c_init();
+}
+
+int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver)
+{
+	unsigned char res[4];
+	int i;
+
+	if (bus_type == BUS_I2C && lcd_id > 0) {
+		*lcd = lcd_id;
+		*fw_ver = lcd_fwrev;
+		return 0;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (onewire_session(REQ_INFO, res)) {
+			*lcd = res[3];
+			*fw_ver = res[2] * 0x100 + res[1];
+			lcd_id = *lcd;
+			DBGOUT("lcd = %d, fw_ver = %x\n", *lcd, *fw_ver);
+			return 0;
+		}
+	}
+
+	/* LCD unknown or not connected */
+	*lcd = 0;
+	*fw_ver = -1;
+
+	return -1;
+}
+
+int onewire_get_lcd_id(void)
+{
+	return lcd_id;
+}
+
+int onewire_set_backlight(int brightness)
+{
+	unsigned char res[4];
+	int i;
+
+	if (brightness == current_brightness)
+		return 0;
+
+	if (brightness > 127)
+		brightness = 127;
+	else if (brightness < 0)
+		brightness = 0;
+
+	if (bus_type == BUS_I2C) {
+		onewire_i2c_do_request((REQ_BL | brightness), NULL);
+		current_brightness = brightness;
+		return 0;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (onewire_session((REQ_BL | brightness), res)) {
+			current_brightness = brightness;
+			return 0;
+		}
+	}
+
+	return -1;
+}
diff --git a/board/friendlyarm/nanopi2/onewire.h b/board/friendlyarm/nanopi2/onewire.h
new file mode 100644
index 0000000..9f6d7cf
--- /dev/null
+++ b/board/friendlyarm/nanopi2/onewire.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#ifndef __ONE_WIRE_H__
+#define __ONE_WIRE_H__
+
+extern void onewire_init(void);
+extern int  onewire_get_info(unsigned char *lcd, unsigned short *fw_ver);
+extern int  onewire_get_lcd_id(void);
+extern int  onewire_set_backlight(int brightness);
+
+#endif /* __ONE_WIRE_H__ */