OpenHarmony南向之LCD显示屏
OpenHarmony南向之LCD显示屏
概述
LCD(Liquid Crystal Display)驱动,通过对显示器上下电、初始化显示器驱动IC(Integrated Circuit)内部寄存器等操作,使其可以正常工作。
HDF Display驱动模型
LCD器件驱动是显示框架最底层的部分。
向上对接到 Display 公共 HAL 层,辅助 HDI 的实现。通过Display-HDI对图形服务提供各类驱动能力接口;
向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。
所以驱动LCD主要在于LCD panel器件驱动。
LCD接口通常可分为MIPI DSI接口、TTL接口和LVDS接口,这里以rk3568平台为例,是常见的mipi接口的显示屏
驱动主要分为2大部分:hcs配置和panel驱动
vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs
drivers/hdf_core/framework/model/display/driver/panel/
下面就这2大部分分别来简单分析下
hcs配置及流程
hcs配置
display :: host {
hostName = "display_host";
device_hdf_drm_panel :: device {
device0 :: deviceNode {
policy = 0;
priority = 197;
preload = 0;
moduleName = "HDF_DRMPANEL";
}
}
device_hdf_disp :: device {
device0 :: deviceNode {
policy = 2;
priority = 196;
permission = 0660;
moduleName = "HDF_DISP";
serviceName = "hdf_disp";
}
}
device_lcd :: device {
...
device3 :: deviceNode {
policy = 0;
priority = 100;
preload = 0;
moduleName = "LCD_ILI9881_ST_5P5";
}
}
device_pwm_bl :: device {
device0 :: deviceNode {
policy = 0;
priority = 95;
preload = 0;
moduleName = "PWM_BL";
deviceMatchAttr = "pwm_bl_dev";
}
}
device_backlight :: device {
device0 :: deviceNode {
policy = 2;
priority = 90;
preload = 0;
permission = 0660;
moduleName = "HDF_BL";
serviceName = "hdf_bl";
}
}
}
大致流程
从上面的hcs配置可以看出,这里使用的是 DRM Panel,根据 priority
值,驱动加载的顺序依次为:
- HDF_BL
- PWM_BL
- LCD
- HDF_DISP
- HDF_DRMPANEL
这个顺序是跟驱动代码里面的逻辑是相匹配的,LCD驱动在init的时候会注册panel(RegisterPanel
),然后在HDF_DISP驱动的init中会通过 GetPanelManager
获取注册的panelManager, 并使用 panelManager
来 DispManagerInit
,HDF_DRMPANEL驱动在init的时候会通过 GetDispManager
获取在前面初始化的 DispManager,并使用DRM框架的接口来init和add相应drm_panel,且将mipi接口联系起来,整个过程是环环相扣。
panel驱动
Panel驱动中最核心的主要是实现以下接口:
struct PanelData {
struct HdfDeviceObject *object;
int32_t (*init)(struct PanelData *panel); /*panel的软件初始化*/
int32_t (*on)(struct PanelData *panel); /*主要控制上电*/
int32_t (*off)(struct PanelData *panel); /*主要控制下电*/
int32_t (*prepare)(struct PanelData *panel); /*亮屏硬件时序初始化, 通过MIPI DCS发送亮屏初始化序列*/
int32_t (*unprepare)(struct PanelData *panel); /*灭屏硬件时序初始化, 通过MIPI DCS发送灭屏代码*/
struct PanelInfo *info; /*Panel的一些参数。见下方*/
enum PowerStatus powerStatus;
struct PanelEsd *esd;
struct BacklightDev *blDev;
void *priv;
};
在驱动初始化接口中实例化后使用 RegisterPanel
接口向display模型注册该panel驱动
以 ili9881_st_5p5
MIPI显示屏驱动为例来看看大致的流程:
...
/*获取panel节点,rk一般都使用的 simple panel*/
panelNode = of_find_compatible_node(NULL, NULL, "simple-panel-dsi");
if (panelNode == NULL) {
HDF_LOGE("%s of_find_compatible_node fail", __func__);
goto FAIL;
}
/*通过panel节点进一步获取mipi dsi的节点*/
panel_dev->dsiDev = of_find_mipi_dsi_device_by_node(panelNode);
if (panel_dev->dsiDev == NULL) {
HDF_LOGE("%s of_find_mipi_dsi_device_by_node fail", __func__);
goto FAIL;
}
/*获取power节点*/
panel_dev->supply = devm_regulator_get(&panel_dev->dsiDev->dev, "power");
if (panel_dev->supply == NULL) {
HDF_LOGE("Get regulator fail");
goto FAIL;
}
/*获取其他gpio控制节点,需根据硬件配置**/
panel_dev->enable_gpio = devm_gpiod_get_optional(&panel_dev->dsiDev->dev, "enable", GPIOD_ASIS);
if (IS_ERR(panel_dev->enable_gpio)) {
HDF_LOGE("get enable_gpio fail");
goto FAIL;
}
...
/*初始化PanelData mipi_dsi_device结构体,下面会展开详细说明*/
PanelResInit(panel_dev);
...
/*注册到Panel Manager*/
if (RegisterPanel(&panel_dev->panel) != HDF_SUCCESS) {
HDF_LOGE("RegisterPanel fail");
goto FAIL;
}
...
PanelResInit
函数主要是关键结构体(struct PanelData
和 struct mipi_dsi_device
)的实例化:
/*mipi dsi 的一些参数*/
panel_dev->dsiDev->lanes = 4; /* 4: dsi,lanes ,number of active data lanes */
panel_dev->dsiDev->format = MIPI_DSI_FMT_RGB888; // dsi,format pixel format for video mode MIPI_DSI_FMT_RGB888
panel_dev->dsiDev->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM \
| MIPI_DSI_MODE_EOT_PACKET);
panel_dev->panel.info = &g_panelInfo; /*Panel的一些参数。见下方*/
panel_dev->panel.init = PanelInit; /*panel的软件初始化,这里为空函数*/
panel_dev->panel.on = PanelOn; /*主要控制上电*/
panel_dev->panel.off = PanelOff; /*主要控制下电*/
panel_dev->panel.prepare = PanelPrepare; /*亮屏硬件时序初始化, 通过MIPI DCS发送亮屏初始化代码*/
panel_dev->panel.unprepare = PanelUnprepare; /*灭屏硬件时序初始化, 通过MIPI DCS发送灭屏代码*/
panel_dev->panel.priv = panel_dev->dsiDev;
关于 MIPI DCS 可以查看我以前的文章: https://notes.z-dd.online/2020/12/30/MIPI-DSI%E4%B9%8BDCS%E7%9B%B8%E5%85%B3/
这里,在v3.2版本的代码里,最终还是调用的是Linux下mipi dsi相关的接口,并没有使用OH实现的mipi的驱动接口
panel相关的硬件参数定义,这个主要跟显示屏硬件有关,这些参数的具体含义可参看我以前的博文:
static struct PanelInfo g_panelInfo = {
.width = 720, /* width */
.height = 1280, /* height */
.hbp = 40, /* horizontal back porch */
.hfp = 40, /* horizontal front porch */
.hsw = 10, /* horizontal sync width */
.vbp = 15, /* vertical back porch */
.vfp = 10, /* vertical front porch */
.vsw = 36, /* vertical sync width */
.clockFreq = 75000000, /* clock */
.pWidth = 68, /* physical width */
.pHeight = 121, /* physical height */
.connectorType = DRM_MODE_CONNECTOR_DPI, /* DRM_MODE_CONNECTOR_DPI=17 */
.blk = { BLK_PWM, MIN_LEVEL, MAX_LEVEL, DEFAULT_LEVEL },
};