[Linux外设驱动详解]RK3588 U-Boot 启动 Logo 显示流程详解

RK3588 U-Boot 启动 Logo 显示流程详解

目录

  1. 概述
  2. 显示子系统架构
  3. Logo显示流程
  4. 关键数据结构
  5. BMP图像处理
  6. VOP2显示控制器
  7. 设备树配置
  8. 时序图
  9. 源码路径参考

概述

RK3588 的 U-Boot 启动 Logo 显示功能基于 DRM (Direct Rendering Manager) 子系统实现,支持多种显示接口(HDMI、eDP、MIPI、LVDS等)和多种格式的 BMP Logo 图片。

主要特性

特性 描述
显示接口 HDMI0/1, eDP0/1, MIPI0/1, LVDS0/1, RGB, BT1120/656
VOP VOP2 (Video Output Processor 2) - 支持4个Video Port
BMP格式 8bpp(RLE8压缩), 24bpp, 32bpp
显示模式 全屏(Fullscreen), 居中(Center)
Logo来源 Resource分区, Distro分区, 内置编译

显示子系统架构

┌─────────────────────────────────────────────────────────────────────┐
│                     RK3588 显示子系统架构                            │
└─────────────────────────────────────────────────────────────────────┘

   ┌─────────────────────────────────────────────────────────────────┐
   │                      应用层 (U-Boot 命令)                       │
   │  rockchip_show_logo  |  rockchip_show_bmp  |  bmp info         │
   └─────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
   ┌─────────────────────────────────────────────────────────────────┐
   │                    DRM Display Framework                        │
   │  rockchip_display.c (显示子系统管理)                            │
   │  ├─ display_init()     初始化显示设备                           │
   │  ├─ display_logo()     显示Logo                                 │
   │  ├─ load_bmp_logo()    加载BMP图片                              │
   │  └─ display_set_plane() 设置显示平面                            │
   └─────────────────────────────────────────────────────────────────┘
                                   │
        ┌──────────────────────────┼──────────────────────────┐
        │                          │                          │
        ▼                          ▼                          ▼
   ┌─────────┐              ┌─────────┐              ┌─────────┐
   │ VOP2    │              │ Panel   │              │ Bridge  │
   │ (CRTC)  │◄────────────►│ (Connector)◄─────────┤ (HDMI/  │
   │         │              │         │              │ MIPI/   │
   │ Overlay │              │ ┌─────┐│              │ eDP)    │
   │ Cluster │              │ │ LCD ││              └─────────┘
   │ Smart   │              │ └─────┘│
   │ Layer   │              │ ┌─────┐│
   │         │              │ │ HDMI││
   │         │              │ └─────┘│
   └─────────┘              └─────────┘
        │                          │
        └──────────┬───────────────┘
                   │
                   ▼
   ┌─────────────────────────────────────────────────────────────────┐
   │                     显示输出                                     │
   │  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐            │
   │  │ HDMI│  │ eDP │  │ MIPI│  │ LVDS│  │ RGB  │            │
   │  └──────┘  └──────┘  └──────┘  └──────┘  └──────┘            │
   └─────────────────────────────────────────────────────────────────┘

Logo显示流程

1. 整体流程

┌────────────────────────────────────────────────────────────────────┐
│                       Logo 显示完整流程                             │
└────────────────────────────────────────────────────────────────────┘

   U-Boot 启动
      │
      ▼
┌──────────────────────────────────────────────────────────────────┐
│  1. rockchip_display_probe()                                     │
│     drivers/video/drm/rockchip_display.c:1871                    │
│     ├─ 解析设备树 display-subsystem                               │
│     ├─ 解析 route 节点获取显示路径                                │
│     ├─ 为每个显示路径创建 display_state                           │
│     ├─ 关联 CRTC (VOP2) 和 Connector (Panel/Bridge)               │
│     ├─ 读取 Logo 配置 (logo,uboot / logo,kernel)                 │
│     └─ 初始化显示缓冲区                                           │
└──────────────────────────────────────────────────────────────────┘
      │
      ▼
┌──────────────────────────────────────────────────────────────────┐
│  2. rockchip_show_logo()                                         │
│     drivers/video/drm/rockchip_display.c:1412                    │
│     遍历所有 display_state,为每个显示设备加载并显示 Logo         │
└──────────────────────────────────────────────────────────────────┘
      │
      ▼
