MT8788 Android 12 HDMI补丁:从DTS到HAL的完整修复指南

📅 发布时间:2026/10/20 12:23:50
MT8788 Android 12 HDMI补丁:从DTS到HAL的完整修复指南
简介针对MT8788平台Android 12系统HDMIIT66121适配的kernel 4.19补丁包面向嵌入式驱动开发与定制ROM维护工程师用于解决HDMI显示输出、CEC/HDCP等功能集成与调试问题。资源以ZIP压缩包形式提供共26个文件包含15个.h头文件、9个.c源文件和2个Makefile包体约81KB结构精简重点覆盖hdmi_drv/hdmitx_drv等主驱动框架以及hdmicec、HDCP、it66121桥接芯片的IO与utility模块头文件多用于寄存器定义与接口声明C代码实现驱动核心流程Makefile便于按模块编译或合入内核源码。目前已有481人学习/下载。可用于快速定位Android 12下HDMI无输出、CEC唤醒或HDCP认证异常等常见问题也可作为MT8788平台驱动移植与二次开发的参考模板省去从零梳理驱动框架的时间其中sha1/mcu及sys/debug相关文件还能辅助验证加解密流程与调试输出适合有一定BSP经验的工程师直接对照补丁改动。1. mt8788 android12 HDMI patch它到底是什么解决了什么一块 MT8788 平台的板子切到 Android 12 之后插上 HDMI 最常见的结局是三种屏幕永久黑着、拔掉再插第二次就认不出来、或者画面对着但声音永远停在喇叭那边。这不是硬件坏了而是 Android 12 对显示、音频和 vendor 分区的要求都变了老的 HDMI 相关驱动没有跟着适配。这份「mt8788 android12 HDMI patch」补丁包就是把这条链路重新打通内核 DTS 节点、HDMI 桥片驱动、HAL 设备枚举、AudioPolicy 路由和 init 服务按 Android 12 的规则补了一遍。适合正在做 MT8788 板卡 Android 12 移植的 BSP 工程师也适合接手上一个升级半途而废的项目、被 HDMI 报错卡住的人。2. 补丁的来龙去脉Android 12 对 HDMI 链路的三层要求2.1 先确认 MT8788 方案的 HDMI 是怎么接出来的MT8788 这颗 SoC 的大多数单板设计没有原生 HDMI PHYHDMI 是靠显示控制器先出 MIPI DSI再接一颗 DSI 转 HDMI 的桥片桥片通过 I2C 被主控配置。所以这份补丁里真正决定 HDMI 生死的就是两个东西桥片的驱动能不能被 probe桥片的热插拔中断能不能正确上报给内核。跟全新 bringup 相比Android 12 升级时最容易翻车的反而不是驱动代码本身而是设备树里引脚和时钟的描述发生了变化。内核升级后 DTS 最常见的兼容性问题有几个pinctrl 的引脚功能描述从直接塞入宏变成了引用 pio 子节点电源域的 regulator 名称变了导致 bridge 的 avcc-supply 找不到对应的固定电压域I2C 总线上的 clock-frequency 被新 dtsi 覆盖桥片的 I2C 时序对不上。踩过这些坑之后我拿到补丁的第一件事永远是先反编译当前固件里的 DTB跟目标 DTS 做一次 diff确认补丁不是把整个 dtsi 替换掉而是只动了跟 HDMI 相关的节点。具体做法是# 把 boot 分区里的 dtb 提出来反编译 adb root sleep 2 adb shell dd if/dev/block/by-name/boot of/data/local/tmp/boot.img adb pull /data/local/tmp/boot.img # 解出 dtb 再反成 dts dtc -I dtb -O dts -o current.dts boot.img这一步我一般会直接在板子上抓因为仓库里的 dts 经常和实际烧进去的不一致只有反编译出来的才是真身。补丁本身没带这套脚本也没关系三分钟就能跑完重点是把 diff 结果和补丁里 dts 的改动对应上避免内核里其他外设节点被误改。如果 diff 里出现了跟 HDMI 无关的节点差异要谨慎看待那可能是补丁的基线分支和你的分支不一样。2.2 Android 12 的分区约束把 HDMI HAL 逼进了 vendor老平台通常在 system 或 product 里直接放私有 so 库Android 12 把 system 分区只读这条规则执行得非常彻底vendor 必须独立启动所有供应商私有的 HAL、daemon、so 都要编进 vendor.img。HDMI 补丁里最常被忽略的部分就是 Android.bp 中的所有权声明。同一个源文件如果编译目标还挂在 product 分区最终表现是系统起来之后完全“看不见”HDMI。我在一个平板项目里遇到过类似的事logcat 里一点 HDMI 相关错误都没有dumpsys display 也没有外接屏幕最后发现 vendor/lib64/hw 下根本不存在 hdmi 对应的 so。原因是 HAL 的 vendor_available 没打开链接器把库丢到了 product。这类问题往往不是一个补丁单独造成的也可能是升级时把整个 vendor 目录换了路径。所以检查补丁时我一般会把 Android.bp 里 proprietary、vendor_available、product_available 逐个过一遍确认最终安装路径是 /vendor。SELinux 也是一个大坑。Android 12 对 vendor 进程访问 sysfs 的限制比 11 更严HDMI 状态是通过 sysfs 节点读的补丁一般会在 sepolicy 里补一条 allow 规则允许 vendor 的 hdmi 相关 domain 读写 sysfs class switch 或者 hdmi 专用节点。如果规则没进去现象非常玄学进程看起来活着热插拔状态永远停留在第一次读到的值。所以我在验证时不只看功能还会配 dmesg 把 avc denied 抓到再反查是缺了哪一条 policy。2.3 补丁包目录结构拿到手先判断这 patch 完不完整以常见 MT8788 平台的内核目录结构为例一份我认可的补丁通常长成这样补丁根目录/ ├── kernel/ │ └── arch/arm64/boot/dts/ │ ├── mt8788.dtsi # 平台级差异 │ └── mt8788-xxx-hdmi.dts # 板级 hdmi 桥片配置 ├── device/ │ └── vendor_xxx/board_xxx/ │ ├── audio_policy_configuration.xml # HDMI 音频 sink 端口 │ ├── init.rc # hdmi 服务与属性 │ ├── sepolicy/ # hdmi 的 sepolicy 规则 │ └── overlay/frameworks/base/ │ └── res/values/config.xml # 显示设备枚举配置 ├── vendor/ │ └── mtk/proprietary/ │ ├── hdmi_hal/Android.bp # HAL 编译目标 │ ├── hdmi_hal/hdmi_hw.cpp # HAL 实现 │ └── modules/hdmi/ # 桥片驱动源码 └── README.md一个在板子上真正跑得通的项目通常要同时覆盖三层。内核层只负责把 HDMI 桥片驱动起来device 层是板级配置负责音频策略、SELinux、init 服务和 overlayvendor 层放 HAL 可执行部分。如果拿到的补丁只有一个目录大概率是“半棵草”后面要自己补。还有一个容易被忽略的点是 Android 12 的 GKI 内核趋势。如果厂商开启 GKIhdmi 桥片驱动只能以 ko 模块方式放在 vendor_boot 里补丁里就应该有 modules.load 或者对应 list 的改动。MT8788 老项目多数没开 GKI但一旦目标版本要求 vendor_boot 单独分区而补丁还是老的“编进内核”方式HDMI 驱动永远不会被加载。拿到补丁先搜 vendor_boot 和 .ko 关键字能省掉很多时间。3. 核心模块拆解一个 HDMI 热插拔事件从硬件到上层的完整链路3.1 内核侧DTS 节点和桥片驱动的加载桥片的 dts 节点是老式平台上 HDMI 补丁里改动量最大的地方i2c1 { status okay; clock-frequency 400000; hdmi_bridge: hdmi-bridge48 { compatible vendor,dsi2hdmi; reg 0x48; // 桥片 i2c 地址 reset-gpio pio 45 GPIO_ACTIVE_LOW; enable-gpio pio 44 GPIO_ACTIVE_HIGH; avcc-supply reg_3v3; // 模拟电源域 interrupt-parent pio; interrupts 46 IRQ_TYPE_EDGE_RISING; // hpd 检测脚 hdmi,audio 1; // 支持音频回传 status okay; }; };这段 dts 里值得解释的参数有几个。reg 是桥片的 7 位 I2C 地址0x48 是常见地址如果你的板子上桥片地址不一样比如 0x3d这里要跟着改否则 i2c 扫描永远找不到内核日志里会一直刷 no such client。reset-gpio 和 enable-gpio 配合完成复位时序复位动作必须等 avcc-supply 稳定之后再拉低再拉高很多上电顺序问题都出在这一步。interrupt-parent 指向 pio 后interrupts 里的 46 是一个普通 GPIO 口HDMI 插座的检测线就接在这里GPIO_ACTIVE_LOW 和 GPIO_ACTIVE_HIGH 的定义必须和原理图一致反了的话热插拔状态永远反相上层每次都会拿到错误状态。hdmi,audio 是驱动私有属性表示这个桥片支持音频回传HAL 读到它才会去打开对应的 audio path。除了 DTS驱动里常见的错误是桥片 probe 之后立刻读 EDID但此时 DSI 链路还没起来读回的内容全是 0xFF 或者 0x00。补丁通常会把首次 EDID 读取推迟到主显示完成一次 commit 之后用 delayed_work 来做。如果补丁里看到这一类改动说明作者把上电时序处理过一遍可以放心些。3.2 HAL 侧netlink 收到 UEvent触发状态上报内核只管把状态写到 sysfs真正让上层感知到“我插了 HDMI”的是 HAL 里的 UEvent 监听。下面这段是这类 HAL 里最典型的一块逻辑static int hdmi_read_state() { char buf[8]; int fd open(/sys/class/switch/hdmi/state, O_RDONLY); if (fd 0) return 0; read(fd, buf, sizeof(buf) - 1); // 读 0 或 1 close(fd); return atoi(buf); } static void hdmi_uevent_listener() { int fd socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); char buf[1024]; while (1) { int len recv(fd, buf, sizeof(buf), 0); if (strstr(buf, hdmi/state)) { int state hdmi_read_state(); hw_connect_display(HWC_DISPLAY_HDMI, state); } } }HAL 通过 netlink 收内核的 uevent收到与 hdmi/state 相关的事件后再读一次 state 节点拿到最新值调用 hw_connect_display 告诉 composer 外接屏幕连接状态。参数方面HWC_DISPLAY_HDMI 是 composer 里约定的外接屏幕 IDstate 为 1 表示已连接0 表示断开。这里常见的问题是 socket 没加 SO_RCVBUFFORCE内核事件一多就丢 uevent表现是拔插一两次之后 HAL 就没反应。我在代码里一般会加 setsockopt 把接收缓冲区拉大到 128KB这属于那种不写上板没感觉、写上空闲的人看半天的改动。另一个坑是 /sys/class/switch/hdmi/state 这个路径在 Android 12 对应的新内核上可能不存在老 vendor 代码很少主动改补丁实际做的事往往是把读取函数改成兼容两个路径先读 /sys/class/switch/hdmi/state没有就读 /sys/bus/i2c/devices/xxxx/hdmi_hpd。3.3 框架侧AudioPolicy 里补 HDMI sink 端口画面之外HDMI 的声音能不能出来绕不开 audio policy 的配置。Android 12 的 audio policy 配置文件里HDMI 作为一个 sink device 必须被显式枚举出来module nameprimary halLibNamelibaudiopolicymanager devicePorts !-- HDMI 作为 audio sink 设备注册进来 -- devicePort tagNameHDMI Out typeAUDIO_DEVICE_OUT_HDMI rolesink addresshdmi/ /devicePorts mixerPorts mixerPort namehdmi output rolesource/ /mixerPorts routes route typemix mixerPort namehdmi output/ devicePort tagNameHDMI Out/ /route /routes /module这段配置的核心作用是把 HDMI 注册成 audio output device没有它 AudioFlinger 永远不认 HDMI。参数上type 里的 AUDIO_DEVICE_OUT_HDMI 是固定枚举不要手写成字符串否则加载直接失败address 是给 HAL 做声卡匹配用的别名tagName 是上层客户端拿到的设备显示名。补丁实践里经常还需要在 device 的 config.xml overlay 里打开与 HDMI 相关的开关比如 config_hdmi_audio这些 flag 决定系统设置界面里会不会出现“HDMI 音频”选项。Android 12 的 audio policy 跟 10/11 还有一个差别HDMI 和 ARC/eARC 的设备枚举被分开老补丁如果只注册了 AUDIO_DEVICE_OUT_HDMI在支持 eARC 的主板上可能拿不到最高音频格式。做维护时我一般会看 dumpsys media.audio_flinger 的输出确认 HDMI 相关 device 有没有被自动路由没有就补上新枚举里对应的 arc 条目。3.4 集成脚本init.rc 里的服务启动和属性配置补完最后一步是把服务挂进 init。下面是这类补丁里一个比较规范的 init.rc 片段# 等待 bootanim 结束再拉 hdmi 服务避免抢显示资源 on property:init.svc.bootanimstopped start vendor.hdmi service vendor.hdmi /vendor/bin/hw/vendor.hdmi class vendor user system group system audio seclabel u:r:vendor_hdmi:s0 on property:vendor.hdmi.ready1 setprop sys.hdmi.connected 1这段 init 里有个不错的技巧是等 bootanim 结束再启动 hdmi 服务避免跟 primary display 抢初始化资源。HAL 起来之后自己把 vendor.hdmi.ready 写 1触发框架层广播 ACTION_HDMI_PLUG上面的 setprop 就是干这个用的。参数上seclabel 必须和 sepolicy 里定义的类型完全一致写错一个字符 init 启动服务时就直接报错重启user 和 group 视设备而定但不给 audio 组的话HAL 打开 ALSA 声卡那一步会 permission denied。从 3.1 到 3.4可以看到一条完整的链路内核把状态写 sysfsHAL 监听 uevent 后调 composeraudio policy 保证声音路由init 负责把服务串起来。补丁缺了任何一环表现都不会是“崩得很明显”而是“看起来整个系统没有 HDMI”。4. 实战排查打上补丁后最容易翻车的 5 个场景在讲具体坑之前先把一套补丁从仓库到板子的落地顺序说清楚内核补丁单独编 boot.imgdevice 和 vendor 的改动编 vendor.img 和 system.img全部烧完再进系统。内核对不上分区、或者 SELinux contexts 没打进去就会出现下面这些奇奇怪怪的现象。4.1 现象 1插上 HDMI 完全黑屏串口里只有 edid read failed原因桥片驱动 probe 成功但 DDC 通道没有上拉电源或者 reset 时序没等 power rail 稳定。很多 DSI 转 HDMI 桥片的 DDC 引脚需要独立的收发供电dts 里漏配了对应电源节点就会出现这个现象而这类电源节点在旧版 DTS 里可能根本没有。解决先看 dmesg 里有没有 probe failed 之前的打印。如果驱动打印了 probe 成功去查 pinctrl 的总线上拉和 DDC 电源如果 probe 都没打印去查节点 compatible 和 i2c 地址。我一般会在 dts 里临时给桥片加一个固定 3.3V 的 regulator再一步步对比确认。注意不要上来就怀疑 EDID 缓存先分清是“读不到”还是“读到的内容不对”。4.2 现象 2补丁打上之后系统起不来串口卡在 service not found 一直重启原因init.rc 引用了不存在的 service或者 seclabel 定义缺失。Android 12 的 init 对这类错误处理得比旧版严格不会跳过去而是直接重启。解决先把 init.rc 里 service 段临时注释掉能起系统就说明是 init 配置问题。然后对比补丁里 sepolicy 目录下的 file_contexts 是否同步改了确认 /vendor/bin/hw/vendor.hdmi 对应的安全上下文存在。补丁里带了 file_contexts 改动但 apply 时被漏掉是这类现象里最高频的原因。4.3 现象 3能出图但系统设置里的分辨率列表只有最低档原因EDID 解析完成后display 的逻辑分辨率走的是 composer 的 mode 列表老 HAL 用固定列表没跟 EDID 刷新也有可能是 DSI 转 HDMI 桥片在主控看来是单条 DSI 的固定 timing无法按 EDID 的 preferred timing 上报。解决先 dumpsys display 看 mode 列表是否包含目标分辨率。不包含就检查补丁有没有在 HAL 里动态构建 mode list。固定 timing 的方案下常见做法是直接在 DTS 里把 virtual display 的分辨率改成面板原生分辨率再在框架层做缩放。这里有个经验别为了“看起来支持 4K”硬上 4K 时序DSI 带宽不够时画面会有横纹这类问题用仪器都不好抓。4.4 现象 4热插拔能识别但声音永远输不出原因audio policy 里的 route 配了但 mixer_paths.xml 没把 HDMI 声卡的 PCM 口接进去或者声卡 index 在 Android 12 下发生了变化。解决cat /proc/asound/cards 确认 HDMI 声卡在不在然后在 mixer_paths.xml 里添加对应 path 的 hdmi 控件。还有一个常见做法是 HAL 里写死声卡编号但这种方案在 Android 12 的 USB 设备老插拔之后很容易错位我一般改成按 device 类型查找声卡而不是用固定 index。4.5 现象 5第一次插上识别正常拔了再插就没反应原因uevent 监听 socket 缓冲不足丢事件或者内核在拔掉时没有把 state 写回 0。前者是 HAL 侧问题后者是内核驱动问题两个都要排查。解决先按 3.2 的方法调整 socket 缓冲再检查内核驱动拔插回调里有没有把 sysfs state 更新成 0。很多桥片驱动只在检测脚上升沿发一次中断下降沿没处理于是第二次插入内核本身就不感知。可以直接在 sysfs 里手动写值测试如果内核层就看不出变化那就不是 HAL 能管的事。5. 二次开发把补丁改成适合你自己那片主板的东西5.1 先把板级参数抽出来同一份补丁落到不同主板上要改的往往不过五处。我习惯整理成一张参数表参数常见值修改位置桥片 I2C 地址0x48 / 0x3dDTS 中桥片节点的 reg热插拔检测 GPIOGPIO46DTS interrupts 编号reset / enable GPIOGPIO45 / GPIO44DTS reset-gpio / enable-gpio供电 railreg_3v3DTS 中 avcc-supply 的实际电压域默认分辨率1080p60HAL mode list 或 DTS 固定 timing这个表的意义是把补丁和原理图对应起来。拿到新板子先看原理图里桥片的 I2C 地址、检测脚、复位脚再回填 DTS千万别把整个 dtsi 照抄否则两块主板之间显示输出会互相干扰甚至你在这块板子上调好的参数换一块板直接黑屏。5.2 两个值得保留的验证习惯一个是 apply 前的 dry-runpatch -p1 --dry-run hdmi.patch。它能把所有 hunk 对不上的地方提前暴露出来对不上的 hunk 我一般手动改行号再 apply而不是用 --force 硬打force 会静默丢内容等到上板才发现少了关键片段。第二个习惯是上板后同时抓内核和框架两层证据dmesg 抓 hdmi/dsi/bridge 关键字再用 dumpsys display 确认外接显示器是否被枚举这两条命令缺一条都可能让你把问题定位错层。从那以后我每次接手 BSP 补丁都强制走一遍“反编译 dts diff → patch dry-run → 上板抓 dmesg → 热插拔循环 20 次”这个顺序补丁里的坑大部分都能在几分钟内浮出来。希望帮到你。本文还有配套的精品资源点击获取