全志linux开发屏幕适配(二)`HDMI`驱动适配说明

HDMI驱动适配
2.2.1、标准分辨率

查看当前已经支持的标准分辨率,文件路径:./sdk-linux-github/brandy/brandy-2.0/u-boot-2018/include/sunxi_display2.h

ini 复制代码
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,
};

可以看到当前已经支持多种常规标准的分辨率,选择需要的分辨率修改设备树配置就好,修改参数如下:

ini 复制代码
//以修改为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、自定义分辨率

​ 总会有特殊的屏幕分辨率,,,

Mermaid 复制代码
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 输出自定义分辨率信号]

​ 那么,先分析新增的屏幕参数,屏幕参数如下:

txt 复制代码
pixelclock = 40000000, 
hactive = 720, 
hfront_porch = 106, 
hback_porch = 120, 
hsync_len = 60, 
vactive = 720, 
vfront_porch = 20, 
vback_porch = 20, 
vsync_len = 4,

先计算出需要在驱动中设置的参数:

  1. 水平总周期(htotal) = hactive + hfront + hback + hsync

    • 720 + 106 = 826
    • 826 + 120 = 946
    • 946 + 60 = 1006

    所以 htotal = 1006

  2. 垂直总周期(vtotal) = vactive + vfront + vback + vsync

    • 720 + 20 = 740
    • 740 + 20 = 760
    • 760 + 4 = 764

    所以 vtotal = 764

  3. 水平 blanking(hblank) = hfront + hback + hsync

    • 106 + 120 = 226
    • 226 + 60 = 286

    所以 hblank = 286

  4. 垂直 blanking(vblank) = vfront + vback + vsync

    • 20 + 20 = 40
    • 40 + 4 = 44

    所以 vblank = 44

  5. 按照 pixelclock40000000Hz)计算实际刷新率

    • 先算分母 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
  • 所以:要真 60Hzpixelclock≈ 46.11504 MHz(不是 40 MHz)。

