4.3.17. LCD Driver Debug Guide

4.3.17.1. LCD Overview

LCD (Liquid Crystal Display) is one of the most common display devices in embedded systems. It renders images by using liquid crystal materials to change light transmission and polarization under an electric field. Since liquid crystals themselves do not emit light, LCDs typically require a backlight module to produce visible images; they also rely on high-speed display interfaces to transmit image data to the host SoC, and use a control channel for display parameter configuration and status management.

The X5 series boards provide a complete and highly integrated set of interfaces for LCD display, including MIPI DSI, I2C, reset signals, power input, and backlight control, enabling developers to easily and reliably connect various LCD panels and accelerate display function development and debugging.

4.3.17.2. Interface Description

The EVB board provides a 24-pin LCD interface, which mainly includes:

  • 1 MIPI DSI clock lane

  • 4 MIPI DSI data signals

  • 1 I2C interface

  • GND, 3.3V power

  • RST reset pin

  • VLEDA, VLEDK backlight power/control pins

The X5 MD board provides a 22-pin LCD interface, which is similar in overall design to the EVB board, with the only difference being in the backlight power section: it does not include VLEDA and VLEDK backlight control pins.

4.3.17.3. Specifications

The MIPI-DSI interface specifications of X5 are as follows:

  • Supports up to four data lanes

  • Maximum rate per lane is 2.5 Gbps

  • Supports Non-Burst-Sync-Pulse synchronization pulse mode, Non-Burst-Sync-Event synchronization event mode, and Burst burst mode

  • Supports MIPI_DSI_CLOCK_NON_CONTINUOUS non-continuous clock mode and MIPI_DSI_CLOCK_CONTINUOUS continuous clock mode

4.3.17.4. Functional Principles

MIPI DSI

MIPI DSI (Display Serial Interface) is a high-speed serial communication standard between the host SoC and the LCD display module, responsible for sending initialization commands and transmitting video frame data. Its core consists of 1 clock Lane + multiple data Lanes, used for synchronization and data transmission respectively. DSI operation includes two main phases: Initialization Phase (Command Mode) and Video Data Transmission Phase (Video Mode).

Initialization Phase (Command Mode)

After the LCD is powered on, it first enters low-power LP mode. The host sends initialization commands to the LCD driver chip via MIPI DSI. This process is accomplished through DCS (Display Command Set) or vendor-specific commands. The initialization process generally includes the following steps:

  • Enter DSI LP mode

  • Perform hardware pin reset, or send a DSI software reset command DCS Soft Reset (0x01)

  • Send register configuration sequence

    • This step is the core of LCD initialization. Each LCD has its own register script, which includes:

    • Color format settings (e.g., RGB888, RGB565)

    • Lane count configuration (1/2/4 Lane)

    • Vertical/horizontal timing parameters (VBP/VFP/HBP/HFP)

    • Refresh rate and polarity settings

    • Gamma curve and power voltage parameters

    • Driver chip internal register initialization

Video Data Transmission Phase (Video Mode)

After initialization is complete, the LCD enters the display working state. At this point, DSI switches to HS (High-Speed) mode for continuous video frame transmission.

Working Characteristics

  • Uses high-speed differential signaling

  • Data rates can reach hundreds of MHz to several GHz

  • Continuously outputs full-frame RGB pixel data

  • Frame rates are typically 60Hz / 90Hz / 120Hz

Data Encapsulation Format

Pixel data is encapsulated as DSI Long Packets:

Video Line Packet

Each line of pixel data is encapsulated as a Long Packet, containing:

  • Packet Header (byte type, length, data ID)

    • e.g.: 0x39 (Long Write)

  • Payload: One full line of pixel data (RGB888/RGB565, etc.)

  • ECC/CRC checksum: Ensures data integrity

An entire frame consists of multiple line packets, combined with synchronization signals (VSync/HSync) to form a complete display image.

Touch

In most small and medium-sized LCD display modules, touch screens (mainly capacitive touch screens) typically communicate with the host SoC via the I2C interface. The touch IC is responsible for collecting touch coordinates, gestures, and other information, and reports it to the system in data packets via I2C. After parsing by the upper-layer input subsystem, it is converted into click, swipe, and other interaction events.

