-
Notifications
You must be signed in to change notification settings - Fork 27
Development
There's no specific mailinglist for tinydrm except for the dri-devel mailinglist for the whole Linux graphics subsystem. Send email to noralf@tronnes.org and CC: dri-devel@lists.freedesktop.org, or create an issue here on github.
As a rule of thumb name the driver after the display controller: ili9320, st7735 etc., skip suffixes unless they're significant e.g. st7735r. If there's custom circuitry in front of the controller that changes the controller interface/protocol, then I guess we'll name the driver after the display or something. Put macros for commands/registers (only the ones you use) in the driver itself, but if it's a lot, put it in a headerfile alongside the driver (not include/drm/tinydrm/).
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/fb_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.
If you're uncertain, better ask first than to have to redo it after patch review.
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.
Caveat
Don't look at mi0283qt for how to enable/disable the controller. It uses devm_add_action() to cleanup, better use .enable()/.disable(). I haven't gotten around to rectify this yet. mi0283qt was the first driver out, and in hindsight I should probably called it ili9341. Don't add ili9341 displays to this driver, add an ili9341 driver instead.
Driver Maintainer
I really prefer a MAINTAINERS entry for drivers. This way someone with knowledge about the driver/controller/display can have a say about the changes before they go in. It also helps me to get ack's on tindyrm changes that I make, since I can't apply patches without someone having agreed first.
Example patchset adding a new driver:
- [PATCH v4 0/5] Support for LEGO MINDSTORMS EV3 LCD display
- [PATCH v4 1/5] drm/tinydrm: Generalize tinydrm_xrgb8888_to_gray8()
- [PATCH v4 2/5] dt-bindings: add binding for Sitronix ST7586 display panels
- [PATCH v4 3/5] drm/tinydrm: add support for LEGO MINDSTORMS EV3 LCD
- [PATCH v4 4/5] ARM: dts: da850-lego-ev3: Add node for LCD display
- [PATCH v4 5/5] ARM: davinci_all_defconfig: enable tinydrm and ST7586
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_tinydrm_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 tinydrm. Only one driver has been converted so far, and that's fb_ili9341 which is now called mi0283qt. I really should have called it ili9341 instead (more in the caveat heading above).
I have no plans to personally convert fbtft drivers to tinydrm. In this repo I did a conversion of the fbtft module to tinydrm, but that was just some testing, don't use that.
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
I did a test some months back using a hacked modetest rendering a dumb buffer on vc4 and scanned out with tinydrm. This worked.
I recently tried the same using X windows, but failed: https://github.com/anholt/linux/issues/10
- 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-get -y install autoconf libtool xutils-dev libpthread-stubs0-dev
$ git clone https://github.com/freedreno/libdrm
$ cd libdrm
$ ./autogen.sh
$ make
Find connector id
$ ./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
$ ./tests/modetest/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
$ ./tests/modetest/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>
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
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.