DRM驱动代码分析:图层参数更新

前言:
无业居家,闭门造车。非常欢迎大家帮忙指正。
有些代码流程是看代码分析的,没有去验证是否正确
我对DRM框架的很多东西都不了解,所以有些地方会比较生硬。熟悉学习需要时间,文章一直堆在草稿箱可能会降低我的积极性,所以我还是先发布了文章,后面慢慢改进。

1.DRM驱动怎么获得图层的宽、高等参数

1.1 图层参数有哪些?

drivers\gpu\drm\drm_blend.c

standard plane properties
SRC_X&drm_framebuffer里源矩形的x坐标偏移值。必须为正。
SRC_Y&drm_framebuffer里源矩形的y坐标偏移值。必须为正。
SRC_W&drm_framebuffer里原矩形的宽。 SRC_X 加上SRC_W要小于source framebuffer的宽。必须为正。
SRC_H&drm_framebuffer里原矩形的高。 SRC_Y 加上SRC_H要小于source framebuffer的高。必须为正。
CRTC_X目标矩形的x坐标偏移,可以为负。
CRTC_Y目标矩形的y坐标偏移,可以为负。
CRTC_W目标矩形的宽。 CRTC_X加上CRTC_W可以超出&drm_crtc当前的可见水平区域
CRTC_H目标矩形的高。 CRTC_Y加上CRTC_H可以超出&drm_crtc当前的可见垂直区域
FB_IDMode object ID of the &drm_framebuffer this plane should scan out.
CRTC_IDMode object ID of the &drm_crtc this plane should be connected to.
additional plane properties
alpha透明度,取值范围是0(透明)~0xfff(不透明)。有些格式每个像素都有一个透明度,比如ARGB8888格式。
drm_plane_create_alpha_property()
rotation旋转角度(包括翻转)
drm_plane_create_rotation_property()
zposz order,指定图层的顺序。
drm_plane_create_zpos_immutable_property() * drm_plane_create_zpos_property()
pixel blend mode合成方式,描述了当前图层像素和背景图层的合成方式。

None”: 忽略像素alpha
out.rgb = plane_alpha * fg.rgb + (1 - plane_alpha) * bg.rgb

Pre-multiplied”: 像素rgb值和像素alpha已经预乘过。
out.rgb = plane_alpha * fg.rgb + (1 - (plane_alpha * fg.alpha)) * bg.rgb

Coverage”: 像素rgb值和像素alpha没有预乘过。
out.rgb = plane_alpha * fg.alpha * fg.rgb + (1 - (plane_alpha * fg.alpha)) * bg.rgb

drm_plane_create_blend_mode_property()
SCALING_FILTERIndicates scaling filter to be used for plane scaler.
drm_plane_create_scaling_filter_property

1.2 struct drm_plane_state

include/drm/drm_plane.h
struct drm_plane_state {
	...
	struct drm_framebuffer *fb;
	...
	int32_t crtc_x;
	int32_t crtc_y;
	uint32_t crtc_w, crtc_h;
	uint32_t src_x;
	uint32_t src_y;
	uint32_t src_h, src_w;
	u16 alpha;
	uint16_t pixel_blend_mode;
	unsigned int rotation;
	unsigned int zpos;
	...
};

struct drm_plane_state里存储了图层的一些参数

crtc_x,图层可见区域在CRTC上的左侧位置(pos x)。数据类型是int32_t有符号整数,位置可以在屏幕之外(代码上怎么处理?)。

crtc_y,图层可见区域在CRTC上的上方位置(pos y)。数据类型是int32_t有符号整数,位置可以在屏幕之外。

crtc_w,图层可见区域在CRTC上的宽

crtc_h,图层可见区域在CRTC上的高

src_x,图层可见区域在图层上的左侧位置(crop start x)

src_y,图层可见区域在图层上的上方位置(crop start y)

src_h,图层可见区域的高

src_w,图层可见区域的宽

alpha,图层透明度,0表示完全透明,0xffff表示完全不透明

pixel_blend_mode,透明度合成公式的选择,表示当前图层的像素用什么方式和背景图层合成。

rotation,图层的旋转角度

zpos,图层在CRTC上的优先级(zorder)

normalized_zpos,归一化的zpos,范围是0~N-1,其中N是CRTC的有效图层

1.3 更新图层属性(plane property)

drivers\gpu\drm\drm_ioctl.c
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER),
drivers\gpu\drm\drm_atomic_uapi.c
int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
for (j = 0; j < count_props; j++)
		ret = drm_atomic_set_property(state, file_priv, obj, prop, prop_value);