┌──────────────────────────────────────────────────────────────────┐
│  3. load_bmp_logo()                                              │
│     drivers/video/drm/rockchip_display.c:1251                    │
│     ├─ 检查 logo cache,已加载则直接返回                          │
│     ├─ 读取 BMP 头部 (512 bytes)                                  │
│     ├─ 确定图片来源:                                              │
│     │   ├─ FROM_RESOURCE: resource 分区                          │
│     │   ├─ FROM_DISTRO: distro 分区                              │
│     │   └─ FROM_INTERNEL: 编译内置                               │
│     ├─ 读取完整 BMP 数据到显示缓冲区                              │
│     ├─ 解码 BMP (如果不是 16/32 bpp)                              │
│     └─ 填充 logo_info 结构体                                      │
└──────────────────────────────────────────────────────────────────┘
      │
      ▼
┌──────────────────────────────────────────────────────────────────┐
│  4. display_logo()                                               │
│     drivers/video/drm/ockchip_display.c:997                      │
│     ├─ display_init()                                            │
│     │   ├─ 初始化 Connector (Panel/Bridge)                       │
│     │   ├─ 获取显示时序 (display_get_timing)                      │
│     │   ├─ 验证时序 (display_mode_valid)                          │
│     │   ├─ 初始化 CRTC (VOP2)                                    │
│     │   └─ 设置显示模式                                          │
│     ├─ 根据 logo bpp 设置 crtc format                            │
│     ├─ 计算 src_rect (Logo尺寸) 和 crtc_rect (显示位置)          │
│     ├─ display_check() 检查配置                                  │
│     ├─ display_set_plane() 设置显示平面                          │
│     └─ display_enable() 启用显示输出                             │
└──────────────────────────────────────────────────────────────────┘
      │
      ▼
   Logo 显示完成

2. 显示子系统初始化

文件位置: drivers/video/drm/rockchip_display.c:1871

static int rockchip_display_probe(struct udevice *dev)
{
   
   
    struct video_priv *uc_priv = dev_get_uclass_priv(dev);
    struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);

    // 1. 初始化显示缓冲区
    init_display_buffer(plat->base);

    // 2. 解析 display-subsystem/route 节点
    route_node = dev_read_subnode(dev, "route");
    ofnode_for_each_subnode(node, route_node) {
   
   
        if (!ofnode_is_available(node))
            continue;

        // 3. 获取连接器 endpoint
        phandle = ofnode_read_u32_default(node, "connect", -1);
        ep_node = of_find_node_by_phandle(phandle);

        // 4. 通过 endpoint 找到对应的 CRTC (VOP)
        vop_node = of_get_parent(port_parent_node);
        uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC,
                                      np_to_ofnode(vop_node), &crtc_dev);
        crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev);

        // 5. 获取 Connector (Panel/Bridge)
        conn = rockchip_of_get_connector(np_to_ofnode(ep_node));

        // 6. 读取 Logo 配置
        if (!ret && !ofnode_read_string_index(node, "logo,uboot", 0, &name))
            memcpy(s->ulogo_name, name, strlen(name));

        if (!ret && !ofnode_read_string_index(node, "logo,kernel", 0, &name))
            memcpy(s->klogo_name, name, strlen(name));

        // 7. 读取显示模式
        ofnode_read_string_index(node, "logo,mode", 0, &name);
        if (!strcmp(name, "fullscreen"))
            s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
        else
            s->logo_mode = ROCKCHIP_DISPLAY_CENTER;

        // 8. 创建 display_state 并加入链表
        s = malloc(sizeof(*s));
        s->conn_state.connector = conn;
        s->crtc_state.crtc = crtc;
        list_add_tail(&s->head, &rockchip_display_list);
    }

    // 9. 预初始化显示设备
    display_pre_init();

    return 0;
}

3. Logo加载流程

文件位置: drivers/video/drm/rockchip_display.c:1251

static int load_bmp_logo(struct logo_info *logo, const char *bmp_name)
{
   
   
    struct rockchip_logo_cache *logo_cache;
    struct bmp_header *header;
    void *dst = NULL, *pdst;
    int size;

    // 1. 查找或创建 logo cache
    logo_cache = find_or_alloc_logo_cache(bmp_name);
    if (logo_cache->logo.mem) {
   
   
        memcpy(logo, &logo_cache->logo, sizeof(*logo));
        return 0;  // 已缓存,直接返回
    }

    // 2. 读取 BMP 头部
    header = malloc(RK_BLK_SIZE);  // 512 bytes

#ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE
    len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE);
    if (len == RK_BLK_SIZE)
        logo_source = FROM_RESOURCE;
