加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

(原创)radeon drm 内核代码解析

(2010-11-17 11:53:30)
标签:

杂谈

分类: linux内核及文件系统
本文主要以radeon为例从三个方面介绍内核drm相关的知识
1。drm初始化流程
2,radeondrmfb的初始化
3。内核drm与应用程序接口


内核的drm主要是为了实现图形的dri硬件加速而服务的,通过提供一系列ioctls的操作,使得应用层的软件可以直接对显卡硬件操作。驱动实际使用drm是经过libdrm封装之后的借口。内核drm主要包括:vblank 事件处理,内存管理,输出管理,framebuffer管理,命令注册,fencing, suspend/resume 支持,dma服务等。

内核drm的核心是围绕struct drm_device 结构展开,每种显卡的drm都会声明一个静态的struct drm_device变量以radeon驱动为例,其声明的变量如下:
267 static struct drm_driver kms_driver = {
268     .driver_features =
269         DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
270         DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM,
271     .dev_priv_size = 0,
272     .load = radeon_driver_load_kms,
273     .firstopen = radeon_driver_firstopen_kms,
274     .open = radeon_driver_open_kms,
275     .preclose = radeon_driver_preclose_kms,
276     .postclose = radeon_driver_postclose_kms,
277     .lastclose = radeon_driver_lastclose_kms,
278     .unload = radeon_driver_unload_kms,
279     .suspend = radeon_suspend_kms,
280     .resume = radeon_resume_kms,
281     .get_vblank_counter = radeon_get_vblank_counter_kms,
282     .enable_vblank = radeon_enable_vblank_kms,
283     .disable_vblank = radeon_disable_vblank_kms,
284 #if defined(CONFIG_DEBUG_FS)
285     .debugfs_init = radeon_debugfs_init,
286     .debugfs_cleanup = radeon_debugfs_cleanup,
287 #endif
288     .irq_preinstall = radeon_driver_irq_preinstall_kms,
289     .irq_postinstall = radeon_driver_irq_postinstall_kms,
290     .irq_uninstall = radeon_driver_irq_uninstall_kms,
291     .irq_handler = radeon_driver_irq_handler_kms,
292     .reclaim_buffers = drm_core_reclaim_buffers,
293     .get_map_ofs = drm_core_get_map_ofs,
294     .get_reg_ofs = drm_core_get_reg_ofs,
295     .ioctls = radeon_ioctls_kms,
296     .gem_init_object = radeon_gem_object_init,
297     .gem_free_object = radeon_gem_object_free,
298     .dma_ioctl = radeon_dma_ioctl_kms,
299     .fops = {
300          .owner = THIS_MODULE,
301          .open = drm_open,
302          .release = drm_release,
303          .unlocked_ioctl = drm_ioctl,
304          .mmap = radeon_mmap,
305          .poll = drm_poll,
306          .fasync = drm_fasync,
307          .read = drm_read,
308 #ifdef CONFIG_COMPAT
309          .compat_ioctl = radeon_kms_compat_ioctl,
310 #endif
311     },
312
313     .pci_driver = {
314          .name = DRIVER_NAME,
315          .id_table = pciidlist,
316          .probe = radeon_pci_probe,
317          .remove = radeon_pci_remove,
318          .suspend = radeon_pci_suspend,
319          .resume = radeon_pci_resume,
320     },
321
322     .name = DRIVER_NAME,
323     .desc = DRIVER_DESC,
324     .date = DRIVER_DATE,
325     .major = KMS_DRIVER_MAJOR,
326     .minor = KMS_DRIVER_MINOR,
327     .patchlevel = KMS_DRIVER_PATCHLEVEL,
328 };
变量函数很多,但是需要关注很少。对这个结构的解释请参考内核Documents/DocBook/drm.tmpl
里面有较详细的解释,这里就不多说了。


1。radeon 初始化。
结构体中声明
272     .load = radeon_driver_load_kms,
radeon的初始化函数便是radeon_driver_load_kms。

radeon_driver_load_kms函数可以分为三个部分
 75     r = radeon_device_init(rdev, dev, dev->pdev, flags);
82     acpi_status = radeon_acpi_init(rdev);
 90     r = radeon_modeset_init(rdev);

a)
先看radeon_device_init 函数,radeon_device_init可以分为三部分
第一部分是对workqueue asic dma mmio_base等初始化
第二部分是radeon_init
第三部分是一些测试函数,

初始化和测试不管 ,主要看下radeon_int,其调用的是asic初始化的函数,
对于rs780的显卡主要是r600_init函数。

