# LCD屏幕调试指南
## LCD 概述
LCD（Liquid Crystal Display）是嵌入式系统中最常见的显示设备之一，它通过液晶材料在电场作用下改变光的透过与偏振来呈现图像。由于液晶本身不具备发光能力，LCD 通常需要搭配背光模组才能形成可视画面；同时依赖高速显示接口与主控 SoC 传输图像数据，并通过控制通道完成显示参数配置和状态管理。

X5 系列板卡为 LCD 显示提供了一组完整且高度集成的接口，包括 MIPI DSI、I²C、复位信号、电源输入以及背光控制等，使开发者能够方便、可靠地接入多种 LCD 屏幕，加速显示功能的开发与调试。

## 接口说明

`EVB` 板卡提供 `24-pin LCD` 接口，主要包括：
- 1 路 MIPI DSI 时钟线
- 4 路 MIPI DSI 数据信号
- 1 路 I²C 接口
- GND、3.3V 电源
- RST 复位引脚
- VLEDA、VLEDK 背光供电/控制引脚

`X5 MD` 板卡提供 `22-pin LCD` 接口，整体与 `EVB` 板卡设计相近，仅在背光供电部分有所区别：不包含 VLEDA、VLEDK 背光控制脚。

## MIPI-DSI规格参数

X5的`MIPI-DSI`接口规格如下：
- 最大支持**四**路数据通路
- 每一路最高速率为`2.5`Gbps
- 支持`Non-Burst-Sync-Pulse`**同步脉冲**模式和`Non-Burst-Sync-Even`**同步事件**模式以及`Burst`**突发**模式
- 支持`MIPI_DSI_CLOCK_NON_CONTINUOUS`**非连续时钟**模式和`MIPI_DSI_CLOCK_CONTINUOUS`**连续时钟**模式

## 功能原理

### MIPI DSI
MIPI DSI（Display Serial Interface）是主控 SoC 与 LCD 显示模块之间的高速串行通信标准，负责 发送初始化命令 和 传输视频帧数据。其核心由 1 路时钟 Lane + 多路数据 Lane 构成，分别用于同步和数据传输。DSI 工作包含两个主要阶段：`初始化阶段（Command Mode）` 和 `视频数据传输阶段（Video Mode）`。

#### 初始化阶段（Command Mode）

LCD 上电后，首先进入低功耗 LP 模式，主控通过 MIPI DSI 向 LCD 驱动芯片发送初始化命令。该过程通过 DCS（Display Command Set） 或厂商自定义命令完成。
初始化流程一般包括以下步骤：

- 进入 DSI LP 模式
- 通过硬件管脚复位，或者发送DSI软件复位命令`DCS Soft Reset (0x01)`

- 发送寄存器配置序列
  - 此步骤是 LCD 初始化的核心，每款 LCD 都有对应的寄存器脚本，内容包括：
  - 色彩格式设置（如 RGB888、RGB565）
  - Lane 数配置（1/2/4 Lane）
  - 垂直/水平时序参数（VBP/VFP/HBP/HFP）
  - 刷新率、极性设置
  - Gamma 曲线、电源电压参数
  - 驱动芯片内部寄存器初始化


#### 视频数据传输阶段（Video Mode）

初始化完成后，LCD 进入显示工作状态。此时 DSI 切换到 HS（High-Speed）模式 以进行持续的视频帧传输。

**工作特性**

- 使用高速差分信号
- 数据速率可达数百 MHz ～ 数 GHz
- 连续输出全帧 RGB 像素数据
- 帧率通常为 60Hz / 90Hz / 120Hz

**数据封装格式**

像素数据会被封装为 DSI 的长包（Long Packet）：

**Video Line Packet**

每一行像素数据封装为一个 长包（Long Packet），包含：

- Packet Header（字节类型、长度、数据 ID）
  - 如：0x39（Long Write）
- Payload：一整行的像素数据（RGB888/RGB565 等）
- ECC/CRC 校验：确保数据完整性

