在OpenHarmony上适配图形显示【2】——调试display hdi的技巧
文章写作环境
openharmony 5.0.0
前提
1.drm内核态驱动ok,modetest atomic测试通过,详细请参考
- https://ost.51cto.com/posts/36377
- https://laval.csdn.net/64afc2bb8e3f043cd26d8082.html
2.芯片 gpu 内核态驱动ok
技巧
技巧1:关闭render_service的开机自动执行,手动执行render_service命令拉起render_service服务。
调试display hdi代码过程中,手动执行render_service命令拉起render_service服务。openharmony图像起来之后再回退补丁。这样就不会因为display部分没写好导致机器重启了。
这样也方便编display hdi的库,然后手动推进去,这样就不用重复编固件调试了
--- a/foundation/graphic/graphic_2d/graphic.cfg 2025-05-23 17:54:58.080877485 +0800
+++ b/foundation/graphic/graphic_2d/graphic.cfg 2025-05-23 17:54:42.130878310 +0800
@@ -24,7 +24,7 @@"services" : [{"name" : "render_service","path" : ["/system/bin/render_service"],
- "critical" : [1, 5, 60],
+ "critical" : [0, 0, 60],"importance" : -20,"uid" : "graphics","gid" : ["system", "tp_host", "data_reserve"],--- a/vendor/xxxx/xxxx/security_config/critical_reboot_process_list.json 2025-08-20 19:38:54.726204891 +0800
+++ b/vendor/xxxx/xxxx/security_config/critical_reboot_process_list.json 2025-08-14 18:07:51.455150508 +0800
@@ -18,7 +18,7 @@},{"name": "render_service",
- "critical": [1, 5, 60]
+ "critical": [0, 0, 60]},{"name": "storage_daemon",
技巧2:开启无线hdc
由于技巧1中选择了render_service手动执行,有时候会占用一个调试串口。
--- a/vendor/xxxx/xxxx/etc/para/hardware_xxxx.para 2025-08-14 18:45:28.000000000 +0800
+++ b/vendor/xxxx/xxxx/etc/para/hardware_xxxx.para 2025-08-22 11:39:48.650294809 +0800
@@ -17,3 +17,7 @@const.product.manufacturer=xxxxconst.product.software.version=xxxxconst.product.oharm.version=xxxx
+persist.hdc.mode=tcp
+persist.hdc.mode.usb=enable
+persist.hdc.mode.tcp=enable
+persist.hdc.port=55555
\ No newline at end of file
技巧3:设置屏幕常亮
有时候因为屏幕亮屏策略没调好的原因导致调试出现问题,调试display hdi的时候最好在源码里面设置屏幕常亮。
--- a/base/powermgr/power_manager/services/native/include/power_state_machine.h 2024-12-18 17:43:37.223539366 +0800
+++ b/base/powermgr/power_manager/services/native/include/power_state_machine.h 2024-12-24 19:25:31.256451651 +0800
@@ -30,7 +30,7 @@#include "hibernate_controller.h"#endif-#define DEFAULT_DISPLAY_OFF_TIME 30000
+#define DEFAULT_DISPLAY_OFF_TIME -1#define DEFAULT_SLEEP_TIME 5000namespace OHOS {--- a/base/powermgr/power_manager/services/native/profile/power_mode_config.xml 2024-12-18 17:43:37.223539366 +0800
+++ b/base/powermgr/power_manager/services/native/profile/power_mode_config.xml 2024-12-24 19:29:29.116384104 +0800
@@ -32,7 +32,7 @@--><switch_proxy version="1"><proxy id="600">
- <switch id="101" value="30000" recover_flag="0"/>
+ <switch id="101" value="-1" recover_flag="0"/><switch id="102" value="0" recover_flag="0"/><switch id="103" value="-1" recover_flag="0"/><switch id="107" value="1" recover_flag="0"/>
技巧4:自行编写一个可执行程序来 查看显示器激活状态
display hdi编写一共有两种策略
- 1.参考rk3568的来写,device/soc/rockchip/rk3568/hardware/display
- 缺点:实现难度较大。
- 2.参考模版来写,模版路径drivers/peripheral/display/hal/default_standard
- 缺点:缺少对双屏的处理逻辑,还有hdmi/dp屏幕的热插拔处理。
笔者建议是,先基于模板来适配,亮屏成功后,参考3568的来增加对多屏的处理,还有热插拔逻辑。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>// 将连接器类型转换为可读字符串
const char* connector_type_to_string(uint32_t type) {switch (type) {case DRM_MODE_CONNECTOR_Unknown: return "Unknown";case DRM_MODE_CONNECTOR_VGA: return "VGA";case DRM_MODE_CONNECTOR_DVII: return "DVI-I";case DRM_MODE_CONNECTOR_DVID: return "DVI-D";case DRM_MODE_CONNECTOR_DVIA: return "DVI-A";case DRM_MODE_CONNECTOR_Composite: return "Composite";case DRM_MODE_CONNECTOR_SVIDEO: return "S-Video";case DRM_MODE_CONNECTOR_LVDS: return "LVDS";case DRM_MODE_CONNECTOR_Component: return "Component";case DRM_MODE_CONNECTOR_9PinDIN: return "9-pin DIN";case DRM_MODE_CONNECTOR_DisplayPort: return "DisplayPort";case DRM_MODE_CONNECTOR_HDMIA: return "HDMI-A";case DRM_MODE_CONNECTOR_HDMIB: return "HDMI-B";case DRM_MODE_CONNECTOR_TV: return "TV";case DRM_MODE_CONNECTOR_eDP: return "eDP";case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";case DRM_MODE_CONNECTOR_DSI: return "DSI";default: return "Unknown";}
}// 将连接状态转换为可读字符串
const char* connection_status_to_string(uint32_t status) {switch (status) {case DRM_MODE_CONNECTED: return "Connected";case DRM_MODE_DISCONNECTED: return "Disconnected";case DRM_MODE_UNKNOWNCONNECTION: return "Unknown";default: return "Invalid";}
}// 扫描已连接的显示器
int scan_connected_displays(int fd) {drmModeResPtr resources;int i;// 获取DRM资源(连接器、CRTC、编码器等)resources = drmModeGetResources(fd);if (!resources) {fprintf(stderr, "Failed to get DRM resources\n");return -1;}printf("Scanning connected displays...\n");printf("Found %d connectors, %d CRTCs, %d encoders\n", resources->count_connectors, resources->count_crtcs, resources->count_encoders);// 遍历所有连接器(通常对应物理显示接口)for (i = 0; i < resources->count_connectors; i++) {drmModeConnectorPtr connector = drmModeGetConnector(fd, resources->connectors[i]);if (!connector) {fprintf(stderr, "Failed to get connector %d\n", resources->connectors[i]);continue;}printf("\nConnector %d: %s, Status: %s\n", connector->connector_id,connector_type_to_string(connector->connector_type),connection_status_to_string(connector->connection));printf(" Encoder ID: %d\n", connector->encoder_id);printf(" Modes available: %d\n", connector->count_modes);// 获取当前活动的编码器和CRTC信息if (connector->encoder_id) {drmModeEncoderPtr encoder = drmModeGetEncoder(fd, connector->encoder_id);if (encoder) {printf(" Encoder CRTC ID: %d\n", encoder->crtc_id);printf(" Encoder type: %d\n", encoder->encoder_type);if (encoder->crtc_id) {printf(" Active CRTC ID: %d\n", encoder->crtc_id);// 获取CRTC(CRT控制器)的详细信息drmModeCrtcPtr crtc = drmModeGetCrtc(fd, encoder->crtc_id);if (crtc) {printf(" CRTC mode: %s\n", crtc->mode_valid ? crtc->mode.name : "Invalid");printf(" CRTC position: %dx%d\n", crtc->x, crtc->y);drmModeFreeCrtc(crtc);}} else {printf(" No active CRTC assigned to encoder\n");}drmModeFreeEncoder(encoder);} else {printf(" Failed to get encoder %d\n", connector->encoder_id);}} else {printf(" No encoder assigned to connector\n");}drmModeFreeConnector(connector);}drmModeFreeResources(resources);return 0;
}int main() {int fd;uint64_t has_dumb;// 打开DRM设备(通常第一个显卡是card0)fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);if (fd < 0) {fprintf(stderr, "Failed to open DRM device\n");return 1;}// 检查是否支持dumb buffer功能(基本图形缓冲区管理)if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || !has_dumb) {fprintf(stderr, "DRM device does not support dumb buffers\n");close(fd);return 1;}// 扫描并显示连接的显示器信息scan_connected_displays(fd);close(fd);return 0;
}
ohos_executable("modeset_crt_info") {cflags_c = ["-Wno-unused-variable",]sources = ["modeset_atomic_crtc/modeset_crt_info.c",]deps = ["//third_party/libdrm:libdrm",]install_images = [ "system" ]install_enable = truepart_name = ""subsystem_name = ""
}
运行效果(rk3568):
详细列出了当前系统检测到的显示连接器和其配置状态,可以查看每个显示器是否被分配了CRTC和Encoder。 在调试display的时候,可以用此程序来查看显示器是否激活