总结修改步骤:

  1. hdmi_core.h 中定义新的 HDMI_VIC(自定义 VIC 值)

    • HDMI 标准的 VIC 通常是 0x00~0xFF 范围内的代码(标准 VIC 有约定的编号)。设备树里已经用了 0x2010x2020x203 做"自定义/扩展"编号(看起来这是项目约定:把自定义模式放在 0x200+ 区间)。新模式需有一个唯一的代码供内部识别并与 EDID / DTD 对应。

    注意与坑

    • 不要与已有值冲突:确保 0x204 未被其它地方占用。检查所有相关头文件以避免重复定义(不同文件复制的头文件需同步)。
    • 十六进制/十进制混用容易出错:在头文件用 0x204(十六进制)更直观,但在其他地方(比如 device tree)可能需要十进制(详见步骤 7)。
    • 如果项目以后会合并标准 VIC,考虑在注释里标注这是"私有/扩展 VIC 区间"。
  2. 在各种 sunxi_display2.h 中增加 DISP_TV_MOD

    • DISP_TV_MOD_*display subsystem 的统一枚举。驱动/设备树/工具链在配置输出模式时使用该枚举值。你在设备树里用的数字(如 dev0_output_mode = <69>;)就是这个枚举的十进制表示(0x45 = 69)。 所以需要在所有相关头文件里添加,确保在不同子系统(board, sdk_demo, platform libs)里都能被识别。

    注意与坑

    • 多处同步:这些头文件在仓库里被拷贝多处------必须全部修改,遗漏一个会出现链接或编译断裂,或在某个子模块编译时缺少定义。把定义统一放到一个共享头文件里是较好的长期做法。
    • 数值冲突:0x45 已被选择,但要检查是否与其它 DISP 类型(VGATVVGA mode)冲突。
    • device treedts)与 fex 使用:注意 device tree<69> 跟这里 0x45 是同一含义(十进制 vs 十六进制),一定要换算正确。
  3. hdmi_mode_tbl 中加入新模式映射

    • hdmi_mode_tbl[] 是框架把平台的 DISP_TV_MOD_*(平台/驱动层内部的分辨率枚举)映射为 HDMIVICVideo Identification Code)或自定义代码。若不在此表中注册,驱动不会把自定义的 DISP 模式映射到 HDMI 层,从而无法输出该分辨率。

    注意与坑

    • u-bootkernel 两处都改:这是必须的。原因:u-boothdmi 层用于早期引导显示(logo / boot messages);kerneldriver 用于系统启动后的显示。若只改其中一处,会出现开机能看到但系统启动后不对的情况(或反之)。
    • 枚举一致性:确保 DISP_TV_MOD_720_720P_60HZHDMI_VIC_720x720P61 在步骤 1、2 中定义的值与这里完全一致(同样的宏/值、同样的十六进制/十进制)。不一致会导致编译或运行时找不到对应项。
  4. EDIDsupported_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:就是刷新率 * 100052.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_tblVIC 定义和 EDID DTD 中一致化。
  5. 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 匹配)。这不是代码错误,而是显示器能力限制。
    diff 复制代码
    # 文件路径
    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;
  6. hdmi_get_video_timming_info() 加入 printk() 日志(调试)

    • 实机调试时这是最直接、最快速确认驱动真正使用的时序参数的办法:很多问题不是写错字段,而是某处被覆盖(比如 device tree/board 配置优先级),或者 EDID 解析/匹配之后变形。打印可以告诉你最终驱动拿到的时序是什么。

    注意

    • 打印很多内容会影响 kernel/u-boot console 输出,建议仅在调试时添加,验证后移除或用 #ifdef DEBUG 包裹。
    • u-boot 打印和 kernel printk 级别序列号不同,注意查看对应日志(串口 / dmesg)。
    diff 复制代码
    # 文件路径
    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;
  7. Device Treeboard.dts)中设置 output_modeframebuffer 大小

    • dev0_output_mode 指定启动时(Bootloader/fex/sys_config driver)应选用的显示模式值。定义的 DISP_TV_MOD_720_720P_60HZ 的值 0x45 = 69,设备树使用十进制 <69>
    • fb0_width/fb0_heightframebuffer 的默认尺寸,驱动会根据 fb 的 长宽 和 输出模式 进行缩放/裁剪/显示配置。它们要和你想要的输出一致,否则可能出现图像被拉伸或黑边。

    注意与坑

    • 十六进制/十进制换算:头文件使用 0x45(16 进制),dts 常以十进制在 <...> 中表示。确保换算正确:0x45 = 4*16 + 5 = 64 + 5 = 69。错一个地方会导致选择错误模式。
    • fb0_width/height 的作用:在某些平台 framebufferbuffer 大小若与输出不一致,驱动会进行缩放。如果想使 framebuffer 同分辨率完全匹配,设置一致是合理的。但注意若使用多屏2D GPU,某些限制(alignmentstride)可能要求 width16/32 的倍数。
    • 设备树生效:修改完 .dts 必须编译生成 .dtb 并烧录/加载,否则系统仍使用旧配置。
  8. sys_config_my.fex 修改 output_mode

    • 许多 Allwinner 平台使用 fex(或其后继格式)作为早期启动的配置文件。output_mode通常被启动脚本bootloader 读取来决定默认输出模式(便于在没有完整 kernel 驱动前就能输出)。
    • fexdevice tree/dts 同步,避免开机阶段和 kernel阶段不一致。

    注意

    • 在同一平台上可能同时存在 fexdtsu-boot env 三处配置:所有位置都要同步(或决定只使用其中一种方式),否则表现会不一致。
    • 修改 fex 后要用平台工具重新生成镜像或把 fex 放到 boot 分区,按平台规范操作。
2.2.3、 supported_dtd 表参数说明

当前全志对于supported_dtd定义如下:

c 复制代码
//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.h
typedef struct {
	/** 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;

typedef struct supported_dtd {
	u32 refresh_rate;/*  1HZ * 1000  */
	dtd_t dtd;
} supported_dtd_t;