The most commonly used I2C capacitive touch screen controllers on embedded platforms include:

  • Goodix: GT911, GT5688, GT7388, etc.

  • FocalTech: FT5436, FT5x06, FT6336, etc.

  • Ilitek: ILI210x series

  • Himax: HX8526, HX83102, etc.

  • Mstar (merged into Novatek): MSG22xx series

These ICs all operate via the I2C interface with similar protocol styles, making system porting relatively easy. You can obtain drivers from the panel manufacturer or touch IC vendor.

Backlight Control

The X5 EVB uses LPWM 1_1 to output a PWM dimming signal. The SY7200AABC receives this signal and generates LED positive voltage (VLEDA) through its internal boost circuit, and produces LED negative current (VLEDK) through constant current control, thereby driving the LCD backlight and enabling brightness adjustment.

  • PWM control signal input (DIM/PWM pin)

    • The PWM duty cycle corresponds to the desired backlight brightness, for example:

    • High duty cycle -> large dimming command -> large LED current -> brighter backlight

    • Low duty cycle -> reduced brightness

    • 0% duty cycle can turn off the backlight

    • SY7200AABC samples the PWM signal and converts it into an LED current adjustment value through internal dimming control

4.3.17.5. Debugging Procedure

Obtain Screen Information

Before adding a new display driver, the following information must be determined:

  • Display timing parameters: hbp, hfp, hsa, vbp, vfp, vsa

  • Display initialization sequence

  • Transmission mode supported by the display: burst or non-burst

  • Clock mode supported by the display: continuous or non-continuous

  • Display lane count: 1, 2, or 4

  • Touch screen driver

  • Backlight control method

Taking the JC050HD134 display as an example, the following information is obtained from the configuration provided by the manufacturer:

#define Width 720
#define Height 1280

#define VFP 20
#define VBP 20
#define VSA 4

#define HFP 32
#define HBP 20
#define HSA 20
  • Initialization sequence

DSI_CMD(0x04);DSI_PA(0xB9);
DSI_PA(0xF1);
DSI_PA(0x12);
DSI_PA(0x83);

DSI_CMD(0x1C);DSI_PA(0xBA);
DSI_PA(0x33); // 1
DSI_PA(0x81); // 2
DSI_PA(0x05); // 3
DSI_PA(0xF9); // 4
DSI_PA(0x0E); // 5
DSI_PA(0x0E); // 6
DSI_PA(0x20); // 7
DSI_PA(0x00); // 8
DSI_PA(0x00); // 9
DSI_PA(0x00); //10
DSI_PA(0x00); //11
DSI_PA(0x00); //12
DSI_PA(0x00); //13
DSI_PA(0x00); //14
DSI_PA(0x44); //15
DSI_PA(0x25); //16
DSI_PA(0x00); //17
DSI_PA(0x91); //18
DSI_PA(0x0A); //19
DSI_PA(0x00); //20
DSI_PA(0x00); //21
DSI_PA(0x02); //22
DSI_PA(0x4F); //23
DSI_PA(0xD1); //24
DSI_PA(0x00); //25
DSI_PA(0x00); //26
DSI_PA(0x37); //27
  • Transmission mode supported by the display: non-burst

  • Clock mode supported by the display: continuous

  • Display lane count: 4

  • Touch screen driver: None

  • Backlight control method: Adapted for 24-pin VLEDA, VLEDK

Kernel Driver Level

There is a well-debugged reference code in the kernel: panel-atk-md0550.c. Subsequent display drivers can be derived from this driver.

Copy kernel/drivers/gpu/drm/panel/panel-atk-md0550.c and rename it to panel-jc-050hd134.c.

All the following modifications are based on panel-jc-050hd134.c.

Modify the name field in the panel_simple_dsi_driver structure to panel-jc-050hd134:

static struct mipi_dsi_driver panel_simple_dsi_driver = {
	.driver =
		{
			.name		= "panel-jc-050hd134",
			.of_match_table = dsi_of_match,
		},
	.probe	  = panel_simple_dsi_probe,
	.remove	  = panel_simple_dsi_remove,
	.shutdown = panel_simple_dsi_shutdown,
};

Modify the drm_display_mode structure:

static const struct drm_display_mode jc_050hd134_mode { // Rename structure to jc_050hd134_mode
	.clock	     = 65000, // Pixel clock in kHz, calculated as: fps * (htotal + vtotal)
	.hdisplay    = 720, // Horizontal active area width
	.hsync_start = 720 + 32, // hdisplay + hfp
	.hsync_end   = 720 + 32 + 20, // hsync_start + hsa
	.htotal	     = 720 + 32 + 20 + 20, // hsync_end + hbp
	.vdisplay    = 1280, // Vertical active area height
	.vsync_start = 1280 + 20, // vdisplay + vfp
	.vsync_end   = 1280 + 20 + 4, // vsync_start + vsa
	.vtotal	     = 1280 + 20 + 4 + 20, // vsync_end + vbp
	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, // Polarity
};

Modify the panel_desc_dsi structure:

static const struct panel_desc_dsi jc_050hd134 { // Rename structure to jc_050hd134
	.desc =
		{
			.modes	   = &jc_050hd134_mode, // Point to timing structure
			.num_modes = 1, // Number of timing modes for this panel, usually one
			.bpc	   = 8, // Bits per color component? RGB888 means 8 bits per color, RGB666 means 6 bits per color
			.size = // Physical size of active area in mm (millimeters)
				{
					.width	= 62,
					.height = 110,
				},
			.connector_type = DRM_MODE_CONNECTOR_DSI,
		},
	.flags	= MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE, // DSI mode flags, indicating the panel operates in video mode and non-burst mode
    // For more flags, refer to kernel/include/drm/drm_mipi_dsi.h
	.format = MIPI_DSI_FMT_RGB888, // Panel color format
	.lanes	= 4, // Number of data lanes
};

mode_flags are used to describe the data transmission method and link behavior between the DSI host and the display panel, as detailed below:

mode_flags Description
MIPI_DSI_MODE_VIDEO Enable video mode, the host continuously sends pixel data according to display timing.
MIPI_DSI_MODE_VIDEO_BURST Enable Burst video mode, pixel data is sent in burst fashion.
MIPI_DSI_MODE_VIDEO_SYNC_PULSE Enable synchronization pulse mode, HSYNC/VSYNC are sent as independent pulses. The timing is closer to the traditional RGB interface with the best compatibility, and is the recommended configuration for most LCDs.
MIPI_DSI_MODE_VIDEO_AUTO_VERT Enable automatic vertical count mode, the DSI controller automatically manages vertical line counting.
MIPI_DSI_MODE_VIDEO_HSE Send HSYNC End packets during VSYNC pulses and vertical porch regions. Used for some panels with strict timing requirements.
MIPI_DSI_MODE_VIDEO_NO_HFP Disable horizontal front porch.
MIPI_DSI_MODE_VIDEO_NO_HBP Disable horizontal back porch.
MIPI_DSI_MODE_VIDEO_NO_HSA Disable horizontal sync active region.
MIPI_DSI_MODE_VSYNC_FLUSH Flush display FIFO during VSYNC pulses to prevent residual old data.
MIPI_DSI_MODE_NO_EOT_PACKET Disable EoT (End of Transmission) packets in HS mode. Some panels or PHYs have incomplete EoT packet support; disabling can improve stability.
MIPI_DSI_CLOCK_NON_CONTINUOUS Enable non-continuous clock mode. The DSI Clock Lane can enter LP state during idle periods, reducing power consumption.
MIPI_DSI_MODE_LPM Enable Low Power Mode (LP) for data transmission. This is critical; most panel initialization commands (DCS/Generic Command) must be sent in LP mode.
MIPI_DSI_HS_PKT_END_ALIGNED In high-speed mode (HS), whether the end times of data packets on multiple Data Lanes are aligned.

Among them, MIPI_DSI_MODE_VIDEO, MIPI_DSI_MODE_VIDEO_SYNC_PULSE, and MIPI_DSI_MODE_LPM are the most commonly used flags in practical projects and are often required configurations.

Modify dsi_of_match for device tree binding preparation:

static const struct of_device_id dsi_of_match[] = {
	{.compatible = "jc-050hd134", .data = &jc_050hd134},
	{
		/* sentinel */
	}};
MODULE_DEVICE_TABLE(of, dsi_of_match);

Modify the panel_simple_dsi_init function, which actually calls dsi_dcs_write_seq to write the mipi initialization sequence into the panel.


If the initialization parameters you receive from the display vendor are in the following format:

DSI_CMD(0x04);DSI_PA(0xB9);
DSI_PA(0xF1);
DSI_PA(0x12);
DSI_PA(0x83);

