【转】DRM (二)基本概念
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【转】DRM (二)基本概念相关的知识,希望对你有一定的参考价值。
参考技术A 如下是DRM Driver 所依赖的 Component Framework。framebuffer是驱动和应用层都能访问的一块内存区域。
可以把它理解为一块画布。画画之前需要将它格式化,指定是画油画,还是国画(色彩模式,比如 RGB24,YUV 等),画布需要多大(分辨率)。
直译为阴极摄像管上下文。它读取当前扫描缓冲区的像素数据,借助于PLL电路,生成视频模式定时信号。
简单来说,它就是显示输出的上下文,对内连接 Framebuffer ,对外连接 Encoder。
可以把它理解为扫描仪。它扫描画布(framebuffer)上的内容,叠加 planes 的内容,传给 encoder。
直译为 编码器。它将内存的像素编码转换为显示器所需要的信号。
可以想象成,它将你的画在不同类型、型号的显示设备上显示,将其转换成不同的电信号,比如 DVID、VGA、YPbPr、CVBS、Mipi、eDP 等。
直译为 连接器。它常常对应于物理连接器 (VGA、DVI、FPD-Link、HDMI、DisplayPort、S-Video …) ,连接到一个显示输出设备 (monitor、laptop panel …) 。
与输出设备相关的信息(如连接状态、EDID数据、DPMS状态或支持的视频模式),也存储在 Connector 内。
直译为 平面。Plane和 framebuffer 一样是内存地址。
它的作用是干什么呢?想象这样一个场景,笔者正在很不专心地一边看动作大片,一边写文章。动作大片每一帧的变化都很大,需要全幅更新。写文章则可能半天挤不出一个字,基本上不需要更新。这是显卡使用的两个极端。一个是全幅高速更新的 Video Mode,一个是文字交互这种小范围更新的 Graphics Mode。此时轮到 Planes 出马,它给 Video 刷新提供了高速通道,使 Video 单独为一个图层,可以叠加在 Graphic 上面或下面,并具有缩放等功能。
Planes可以有多个。最后扫描仪(CRTC)扫描的画,实际上是framebuffer 和 若干planes 的组合(Blending)。
厂商可能会对planes的类型、用途做出规范。比如,
DRM_PLANE_TYPE_PRIMARY: 一定要有,由于显示背景或者图像内容
DRM_PLANE_TYPE_OVERLAY: 用于显示Overlay
DRM_PLANE_TYPE_CURSOR: 用于显示鼠标
Linux DRM(二)基本概念和特性
Display中的DRM模块介绍
DRM/KMS 基本组件介绍
Android Qcom lcd display 学习
DRM
Linux DRM(二)基本概念和特性
DRM(Direct Rendering Manager)学习简介
DRM 的诞生就是用来处理多个程序对 Video Card 资源的协同使用问题,通过单单一个device node来控制所有硬件设备、通用访问硬件接口、通用内存管理机制。Framebuffer的架构它不能用于处理 基于视频卡的 GPU 的 3D 加速。
DRM从模块上划分,可以简单分为3部分:libdrm、KMS、GEM
libdrm:对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装。
KMS: 更新画面和设置显示参数。更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置。设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等。
GEM:Graphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。
KMS
CRTC 对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller
ENCODER 负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或DSI Controller
CONNECTOR 连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder驱动绑定在一起
FrameBuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素
Qcom Code
DRM top level control
kernel/msm-4.19/techpack/display/msm/msm_drv.c
Controls all the blocks – crtc/encoder/planes/interface Adreno DPU 显示图像处理器相关
kernel/msm-4.19/techpack/display/msm/sde/sde_kms.c
msm_pdev_probe
add_display_components(&pdev->dev, &match);
component_match_add(dev, matchptr, compare_of, node);/*qcom,sde-kms connectors = <&sde_dsi>;*/
component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
/* master在component框架中component_list链表找到适配于自己的component组件,注册自己为master到component框架
dsi_display调用component_add 把自己添加到component_list, crtc+plane+encoder+connector 都是在DSI Controller驱动中 */
static const struct component_master_ops msm_drm_ops =
.bind = msm_drm_bind,
.unbind = msm_drm_unbind,
;
msm_drm_bind /* 当该master下的所有设备(dsi wb dp等)都初始化完成后,调用该回调的bind */
msm_drm_init(dev, &msm_driver);
msm_mdss_init(ddev);
msm_component_bind_all(dev, ddev);/* Bind all our sub-components: */
component_bind_all(dev, drm_dev);/* 调用所有从设备的bind 调用dsi_display_bind*/
_msm_drm_init_helper(priv, ddev, dev, pdev);
sde_kms_init(ddev);/* .compatible = "qcom,sde-kms" */
msm_kms_init(&sde_kms->base, &kms_funcs);/*.hw_init .prepare_commit kms_func函数注册
(kms)->funcs->hw_init(kms); /* 上述init中注册*/
_sde_kms_hw_init_blocks(sde_kms, dev, priv);
_sde_kms_drm_obj_init(sde_kms); /* create the DRM related objects */
_sde_kms_get_displays(sde_kms) /* 这里对应到Interface了 */
dsi_display_get_active_displays(sde_kms->dsi_displays,sde_kms->dsi_display_count);/* dsi */
wb_display_get_displays(sde_kms->wb_displays,sde_kms->wb_display_count);/* wb */
dp_display_get_displays(sde_kms->dp_displays,sde_kms->dp_display_count);/* dp */
_sde_kms_setup_displays(dev, priv, sde_kms)
sde_encoder_init(dev, &info);
drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
sde_connector_init(dev,encoder,dsi_display_get_drm_panel(display),display,
&dsi_ops,DRM_CONNECTOR_POLL_HPD, DRM_MODE_CONNECTOR_DSI);
drm_connector_init(dev,&c_conn->base, &sde_connector_ops, connector_type);
sde_plane_init(dev, catalog->sspp[i].id, primary,(1UL << max_crtc_count) - 1, 0);
drm_universal_plane_init(dev, plane, 0xff, &sde_plane_funcs, psde->formats,/* plane初始化 */
psde->nformats,NULL, type, NULL);
sde_crtc_init(dev, primary_planes[i]); /* Create one CRTC per encoder */
drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs, NULL);
drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
priv->planes[i]->possible_crtcs =(1 << priv->num_crtcs) - 1; //plane to crtcs
priv->encoders[i]->possible_crtcs = (1 << priv->num_crtcs) - 1;//crtcs ti encoder
/* 一个CRTC 可以连接多个 Encoder 用于实现多屏显示 */
PLANE
Linux DRM PLANE完全解析
PLANE 代表显示图层,每个 CRTC 需要定义一个 primary plane 以及可选的 overlay plane、cursor plane
PLANE 存在的意义主要有两点:增强系统灵活性、提高系统性能
(1)对于背景和光标等变化不频繁的基本图显输出,可用通用 plane 来实现。而那些频繁变化则可由专用的 plane 来实现。
(2)plane 具备图像缩放、剪裁、多图层叠加等基本的图像处理功能,因此,可以让 GPU 来将更多的精力放在图形渲染上。
drm_universal_plane_init
type = drm_plane_type
enum drm_plane_type
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_CURSOR,
;
static const struct drm_plane_funcs sde_plane_funcs =
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = sde_plane_destroy,
...
CTRC
Linux DRM CRTC完全解析
DRM 下的 CRTC 代表 RGB 数据管道,从&drm_plane 接收像素数据并将其混合到一起,传输给下级显示设备&drm_encoder。由&drm_display_mode 控制时序。
CRTC 模块存在的意义主要有以下三点:
(1)统一协调 FB、DRM、用户空间代码之间对图显输出的控制
(2)图显模式的配置权回收到 kernel 空间,避免用户空间代码直接控制图显控制器引起 kernel panic
(3)kernel 空间实现的图显输出暂停与恢复相关代码,可以方便的调用图显控制器的配置函数
drm_crtc_init_with_planes 初始化 CRTC 模块
drm_crtc_helper_add 注册 CRTC 功能
static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs =
.mode_fixup = sde_crtc_mode_fixup,
.disable = sde_crtc_disable,
.atomic_enable = sde_crtc_enable,
.atomic_check = sde_crtc_atomic_check,
.atomic_begin = sde_crtc_atomic_begin,
.atomic_flush = sde_crtc_atomic_flush,
;
ENCODER/CONNECTOR
kernel图显系统的DRM ENCODER和CONNECTOR模块
DRM ENCODER 和 CONNECTOR 模块由图显外设抽象而来,从传统意义上来讲,ENCODER 属于控制器部分,将内存的像素编码转换为显示器所需要的信号例如VGA、MIPI;而 CONNECTOR 包含外设 PHY 或者显示器参数,代表连接的显示设备连接状态,支持的视频模式等。这两部分紧密关联,因此,软件 DRM 架构下的两者需要按照一个整体来理解和实现。
drm_encoder_init
drm_encoder_helper_add
static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs =
.mode_set = sde_encoder_virt_mode_set,
.disable = sde_encoder_virt_disable,
.enable = sde_encoder_virt_enable,
.atomic_check = sde_encoder_virt_atomic_check,
;
static const struct sde_connector_ops dsi_ops =
.detect = dsi_conn_detect,
.get_modes = dsi_connector_get_modes,
.get_info = dsi_display_get_info,
.set_backlight = dsi_display_set_backlight,
.clk_ctrl = dsi_display_clk_ctrl,
.set_power = dsi_display_set_power,
drm_connector_init
static const struct drm_connector_funcs sde_connector_ops =
.reset = sde_connector_atomic_reset,
.detect = sde_connector_detect,
.destroy = sde_connector_destroy,
.fill_modes = sde_connector_fill_modes,
DSI/PANEL
kernel/msm-4.19/techpack/display/msm/dsi/dsi_display.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_ctrl.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_phy.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_panel.c
module_param_string(name, string, len, perm); 内核把字符串直接复制到程序中的字符数组
name是外部的参数名,string是内部的变量名,len是以string命名的buffer大小,perm表示sysfs的访问权限
static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
module_param_string(dsi_display0, dsi_display_primary, MAX_CMDLINE_PARAM_LEN, 0600);
MODULE_PARM_DESC(dsi_display0, 参数格式的说明
"msm_drm.dsi_display0=<display node>:<configX> where <display node> is 'primary dsi display node name' and
<configX> where x represents index in the topology list");
cat /proc/cmdline: msm_drm.dsi_display0=qcom,mdss_dsi_hx8399_1080_video
dsi_phy_drv_register();
platform_driver_register(&dsi_phy_platform_driver);
dsi_ctrl_drv_register();
platform_driver_register(&dsi_ctrl_driver);
dsi_display_parse_boot_display_selection(); /* 解析得到 boot_display.name boot_disp_en = true */
platform_driver_register(&dsi_display_driver);
Linux Command Line 详细解析
saved_command_line 用于保存没有处理过的命令行参数
dsi_display_dev_probe
boot_disp_en = true /* 当前dsi控制器支持的panel列表中查找uefi传递的panel */
of_find_node_by_name(mdp_node, boot_disp->name);
else
display_panel = getDisplayPanel();
display_panel_name = strstr ( saved_command_line, "hx8399");
panel_node = of_parse_phandle(node,"qcom,dsi-default-panel1", 0);
dtsi:
qcom,dsi-default-panel1 = <&dsi_hx8399_1080_video>;
dsi_display_init(display);
dsi_display_dev_init(display);
dsi_display_parse_dt(display);/* 解析dtsi包括TE,初始化一些handel 包括dsi_ctrl dsi_phy */
dsi_display_res_init(display);
dsi_panel_get(&display->pdev->dev, display->panel_node, display->parser_node,
display->display_type, display->cmdline_topology); ↓↓↓
dsi_panel_parse_power_cfg(panel);
dsi_panel_parse_bl_config(panel); /* parse dtsi */
drm_panel_add(&panel->drm_panel); /* Let DRM can get panel name */
component_add(&pdev->dev, &dsi_display_comp_ops);
/* component框架中通过component_add添加管理多个组件的加载,保证都正常工作
其中注册了两个函数 .bind = dsi_display_bind, .unbind = dsi_display_unbind */
Linux component子系统
kernel图显系统里DRM模块的注册与绑定
内核加载每个模块时间不定,component框架能保证在最后初始化的设备加载前,所需设备全部加载完毕。注册主要分为两类设备:
(1)master即超级设备,执行probe使用component_master_add_with_match函数注册自己到component框架中。
(2)component即普通设备,执行probe使用component_add函数注册自己到component框架中。
Linux notifier chain子系统
Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain),而对应Display来说紧密相关就是TP,通过内核通知链通知TP进行Resume和Suspend等操作。
在msm_kms_init初始化的时候注册的函数,通过内核通知链去通知TP resume和suspend流程
.prepare_commit = sde_kms_prepare_commit,
_sde_kms_drm_check_dpms(state, DRM_PANEL_EARLY_EVENT_BLANK);
.complete_commit = sde_kms_complete_commit,
_sde_kms_drm_check_dpms(old_state, DRM_PANEL_EVENT_BLANK);
_sde_kms_drm_check_dpms
sde_kms_get_blank(crtc->state, connector->state);
case SDE_MODE_DPMS_ON:
blank = DRM_PANEL_BLANK_UNBLANK; //new mode:亮屏操作
break;
case SDE_MODE_DPMS_LP1:
case SDE_MODE_DPMS_LP2:
blank = DRM_PANEL_BLANK_LP;
break;
case SDE_MODE_DPMS_OFF:
default:
blank = DRM_PANEL_BLANK_POWERDOWN;//new mode:灭屏操作
break;
notifier_data.data = &new_mode;
drm_panel_notifier_call_chain(connector->panel,event, ¬ifier_data);
tp driver:
data->fb_notif.notifier_call = fb_notifier_callback;
drm_panel_notifier_register(data->active_panel,&data->fb_notif);
fb_notifier_callback
if (action == DRM_PANEL_EARLY_EVENT_BLANK && *transition == DRM_PANEL_BLANK_POWERDOWN)
mxt_secure_touch_stop(mxt_dev_data, 0);
else if (action == DRM_PANEL_EVENT_BLANK)
if (*transition == DRM_PANEL_BLANK_UNBLANK)
mxt_resume(&mxt_dev_data->client->dev); /* resume */
else if (*transition == DRM_PANEL_BLANK_POWERDOWN)
mxt_suspend(&mxt_dev_data->client->dev);/* suspend */
————————————————
以上是关于【转】DRM (二)基本概念的主要内容,如果未能解决你的问题,请参考以下文章