drivers\gpu\drm\drm_atomic_uapi.c
int drm_atomic_set_property(struct drm_atomic_state *state, struct drm_file *file_priv,
			    struct drm_mode_object *obj, struct drm_property *prop, uint64_t prop_value)
case DRM_MODE_OBJECT_PLANE:
ret = drm_atomic_plane_set_property(plane, plane_state, file_priv, prop, prop_value);
drivers\gpu\drm\drm_atomic_uapi.c
static int drm_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state,
									struct drm_file *file_priv, struct drm_property *property, uint64_t val)
if (property == config->prop_fb_id) {
		struct drm_framebuffer *fb;
	
		fb = drm_framebuffer_lookup(dev, file_priv, val);
		drm_atomic_set_fb_for_plane(state, fb);
		if (fb)
			drm_framebuffer_put(fb);
	} else if (property == config->prop_in_fence_fd) {
	      ...
	} else if (property == config->prop_crtc_x) {
		state->crtc_x = U642I64(val);
	} else if (property == config->prop_crtc_y) {
		state->crtc_y = U642I64(val);
	} else if (property == config->prop_crtc_w) {
		state->crtc_w = val;
	} else if (property == config->prop_crtc_h) {
		state->crtc_h = val;
	} else if (property == config->prop_src_x) {
		state->src_x = val;
	} else if (property == config->prop_src_y) {
		state->src_y = val;
	} else if (property == config->prop_src_w) {
		state->src_w = val;
	} else if (property == config->prop_src_h) {
		state->src_h = val;
	} else if (property == plane->alpha_property) {
		state->alpha = val;
	} else if (property == plane->blend_mode_property) {
		state->pixel_blend_mode = val;
	} else if (property == plane->rotation_property) {
		...
		state->rotation = val;
	} else if (property == plane->zpos_property) {
		state->zpos = val;
	} else if (property == plane->color_encoding_property)
	...
	} else if (plane->funcs->atomic_set_property) {
		return plane->funcs->atomic_set_property(plane, state,
				property, val);
	} else
	...

更新drm_plane_state里的crtc_x,crtc_y,crtc_w,crtc_h,src_x,src_y,src_w,src_h,alpha,blend_mode,rotation,zpos等属性的值。

如果有驱动私有的plane相关属性,属性值的更新需要实现struct drm_plane_funcs里的以下函数:
int (*atomic_set_property)(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val);
比如高通实现的:
static int sde_plane_atomic_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val)

1.4 struct drm_plane_helper_funcs

include\drm\drm_modeset_helper_vtables.h
struct drm_plane_helper_funcs {
	int (*prepare_fb)(struct drm_plane *plane, struct drm_plane_state *new_state);
	void (*cleanup_fb)(struct drm_plane *plane, struct drm_plane_state *old_state);
	int (*atomic_check)(struct drm_plane *plane, struct drm_atomic_state *state);
	void (*atomic_update)(struct drm_plane *plane, struct drm_atomic_state *state);
	void (*atomic_disable)(struct drm_plane *plane, struct drm_atomic_state *state);
	int (*atomic_async_check)(struct drm_plane *plane, struct drm_atomic_state *state);
	void (*atomic_async_update)(struct drm_plane *plane, struct drm_atomic_state *state);
};

prepare_fb,准备framebuffer。e.g. pinning its backing storage or relocating it into a contiguous block of VRAM. Other possible preparatory work includes flushing caches.

cleanup_fb,清除prepare_fb生成的framebuffer和图层配置。

atomic_check,检查图层

atomic_update,更新图层状态,需要在 &drm_crtc_helper_funcs.atomic_begin 和 drm_crtc_helper_funcs.atomic_flush callbacks.之间调用。输入参数state是旧的,plane->state是更新后的。

atomic_disable,禁用图层

atomic_async_check,检查图层原子状态能否异步更新。这里的异步是指"not vblank synchronized"

atomic_async_update,图层异步更新

1.5 将更新后的属性值配到设备寄存器

drivers\gpu\drm\drm_atomic_helper.c
void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *old_state, uint32_t flags)
funcs->atomic_update(plane, old_state);
vendor\qcom\opensource\display-drivers\msm\sde\sde_plane.c
static const struct drm_plane_helper_funcs sde_plane_helper_funcs = {
		.prepare_fb = sde_plane_prepare_fb,
		.cleanup_fb = sde_plane_cleanup_fb,
		.atomic_check = sde_plane_atomic_check,
		.atomic_update = sde_plane_atomic_update,
};
vendor\qcom\opensource\display-drivers\msm\sde\sde_plane.c
static void sde_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state)
ret = sde_plane_sspp_atomic_update(plane, old_state);
static int sde_plane_sspp_atomic_update(struct drm_plane *plane,	struct drm_plane_state *old_state)
_sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb);
_sde_plane_update_properties(plane, crtc, fb);
static void _sde_plane_update_properties(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb)
struct drm_plane_state *state;
struct sde_plane_state *pstate;

