1# MIPI DSI
2
3## 概述
4
5### 功能简介
6
7DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface (MIPI) Alliance)制定的规范,旨在降低移动设备中显示控制器的成本。它以串行的方式发送像素数据或指令给外设(通常是LCD或者类似的显示设备),或从外设中读取状态信息或像素信息;它定义了主机、图像数据源和目标设备之间的串行总线和通信协议。
8
9MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道都可以用于单向的高速传输,但只有第一个数据通道才可用于低速双向传输,从属端的状态信息、像素等是通过该数据通道返回。时钟通道专用于在高速传输数据的过程中传输同步时钟信号。
10
11图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。
12
13**图 1** DSI发送、接收接口
14
15![DSI发送、接收接口](figures/DSI发送-接收接口.png)
16
17DSI标准对应D-PHY、DSI、DCS规范,可分为四层:
18
19- PHY Layer
20
21    PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
22
23- Lane Management层
24
25    负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。
26
27- Low Level Protocol层
28
29    定义了如何组帧和解析以及错误检测等。
30
31- Application层
32
33    描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
34
35### 运作机制
36
37MIPI DSI软件模块各分层的作用为:
38
39- 接口层:提供打开设备、写入数据和关闭设备的接口。
40
41- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
42
43- 适配层:实现其它具体的功能。
44
45![](../public_sys-resources/icon-note.gif) **说明:**<br>核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
46
47**图 2** DSI无服务模式结构图
48
49![DSI无服务模式结构图](figures/无服务模式结构图.png)
50
51### 约束与限制
52
53由于使用无服务模式,MIPI_DSI接口暂不支持用户态使用。
54
55## 使用指导
56
57### 场景介绍
58
59MIPI DSI主要用于连接显示屏。
60
61### 接口说明
62
63MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/mipi_dsi_if.h64
65**表1** MIPI DSI API接口功能介绍
66
67| 功能分类 | 接口名 |
68| -------- | -------- |
69| DevHandle MipiDsiOpen(uint8_t id) | 获取MIPI&nbsp;DSI操作句柄 |
70| void MipiDsiClose(DevHandle handle) | 释放MIPI&nbsp;DSI操作句柄 |
71| int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg \*cfg) | 设置MIPI&nbsp;DSI相关配置 |
72| int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg \*cfg) | 获取当前MIPI&nbsp;DSI相关配置 |
73| void MipiDsiSetLpMode(DevHandle handle) | 设置MIPI&nbsp;DSI进入Low&nbsp;power模式 |
74| void MipiDsiSetHsMode(DevHandle handle) | 设置MIPI&nbsp;DSI进入High&nbsp;speed模式 |
75| int32_t MipiDsiTx(DevHandle handle, struct DsiCmdDesc \*cmd) | DSI发送指令 |
76| int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc \*cmd, int32_t readLen, uint8_t \*out) | MIPI&nbsp;DSI按期望长度回读数据 |
77
78### 开发步骤
79
80使用MIPI DSI的一般流程如下图所示。
81
82**图 3** MIPI DSI使用流程图
83
84![MIPI DSI使用流程图](figures/MIPI-DSI使用流程图.png)
85
86#### 获取MIPI DSI操作句柄
87
88在进行MIPI DSI进行通信前,首先要调用MipiDsiOpen获取操作句柄,该函数会返回指定通道ID的操作句柄。
89
90```c
91DevHandle MipiDsiOpen(uint8_t id);
92```
93
94**表 2** MipiDsiOpen的参数和返回值描述
95
96| **参数** | **参数描述** |
97| -------- | -------- |
98| id | uint8_t类型,MIPI&nbsp;DSI通道ID |
99| **返回值** | **返回值描述** |
100| NULL | 获取失败 |
101| 设备句柄 | 获取到指令通道的操作句柄,&nbsp;类型为DevHandle |
102
103假设系统中的MIPI DSI通道为0,获取该通道操作句柄的示例如下:
104
105
106```c
107DevHandle mipiDsiHandle = NULL;  // 设备句柄
108chnId = 0;                       // MIPI DSI通道ID
109
110// 获取操作句柄
111mipiDsiHandle = MipiDsiOpen(chnId);
112if (mipiDsiHandle == NULL) {
113    HDF_LOGE("MipiDsiOpen: mipi dsi open fail.\n");
114    return NULL;
115}
116```
117
118#### MIPI DSI相应配置
119
120- 写入MIPI DSI配置
121
122    ```c
123    int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg);
124    ```
125
126    **表 3** MipiDsiSetCfg的参数和返回值描述
127
128    | **参数** | **参数描述** |
129    | -------- | -------- |
130    | handle | DevHandle类型,操作句柄 |
131    | cfg | 结构体指针类型,MIPI&nbsp;DSI相应配置buf&nbsp;指针 |
132    | **返回值** | **返回值描述** |
133    | HDF_SUCCESS | 设置MIPI DSI配置成功 |
134    | 负数 | 设置MIPI DSI配置失败 |
135
136    ```c
137    int32_t ret;
138    struct MipiCfg cfg = {0};
139
140    // 当前对接的屏幕配置如下
141    cfg.lane = DSI_4_LANES;
142    cfg.mode = DSI_CMD_MODE;
143    cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS;
144    cfg.format = FORMAT_RGB_24_BIT;
145    cfg.pixelClk = 174;
146    cfg.phyDataRate = 384;
147    cfg.timingInfo.hsaPixels = 50;
148    cfg.timingInfo.hbpPixels = 55;
149    cfg.timingInfo.hlinePixels = 1200;
150    cfg.timingInfo.yResLines = 1800;
151    cfg.timingInfo.vbpLines = 33;
152    cfg.timingInfo.vsaLines = 76;
153    cfg.timingInfo.vfpLines = 120;
154    cfg.timingInfo.xResPixels = 1342;
155    // 写入配置数据
156    ret = MipiDsiSetCfg(mipiDsiHandle, &cfg);
157    if (ret != HDF_SUCCESS) {
158        HDF_LOGE("MipiDsiSetCfg: set mipi cfg fail, ret:%d\n", ret);
159        return ret;
160    }
161    ```
162
163- 获取当前MIPI DSI的配置
164
165    ```c
166    int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg);
167    ```
168
169    **表 4** MipiDsiGetCfg的参数和返回值描述
170
171    | **参数** | **参数描述** |
172    | -------- | -------- |
173    | handle | DevHandle类型,操作句柄 |
174    | cfg | 结构体指针,MIPI&nbsp;DSI相应配置buf&nbsp;指针 |
175    | **返回值** | **返回值描述** |
176    | HDF_SUCCESS | 获取当前MIPI DSI的配置成功 |
177    | 负数 | 获取当前MIPI DSI的配置失败 |
178
179    ```c
180    int32_t ret;
181    struct MipiCfg cfg;
182    memset(&cfg, 0, sizeof(struct MipiCfg));
183    ret = MipiDsiGetCfg(mipiDsiHandle, &cfg);
184    if (ret != HDF_SUCCESS) {
185        HDF_LOGEMipiDsiGetCfg: get mipi cfg fail, ret:%d!\n", ret);
186        return ret;
187    }
188    ```
189
190#### 发送/回读控制指令
191
192- 发送指令
193
194    ```c
195    int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd);
196    ```
197
198    **表 5** MipiDsiTx的参数和返回值描述
199
200    | **参数** | **参数描述** |
201    | -------- | -------- |
202    | handle | DevHandle类型,操作句柄 |
203    | cmd | 结构体指针类型,需要发送的指令数据指针 |
204    | **返回值** | **返回值描述** |
205    | HDF_SUCCESS | 发送成功 |
206    | 负数 | 发送失败 |
207
208    ```c
209    int32_t ret;
210    struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
211    if (cmd == NULL) {
212        return HDF_FAILURE;
213    }
214    cmd->dtype = DTYPE_DCS_WRITE;
215    cmd->dlen = 1;
216    cmd->payload = OsalMemCalloc(sizeof(uint8_t));
217    if (cmd->payload == NULL) {
218        HdfFree(cmd);
219        return HDF_FAILURE;
220    }
221    *(cmd->payload) = DTYPE_GEN_LWRITE;
222    MipiDsiSetLpMode(mipiHandle);
223    ret = MipiDsiTx(mipiHandle, cmd);
224    MipiDsiSetHsMode(mipiHandle);
225    if (ret != HDF_SUCCESS) {
226        HDF_LOGE("MipiDsiTx: mipi dsi tx fail, ret:%d\n", ret);
227        HdfFree(cmd->payload);
228        HdfFree(cmd);
229        return ret;
230    }
231    HdfFree(cmd->payload);
232    HdfFree(cmd);
233    ```
234
235- 回读指令
236
237    ```c
238    int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out);
239    ```
240
241    **表 6** MipiDsiRx的参数和返回值描述
242
243    | **参数** | **参数描述** |
244    | -------- | -------- |
245    | handle | DevHandle类型,操作句柄 |
246    | cmd | 结构体指针类型,需要回读的指令数据指针 |
247    | readLen | uint32_t类型,期望回读的数据长度 |
248    | out | uint8_t类型指针,回读的数据 |
249    | **返回值** | **返回值描述** |
250    | HDF_SUCCESS | 获取成功 |
251    | 负数 | 获取失败 |
252
253    ```c
254    int32_t ret;
255    uint8_t readVal = 0;
256
257    struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc));
258    if (cmdRead == NULL) {
259        return HDF_FAILURE;
260    }
261    cmdRead->dtype = DTYPE_DCS_READ;
262    cmdRead->dlen = 1;
263    cmdRead->payload = OsalMemCalloc(sizeof(uint8_t));
264    if (cmdRead->payload == NULL) {
265        HdfFree(cmdRead);
266        return HDF_FAILURE;
267    }
268    *(cmdRead->payload) = DDIC_REG_STATUS;
269    MipiDsiSetLpMode(mipiDsiHandle);
270    ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal);
271    MipiDsiSetHsMode(mipiDsiHandle);
272    if (ret != HDF_SUCCESS) {
273        HDF_LOGE("MipiDsiRx: mipi dsi rx fail, ret:%d\n", ret);
274        HdfFree(cmdRead->payload);
275        HdfFree(cmdRead);
276        return HDF_FAILURE;
277    }
278    HdfFree(cmdRead->payload);
279    HdfFree(cmdRead);
280    ```
281
282
283#### 释放MIPI DSI操作句柄
284
285MIPI DSI使用完成之后,需要释放操作句柄,释放句柄的函数如下所示:
286
287```c
288void MipiDsiClose(DevHandle handle);
289```
290
291该函数会释放掉由MipiDsiOpen申请的资源。
292
293**表 7** MipiDsiClose的参数和返回值描述
294
295| 参数 | 参数描述 |
296| -------- | -------- |
297| handle | DevHandle类型,MIPI&nbsp;DSI操作句柄 |
298
299```c
300MipiDsiClose(mipiHandle); // 释放掉MIPI DSI操作句柄
301```
302
303## 使用实例
304
305本例拟对Hi3516DV300开发板上MIPI DSI设备进行操作。
306
307MIPI DSI完整的使用示例如下所示:
308
309```c
310#include "hdf_log.h"
311#include "mipi_dsi_if.h"
312#include "osal_mem.h"
313
314#define DTYPE_DCS_WRITE 0x05
315#define DTYPE_DCS_READ 0x06
316#define DTYPE_GEN_LWRITE 0x29
317#define DDIC_REG_STATUS 0x0A
318
319
320int32_t PalMipiDsiTestSample(void)
321{
322    uint8_t chnId;
323    int32_t ret;
324    DevHandle mipiDsiHandle = NULL;
325
326    // 设备通道编号
327    chnId = 0;
328    // 获取操作句柄
329    mipiDsiHandle = MipiDsiOpen(chnId);
330    if (mipiDsiHandle == NULL) {
331        HDF_LOGE("MipiDsiOpen: failed!\n");
332        return HDF_FAILURE;
333    }
334    // 配置相应参数
335    struct MipiCfg cfg = {0};
336    cfg.lane = DSI_2_LANES;
337    cfg.mode = DSI_VIDEO_MODE;
338    cfg.format = FORMAT_RGB_24_BIT;
339    cfg.burstMode = VIDEO_BURST_MODE;
340    cfg.timing.xPixels = 480;           // 480: width
341    cfg.timing.hsaPixels = 10;          // 10: horizontal sync porch
342    cfg.timing.hbpPixels = 20;          // 20: horizontal back porch
343    cfg.timing.hlinePixels = 530;       // 530: horizontal total width
344    cfg.timing.vsaLines = 2;            // 2: vertiacl sync width
345    cfg.timing.vbpLines = 14;           // 14: vertiacl back porch
346    cfg.timing.vfpLines = 16;           // 16: vertiacl front porch
347    cfg.timing.ylines = 960;            // 960: height
348    cfg.timing.edpiCmdSize = 0;         // 0 : no care
349    cfg.pixelClk = 31546;               // 31546: pixel clk
350    cfg.phyDataRate = 379;              // 379: mipi clk
351    // 写入配置数据
352    ret = MipiDsiSetCfg(mipiDsiHandle, &cfg);
353    if (ret != 0) {
354        HDF_LOGE("PalMipiDsiTestSample: set mipi dsi cfg fail, ret:%d\n", ret);
355        return ret;
356    }
357    // 发送PANEL初始化指令
358    struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
359    if (cmd == NULL) {
360        return -1;
361    }
362    cmd->dataType = DTYPE_DCS_WRITE;
363    cmd->dataLen = 1;
364    cmd->payload = OsalMemCalloc(sizeof(uint8_t));
365    if (cmd->payload == NULL) {
366        OsalMemFree(cmd);
367        return -1;
368    }
369    *(cmd->payload) = DTYPE_GEN_LWRITE;
370    MipiDsiSetLpMode(mipiDsiHandle);
371    ret = MipiDsiTx(mipiDsiHandle, cmd);
372    MipiDsiSetHsMode(mipiDsiHandle);
373    if (ret != HDF_SUCCESS) {
374        HDF_LOGE("PalMipiDsiTestSample: mipi dsi tx fail, ret:%d\n", ret);
375        OsalMemFree(cmd->payload);
376        OsalMemFree(cmd);
377        return ret;
378    }
379    OsalMemFree(cmd->payload);
380    OsalMemFree(cmd);
381    // 回读panel状态寄存器
382    uint8_t readVal = 0;
383    struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc));
384    if (cmdRead == NULL) {
385        return -1;
386    }
387    cmdRead->dataType = DTYPE_DCS_READ;
388    cmdRead->dataLen = 1;
389    cmdRead->payload = OsalMemCalloc(sizeof(uint8_t));
390    if (cmdRead->payload == NULL) {
391        OsalMemFree(cmdRead);
392        return -1;
393    }
394    *(cmdRead->payload) = DDIC_REG_STATUS;
395    MipiDsiSetLpMode(mipiDsiHandle);
396    ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal);
397    MipiDsiSetHsMode(mipiDsiHandle);
398    if (ret != HDF_SUCCESS) {
399        HDF_LOGE("PalMipiDsiTestSample: mipi dsi rx fail, ret:%d\n", ret);
400        OsalMemFree(cmdRead->payload);
401        OsalMemFree(cmdRead);
402        return ret;
403    }
404    OsalMemFree(cmdRead->payload);
405    OsalMemFree(cmdRead);
406    HDF_LOGD("PalMipiDsiTestSample: mipi dsi tests end");
407    // 释放MIPI DSI设备句柄
408    MipiDsiClose(mipiDsiHandle);
409    return ret;
410}
411```
412