目录
[uefi 显示接口类型配置](#uefi 显示接口类型配置)
[DTS 解析](#DTS 解析)
[channel open HDMI时序](#channel open HDMI时序)
[HDMI 静态时序](#HDMI 静态时序)
[hdmi_vo_prepare FRL时序获取](#hdmi_vo_prepare FRL时序获取)
前述系统篇,edid从转换芯片中读取,转换芯片中的EDID 像素时钟频率与屏幕支持的不符合,导致极化。
而uefi中,即使EDID里面设置了屏幕支持的时钟频率,依然会极化。那时钟频率必然有其他来源。本文梳理uefi中的流程。海思在这uefi和系统中的两套设计的确很垃圾,合二为一有何不可。
uefi 显示接口类型配置
硬件设计变更,比如从mipi变更为hdmi,即需要修改配置,否则无法显示。本章示例接口变更带来的流程和配置。
GOP目录代码结构
disp 对应XDP 或者VDP

显示接口类型定义
code\uefi\VendorPkg\Include\Library\drv_disp_ext.h
typedef enum {
EXT_DRV_DISP_INTF_TYPE_HDMI = 0,
EXT_DRV_DISP_INTF_TYPE_LCD,
EXT_DRV_DISP_INTF_TYPE_BT1120,
EXT_DRV_DISP_INTF_TYPE_BT656,
EXT_DRV_DISP_INTF_TYPE_YPBPR = 0x8,
EXT_DRV_DISP_INTF_TYPE_RGB,
EXT_DRV_DISP_INTF_TYPE_CVBS,
EXT_DRV_DISP_INTF_TYPE_SVIDEO,
EXT_DRV_DISP_INTF_TYPE_VGA,
EXT_DRV_DISP_INTF_TYPE_MIPI,
EXT_DRV_DISP_INTF_TYPE_PANEL = 0x16,
EXT_DRV_DISP_INTF_TYPE_ALL,
EXT_DRV_DISP_INTF_TYPE_MAX
} disp_intf_type;
显示流程
code\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_display.c
td_s32 drv_disp_open(ext_drv_display disp)
{
td_s32 ret;
disp_channel *disp_chan = TD_NULL;
get_channel_by_id(disp, disp_chan);
soc_err_disp("config drv_disp_open\n",disp,disp_chan->intf_info.display_intf.intf_type); //显示接口类型的
//disp 0 disp intf_type: 24
// disp 1 disp intf_type: 24
ret = disp_get_pdm_param(disp);
....
....
....
....
disp_chan->open = TD_TRUE;
soc_err_disp("after config drv_disp_open\n",disp,disp_chan->intf_info.display_intf.intf_type); //函数最后查看显示接口类型,明确接口类型的赋值在此函数中
//disp 0 disp intf_type: 0 HDMI
// disp 1 disp intf_type: 22 PANEL //这个与实际不符合。
return TD_SUCCESS; /* must sucess to let open the other chanel, kernel will check open state for boot open error*/
上述流程中,初始时,显示接口的类型为最大值,即无效的显示接口;在上述接口后,获取到具体的显示接口类型。但硬件设计的两个接口类型都是HDMI,此时获取的一个接口类型为PANEL,与实际不符合。
DTS配置
&pdm_disp1 {
intf_0 = <SOCT_DISP_INTF_TYPE_PANEL 0 0 0 0 0 0 0>;
};
修改为:
&pdm_disp1 {
intf_0 = <SOCT_DISP_INTF_TYPE_HDMI 1 0 0 0 0 0 0>;
};
即将disp1 也配置为HDMI,并且是HDMI 1 ,而后上述open接口即可以获取到与硬件设计一致的显示接口类型。即可以正常显示。
时序参数配置
关键数据结构disp_channel
typedef struct {
ext_drv_display disp;
td_bool open;
disp_setting setting;
td_bool disp_setting_change;
/* disp intf info */
disp_intf_ctrl intf_info;
/* panel */
disp_output_connect_type out_connect_type;
disp_src_info disp_src_info;
ext_disp_display_info disp_info;
disp_proc_debug disp_debug;
} disp_channel;
DTS 解析
\code\uefi\VendorPkg\Library\Pdm\PdmLib.c
Ret = FdtGetDtsConfigU32ArrayByName (DispNodeName, "disp_timing", (UINT32 *)&DispParam->disp_timing,
sizeof(DispParam->disp_timing) / sizeof(UINT32));
if (Ret < (EFI_STATUS)(sizeof(DispParam->disp_timing) / sizeof(INT32))) {
DEBUG ((EFI_D_ERROR, "get disp_timing fail\n"));
return EFI_PDM_ERROR;
}
disp_timing=<0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
//<vfb vbb vact hfb hbb hact vpw hpw idv ihs ivs clkreversal pix_freq refresh_rate>
PDM参数解析流程
PDM即显示管理,将display表示的显示实体与具体显示接口(例如HDMI)建立关联。并解析用户设置的PDM参数,例如显示的硬件路由,显示格式等参数。进而为后续具体的初始化,时序参数配置等建立基础。
\uefi\VendorPkg\Drivers\Gop\Hdmitx\drv_hdmitx.c
\uefi\VendorPkg\Drivers\Gop\Hdmitx\drv_hdmitx.c
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_disp_interface.c
uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_display.c

channel open HDMI时序
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_display.c
disp_set_channel_open-->disp_set_output_timing--》drv_disp_update_intf_info
if (format == EXT_DRV_DISP_FMT_CUSTOM) {
intf_info->disp_timing.static_timing.timing = *custom_timing;
intf_info->disp_timing.static_timing.vic_num = 0;
intf_info->disp_timing.static_timing.interlace = TD_FALSE;
intf_info->disp_timing.static_timing.pix_repeat = 1;
intf_info->disp_timing.static_timing.disp_fmt = EXT_DRV_DISP_FMT_CUSTOM;
intf_info->disp_timing.static_timing.disp_3d_mode = EXT_DRV_DISP_STEREO_NONE;
intf_info->disp_timing.static_timing.aspect_ratio.aspect_ratio_w = DISP_ASPECT_RATION_W;
intf_info->disp_timing.static_timing.aspect_ratio.aspect_ratio_h = DISP_ASPECT_RATION_H;
} else {
ret = drv_disp_timing_get_timing_info(format, &(intf_info->disp_timing.static_timing), &timing_info);
if (ret != TD_SUCCESS) {
soc_err_disp("get timing cfg is error, fmt is %d\n", format);
return ret;
}
}
drv_disp_update_interface_info(disp, format, intf_info);
如果DTS中的format字段配置为 : EXT_DRV_DISP_FMT_CUSTOM = 0xC0,则时序采用dts中的。否则 通过format 获取具体时序参数的索引字段。
HDMI 静态时序
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_display.c
disp_set_channel_open-》disp_set_output_timing
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_disp_timing.c
drv_disp_update_intf_info--》drv_disp_timing_get_timing_info
根据上面的流程可知,时序参数由format决定。例如DTS中默认配置的1080 60fps,其时序参数如下,可以看到此处148MHZ时钟。VIC为329。
static disp_timing_infog_disp_format_param[]
uefi\VendorPkg\Include\Library\drv_disp_ext.h
EXT_DRV_DISP_FMT_VESA_1920X1080_60 (a9)与dts中的默认配置format
#define SOCT_DISP_FMT_VESA_1920X1080_60 169 (a9)
/* |--INTFACE---||-----TOP-----||---HORIZON----||---BOTTOM----||-PULSE-||-INVERSE-| */
/* Synm Iop Itf Vact Vbb Vfb Hact Hbb Hfb Bvact Bvbb Bvfb Hpw Vpw Hmid bIdv bIhs bIvs */
// 11 UAPI_ENC_FMT_480P_60,
// 73
{
EXT_DRV_DISP_FMT_VESA_1920X1080_60,
EXT_DRV_DISP_STEREO_NONE,
{ EXT_DRV_COLOR_PRIMARY_BT709, EXT_DRV_COLOR_CS_RGB, EXT_DRV_COLOR_FULL_RANGE,
EXT_DRV_COLOR_TRANSFER_TYPE_GAMMA_SDR, EXT_DRV_COLOR_MATRIX_COEFFS_BT709
},
TD_FALSE,
60000,
148500,
329,
{ 16, 9 },
{ 1, 1080, 41, 4, 1920, 192, 88, 1, 1, 1, 44, 5, 1, 0, 0, 0 },
},
修改此处的时序为141MHZ,规避UEFI中极化问题。同时VIC修改为对应的401.


hdmi_vo_prepare FRL时序获取
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_display.c
disp_set_channel_open-》disp_set_output_timing-》drv_disp_intf_output_prepare
\uefi\VendorPkg\Drivers\Gop\Disp\disp\drv_disp_interface.c
drv_disp_intf_output_prepare
\uefi\VendorPkg\Drivers\Gop\Hdmitx**\drv_hdmitx.c**
VIC 修改为401后,FRL时序流程跑不通,具体流程如下:
hdmi_vo_timing_prepare--》hdmi_cur_hw_config_decision--》hdmi_get_hdmi_mode_config-->drv_hdmitx_modes_get_frl_req_by_band
struct frl_requirements *drv_hdmitx_modes_get_frl_req_by_band(const struct band_mode *in)
{
td_u32 i;
struct band_mode band;
if (in == TD_NULL) {
soc_log_alert("ptr is null.\n");
return TD_NULL;
}
band.vic = in->vic;
band.color_depth = in->color_depth;
band.color_format = (in->color_format == RGB444) ? YCBCR444 : in->color_format;
if (get_table_index_by_vic(band.vic) == 0) {
soc_log_dbg("index is zero.\n");
return TD_NULL;
}
// 此处frl table也不支持401 的vic,也会返回空。
for (i = 0; i < ARRAY_SIZE(g_frl_req_table); i++) {
if (band.vic == g_frl_req_table[i].band_mode.vic &&
band.color_format == g_frl_req_table[i].band_mode.color_format &&
band.color_depth == g_frl_req_table[i].band_mode.color_depth) {
return &g_frl_req_table[i];
}
}
return TD_NULL;
}
//如下接口不支持401的VIC,导致函数返回失败。而我们的屏幕采用了401的VIC
static td_u32 get_table_index_by_vic(td_u32 vic)
{
if (vic > 0 && vic <= VIC_5120X2160P100_64_27) {
return vic;
} else if (vic >= VIC_5120X2160P120_64_27 && vic <= VIC_4096X2160P120_256_135) {
return vic - 65; /* 65 = (192-128+1) */
} else if (vic >= VIC_VESA_800X600P60 && vic <= VIC_VESA_1680X1050P60RB) {
return vic - 102; /* 102 = (256-220+1)-(192-128 + 1) */
} else {
return 0;
}
}
由于不采用FRL 模式,这里临时将FRL 相关流程注释掉。
总结
1)DTS中有些参数只给UEFI的,例如PDM中的inte_type字段,虽然之前配置和硬件不一致,但系统中可以显示,但uefi不显示。说明此字段只有uefi中会解析使用。
2) UEFI中的EDID完全是静态配置的。虽然看代码由动态流程,很多都是废代码,海思在这方面的实现仅仅是为了和系统代码的复用,导致uefi存在大量冗余代码,带来困扰。
例如: 此文件中也有时序,而通常的逻辑可能以为是从接口层获取EDID时序参数。但实际用的
\uefi\VendorPkg\Drivers\Gop\Hdmitx\drv_hdmitx_modes.c
static struct hdmitx_timing_mode g_timing_mode_table[] = {
hdmi_get_hdmi_mode_config

3)目前BIOS配置菜单还不能显示,提示如下

800x600 这个频率也不支持。
采用HDMI转EDP,原本经常连接外部显示接口的HDMI ,变为连接内嵌屏幕的EDP接口。导致时序参数调整带来较多工作。
4)在走读DTS及代码时,我们发现: pixel_format=<SOCT_PIXEL_FORMAT_ARG8888>;而根据之前走读手册知晓,4K60HZ在此像素格式时,需要采用放大功能,那么如何配置放大功能呢?
后续对于BIOS 配置菜单的显示 及像素格式带来的放大功能配置将做进一步研究。