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/Kconfig b/board/friendlyarm/Kconfig
new file mode 100644
index 0000000..f8f9cfd
--- /dev/null
+++ b/board/friendlyarm/Kconfig
@@ -0,0 +1,37 @@
+choice
+ prompt "LCD backlight control"
+ optional
+ default S5P4418_ONEWIRE
+
+config S5P4418_ONEWIRE
+ bool "I2C / 1-Wire"
+ help
+ This enables LCD-Backlight control for FriendlyARM LCD-panels.
+ I2C is used if available, otherwise 1-Wire is used.
+
+config PWM_NX
+ bool "PWM"
+ help
+ This enables LCD-Backlight control via PWM.
+endchoice
+
+config ROOT_DEV
+ int "ROOT_DEV"
+ help
+ Environment variable rootdev is set to this value if env. var. firstboot
+ does not exist. Otherwise rootdev is set to the MMC boot device. rootdev
+ determines (together with env. var. bootpart) where the OS (linux) is
+ booted from.
+
+config BOOT_PART
+ int "BOOT_PART"
+ help
+ Environment variable bootpart is set to this value. bootpart determines
+ (together with env. var. rootdev) where the OS (linux) is booted from.
+
+config ROOT_PART
+ int "ROOT_PART"
+ help
+ Environment variable rootpart is set to this value.
+
+source "board/friendlyarm/nanopi2/Kconfig"
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__ */