LINUX DRM 入门一条龙

这篇具有很好参考价值的文章主要介绍了LINUX DRM 入门一条龙。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

#使用nanopiM3(s5p6818), LINUX 内核4.4.172. 使用HDMI接口显示图像.

#源码:usb2hdmi: 使用usb传输FB数据,hdmi接口输出.

#情景分析法, 使用modetest命令作为应用层的测试例程:modetest -M nexell -s 41@30:1280x720 #但我并不想阅读modetest的源码,这里使用大体相同的另一个libdrm例程.   #以下为仅保留框架的libdrm例程, 下面就基于此例程进行驱动代码分析.

    int dri_fd  = open("/dev/dri/card0",O_RDWR | O_CLOEXEC);

    ioctl(dri_fd, DRM_IOCTL_SET_MASTER, 0);
 
    res.fb_id_ptr=(uint64_t)res_fb_buf;
    res.crtc_id_ptr=(uint64_t)res_crtc_buf;
    res.connector_id_ptr=(uint64_t)res_conn_buf;
    res.encoder_id_ptr=(uint64_t)res_enc_buf;
    ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
 
    conn.connector_id=res_conn_buf[i];
    conn.modes_ptr=(uint64_t)conn_mode_buf;
    ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn);

    create_dumb.width = conn_mode_buf[0].hdisplay;
    create_dumb.height = conn_mode_buf[0].vdisplay;
    create_dumb.bpp = 32;
    ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);

    ioctl(dri_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb);

    map_dumb.handle=create_dumb.handle;
    ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb);

    fb_base[i] = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, dri_fd, map_dumb.offset);
    fb_w[i]=create_dumb.width;
    fb_h[i]=create_dumb.height;

    enc.encoder_id=conn.encoder_id;
    ioctl(dri_fd, DRM_IOCTL_MODE_GETENCODER, &enc);

    crtc.crtc_id=enc.crtc_id;
    ioctl(dri_fd, DRM_IOCTL_MODE_GETCRTC, &crtc);

    crtc.fb_id=cmd_dumb.fb_id;
    crtc.set_connectors_ptr=(uint64_t)&res_conn_buf[i];
    crtc.mode=conn_mode_buf[0];
    ioctl(dri_fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
 
    ioctl(dri_fd, DRM_IOCTL_DROP_MASTER, 0);
 
    /* 写FB */
    int x,y;
    for (i=0;i<100;i++)
    {
        int j;
        for (j=0;j<res.count_connectors;j++)
        {
            int col=(rand()%0x00ffffff)&0x00ff00ff;
            for (y=0;y<fb_h[j];y++)
                for (x=0;x<fb_w[j];x++)
                {
                    int location=y*(fb_w[j]) + x;
                    *(((uint32_t*)fb_base[j])+location)=col;
                }
        }
        usleep(100000);
    }
 
    return 0;
}

#1
int dri_fd  = open("/dev/dri/card0",O_RDWR | O_CLOEXEC);
#应用层open() => DRM  drm_stub_open() =>  nx_drm_driver->nx_drm_fops->drm_open
#此步骤只是初始化一些链表而已.

#2
ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
res.connector_id_ptr=(uint64_t)res_conn_buf;
#对应 drm_mode_getresources()
#遍历drm->mode_config.connector_list和drm->mode_config.crtc_list链表,
#找到所有的connector和crtc的ID,返回给应用程序.

#3    
#conn 为输入参数, 传给ioctl
conn.connector_id=res_conn_buf[i];
#这里保存#2步骤获得的connector_id, 即确定最终要显示图像的显示器(屏幕).
conn.modes_ptr=(uint64_t)conn_mode_buf;
ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn);
#此ioctl步骤, 最终调用drm_mode_getresources(), 获取该显示器所支持的显示模式,
#即是显示分辨率,刷新率等信息.
#驱动将根据传入的connector_id, 确定connector.
#当前情景是使用HDMI接口进行显示,驱动使用i2c总线读取显示器的EDID,并进行解析,见panel_hdmi_ops_get_modes().
#将得到的个个显示模式, 加入connector->modes队列. 见drm_mode_connector_list_update().

