全志linux开发屏幕适配(二)`HDMI`驱动适配说明
HDMI驱动适配
2.2.1、标准分辨率
查看当前已经支持的标准分辨率,文件路径:./sdk-linux-github/brandy/brandy-2.0/u-boot-2018/include/sunxi_display2.h
enum disp_tv_mode { DISP_TV_MOD_480I = 0, DISP_TV_MOD_576I = 1, DISP_TV_MOD_480P = 2, DISP_TV_MOD_576P = 3, DISP_TV_MOD_720P_50HZ = 4, DISP_TV_MOD_720P_60HZ = 5, DISP_TV_MOD_1080I_50HZ = 6, DISP_TV_MOD_1080I_60HZ = 7, DISP_TV_MOD_1080P_24HZ = 8, DISP_TV_MOD_1080P_50HZ = 9, DISP_TV_MOD_1080P_60HZ = 0xa, DISP_TV_MOD_1080P_24HZ_3D_FP = 0x17, DISP_TV_MOD_720P_50HZ_3D_FP = 0x18, DISP_TV_MOD_720P_60HZ_3D_FP = 0x19, DISP_TV_MOD_1080P_25HZ = 0x1a, DISP_TV_MOD_1080P_30HZ = 0x1b, DISP_TV_MOD_PAL = 0xb, DISP_TV_MOD_PAL_SVIDEO = 0xc, DISP_TV_MOD_NTSC = 0xe, DISP_TV_MOD_NTSC_SVIDEO = 0xf, DISP_TV_MOD_PAL_M = 0x11, DISP_TV_MOD_PAL_M_SVIDEO = 0x12, DISP_TV_MOD_PAL_NC = 0x14, DISP_TV_MOD_PAL_NC_SVIDEO = 0x15, DISP_TV_MOD_3840_2160P_30HZ = 0x1c, DISP_TV_MOD_3840_2160P_25HZ = 0x1d, DISP_TV_MOD_3840_2160P_24HZ = 0x1e, DISP_TV_MOD_4096_2160P_24HZ = 0x1f, DISP_TV_MOD_4096_2160P_25HZ = 0x20, DISP_TV_MOD_4096_2160P_30HZ = 0x21, DISP_TV_MOD_3840_2160P_60HZ = 0x22, DISP_TV_MOD_4096_2160P_60HZ = 0x23, DISP_TV_MOD_3840_2160P_50HZ = 0x24, DISP_TV_MOD_4096_2160P_50HZ = 0x25, DISP_TV_MOD_1280_1024P_60HZ = 0x41, DISP_TV_MOD_1024_768P_60HZ = 0x42, DISP_TV_MOD_900_540P_60HZ = 0x43, DISP_TV_MOD_1920_720P_60HZ = 0x44, /* * vga * NOTE:macro'value of new solution must between * DISP_VGA_MOD_640_480P_60 and DISP_VGA_MOD_MAX_NUM * or you have to modify is_vag_mode function in drv_tv.h */ DISP_VGA_MOD_640_480P_60 = 0x50, DISP_VGA_MOD_800_600P_60 = 0x51, DISP_VGA_MOD_1024_768P_60 = 0x52, DISP_VGA_MOD_1280_768P_60 = 0x53, DISP_VGA_MOD_1280_800P_60 = 0x54, DISP_VGA_MOD_1366_768P_60 = 0x55, DISP_VGA_MOD_1440_900P_60 = 0x56, DISP_VGA_MOD_1920_1080P_60 = 0x57, DISP_VGA_MOD_1280_720P_60 = 0x58, DISP_VGA_MOD_1920_1200P_60 = 0x5a, DISP_VGA_MOD_MAX_NUM = 0x5b, DISP_TV_MODE_NUM = 0x5c, };可以看到当前已经支持多种常规标准的分辨率,选择需要的分辨率修改设备树配置就好,修改参数如下:
//以修改为720p60fps为例 screen0_output_type = <3>; screen0_output_mode = <5>; dev0_output_type = <3>; dev0_output_mode = <5>; dev0_screen_id = <0>; dev0_do_hpd = <1>; fb0_format = <0>; fb0_width = <1280>; fb0_height = <720>;2.2.2、自定义分辨率
总会有特殊的屏幕分辨率,,,
graph TD A[自定义分辨率参数步骤] --> B[新增 自定义分辨率 枚举] B --> C[U-Boot 层定义更新<br/>brandy-2.0/u-boot-2018] B --> D[Kernel 层定义更新<br/>kernel/linux-4.9] C --> C0[hdmi_core.c/h 添加<br/> 自定义 VIC 值] C --> C1[sunxi_display2.h 添加<br/> 自定义 display subsystem 枚举] C --> C2[hdmi_core.c中的hdmi_mode_tbl 添加<br/> 新模式映射] C --> C3[core_edid.c的 supported_dtd 表中添加<br/>自定义 DTD] C --> E[edid.c 添加对应<br/> timing 参数] D --> H[同步修改 kernel<br/> 的 hdmi_core.c/h] D --> I[同步添加<br/> kernel edid.c / core_edid.c] I --> J[编译 kernel,支持新 mode] E --> K[调试打印 timing 参数,验证时序] J --> L[修改 DTS / sys_config.fex] L --> M[dev0_output_mode = 自定义 display subsystem 枚举<br/>fb0_width/fb0_height对应分辨率] M --> N[sys_config_my.fex: <br/>output_mode = 自定义 display subsystem 枚举] N --> O[U-Boot 加载时读取 fex → 传递给 Kernel] O --> P[Kernel 读取 /disp2/hdmi_core 解析 自定义 VIC 值] P --> Q[显示控制器 DE 初始化完成<br/>LCD/HDMI 输出自定义分辨率信号] 那么,先分析新增的屏幕参数,屏幕参数如下:
pixelclock = 40000000, hactive = 720, hfront_porch = 106, hback_porch = 120, hsync_len = 60, vactive = 720, vfront_porch = 20, vback_porch = 20, vsync_len = 4,先计算出需要在驱动中设置的参数:
水平总周期
(htotal) = hactive + hfront + hback + hsync- 720 + 106 = 826
- 826 + 120 = 946
- 946 + 60 = 1006
所以
htotal = 1006垂直总周期
(vtotal) = vactive + vfront + vback + vsync- 720 + 20 = 740
- 740 + 20 = 760
- 760 + 4 = 764
所以
vtotal = 764水平
blanking(hblank) = hfront + hback + hsync- 106 + 120 = 226
- 226 + 60 = 286
所以
hblank = 286垂直
blanking(vblank) = vfront + vback + vsync- 20 + 20 = 40
- 40 + 4 = 44
所以
vblank = 44按照
pixelclock(40000000Hz)计算实际刷新率- 先算分母
htotal * vtotal:1006 * 764- 1006 * 700 = 704,200
- 1006 * 60 = 60,360
- 1006 * 4 = 4,024
- 合计 = 704,200 + 60,360 + 4,024 = 768,584
- 则刷新率 =
pixelclock / (htotal * vtotal)=40,000,000 / 768,584- 768,584 * 52 = 39,966,368
- 40,000,000 - 39,966,368 = 33,632 (余数)
- 33,632 / 768,584 ≈ 0.043758…
- 所以刷新率 ≈ 52.04375839205604 Hz
- 四舍五入(
mHz)=>52.043758... * 1000 ≈ 52043.758...-> 52044
- 先算分母
结论:用 40 MHz 和提供的
porch/hsync/vsync,输出大约52.044 Hz,并非60 Hz。如果你真正想要60.000 Hz,需要更高的pixelclock(下面给出计算)。要得到 60 Hz 的
pixelclock(反算)
- 需要
pixelclock_required = htotal * vtotal * 60- 我们已经有
htotal * vtotal = 768,584- 768,584 * 60 = 46,115,040 Hz = 46.11504
MHz- 所以:要真
60Hz,pixelclock≈ 46.11504MHz(不是 40MHz)。
总结修改步骤:
在
hdmi_core.h中定义新的HDMI_VIC(自定义VIC值)HDMI标准的VIC通常是0x00~0xFF范围内的代码(标准VIC有约定的编号)。设备树里已经用了0x201、0x202、0x203做“自定义/扩展”编号(看起来这是项目约定:把自定义模式放在0x200+区间)。新模式需有一个唯一的代码供内部识别并与EDID/DTD对应。
注意与坑
- 不要与已有值冲突:确保
0x204未被其它地方占用。检查所有相关头文件以避免重复定义(不同文件复制的头文件需同步)。 - 十六进制/十进制混用容易出错:在头文件用
0x204(十六进制)更直观,但在其他地方(比如device tree)可能需要十进制(详见步骤 7)。 - 如果项目以后会合并标准
VIC,考虑在注释里标注这是“私有/扩展VIC区间”。
在各种
sunxi_display2.h中增加DISP_TV_MODDISP_TV_MOD_*是display subsystem的统一枚举。驱动/设备树/工具链在配置输出模式时使用该枚举值。你在设备树里用的数字(如dev0_output_mode = <69>;)就是这个枚举的十进制表示(0x45= 69)。 所以需要在所有相关头文件里添加,确保在不同子系统(board,sdk_demo,platform libs)里都能被识别。
注意与坑
- 多处同步:这些头文件在仓库里被拷贝多处——必须全部修改,遗漏一个会出现链接或编译断裂,或在某个子模块编译时缺少定义。把定义统一放到一个共享头文件里是较好的长期做法。
- 数值冲突:
0x45已被选择,但要检查是否与其它DISP类型(VGA、TV、VGA mode)冲突。 device tree(dts)与fex使用:注意device tree的<69>跟这里0x45是同一含义(十进制vs十六进制),一定要换算正确。
在
hdmi_mode_tbl中加入新模式映射hdmi_mode_tbl[]是框架把平台的DISP_TV_MOD_*(平台/驱动层内部的分辨率枚举)映射为HDMI的VIC(Video Identification Code)或自定义代码。若不在此表中注册,驱动不会把自定义的DISP模式映射到HDMI层,从而无法输出该分辨率。
注意与坑
- 在
u-boot与kernel两处都改:这是必须的。原因:u-boot的hdmi层用于早期引导显示(logo/boot messages);kernel的driver用于系统启动后的显示。若只改其中一处,会出现开机能看到但系统启动后不对的情况(或反之)。 - 枚举一致性:确保
DISP_TV_MOD_720_720P_60HZ和HDMI_VIC_720x720P61在步骤 1、2 中定义的值与这里完全一致(同样的宏/值、同样的十六进制/十进制)。不一致会导致编译或运行时找不到对应项。
在
EDID的supported_dtd表中添加自定义DTD- 设备的
EDID解析常用一个“支持时序表”(supported detailed timing table/supported_dtd)来匹配sink(显示器)返回的EDID信息。把自定义分辨率加入这个表,HDMI驱动在遇到相应EDID时才能识别并接受该模式。 - 结构中通常包括:一个“刷新率
x1000”(我的是52044)、一个结构体数组(包含VIC code,pixelclock,hactive,hblank,hfront,hsync,hback,vactive,vblank,vfront,vsync等字段)。
重要参数的来源与计算(逐步)
hblank = hfront_porch + hback_porch + hsync_len = 106 + 120 + 60 = 286(见上)。vblank = vfront_porch + vback_porch + vsync_len = 20 + 20 + 4 = 44(见上)。- 我在结构里写的
40000:依据项目里其它DTD条目的格式,这里是pixelclock单位为kHz(即40000 => 40,000 kHz = 40 MHz)。一定要跟项目里现有条目的单位一致(有的实现用10kHz单位,有的用kHz单位)。仔细查看supported_dtd_t的定义或邻近已有条目(例如233500)可推断单位。 - 第一项
52044:就是刷新率* 1000(52.043758... Hz -> 52044),EDID匹配逻辑会读取并比较这个刷新率标识。
如何找清楚每个字段的含义(建议)
- 在
edid.c中搜索supported_dtd_t或结构定义,确认每个数组索引代表哪个时序字段,然后按顺序填入。这比盲目复制数字稳妥。 - 如果不能立即找到
struct定义,用现有已正确模式的一行做对照,把已知模式(例如1080p)中每个数字跟你的时序逐项比对(这正是你已经做的)。
常见坑
- 单位错认(
10kHz/kHz/Hz):会导致刷新率clock全错,显示器拒绝信号。始终按项目里已有条目单位写入。 - 字段顺序错:很多实现自行封装
DTD,字段顺序不是标准EDID的字节序。 EDID vs VIC:某些sink会通过CEA里的VIC匹配而忽略DTD;需要同时在hdmi_mode_tbl、VIC定义和EDID DTD中一致化。
- 设备的
在
core_edid.c中把新的HDMI_VIC加入“被特殊处理”的判断- 部分代码路径会先尝试用
CEA/VIC(标准编号)匹配显示器能力;对自定义的扩展VIC(如0x201..0x204)希望直接使用DTD的精确时序信息,而不要用CEA代码强行匹配(避免被错误映射到标准VIC),因此将这些code置 0 强制使用DTD数据。
注意与坑
- 顺序敏感:这个分支需要放在合适位置,否则可能在
edid_done=false或其他情况下被覆盖。 - 兼容性:如果某些
sink只支持CEA标准模式,而不支持自定义的DTD,那么当驱动把code置 0 后,显示器可能不显示(因为没有标准VIC匹配)。这不是代码错误,而是显示器能力限制。
# 文件路径 hdmi2/hdmi_core/core_edid.c # 添加内容 # @@ -266,12 +266,13 @@ static void edid_set_video_prefered(sink_edid_t *sink_cap, } if ((pVideo->mDtd.mCode == 0x201) - || (pVideo->mDtd.mCode == 0x202) - || (pVideo->mDtd.mCode == 0x203)) { - pVideo->mCea_code = 0; - pVideo->mHdmi_code = 0; - return; - } + || (pVideo->mDtd.mCode == 0x202) + || (pVideo->mDtd.mCode == 0x203) + || (pVideo->mDtd.mCode == 0x204)) { + pVideo->mCea_code = 0; + pVideo->mHdmi_code = 0; + return; + } #if defined(__LINUX_PLAT__) if (!core->mode.edid_done) { pVideo->mCea_code = pVideo->mDtd.mCode;- 部分代码路径会先尝试用
在
hdmi_get_video_timming_info()加入printk()日志(调试)- 实机调试时这是最直接、最快速确认驱动真正使用的时序参数的办法:很多问题不是写错字段,而是某处被覆盖(比如
device tree/board配置优先级),或者EDID解析/匹配之后变形。打印可以告诉你最终驱动拿到的时序是什么。
注意
- 打印很多内容会影响
kernel/u-boot console输出,建议仅在调试时添加,验证后移除或用#ifdef DEBUG包裹。 u-boot打印和kernel printk级别序列号不同,注意查看对应日志(串口 /dmesg)。
# 文件路径 brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/hdmi2/hdmi_core/hdmi_core.c # 添加内容 # @@ -1060,9 +1060,18 @@ s32 hdmi_get_video_timming_info(struct disp_video_timings **video_info) info.vactive_space = 0; info.trd_mode = 0; } + printk("%s %d \n",__func__, __LINE__); + printk("info.pixel_clk:%d \n", info.pixel_clk ); + printk("x_res:%d \n", info.x_res ); + printk("y_res:%d \n", info.y_res ); + printk("hor_total_time:%d \n", info.hor_total_time ); + printk("hor_sync_time:%d \n", info.hor_sync_time ); + printk("hor_back_porch:%d \n", info.hor_back_porch ); + printk("hor_front_porch:%d \n", info.hor_front_porch ); + printk("ver_total_time:%d \n", info.ver_total_time ); + printk("ver_sync_time:%d \n", info.ver_sync_time ); + printk("ver_back_porch:%d \n", info.ver_back_porch ); + printk("ver_front_porch:%d \n", info.ver_front_porch ); *video_info = &info; return 0;- 实机调试时这是最直接、最快速确认驱动真正使用的时序参数的办法:很多问题不是写错字段,而是某处被覆盖(比如
在
Device Tree(board.dts)中设置output_mode和framebuffer大小dev0_output_mode指定启动时(Bootloader/fex/sys_config及driver)应选用的显示模式值。定义的DISP_TV_MOD_720_720P_60HZ的值0x45 = 69,设备树使用十进制<69>。fb0_width/fb0_height是framebuffer的默认尺寸,驱动会根据fb的 长宽 和 输出模式 进行缩放/裁剪/显示配置。它们要和你想要的输出一致,否则可能出现图像被拉伸或黑边。
注意与坑
- 十六进制/十进制换算:头文件使用
0x45(16 进制),dts常以十进制在<...>中表示。确保换算正确:0x45 = 4*16 + 5 = 64 + 5 = 69。错一个地方会导致选择错误模式。 fb0_width/height的作用:在某些平台framebuffer的buffer大小若与输出不一致,驱动会进行缩放。如果想使framebuffer同分辨率完全匹配,设置一致是合理的。但注意若使用多屏2D GPU,某些限制(alignment、stride)可能要求width是16/32的倍数。- 设备树生效:修改完
.dts必须编译生成.dtb并烧录/加载,否则系统仍使用旧配置。
在
sys_config_my.fex修改output_mode- 许多
Allwinner平台使用fex(或其后继格式)作为早期启动的配置文件。output_mode通常被启动脚本bootloader读取来决定默认输出模式(便于在没有完整kernel驱动前就能输出)。 - 把
fex与device tree/dts同步,避免开机阶段和kernel阶段不一致。
注意
- 在同一平台上可能同时存在
fex、dts、u-boot env三处配置:所有位置都要同步(或决定只使用其中一种方式),否则表现会不一致。 - 修改
fex后要用平台工具重新生成镜像或把fex放到boot分区,按平台规范操作。
- 许多
2.2.3、supported_dtd表参数说明
当前全志对于supported_dtd定义如下:
//brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/hdmi2/hdmi_core/api/edid.h//brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/hdmi2/hdmi_core/api/core_api.htypedefstruct{/** VIC code */u32 mCode;/** Identifies modes that ONLY can be displayed in YCC 4:2:0 */u8 mLimitedToYcc420;/** Identifies modes that can also be displayed in YCC 4:2:0 */u8 mYcc420;u16 mPixelRepetitionInput;/** In units of 1KHz */u32 mPixelClock;/** 1 for interlaced, 0 progressive */u8 mInterlaced;u16 mHActive;u16 mHBlanking;u16 mHBorder;u16 mHImageSize;/*For picture aspect ratio*/u16 mHSyncOffset;u16 mHSyncPulseWidth;/** 0 for Active low, 1 active high */u8 mHSyncPolarity;u16 mVActive;u16 mVBlanking;u16 mVBorder;u16 mVImageSize;/*For picture aspect ratio*/u16 mVSyncOffset;u16 mVSyncPulseWidth;/** 0 for Active low, 1 active high */u8 mVSyncPolarity;}dtd_t;typedefstructsupported_dtd{u32 refresh_rate;/* 1HZ * 1000 */dtd_tdtd;}supported_dtd_t;结构体字段逐项详解(dtd_t)
mCode—u32—VIC代码- 含义:
Video Identification Code,标识该模式的“编号”。 - 单位/格式:整数,通常用十六进制写(如
0x204),也可以十进制。 - 约定:把自定义扩展放在
0x200+区间,所以用0x204合理。 - 验证:此值要与
hdmi_core.h中的HDMI_VIC_...、hdmi_mode_tbl中的映射一致。
- 含义:
mLimitedToYcc420/mYcc420—u8- 含义:
mLimitedToYcc420:如果为 1,表示该 mode 只能以YCbCr 4:2:0格式显示(通常用于某些HDMI 2.0/HEVC场景),否则为 0。mYcc420:表示该模式“也可以”以YCbCr 4:2:0显示(向后兼容标记)。
- 常用取值:大多数普通
RGB模式都写0。 - 何时用 1:针对编码 / 带宽 / 色度子采样限制时才设为 1。
- 含义:
mPixelRepetitionInput—u16- 含义:像素重复系数(pixel repetition / pixel duplication),常出现在某些低分辨率被上采样到更高传输时序的场合。
- 常用值:
0表示无重复(大多数模式)。若是 2 表示每像素水平重复 2 次等。 - 建议:不需要时填
0。
mPixelClock—u32(单位:1 KHz)- 明确单位:注释写着 “
In units of 1KHz” —— 也就是说要把pixel clock写成千赫(kHz)。- 例如
40,000,000 Hz(40 MHz)写作40000(kHz)。
- 例如
- 计算已有
target pixelclock时直接除 1000;也可以target refresh时可反算,见下面示例)。 - 常见坑:误把单位当成
Hz或10 kHz,会导致数值相差1000或10倍,显示器会拒绝。
- 明确单位:注释写着 “
mInterlaced—u8- 含义:
1= 交错(interlaced),0= 逐行(progressive)。 - 对应你场景:
720×720很可能是progressive,即0。
- 含义:
水平/垂直像素时序(关键字段)
EDID/Detailed Timing常用字段表示法与驱动里字段的对应(常见解释):mHActive:有效像素宽(像素数)mHBlanking:水平blanking总像素数 =hfront+hsync+hback(注意:驱动里有单独的HSyncOffset与HSyncPulseWidth,但HBlanking仍为总空白像素)mHBorder:水平边框(像素)。几乎总为0。mHImageSize:图像水平物理长度(通常以mm为单位,用于画面纵横比)。- 如果不知道物理尺寸,填
0(驱动大多数时候只用 aspect ratio 或直接忽略)。
- 如果不知道物理尺寸,填
mHSyncOffset:水平同步偏移 = 前肩(front porch)的像素数(即H front porch)mHSyncPulseWidth:hsync脉宽(像素数)mHSyncPolarity:0 = active low,1 = active high(必须与显示器EDID或你想要的时序一致)
对应垂直:
mVActive:有效行数(行)mVBlanking:垂直 blanking 总行数 =vfront+ vsync + vbackmVBorder:垂直边框(行),通常0mVImageSize:图像垂直物理高度(mm),不确定可设0mVSyncOffset:垂直前肩(front porch)行数mVSyncPulseWidth:vsync 脉宽(行)mVSyncPolarity:0/1 与水平类似
注意:
mHBlanking不是hfront_porch + hback_porch(它还应包含hsync_len)。字段mHSyncOffset与mHSyncPulseWidth再细分了 blanking 的组成部分;总的关系:mHBlanking = mHSyncOffset + mHSyncPulseWidth + hback_porch一般直接算
mHBlanking = hfront + hsync + hback,mHSyncOffset = hfront,mHSyncPulseWidth = hsync,这与驱动实现一致。同理垂直方向
mVBlanking = vfront + vsync + vback。
supported_dtd_t的refresh_rate字段u32 refresh_rate; /* 1HZ * 1000 */- 含义:把刷新率(Hz)乘以 1000 后的整数存储(例如 52.0437… Hz -> 存
52044)。 - 计算建议:
int(round(refresh_hz * 1000))或者按整数方式四舍五入。
- 含义:把刷新率(Hz)乘以 1000 后的整数存储(例如 52.0437… Hz -> 存
2.2.4、supported_dtd参数计算演示
示例参数:
# 厂家提供屏幕参数示例 pixelclock = 40,000,000 Hz hactive = 720 hfront_porch = 106 hback_porch = 120 hsync_len = 60 vactive = 720 vfront_porch = 20 vback_porch = 20 vsync_len = 4水平总周期
htotal逐位计算:
hactive + hfront = 720 + 106 = 826hback = 826 + 120 = 946hsync = 946 + 60 = 1006
所以htotal = 1006。
垂直总周期
vtotal逐位计算:
vactive + vfront = 720 + 20 = 740vback = 740 + 20 = 760vsync = 760 + 4 = 764
所以vtotal = 764。
总像素数每帧 =
htotal*vtotal逐步乘法(为确保不出错,逐步写出中间):
- 1006 * 764
- 1006 * 700 = 704,200
- 1006 * 60 = 60,360
- 1006 * 4 = 4,024
- 合计 = 704,200 + 60,360 + 4,024 = 768,584
所以
pixels_per_frame = 768,584。- 1006 * 764
按
40 MHz计算刷新率pixelclock = 40,000,000 Hzrefresh = pixelclock / pixels_per_frame40,000,000 / 768,584 ≈ ?
逐步除法(找整近似):
768,584 * 52 = 39,966,36840,000,000 − 39,966,368 = 33,632(余数)33,632 / 768,584 ≈ 0.043758...- 所以
refresh ≈ 52.04375839205604 Hz
把它 *1000 并四舍五入:
52.043758... * 1000 = 52043.758...- 四舍五入 =>
52044
因此
refresh_rate字段写52044。mPixelClock(单位kHz)pixelclock_khz = 40,000,000 / 1000 = 40000 kHz- 所以写
mPixelClock = 40000。
hblank / vblankhblank = hfront + hsync + hback = 106 + 60 + 120 = 286vblank = vfront + vsync + vback = 20 + 4 + 20 = 44
其它直接字段映射
mHActive = 720mHBlanking = 286mHBorder = 0(通常)mHImageSize = 0(除非你知道屏幕物理宽mm,通常不必填)mHSyncOffset = hfront_porch = 106mHSyncPulseWidth = hsync_len = 60mHSyncPolarity =根据硬件,常用1(active high)或0(active low)。mVActive = 720mVBlanking = 44mVBorder = 0mVImageSize = 0mVSyncOffset = vfront_porch = 20mVSyncPulseWidth = vsync_len = 4mVSyncPolarity =同H同理,示例里用1。
其余
flagmInterlaced = 0 (progressive)mLimitedToYcc420 = 0mYcc420 = 0mPixelRepetitionInput = 0
那么最终数据为
/* 720x720 @ ~52.044Hz, pixelclock 40 MHz */{52044,{0x204,/* mCode: HDMI_VIC_720x720P61 */0,/* mLimitedToYcc420 */0,/* mYcc420 */0,/* mPixelRepetitionInput */40000,/* mPixelClock (kHz) -> 40,000 kHz = 40 MHz */0,/* mInterlaced */720,/* mHActive */286,/* mHBlanking (hfront + hsync + hback = 106+60+120) */0,/* mHBorder */0,/* mHImageSize (mm) - 0 if unknown */106,/* mHSyncOffset (front porch) */60,/* mHSyncPulseWidth */1,/* mHSyncPolarity (1 = active high) */720,/* mVActive */44,/* mVBlanking (vfront + vsync + vback = 20+4+20) */0,/* mVBorder */0,/* mVImageSize (mm) - 0 if unknown */20,/* mVSyncOffset (vertical front porch) */4,/* mVSyncPulseWidth */1}},/* mVSyncPolarity (1 = active high) */重点注意:
如果需要“真正60Hz” 的替代(反算pixelclock并写入),希望最终刷新≈60.000 Hz(不是52Hz),也就是修改刷新率,那么需要修改pixelclock:
pixelclock_required_hz = htotal * vtotal * 60
已知htotal * vtotal = 768,584,逐步乘:768,584 * 60 = 768,584 * (6 * 10) = (768,584 * 6) * 10768,584 * 6 = 4,611,504*10 => 46,115,040
所以需要46,115,040 Hz≈46.11504 MHz.
写入mPixelClock时以 kHz 单位:46,115,040 / 1000 = 46115.04→ 四舍五入写46115(kHz)。
用mPixelClock = 46115时的实际 refresh:
pixelclock_hz = 46,115,000refresh = 46,115,000 / 768,584 ≈ 59.999948... Hzrefresh * 1000 ≈ 59999.948-> 四舍五入为60000(即显示接近60.000Hz)
对应的supported_dtd_t行(60Hz近似):
/* 720x720 @ ~60Hz (pixelclock rounded to 46115 kHz -> ~59.99995Hz) */{60000,{0x204,0,0,0,46115,/* mPixelClock (kHz) ~ 46115 => 46.115 MHz */0,720,286,0,0,106,60,1,720,44,0,0,20,4,1}},虽然这能把refresh_rate写成60000(表示60.000Hz),实际能否成功取决于:
- 目标显示器是否接受该时序(很多显示器对
pixel clock/porch/sync polarity有要求)。 HDMI传输链(PHY)能否以该pixelclock工作(有些PHY对非标准clocks有限制)。
2.2.5、supported_dtd参数常见问题
- 单位误会:
mPixelClock单位是kHz(注意不是Hz,也不是10kHz)。refresh_rate要1000(Hz*1000)。
- 字段顺序 / 含义:
- 一定按
dtd_t定义的顺序填数值(驱动直接memcpy解析数组);若顺序错位,会导致非常奇怪的时序。
- 一定按
mHImageSize/mVImageSize:- 这些通常是以
mm为单位的物理尺寸,用于计算像素纵横比(PAR)。如果不知道设备的物理尺寸,填0比填错误值更安全。
- 这些通常是以
mPixelRepetitionInput:- 多数情形填
0。如果设备需要像素重复(比如720p输出但显示器期望1440时序),才会用到。
- 多数情形填
HSYNC/VSYNC极性:- 如果显示器
EDID指定了极性,尽量跟随;如果不确定,实验1(active high)或0(active low)。多数LCD采用positive(1),但并非绝对。
- 如果显示器
EDID DTD vs CEA VIC匹配:- 即使把
DTD加入supported_dtd_t,显示器可能更倾向于使用CEA的标准VIC。如果新增了自定义VIC,必须同步在core_edid.c、hdmi_mode_tbl等处做判断以优先使用DTD。
- 即使把
- 四舍五入规则:
- 对
mPixelClock用kHz四舍五入(或向下取整),并根据结果计算refresh_rate(Hz*1000),然后四舍五入到整数。
- 对
