文章目录
1、资料快车
1、Linux DRM Developer's Guide英文资料
2)http://landley.net/kdocs/htmldocs/drm.html
2、基于DRM的kernel图显系统(Linux and SOC专栏)
https://blog.csdn.net/weixin_43644245/category_11305753.html
3、蜗窝科技(graphic subsystem)
http://www.wowotech.net/sort/graphic_subsystem
4、野火:
https://doc.embedfire.com/linux/rk356x/linux_base/zh/latest/linux_app/drm/drm.html
5、Linux Graphics领域专家-何小龙专栏
https://blog.csdn.net/hexiaolong2009/category_9281458.html
6、深入理解Linux DRM显示子系统:架构、实战项目与关键问题全解析
https://blog.csdn.net/Interview_TC/article/details/148558251
7、【嵌入式Linux应用开发 | DRM 】在泰山派RK3566使用DRM控制屏幕(超详细)
https://modelers.csdn.net/6912deb25511483559e7586f.html
8、RK Graphics and Multimedia user guide

2、preface
1)传统的基于FB显示开发已经不能满足当下日益发展的显示硬件,有很多新的硬件技术产生,多层合成(即HWC)、VSYNC(HWC功能之一)、ION/DMA-BUF(android的gralloc)、异步更新、fence机制,这些驱动互相独立存在,开发DRM则为了解决这些问题;统一架构集中管理的需求迫在眉睫;
2)DRM架构将显示相关的机制统一管理,同时DRM可以统一管理GPU、Display驱动、HWC驱动、Fence驱动等等,使得软件架构更为统一,方便管理和维护;
3)DRM已经是Linux目前主流的图形显示框架,比如应用在智能座舱、智慧硬件、工业大屏、医疗影像;
4)高版本Android系统当然也要跟上,使用DRM框架;
DRM的意义?
1)假如没有DRM,则kernel下的Display 相关的模块则是独立,应用层使用它们也是互相独立,则会出现以下情况,试想一下,用户层调用将会是多么的繁琐,扩展性也差;

2)而DRM则为了解决零散的问题,提供一套框架(这套框架当然由专业的团队复杂维护),将上述模块在内核层"包起来",建立互相联系,并向上提供统一的调用接口,思想与ALSA、V4L2等等是类似的;

用户层统一通过libdrm来使用Display模块
从用户层的角度,来看看DRM的好处
1、使用DRM的伪代码:
int main(int argc, char **argv)
{
/* open the drm device */
open("/dev/dri/card0");
/* get crtc/encoder/connector id */
drmModeGetResources(...);
/* get connector for display mode */
drmModeGetConnector(...);
/* create a dumb-buffer */
drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB);
/* bind the dumb-buffer to an FB object */
drmModeAddFB(...);
/* map the dumb buffer for userspace drawing */
drmIoctl(DRM_IOCTL_MODE_MAP_DUMB);
mmap(...); //当执行完mmap之后,我们就可以直接在应用层对framebuffer进行绘图操作了。
/* start display */
drmModeSetCrtc(crtc_id, fb_id, connector_id, mode); //正式开始显示
}
通过DRM框架提供的接口,仅需调用少量的函数完成一系列的配置和操作
如果没有DRM,这段代码则会相当复杂;
3、术语&基本概念
1)术语
1、DRM - Direct rendering manager 直接图像管理
注意区分 DRM(Digital Rights management) 数字版权管理 - hdcp/playready/widewive
DRI - Direct rendering Infrastructure
KMS - kernel mode setting
VBL - vblank - vertical blank 垂直消隐
DP - display port
plane : 硬件图层
两种内存模型:
DUMB:只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景
prime:连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景
2)vblank handing
场景:在游戏中关闭垂直同步时如果帧速率超过显示器的刷新率 (例如 60Hz 显示器为每秒 120 帧) 的话,就会出现屏幕撕裂情形
https://blog.csdn.net/yaoyutian/article/details/121128725
基本概念:vertical blank : 直译垂直空白,指的是扫描点从画面的右下角到左上角的时间间隔,术语叫做垂直消隐,也称场消隐
作用:底层驱动 硬件产生vblank中断信号,并会向上提供给VSYNC使用
4、DRM框架
1)DRM系统框架

1、分配两部分
1)应用层- libdrm,应用程序可以选择使用libdrm方便地访问DRM各个组件驱动;
2)内核驱动层
1、GEM (graphic execution manager) - 管理Framebuffer内存分配,代替Android的ION/DMA-BUF
2、KMS (kernel mode setting) - 功能丰富,包括CRTC、Encoder、Connector等
1)更新画面:显示buffer的切换、多图层的合成方式,以及每个图层的显示位置;
2)设置显示参数:包括分辨率、刷新率、电源状态(唤醒休眠)等
3、GPU驱动 - 开源驱动panfrost/Lima
2)DRM子系统框架

