(原创)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函数可以分为三个部分
和
82 acpi_status =
radeon_acpi_init(rdev);
和
a)
先看radeon_device_init 函数,radeon_device_init可以分为三部分
第一部分是对workqueue asic dma mmio_base等初始化
第二部分是radeon_init
第三部分是一些测试函数,
初始化和测试不管 ,主要看下radeon_int,其调用的是asic初始化的函数,
对于rs780的显卡主要是r600_init函数。
在r600_init函数中
r600_init 结束
b)
radeon_acpi_init acpi电源管理的初始化 没仔细研究
c)
radeon_modeset_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)设置模式(分辨率
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和与之对应的函数。

加载中…