rk3288_7.1平台基于DRM框架的HDMI开发总结
概述
rk3288在kernel4.4开始使用的显示框架是基于DRM的。DRM全称是Direct Rendering Manager,管理进行显示输出的, buffer分配, 帧缓冲.
libdrm库提供了一系列友好的控制封装, 使用户可以方便的进行显示的控制, 但并不是 只能通过libdrm库来控制drm, 用户可以直接操作drm的
ioctl或者是使用framebuffer的 接口实现显示操作。
设备节点
DRM 的设备节点为 “/dev/dri/cardX”, X为0-15的数值.
默认使用的是/dev/dri/card0
节点控制hdmi和lvds的显示开关
HDMI:
echo on > /sys/class/drm/card0-HDMI-A-1/status //打开hdmiout显示
echo off > /sys/class/drm/card0-HDMI-A-1/status //关闭hdmiout显示
cat /sys/class/drm/card0-HDMI-A-1/status //打开是connected,关闭是disconnected
LVDS:
ls -dl /sys/class/drm/*/st* && cat /sys/class/drm/card0-LVDS-1/st*
echo on > /sys/class/drm/card0-LVDS-1/st* //开
echo off > /sys/class/drm/card0-LVDS-1/st* //关
cat /proc/device-tree/display-subsystem/route/route-lvds/status
HDMI/DP相关
1、首先需要在对应dts文件使能对应显示设备节点
打开显示设备执行相关hdmi的probe函数,注册显示设备驱动,如打开 HDMI 需要添加:
&hdmi {
status = “okay”;
};
使能显示接口组件
display-subsystem 注册会把所有打开的设备以组件的形式加在一起,等所有的组件加载完毕后,统一进行 bind/unbind(绑定或者解绑)
基于component框架, 在probe阶段解析dts中各个设备的信息, 加到component match 列表中, 等所有设备加
载完毕后, 就会引发master设备的bind.
drm/rockchip为什么需要使用Component?
drm下挂了许多的设备, 启动顺序经常会引发各种问题:
1.一个驱动完全有可能因为等另一个资源的准备, 而probe deferral, 导致顺序不定
2.子设备没有加载好, 主设备就加载了, 导致设备无法工作
3.子设备相互之间可能有时序关系,不定的加载顺序,可能带来有些时候设备能工作,有些时候又不能工作
4.现在编kernel是多线程编译的,编译的前后顺序也会影响驱动的加载顺序.
这时就需要有一个统一管理的机制, 将所有设备统合起来, 按照一个统一的顺序加载,
Display-subsystem正是用来解决这个问题的, 依赖于component的驱动, 通过这个驱动,
可以把所有的设备以组件的形式加在一起, 等所有的组件加载完毕后, 统一进行bind/unbind.
开机 LOGO
如果uboot logo未开启,那kernel阶段也无法显示开机logo,只能等到android启动后才能看到显示;在dts里面将对应的route使能即可打开uboot logo支持,比如:
打开hdmi的uboot logo显示:
&route_hdmi {
status = “okay”; //okay打开,disabled关闭
connect = <&vopb_out_hdmi>; //vopb_out_hdmi对应hdmi关联vopb
};
2、当显示设备节点打开时,显示接口对应vopb和vopl的ports都会打开,需要关闭用不到的那个VOP。
比如hdmi绑定到vopb需要添加:
&hdmi_in_vopl {
status = “disabled”;
};
反之若绑定到 vopl 则添加:
&hdmi_in_vopb {
status = “disabled”;
};
如果平台只有一个 VOP,可以跳过。
3、查看当前可用显示分辨率列表
rk3288:/ # cat /sys/class/drm/card0-HDMI-A-1/modes
3840x2160p30
3840x2160p30
3840x2160p25
3840x2160p24
1920x1080p60
1920x1080p60
1920x1080p50
1280x720p60
1280x720p50
800x600p75
800x600p72
720x576p50
720x480p60
4、查看当前分辨率
rk3288:/ # cat /sys/class/drm/card0-HDMI-A-1/mode
3840x2160p24
5、查看vop状态(rk3288/3399平台有2vop,vopb支持4k,vopl支持2k)
说明:
ACTIVE 表示正在运行中,如果是VOP [ff940000.vop]: DISABLED,表示vop没在运行。;
ff930000对应vopb的寄存器vopb: [email protected];
ff940000对应vopb的寄存器vopl: [email protected];
3840x2160p24是对应当前显示分辨率和频率
130|rk3288:/ $ cat /d/dri/0/summary
VOP [ff930000.vop]: ACTIVE //active表示正在运行中,ff930000对应vopb的寄存器vopb: [email protected]
Connector: HDMI-A //对应屏的接口名称
overlay_mode[0] bus_format[100a] output_mode[f] color_space[3]
Display mode: 3840x2160p24 //对应当前显示分辨率
clk[297000] real_clk[297000] type[40] flag[5]
H: 3840 5116 5204 5500
V: 2160 2168 2178 2250
win0-0: ACTIVE
format: AB24 little-endian (0x34324241) SDR[0] color_space[0]
csc: y2r[0] r2r[0] r2y[0] csc mode[0]
zpos: 0
src: pos[0x0] rect[1920x1080]
dst: pos[0x0] rect[3840x2160]
buf[0]: addr: 0x0378c000 pitch: 7680 offset: 0
win1-0: DISABLED
win2-0: DISABLED
win2-1: DISABLED
win2-2: DISABLED
win2-3: DISABLED
win3-0: DISABLED
win3-1: DISABLED
win3-2: DISABLED
win3-3: DISABLED
post: sdr2hdr[0] hdr2sdr[0]
pre : sdr2hdr[0]
post CSC: r2y[0] y2r[0] CSC mode[1]
VOP [ff940000.vop]: ACTIVE
Connector: LVDS
overlay_mode[0] bus_format[100a] output_mode[0] color_space[0]
Display mode: 1280x720p60
clk[74250] real_clk[74250] type[8] flag[5]
H: 1280 1390 1430 1650
V: 720 725 730 750
win0-0: ACTIVE
format: AB24 little-endian (0x34324241) SDR[0] color_space[0]
csc: y2r[0] r2r[0] r2y[0] csc mode[0]
zpos: 0
src: pos[0x0] rect[1280x720]
dst: pos[0x0] rect[1280x720]
buf[0]: addr: 0x05e3c000 pitch: 5120 offset: 0
win1-0: DISABLED
win2-0: DISABLED
win2-1: DISABLED
win2-2: DISABLED
win2-3: DISABLED
win3-0: DISABLED
win3-1: DISABLED
win3-2: DISABLED
win3-3: DISABLED
post: sdr2hdr[0] hdr2sdr[0]
pre : sdr2hdr[0]
post CSC: r2y[0] y2r[0] CSC mode[1]
6、查看HDMI当前输出状态
rk3288:/ # cat /d/dw-hdmi/status
PHY: enabled Mode: HDMI
Pixel Clk: 297000000Hz TMDS Clk: 297000000Hz
Color Format: RGB Color Depth: 8 bit
Colorimetry: ITU.BT709 EOTF: Unsupported
HDMI Output Status 表示当前 PHY 状态,只有当 PHY 使能的时候才会有后续打印。
Pixel Clk 表示当前输出的像素时钟。
TMDS Clk 表示当前输出 HDMI 符号率。
Color Format 表示输出的颜色格式,取值 RGB、YUV444、YUV422、YUV420。
Color Depth 表示输出的颜色深度,取值 8bit、10bit、12bit、16bit。
Colorimery 表示输出的颜色标准,取值 ITU.BT601、ITU.BIT709、ITU.BT2020
EOTF 表示输出的 HDR 电光转换曲线方式,这里不支持。
7、查看HDMI的插拔状态:
rk3288:/ $ cat sys/class/switch/hdmi/state
8、绑定PLL只有RK3399需要)
一般是只有rk3399需要,但是我之前调试的rk3288双hdmi out输出的时候也设置绑定了一下
rk3399 的hdmi所绑定的vop时钟需要挂载到vpll上,若是双显,需将另一个vop时钟挂
到cpll,这样可以分出任意dclk的频率;如当hdmi绑定到vopb时配置
&vopb {
assigned-clocks = <&cru DCLK_VOP0_DIV>;
assigned-clock-parents = <&cru PLL_VPLL>;
};
&vopl {
assigned-clocks = <&cru DCLK_VOP1_DIV>;
assigned-clock-parents = <&cru PLL_CPLL>;
}
当hdmi绑定到vopl时配置:
&vopb {
assigned-clocks = <&cru DCLK_VOP0_DIV>;
assigned-clock-parents = <&cru PLL_CPLL>;
};
&vopl {
assigned-clocks = <&cru DCLK_VOP1_DIV>;
assigned-clock-parents = <&cru PLL_VPLL>;
};
查看显示时钟:
rk3288:/ # cat /sys/kernel/debug/clk/clk_summary | grep vop
dclk_vop1 0 2 74250000 0 0
dclk_vop0 2 3 297000000 0 0
hclk_vop0 4 5 49500000 0 0
hclk_vop1 0 4 49500000 0 0
aclk_vop1 0 4 297000000 0 0
aclk_vop0 4 5 297000000 0 0
关于dclk:
即pixel clock, 像素时钟, 该时钟由具体的显示timing决定, 如果dclk不正确, 可能导致fps不对或直接不显示.edp, mipi, lvds等显示接口对应dclk的容忍性较好, 有些偏差也不影响正常显示.
但hdmi, dp等高清显示接口,是有严格要求的, 这类显示接口的频率要给的很精准.
像素时钟,跟数据传输有关,液晶面板分辨率越高,像素时钟信号的频率也越高。在一行内,像素时钟的个数与液晶面板一行内所具有的像素数量相等。例如,对于1024×768的液晶面板,
一行有1024个像素,则在一行中(对应于有效视频区间)像素时钟的个数也是1024个。
aclk_vop:
如果该时钟频率太低, 可能会导致显示出现抖动, 另外如果aclk 没有使能的话, 访问vop的寄存器也可能引发总线挂死
hclk_vop:
如果该时钟未使能, 不能访问vop的寄存器, 一但访问vop寄存器, 会造成总线挂死.
9、主副显示器设置:
可以在产品配置目录下的system.prop里(如 device/rockchip/rk3288/rk3288_mid/system.prop)添加如下两个属性:
sys.hwc.device.primary = HDMI-A //设置显示接口做为主显,这里设置HDMI接口为主显
sys.hwc.device.extend = LVDS //设置显示接口做为副显,这里设置LVDS接口为副显
默认情况下(即以上属性未配置时),不支持热拔插设备(如 CVBS/MIPI/LVDS 等)会作为主显,支持热插拔设备(如HDMI/DP等)会作为次显。通常主、副显只配置一个显示接口,
例如RK3399MID SDK默认采用的配置, HDMI作为主显示,EDP作为副显示。
关于接口名称可以参见hardware/rockchip/hwcomposer/drmresources.cpp里的定义:
struct type_name connector_type_names[] = {
{ DRM_MODE_CONNECTOR_Unknown, “unknown” },//未知接口
{ DRM_MODE_CONNECTOR_VGA, “VGA” }, //VGA
{ DRM_MODE_CONNECTOR_DVII, “DVI-I” },//DVI,暂不支持
{ DRM_MODE_CONNECTOR_DVID, “DVI-D” },//DVI,暂不支持
{ DRM_MODE_CONNECTOR_DVIA, “DVI-A” },//DVI,暂不支持
{ DRM_MODE_CONNECTOR_Composite, “composite” },//不支持
{ DRM_MODE_CONNECTOR_SVIDEO, “s-video” },//S端子
{ DRM_MODE_CONNECTOR_LVDS, “LVDS” },//LVDS
{ DRM_MODE_CONNECTOR_Component, “component” },//分量信号YPbPr
{ DRM_MODE_CONNECTOR_9PinDIN, “9-pin DIN” },//不支持
{ DRM_MODE_CONNECTOR_DisplayPort, “DP” },//DP
{ DRM_MODE_CONNECTOR_HDMIA, “HDMI-A” },//HDMI A型口
{ DRM_MODE_CONNECTOR_HDMIB, “HDMI-B” },//HDMI B型口,不支持
{ DRM_MODE_CONNECTOR_TV, “TV” },// CVBS
{ DRM_MODE_CONNECTOR_eDP, “eDP” },//EDP
{ DRM_MODE_CONNECTOR_VIRTUAL, “Virtual” },//不支持
{ DRM_MODE_CONNECTOR_DSI, “DSI” },//MIPI
};
10、主副屏接口查询:
rk3288:/ $ getprop | grep sys.hwc.device.main //查询当前主显的输出接口
[sys.hwc.device.main]: [HDMI-A-1]
rk3288:/ $ getprop | grep sys.hwc.device.aux //查询当前副显的输出接口
[sys.hwc.device.aux]: [LVDS]
11、通过persist.sys.resolution.main以及persist.sys.resolution.aux设置主副屏分辨率,
每次设置完更新 sys.display.timeline(每次加 1)使分辨率生效,例子如下(可以getprop | grep sys.display.timeline查看下sys.display.timeline属性):
设置 4k60:
setprop persist.sys.resolution.main [email protected]
setprop sys.display.timeline 1
设置 1080p60:
setprop persist.sys.resolution.main [email protected]
setprop sys.display.timeline 2
12、如何输出特殊分辨率
相关文件:
kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
kernel/drivers/gpu/drm/drm_edid.c
如上修改:
1、在edid_cea_modes数据的最后定义特殊的分辨率(drm_edid.c文件中)。
2、把def_mode数组的第一个值改成特殊分辨率对应的 vic,我们上面定义的是108(dw-hdmi.c文件中).
3、edid = NULL;强制把edid赋为NULL,不管有没有读到edid都强制按def_modes来显示。
13、modetest
modetest是libdrm源码自带的调试工具, 可以对drm进行一些基础的调试
获取modetest:
mmm external/libdrm/tests
编译得到:
[email protected]:/work/zwei/czd/rk3288_7.1_mid$ find out/target/product/rk3288/ -name modetest
out/target/product/rk3288/symbols/system/bin/modetest
out/target/product/rk3288/system/bin/modetest
14、开机起来设置分辨率流程分析:
首先会通过i2c获取屏的edid信息。
EDID的全称是Extended Display Identification Data(扩展显示标识数据),共有128字节。其中包含有关显示器及其性能的参数,包括供应商信息、最大图像大小、颜色设置、厂商预设置、
频率范围的限制以及显示器名和***的字符串等等。形象地说,EDID就是显示器的身份证、户口本、技能证书等证件的集合,目的就是告诉别人我是谁,我从哪来,我能干什么。
如何查看edid信息?
cat sys/class/drm/card0-HDMI-A-1/edid > /data/edid.bin //然后把edid.bin拷贝出来
或者:cat sys/class/drm/card0-HDMI-A-1/edid |busybox hexdump
驱动文件清单
Core:
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
framebuffer drivers/gpu/drm/rockchip/rockchip_drm_fb.c
gem drivers/gpu/drm/rockchip/rockchip_drm_gem.c
vop:
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
lvds:
drivers/gpu/drm/rockchip/rockchip_lvds.c
rga:
drivers/gpu/drm/rockchip/rockchip_drm_rga.c
mipi:
drivers/gpu/drm/rockchip/dw-mipi-dsi.c
hdmi:
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
drivers/gpu/drm/bridge/dw-hdmi.c
inno hdmi:
drivers/gpu/drm/rockchip/inno_hdmi.c
edp:
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
dp:
drivers/gpu/drm/rockchip/cdn-dp-core.c
drivers/gpu/drm/rockchip/cdn-dp-reg.c
具体相关接口分析:
基于component框架, 在probe阶段解析dts中各个设备的信息, 加到component match 列表中, 等所有设备加
载完毕后, 就会引发master设备的bind.
Rockchip drm主设备驱动:drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
VOP驱动:drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
图层接口:
static const struct drm_plane_helper_funcs plane_helper_funcs = {
// 预先对图层进行处理
.prepare_fb = vop_plane_prepare_fb,
// 图层显示完成后的处理
.cleanup_fb = vop_plane_cleanup_fb,
// 在显示前进行参数检查
.atomic_check = vop_plane_atomic_check,
// 更新图层参数
.atomic_update = vop_plane_atomic_update,
// 关闭图层
.atomic_disable = vop_plane_atomic_disable,
};
VOP接口:
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
// 使能vop, 在这里面会将timing配好
.enable = vop_crtc_enable,
// 关闭vop
.disable = vop_crtc_disable,
// 对timing进行检查修正
.mode_fixup = vop_crtc_mode_fixup,
// 在一帧显示开始前做的处理
.atomic_begin = vop_crtc_atomic_begin,
// 检查显示的参数
.atomic_check = vop_crtc_atomic_check,
// 提交硬件显示
.atomic_flush = vop_crtc_atomic_flush,
};
接口执行顺序:
vop_plane_atomic_check //在显示前进行参数检查
vop_plane_prepare_fb //预先对图层进行处理
vop_plane_atomic_update //更新图层参数
vop_crtc_atomic_flush //提交硬件显示
vop_plane_cleanup_fb //图层显示完成后的处理
对应文件:
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
./drivers/gpu/drm/drm_edid.c
调用get_modes获取分辨率的时候会去调用.get_modes = dw_hdmi_connector_get_modes
->edid = drm_get_edid(connector, hdmi->ddc); //获取edid
->drm_do_get_edid
->drm_do_probe_ddc_edid //尝试通过i2获取edid信息,成功时为0,失败时为-1。
->drm_get_displayid //如果edid存在,则获取显示id
如果获取到edid,则调用:
hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); //检测显示器是否是HDMI,是就返回true,否则return false;
->drm_find_cea_extension //
->drm_find_edid_extension //在edid中搜索CEA扩展块
->if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
return true; //因为HDMI标识符在特定于供应商的块中,所以从CEA扩展的所有数据块中搜索它。
hdmi->sink_has_audio = drm_detect_monitor_audio(edid); //检测显示器audio音频功能,如果显示器支持音频,就返回true,否则return false;
drm_mode_connector_update_edid_property(connector, edid); //更新连接器的edid属性
cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
ret = drm_add_edid_modes(connector, edid); //从连接的显示器中读取到的edid数据添加分辨率[mode](如果mode可用),
*edid spec说模式应该按以下顺序优先:
*-首选详细模式
*-来自基础块的其他详细模式
*-扩展块的详细模式
*-CVT 3字节代码模式
*-标准定时代码
*-已建立的时间代码
*-根据GTF或CVT范围信息推断的模式
/* Store the ELD */
drm_edid_to_eld(connector, edid); //从edid构建ELD, 填充ELD(类似于edid的数据)缓冲区以传递给音频驱动程序。这个HDCP和端口字段留给图形驱动程序填写。
drm_mode_connector_update_hdr_property(connector, metedata); //原子替换现有blob属性
如果获取不到edid,则调用:
hdmi->sink_is_hdmi = true;
hdmi->sink_has_audio = true;
for (i = 0; i < sizeof(def_modes); i++) { //去遍历def_mode数组的值,他们对应在edid_cea_modes里面的分辨率[mode]-->取自CEA-861规范。
mode = drm_display_mode_from_vic_index(connector, def_modes, 31, i);
->调用vic = svd_to_vic(video_db[video_index]); // vic值对应 edid_cea_modes 中的分辨率参数
->if (!drm_valid_cea_vic(vic))
->vic > 0 && vic < ARRAY_SIZE(edid_cea_modes); 则设置 edid_cea_modes 里面相应的分辨率
->newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
->drm_mode_copy(nmode, mode);分配和复制现有模式, 返回:成功时指向复制模式的指针,错误时为空。
15、Dclk 频率(像素时钟)
只要是数字信号处理电路,就必须有时钟信号。在液晶面板中,像素时钟是一个非常重要的时钟信号。像素时钟信号的频率与液晶面板的工作模式有关,液晶面板分辨率越高,像素时钟信号的频率也越高。
在一行内,像素时钟的个数与液晶面板一行内所具有的像素数量相等。例如,对于1024×768的液晶面板,一行有1024个像素,则在一行中(对应于有效视频区间)像素时钟的个数也是1024个。
无论对TTL接口液晶面板,还是对LVDS接口面板,像素时钟信号都有以下两个方面的作用:
(1)指挥RGB信号按顺序传输。像素时钟信号就像指挥员指挥队伍时发出的口令“一、二,一、二……”,数字RGB信号在像素时钟信号的作用下,按照一定的顺序,由驱动板传输到液晶面板中,使各电路按照
一定的节拍协调地工作。
(2)确保数据传输的正确性。无论是驱动板电路,还是液晶面板电路,在读取数字RGB信号时,都是在像素时钟的作用与控制下进行的,各电路只有在像素时钟的下降沿(或上升沿)到来时才对数字RGB数
据进行读取,以确保读取数据的正确性。
HAL层代码目录: /hardware/rockchip/hwcomposer
上一篇: JQuery Tree插件——zTree v3.5.13 版 发布
下一篇: Docker数据持久化
推荐阅读
-
基于Metronic的Bootstrap开发框架经验总结(18)-- 在代码生成工具Database2Sharp中集成对Bootstrap-table插件的分页及排序支持
-
EpiiAdmin 开源的php交互性管理后台框架, 让复杂的交互变得更简单!Phper快速搭建交互性平台的开发框架,基于Thinkphp5.1+Adminlte3.0+Require.js。
-
rk3288_7.1平台基于DRM框架的HDMI开发总结
-
Linux驱动学习--DRM框架介绍及基于DRM框架的HDMI开发
-
基于Metronic的Bootstrap开发框架经验总结(1)-框架总览及菜单模块的处理_html/css_WEB-ITnose
-
基于Thinkphp323框架开发的调试方法总结
-
基于Thinkphp323框架开发的调试方法总结
-
基于Metronic的Bootstrap开发框架经验总结(18)-- 在代码生成工具Database2Sharp中集成对Bootstrap-table插件的分页及排序支持
-
基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts
-
基于Zabbix平台的java开发总结(API简介)