-
Notifications
You must be signed in to change notification settings - Fork 27
Development
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.
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.
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" <noralf@tronnes.org> (maintainer:DRM TINYDRM DRIVERS)
David Airlie <airlied@linux.ie> (maintainer:DRM DRIVERS)
dri-devel@lists.freedesktop.org (open list:DRM DRIVERS)
linux-kernel@vger.kernel.org (open list)
(Dave can be skipped)
Resources:
- Mailinglist: dri-devel
- Patchwork DRI devel: https://patchwork.freedesktop.org/project/dri-devel/patches/
- Video: Write and Submit your first Linux kernel Patch
- HOWTO do Linux kernel development
- Submitting patches: the essential guide to getting your code into the kernel
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
Build documentation:
# install sphinx
$ make htmldocs
Result is in Documentation/output.
- https://dri.freedesktop.org/docs/drm/gpu/tinydrm.html
- https://dri.freedesktop.org/docs/drm/process/index.html
- https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-mm.html#prime-buffer-sharing
- https://dri.freedesktop.org/docs/drm/driver-api/dma-buf.html
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
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)
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
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.
There's a discussion regarding drm and backlight: KMS backlight ABI proposition