组件介绍:
1)DRM中Framebuffer也是一片存放图像的内存区域, 且需要设置图像的格式(RGB888,YUV,C8等) 以及画布的大小 - 配置图像格式,与实际的显示设备无关;
2)plane(图层): 一个包含项CRTC发送数据的缓存块的内存对象,每个CRTC必须关联一个Planes,他是CRTC决定采用哪种视频模式的根据 - 显示分辨率、像素大小、像素格式,刷新率等;通常驱动会把framebuffer绑定到 DRM_PLANE_TYPE_PRIMARY 上。
Planes会分为三种类型:
1、DRM_PLANE_TYPE_PRIMARY: 主要图层,显示背景或者图像内容,每个CRTC中含一个
2、DRM_PLANE_TYPE_OVERLAY: 用于显示叠加、缩放,每个CRTC中含一个以上,比如overlay图层播放视频
3、DRM_PLANE_TYPE_CURSOR: 用于显示鼠标,每个CRTC中含0-N个
plane包含图像数据?还是属性集合?与framebuffer的关系?
drm中的plane是指硬件图层,注意区别应用层的plane 不同概念,固定数量,不同平台支持的数量不同;
多个plane给到crct进行合成(即HWC的功能),需要硬件支持,硬件不支持则也必然有对应软件实现;
图显数据流(一对一场景):fb -> plane -> crtc -> encoder -> connector;
图显数据流(多对多场景):plane固定不变,framebuffer则由用户自行分配,用户构造好数据再指定给哪个plane;

3)CRTC (代表显示设备) :配置实际的显示设备参数,在DRM显示系统中CRTC会配置display timings和显示分辨率(Planes提供) 来扫描framebuffer上的内容,传给Encoder。
4)Encoder : 将pixel像素编码(转换)为显示器所需要的信号,比如HDMI connector,Encoder需要将图像转换为TMDS信号;
5)Connector:物理连接器,LCD/HDMI/DP,与当前物理连接的输出设备相关的信息(如连接状态,EDID数据,DPMS状态或支持的视频模式)也存储在 Connector 内。
6)Bridge:有些SOC缺乏某种Connector,可以使用Bridge来转接,比如DSI2HDMI;
多屏异显功能
采用RK3568的板卡支持多屏异显,最大可支持3屏异显,RK3568总plane数量为6个
- 当开启一个屏幕时,屏幕会分配3个图层(第三个图层Cluster仅支持AFBC格式的图像)
- 当开启两个屏幕时,两个屏幕均会分配3个图层(第三个图层Cluster仅支持AFBC格式的图像)
- 当开启三个屏幕时,第一个屏幕分配3个图层,第二个屏幕分配两个图层, 最后屏幕分配一个图层 (第一个屏幕的第三个图层Cluster以及第二个屏幕的第二个图层Cluster仅支持AFBC格式的图像)
小结-DRM框架元素汇总

