Development - notro/tinydrm GitHub Wiki

New driver

As a rule of thumb name the driver after the display controller: ili9320, st7735 etc., skip suffixes unless they're significant e.g. st7735r. Put macros for commands/registers (only the ones you use) in the driver itself.

Write the driver in a way that makes it easy to support for more displays. Here's an out-of-tree example: https://github.com/notro/tinydrm/blob/master/ili9325.c

Every driver needs a Device Tree binding document supplied as a separate patch. The display is given a compatible string that matches the display, not the controller. If the controller is very simple configuration wise, so it can be represented with a few Device Tree properties, then we can have a compatible string for the controller itself. ssd1307 is an example of such a controller (binding, driver).

Some controllers have very similar/identical commands/registers and can share a driver, like ili9320/ili9325 and ili9340/ili9341.

MIPI DCS/DBI

Some controllers are MIPI DCS (Display Command Set), DBI (Display Bus Interface) compatible. Easily spotted by the 0x2a, 0x2b, 0x2c commands for setting the GRAM write window.

There's a lot of displays with MIPI DCS/DBI compatible controllers out there, and that's the reason I don't want one big mipi-dbi driver that supports all these displays. Instead create a driver for the controller and put the command macro definitions inside the driver. These drivers can use the mipi-dbi library.

Parallel bus

Most MIPI DBI controller supports an Intel 8080 type parallel databus. Linux doesn't have a subsystem for such a bus and driving the bus using regular gpios is very slow, much slower than spi with dma. Some boards like the Raspberry Pi have a hardware bus (SMI) that could speed this up, but I haven't got the time to write a new Linux bus.

Monochrome/greyscale

DRM_FORMAT_XRGB8888 is the emulation format for these displays so they can work with all kinds of userspace. There's no monochrome or greyscale formats yet, but work is being done in this area.

Patches

The drm subsystem is changing all the time, so base your patches on drm-misc:drm-misc-next where the tinydrm patches are actually applied.

Select the recipients for your patch:

$ ./scripts/get_maintainer.pl some_new_driver.patch
"Noralf Trønnes" <[email protected]> (maintainer:DRM TINYDRM DRIVERS)
David Airlie <[email protected]> (maintainer:DRM DRIVERS)
[email protected] (open list:DRM DRIVERS)
[email protected] (open list)

(Dave can be skipped)

Resources:

fbtft

tinydrm was created because fbtft couldn't move out of staging due to fbdev being closed to new drivers. This means that the fbtft drivers have to be converted to DRM.

See: fbtft

Documentation

Build documentation:

# install sphinx
$ make htmldocs

Result is in Documentation/output.

PRIME

modetest

modetest is a simple tool to test drm drivers (test image).

Build

$ sudo apt install meson libtool xutils-dev
$ git clone https://gitlab.freedesktop.org/mesa/drm.git libdrm
$ cd libdrm
$ meson builddir/
$ ninja -C builddir/ install

Find connector id

$ libdrm/builddir/tests/modetest/modetest -M "mi0283qt" -c

Connectors:
id      encoder status          name            size (mm)       modes   encoders
25      28      connected       Virtual-1       58x43           1       28
  modes:
        name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot)
  320x240 0 320 320 320 320 240 240 240 240 1 flags: ; type: preferred, driver
  props:
        2 DPMS:
                flags: enum
                enums: On=0 Standby=1 Suspend=2 Off=3
                value: 0

Display a test image

$ modetest -M "mi0283qt" -s 25:320x240@RG16
setting mode 320x240-0Hz@RG16 on connectors 25, crtc 27

<press enter to stop>

(RG16 is RGB565, XR24 is XRGB8888)

How fast is it

$ modetest -M "mi0283qt" -s 25:320x240@RG16 -v
setting mode 320x240-0Hz@RG16 on connectors 25, crtc 27
freq: 31.44Hz
freq: 31.44Hz
freq: 31.44Hz
freq: 31.44Hz
freq: 31.44Hz

<press enter to stop>

Verbose modetest:

$ LIBGL_DEBUG=verbose modetest

Debug

drm_debug

Turn on most drm debug messages

$ echo 0xf | sudo tee /sys/module/drm/parameters/debug

From include/drm/drmP.h

#define DRM_UT_CORE             0x01
#define DRM_UT_DRIVER           0x02
#define DRM_UT_KMS              0x04
#define DRM_UT_PRIME            0x08
#define DRM_UT_ATOMIC           0x10
#define DRM_UT_VBL              0x20
#define DRM_UT_STATE            0x40

mipi_dbi

Turn on and off display:

$ echo "28" | sudo tee /sys/kernel/debug/dri/0/command
$ echo "29" | sudo tee /sys/kernel/debug/dri/0/command

Dump MIPI DCS commands:

$ sudo cat /sys/kernel/debug/dri/0/command
04: 000000
06: 00
07: 00
08: 00
09: 94530400
0a: 9c
0b: 28
0c: 05
0d: 00
0e: 00
0f: c0
2e: 00c4
3e: 00c4
45: 00
52: 00
54: 00
56: 00
5f: 00
a1: 00
a8: 00

Use tool from tinydrm repo:

$ sudo ./tinydrm/tools/mipi-dcs
File: /sys/kernel/debug/dri/0/command

GET_DISPLAY_ID(04h): 000000 (0b0)
    ID1 = 00
    ID2 = 00
    ID3 = 00
GET_RED_CHANNEL(06h): 00 (0b0)
GET_GREEN_CHANNEL(07h): 00 (0b0)
GET_BLUE_CHANNEL(08h): 00 (0b0)
GET_DISPLAY_STATUS(09h): 94530400 (0b10010100010100110000010000000000)
    D31=1: Booster voltage status: On
    D30=0: Row address order
    D29=0: Column address order
    D28=1: Row/column exchange
    D27=0: Vertical refresh: Top to Bottom
    D26=1: RGB/BGR order: BGR
    D25=0: Horizontal refresh order: Left to Right
    D24=0: Reserved
    D23=0: Reserved
    D[22:20]=2373: Interface color pixel format: 1 0 1
    D19=0: Idle mode: Off
    D18=0: Partial mode: Off
    D17=1: Sleep: Out
    D16=1: Display normal mode: On
    D15=0: Vertical scrolling status: Off
    D14=0: Reserved
    D13=0: Inversion status
    D12=0: All pixel ON
    D11=0: All pixel OFF
    D10=1: Display: On
    D9=0: Tearing effect line: Off
    D[8:6]=38882320: Gamma curve selection: 0 0 0
    D5=0: Tearing effect line mode: Mode 1, V-Blanking only
    D4=0: Reserved
    D3=0: Reserved
    D2=0: Reserved
    D1=0: Reserved
    D0=0: Reserved
GET_POWER_MODE(0Ah): 9c (0b10011100)
    D7=1: Booster On
    D6=0: Idle Mode Off
    D5=0: Partial Mode Off
    D4=1: Sleep Out Mode
    D3=1: Display Normal Mode On
    D2=1: Display is On
    D1=0: Reserved
    D0=0: Reserved
GET_ADDRESS_MODE(0Bh): 28 (0b101000)
    D7=0: Page Address Order: Top to Bottom
    D6=0: Column Address Order: Left to Right
    D5=1: Page/Column Order: Reverse Mode
    D4=0: Line Address Order: LCD Refresh Top to Bottom
    D3=1: RGB/BGR Order: BGR
    D2=0: Display Data Latch Data Order: LCD Refresh Left to Right
    D1=0: Reserved
    D0=0: Reserved
GET_PIXEL_FORMAT(0Ch): 05 (0b101)
    D7=0: Reserved
    D[6:4]=0: DPI: Reserved
    D3=0: Reserved
    D[2:0]=5: DBI: 16 bits/pixel
GET_DISPLAY_MODE(0Dh): 00 (0b0)
    D7=0: Vertical Scrolling Status: Off
    D6=0: Reserved
    D5=0: Inversion: Off
    D4=0: Reserved
    D3=0: Reserved
    D[2:0]=0: Gamma Curve Selection: GC0