#3    
create_dumb.width = conn_mode_buf[0].hdisplay;
create_dumb.height = conn_mode_buf[0].vdisplay;
create_dumb.bpp = 32;
ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
#此ioctl最终调用 drm_mode_create_dumb_ioctl()
#应用层输入必须的参数: 长,宽,每个像素大小(bpp), 长x宽xBPP为期望申请的FB的大小.
#再进行一些对齐工作,即为最终要申请的FB的大小. 见nx_drm_gem_dma_alloc().

#4
cmd_dumb.handle=create_dumb.handle;
ioctl(dri_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb);
#对应drm_mode_addfb2()
#通过handle索引到对于的fb, 将此fb加入drm->mode_config.fb_list链表.

# 将此fb->base加入 drm->mode_config.crtc_idr 链表..., 并返回对应的ID号, 将此ID号返回应用程序.
# 见drm_framebuffer_init()

#5
ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb);
#对应drm_mode_mmap_dumb_ioctl()
#与mmap相关.

#6
fb_base[i] = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, dri_fd, map_dumb.offset);
#对应nx_drm_gem_fops_mmap(). 内存映射, 使得应用程序可以直接写FB, 无需任何write()和ioctrl().

#7
enc.encoder_id=conn.encoder_id;
ioctl(dri_fd, DRM_IOCTL_MODE_GETENCODER, &enc);
#根据connector所连接的encoder的ID,得到对应encoder的结构体.
crtc.crtc_id=enc.crtc_id;
ioctl(dri_fd, DRM_IOCTL_MODE_GETCRTC, &crtc);
#根据encoder结构体内的ctrc_id信息,获得对应的CRTC结构体.

#8
crtc.fb_id=cmd_dumb.fb_id;
crtc.set_connectors_ptr=(uint64_t)&res_conn_buf[i];
crtc.mode=conn_mode_buf[0];
ioctl(dri_fd, drm_ioctl_mode_setcrtc, &crtc);
#对应drm_mode_setcrtc()
#应用程序传入必须参数:FB_ID, connector_id(包含于set_connectors_ptr), 欲设置的显示模式(conn_mode_buf)
#1. 使用crtc_id 遍历drm->mode_config.crtc_list 找到对应的crtc.
#    使用connector_id 遍历 drm->mode_config.connector_list 找到对应 connector.
#   使用fb_id 遍历 drm->mode_config.crtc_idr 找到对应的 FB.
#2. 使用crtc,connector 找到对应的硬件寄存器, 使用conn_mode_buf的参数设置寄存器.
#3. 将FB的物理地址写入硬件寄存器, 以此确定要显示的内容. nx_mlc_set_rgblayer_address()
#4. 正常地进行剩余的寄存器设置,这些基本是固定设置.无需使用使用应用程序的传来的参数.

#总体概括:


#总的而已, 在set_crtc之前的所以操作, 是在收集crtc,encoder,connector.
#先确定connector_id, 根据connector_id找到对应的encoder_id, 根据encoder_id找到对应的crtc_id.
#这样,完整的图像信号传输路径就完成了.
#根据这些id找到对应的结构体.
#借助crtc,encoder结构体,可以找到对应的硬件的寄存器地址(这些地址读取自dts).
#而根据connector结构体, 可以获得connector支持的显示模式,
#应用程序便可以选择其中一种显示模式, 创建对应大小的FB, 并传给set_crtc(), 确定要显示的内容.
#补充:
#当前例程仅使用到一个plane,即一个FB. 当要使用第二个plane时, 需要使用到DRM框架的 drm_mode_setplane()函数.
#形成FB => plane => crtc的关系.

#DRM术语说明


#PLANE
#图层.可以多图层,分辨率不同,位置不同,相互叠加显示于屏幕上.
#譬如显示linux桌面, 会使用两个plane, 一个用于显示鼠标, 一个用于显示其他东西,桌面,图标,应用..
#当前情景仅使用一个plane.
#FB
#一个plane对应于一个fb.
#GEM
#分配FB.

#CONNECTOR
#屏幕. 譬如, HDMI屏幕, lvds屏幕. 即各种接口的屏幕.
#ENCODER
#图像信号转换器. 连接各种屏幕(connector), 将其他图像信号转为对应屏幕的信号.

