RK3588 HDMI 休眠唤醒性能优化实战:从 6 秒到 100 毫秒的极致优化

📝 前言

在嵌入式显示系统开发中,HDMI 休眠唤醒速度直接影响用户体验。本文记录了在 RK3588 平台上,将 HDMI 唤醒时间从 6 秒优化到 100 毫秒的完整过程,性能提升达到 98%

适用平台

  • 芯片:RK3588
  • 系统:Android 11
  • 内核:Linux 5.10

关键词RK3588 DRM DPMS HDMI优化 显示性能


🎯 问题描述

原始问题

在项目中使用 sysfs 的 status 节点控制 HDMI 显示器的休眠和唤醒:

# 休眠
echo off > /sys/devices/platform/display-subsystem/drm/card0/card0-HDMI-A-1/status

# 唤醒
echo detect > /sys/devices/platform/display-subsystem/drm/card0/card0-HDMI-A-1/status

遇到的性能问题

  • 🐌 休眠耗时:约 3 秒
  • 🐌 唤醒耗时:约 6-8 秒
  • 😫 用户体验极差,屏幕长时间黑屏

实测日志分析

休眠流程:
15:04:50.170  [hw_output] 写入 status off
15:04:50.290  [hw_output] 检测到断开事件 (+120ms)
15:04:53.039  [hwc-drm] 开始处理断开 (+2749ms) ← 瓶颈
15:04:53.123  [DrmDevice] PowerDown 完成 (+84ms)
总耗时: ~3秒

唤醒流程:
15:27:42.809  按下电源键
15:27:49.150  [hwc-drm] 收到热插拔事件 (+6341ms) ← 瓶颈
15:27:50.600  发送 hotplug 到 SurfaceFlinger (+1450ms)
15:27:51.175  VOP2 初始化完成 (+575ms)
总耗时: ~8秒

瓶颈定位:大量时间消耗在 EDID 读取和模式探测(fill_modes)。


🔍 深入分析

原有实现机制

查看内核代码 drivers/gpu/drm/drm_sysfs.c 中的 status_store 函数:

static ssize_t status_store(struct device *device,
                           struct device_attribute *attr,
                           const char *buf, size_t count)
{
    // ...
    if (old_force != connector->force || !connector->force) {
        // 问题:每次都执行完整的模式探测,包括 EDID 读取
        connector->funcs->fill_modes(connector,
                                     dev->mode_config.max_width,
                                     dev->mode_config.max_height);
    }
    // ...
}

性能瓶颈分析

  1. EDID 读取慢:通过 I2C 读取显示器 EDID 信息需要 1-2 秒
  2. 模式探测慢:解析 EDID 并构建支持的显示模式列表
  3. 热插拔流程重:触发完整的 HWC 显示重初始化
  4. 无缓存机制:每次唤醒都重新读取 EDID,即使显示器未改变

💡 优化方案

方案 1:Status 节点智能缓存

核心思想:利用模式缓存,跳过不必要的 EDID 读取。

实现逻辑

  • 休眠时:跳过 fill_modes,直接断开
  • 唤醒时
    • 检查是否有缓存的模式列表
    • 有缓存 → 直接使用,跳过 EDID 读取
    • 无缓存 → 执行完整探测(首次或更换显示器)

方案 2:DPMS 快速电源控制(推荐)⭐

核心思想:使用硬件级 DPMS(Display Power Management Signaling)直接控制电源。

关键优势

  • 硬件级控制:直接操作 VOP2 寄存器
  • 不触发热插拔:保持连接状态
  • 无需 EDID:完全跳过模式探测
  • 响应极快:<100ms

🔧 代码实现

一、内核层优化

文件drivers/gpu/drm/drm_sysfs.c

1.1 优化 status 节点(智能缓存)
static ssize_t status_store(struct device *device,
                           struct device_attribute *attr,
                           const char *buf, size_t count)
{
    struct drm_connector *connector = to_drm_connector(device);
    struct drm_device *dev = connector->dev;
    enum drm_connector_force old_force;
    int ret;

    ret = mutex_lock_interruptible(&dev->mode_config.mutex);
    if (ret)
        return ret;

    old_force = connector->force;

    // 解析命令
    if (sysfs_streq(buf, "detect"))
        connector->force = 0;
    else if (sysfs_streq(buf, "on"))
        connector->force = DRM_FORCE_ON;
    else if (sysfs_streq(buf, "off"))
        connector->force = DRM_FORCE_OFF;
    else
        ret = -EINVAL;

    if (old_force != connector->force || !connector->force) {
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d\n",
                      connector->base.id, connector->name,
                      old_force, connector->force);

        if (connector->force == DRM_FORCE_OFF) {
            /* 优化点1:休眠时跳过 fill_modes */
            connector->status = connector_status_disconnected;
            drm_sysfs_hotplug_event(dev);
        } else {
            /* 优化点2:唤醒时使用缓存 */
            bool need_full_detect = false;

            // 检查是否有缓存的模式
            if (list_empty(&connector->modes)) {
                // 无缓存,需要完整探测
                need_full_detect = true;
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] No cached modes, doing full detection\n",
                              connector->base.id, connector->name);
            } else if (old_force == DRM_FORCE_OFF) {
                // 从休眠唤醒,使用缓存
                connector->status = connector_status_connected;
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Fast resume with cached modes\n",
                              connector->base.id, connector->name);
            } else {
                // detect 命令强制完整探测
                need_full_detect = !connector->force;
            }

            if (need_full_detect) {
                // 只在必要时进行完整探测
                connector->funcs->fill_modes(connector,
                                             dev->mode_config.max_width,
                                             dev->mode_config.max_height);
            }

            // 总是发送热插拔事件
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乡野码圣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值