结构体字段逐项详解(dtd_t

  1. mCode --- u32 --- VIC 代码

    • 含义:Video Identification Code,标识该模式的"编号"。
    • 单位/格式:整数,通常用十六进制写(如 0x204),也可以十进制。
    • 约定:把自定义扩展放在 0x200+ 区间,所以用 0x204 合理。
    • 验证:此值要与 hdmi_core.h 中的 HDMI_VIC_...hdmi_mode_tbl 中的映射一致。
  2. mLimitedToYcc420 / mYcc420 --- u8

    • 含义:
      • mLimitedToYcc420:如果为 1,表示该 mode 只能以 YCbCr 4:2:0 格式显示(通常用于某些 HDMI 2.0/HEVC 场景),否则为 0。
      • mYcc420:表示该模式"也可以"以 YCbCr 4:2:0 显示(向后兼容标记)。
    • 常用取值:大多数普通 RGB 模式都写 0
    • 何时用 1:针对编码 / 带宽 / 色度子采样限制时才设为 1。
  3. mPixelRepetitionInput --- u16

    • 含义:像素重复系数(pixel repetition / pixel duplication),常出现在某些低分辨率被上采样到更高传输时序的场合。
    • 常用值:0 表示无重复(大多数模式)。若是 2 表示每像素水平重复 2 次等。
    • 建议:不需要时填 0
  4. mPixelClock --- u32 (单位:1 KHz

    • 明确单位:注释写着 "In units of 1KHz" ------ 也就是说要把 pixel clock 写成千赫 (kHz)
      • 例如 40,000,000 Hz40 MHz)写作 40000kHz)。
    • 计算已有 target pixelclock 时直接除 1000;也可以 target refresh 时可反算,见下面示例)。
    • 常见坑:误把单位当成 Hz10 kHz,会导致数值相差 100010 倍,显示器会拒绝。
  5. mInterlaced --- u8

    • 含义:1 = 交错(interlaced),0 = 逐行(progressive)。
    • 对应你场景:720×720 很可能是 progressive,即 0
  6. 水平/垂直像素时序(关键字段)

    EDID/Detailed Timing 常用字段表示法与驱动里字段的对应(常见解释):

    • mHActive:有效像素宽(像素数)
    • mHBlanking:水平 blanking 总像素数 = hfront + hsync + hback(注意:驱动里有单独的 HSyncOffsetHSyncPulseWidth,但 HBlanking 仍为总空白像素)
    • mHBorder:水平边框(像素)。几乎总为 0
    • mHImageSize:图像水平物理长度(通常以 mm 为单位,用于画面纵横比)。
      • 如果不知道物理尺寸,填 0(驱动大多数时候只用 aspect ratio 或直接忽略)。
    • mHSyncOffset:水平同步偏移 = 前肩(front porch)的像素数(即 H front porch
    • mHSyncPulseWidthhsync 脉宽(像素数)
    • mHSyncPolarity0 = active low1 = active high(必须与显示器 EDID 或你想要的时序一致)

    对应垂直:

    • mVActive:有效行数(行)
    • mVBlanking:垂直 blanking 总行数 = vfront + vsync + vback
    • mVBorder:垂直边框(行),通常 0
    • mVImageSize:图像垂直物理高度(mm),不确定可设 0
    • mVSyncOffset:垂直前肩(front porch)行数
    • mVSyncPulseWidth:vsync 脉宽(行)
    • mVSyncPolarity:0/1 与水平类似

    注意:

    • mHBlanking 不是 hfront_porch + hback_porch(它还应包含 hsync_len)。字段 mHSyncOffsetmHSyncPulseWidth 再细分了 blanking 的组成部分;总的关系:

      复制代码
      mHBlanking = mHSyncOffset + mHSyncPulseWidth + hback_porch

      一般直接算 mHBlanking = hfront + hsync + hbackmHSyncOffset = hfrontmHSyncPulseWidth = hsync,这与驱动实现一致。

    • 同理垂直方向 mVBlanking = vfront + vsync + vback

  7. supported_dtd_trefresh_rate 字段

    复制代码
    u32 refresh_rate; /* 1HZ * 1000 */
    • 含义:把刷新率(Hz)乘以 1000 后的整数存储(例如 52.0437... Hz -> 存 52044)。
    • 计算建议:int(round(refresh_hz * 1000)) 或者按整数方式四舍五入。
2.2.4、 supported_dtd参数计算演示

示例参数:

ini 复制代码
# 厂家提供屏幕参数示例
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
  1. 水平总周期 htotal

    逐位计算:

    • hactive + hfront = 720 + 106 = 826
    • hback = 826 + 120 = 946
    • hsync = 946 + 60 = 1006
      所以 htotal = 1006
  2. 垂直总周期 vtotal

    逐位计算:

    • vactive + vfront = 720 + 20 = 740
    • vback = 740 + 20 = 760
    • vsync = 760 + 4 = 764
      所以 vtotal = 764
  3. 总像素数每帧 = 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

  4. 40 MHz 计算刷新率

    • pixelclock = 40,000,000 Hz
    • refresh = pixelclock / pixels_per_frame
      • 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...
    • 所以 refresh ≈ 52.04375839205604 Hz

    把它 *1000 并四舍五入:

    • 52.043758... * 1000 = 52043.758...
    • 四舍五入 => 52044

    因此 refresh_rate 字段写 52044

  5. mPixelClock(单位 kHz

    • pixelclock_khz = 40,000,000 / 1000 = 40000 kHz
    • 所以写 mPixelClock = 40000
  6. hblank / vblank

    • hblank = hfront + hsync + hback = 106 + 60 + 120 = 286
    • vblank = vfront + vsync + vback = 20 + 4 + 20 = 44
  7. 其它直接字段映射

    • mHActive = 720
    • mHBlanking = 286
    • mHBorder = 0(通常)
    • mHImageSize = 0(除非你知道屏幕物理宽 mm,通常不必填)
    • mHSyncOffset = hfront_porch = 106
    • mHSyncPulseWidth = hsync_len = 60
    • mHSyncPolarity = 根据硬件,常用 1active high)或 0active low)。
    • mVActive = 720
    • mVBlanking = 44
    • mVBorder = 0
    • mVImageSize = 0
    • mVSyncOffset = vfront_porch = 20
    • mVSyncPulseWidth = vsync_len = 4
    • mVSyncPolarity =H 同理,示例里用 1
  8. 其余 flag

    • mInterlaced = 0 (progressive)
    • mLimitedToYcc420 = 0
    • mYcc420 = 0
    • mPixelRepetitionInput = 0