#endif

    if (rockchip_read_distro_logo(header, bmp_name, RK_BLK_SIZE) == 0)
        logo_source = FROM_DISTRO;
    else {
   
   
        header = (struct bmp_header *)logo_bmp;
        logo_source = FROM_INTERNEL;
    }

    // 3. 获取 BMP 信息
    logo->bpp = get_unaligned_le16(&header->bit_count);
    logo->width = get_unaligned_le32(&header->width);
    logo->height = get_unaligned_le32(&header->height);
    size = get_unaligned_le32(&header->file_size);

    // 4. 分配显示缓冲区
    pdst = get_display_buffer(size);

    // 5. 读取完整 BMP 数据
    if (logo_source == FROM_RESOURCE)
        rockchip_read_resource_file(pdst, bmp_name, 0, size);
    else if (logo_source == FROM_DISTRO)
        rockchip_read_distro_logo(pdst, bmp_name, size);
    else
        pdst = (void*)logo_bmp;  // 内置

    // 6. 解码 BMP (如果不是 16/32 bpp)
    if (!can_direct_logo(logo->bpp)) {
   
   
        // 需要解码,使用 bmpdecoder
        logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp;
        dst_size = logo->width * logo->height * logo->bpp >> 3;
        dst = get_display_buffer(dst_size);

        bmpdecoder(pdst, dst, logo->bpp);  // 解码为 RGB565/RGB888
        logo->offset = 0;
        logo->ymirror = 0;
    } else {
   
   
        // 16/32 bpp 可以直接使用
        logo->offset = get_unaligned_le32(&header->data_offset);
        if (reserved == BMP_PROCESSED_FLAG)
            logo->ymirror = 0;
        else
            logo->ymirror = 1;
    }

    logo->mem = dst;

    // 7. 缓存 logo 信息
    memcpy(&logo_cache->logo, logo, sizeof(*logo));

    // 8. 刷新缓存
    flush_dcache_range((ulong)dst, ALIGN((ulong)dst + dst_size, CONFIG_SYS_CACHELINE_SIZE));

    return ret;
}

4. Logo显示流程

文件位置: drivers/video/drm/rockchip_display.c:997

static int display_logo(struct display_state *state)
{
   
   
    struct crtc_state *crtc_state = &state->crtc_state;
    struct connector_state *conn_state = &state->conn_state;
    struct logo_info *logo = &state->logo;
    int hdisplay, vdisplay, ret;

    // 1. 初始化显示设备
    ret = display_init(state);
    if (!state->is_init || ret)
        return -ENODEV;

    // 2. 根据 logo bpp 设置格式
    switch (logo->bpp) {
   
   
    case 16:
        crtc_state->format = ROCKCHIP_FMT_RGB565;
        break;
    case 24:
        crtc_state->format = ROCKCHIP_FMT_RGB888;
        break;
    case 32:
        crtc_state->format = ROCKCHIP_FMT_ARGB8888;
        break;
    default:
        printf("can't support bmp bits[%d]\n", logo->bpp);
        return -EINVAL;
    }

    // 3. 设置源矩形 (Logo尺寸)
    hdisplay = conn_state->mode.crtc_hdisplay;
    vdisplay = conn_state->mode.vdisplay;
    crtc_state->src_rect.w = logo->width;
    crtc_state->src_rect.h = logo->height;
    crtc_state->src_rect.x = 0;
    crtc_state->src_rect.y = 0;
    crtc_state->ymirror = logo->ymirror;
    crtc_state->rb_swap = 0
内容概要:本文深入解析了RK3588芯片在DRM框架下实现HDMI热插拔检测(HPD)的技术原理与实现机制。文章首先介绍RK3588芯片的强大性能及其在智能安防、工业控制、边缘计算等领域的广泛应用,随后详细阐述HDMI接口基础与热插拔功能的工作原理。重点剖析了HPD的硬件信号传输机制、热插拔事件的检测逻辑与判断准则,并系统讲解了Linux内核中DRM框架的架构组成(如libdrm、KMS、GEM)及其在图形显示管理中的核心作用。进一步,文章揭示了DRM框架下HPD处理的关键流程,包括中断响应、状态检测与KMS事件触发,并强调设备树配置(如hpd-gpios属性)对HPD功能实现的重要性。最后通过实战演练,指导读者完成HPD的设备树配置、驱动代码分析与常见问题调试,涵盖信号不稳定、驱动加载失败、显示异常等问题的解决方案。; 适合人群:具备嵌入式系统开发经验,熟悉Linux内核驱动与设备树配置的中高级研发人员,尤其是从事显示系统开发RK3588平台开发的工程师。; 使用场景及目标:①掌握RK3588平台上HDMI热插拔检测的完整实现机制;②理解DRM框架下HPD中断处理流程与关键函数调用关系;③能够独立完成设备树配置、驱动调试及常见故障排查;④提升在实际项目中对显示子系统稳定性与可靠性的把控能力。; 阅读建议:建议结合RK3588开发板进行实践操作,边读边调试,重点关注设备树配置与驱动代码的对应关系,并利用示波器和串口工具辅助分析HPD信号与内核日志,以加深对热插拔检测全过程的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JustaUncle

一杯咖啡,换我肝到天亮!

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

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

打赏作者

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

抵扣说明:

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

余额充值