整个帧由多行长包构成，结合同步信号（VSync/HSync）形成完整显示画面。

### 触控

在多数中小尺寸 LCD 显示模组中，触摸屏（主要为 电容式触摸屏）通常通过 I²C 接口与主控 SoC 通信。触控 IC 负责采集触摸坐标、手势等信息，并以数据包形式通过 I²C 上报给系统，由上层输入子系统解析后转化为点击、滑动等交互事件。

目前嵌入式平台最常用的 I²C 接口电容屏控制器包括：
- Goodix：GT911、GT5688、GT7388 等
- FocalTech：FT5436、FT5x06、FT6336 等
- Ilitek：ILI210x 系列
- Himax：HX8526、HX83102 等
- Mstar（已并入联咏）：MSG22xx 系列

这些 IC 都基于 I²C 接口工作，协议风格类似，系统移植较为容易，您可以从屏厂或触控 IC 厂家获取驱动。

### 背光控制

X5 EVB 使用`LPWM 1_1`输出一个 PWM 调光信号，`SY7200AABC` 接收该信号后，通过内部升压电路产生 LED 正极电压（VLEDA），并通过恒流控制产生 LED 负极电流（VLEDK），从而驱动 LCD 背光并实现亮度调节。

- PWM 控制信号输入（DIM/PWM 引脚）
  - PWM 的占空比对应所需背光亮度，例如：
  - 占空比高 → 调光指令大 → LED 电流大 → 背光更亮
  - 占空比低 → 亮度降低
  - 0% 占空比可实现背光关闭
  - SY7200AABC 通过对 PWM 信号进行采样和内部调光控制，将其转换为 LED 电流调节量

## 调试流程

### 获取屏幕信息

在添加新屏幕驱动之前，需要确定以下信息：
- 屏幕的时序，即`hbp`、`hfp`、`hsa`、`vbp`、`vfp`、`vsa`
- 屏幕的初始化序列
- 屏幕支持的传输模式，是`burst`还是`non-burst`
- 屏幕支持的时钟模式，是`continuous`还是`non-continuous`
- 屏幕lane数，是`1`还是`2`还是`4`
- 触摸屏驱动
- 背光控制方式

以`JC050HD134`这款屏幕为例，从厂家提供的配置中得知以下信息：

- 时序
```C
#define Width 720
#define Height 1280

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

#define HFP 32
#define HBP 20
#define HSA 20
```

- 初始化序列
```C
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
```

- 屏幕支持的传输模式 `non-burst`
- 屏幕支持的时钟模式 `continuous`
- 屏幕lane数 `4`
- 触摸屏驱动 `无`
- 背光控制方式 `适配24pin的VLEDA、VLEDK`

### 开发Kernel 驱动

Kernel里面有一份已经调试好的参考代码：`panel-atk-md0550.c`，后续的屏幕驱动可以从这份驱动上面派生出来。

将`kernel/drivers/gpu/drm/panel/panel-atk-md0550.c`拷贝一份，并重命名为`panel-jc-050hd134.c`。

以下修改都是基于`panel-jc-050hd134.c`。

修改的`panel_simple_dsi_driver`的`name`属性字段为`panel-jc-050hd134`：
```C
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,
};
```

修改`drm_display_mode`结构体：
```C
static const struct drm_display_mode jc_050hd134_mode = { // 修改结构体名字为 jc_050hd134_mode
	.clock	     = 65000, //像素时钟 单位为khz，计算公式为：fps * (htotal + vtotal)
	.hdisplay    = 720, //可视区域宽
	.hsync_start = 720 + 32, //hdisplay + hfp
	.hsync_end   = 720 + 32 + 20, //hsync_start + hsa
	.htotal	     = 720 + 32 + 20 + 20, //hsync_end + hbp
	.vdisplay    = 1280, // 可视区域高
	.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, //极性
};
```

