Using the LCDs
français | english
This wiki
This page

This tutorial aims at getting you ready to use the LCDs of the extension board in an embedded Linux systems. It assumes that you already have a working embedded Linux configuration and that you have access to the Altera toolchain.

Warning: at the moment, only the following displays are supported:
    * The VGA port,
    * The ER TFT043, and
    * The ER TFT035.


As mentionned in the introduction, you must have a working Linux system on an SD card. You do not need to have built everything from scratch but you need to be able to recompile the kernel sources. Typically, your SD card should at least consist of the following partitions:

  • A boot partition that contains the kernel image and the compiled device tree;
  • A partition containing the root filesystem, i.e. the filesystem automatically mounted by the kernel that contains the root directory “/”; and
  • A raw preloader partition which contains the initialization code that is executed at boot time.

Hardware overview

The hardware is divided into two components. The frame manager is responsible for accessing the frame buffer in memory. The LCD interface (at the moment, only a VGA interface called VGA sequencer is available) is responsible for transforming the stream of pixels it receive from the frame manager in the correct timing for the LCD. You are responsible for generating the correct VIDEO_CLK depending on your LCD with a PLL from Altera.

The Qsys components can be downloaded here.

Enabling framebuffer support in Linux

As the provided driver is a framebuffer driver, our first goal is to enable the framebuffer support in your kernel. To do this, you must recompile your kernel. Don’t worry: if you have already done it once, it’s going to be fast. For all the following terminal manipulations, be sure to be in a terminal where the global variable ARCH and CROSS_COMPILE have been set to arm and to the prefix of your cross-compiling toolchain (e.g. arm-linux-gnueabihf- if you are using the Embedded command shell from Altera), respectively.

In your terminal, cd wherever your kernel sources are. Then, type the following command:

$ make menuconfig

If you get an error, it might be that you don't have ncurses installed. In (K)Ubuntu, you can use the following command to install it:

sudo apt-get install libncurses-dev

The make menuconfig command should open a text-based user interface that lets you configure the kernel build. Then, use the following path:

Device drivers -> Graphics support -> Frame buffer devices

You should now be able to use the Y key to enable the options as follows:

(You might need to enable the very first to see the other ones appear.)

Then, press the ESC key until you are asked whether you want to save before quitting. Choose to save. Then, it is time to recompile the kernel with the following command:

$ make zImage

Your new kernel image can be found in <linux-source-path>/arch/arm/boot/zImage. You need to copy it on the boot partition of your SD card.

Adapting and compiling the device tree

Drivers are board-agnostic. Otherwise, they would need to be rewritten from scratch for each board. Therefore, a compiled structure called the Device Tree is loaded in kernel’s memory by the bootloader. The kernel makes this structure available to its drivers so that they can read their configuration parameters from it. In such a framework, a new board “only” requires a device tree to be written.

One of the first task performed by our framebuffer driver is to read the device tree to determine the configuration of the board and your Qsys design. Some parameters are compulsory, some aren’t. For the driver to work, the following parameters must be defined:

  • The compatible string[1] set to “prsoc,display”;
  • The address of the frame manager Avalon-MM slave port as viewed from the HPS;
  • The width and height of the screen and the buffer;
  • The address of the LCD interface Avalon-MM slave port as viewed from the HPS; and
  • The IRQ number of the frame manager (more on this in a minute).

The non-compulsory parameters is the prsoc,reg-init parameter. It defines the initialization sequence of the registers in a key-value fashion where the key is the offset and the value is what should be written to it. The offsets are computed from the address of the LCD interface Avalon‑MM slave port specified above. If this parameter isn’t provided, the LCD interface is left as is.

Differentiating the dimensions of the screen and the one of the buffer can be used to perform ping-pong buffering (sometimes referred to as double buffering or triple buffering depending on the number of buffers). This means that your application can mmap a larger buffer than the screen and can draws in non-visible portion of the screen. For those of you that are already familiar with the userland API exposed by framebuffer, the application can then use a code resembling the following to swap buffers:

// var_info is the fb_var_screeninfo structure
var_info.yoffset = i * var_info.yres;
int ret = ioctl(fb_fd, FBIOPAN_DISPLAY, &var_info);
assert(ret >= 0);

You can find a full dummy example of working ping-pong buffering here.

Here is a full example device tree that configures the ER TFT043:

#include "socfpga_cyclone5_de0_sockit.dts"
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>

#define VGA_SEQUENCER_REG_CSR   0x00
#define VGA_SEQUENCER_REG_HBP   0x04
#define VGA_SEQUENCER_REG_HFP   0x08
#define VGA_SEQUENCER_REG_VBP   0x0c
#define VGA_SEQUENCER_REG_VFP   0x10

/ {
  soc {
      display {
              compatible = "prsoc,display";
              reg = <0xff200080 0x40   /* Frame manager <address span> */
                     0xff200000 0x80>; /* VGA sequencer  <address span> */
              interrupts = <GIC_SPI 40 IRQ_TYPE_EDGE_RISING>;
              prsoc,screen-width  = <480>;
              prsoc,screen-height = <272>;
              prsoc,buffer-width  = <480>;
              prsoc,buffer-height = <544>; // -> 2 buffers
              prsoc,reg-init = <VGA_SEQUENCER_REG_VSYNC 10>,
                               <VGA_SEQUENCER_REG_VBP 2>,
                               <VGA_SEQUENCER_REG_VDATA 272>,
                               <VGA_SEQUENCER_REG_VFP 3>,
                               <VGA_SEQUENCER_REG_HSYNC 41>,
                               <VGA_SEQUENCER_REG_HBP 47>,
                               <VGA_SEQUENCER_REG_HDATA 480>,
                               <VGA_SEQUENCER_REG_HFP 8>,
                               <VGA_SEQUENCER_REG_CSR 1>;

In this example, the IRQ line of the frame manager is wired to FPGA_IRQ0 [1], the first IRQ slot for FPGA-to-HPS interrupts. This corresponds to the GIC interrupt number 72 which is the 40th (= 72-32) shared peripheral interrupt (SPI). The latter is the rationale behind the 40 appearing in the interrupts property. For more information, see or

You need to put your DTS file, say mydts.dts, in the <linux-source>/arch/arm/boot/dts folder, then type the command:

$ make mydts.dtb

This will compile the DTS file and you can put the resulting DTB file on your SD card’s boot partition. The DTB file can be found in the <linux-source>/arch/arm/boot/dts folder and is named mydts.dtb without surprise.

Compiling and installing the framebuffer

The framebuffer sources can be downloaded here.

In your terminal, cd wherever the framebuffer sources are on your machine. Then, edit the Makefile by setting the KERNEL_PATH variable to wherever your kernel sources are on your machine. Then, issue a make command. The binary you need to put on your root filesystem partition is the one ending with the “ko” extension.

You can then load it on the target after boot using:

$ insmod <name-of-the-driver>.ko

If the device tree is correct, the driver should properly load. Otherwise, a meaningful error message should indicate what's missing.

[1] The compatible string is the driver identifier of a device tree node, i.e. a driver specifies the compatible strings for which it must be loaded. Therefore, if you do not specify it, our framebuffer driver won’t be loaded.