GET_SIGNAL_MODE(0Eh): 00 (0b0)
    D7=0: Tearing Effect Line: Off
    D6=0: Tearing Effect Line Output Mode: Mode 1
    D5=0: Reserved
    D4=0: Reserved
    D3=0: Reserved
    D2=0: Reserved
    D1=0: Reserved
    D0=0: Reserved
GET_DIAGNOSTIC_RESULT(0Fh): c0 (0b11000000)
    D7=1: Register Loading Detection: OK
    D6=1: Functionality Detection: OK
    D5=0: Chip Attachment Detection: OK or unimplemented
    D4=0: Display Glass Break Detection: OK or unimplemented
    D3=0: Reserved
    D2=0: Reserved
    D1=0: Reserved
    D0=0: Reserved
READ_MEMORY_START(2Eh): 0000 (0b0)
READ_MEMORY_CONTINUE(3Eh): 0000 (0b0)
GET_SCANLINE(45h): 00 (0b0)
GET_DISPLAY_BRIGHTNESS(52h): 00 (0b0)
GET_CONTROL_DISPLAY(54h): 00 (0b0)
GET_POWER_SAVE(56h): 00 (0b0)
GET_CABC_MIN_BRIGHTNESS(5Fh): 00 (0b0)
READ_DDB_START(A1h): 00 (0b0)
READ_DDB_CONTINUE(A8h): 00 (0b0)

SPI trace

Event tracing can be used see what's been written to the controller:

#enable (use spi_transfer_stop for rx result)
echo 1 | sudo tee /sys/kernel/debug/tracing/events/spi/spi_transfer_start/enable

#disable all
echo | sudo tee /sys/kernel/debug/tracing/set_event

#start
echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on

#stop
echo 0 | sudo tee /sys/kernel/debug/tracing/tracing_on

#view
sudo cat /sys/kernel/debug/tracing/trace

#clear
echo | sudo tee /sys/kernel/debug/tracing/trace

Platform data

tinydrm doesn't support platform data, but it's still possible use it without Device Tree. This example is taken from my work on fb_ili9325.

#include <linux/gpio/machine.h>
#include <linux/property.h>
#include <linux/spi/spi.h>

static const struct spi_board_info hy28a = {
	.modalias = "fb_ili9320",
	.max_speed_hz = 32000000,
	.bus_num = 0,
	.chip_select = 0,
	.mode = SPI_MODE_3,
};

static struct property_entry hy28a_properties[] = {
	PROPERTY_ENTRY_BOOL("bgr"),
	PROPERTY_ENTRY_U32("rotation", 270),
	{ },
};

static struct gpiod_lookup_table hy28a_gpios = {
	.dev_id = "spi0.0",
	.table = {
		GPIO_LOOKUP("pinctrl-bcm2835", 25, "reset", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("pinctrl-bcm2835", 18, "led", GPIO_ACTIVE_LOW),
		{ },
	},
};

static int bus_notifier_call(struct notifier_block *n, unsigned long action, void *data)
{
	struct device *dev = data;

	if (action == BUS_NOTIFY_ADD_DEVICE) {
		if (!strcmp(dev_name(dev), "spi0.0")) {
			dev_info(dev, "hy28a registered\n");
			device_add_properties(dev, hy28a_properties);
		}
	}

	return NOTIFY_DONE;
}

static struct notifier_block bus_notifier = {
	.notifier_call = bus_notifier_call,
};

static int __init hy28a_register(void)
{
	int ret;

	gpiod_add_lookup_table(&hy28a_gpios);

	bus_register_notifier(&spi_bus_type, &bus_notifier);
	ret = spi_register_board_info(&hy28a, 1);
	if (ret) {
		pr_err("Failed to register hy28a\n");
		return ret;
	}

	return 0;
}

gpiod_add_lookup_table() isn't exported so this can't be done from a loadable module.

Other stuff

There's a discussion regarding drm and backlight: KMS backlight ABI proposition