0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

驱动之路#04:LCD 驱动程序分析(基于RK3576)

BSP调试从0到1 来源:嵌入式分享 作者:嵌入式分享 2026-02-17 08:20 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

题图:河北太行山脉,山头密密麻麻都被太阳能板覆盖了。

欢迎关注,每周更新!☞

本合集分享的是,我当初学习Linux驱动的来时路——《《驱动之路》开篇:自序&前言》。

正文

在实际工作中,虽然并不需要我们从零编写 LCD 驱动程序,但必须掌握 LCD 驱动框架 —— 这是分析现有驱动、定位问题,以及在必要时修改驱动以适配硬件或需求的必修课。

分析之前,首先需要掌握字符设备驱动的基本框架,看前面文章《驱动之路#01:Hello World!》。LCD 驱动本质上也是字符设备驱动,理解了字符设备驱动的框架,有助于分析 LCD 驱动程序。

下面我们就运用前面掌握的字符设备驱动基本框架,以 RK3576 平台的 panel-simple.c 为例,浅浅分析 LCD 驱动。

驱动位置:kernel-6.1/drivers/gpu/drm/panel/panel-simple.c

如何阅读驱动程序,准确说是如何阅读字符设备驱动程序?阅读一个字符设备驱动程序,应该从它的入口函数开始看。

看到 panel-simple.c 的入口函数*register() 注册了 3 个驱动,说明该驱动程序同时兼容DPIDSI和SPI接口连接的 panel。

wKgZO2mTtV6ABC1gAADoM4XiIH8808.png

拿 panel_simple_dsi_driver 举例分析,继续往下看。当 driver 与 device(即 dts) 的 compatible 属性匹配就会调用 panel_simple_dsi_probe 函数。

wKgZO2mTtV6AV8ayAAHNHHsufxM945.png

只有我们弄清楚panel_simple_dsi_probe函数的具体操作就基本了解了panel_simple_dsi_driver驱动,这就是分析字符设备驱动程序的大致流程。

接下来,分析 panel_simple_dsi_probe 函数,直接看代码注释。