在r600_init函数中
 radeon_dummy_page_init 主要是分配内存。
 radeon_get_bios     主要是获得VBIOS代码,途径很多,调试中采用的主要是在bios将VBIOS内容拷到framebuffer里,在内核初始化的时候获得。
 radeon_atombios_init 根据VBIOS内容做的一些初始化工作。
 r600_card_posted 判断crtc是否可用
 r600_scratch_init ,radeon_surface_init 一些简单初始化
 radeon_get_clock_info,radeon_clocks_init 对时钟的初始化
 radeon_fence_driver_init,radeon_agp_init 一些初始化
 r600_mc_init 对gpu的一些基地址的初始化,aper_base ,vram_base 等
 radeon_bo_init 是内存管理器ttm的初始化
 radeon_irq_kms_init 申请显卡irq
 r600_ring_init,r600_ih_ring_init :ring buffer 的初始化 ,主要是ring size的相
 关的大小初始化。
 r600_pcie_gart_init :对gart的初始化
 r600_startup :显卡启动,执行了一系列函数如下:
     r600_init_microcode: load 一系列firmware的bin文件
     r600_mc_program:  对memory control的一些初始化
     r600_agp_enable :  激活agp
     r600_pcie_gart_enable: 激活gart
     r600_gpu_init:      gpu初始化,主要是配置一些寄存器
     r600_blit_init   :  blit的一些初始化
     r600_irq_init   :  显卡irq的激活
     radeon_ring_init : ring  buffer 的初始化
     r600_cp_load_microcode  r600_cp_resume : 一些寄存器配置,包括ring buffer的
     测试
     r600_wb_enable : write back buffer的激活
 radeon_ib_pool_init: IB的初始化
 r600_ib_test :   IB buffer的测试
 r600_audio_init : 声卡的初始化
r600_init  结束

b)
radeon_acpi_init acpi电源管理的初始化 没仔细研究

c)
radeon_modeset_init :另外一些显示相关的初始化
  radeon_i2c_init: 对显卡i2c线的初始化
  radeon_combios_check_hardcoded_edid : 检查显示器的edid信息
  radeon_crtc_init: crtc初始化。对于每个connector通路,都有一个相应的crtc 和
                    encoder帮助其解析framebuffer信息。
  radeon_setup_enc_conn:获得显卡的connector,并绑定相应的encoder
  radeon_hpd_init :  hpd的初始化,没研究
  radeon_pm_init  : power manage 初始化,调试中这个函数经常导致内核dump
  radeon_fbdev_init : radeondrmfb 初始化 ,下面将仔细讨论
  drm_kms_helper_poll_init : 窗口显示激活。

2.radeondrmfb的初始化
初始化函数: radeon_fbdev_init
radeon_fbdev_init函数中:

前半部分主要是对radeon_fbdev 结构体的初始化,包括 
rfbdev->helper.funcs = &radeon_fb_helper_funcs;
以及
drm_fb_helper_init
drm_fb_helper_single_add_all_connectors,
两个函数

最后调用drm_fb_helper_initial_config:
drm_fb_helper_initial_config函数中:
  drm_helper_disable_unused_functions,显将所有的输出端口关闭
  drm_fb_helper_parse_command_line  : 处理内核传进来的参数,比如启动内核可
                      传video=VGA-0:e 表示对VGA-0 通路强
                      启用(不同显卡参数不同)
  drm_fb_helper_probe_connector_modes:对每个通路(connector)设置模式(分辨率
                                           等)
    drm_setup_crtcs : 设置crtc
    drm_fb_helper_single_fb_probe:初始化并注册radeondrmfb
    在drm_fb_helper_single_fb_probe函数中:
         函数中前面一大段都是对connector crtc的设置
         之后
         new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
         这便是对radeondrmfb的初始化
          上面提到了rfbdev->helper.funcs = &radeon_fb_helper_funcs;
         这里调用的fb_probe便是radeon_fb_helper_funcs变量成员:
         radeon_fb_find_or_create_single,
         radeon_fb_find_or_create_single中函数radeonfb_create便是对
         radeondrmfb的初始化,其初始化过程基本上跟一般的framebuffer初始化
         没有什么两样,这里就不再考虑
        最后
        调用的register_framebuffer(info),完成了radeondrmfb的注册,此时
        framebuffer可用,于是vga显示基本上是从这里可以看见的。
3.drm与应用程序接口
内核将drm看作一种硬件设备,于是对drm的操作就遵循对一般设备操作,如上提到的
kms_driver中包含了对drm操作的接口。
299     .fops = {
300          .owner = THIS_MODULE,
301          .open = drm_open,
302          .release = drm_release,
303          .unlocked_ioctl = drm_ioctl,
304          .mmap = radeon_mmap,
305          .poll = drm_poll,
306          .fasync = drm_fasync,
307          .read = drm_read,
308 #ifdef CONFIG_COMPAT
309          .compat_ioctl = radeon_kms_compat_ioctl,
310 #endif
311     },
其中drm_open打开drm, mmap是虚拟地址到设备io地址映射使用的函数,compat_ioctl
则是是用ioctl控制drm的接口。

应用程序调用drm主要是通过ioctl系统调用,最终调用的是radeon_kms_compat_ioctl.