#CRTC
#目前我认为crtc 在DRM框架上有以下两个作用:
#1. 保存显示模式的参数.分辨率,刷新率等.
#2. 作为图像信号传输的起点, 而connector为终点. 因为只是作为起点,可以对应于任何在图像信号传输路径
#   上处于起点的硬件.
#   图像信号传输路径上, 可以有多个crtc, encoder 和 connector.
#   譬如我使用的nanopiM3(s5p6818), 其硬件上的图像信号传输路径为一下:

    mlc(multi layer controler0) -> dpc(displayer controler0)--|
                                                              |-->hdmi encoder -> "hdmi 屏幕"
                                                              |-->lvds encoder -> "lvds 屏幕"
    mlc(multi layer controler1) -> dpc(displayer controler1)--|

#其中 mlc和dpc对应 crtc, 则这里会有两个crtc.
#mlc 是多图层控制器, 6818的mlc提供3个图层(plane)的支持, 图层按需开启和使用.
#mlc将图层混合后,传给dpc(显示控制器).

#关于DRM框架模块 与硬件的对应关系,这里举s3c2440为例子进行说明


#例子: s3c2440 仅拥有一个显示控制器 和一块RGB的屏幕.
        FB0,FB1 => plane => crtc("2440显示控制器") => encoder ("空")  => connector("RGB屏幕")
#2440 没有mlc多图层控制器, 所以只支持一个plane, 可能多图层混合的工作由软件完成吧?
#2440 没有encoder, 2440显示控制器 输出的是RGB信号, 只能接入RGB屏幕. 若添加上RGB转HDMI的HDMI_encoder,
#就可以接入HDMI屏幕了. 所以这里的encoder可以为空.

#DRM 框架示意  

             FB1 -> PLANE1 -> GEM -> FB0 -> PLANE0("图层") -> CRTC("多图层混合","显示控制,包含显示参数") -> ENCODER -> CONNECTOR       

########################################### #DRM驱动的初始化流程分析


#1. drm_core_init()
#   申请DRM_MAJOR(226) 为主设备号.

#2. panel_hdmi_probe()
#   是HDMI encoder设备的初始化流程.
#panel_hdmi_get_display()  获取dts上的HDMI的寄存器地址.
#hdmi_ops_open()  读状态寄存器, 开启HDMI电缆插入和拔出中断
#panel_hdmi_parse_dt() 获得与该HDMI encoder配合的i2c 控制器,将在以后用于读取EDID.

#3. nx_drm_bind()
# 是crtc(mlc, dpc)设备的初始化流程
# 创建  struct drm_device *drm_dev; 结构体, 此结构体相当于 一般设备驱动中的 struct device *dev...
# 初始化 drm_dev->mode_config 上的队列.
#  nx_drm_crtc_create() 创建crtc 结构体, 解析dts,获得该设备的寄存器地址.
# crtc结构体 加入drm_dev->mode_config->crtc_list.
# drm_minor_alloc() 获得次设备号, 设置设备类型为 legacy, 设备节点名为 "cardx"
# drm_minor_register() 注册字符设备.

#4 nx_drm_connector_attach()
# 创建    struct drm_connector *connector; 结构体, 加入drm_dev->mode_config->connector_list 链表.
# 创建    struct drm_encoder *encoder; 结构体, 加入drm_dev->mode_config->encoder_list 链表.
#  读HDMI状态寄存器, 判断HDMI电缆是否插入. hdmi_ops_is_connected()

# 详细的各个结构体的作用,及其包含的信息, 见drm_read.c · suiren/usb2hdmi - Gitee.com文章来源地址https://www.toymoban.com/news/detail-632340.html