DSI_CMD(0x1C);DSI_PA(0xBA);
DSI_PA(0x33); // 1
DSI_PA(0x81); // 2
DSI_PA(0x05); // 3
DSI_PA(0xF9); // 4
DSI_PA(0x0E); // 5
DSI_PA(0x0E); // 6
DSI_PA(0x20); // 7
DSI_PA(0x00); // 8
DSI_PA(0x00); // 9
DSI_PA(0x00); //10
DSI_PA(0x00); //11
DSI_PA(0x00); //12
DSI_PA(0x00); //13
DSI_PA(0x44); //14
DSI_PA(0x25); //15
DSI_PA(0x00); //16
DSI_PA(0x91); //17
DSI_PA(0x0A); //18
DSI_PA(0x00); //19
DSI_PA(0x00); //20
DSI_PA(0x02); //21
DSI_PA(0x4F); //22
DSI_PA(0xD1); //23
DSI_PA(0x00); //24
DSI_PA(0x00); //25
DSI_PA(0x37); //26

......

Convert to driver code:

dsi_dcs_write_seq(dsi, 0xb9, 0xF1, 0x12, 0x83);
dsi_dcs_write_seq(dsi, 0xBA, 0x33, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, 0x00, 0x00, 0x00,
			  0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x91, 0x0A, 0x00, 0x00, 0x02,
			  0x4F, 0xD1, 0x00, 0x00, 0x37);
......

Note that the first value in each vendor-provided initialization sequence indicates the length of that sequence and should not be included when converting to driver code, as it will be automatically calculated during write operations.


If the initialization sequence from the vendor is in the following format:

panel-init-sequence-zero = [
 39 00 04 B9 FF 83 94
 15 00 02 36 01
 39 00 07 BA 63 03 68 6B B2 C0
 39 00 0B B1 48 12 72 09 32 54 71 71 57 47
......
]

