Linux Framebuffer fbtft with SPI DMA for Intel Edison

When you want to connect a display to the Intel Edison module, you should utilise the existing Linux infrastructure and use a kernel framebuffer driver instead of writing your own screen functions based on Arduino libraries.

The schematics and more technical information about my display and board are in my previous post: Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322.

At the time of writing the article, the Linux kernel SPI support was broken in the original Intel kernel. So, I used Primiano’s kernel that fixed the SPI support. It works perfectly for me, it may not contain the latest Intel’s modifications. Use it on your own risk.

First, I downloaded the Intel sources from I extracted them into ~/edison in my home directory and followed the instructions for compiling it.

cd ~/edison/poky/
source oe-init-build-env ../build_edison/
bitbake edison-image u-boot
../poky/meta-intel-edison/utils/flash/ .

Then I replaced the kernel in ~/edison/poky/linux-kernel by my own, but I kept the directories .git and .meta intact.
I increased the revision number in the variable PR = “r3″ in the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/
I added my new configuration parameters for fbtft into the file ~/edison/poky/linux-kernel/arch/x86/configs/i386_edison_defconfig

I downloaded the fbtft sources. I created the folder ~/edison/poky/linux-kernel/drivers/video/fbtft and extracted the fbtft sources into the folder.

I added the line in ~/edison/poky/linux-kernel/drivers/video/Kconfig before the line “endmenu”.

source "drivers/video/fbtft/Kconfig"

I added the line in ~/edison/poky/linux-kernel/drivers/video/Makefile

obj-$(CONFIG_FB_TFT)    += fbtft/

Then I edited the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft_device.c

I added the header

#include <linux/spi/intel_mid_ssp_spi.h>

Then I added the description of my display into the list of displays.
Where “reset” uses the number 49 of the GP49 pin, “dc” is GP15.
The pin “led” is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c