414 long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
415 {
416     unsigned int nr = DRM_IOCTL_NR(cmd);
417     int ret;
418
419     if (nr < DRM_COMMAND_BASE)
420         return drm_compat_ioctl(filp, cmd, arg);
421
422     ret = drm_ioctl(filp, cmd, arg);
423
424     return ret;
425 } 

a)
当cmd的值小于DRM_COMMAND_BASE(0x40),调用drm_compat_ioctl
394 long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
395 {
396     unsigned int nr = DRM_IOCTL_NR(cmd);
397     drm_ioctl_compat_t *fn = NULL;
398     int ret;
399
400     if (nr < DRM_COMMAND_BASE)
401         return drm_compat_ioctl(filp, cmd, arg);
402
403     if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls))
404         fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE];
405
406     if (fn != NULL)
407         ret = (*fn) (filp, cmd, arg);
408     else
409         ret = drm_ioctl(filp, cmd, arg);
410
411     return ret;
412 }
最终调用的一定是 drm_compat_ioctl(filp, cmd, arg) ,因为nr < DRM_COMMAND_BASE是一定的。
1059 long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1060 {
1061     unsigned int nr = DRM_IOCTL_NR(cmd);
1062     drm_ioctl_compat_t *fn;
1063     int ret;
1064
1065    
1069     if (nr >= ARRAY_SIZE(drm_compat_ioctls))
1070         return drm_ioctl(filp, cmd, arg);
1071
1071
1072     fn = drm_compat_ioctls[nr];
1073
1074     if (fn != NULL)
1075         ret = (*fn) (filp, cmd, arg);
1076     else
1077         ret = drm_ioctl(filp, cmd, arg);
1078
1079     return ret;
1080 }
其调用的大多是drm_compat_ioctls函数
1016 drm_ioctl_compat_t *drm_compat_ioctls[] = {
1017     [DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version,
1018     [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE32)] = compat_drm_getunique,
1019     [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP32)] = compat_drm_getmap,
1020     [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT32)] = compat_drm_getclient,
1021     [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS32)] = compat_drm_getstats,
1022     [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE32)] = compat_drm_setunique,
1023     [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP32)] = compat_drm_addmap,
1024     [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS32)] = compat_drm_addbufs,
1025     [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS32)] = compat_drm_markbufs,
1026     [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS32)] = compat_drm_infobufs,
1027     [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS32)] = compat_drm_mapbufs,
1028     [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS32)] = compat_drm_freebufs,
1029     [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP32)] = compat_drm_rmmap,
1030     [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX32)] = compat_drm_setsareactx,
1031     [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX32)] = compat_drm_getsareactx,
1032     [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX32)] = compat_drm_resctx,
1033     [DRM_IOCTL_NR(DRM_IOCTL_DMA32)] = compat_drm_dma,
1034 #if __OS_HAS_AGP
1035     [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE32)] = compat_drm_agp_enable,
1036     [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO32)] = compat_drm_agp_info,
1037     [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC32)] = compat_drm_agp_alloc,
1038     [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE32)] = compat_drm_agp_free,
1039     [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND32)] = compat_drm_agp_bind,
1040     [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND32)] = compat_drm_agp_unbind,
1041 #endif
1042     [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC32)] = compat_drm_sg_alloc,
1043     [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE32)] = compat_drm_sg_free,
1044 #if defined(CONFIG_X86) || defined(CONFIG_IA64)
1045     [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw,
1046 #endif
1047     [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank,
1048 };
其他的则调用drm_ioctls里面的函数,因为nr < DRM_COMMAND_BASE 在drm_ioctl中调用的是
451     else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
452         ioctl = &drm_ioctls[nr];
453         cmd = ioctl->cmd;
454         usize = asize = _IOC_SIZE(cmd);
455     } else
分支的函数 即drm_ioctls结构体的函数,结构体与drm_compat_ioctls类似,这里就不列出来。

b)
在radeon_kms_compat_ioctl中 当nr > DRM_COMMAND_BASE时调用的是drm_ioctl,其中调用的分支是:
442     if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) &&
443         (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
444         u32 drv_size;
445         ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE];
446         drv_size = _IOC_SIZE(ioctl->cmd_drv);
447         usize = asize = _IOC_SIZE(cmd);
448         if (drv_size > asize)
449             asize = drv_size;
450     }
因此其调用的是注册的&dev->driver->ioctls[nr - DRM_COMMAND_BASE],即ioctls结构体,
上面kms_driver 中,ioctls结构体是
295     .ioctls = radeon_ioctls_kms,
因此 其调用的是radeon_ioctls_kms结构体定义的一系列操作,因为其与drm_compat_ioctls类似,这里就不将代码列出来。


虽然应用程序可以直接通过系统调用open ioctl等与drm交互,但是实际上在drm使用中为了方便总是在系统中安装了libdrm对drm的
各个操作进行封装,这样更方便使用而不用去记忆那些ioctl的num和与之对应的函数。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有