修改`panel_desc_dsi`结构体：
```C
static const struct panel_desc_dsi jc_050hd134 = { //结构体重命名为 jc_050hd134
	.desc =
		{
			.modes	   = &jc_050hd134_mode, // 指向时序结构体
			.num_modes = 1, // 这个面板有多少组时序，一般来讲只有一组
			.bpc	   = 8, // 每种色彩占多少bit？RGB888 即每个颜色占8bit，RGB666 即每个颜色占6bit
			.size = // 可视区域的物理大小，以mm（毫米）为单位
				{
					.width	= 62,
					.height = 110,
				},
			.connector_type = DRM_MODE_CONNECTOR_DSI,
		},
	.flags	= MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE, // DSI模式标志位，这里表明该面板工作在video模式下，并且是non-burst模式
	//更多标志位，请参见kernel/include/drm/drm_mipi_dsi.h
	.format = MIPI_DSI_FMT_RGB888,// 面板色彩类型
	.lanes	= 4, //数据lane数
};
```
`mode_flags`用于描述 DSI 主机与显示面板之间的数据传输方式及链路行为，具体如下：

| mode_flags | 描述 |
|--------|------|
| MIPI_DSI_MODE_VIDEO | 启用视频模式，主机持续按照显示时序发送像素数据。 |
| MIPI_DSI_MODE_VIDEO_BURST | 启用 Burst 视频模式，像素数据以突发方式发送。 |
| MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 启用 同步脉冲模式，HSYNC / VSYNC 以独立的脉冲形式发送，时序更接近传统 RGB 接口，兼容性最好，是多数 LCD 的推荐配置。 |
| MIPI_DSI_MODE_VIDEO_AUTO_VERT | 启用 自动垂直计数模式，DSI 控制器自动管理垂直方向的行计数。 |
| MIPI_DSI_MODE_VIDEO_HSE | 在 VSYNC 脉冲和垂直 Porch 区域 发送 HSYNC End 包。用于部分对时序要求严格的面板。 |
| MIPI_DSI_MODE_VIDEO_NO_HFP | 禁用 水平前沿。 |
| MIPI_DSI_MODE_VIDEO_NO_HBP | 禁用 水平后沿。 |
| MIPI_DSI_MODE_VIDEO_NO_HSA | 禁用 水平同步有效区。 |
| MIPI_DSI_MODE_VSYNC_FLUSH | 在 VSYNC 脉冲时 刷新显示 FIFO，防止旧数据残留。 |
| MIPI_DSI_MODE_NO_EOT_PACKET |在 HS 模式 下禁用 EoT（End of Transmission）包。部分面板或 PHY 对 EoT 包支持不完整，禁用可提升稳定性。 |
| MIPI_DSI_CLOCK_NON_CONTINUOUS | 启用 非连续时钟模式（Non-continuous Clock）。空闲期间 DSI Clock Lane 可进入 LP 状态，降低功耗。 |
| MIPI_DSI_MODE_LPM | 启用 低功耗模式（Low Power Mode, LP）传输数据。非常关键，绝大多数面板初始化命令（DCS / Generic Command）必须在 LP 模式下发送。 |
| MIPI_DSI_HS_PKT_END_ALIGNED | 高速模式（HS）下，多条 Data Lane 的数据包结束时间是否对齐。 |

其中，`MIPI_DSI_MODE_VIDEO`、`MIPI_DSI_MODE_VIDEO_SYNC_PULSE` 以及 `MIPI_DSI_MODE_LPM` 这三个标志位在实际项目中最为常用，也往往是必须配置的。

修改`dsi_of_match`，为了绑定设备树做准备：
```C
static const struct of_device_id dsi_of_match[] = {
	{.compatible = "jc-050hd134", .data = &jc_050hd134},
	{
		/* sentinel */
	}};
MODULE_DEVICE_TABLE(of, dsi_of_match);
```