/** MIPI-DSI 面板探测函数* 负责初始化DSI接口的面板设备*/staticintpanel_simple_dsi_probe(structmipi_dsi_device *dsi){ /* 局部变量定义 */ structpanel_simple *panel;   // 通用面板数据结构 structdevice *dev = &dsi->dev; // 获取设备指针,简化后续访问 conststructpanel_desc_dsi *desc;// DSI面板描述符(预定义或动态) structpanel_desc_dsi *d;    // 动态分配的描述符指针 conststructof_device_id *id; // 设备树匹配结果 interr;            // 错误码 /* 1. 设备树匹配 - 检查设备是否在驱动支持列表中 */  id = of_match_node(dsi_of_match, dsi->dev.of_node); if(!id)   return-ENODEV; // 设备不在支持列表中,返回设备不存在错误 /* 2. 面板描述符处理 */ if(!id->data) {   /* 情况A: 设备树中定义了自定义面板,没有预定义描述符 */   // 动态分配DSI面板描述符内存    d = devm_kzalloc(dev,sizeof(*d), GFP_KERNEL);   if(!d)     return-ENOMEM; // 内存分配失败   /* 从设备树节点解析面板参数 */    err = panel_simple_dsi_of_get_desc_data(dev, d);   if(err) {      dev_err(dev,"failed to get desc data: %dn", err);     returnerr; // 参数解析失败    }  } /* 注意: 如果 id->data 不为空,表示使用预定义的描述符,跳过动态分配 */ /* 3. 选择最终使用的描述符 */  desc = id->data ? id->data : d; // 优先使用预定义,否则使用动态解析的描述符 /* 4. 调用通用面板探测函数进行基础初始化 */  err = panel_simple_probe(&dsi->dev, &desc->desc); if(err < 0)        return err;  // 通用面板初始化失败    /* 5. 获取面板数据结构并关联DSI设备 */    panel = dev_get_drvdata(dev);  // 从设备私有数据中获取panel_simple指针    panel->dsi = dsi;       // 将DSI设备指针保存到面板结构中,建立双向关联 /* 6. 背光设备处理 */ if(!panel->base.backlight) {   /* 如果面板没有背光设备,注册一个DCS背光设备 */   structbacklight_properties props; // 背光属性结构   /* 初始化背光属性 */    memset(&props,0,sizeof(props));    props.type = BACKLIGHT_RAW;   // 背光类型:原始模式    props.brightness =255;     // 初始亮度:最大值    props.max_brightness =255;   // 最大亮度:255级   /* 注册背光设备 */    panel->base.backlight =        devm_backlight_device_register(dev, dev_name(dev),                       dev, panel, &dcs_bl_ops,                       &props);   /* 错误处理 */   if(IS_ERR(panel->base.backlight)) {      err = PTR_ERR(panel->base.backlight);      dev_err(dev,"failed to register dcs backlight: %dn", err);     returnerr; // 背光设备注册失败    }  } /* 注意: 如果面板已有背光设备(如通过设备树引用),则跳过此步骤 */ /* 7. 配置DSI主机控制器参数 */  dsi->mode_flags = desc->flags; // 设置DSI工作模式标志  dsi->format = desc->format;   // 设置像素数据格式  dsi->lanes = desc->lanes;    // 设置数据通道数量 /* 8. 将面板附加到DSI主机 */  err = mipi_dsi_attach(dsi); if(err) {   /* 附件失败时的清理工作 */   structpanel_simple *panel = mipi_dsi_get_drvdata(dsi);    drm_panel_remove(&panel->base); // 从DRM子系统移除面板  } /* 9. 返回执行结果 */ returnerr; // 成功返回0,失败返回错误码}

我们在设备树中定义的屏参以及mipi 屏幕相关初始化代码。

wKgZO2mTtV6AH792AAK8_dj-zhw331.png

最终是通过函数 panel_simple_dsi_of_get_desc_data()和 panel_simple_of_get_desc_data()获取到的。

wKgZO2mTtV6ACTP8AARQ5GMJv50168.png

至此,mipi-dsi 屏幕驱动分析完毕!能分析阅读整体驱动框架和函数调用流程就行,不需要花大量时间和精力去解读每行代码解读,AI时代没多大意义。

最后附上两个比较重要的函数,供阅读参考。

附录:static int panel_simple_dsi_of_get_desc_data(struct device *dev,          struct panel_desc_dsi *desc){  struct device_node *np = dev->of_node;  u32val;  int err; // 1. 首先解析通用面板参数  err = panel_simple_of_get_desc_data(dev, &desc->desc); if(err)   returnerr; // 2. 设置连接器类型为DSI  desc->desc.connector_type = DRM_MODE_CONNECTOR_DSI; // 3. 从dts解析DSI特有参数 if(!of_property_read_u32(np,"dsi,flags", &val))    desc->flags =val; if(!of_property_read_u32(np,"dsi,format", &val))    desc->format =val; if(!of_property_read_u32(np,"dsi,lanes", &val))    desc->lanes =val; return0;}函数 panel_simple_dsi_of_get_desc_data( )中调用 panel_simple_dsi_of_get_desc_data( )获取面板 desc_data。static int panel_simple_of_get_desc_data(struct device *dev,          struct panel_desc *desc){  struct device_node *np = dev->of_node;  u32 bus_flags; constvoid *data;  int len;  int err; // 1. 解析显示时序 if(of_child_node_is_present(np,"display-timings")) {    struct drm_display_mode *mode;    mode = devm_kzalloc(dev, sizeof(*mode), GFP_KERNEL);   if(!mode)     return-ENOMEM;   // 从设备树解析DRM显示模式   if(!of_get_drm_display_mode(np, mode, &bus_flags,            OF_USE_NATIVE_MODE)) {      desc->modes = mode;      desc->num_modes =1;      desc->bus_flags = bus_flags;    }  }elseif(of_child_node_is_present(np,"panel-timing")) {    struct display_timing *timing;    struct videomode vm;    timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);   if(!timing)     return-ENOMEM;   // 从设备树解析显示时序   if(!of_get_display_timing(np,"panel-timing", timing)) {      desc->timings = timing;      desc->num_timings =1;     // 转换时序标志为总线标志      bus_flags =0;      vm.flags = timing->flags;      drm_bus_flags_from_videomode(&vm, &bus_flags);      desc->bus_flags = bus_flags;    }  } // 2. 解析基本面板属性 if(desc->num_modes || desc->num_timings) {    of_property_read_u32(np,"bpc", &desc->bpc);     // 每颜色位数    of_property_read_u32(np,"connector-type", &desc->connector_type);    of_property_read_u32(np,"bus-format", &desc->bus_format);// 总线格式    of_property_read_u32(np,"width-mm", &desc->size.width); // 物理宽度    of_property_read_u32(np,"height-mm", &desc->size.height);// 物理高度  } // 3. 解析时序延迟参数  of_property_read_u32(np,"prepare-delay-ms", &desc->delay.prepare);  of_property_read_u32(np,"enable-delay-ms", &desc->delay.enable);  of_property_read_u32(np,"disable-delay-ms", &desc->delay.disable);  of_property_read_u32(np,"unprepare-delay-ms", &desc->delay.unprepare);  of_property_read_u32(np,"reset-delay-ms", &desc->delay.reset);  of_property_read_u32(np,"init-delay-ms", &desc->delay.init); // 4. 解析初始化序列 data= of_get_property(np,"panel-init-sequence", &len); if(data) {    desc->init_seq = devm_kzalloc(dev, sizeof(*desc->init_seq), GFP_KERNEL);   if(!desc->init_seq)     return-ENOMEM;   // 解析初始化命令序列    err = panel_simple_parse_cmd_seq(dev,data, len, desc->init_seq);   if(err) {      dev_err(dev,"failed to parse init sequencen");     returnerr;    }  } // 5. 解析退出序列 data= of_get_property(np,"panel-exit-sequence", &len); if(data) {    desc->exit_seq = devm_kzalloc(dev, sizeof(*desc->exit_seq), GFP_KERNEL);   if(!desc->exit_seq)     return-ENOMEM;   // 解析退出命令序列    err = panel_simple_parse_cmd_seq(dev,data, len, desc->exit_seq);   if(err) {      dev_err(dev,"failed to parse exit sequencen");     returnerr;    }  } return0;}

(完)

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • lcd
    lcd
    +关注

    关注

    36

    文章

    4630

    浏览量

    177867
  • 驱动程序
    +关注

    关注

    19

    文章

    872

    浏览量

    50699
  • rk3576
    +关注

    关注

    1

    文章

    291

    浏览量

    1663
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    硬核进阶:RK3576 Android15 驱动与系统开发实战指南

    RK3576 探索之旅】系列文章导航及功能全景介绍(基于android14) 之前有出过系列的文章,但这是自己公司定制项目,暂时没有相关硬件,有需求可以私信我,这个系列主要还是驱动的开发,接下来
    的头像 发表于 01-26 22:29 788次阅读
    硬核进阶:<b class='flag-5'>RK3576</b> Android15 <b class='flag-5'>驱动</b>与系统开发实战指南

    迅为RK3576对比RK3568:AI算力与接口的全面跃迁

    迅为RK3576对比RK3568:AI算力与接口的全面跃迁
    的头像 发表于 12-22 15:24 2339次阅读
    迅为<b class='flag-5'>RK3576</b>对比<b class='flag-5'>RK</b>3568:AI算力与接口的全面跃迁

    迅为如何在RK3576上部署YOLOv5;基于RK3576构建智能门禁系统

    迅为如何在RK3576开发板上部署YOLOv5;基于RK3576构建智能门禁系统
    的头像 发表于 11-25 14:06 1929次阅读
    迅为如何在<b class='flag-5'>RK3576</b>上部署YOLOv5;基于<b class='flag-5'>RK3576</b>构建智能门禁系统

    RK3576驱动高端显控系统升级:多屏拼控与AI视觉融合解决方案

    下达任务指令,副屏监测 AI 分析结果,大屏实时展示各区域画面与运行状态,真正实现 “一屏决策,多屏联动”。 八路摄像头输入:实现多源视频融合与 AI 识别RK3576 原生支持八路摄像头输入
    发表于 11-21 17:51

    【作品合集】米尔RK3576开发板测评

    米尔RK3576开发板测评作品合集 产品介绍: RK3576 是瑞芯微一款面向AI市场推出的高性能处理器,它配备了四核Cortex-A72和四 核Cortex-A53 的 CPU,集成了6TOPS
    发表于 09-11 10:19

    【作品合集】灵眸科技EASY EAI Orin Nano(RK3576)开发板测评

    【EASY EAI Orin Nano(RK3576)开发板试用体验】01-开箱报告及开发环境准备 【EASY EAI Orin Nano(RK3576)开发板试用体验】02-拓展空间及内核分析
    发表于 09-09 09:59

    瑞芯微RK3576RK3576S有什么区别,性能参数配置与型号差异解析

    瑞芯微第二代8nm高性能AIOT平台RK3576家族再添新成员-RK3576S,先说结论:相较主型号的RK3576/RK3576J,性能略有缩减,而功耗有所降低。主要应用于商显终端、智
    的头像 发表于 08-14 23:57 2645次阅读
    瑞芯微<b class='flag-5'>RK3576</b>与<b class='flag-5'>RK3576</b>S有什么区别,性能参数配置与型号差异解析

    RK这2款旗舰芯片RK3588 PK RK3576,谁是最优选

    在 AI 边缘计算领域,瑞芯微的 RK3588 和 RK3576 都是备受瞩目的处理器。在中国半导体产业的版图中,瑞芯微作为国内 SoC 芯片领跑者,凭借其在处理器芯片设计领域的深厚积累和持续创新
    发表于 07-10 18:24

    Mpp支持RK3576

    想问下,https://github.com/rockchip-linux/mpp这里面支持RK3576么,看介绍没有提到说支持RK3576 目前是买了个rk3576的机顶盒,搭载了安卓14,想做安卓视频硬解。
    发表于 06-13 15:35

    RK3576 vs RK3588:为何越来越多的开发者转向RK3576

    (图形性能更强) 分析RK3576 的 A72+A53+M0 组合 在能效比上更优,适合需要长时间运行的设备(如 IoT、平板)。 RK3588 的 A76 架构 单核性能更强,适合高性能计算场景(如
    发表于 05-30 08:46

    适配多种系统,米尔瑞芯微RK3576核心板解锁多样化应用

    米尔电子发布的基于瑞芯微 RK3576 核心板和开发板,具备高性能数据处理能力、领先的AI智能分析功能、多样化的显示与操作体验以及强大的扩展性与兼容性,适用于多种应用场景。目前米尔电子为
    发表于 05-23 16:07

    基于RK3576开发板的人脸识别算法

    RK3576开发板展示人脸识别算法例程和API说明
    的头像 发表于 05-07 16:48 2892次阅读
    基于<b class='flag-5'>RK3576</b>开发板的人脸识别算法

    基于RK3576开发板的RTC使用说明

    文章主要展示RK3576开发板的RTC信息和快速上手例程
    的头像 发表于 05-07 15:04 2506次阅读
    基于<b class='flag-5'>RK3576</b>开发板的RTC使用说明

    基于RK3576开发板的PWN使用说明

    RK3576开发板使用PWN教程及Demo
    的头像 发表于 05-07 14:07 2458次阅读
    基于<b class='flag-5'>RK3576</b>开发板的PWN使用说明

    基于RK3576开发板的TF卡槽使用说明

    RK3576开发板使用TF卡槽
    的头像 发表于 05-07 09:24 2247次阅读
    基于<b class='flag-5'>RK3576</b>开发板的TF卡槽使用说明