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 的 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

 

 
  1. drmModeConnector* FindConnector(int fd)

  2. {

  3. drmModeRes *resources = drmModeGetResources(fd); //drmModeRes描述了计算机所有的显卡信息:connector,encoder,crtc,modes等。

  4. if (!resources)

  5. {

  6. return NULL;

  7. }

  8.  
  9. drmModeConnector* conn = NULL;

  10. int i = 0;

  11. for (i = 0; i < resources->count_connectors; i++)

  12. {

  13. conn = drmModeGetConnector(fd, resources->connectors[i]);

  14. if (conn != NULL)

  15. {

  16. //找到处于连接状态的Connector。

  17. if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0 && conn == NULL)

  18. {

  19. break;

  20. }

  21. else

  22. {

  23. drmModeFreeConnector(conn);

  24. }

  25. }

  26. }

  27.  
  28. drmModeFreeResources(resources);

  29. return conn;

  30. }

 

 

查找与Connector匹配的Crtc

 

 
  1. int FindCrtc(int fd, drmModeConnector *conn)

  2. {

  3. drmModeRes *resources = drmModeGetResources(fd);

  4. if (!resources)

  5. {

  6. fprintf(stderr, "drmModeGetResources failed\n");

  7. return -1;

  8. }

  9.  
  10. unsigned int i, j;

  11.  
  12. for (i = 0; i < conn->count_encoders; ++i)

  13. {

  14. drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);

  15. if (NULL != enc)

  16. {

  17. for (j = 0; j < resources->count_crtcs; ++j)

  18. {

  19. // connector下连接若干encoder,每个encoder支持若干crtc,possible_crtcs的某一位为1代表相应次序(不是id哦)的crtc可用。

  20. if ((enc->possible_crtcs & (1 << j)))

  21. {

  22. int id = resources->crtcs[j];

  23. drmModeFreeEncoder(enc);

  24. drmModeFreeResources(resources);

  25. return id;

  26. }

  27. }

  28.  
  29. drmModeFreeEncoder(enc);

  30. }

  31. }

  32.  
  33. drmModeFreeResources(resources);

  34. return -1;

  35. }

 

 

 

绘制一张全色的图:

 

 
  1. void SetColor(unsigned char *dest, int stride, int w, int h)

  2. {

  3. struct color {

  4. unsigned r, g, b;

  5. };

  6.  
  7. struct color ccs[] = {

  8. { 255, 0, 0 },

  9. { 0, 255, 0 },

  10. { 0, 0, 255 },

  11. { 255, 255, 0 },

  12. { 0, 255, 255 },

  13. { 255, 0, 255 }

  14. };

  15.  
  16. static int i = 0;

  17.  
  18. unsigned int j, k, off;

  19. unsigned int r = 255;

  20. unsigned int g = 1;

  21. unsigned int b = 1;

  22.  
  23. for (j = 0; j < h; ++j)

  24. {

  25. for (k = 0; k < w; ++k)

  26. {

  27. off = stride * j + k * 4;

  28. *(uint32_t*)&(dest[off]) = (ccs[i].r << 16) | (ccs[i].g << 8) | ccs[i].b;

  29. }

  30. }

  31.  
  32. i++;

  33.  
  34. printf("draw picture\n");

  35. }

 

 

主函数:

 

 
  1. #define _FILE_OFFSET_BITS 64

  2. #include

  3. #include

  4. #include

  5. #include

  6. #include

  7. #include

  8. #include

  9. #include

  10. #include

  11. int main(int argc, char *argv[])

  12. {

  13. int ret, fd;

  14. fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC | O_NONBLOCK);

  15. if (fd < 0)

  16. {

  17. /* Probably permissions error */

  18. fprintf(stderr, "couldn't open %s, skipping\n", "");

  19. return -1;

  20. }

  21.  
  22. drmSetMaster(fd);

  23.  
  24. drmModeConnectorPtr connector = FindConnector(fd);

  25. int width = connector->modes[0].hdisplay;

  26. int height = connector->modes[0].vdisplay;

  27.  
  28. printf("display is %d*%d.\n", width, height);

  29.  
  30. int crtcid = FindCrtc(fd, connector);

  31.  
  32. struct drm_mode_create_dumb creq;

  33. memset(&creq, 0, sizeof(creq));

  34.  
  35. creq.width = width;

  36. creq.height = height;

  37. creq.bpp = 32;

  38. creq.flags = 0;

  39.  
  40. ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);

  41. if (ret)

  42. {

  43. printf("create dumb failed!\n");

  44. }

  45.  
  46. uint32_t framebuffer = -1;

  47. uint32_t stride = creq.pitch;

  48.  
  49. //使用缓存的handel创建一个FB,返回fb的id:framebuffer。

  50. ret = drmModeAddFB(fd, width, height, 24, 32, creq.pitch, creq.handle, &framebuffer);

  51.  
  52. if (ret)

  53. {

  54. printf("failed to create fb\n");

  55. return -1;

  56. }

  57.  
  58.  
  59. struct drm_mode_map_dumb mreq; //请求映射缓存到内存。

  60. mreq.handle = creq.handle;

  61.  
  62. ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);

  63. if (ret)

  64. {

  65. printf("map dumb failed!\n");

  66. }

  67.  
  68. // 猜测:创建的缓存位于显存上,在使用之前先使用drm_mode_map_dumb将其映射到内存空间。

  69. // 但是映射后缓存位于内核内存空间,还需要一次mmap才能被程序使用。

  70. unsigned char* buf = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);

  71. if (buf == MAP_FAILED)

  72. {

  73. printf("mmap failed!\n");

  74. }

  75.  
  76. memset(buf, 255, creq.size);

  77.  
  78. //一切准备完毕,只差连接在一起了!

  79. ret = drmModeSetCrtc(fd, crtcid, framebuffer, 0, 0, &connector->connector_id, 1, connector->modes);

  80.  
  81. if (ret)

  82. {

  83. fprintf(stderr, "failed to set mode: %m\n");

  84. return -1;

  85. }

  86.  
  87. int cc = 0;

  88. while (cc < 5)

  89. {

  90. SetColor(buf, stride, width, height);

  91. drmModePageFlip(fd, crtcid, framebuffer, DRM_MODE_PAGE_FLIP_EVENT, 0);

  92. cc++;

  93. sleep(2);

  94. }

  95.  
  96. printf("over\n");

  97. getchar();

  98. close(fd);

  99. exit(0);

  100. return ret;

  101. }

你可能感兴趣的:(linux,驱动)