修改`panel_simple_dsi_init`函数，这个函数实际上调用`dsi_dcs_write_seq`这个函数往面板里面写入`mipi`初始化序列。


------------------
初始化参数转换为驱动代码：

```C
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);
......
```

可以看到厂商提供的两组初始化序列里，第一个写入的是这组初始化序列的长度，转换为驱动代码时不用带上，会在写入时自动计算。

-------

如果您从屏幕厂商拿到的初始化序列形如下列这种：
```C
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
......
]
```
转换为驱动代码：

```C
	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);

```

在这里，我们以第一组序列`39 00 04 B9 FF 83 94`为例，进行解释：

>  39 表示本组初始化序列是按照`MIPI_DSI_DCS_LONG_WRITE` (0x39)数据类型写入，转换为驱动代码时不需要填写，会根据本组参数长度自动生成\
>  00 表示本次写入之后，睡眠0ms，可以调用`msleep(ms)`函数实现\
>  04 标识本次写入序列的长度，会自动生成不用填写\
>  后续的就是初始化序列了

对于上电-复位时序有要求的屏幕，可以修改`panel_simple_prepare`和`panel_simple_unprepare`这两个函数中的`gpio`行为，这两个函数分别对应初始化和反初始化状态。

---------------

修改`kernel/drivers/gpu/drm/panel/Kconfig`和`kernel/drivers/gpu/drm/panel/Makefile`

**Kconfig**:
```shell
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 support
	  MIPI DSI interface.
```
**Makefile**:
```shell
obj-$(CONFIG_DRM_PANEL_JC_050HD134) += panel-jc-050hd134.o
```

### 配置Kernel 设备树
在板级设备树，此处以`kernel/arch/arm64/boot/dts/hobot/x5-evb.dtsi`为例

在`mipi_dsi0`节点添加下列属性：

```C
&mipi_dsi0 {

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

	dsi_panel0@0 {
		compatible = "jc-050hd134"; //此处与 dsi_of_match 一致
		reg = <0>;

		pinctrl-names = "default";
		pinctrl-0 = <&lsio_gpio0_14>; // 与复位管脚相关
		reset-gpios = <&ls_gpio0_porta 14 GPIO_ACTIVE_HIGH>; // 与复位管脚相关 什么电平有效需要根据您的屏幕来决定
		backlight = <&dsi_backlight>; // 背光相关

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

};

```


添加`dsi_backlight`节点：
```C

&dsi_backlight {
	status = "okay";
	pwms = <&lpwm1 1 1000000>; //与硬件PCB有关，检查您的背光方波信号来源于哪里 这里表示背光信号来源于LPWM1_1
	// 其余属性和信息，请参考 kernel/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml
};

```

### 编译

请先参考4.1章搭建好编译环境，并能成功编译出镜像之后再做下面操作！

执行`./bd.sh boot menuconfig`进入内核的配置菜单，按照以下路径进入`Panels`编译选项：

```
Device Drivers  --->
	Graphics support  --->
		Display Panels  --->
```
找到`JC 050HD134 panel`，按下空格，将其作为模块编译。然后保存配置并退出。

执行`./bd.sh all` 编译生成镜像，然后将镜像烧录到板子，接上屏幕并上电。


### 测试
成功进入内核之后，执行以下命令加载驱动：

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

然后，执行 `dmesg` 命令可以在日志最后看到下面打印：

```shell
[ 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
```

当出现 `[drm] Initialized vs-drm 1.0.0 20191101 for 3e000000.disp_apb:display-subsystem on minor 0` 表示显示驱动加载成功。

执行`modetest -M vs-drm -c`查看`connectors`的状态：
```bash
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:
```
使用`modetest -M vs-drm -a -s 73@31:720x1280 -P 33@31:720x1280@NV12`命令进行测试

如果一切顺利，连接的屏幕将会亮起并显示下图的`pattern`（图片经过旋转）：

![modetest-pattern](_static/_images/25-LCD_Driver_Debug_Guide/pattern.png)