/* Code Musings */

NanoPi M3 and LVDS

In my last post, I briefly mentioned that I chose the Nano Pi M3 because of it’s size and, more importantly, because it had an on-board LVDS header. In this post, I’ll be detailing how I managed to get this board talking to my Chimei Innolux EJ070NA-01J screen.


If you’re here because you want to use the LVDS header, I’d encourage you to first read the Nano Pi M3 Wiki page, particularly the sections dealing with compiling your own kernel. Yes, you will be hacking up driver. Furthermore, you will need a serial board, such as the PSU-ONECOME as mentioned in the wiki, or a USB-to-Serial board (ie. FT232RL), like the one I have.

Hardware Setup

In between the NanoPi and the screen sits a PCB800182 signal converter board. Plug the parts together, including the serial board as shown below. Connect GND, Rx, and Tx from the NanoPi to GND, Tx and Rx pins on the serial board respectively. Do NOT connect the 5V line because you will be powering the NanoPi via USB cable.

You will need two USB cables connected to your computer; one for the serial board and one for the NanoPi. Since the screen is drawing power through the NanoPi, be aware that quite a bit of power will be drawn from your USB port. A standard USB port outputs ~500-900 mA, which may not enough. On some laptops, there is a USB charging port which can crank out up to 1,500 mA of power. If you have this port, use it. Otherwise, the NanoPi can shut down randomly if it’s not getting enough power. Alternatively, if you’re handy with a soldering iron, the PCB800182 can supply power to the screen and itself; it requires a bit of manual wiring. Another option would be to use a powered USB hub.

On your PC, boot into your favourite Ubuntu-based distro. You will require the following:

Minicom is a serial terminal emulator that allows us to talk to the board via UART. Once you have the serial board plugged in, issue the following to start listening:

Flashing the SD Card

The NanoPi wiki contains instructions on how to flash your SD card with a preloaded linux or android distribution. For the purpose of this post, I’ll be using the Ubuntu Core QtE image. To flash the image into your card, issue the following (substitute file name and SD Card path):

Once that completes, throw the card into the NanoPi.

The Bootloader

When the NanoPi is booted up, the first thing that runs is the U-Boot bootloader. Minicom should spring to life and start displaying rather meaningless information to the screen.

Before it continues booting the Linux kernel, it will ask to interrupt the autoboot sequence with a key stroke. Doing so will result in a user prompt.

If the LCD screen is connected to the LVDS header on the NanoPi, you may see a logo displayed. Or, at the very least, the screen being powered. It’s expected that the screen won’t work properly at this point.

You may continue booting by issuing a boot command into the prompt.

Modifying U-Boot

The U-Boot source code can be obtained using the steps detailed on the NanoPi wiki. If everything is set up correctly, you should be able to compile the source and generate U-Boot binary.

The bootloader detects the video output device by probing both HDMI and the RGB port on the board. Then, it initialises the display and displays a FriendlyArm image before passing control to the kernel. Notice that I did not mention anything about detecting a display device connected to the LVDS header. The reason being, is that the display detection code lacks any notion of LVDS output; the header seems to be powered regardless. In it’s default configuration, it will display anything in the frame buffer regardless of where the video signal is destined for.

Side Note on U-Boot Initialisation

The U-Boot code initialises the board by calling into board_early_init_f() first, then board_init() in board/s5p6818/nanopi3/board.c. The former function initialises the onewire library and probes the RGB port. The latter function calls into bd_lcd_init(), where the result of the probe is used to initialise display info structures. And within this process is where the aforementioned problem lies. If the onewire probe did not find an LCD screen, the code defaults to using HDMI.

Since the code lacks any way of probing the LVDS header, the only avenue left is to hard-code the screen we’re using. For the sake of my project, this is not that big of an issue.

LVDS Initialisation Code

The ultimate goal is to initialise a struct nxp_lcd with the correct timing values so that the screen can function properly. The easiest way is to use the framework developed by the FriedlyArm folks located in board/s5p6818/nanopi3. It’s not the prettiest code, mind you, but it does the job. The contents of display.c contain the code to initialise the video parameters with the correct display timings; we will start there. Add the following function:

And modify the bd_display() function as follows:

LCD Timings

To use an LCD screen, the driver needs to know specific information about the screen; mainly timing values and resolution. Those values are published inside datasheets that can be downloaded from a manufacturer’s website. Unfortunately for me, I had to do a bit more digging to find the timing values for the EJ070NA-01J because not all values were published in their PDF. Luckily, I found someone that had the same screen. Moving on, the screen timings need to be added into the lcds.c file as follows:

Then add the structure into the LCD config list using the LVDS name:

Enabling the Screen

Finally, the only step remaining is to enable the screen’s usage. As mentioned in the U-Boot side note, there is no way to detect the LVDS screen using the framework provided. So, in bd_lcd_init(), the only thing left is to hard-code the screen’s usage:

Then build the bootloader.

Flashing the Bootloader

To flash the newly created U-Boot binary, interrupt the autoboot process on the NanoPi and enter the bootloader into fastboot mode. Then, flash the bootloader and reboot.

When you boot up, you should be greeted with a perfectly rendered boot logo!

Kernel Hacking

For this step, you will need to pull down the Linux kernel source code. The steps are detailed in the NanoPi wiki.

Once the bootloader finishes doing it’s work, it launches the Linux kernel and passes in the name of the LCD screen as a command line parameter. In our case, the name of the display will be LVDS, as defined above. During boot, that parameter is looked up in the nanopi3_lcd_config list, which is similar to the list from the U-Boot code, and the timings are subsequently used.

Add the following to arch/arm/plat-s5p6818/nanopi3/lcds.c:

Compiling the Kernel

The wiki documents the steps needed to compile the kernel. In the menuconfig step, enable the LVDS option buried deep within the menu:

Finally, make the kernel and copy the generated uImage onto the boot partition of the SD card.

Once you boot the NanoPi up, the kernel boot text should be rendered perfectly on the screen.

The QtE-Demo should load up shortly after boot.

4 Responses to “NanoPi M3 and LVDS”

  • Happy User

    That is an impressive tutorial. Wow!

    I came across this while dealing with a similar mess with a Banana Pi-M2. I got that one for the exact same reason – the LVDS connector. What I am trying to do is hook up an automotive grade display. These are by far better than anything you can find on the consumer market. You can get one of these from eBay as a spare part for cars obviously.

    Should have done my research before ordering the board. After reading this I am starting to think I should order one of the NanoPi-M3 boards and use this. It will definitely be worth the time saved.

    Any advice? Should I invest into a NanoPi M3 or you’re thinking some other SBC?

    • Mark

      The M3 is a decent board, I would recommend it. The downside is that you’re stuck with Linux 3.x, because Samsung (or Nexell) has not ported their drivers to a newer linux kernel. Another recommendation would be to power the screen independently of the LVDS connector; because it can draw quite a bit of juice.

  • Happy User

    Thanks for the heads-up.

    What I am dealing with now is virtually the same. I have to choose between tweaking a working solution with Linux 3.x kernel that uses FEX files for configuring *or* be the first one to push through a LVDS device tree configuration for a board with the now deprecated A31s SOC.

    So, for now I’ll hold off purchasing a NanoPi M3 until your 4th post “NanoPi M3 and LVDS with Linux Kernel 4.4” is out 😉

    Keep up the great work. It is definitely professional grade.

  • Leave a Reply