到了这里,关于LINUX DRM 入门一条龙的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网站部署上线一条龙文档

    想要将django项目部署在服务器上,本质上需要三大部分: 将代码上传到服务器 在服务器上 获取代码、安装服务、配置环境 启动服务 上传代码的方式有很多种,例如:FTP工具、scp命令、rsync服务、svn等,不过目前公司主流的都是使用git+代码托管平台。 本地电脑,安装git并使

    2024年02月03日
    浏览(62)
  • MySQL学习路线一条龙

    在当前的IT行业,无论是校园招聘还是社会招聘,MySQL的重要性不言而喻。 面试过程中,MySQL相关的问题经常出现,这不仅因为它是最流行的关系型数据库之一,而且在日常的软件开发中,MySQL的应用广泛,尤其是对于Java后端开发者来说,熟练掌握MySQL已成为他们技术能力评估

    2024年04月08日
    浏览(34)
  • 鱼皮 C++ 学习路线一条龙!

    大家好,我是鱼皮 🧑🏻‍🦲。 最近有很多小伙伴私信问我有没有 C++ 的学习路线,其中甚至有刚高考完的朋友! 那就给大家分享一下吧~ 在编写本路线时,鱼皮参考了多位 C++ 专业大佬的建议,但仍做不到完美,有意见和想法欢迎指正。 语言特性 比较官方,仅供参考 C++

    2024年02月16日
    浏览(73)
  • 番外篇 萌新版开发交付一条龙(☆▽☆)

    学习了一段时间的django和vue,对于前后端开发有了一个初步的了解,这里记录一下编写的流程和思路,主要是为了后面如果遗忘从哪里开始操作做一个起步引导作用 参考下前面django的文档https://moziang.blog.csdn.net/article/details/130720709 1、安装django环境 目录结构 2、项目添加应用模

    2024年02月21日
    浏览(30)
  • JDK17、IDEA 2022【安装一条龙】

    本次安装 安装环境为:Windows11系统 jdk版本为:jdk-17.0.5 IDEA 版本为:ideaIU-2022.1.2 下载地址: 👉下载点我 双击下载的好的 .exe 文件 新建系统变量 配置Path 😇可直接复制 3. 🧪测试环境变量是否配置成功 🎊恭喜你,JDK 17安装成功了 下载地址: 👉下载点我 官网下载,选择适合

    2024年02月03日
    浏览(37)
  • python打包和反编译一条龙

    安装Pyinstaller 在文件所在的位置启动cmd,命令如下 其中 -F 参数代表制作独立的可执行程序。 w 是指程序启动的时候不会打开命令行。如果不加-w的参数,就会有黑洞洞的控制台窗口出来。比如在刚才的脚本里我加一行 print(\\\'Hello World!\\\') ,那么就不要放-w参数了,不然运行会报

    2024年02月19日
    浏览(33)
  • 单相PWM整流从硬件到软件一条龙

    首先很想吐槽国内开源环境,实在是无语,大家都不愿意分享资源,都需要花钱,主要是花钱也不一定能找到你想要的东西。今年的电赛电源题,到现在了,我都还没看到CSDN上有能让我看懂的东西。所以我和同伴一起从零开始学习PWM整流,直到实现,我打算免费共享出来,能

    2024年02月16日
    浏览(30)
  • 传奇开服一条龙GEE引擎登录器配置教程

    1、首先我们打开我们版本文件夹找到登录器文件夹进入找到GEE登录器配置器(MakeGameLogin.exe)打开 2、接下来开始填写我们的主列表和备列表,这时候我们要自己创建一个列表,因为登录器自带的列表肯定是读取不了的,我们在网站文件夹新建一个txt文本名称可以随意填写如

    2024年02月08日
    浏览(35)
  • Nginx 可视化神器!复杂配置一键生成,监控管理一条龙!

    nginxWebUI是一款图形化管理nginx配置的工具,可以使用网页来快速配置nginx的各项功能,包括http协议转发、tcp协议转发、反向代理、负载均衡、静态html服务器、ssl证书自动申请、续签、配置等。配置好后可一建生成nginx.conf文件,同时可控制nginx使用此文件进行启动与重载,完成

    2024年02月07日
    浏览(33)
  • Debian11下编译ADAravis和Motor模块的一条龙过程

    一年前整理的上面文,这几天重新走了一遍,有些地方会碰到问题,需要补充些环节,motor模块以前和areaDetector一条龙编译时,总是有问题,当时就没尝试了,这几天尝试了一下,流程再总结如下: 准备: Debian11的基础安装(我用的是网络安装的虚拟机,选安装包时选ssh服务

    2024年01月22日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包