DRM几个重要的结构体及panel开发

一、DRM

        Linux下的DRM框架内容众多,结构复杂。本文将简单介绍下开发过程中用到的几个结构体。这几个结构体都在之前文章里面开发DRM驱动时用到的,未用到的暂不介绍。

        DRM中的KMS包含Framebuffer、CRTC,ENCODER,CONNECTOR,PLANE,VBLANK,property。因此,开发DMR驱动也是围绕这几个部分展开。驱动的逻辑部分需要包含这些。开发DRM驱动可以简化为如下操作。

1、初始化结构体

DRM几个重要的结构体及panel开发_第1张图片

 2、注册

DRM几个重要的结构体及panel开发_第2张图片

3、 给 driver_features 添加上 DRIVER_MODESET,告诉 DRM Core 当前驱动支持 modesetting 操作,创建plane、crtc、encoder、connector 这4个 drm_mode_object。

4、给 driver_features 添加上 DRIVER_GEM 标志位,告诉 DRM Core 该驱动支持 GEM 操作。添加 FB 和 GEM 支持:dumb_create 回调接口用于创建 gem object,并分配物理 buffer。这里直接使用 CMA helper 函数来实现;fb_create 回调接口用于创建 framebuffer object,并绑定 gem objects。这里直接使用 CMA helper 函数实现。fops 中的 mmap 接口,用于将 dumb buffer 映射到 userspace,它依赖 drm driver 中的 gem_vm_ops 实现。这里也直接使用 CMA helper 函数来实现。DRM几个重要的结构体及panel开发_第3张图片

DRIVER_MODESET:表示支持modesetting 操作

DRIVER_GEM:表示支持GEM 操作,用于操作对内存的分配、释放

DRIVER_ATOMIC:支持 Atomic 操作,用于操作各种属性

.fops = &drv_driver_fops,驱动的fops结构体

.dumb_create = stm_gem_cma_dumb_create,创建gem object,分配屋里buffer,可以使用CMA helper函数实现

 其中,

1)xxx_funcs 必须有,xxx_helper_funcs 可以没有。

2)drm_xxx_init() 必须有,drm_xxx_helper_add() 可以没有。

3)只有当 xxx_funcs 采用 DRM 标准的 helper 函数实现时,才有可能 需要定义 xxx_helper_funcs 接口。

4xxx_funcs.destroy() 接口必须实现

helper 函数的作用:drm_xxx_funcs 是 drm ioctl 操作的最终入口,但是对于大多数 SoC 厂商来说,它们的 drm_xxx_funcs 操作流程基本相同(你抄我,我抄你),只是在寄存器配置上存在差异,因此开发者们将那些 common 的操作流程做成了 helper 函数,而将那些厂商差异化的代码放到了 drm_xxx_helper_funcs 中去,由 SoC 厂商自己实现。

        开发一个DRM驱动大致流程如上所述。总之,就是实现KMS中的各个部分。GEM中的部分可以使用CAM中的接口完成。

二、几个结构体

        1、struct drm_plane_funcs结构体

        plane是图层相关部分。结构体部分成员如下:

struct drm_plane_funcs 结构体成员:
1、	update_plane:为给定的CRTC和framebuffer启用并配置平面
2、	disable_plane:关闭plane
3、	destroy:清除plane所有的资源
4、	reset:复位软硬件状态为关闭
5、	set_property:更新plane的property
6、	atomic_duplicate_state:复制当前的plane状态并返回
7、	atomic_destroy_state:销毁当前的plane状态
8、	atomic_set/get_property:原子操作设置/获得property
9、	format_mod_supported:该可选挂钩用于DRM,以确定给定的格式/修饰符组合是否对平面有效

struct drm_plane_helper_funcs部分成员如下:

struct drm_plane_helper_funcs结构体成员:
1、	prepare_fb:该钩子通过固定其后备存储器或将其重新定位到连续的VRAM块,为扫描准备帧缓冲区。其他可能的准备工作包括冲洗缓存。
2、	cleanup_fb:清除在prepare_fb分配的给framebuffer和plane的资源
3、	atomic_check:检查该挂钩中的plane特定约束
4、	atomic_update:更新plane状态
5、	atomic_disable:关闭
6、	atomic_async_check:异步检查
7、	atomic_async_update:异步更新状态

使用示例:

static const struct drm_plane_funcs ltdc_plane_funcs = {
    .update_plane = drm_atomic_helper_update_plane,
    .disable_plane = drm_atomic_helper_disable_plane,
    .destroy = drm_plane_cleanup,
    .reset = drm_atomic_helper_plane_reset,
    .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
    .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
    .atomic_print_state = ltdc_plane_atomic_print_state,
    .format_mod_supported = ltdc_plane_format_mod_supported,
};


static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
	.prepare_fb = drm_gem_fb_prepare_fb,       //准备分配资源
	.atomic_check = ltdc_plane_atomic_check,   //检查驱动
	.atomic_update = ltdc_plane_atomic_update,   //更新plane状态
	.atomic_disable = ltdc_plane_atomic_disable,  //关闭
};

     2、struct drm_crtc_funcs结构体

        部分结构体成员如下:

struct drm_crtc_funcs结构体成员:
1、	reset;复位,设置为关闭状态
2、	destroy:清除CRTC资源
3、	cursor_set:更新鼠标图像
4、	cursor_move:更新光标位置
5、	gamma_set:gamma设置
6、	set_config:修改配置
7、	page_flip:修改帧缓冲页,避免垂直消影期间用新的缓冲区替代时产生撕裂
8、	set_property:设置property
9、	atomic_duplicate_state:复制当前的状态
10、	atomic_destroy_state:销毁复制的状态
11、	atomic_get_property:获取atomic_get_property
12、	set_crc_source:更改CRC的来源
13、	get_vblank_counter 获取CRTC的vblank计数器
14、	disable_vblank:关闭消影

struct drm_crtc_helper_funcs结构体成员
1、	dpms:控制CRTC电源功率
2、	commit:提交新的模式
3、	mode_valid:用于检查特定模式在此crtc中是否有效
4、	mode_fixup:用于验证模式
5、	mode_set:设置模式
6、	mode_set_nofb:用于更新CRTC的显示模式,而不更改主平面配置的任何内容
7、	mode_set_base:设置新的帧缓冲区和扫描位置
8、	atomic_flush:完成CRTC上多个平面的更新
9、	atomic_enable/disable:打开/关闭

使用示例如下:

static const struct drm_crtc_funcs ltdc_crtc_funcs = {
	.destroy = drm_crtc_cleanup,  //清除CRTC资源
	.set_config = drm_atomic_helper_set_config,  //设置配置
	.page_flip = drm_atomic_helper_page_flip,  //修改帧缓冲页,避免垂直消影期间用新的缓冲区替代时产生撕裂
	.reset = drm_atomic_helper_crtc_reset,  // 复位,设置为关闭状态
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,   // 复制当前状态
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,  //  销毁当前状态
	.enable_vblank = ltdc_crtc_enable_vblank,    //  使能消影
	.disable_vblank = ltdc_crtc_disable_vblank,   // 关闭消影
	.gamma_set = drm_atomic_helper_legacy_gamma_set,   //gamma设置
}; 


struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
	.mode_valid = ltdc_crtc_mode_valid,  //用于检查特定模式红CRTC是否有效
	.mode_fixup = ltdc_crtc_mode_fixup,    //用于验证模式  
	.mode_set_nofb = ltdc_crtc_mode_set_nofb,   //用于更新CRTC的显示模式,而不更改主平面配置的任何内容
	.atomic_flush = ltdc_crtc_atomic_flush,   //完成CRTC上多个平面的更新
	.atomic_enable = ltdc_crtc_atomic_enable,  // 打开
	.atomic_disable = ltdc_crtc_atomic_disable,  //关闭
};

3、struct drm_encoder_helper_funcs结构体:

static const struct drm_encoder_funcs ltdc_encoder_funcs = {
	.destroy = drm_encoder_cleanup,
};

static const struct drm_encoder_helper_funcs ltdc_encoder_helper_funcs = {
	.disable = ltdc_encoder_disable,
	.enable = ltdc_encoder_enable,
	.mode_set = ltdc_encoder_mode_set,
};

4、drm_panel相关

drm_panel这部分是在drivers\gpu\drm\panel\panel-simple.c添加。具体则是:

static const struct of_device_id platform_of_match[]下面添加一个自己的of_device_id。

{
		.compatible = "alientek,lcd-rgb",
		 .data = &HTQ_alientek_desc,
},

HTQ_alientek_desc结构体描述的是所用屏幕的信息,包括显示模式、像素格式等。

static const struct panel_desc HTQ_alientek_desc = { 
	.modes = &HTQ_ATK7016_mode, 
	.num_modes = 1, 
	.bus_format = MEDIA_BUS_FMT_RGB888_1X24, 
};

显示模式在HTQ_ATK7016_mode这个结构体里面具体描述,如时钟、分辨率、刷新率等等。

static const struct drm_display_mode HTQ_ATK7016_mode = { 
	.clock = 51200, /* LCD像素时钟,单位KHz */ 
	.hdisplay = 1024, /* LCD X轴像素个数 */
	.hsync_start = 1024 + 140, /* LCD X轴+hbp的像素个数 */ 
	.hsync_end = 1024 + 140 + 20, /* LCD X轴+hbp+hspw的像素个数 */ 
	.htotal = 1024 + 140 + 20 + 160,/* LCD X轴+hbp+hspw+hfp */
	.vdisplay = 600, /* LCD Y轴像素个数 */ 
	.vsync_start = 600 + 20, /* LCD Y轴+vbp的像素个数 */ 
	.vsync_end = 600 + 20 + 3, /* LCD Y轴+vbp+vspw的像素个数 */ 
	.vtotal = 600 + 20 + 3 + 12,/* LCD Y轴+vbp+vspw+vfp */  
	.vrefresh = 60, /* LCD的刷新频率为60HZ */ 12 
}; 

添加上这些,就能将设备树上的屏幕信息和驱动对应上。DRM框架就能驱动屏幕。

以上几个结构体分别对应KMS里面的crtc、plane、encoder、panel几个部分。对应着写好代码即可。都比较简单。最后一个是drm_panel相关的,屏幕这部分可以使用已经定义好的struct panel_desc结构体去描述,struct panel_desc结构体里面有drm_panel,struct panel_desc可以看作是drm_panel的继承。

panel-simple.c里面实际上使用的是struct mipi_dsi_driver panel_simple_dsi_driver结构体作为驱动描述结构体。

static struct mipi_dsi_driver panel_simple_dsi_driver = {
	.driver = {
		.name = "panel-simple-dsi",
		.of_match_table = dsi_of_match,
	},
	.probe = panel_simple_dsi_probe,
	.remove = panel_simple_dsi_remove,
	.shutdown = panel_simple_dsi_shutdown,
};

本质上依然是个platform驱动。在panel_simple_init函数里面可以看到

static int __init panel_simple_init(void)
{
	int err;

	err = platform_driver_register(&panel_simple_platform_driver);
	if (err < 0)
		return err;

	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
		err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
		if (err < 0)
			return err;
	}

	return 0;
}
module_init(panel_simple_init);

注册一个platform驱动,在panel_simple_dsi_probe函数中进行匹配,根据上面static const struct of_device_id platform_of_match[]中已经定义的数组数据。

三、总结

        DRM驱动是目前Linux内核主流的显示框架,支持现代的显示技术,社区参与度、活跃度都很高,传统的framebuffer基本上已经处于半废弃状态。DRM驱动开发离不开KMS中的几个模块,按照定义好的结构体填充代码,完成开发,还算比较方面。

相关链接:

linux驱动系列学习之DRM(十)

你可能感兴趣的:(linux驱动学习,linux,驱动开发,arm开发)