Convert to driver code:

	dsi_dcs_write_seq(dsi, 0xb9, 0xff, 0x83, 0x94);
	dsi_dcs_write_seq(dsi, 0x36, 0x01);
	dsi_dcs_write_seq(dsi, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
	dsi_dcs_write_seq(dsi, 0xb1, 0x48, 0x12, 0x72, 0x09, 0x32, 0x54, 0x71, 0x71, 0x57, 0x47);

Take the first sequence 39 00 04 B9 FF 83 94 as an example for explanation:

39 indicates that this initialization sequence uses MIPI_DSI_DCS_LONG_WRITE (0x39) data type, which does not need to be written in driver code and will be automatically generated based on the parameter length
00 means delay 0ms after this write operation, which can be implemented via msleep(ms)
04 indicates the length of the current write sequence, which will be automatically generated and does not need to be filled in
The following bytes are the actual initialization sequence

For displays requiring specific power-on and reset timing sequences, modify the gpio behaviors in the panel_simple_prepare and panel_simple_unprepare functions, which correspond to initialization and deinitialization states respectively.


Modify kernel/drivers/gpu/drm/panel/Kconfig and kernel/drivers/gpu/drm/panel/Makefile

Kconfig:

config DRM_PANEL_JC_050HD134
	tristate "JC 050HD134 panel"
	depends on OF
	depends on DRM_MIPI_DSI
	depends on BACKLIGHT_CLASS_DEVICE
	select VIDEOMODE_HELPERS
	help
	  Say Y here if you want to enable support for the JC050HD134
	  panel with 720x1280 resolution. This panel supports
	  MIPI DSI interface.

Makefile:

obj-$(CONFIG_DRM_PANEL_JC_050HD134) += panel-jc-050hd134.o

Kernel Device Tree Level

In the board-level device tree, using kernel/arch/arm64/boot/dts/hobot/x5-evb.dtsi as an example:

Add the following properties to the mipi_dsi0 node:

&mipi_dsi0 {

	status = "okay";
	ports {
		port@1 {
			reg = <1>;
			mipi_dsi_out: endpoint {
				remote-endpoint = <&panel_in>;
			};
		};
	};

	dsi_panel0@0 {
		compatible = "jc-050hd134"; // Must match dsi_of_match
		reg = <0>;

		pinctrl-names = "default";
		pinctrl-0 = <&lsio_gpio0_14>; // Related to reset pin
		reset-gpios = <&ls_gpio0_porta 14 GPIO_ACTIVE_HIGH>; // Reset pin, active level depends on your display
		backlight = <&dsi_backlight>; // Backlight related

		port {
			panel_in: endpoint {
				remote-endpoint =
					<&mipi_dsi_out>;
			};
		};
	};

};

Add the dsi_backlight node:

&dsi_backlight {
	status = "okay";
	pwms = <&lpwm1 1 1000000>; // Hardware PCB dependent, check where your backlight PWM signal comes from; here indicates it comes from LPWM1_1
    // For other properties and information, refer to kernel/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml
};

Compilation

Please first refer to Chapter 4.1 to set up the compilation environment and ensure you can successfully compile an image before proceeding!

Run ./bd.sh boot menuconfig to enter the kernel configuration menu, then navigate to the Panels compilation option via the following path:

Device Drivers  --->
    Graphics support  --->
        Display Panels  --->

Locate JC 050HD134 panel, press space to compile it as a module, then save and exit.

Run ./bd.sh all to compile and generate the image, then flash the image onto the board, connect the display, and power on.

Testing

After successfully entering the kernel, execute the following commands to load the driver:

modprobe panel-jc-050hd134
modprobe vio_n2d
modprobe vs-x5-syscon-bridge
modprobe vs_drm

Then, run the dmesg command and check the end of the log for output similar to:

[ 3343.383052] vs-disp-sif 3e080000.vs-sif: Adding to iommu group 3
[ 3343.384118] vs-dc 3e000000.dc8000Nano: Adding to iommu group 4
[ 3343.385252] vs-bt1120 3e010000.bt1120: Adding to iommu group 5
[ 3343.387555] horizon-lsio-pinctrl 34180000.lsio_iomuxc: set pin = 14 direction to input
[ 3343.387567] horizon-lsio-pinctrl 34180000.lsio_iomuxc: map pin14 to gpio[0] - 14
[ 3343.387635] panel-jc-050hd134 3e060000.mipi_dsi0.0: supply power not found, using dummy regulator
[ 3343.391241] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e080000.vs-sif (ops sif_component_ops [vs_drm])
[ 3343.391529] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e000000.dc8000Nano (ops dc_component_ops [vs_drm])
[ 3343.391692] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e010000.bt1120 (ops bt1120_component_ops [vs_drm])
[ 3343.391730] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e000000.disp_apb:bt1120_bridge (ops bt1120_bridge_component_ops [vs_drm])
[ 3343.391764] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e000000.disp_apb:bt1120_bridge_wb (ops bt1120_bridge_component_ops [vs_drm])
[ 3343.391816] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e060000.mipi_dsi0 (ops dsi_component_ops [vs_drm])
[ 3343.391917] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e000000.disp_apb:dsi-encoder (ops encoder_component_ops [vs_drm])
[ 3343.391976] vs-drm 3e000000.disp_apb:display-subsystem: bound 3e000000.disp_apb:hdmi-encoder (ops encoder_component_ops [vs_drm])
[ 3343.392815] [drm] Initialized vs-drm 1.0.0 20191101 for 3e000000.disp_apb:display-subsystem on minor 0

When the line [drm] Initialized vs-drm 1.0.0 20191101 for 3e000000.disp_apb:display-subsystem on minor 0 appears, it indicates that the display driver has loaded successfully.

Run modetest -M vs-drm -c to check the status of connectors:

Connectors:
id      encoder status          name            size (mm)       modes   encoders
73      0       connected 		DSI-1           62x110             1       72
  modes:
        index name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot
  #0 720x1280 61.99 720 752 772 792 1280 1300 1304 1324 65000 flags: nhsync, nvsync; type: preferred, driver
  props:
        1 EDID:
                flags: immutable blob
                blobs:

                value:
        2 DPMS:
                flags: enum
                enums: On=0 Standby=1 Suspend=2 Off=3
                value: 0
        5 link-status:
                flags: enum
                enums: Good=0 Bad=1
                value: 0
        6 non-desktop:
                flags: immutable range
                values: 0 1
                value: 0
        4 TILE:
                flags: immutable blob
                blobs:

                value:

Test using the command: modetest -M vs-drm -a -s 73@31:720x1280 -P 33@31:720x1280@NV12

If everything goes smoothly, the connected display will light up and show the pattern shown in the image below (image rotated):

modetest-pattern