It seems that the chip SSD1322 supports speeds only up to 12.5MHz, but the Edison must be able to support speeds up to 25MHz.

   .name = "er_oled028",
   .spi = &(struct spi_board_info) {
       .modalias = "fb_ssd1322",
       .max_speed_hz = 12500000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = true,
        .platform_data = &(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           .gpios = (const struct fbtft_gpio []) {
               { "reset", 49 },
               { "dc", 15},
               { "led", 14},

I modified the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft-core.c

--- fbtft-core.c	2015-03-05 02:54:01.000000000 -0800
+++	2016-05-30 19:19:21.000000000 -0700
@@ -863,7 +863,7 @@
 	if (txbuflen > 0) {
 		if (dma) {
 			dev->coherent_dma_mask = ~0;
-			txbuf = dmam_alloc_coherent(dev, txbuflen, &par->txbuf.dma, GFP_DMA);
+			txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_DMA | GFP_ATOMIC);
 		} else {
 			txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL);

I added the lines to the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/conf/machine/edison.conf to autoload the fbtft module.
Where busnum=5 is the SPI bus number on the Edison.,
name=er_oled028 defines my oled display from fbtft_device.c,
debug=7 shows more debugging information. It’s optional.

KERNEL_MODULE_AUTOLOAD += "fbtft_device"
module_conf_fbtft_device = "options fbtft_device name=er_oled028 busnum=5 debug=7"

Then I recompiled the kernel and flashed my Edison.

I compiled mplayer on the Edison and played a sample video. I couldn’t find a video with the same dimensions as my display, but it should work in the full-screen mode too.

/usr/local/bin/mplayer -vo fbdev ultra.mp4

Linux kernel for Edison with SPI DMA fixes and fbtft framebuffer

Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322

Update: the version with working SPI DMA

The Intel Edison doesn’t have a video interface; so, you connect an OLED or TFT LCD display via the SPI interface using the Linux kernel video module framebuffer.

I used fbtft – Linux Framebuffer drivers for small TFT LCD display modules by Noralf Tronnes.

My display is 2.8″ OLED Display 256×64 Graphic Module SSD1322.

After reading the documentation for the display, I connected it to the Edison via the 4-wire SPI interface, which requires an additional GPIO pin for the D/C signal (Write Data/Write Command). The 3-wire SPI interface uses the 9 bit SPI interface and should be avoided.

I compared the timing diagrams for the SSD1322 chip with the SPI Transfer Modes and found that it uses the SPI Mode 3.


Here is the schematic of my SSD1322 shield for the Intel Edison.

SSD1322 Shield for Intel Edison
The SSD1322 Display Shield for the Intel Edison
Connections between the Edison and the Display:

GP49 -> RES(Reset Signal Input)
GP15 -> D/C(Data/Command Control)
GP110_SPI_2_FS0(CS0) -> CS(Chip Select Input)
GP109_SPI_2_CLK -> D0/SCLK(Serial Clock)
GP115_SPI_2_TXD -> D1/SDIN(Serial Data Input)
I also connected GP14 to the SHDN pin of the +12V voltage regulator.

fbtft download and configuration

The latest code from the notro/fbtft repository didn’t work for me.
I used the older code presslab-us/fbtft, which also included support for my chip SSD1322.

I prepared the Edison directory with the Linux sources as described in my article Building Yocto Linux for Intel Edison.

After I compiled the Edison image for the first time, I copied these two hidden directories into another folder (I placed the Edison sources into the “edison” directory in my home directory); so, I can return the sources to the original state by copying these hidden directories back at any moment.


I created the directory “fbtft” and copied all fbtft files there.

mkdir ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft

I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Kconfig before the line “endmenu”.

source "drivers/video/fbtft/Kconfig"

I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Makefile

obj-$(CONFIG_FB_TFT)    += fbtft/

Then I edited the file ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft/fbtft_device.c

I added the header

#include <linux/spi/intel_mid_ssp_spi.h>

Then I added the description of my display into the list of displays.
Where “reset” uses the number 49 of the GP49 pin, “dc” is GP15.
The pin “led” is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c

DMA has to be disabled because the DMA code in intel_mid_ssp_spi.h is broken.
If you enable it, you can see errors like this: “intel_mid_ssp_spi_unified 0000:00:07.1: ERROR : DMA buffers already mapped”

   .name = "er_oled028",
   .spi = &(struct spi_board_info) {
       .modalias = "fb_ssd1322",
       .max_speed_hz = 12000000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = false,
        .platform_data = &(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           .gpios = (const struct fbtft_gpio []) {
               { "reset", 49 },
               { "dc", 15},
               { "led", 14},

I wrote my custom initialization code for the display ER_OLED028 in fb_ssd1322.c following the documentation for my display.

static int init_display(struct fbtft_par *par)
   fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

   //reset the chip
   fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
   gpio_set_value(par->gpio.reset, 0);
   gpio_set_value(par->gpio.reset, 1);

   write_reg(par, 0xfd, 0x12); /* Unlock OLED driver IC */
   write_reg(par, 0xae); /* Display Off */
   write_reg(par, 0xb3, 0x91); /* Display divide clockratio/frequency */
   write_reg(par, 0xca, 0x3f); /* Multiplex ratio, 1/64, 64 COMS enabled */
   write_reg(par, 0xa2, 0x00); /* Set offset, the display map starting line is COM0 */
   write_reg(par, 0xa1, 0x00); /* Set start line position */
   write_reg(par, 0xa0, 0x14, 0x11); /* Set remap, horiz address increment, disable colum address remap, */
				                      /*  enable nibble remap, scan from com[N-1] to COM0, disable COM split odd even */
   write_reg(par, 0xb5, 0x00); /* Set GPIO */
   write_reg(par, 0xab, 0x01); /* Select internal VDD */
   write_reg(par, 0xb4, 0xa0, 0xfd); /* Display enhancement A, external VSL, enhanced low GS display quality */
   write_reg(par, 0xc1, 0xff); /* Contrast current, 256 steps, default is 0x7F */
   write_reg(par, 0xc7, 0x0f); /* Master contrast current, 16 steps, default is 0x0F */
   write_reg(par, 0xb1, 0xe2); /* Phase Length */
   write_reg(par, 0xd1, 0x82, 0x20); /* Display enhancement B */
   write_reg(par, 0xbb, 0x1f); /* Pre-charge voltage */
   write_reg(par, 0xb6, 0x08); /* Set Second Pre-Charge Period */
   write_reg(par, 0xbe, 0x07); /* Set VCOMH */
   write_reg(par, 0xa6); /* Normal display */

   //enable VCC
   gpio_set_value(par->gpio.led[0], 1);
   write_reg(par, 0xaf); /* Display ON */

   return 0;

After all modifications, I created a patch file.
While committing to git, I added the comment line “fbtft_ssd1322″.

cd ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video
git add .
git commit
git format-patch -1

It created the patch file “0001-fbtft_ssd1322.patch”. I renamed it to “fbtft_ssd1322.patch” and copied it to:


I created the kernel configuration file fbtft.cfg in the same directory “recipes-kernel/linux/files/”:

# CONFIG_FB_TFT_GU39XX is not set
# CONFIG_FB_TFT_HX8340BN is not set
# CONFIG_FB_TFT_HX8347D is not set
# CONFIG_FB_TFT_ILI9320 is not set
# CONFIG_FB_TFT_ILI9325 is not set
# CONFIG_FB_TFT_ILI9341 is not set
# CONFIG_FB_TFT_PCD8544 is not set
# CONFIG_FB_TFT_SSD1289 is not set
# CONFIG_FB_TFT_SSD1351 is not set
# CONFIG_FB_TFT_ST7735R is not set
# CONFIG_FB_FLEX is not set

I commented the line in the file board.c for the device ads7955 that occupies the same SPI busnum=5, cs=0 that we use for the display.
Then I ran the same git sequence to generate the patch file “platform_ssd1322.patch” and copied it into the same “recipes-kernel/linux/files” directory.

From c83f14b37bc2dcfa97b36214f02b15ddaad51a47 Mon Sep 17 00:00:00 2001
From: Farit <>
Date: Sat, 9 Apr 2016 22:02:19 -0700
Subject: [PATCH] platform_ssd1322

 arch/x86/platform/intel-mid/board.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/platform/intel-mid/board.c b/arch/x86/platform/intel-mid/board.c
index eb3d8a4..fca6440 100644
--- a/arch/x86/platform/intel-mid/board.c
+++ b/arch/x86/platform/intel-mid/board.c
@@ -111,7 +111,7 @@ struct devs_id __initconst device_ids[] = {
 	/* SPI devices */
 	{"spidev", SFI_DEV_TYPE_SPI, 0, &spidev_platform_data, NULL},
-	{"ads7955", SFI_DEV_TYPE_SPI, 0, &ads7955_platform_data, NULL},
+//	{"ads7955", SFI_DEV_TYPE_SPI, 0, &ads7955_platform_data, NULL},
 	{"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data, NULL},
 	{"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data, NULL},
 	{"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data,

When all patch and configuration files were ready, I added links to them into the file “~/edison/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/linux-yocto_3.10.bbappend”.

SRC_URI += "file://defconfig"
SRC_URI += "file://upstream_to_edison.patch"

SRC_URI += "file://fbtft.cfg"
SRC_URI += "file://fbtft_ssd1322.patch"
SRC_URI += "file://platform_ssd1322.patch"
fbtft Module Compilation

Then I recompiled the Linux image and installed it.

cd ~/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image
Loading and Testing the Framebuffer Module

I loaded the fbtft module supplying the name of the display and the SPI bus number (it’s 5 on the Edison).

modprobe fbtft_device name=er_oled028 busnum=5

Sometimes, it’s not loaded. I need to investigate the problems.
When it loads, the “dmesg” command shows:

[   32.254181] fbtft_device:  GPIOS used by 'er_oled028':
[   32.254205] fbtft_device:    'reset' = GPIO49
[   32.254221] fbtft_device:    'dc' = GPIO15
[   32.254234] fbtft_device:    'led' = GPIO14
[   32.254246] fbtft_device:  SPI devices registered:
[   32.254264] fbtft_device:      spidev spi5.1 25000kHz 8 bits mode=0x00
[   32.254281] fbtft_device:      fb_ssd1322 spi5.0 12500kHz 8 bits mode=0x03
[   32.394493] graphics fb0: fb_ssd1322 frame buffer, 256x64, 32 KiB video memory, 8 KiB buffer memory, fps=20, spi5.0 at 12 MHz

Then I can test the module by sending random values to the framebuffer socket “/dev/fb0″.

cat /dev/urandom > /dev/fb0

Here is the result.

Sending Random Data to the Framebuffer
Sending Random Data to the Framebuffer

I can also load the fbcon module to display the console on the display.

modprobe fbcon
Edison Console via the Framebuffer
Edison Console via the Framebuffer

There are patches on the Edison forum that may help to fix the DMA issue. Otherwise, the display will be relatively slow.
Check this link stewart maguire’s patches for DMA and FBTFT support.
And this Intel MID SSP SPI driver getting stuck in kernel space.


Configuration and patch files for fbtft on Intel Edison