pstate = to_sde_plane_state(state);
_sde_plane_update_format_and_rects(psde, pstate, fmt);
static void _sde_plane_update_format_and_rects(struct sde_plane *psde, struct sde_plane_state *pstate,
													const struct sde_format *fmt)
psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, pstate->const_alpha_en, src_flags, pstate->multirect_index);
vendor\qcom\opensource\display-drivers\msm\sde\sde_hw_sspp.c
static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx, const struct sde_format *fmt,
		bool const_alpha_en, u32 flags, enum sde_sspp_multirect_index rect_mode)
SDE_REG_WRITE(c, format_off + idx, src_format);

更新SDE图层format参数寄存器

drm_plane_state里的有些属性值会保存到高通自己定义的变量里。

todo :看高通代码,根据psde->revalidate决定更新所有参数,还是更新有变化的参数。(这样会更好吗?更新所有参数会很耗时吗?)

2.DRM驱动怎么获得图层的内存地址

2.1 struct drm_gem_object

struct drm_gem_object {
	struct kref refcount;
	unsigned handle_count;
	struct drm_device *dev;
	struct file *filp;
	struct drm_vma_offset_node vma_node;
	size_t size;
	int name;
	struct dma_buf *dma_buf;
	struct dma_buf_attachment *import_attach;
	struct dma_resv *resv;
	struct dma_resv _resv;
	const struct drm_gem_object_funcs *funcs;
};


refcount,gem object的计数

handle_count,文件私有句柄计数

dev,drm设备

filp, SHMEM file node used as backing storage for swappable buffer objects.GEM also supports driver private objects with driver-specific backing storage (contiguous CMA memory, special reserved blocks). In this case @filp is NULL.

vma_node,用来mmap的映射信息。

size,gem object的大小,以字节为单位。

name,gem object的全局名称,从1开始。0表示未命名。

dma_buf,gem object的dma_buf

import_attach,gem object的dma_buf_attachment。
                struct dma_buf_attachment存放了dma-buf和device的连接关系[2]

resv,预留对象指针

_resv,预留对象

funcs,可选。替代&drm_driver GEM callbacks里的对应函数。

2.2 通过dma buf的fd id获得图层的内存地址

片描述](https://img-blog.csdnimg.cn/a45e36ee8ce14e758c03eef9968f22f5.png)

通过dma-buf的fb id(prime_fd)获得dma-buf

在这里插入图片描述

dma_buf_attach:将设备加到attachment的列表中

dma_buf_map_attachment:产生sg_table。sg_table存储了物理内存的相关信息。
关于sg_table的概念阅读wowotech的Linux kernel scatterlist API介绍

gem_prime_import_sg_table:产生drm_gem_object

obj->import_attach = attach:将attachment赋给gem object

上层调IOCTL将dma buf的fd id传下来,实现prime_fd到handle的转化,这个过程中也生成sg_table了,相关信息保存在drm_gem_object。
那后面就可以通过drm_gem_object来获得图层内存的地址,方便设置图层地址寄存器。

在这里插入图片描述

图层地址寄存器的配置也是在sde_plane_atomic_update里完成的。
按以下顺序获得sg_table,得到了图层的内存地址
struct drm_plane -> struct drm_plane_state -> struct drm_framebuffer -> struct drm_gem_object ->struct dma_buf_attachment *import_attach -> struct sg_table–>struct scatterlist -> struct page -> pfn -> phys addr

在这里插入图片描述

_sde_format_populate_addrs_linear里看到plane_addr的值有两种情况,分别来自msm_framebuffer_iova()和msm_framebuffer_phys()。从函数名上看,前者应该是IO虚拟地址,后者是物理地址。

在这里插入图片描述

关于page、pfn、memory model的概念可以看这篇文章wowotech linuxer《Linux内存模型》
这篇文章写得真好,我有些地方没看懂,以后再学习了。

3.缩写

SSPP:Source Surface Processor Pipes

4.参考资料

[1]DRM应用程序进阶 (Property)
[2]dma-buf 由浅入深(三) —— map attachment
[3]Linux kernel scatterlist API介绍

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值