Skip to content
Noralf Trønnes edited this page Jan 23, 2024 · 28 revisions

Now that fbtft_device is gone Device Tree overlays is the way to use fbtft displays.

There is an fbtft overlay that covers most of the functionality that fbtft_device had. See the fbtft section in the overlays/README for information.

One thing that the fbtft overlay does not support is overriding the driver initialization sequence using the DT property init. So to override the controller config one needs to create an overlay.

Many of the fbtft drivers are for MIPI DBI compatible controllers (uses MIPI_DCS_SET_COLUMN_ADDRESS) and are interchangeable between such controllers. It's the init sequence that makes it work with a particular display (you might want to consider panel-mipi-dbi for this use case).

There are 2 problems that can arise when overriding init, they are the 2 functions that runs after init() and change the config if they are present in the driver: set_var() and set_gamma().

Looking at the fbtft MIPI DBI drivers, all of them have set_var(), so there's no avoiding that function call. Some drivers ignore illegal values, so using such a value makes set_var() a no-op (scanning through the fbdev subsystem it looks like using an illegal value should have no ill consequences, at least fbcon and Xorg are working).

This example is for a Watterott RPi-Display which has an ILI9341 controller and is supported by the fb_ili9341 driver. The fb_s6d02a1 driver is used because it is MIPI DBI compatible like fb_ili9341, it ignores illegal rotate values in set_var() and doesn't have set_gamma(). This means that the config provided by the init property will not be altered.

fb_ili9341.c:init_display() is converted to the init property format:

  • The MIPI DCS command value is OR'ed with 0x01000000 to mark it as such. The data values are not changed.
  • Milli second delays in the init sequence are OR'ed with 0x02000000 to mark it as a delay.
/dts-v1/;
/plugin/;

/ {
	compatible = "brcm,bcm2835";

	// Enable the SPI controller
	fragment@0 {
		target = <&spi0>;
		__overlay__ {
			status = "okay";
		};
	};

	// Enabling the SPI controller also enables spidev on cs0, so we have disable it
	fragment@1 {
		target = <&spidev0>;
		__overlay__ {
			status = "disabled";
		};
	};

	fragment@2 {
		target = <&spi0>;
		__overlay__ {
			/* needed to avoid dtc warning */
			#address-cells = <1>;
			#size-cells = <0>;

			display: display@0{
				// MIPI DBI compatible driver
				// This driver is used because it can ignore an illegal rotate value
				// and doesn't have a set_gamma() function.
				compatible = "samsung,s6d02a1";
				reg = <0>; // Chip Select 0

				spi-max-frequency = <32000000>; // 32MHz

				reset-gpios = <&gpio 23 1>; // reset is active low hence the 1
				dc-gpios = <&gpio 24 0>;
				led-gpios = <&gpio 18 0>;

				width = <320>;
				height = <240>;
				buswidth = <8>; // Most controllers use this value

				// This is a hack to prevent fb_s6d02a1.c:set_var() from issuing
				// command 0x36 after the init sequence has been applied and thus
				// changing the config.
				rotate = <1>;

				// see drivers/staging/fbtft/fb_ili9341.c:init_display() for how this looks in the driver
				// Command 0x36 is taken from the fb_ili9341.c:set_var() function.
				init = <0x1000001
					0x2000005
					0x1000028
					0x10000cf 0x00 0x83 0x30
					0x10000ed 0x64 0x03 0x12 0x81
					0x10000e8 0x85 0x01 0x79
					0x10000cb 0x39 0x2c 0x00 0x34 0x02
					0x10000f7 0x20
					0x10000ea 0x00 0x00
					0x10000c0 0x26
					0x10000c1 0x11
					0x10000c5 0x35 0x3e
					0x10000c7 0xbe
					0x100003a 0x55
					0x1000036 0x28
					0x10000b1 0x00 0x1b
					0x1000026 0x01
					0x10000f2 0x08
					0x1000026 0x01
					0x10000e0 0x1f 0x1a 0x18 0x0a 0x0f 0x06 0x45 0x87 0x32 0x0a 0x07 0x02 0x07 0x05 0x00
					0x10000e1 0x00 0x25 0x27 0x05 0x10 0x09 0x3a 0x78 0x4d 0x05 0x18 0x0d 0x38 0x3a 0x1f
					0x10000b7 0x07
					0x10000b6 0x0a 0x82 0x27 0x00
					0x1000011
					0x2000064
					0x1000029
					0x2000064>;

				debug = <0x4000000>; // print init commands to the kernel log
			};
		};
	};
};

Compile the overlay and try it out:

# Compile the overlay
$ dtc -@ -I dts -O dtb -o mydisplay.dtbo mydisplay-overlay.dts

# Load the overlay from the current directory
$ sudo dtoverlay -d . mydisplay.dtbo

# Verify that it worked
$ dmesg
[  240.563768] graphics fb0: fb_s6d02a1 frame buffer, 320x240, 150 KiB video memory, 4 KiB buffer memory, fps=20, spi0.0 at 32 MHz
$ ls /sys/bus/spi/devices/spi0.0/graphics/
fb0

# Unload the overlay
$ sudo dtoverlay -r mydisplay

# The SPI device is gone
$ ls /sys/bus/spi/devices/spi0.0/graphics/
ls: cannot access '/sys/bus/spi/devices/spi0.0/graphics/': No such file or directory

# Make the overlay available to the bootloader
$ sudo cp mydisplay.dtbo /boot/overlays/

Load the overlay during boot by adding this to /boot/config.txt:

dtoverlay=mydisplay

Refs:

Overlays

Overlays available in the Raspberry Pi kernel (links point to 5.10, make sure to pick the current version).

If you have made an overlay and want to share it, add a link here. GitHub Gist can be used to store the overlay.

Clone this wiki locally