前言:
无业居家,闭门造车。非常欢迎大家帮忙指正。
有些代码流程是看代码分析的,没有去验证是否正确
我对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_ID | Mode object ID of the &drm_framebuffer this plane should scan out. |
CRTC_ID | Mode 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() |
zpos | z 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_FILTER | Indicates 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获得图层的内存地址
通过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介绍