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)
-
{
-
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");
-
}
主函数:
-
#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;
-
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;
-
}
相关阅读
删除亚马逊Kindle电子书的DRM,将AZW转为PDF格式文档
首先声明:我个人并不提倡盗版电子书,毕竟Kindle是一款很不错的电子设备,其次,亚马逊拥有电子书籍的版权,使用盗版是对电商、作者权益
Widevine DRM介绍:https://blog.csdn.net/tody_guo/article/details/38345199Widevine DRM Android流程分析:https://blog.csdn.net
亚马逊kindle,awzDRM解锁 读前请注意:因为解锁去除 Kindle 电子书的 DRM 保护需要用到购买该书的 Kindle 设备的序列号,所以请确保
经常下到一些WMV格式需要许可证的片子,实在令本人非常反感,心想:这么好看的片子不下又可惜,下了嘛看起来又麻烦,而且不一定每个时间段