3)AML方案的drm-meson
1、整体框架
注意有两部分
1)项目部分 - TV项目
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm
2)开源部分-通用
┌─────────────────────────────────────────────────────────┐
│ DRM Framework │
│ (drm_device, drm_crtc, drm_plane, drm_encoder) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Meson DRM Driver (meson_drv.c) │
│ - 驱动初始化和组件管理 │
│ - DRM 设备注册 │
│ - 中断处理 │
└─────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ meson_crtc │ │ meson_plane │ │meson_overlay │
│ (CRTC) │ │ (OSD1) │ │ (VD1) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Video Processing Pipeline │
│ │
│ VIU (Video Input Unit) ──► VPP (Video Post Process) │
│ │ │ │
│ │ ▼ │
│ └──────────────────► VENC (Video Encoder) │
│ │ │
│ ▼ │
│ HDMI / CVBS Output │
└─────────────────────────────────────────────────────────┘
源码结构
drivers/gpu/drm/meson/
├── meson_drv.c/h # 主驱动入口,DRM 设备管理
├── meson_crtc.c/h # CRTC(显示控制器)
├── meson_plane.c/h # 主平面层(OSD1,用于 UI)
├── meson_overlay.c/h # 覆盖层(VD1,用于视频)
├── meson_venc.c/h # 视频编码器(ENCI/ENCP)
├── meson_viu.c/h # 视频输入单元(OSD 处理)
├── meson_vpp.c/h # 视频后处理
├── meson_vclk.c/h # 视频时钟管理
├── meson_registers.h # 寄存器定义
├── meson_dw_hdmi.c/h # HDMI 输出(可选)
└── meson_venc_cvbs.c/h # CVBS 输出
LVDS
android\vendor\amlogic\common\kernel\common_5.4\drivers\gpu\drm\panel\panel-lvds.c
5、源码分析
1)源码框架
1、源码目录
├── i2c //HDMI DDC/EDID支持
├── ingenic
├── lib
├── lima //lima GPU驱动
├── mcde //multichannel display engine
├── meson
├── panel //显示面板驱动
├── panfrost //panfrost GPU驱动
├── rcar-du
├── scheduler //GPU作业调度器
├── selftests
├── ttm
├── udl //USB displaylink video
├── vgem //Virtual graphic execution manager - 软件实现GEM
├── vkms //virtual kernel mode setting - 软件实现KMS
├── bridge //显示桥接芯片,比如DSI2HDMI
├── ati_pcigart.c
├── drm_agpsupport.c
├── drm_atomic.c
├── drm_atomic_helper.c
├── drm_atomic_state_helper.c
├── drm_atomic_uapi.c
├── drm_auth.c
├── drm_blend.c
├── drm_bridge.c
├── drm_bufs.c
├── drm_cache.c
├── drm_client.c
├── drm_client_modeset.c
├── drm_color_mgmt.c
├── drm_connector.c
├── drm_context.c
├── drm_crtc.c
├── drm_crtc_helper.c
├── drm_crtc_helper_internal.h
├── drm_crtc_internal.h
├── drm_damage_helper.c
├── drm_debugfs.c
├── drm_debugfs_crc.c
├── drm_dma.c
├── drm_dp_aux_dev.c
├── drm_dp_cec.c
├── drm_dp_dual_mode_helper.c
├── drm_dp_helper.c
├── drm_dp_mst_topology.c
├── drm_drv.c
├── drm_dsc.c
├── drm_dumb_buffers.c
├── drm_edid.c
├── drm_edid_load.c
├── drm_encoder.c
├── drm_encoder_slave.c
├── drm_fb_cma_helper.c
├── drm_fb_helper.c
├── drm_file.c
├── drm_flip_work.c
├── drm_format_helper.c
├── drm_fourcc.c
├── drm_framebuffer.c
├── drm_gem.c
├── drm_gem_cma_helper.c
├── drm_gem_framebuffer_helper.c
├── drm_gem_shmem_helper.c
├── drm_gem_vram_helper.c
├── drm_hashtab.c
├── drm_hdcp.c
├── drm_internal.h
├── drm_ioc32.c
├── drm_ioctl.c
├── drm_irq.c
├── drm_kms_helper_common.c
├── drm_lease.c
├── drm_legacy.h
├── drm_legacy_misc.c
├── drm_lock.c
├── drm_memory.c
├── drm_mipi_dbi.c
├── drm_mipi_dsi.c
├── drm_mm.c
├── drm_mode_config.c
├── drm_mode_object.c
├── drm_modes.c
├── drm_modeset_helper.c
├── drm_modeset_lock.c
├── drm_of.c
├── drm_panel.c
├── drm_panel_orientation_quirks.c
├── drm_pci.c
├── drm_plane.c
├── drm_plane_helper.c
├── drm_prime.c
├── drm_print.c
├── drm_probe_helper.c
├── drm_property.c
├── drm_rect.c
├── drm_scatter.c
├── drm_scdc_helper.c
├── drm_self_refresh_helper.c
├── drm_simple_kms_helper.c
├── drm_syncobj.c
├── drm_sysfs.c
├── drm_trace.h
├── drm_trace_points.c
├── drm_vblank.c
├── drm_vma_manager.c
├── drm_vm.c
├── drm_vram_helper_common.c
├── drm_vram_mm_helper.c
├── drm_writeback.c
2、
1、DRM源码实现
android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm
2、AML方案的DRM主机驱动代码(利用drm框架接口完成核心任务)
android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson
3、各模块debug
android\vendor\amlogic\common\kernel\common_5.4\drivers\gpu\drm\drm_drv.c
MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
"\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n"
"\t\tBit 1 (0x02) will enable DRIVER messages (drm controller code)\n"
"\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n"
"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n"
"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n"
"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n"
"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n"
"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
module_param_named(debug, drm_debug, int, 0600);
2)数据结构
1. component
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/component.h
struct component_ops {
int (*bind)(struct device *comp, struct device *master, void *master_data);
int (*unbinde)()
}
1、drm_device
android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_device.h
struct drm_device {
struct list_head legacy_dev_list;
struct device *dev;
struct drm_driver *driver;
void *dev_private;
struct drm_minor *primary;
struct drm_minor *rendor;
struct drm_master *master;
struct list_head clientlist;
struct drm_vblank_crtc *vblank;
spinlock_t vb1_lock;
u32 max_vblank_count;
unsigned int num_crtcs; //crtcs数量
struct drm_mode_config mode_config;
//GEM information
struct mutex object_name_lock;
struct idr object_name_idr; //idr, id allocator
struct drm_vma_offset_manager *vma_offset_manager;
struct drm_vram_mm *vram_mm;
struct drm_fb_helper *fb_helper;
}
2、drm_driver
android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_drv.h
struct drm_driver {
int (*load)(struct drm_device *, ...);
int (*open)(struct drm_device *, struct drm_file *);
u32 (*get_vblank_counter)(struct drm_device *dev, unsigned int pipe);
int (*enbale_vblank) (struct drm_device *dev, unsigned int pipe);
irqreturn_t(*irq_handler) (int irq, void *arg);
int (*master_create)(struct drm_device *dev, struct drm_master *master);
int (*debugfs_init)(struct drm_minor *minor);
int (*gem_open_object) (struct drm_gem_object *, struct drm_file *);
struct drm_gem_object *(*gem_create_object)(struct drm_device *dev,
size_t size);
int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv,
uint32_t handle, uint32_t flags, int *prime_fd);
struct vm_operations_struct *gem_vm_ops;
struct drm_ioctrl_desc *ioctrl;
struct file_operations *fops;
int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv);
}
3)DRM的component注册机制
1、图显系统包括图显处理器、时序控制设备、编解码设备、PHY等,每个设备的驱动代码可以独立编写,最后通过drm api组织成一个整体,模块化大成;
2、设计思想:
1)master代表图显控制器;辅助单元component,是一堆的图显控制器外围设备,如HDMI、MIPI、LVDS等等。
2)组件式代码架构,由设备树代码和驱动代码组成。
设备树代码
在设备树文件中定义一个master设备结点,以master设备结点为核心,规划和其他组件之间的 连接关系,形成一个完整的图显系统拓扑结构图。
驱动代码
master负责绑定各个component组件,当所有组件均匹配、绑定成功后,DRM图显系统才能正常工作。任何一个组件或master结点被移除,都会引起解绑定事件并将subsystem的状态设置为down。
3、数据结构和接口
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/component.h
struct component_ops {
int (*bind)(struct device *comp, struct device *master, void *master_data);
int (*unbinde)()
}
4.
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson/meson_dw_hdmi.c
static const struct component_ops meson_dw_hdmi_ops = {
.bind = meson_dw_hdmi_bind,
.unbind = meson_dw_hdmi_unbind,
};
static int meson_dw_hdmi_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &meson_dw_hdmi_ops);
}
5.component匹配规则的创建
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_of.c
drm_of_component_match_add(struct device *master, struct component_match **matchptr, int (*compare), struct device_node *node)
/android/vendor/amlogic/common/kernel/common_5.4/drivers/base/component.c
--component_match_add_release()
3)master
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson/meson_drv.c
基本数据结构
1.drm_device代表显卡设备
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_device.h
struct drm_device {
struct device *dev;
struct drm_driver *driver;
struct drm_master *master;
struct drm_vblank_crtc *vblank;
struct drm_mode_config mode_config; //KMS
struct drm_vram_mm *vram_mm;
struct drm_fb_helper *fb_helper;
struct drm_device_dma *dma;
}
2.master
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_auth.h
struct drm_master {
struct drm_device *dev;
struct drm_master *lessor;
}
kernel下的master的初始化
static struct platform_driver meson_drm_platform_driver = {
.probe = meson_drv_probe,
.shutdown = meson_drv_shutdown,
.driver = {
.name = "meson-drm",
.of_match_table = dt_match,
},
};
module_platform_driver(meson_drm_platform_driver);
meson_drv_probe()
--for_each_endpoint_of_node() //读取设备树节点
//创建并初始化各个component
--meson_drv_bind_master(&pdev->dev)
----drm_vblank_init(drm, 1);
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_mode_config.c
----drm_mode_config_init(drm); //初始化KMS
//Hardware Inittialization
----meson_vpu_init(priv);
----meson_venc_init(priv);
----meson_vpp_init(priv);
----meson_viu_init(priv);
----component_bind_all(drm->dev, drm);//绑定所有组件
----meson_plane_create(priv);
----meson_overlay_create(priv);
----meson_crtc_create(priv);
----drm_dev_register(drm, 0); //注册drm核心
1.实例化CRTC的父设备(图显控制器)和功能函数;
2.CRTC的OBJECT添加到DRM中;
3.绑定drm_mode_object;
--component_master_add_with_match(&pdev->dev, &meson_drv_master_ops, match)
3)KMS
与CRTC的差异?由CRTC负责调用KMS接口,进行设置
数据结构
1.drm_mode_config
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_mode_config.h
struct drm_mode_config {
struct drm_property *tv_subconnector_property; //大量的各种属性
struct drm_mode_config_funcs *funcs;
struct drm_atomic_state *suspend_state;
struct drm_mode_config_helper_funcs *helper_private;
}
2.drm_mode_config_funcs
struct drm_mode_config_funcs {
struct drm_framebuffer *(*fb_create)();
struct drm_format_info *(*get_format_info)();
enum drm_mode_status (*mode_valid)();
int (*atomic_commit)()
int (*atomic_check)();
}
3.驱动实现
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_drv.c
static const struct drm_mode_config_funcs meson_mode_config_funcs = {
.output_poll_changed = am_meson_fb_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = meson_atomic_commit,
#ifdef CONFIG_DRM_MESON_USE_ION
.fb_create = am_meson_fb_create,
#else
.fb_create = drm_gem_fb_create,
#endif
};
用户会提交各种各样的图显系统的模式配置信息,比如屏幕分辨率以及色彩属性 都是模式配置