那么最终数据为

c 复制代码
/* 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) * 10
    • 768,584 * 6 = 4,611,504
    • *10 => 46,115,040
      所以需要 46,115,040 Hz46.11504 MHz.

写入 mPixelClock 时以 kHz 单位:46,115,040 / 1000 = 46115.04 → 四舍五入写 46115(kHz)。

mPixelClock = 46115 时的实际 refresh:

  • pixelclock_hz = 46,115,000
  • refresh = 46,115,000 / 768,584 ≈ 59.999948... Hz
  • refresh * 1000 ≈ 59999.948 -> 四舍五入为 60000(即显示接近 60.000Hz

对应的 supported_dtd_t 行(60Hz 近似):

c 复制代码
/* 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参数常见问题
  1. 单位误会:
    • mPixelClock 单位是kHz(注意不是 Hz,也不是 10kHz)。
    • refresh_rate1000Hz*1000)。
  2. 字段顺序 / 含义:
    • 一定按 dtd_t 定义的顺序填数值(驱动直接 memcpy解析数组);若顺序错位,会导致非常奇怪的时序。
  3. mHImageSize / mVImageSize
    • 这些通常是以 mm 为单位的物理尺寸,用于计算像素纵横比(PAR)。如果不知道设备的物理尺寸,填 0 比填错误值更安全。
  4. mPixelRepetitionInput
    • 多数情形填 0。如果设备需要像素重复(比如 720p 输出但显示器期望 1440 时序),才会用到。
  5. HSYNC / VSYNC 极性:
    • 如果显示器 EDID 指定了极性,尽量跟随;如果不确定,实验 1active high)或 0active low)。多数 LCD采用 positive(1),但并非绝对。
  6. EDID DTD vs CEA VIC 匹配:
    • 即使把 DTD 加入 supported_dtd_t,显示器可能更倾向于使用 CEA 的标准 VIC。如果新增了自定义 VIC,必须同步在 core_edid.chdmi_mode_tbl 等处做判断以优先使用 DTD
  7. 四舍五入规则:
    • mPixelClockkHz 四舍五入(或向下取整),并根据结果计算 refresh_rateHz*1000),然后四舍五入到整数。
相关推荐
凡人叶枫1 小时前
Effective C++ 条款38:通过复合塑模出 has-a 或 \“根据某物实现出\
linux·开发语言·c++·windows
charlie1145141912 小时前
嵌入式Linux驱动开发——从轮询到中断
linux·开发语言·驱动开发·嵌入式
无限进步_2 小时前
【Linux】系统级文件I/O与文件描述符深度剖析
linux·运维·服务器
ShineWinsu2 小时前
对于Linux:线程局部存储(TLS)和线程封装的解析
linux·c++·面试·线程·tls·线程封装·线程局部存储
2023自学中2 小时前
imx6ull开发板,sd卡启动运行linux,手动给开发板的 emmc 做分区、烧系统
linux·嵌入式·开发板
暮云星影2 小时前
瑞芯微rk3566开发FIT Secure Boot
linux·arm开发·驱动开发·安全
biter down2 小时前
2:Ubuntu 22.04 LTS 的完整下载教程
linux·运维·ubuntu
零陵上将军_xdr2 小时前
为什么DCL单例要加volatile?——CPU乱序执行与内存屏障
java·linux
杨云龙UP2 小时前
Oracle/ODA RAC /u01 空间告警处理指南:grid 用户监听日志清理_2026-06-15
linux·数据库·oracle·oracle linux·oda·监听日志·在线清理