linux drm 架构及linux drm 架构 之代码分析
转自:
http://manpages.ubuntu.com/manpages/utopic/man7/drm-kms.7.html
根据自己的理解来转述一下:
摘要:
DRM 是linux 下的图形渲染架构(Direct Render Manager) , 具体的说是显卡驱动的一种架构(驱动如何玩? 把功能封装成 open/close/ioctl 等标准接口,应用程序调用这些接口来驱动设备)。
作为显卡,最基本的功能就是把用户的绘图输出到显示屏上,DRM 如何去实现呢,先看看DRM 把“这件事”给你概括的几个基本要素:
画布(FrameBuffer) , 绘图现场(CRTC) , 输出转换器(Encoder) , 连接器(Connector) , 然后就到显示屏了
1 画布( FrameBuffer )
对计算机来说,FrameBuffer 就是一块驱动和应用层都能访问的内存,当然画图之前要有一定的格式化,比方说我可以规定什么样的色彩模式(RGB24 , I420 , YUUV 等等), 分辨率是多大,还有啥参数,那就要到绘图现场去看了 :p
2 绘图现场(CRTC)
简写翻译过来是阴级摄像管上下文,在DRM 里 CRTC 就表示显示输出的上下文了,首先 CRTC 内指一个 FrameBuffer 地址, 外连一个Encoder。 它们俩之间如何沟通? 这就是显示模式(ModeSet)要做的事情,ModeSet 包括了像前面提到的色彩模式 , 还有说显示的时序(timings , ModeLines 等都代表了这个意西)等, 通常时序可以按以下来表达
PCLK HFP HBP HSW X_RES VFP VBP VSW Y_RES
像素时钟 水平前回扫 水平后回扫 水平同步头 水平有效长度 垂直前回扫 垂直后回扫 垂直同步头 垂直有效长度
一个CRTC 可以连接多个 Encoder , 干啥用,实现复制屏幕功能。
3 输出转换器(Encoder )
想想 CRT 这种土疙瘩就够复杂了,我们的显卡很牛奔的可以连接各种不同的设备,显然输出需要不同的信号转换器,将内存的像素转换成显示器需要的信号(DVID , VGA , YPbPr , CVBS 等等……)
4 连接器 (Connector )
不是指物理线,回到DRM 这是一个抽象的数据结构 ,代表连接的显示设备,从这里我们可以得到设备的EDID , DPMS 连接状态等.
5 显示面(Planner)
咦,怎么多出来一个。我也很呐闷,以上的东东不够地干活? 其实很多创新往往源于人对现实界的不满足。你又要看文字学习,又要看电影打游戏, 还有厉害的可以一边聊天一边看电影。 这里对立出来两个概念,像文字交互这种小范围更新的Graphics 模式,和全幅更新速度奇快的 Video 模式,这两种模式将显卡的使用拉上了两个极端。
于是 Planner 的概念就发挥了很好的作用,它给视频刷新提供了一条绿色通道,偶不和图形搞在一起了,偶是一个新的图层(或overlay),可以叠加在Graphic之上或之下,偶还可以缩放…
文档上说 Planner 也在 FrameBuffer 上,这个没关系,这里我们看出来 CRTC 里要显示的东东应该是一种组合(blending)了。
看懂了概念,下一篇来分析具体的数据结构和接口。
参考文档:
http://manpages.ubuntu.com/manpages/utopic/man7/drm-kms.7.html
http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_pinchart.pdf
http://landley.net/kdocs/htmldocs/drm.html
http://events.linuxfoundation.org/sites/events/files/slides/brezillon-drm-kms.pdf
http://elinux.org/images/7/71/Elce11_dae.pdf
一 上一篇介绍了 linux 的显示驱动drm 的架构,在这里按一定顺序回顾一下:
1 我把显示器连到显卡的DVI输出口, 这个连接抽象成 Connector
2 在 DVI 的 Connector 上驱动会分配 DVI 信号的 Encoder , 如果没分配, connector 资源上会找到 所有可用的 encoders
3 encoder 是为图像扫描现场 crtc 服务的, 驱动可能会给encoder 分配crtc , 或者从 encoder 的 possible_crtc 上能找到可用的
4 crtc 扫描现场要配置显示图像的物理内存区 fb
5 fb -> crtc -> encoder - > connector 这种关系绑定之后,绘图工作已经开始, 你可以在fb 上任意写画,然后立马得到显示!
6 然而为了避免图像撕裂,可以建立多 fb (缓冲) 通过 pageFlip 操作来刷新画图。
7 当然还有专为video 刷新用的plane , plane 也要绑定到 crtc 才能工作。
二 总结 + drm api 的使用:
api 使用参考 David Herrmann <dh.herrmann@googlemail.com> 的 drm-howto 以及 weston 还有 drm自带的 modetest 程序
drm api 核心配置就是要绑定一个 crtc 的关系 fb -> crtc -> encoder - > connector
我们来看“一”的回顾,咋是按照逆向的顺序?哈哈其实这样才顺理成章, api 如何用,往下看:
1 首先要打开drm 驱动模块,然后获取所有的资源
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
drmModeRes res = drmModeGetResources(fd);
res 里有啥, res 里告诉了有几个connector , 几个 encoder , 几个 crtc 等以及他们的id , 完全不成套!
2 从 connector 开始, 顺藤摸瓜
先获取 connecotr 的具体资源
drmModeConnector * conn = drmModeGetConnector(fd, res->connectors[i]);
conn资源里重要的有两部分,一部分是通过线缆读出来显示器的 "modes " 如 1920x1080@60 等, 当然你要选一个最喜欢的
另一部分是encoder 按照一章节的2顺序开始找 encoder (看 drmModeConnector 的定义) :p
3 给encoder 寻找合适的 crtc
按照一章节的 3 顺序找 crtc
4 为 crtc 创建fb
drm 的例子 modetest 写的很详细, ARGB 可以直接 drmModeAddFB , 多平面的fb 可以用 drmModeAddFB2
5 绑定
核心四元组 < fb , crtc , conn , mode >
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode);
6 pageFlip
extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data);
这个的玩法得好好记下,
首先 poll (drm_fd ) 可以收到 drm 的 POLLIN 消息, 消息里面无非两种一种是 VBLANK , 一种是 pageFlip complete
其次 收到 消息后必须要 调用drmHandleEvent(drm_fd , &evctx); 来处理消息 ,记得必须把你的 pageFlip 处理函数填到evctx里,
pageFlip 处理函数里干啥呢, pageFlip 一帧结束了,当然要 pageFlip 下一帧!
7 plane
plane 的玩法没搞明白,尝试了下,使用多缓冲 调 drmModeSetPlane 来换页可以实现视频的播放,但遇到两个问题:
其一是 SetPlane 的时机, 如何等待一个显示器的场同步? 看了 vblank 的code 感觉 vblank 是针对一个显卡的同步,但我有多个显示器呢?
其二是SetPlane 的运行耗时,我的 i3 cpu 执行一次用了 22 ~ 23 ms , 莫名其妙。
本想着plane 的yuv 通道可以节省资源,有这两大问题在就没法办了, pageFlip 的 fb 只能跟显示器相同颜色空间,并且通常是 ARGB !!
---------------------
作者:walletiger
来源:CSDN
原文:https://blog.csdn.net/walletiger/article/details/46596399
linux ModeSetting学习
2014年04月13日 20:35:24 Libresoft 阅读数:3725 标签: linux kernel mode setting drm
1、数据及数据结构:
Connector:代表显卡上的插口,有几个Connector表示会有几个输出。
CRTC:crt controller,负责将帧缓存中的数据传送到Connector,数据在传送到Connector之前会经过Encoder。 帧缓存 --> CRTC --> Encoder --> Connector --> 显示器。
FrameBuffer:在这里帧缓存并不是指的显存上的某一块区域,而是Linux DRM抽象出来的一个概念,用fb_id表示。
drm_mode_create_dumb:drmIoctl的参数,意思是请求内核创建缓存。 这个才是真正的内存块,可以使用mmap映射到程序虚拟内存中。Framebuffer的创建中必须指定一个缓存的id。
2、函数:
drmModeSetCrtc:最核心的函数之一,它负责将建立从帧缓存到Connector的关联。只有调用它,显示器才能显示缓存中的数据。
drmModePageFlip:直译就是翻页,笔者的理解是 drm_mode_create_dumb类型的缓存其实是双缓存,只有调用此函数之后,crtc才能将之前写入的数据传送给显示器。
3、代码:
找到处于连接状态的Connector
drmModeConnector* FindConnector(int fd)
{
drmModeRes *resources = drmModeGetResources(fd); //drmModeRes描述了计算机所有的显卡信息:connector,encoder,crtc,modes等。
if (!resources)
{
return NULL;
}
drmModeConnector* conn = NULL;
int i = 0;
for (i = 0; i < resources->count_connectors; i++)
{
conn = drmModeGetConnector(fd, resources->connectors[i]);
if (conn != NULL)
{
//找到处于连接状态的Connector。
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0 && conn == NULL)
{
break;
}
else
{
drmModeFreeConnector(conn);
}
}
}
drmModeFreeResources(resources);
return conn;
}
查找与Connector匹配的Crtc
int FindCrtc(int fd, drmModeConnector *conn)
{
drmModeRes *resources = drmModeGetResources(fd);
if (!resources)
{
fprintf(stderr, "drmModeGetResources failed\n");
return -1;
}
unsigned int i, j;
for (i = 0; i < conn->count_encoders; ++i)
{
drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
if (NULL != enc)
{
for (j = 0; j < resources->count_crtcs; ++j)
{
// connector下连接若干encoder,每个encoder支持若干crtc,possible_crtcs的某一位为1代表相应次序(不是id哦)的crtc可用。
if ((enc->possible_crtcs & (1 << j)))
{
int id = resources->crtcs[j];
drmModeFreeEncoder(enc);
drmModeFreeResources(resources);
return id;
}
}
drmModeFreeEncoder(enc);
}
}
drmModeFreeResources(resources);
return -1;
}
绘制一张全色的图:
void SetColor(unsigned char *dest, int stride, int w, int h)
{
struct color {
unsigned r, g, b;
};
struct color ccs[] = {
{ 255, 0, 0 },
{ 0, 255, 0 },
{ 0, 0, 255 },
{ 255, 255, 0 },
{ 0, 255, 255 },
{ 255, 0, 255 }
};
static int i = 0;
unsigned int j, k, off;
unsigned int r = 255;
unsigned int g = 1;
unsigned int b = 1;
for (j = 0; j < h; ++j)
{
for (k = 0; k < w; ++k)
{
off = stride * j + k * 4;
*(uint32_t*)&(dest[off]) = (ccs[i].r << 16) | (ccs[i].g << 8) | ccs[i].b;
}
}
i++;
printf("draw picture\n");
}
主函数:
#define _FILE_OFFSET_BITS 64
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int ret, fd;
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
{
/* Probably permissions error */
fprintf(stderr, "couldn't open %s, skipping\n", "");
return -1;
}
drmSetMaster(fd);
drmModeConnectorPtr connector = FindConnector(fd);
int width = connector->modes[0].hdisplay;
int height = connector->modes[0].vdisplay;
printf("display is %d*%d.\n", width, height);
int crtcid = FindCrtc(fd, connector);
struct drm_mode_create_dumb creq;
memset(&creq, 0, sizeof(creq));
creq.width = width;
creq.height = height;
creq.bpp = 32;
creq.flags = 0;
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
if (ret)
{
printf("create dumb failed!\n");
}
uint32_t framebuffer = -1;
uint32_t stride = creq.pitch;
//使用缓存的handel创建一个FB,返回fb的id:framebuffer。
ret = drmModeAddFB(fd, width, height, 24, 32, creq.pitch, creq.handle, &framebuffer);
if (ret)
{
printf("failed to create fb\n");
return -1;
}
struct drm_mode_map_dumb mreq; //请求映射缓存到内存。
mreq.handle = creq.handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret)
{
printf("map dumb failed!\n");
}
// 猜测:创建的缓存位于显存上,在使用之前先使用drm_mode_map_dumb将其映射到内存空间。
// 但是映射后缓存位于内核内存空间,还需要一次mmap才能被程序使用。
unsigned char* buf = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);
if (buf == MAP_FAILED)
{
printf("mmap failed!\n");
}
memset(buf, 255, creq.size);
//一切准备完毕,只差连接在一起了!
ret = drmModeSetCrtc(fd, crtcid, framebuffer, 0, 0, &connector->connector_id, 1, connector->modes);
if (ret)
{
fprintf(stderr, "failed to set mode: %m\n");
return -1;
}
int cc = 0;
while (cc < 5)
{
SetColor(buf, stride, width, height);
drmModePageFlip(fd, crtcid, framebuffer, DRM_MODE_PAGE_FLIP_EVENT, 0);
cc++;
sleep(2);
}
printf("over\n");
getchar();
close(fd);
exit(0);
return ret;
}
linux drm 架构及linux drm 架构 之代码分析相关推荐
- linux安全策略查询代码,Linux多安全策略和动态安全策略框架模块代码分析报告(14)...
函数名称 函数功能 selinux_set_mapping() 计算参数map中客体类别的数量,并将map中字符形式的类别-权限映射转换为数值形式的类别权限映射 map_class() 将客体类别在策 ...
- Linux Alsa声卡驱动(2):代码分析
一:初始化/注册声卡设备 (1)注册ALSA kernel\sound\core:sound.cint __init alsa_sound_init(void) {... ...if (registe ...
- Linux内核4.14版本——drm框架分析(1)——drm简介
目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...
- 【Linux 内核 内存管理】内存管理架构 ① ( 内存管理架构组成 | 用户空间 | 内核空间 | MMU 硬件 | Linux 内核架构层次 | Linux 系统调用接口 )
文章目录 一.内存管理架构组成 ( 用户空间 | 内核空间 | MMU 硬件 ) 二.Linux 内核架构层次 三.Linux 系统调用接口 一.内存管理架构组成 ( 用户空间 | 内核空间 | MM ...
- Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能: 1 ...
- linux与安卓系统目录,android系统架构及源码目录结构
1. android系统架构: android系统架构采用了分层架构的思想,如下图所示,从上到下共4层,分别为:应用程序层.应用程序框架层.系统库和android运行时层.linux内核层. 每层功能 ...
- Linux 之八 完整嵌入式 Linux 环境、(交叉)编译工具链、CPU 体系架构、嵌入式系统构建工具
最近,工作重心要从裸机开发转移到嵌入式 Linux 系统开发,由于之前对嵌入式 Linux 环境并不是很了解,因此,第一步就是需要了解如何搭建一个完整的嵌入式 Linux 环境.现在将学习心得记录 ...
- linux目录结构来源6,Linux入门基础 #6 Linux系统目录架构
Linux入门基础 #6 Linux系统目录架构 /bin 即binary,用于保存各种(二进制)可执行文件. 在CLI中输入执行的命令都保存在这里. 此目录下的所有命令都是普通权限用户也可执行的. ...
- linux驱动架构变化,Linux网卡驱动架构分析
一.网卡驱动架构 由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动. 二.重要数据结构 1.Linux内核中每一个网卡由一个net_device结构来描述. ...
最新文章
- mvc html的扩展,关于asp.net mvc 3:Razor无法正确呈现HtmlHelper扩展方法
- python简单代码演示效果-10分钟教你用python 30行代码搞定简单手写识别!
- [转]]将 ASP.NET MVC3 Razor 项目部署到虚拟主机中
- 互联网协议入门(一)
- linux oracle脚本编写,Linux 脚本编写基础(一)--语法
- 激怒开源社区,微软悄悄删除2500行功能代码后致歉:已恢复!
- 敏捷开发般若敏捷系列之九:敏捷开发与本能反应
- chrome jsp 显示不正常_selenium+java谷歌浏览器 网站打开不正常
- IronPython for ASP.NET 部署注意事项
- Topcoder SRM 656 (Div.1) 250 RandomPancakeStack - 概率+记忆化搜索
- Git问题解决:warning: Pulling without specifying how to reconcile divergent branches is discouraged. You
- Linux系统下强制删除文件
- BeEF-XSS详细使用教程
- 桃源网盘php,桃源居业主自建论坛 - Powered by PHPWind
- c++ IO多路复用
- 推荐系统学习笔记-FNN
- (附Matlab程序)(一)基于DCT编码的图像压缩:显示灰度图像 反余弦变换恢复图 DCT变换图 余弦变换系数图
- ctr 平滑_根据样本数进行点击率CTR的修正 - Wilson CTR
- HDU 4545 (模拟) 魔法串
- VMware卸载辛酸历程