实例化KMS的各种资源

3)CRTC
功能:
DRM下的CRCT代表RGB数据通道,从drm_plane接收像素数据并将其混合到一起,传输给下级显示设备drm_encoder,由drm_display_mode控制时序;
数据结构
1.
struct drm_crtc {
struct drm_device *dev;
struct device_nde *port;
struct drm_plane *primary;
struct drm_plane *cursor;
struct drm_display_mode mode;
struct drm_display_mode hwmode;
struct drm_crtc_funcs *funcs;
drm_crtc_helper_funcs *helper_private;
drm_object_properties properties;
struct drm_property *scaling_filter_property;
struct drm_crtc_state *state;
}
2.主要用来控制 CRTC
struct drm_crtc_funcs {
void (*reset)(struct drm_crtc *crtc);
int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height);
int (*set_config)(struct drm_mode_set *set,
struct drm_modeset_acquire_ctx *ctx);
int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
uint32_t size,
struct drm_modeset_acquire_ctx *ctx);
int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags,
struct drm_modeset_acquire_ctx *ctx);
int (*enable_vblank)(struct drm_crtc *crtc);
}
3.配置 CRTC 相关模块时所需的中间辅助代码
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_modeset_helper_vtables.h
struct drm_crtc_helper_funcs {
void (*prepare)(struct drm_crtc *crtc);
void (*commit)(struct drm_crtc *crtc);
int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb);
int (*atomic_check)(struct drm_crtc *crtc, struct drm_crtc_state *state);
void (*atomic_begin)(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state);
void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state);
void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state);
}
4. drm_display_mode - 屏参配置
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_modes.h
drm_display_mode
* Active Front Sync Back
* Region Porch Porch
* <-----------------------><----------------><-------------><-------------->
* //////////////////////|
* ////////////////////// |
* ////////////////////// |.................. ................
* _______________
* <----- [hv]display ----->
* <------------- [hv]sync_start ------------>
* <--------------------- [hv]sync_end --------------------->
* <-------------------------------- [hv]total ----------------------------->*
struct drm_display_mode {
char name[DRM_DISPLAY_MODE_LEN];
/**
* @clock:
*
* Pixel clock in kHz.
*/
int clock; /* in kHz */
int hdisplay;
int hsync_start;
int hsync_end;
int htotal;
int hskew;
int vdisplay;
int vsync_start;
int vsync_end;
int vtotal;
int vscan;
/**
* @crtc_clock:
*
int crtc_clock;
int crtc_hdisplay;
int crtc_hblank_start;
int crtc_hblank_end;
int crtc_hsync_start;
int crtc_hsync_end;
int crtc_htotal;
int crtc_hskew;
int crtc_vdisplay;
int crtc_vblank_start;
int crtc_vblank_end;
int crtc_vsync_start;
int crtc_vsync_end;
int crtc_vtotal;
}
crtc初始化
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson/meson_crtc.c
meson_crtc_create()
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_crtc.c
--drm_crtc_init_with_planes(priv->drm, crtc, priv->primary_plane, NULL, &meson_crtc_funcs, "meson_crtc")
--meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)
----meson_crtc->enable_osd1 = meson_g12a_crtc_enable_osd1;
----meson_crtc->enable_vd1 = meson_g12a_crtc_enable_vd1;
----meson_crtc->viu_offset = MESON_G12A_VIU_OFFSET;
TEST
1、CRTC模式配置
echo 0xf > /sys/module/drm/parameters/debug
测试程序
./tests/modetest/modetest -M vmwgfx -D 0 -a -s 34@36:1280x960 -P 32@36:1280x960 -Ftiles
2、dmesg信息
[46312.678360] [drm:drm_vblank_enable [drm]] enabling vblank on crtc 0, ret: -22
[46312.728218] [drm:drm_ioctl [drm]] pid=1036, dev=0xe200, auth=1, DRM_IOCTL_MODE_SETCRTC
[46312.728238] [drm:drm_mode_setcrtc [drm]] [CRTC:36:crtc-0]
[46312.728593] [drm:drm_calc_timestamping_constants [drm]] crtc 36: hwmode: htotal 1737, vtotal 1011, vdisplay 861
[46312.729432] [drm:vmw_du_crtc_gamma_set [vmwgfx]] 0 r/g/b = 0x0000 / 0x0000 / 0x0000
[46416.541769] [drm:drm_ioctl [drm]] pid=94191, dev=0xe200, auth=1, DRM_IOCTL_MODE_GETCRTC
3)plane-即HWC
基本作用:
1)DRM PLANE 从 drm_framebuffer 接收数据,构造送显图像的雏形,完成图像的剪裁、缩放、旋转、叠加等效果后发送到&drm_crtc。除此之外,还通过 drm_plane_state来提供图像旋转功能。
2)与CRTC的关系,每个CRTC 需要定义一个 primary plane 以及可选的 overlay plane、cursor plane。
3) PLANE(即HWC)存在的意义主要有两点:
1、增强系统灵活性
对于桌面系统而言,显示器背景图案和鼠标光标通常是基本的显示元素,并且一直存在直到系统关机。因此,对于这种变化不是很频繁的基本图显输出,可以由通用 plane 来实现。而那些频繁变化的图显输出,交由专用的 plane 来实现。
2、提高系统性能
由于 plane 具备图像缩放、剪裁、多图层叠加等功能,因此,可以让 GPU 来将更多的精力放在图形渲染上,这种基本的图像处理交由 plane 实现。
4)plane的数量由硬件决定(即HWC合成的层数)
1、AML平台划分为:vd1、vd2、osd1、osd2等
2、
数据结构
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_plane.h
struct drm_plane {
struct drm_device *dev;
struct drm_mode_object base;
uint32_t possible_crtcs;
uint32_t *format_types;
unsigned int format_count;
uint64_t *modifiers;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
struct drm_plane_funcs *funcs;
enum drm_plane_type type;
struct drm_plane_helper_funcs *helper_private;
struct drm_property *scaling_filter_property;
}
enum drm_plane_type {
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_CURSOR,
}
初始化
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson/meson_plane.c
meson_plane_create()
--drm_universal_plane_init(priv->drm, plane, 0xFF, &meson_plane_funcs, supported_drm_formats)
--drm_plane_helper_add(plane, &meson_plane_helper_funcs);
--drm_plane_create_zpos_immutable_property(plane, 1);
--priv->primary_plane = plane;
更新plane,执行合成动作
1、meson_plane_atomic_update()
2、drm_atomic_helper_update_plane()
测试验证
echo 0xf > /sys/module/drm/parameters/debug
./tests/modetest/modetest -M vmwgfx -D 0 -a -s 34@36:1280x960 -P 32@36:1280x960 -Ftiles
dmesg
atomic_set_planes()
[ 438.833181] [drm:drm_ioctl [drm]] pid=8888, dev=0xe200, auth=1, DRM_IOCTL_MODE_OBJ_GETPROPERTIES
[ 438.833191] [drm:drm_ioctl [drm]] pid=8888, dev=0xe200, auth=1, DRM_IOCTL_MODE_GETPROPERTY
...
[ 794.887990] [drm:drm_atomic_set_fb_for_plane [drm]] Set [FB:227] for plane state 0000000082b6801c
...
[ 796.326527] [drm:drm_atomic_get_plane_state [drm]] Added [PLANE:32:plane-0] 00000000c67dc9f6 state to 000000006b70d79e
图层叠加流程

plane属性对应的显示参数关系图:

4)Encoder&connector
基本概念
Encoder包含外设控制器功能(LCD控制器、HDMI控制器);
connecter包含外设PHY或显示器参数;
数据结构
1、
/android/common-5.15/common/include/drm/drm_encoder.h
struct drm_encoder {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
char *name;
int encoder_type;
unsigned index;
uint32_t possible_crtcs;
uint32_t possible_clones;
struct drm_crtc *crtc;
struct list_head bridge_chain;
const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funs *helper_private;
}
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_encoder.h
struct drm_encoder_funcs {
void (*reset)(struct drm_encoder *encoder);
void (*destroy)(struct drm_encoder *encoder);
int (*late_register)(struct drm_encoder *encoder);
void (*early_unregister)(struct drm_encoder *encoder);
}
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_modeset_helper_vtables.h
struct drm_encoder_helper_funcs {
void (*dpms)(struct drm_encoder *encoder, int mode);
enum drm_mode_status (*mode_valid)(struct drm_encoder *crtc,
const struct drm_display_mode *mode);
void (*prepare)(struct drm_encoder *encoder);
void (*commit)(struct drm_encoder *encoder);
void (*atomic_mode_set)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder);
enum drm_connector_status (*detect)(struct drm_encoder *encoder,
struct drm_connector *connector);
void (*atomic_enable)(struct drm_encoder *encoder,
struct drm_atomic_state *state);
int (*atomic_check)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
}
meson下有两个encoder
1.HDMI
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_hdmi.c
2.CVBS
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_cvbs.c
3.lcd
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_lcd.c
int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...)
1、hdmi encoder初始化
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/meson/meson_dw_hdmi.c
static struct platform_driver meson_dw_hdmi_platform_driver = {
.probe = meson_dw_hdmi_probe,
.remove = meson_dw_hdmi_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = meson_dw_hdmi_of_table,
},
};
module_platform_driver(meson_dw_hdmi_platform_driver);
1.组件添加
meson_dw_hdmi_probe(struct platform_device *pdev)
--component_add(&pdev->dev, &meson_dw_hdmi_ops);
2.bind
meson_dw_hdmi_bind()
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_modeset_helper_vtables.h
--drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs);
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_encoder.c
2.构造encode结构体
--drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs,DRM_MODE_ENCODER_TMDS, "meson_hdmi");
----drm_mode_object_add(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
5)GEM
基本概念
Graphics Execution Management
Buffer管理和分配,类似android下的ion(低版本)、高版本DMA-BUFF(Android12),对接Android的gralloc hal
cma : contiguous memory allocator
vma
数据结构
1.drm_gem_object作为gem模块的内存分配单位
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_gem.h
struct drm_gem_object {
struct kref refcount;
unsigned handle_count;
struct drm_device *dev;
struct file *file;
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;
struct drm_gem_object_funcs *funcs;
}
2.用户空间ioctl接口
drm_gem_object的分配和销毁
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, 0),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, 0),
3.内核通过kmalloc、dma、mmap等内存分配接口 分配对应内存,再返回给应用程序
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_gem.c
/android/vendor/amlogic/common/kernel/common_5.4/drivers/dma-buf
1)object 的create/destroy等接口
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_drv.c
static struct drm_driver meson_driver = {
/* GEM Ops */
.dumb_create = am_meson_gem_dumb_create,
.dumb_destroy = am_meson_gem_dumb_destroy,
.dumb_map_offset = am_meson_gem_dumb_map_offset,
.gem_free_object_unlocked = am_meson_gem_object_free,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.ioctls = meson_ioctls,
.num_ioctls = ARRAY_SIZE(meson_ioctls),
}
函数 drm_mode_create_dumb()里创建 buffer 的功能实体,需要由驱动来实现,当然,驱动代码若未实现 SoC 特定的 create buffer 代码,也可以使用 DRM 提供的 drm_gem_cma_dumb_create()。
4.GEM object的使用
GEM object是在内核空间创建的,并没有导出到用户空间。在用户空间有两种操控方法,第一种是基于I/O 的方式,第二种是mmap 的方式。而实际应用中,普遍采用的是后者。
1)mmap映射给用户空间
DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, 0),
--drm_gem_dumb_map_offset()
----drm_gem_create_mmap_offset()
----drm_vma_node_offset_addr()
应用层完成GEM object的内存映射后,可以向该缓冲区刷新图像数据。完成fb相关的其他配置后,将地址信息传递给图像处理器的plane即可。
2)填充数据
/android/external/libdrm/tests/util/pattern.c
fill_tiles_rgb32()
1、应用层配置framebuffer
atomic_set_plane()
{
if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
handles, pitches, offsets, &p->fb_id, 0)) {
fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
return -1;
}
}
2、将GEM object的handle和FRAMEBUFFER的fb_id关联,由驱动获取GEM内存地址,再将地址信息传递给图像处理器的PLANE模块。
struct drm_gem_cma_object *obj;
obj = drm_fb_cma_get_gem_obj(fb, plane_index);
if (WARN_ON(!obj))
return;
paddr = obj->paddr;
6)FRAMEBUFFER
1、FB的地位层级
参照传统fb架构,Drm下实现的FRAMEBUFFER,额外增加了GEM管家统一管理FRAMEBUFFER
2、基本数据结构
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_framebuffer.h
struct drm_framebuffer {
struct drm_device *dev;
struct drm_mode_object base;
char comm[TASK_COMM_LEN]; //每一块fb内存 的名字
struct drm_format_info *format;
struct drm_framebuffer_funcs *funcs;
struct drm_gem_object *obj[4];
}
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_framebuffer.h
struct drm_framebuffer_funcs {
void (*destory)(struct drm_framebuffer *framebuffer);
int (*create_handle)(struct drm_framebuffer *fb, struct drm_file *file_priv, int *handle);
int (*dirty)(struct drm_framebuffer *framebuffer,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clip )
}
/android/vendor/amlogic/common/kernel/common_5.4/include/drm/drm_mode_object.h
struct drm_mode_object {
uint32_t id;
uint32_t type;
struct drm_object_properties *properties;
struct kref refcount;
void (*free_cb)(struct kref *kref);
};
3、用户层申请fb
/android/external/libdrm/include/drm/drm.h
对应的ioctrl
#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2)
/android/external/libdrm/include/drm/drm_mode.h
struct drm_mode_fb_cmd2 {
__u32 fb_id; //kernel DRM fb_create()创建的 fb id
__u32 width;
__u32 height;
__u32 pixel_format;
__u32 flags;
__u32 handles[4]; //GEM 的 handle,最多支持 4 个内存区域
__u32 pitches[4]; //单行数据所占的内存大小,单位是 Byte
__u32 offsets[4];
__u64 modifier[4];
};
/android/external/libdrm/xf86drmMode.c
drmModeAddFB2WithModifiers()
--DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)
----陷入到内核
4、kernel下的meson fb初始化
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_drv.c
am_meson_drm_bind()
--am_meson_drm_fbdev_init()
----am_meson_create_drm_fbdev(dev, drmdev->primary_plane);
------drm_fb_helper_prepare(dev, helper, &meson_drm_fb_helper_funcs);
--------am_meson_drm_fbdev_probe()
----------am_meson_drm_framebuffer_init()
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_fbdev.c
static const struct drm_fb_helper_funcs meson_drm_fb_helper_funcs = {
.fb_probe = am_meson_drm_fbdev_probe,
};
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_fb.c
struct drm_framebuffer_funcs am_meson_fb_funcs = {
.create_handle = am_meson_fb_create_handle, //must for fbdev emulate
.destroy = am_meson_fb_destroy,
};
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_fb.c
am_meson_drm_framebuffer_init()
--am_meson_fb_alloc()
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_framebuffer.c
----drm_framebuffer_init(dev, &meson_fb->base, &am_meson_fb_funcs);
------__drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,); //注册fb类型的 object
------drm_mode_object_register(dev, &fb->base);
5、plane使用fb
1) plane关联fb
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/drm_atomic_uapi.c
drm_atomic_plane_set_property()
--drm_atomic_set_fb_for_plane()
add_property(dev, p->plane_id, "FB_ID", p->fb_id);
add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
7)场景分析-使用drm播放logo
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_drv.c
am_meson_drm_bind()
/android/vendor/amlogic/common/kernel/common_5.4/drivers/amlogic/drm/meson_logo.c
--am_meson_logo_init()
----drm_framebuffer_put(fb);
6、Android下的libdrm
1、Android libdrm基本API: https://www.jianshu.com/p/d21d7093fbff
2、源码分析
谁来调用?hwcomposer
1、drm接口汇总
/android/external/libdrm/core-symbols.txt
1、打开/dev/dri/card0设备节点
/android/external/libdrm/xf86drm.c //User-level interface to DRM device,xFree86为历史命名
drmOpen()
--drmOpenWithType()
----drmOpenByName()
------drmOpenDevice()
/android/external/libdrm/xf86drm.h
#define DRM_DIR_NAME "/dev/dri"
#define DRM_PRIMARY_MINOR_NAME "card"
#define DRM_CONTROL_MINOR_NAME "controlD"
#define DRM_RENDER_MINOR_NAME "renderD"
2、打开/dev/dri/Control设备节点
drm_public int drmOpenControl(int minor)
{
return drmOpenMinor(minor, 0, DRM_NODE_CONTROL);
}
3、打开/dev/dri/Render设备节点
drm_public int drmOpenRender(int minor)
{
return drmOpenMinor(minor, 0, DRM_NODE_RENDER);
}
4、drmIoctl
/android/external/libdrm/xf86drm.c
drmIoctl(int fd, unsigned long request, void *arg);
/android/external/libdrm/include/drm/drm.h
#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc)
#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc)
5、常用API
1) drmSetClientCap(int fd, uint64_t capability, uint64_t value); //设置 drm 的capability 属性
2) drmModeGetResources(int fd); //获取connector、encoder、crtc的数量和ID信息
7、DRM调试
/sys/module/drm/parameters
echo 0xf > /sys/module/drm/parameters/debug //打开DRM打印
测试crtc
./tests/modetest/modetest -M vmwgfx -D 0 -a -s 34@36:1280x960 -P 32@36:1280x960 -Ftiles
dmesg --follow > /data/dmesg_drm_1.log &