ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3588 HDMI IN热插拔问题剖析:从HPD到UEvent的完整链路

RK3588 HDMI IN热插拔问题剖析:从HPD到UEvent的完整链路 做RK3588方案的兄弟应该都绕不开这个坑Android下做HDMI IN插上信号源没画面拔了再插直接黑屏重启才恢复。我在RK3588的板子上前后折腾过好几个版本从kernel到Android HAL层都翻过最后才把热插拔这条链路彻底捋顺。这篇文章就把HDMI IN热插拔的原理、定位方法和改法完整梳理一遍包含硬件检查、设备树配置、驱动日志判断、应用层事件监听以及我踩过的几个真实坑适合正在做RK3588 Android12/13平台视频采集、车载系统、大屏交互或者多路显示的工程师参考。先说结论RK3588的HDMI IN本质上是一个完整的HDMI接收端热插拔能不能正常工作不单是应用层的事而是从硬件HPD检测、EDID读取、内核驱动状态机到Android UEvent上报的一条完整链路。任何一个环节断了表现就是“插上没反应”或者“拔掉插不回来”。这篇文章我会从最容易被忽视的硬件细节开始讲然后一层一层往上走到代码修复最后给一份排查速查表你可以直接对着查。1. 先搞明白RK3588的HDMI IN在Android下到底是什么链路1.1 热插拔的物理基础HPD信号到底是谁拉谁HDMI接口上有专门的Hot Plug Detect引脚也就是19脚通常大家简称HPD。很多人一上来就查软件其实这个脚在硬件上就容易搞反。RK3588做HDMI IN时它扮演的是HDMI Sink角色也就是接收端外面接进来的机顶盒、电脑、游戏机是Source端。Source端通过HPD脚检测Sink是否存在当HPD电平拉高Source才认为对面有显示器或者采集设备才开始输出TMDS信号和5V电源。所以这个HPD脚在RK3588这边是由HDMI RX控制器或者外部电路主动拉高的不是被动等待输入。我在实际项目里就遇到过硬件工程师把HPD接地或者悬空的情况结果就是HDMI IN永远检测不到信号插什么设备都没反应。这不是软件能救回来的。所以在深入看代码之前一定要先确认硬件上HPD脚有没有正确接到RK3588的HDMI RX对应引脚上并且有合适的上拉电路。正常来说HPD脚在RX端应该有一个上拉电阻常见的是10K或者100K到3.3V具体看RK3588方案设计参考原理图。如果没有这个上拉Source端可能识别不到sink存在表现为插入时完全没有热插拔中断。另外还要看5V脚。HDMI的18脚是Source给Sink提供的5V电源检测信号RK3588的HDMI RX控制器通常也会检测这个5V用来判断有没有设备插入。很多方案的HPD检测其实是由硬件比较器配合GPIO中断完成的不是直接量HDMI的19脚电平。这块要对着自己的原理图确认不要想当然。1.2 从驱动到Android应用的数据通路RK3588的HDMI IN硬件上由HDMI RX控制器、PHY以及内部的视频处理通路组成内核驱动通常会把它抽象为V4L2设备或者DRM bridge。在常见SDK中插入信号后驱动要做几件事检测HPD/5V变化、读取Source的EDID、根据EDID中的分辨率时序配置RX PHY和视频时序、启动视频流把接收到的视频数据送到ISP或者直接送到显示控制器。到了系统层如果走的是V4L2路线应用层通过 /dev/videoX 节点打开设备设置输入格式然后从队列里拿buffer。如果走的是DRM/KMS路线系统会把HDMI IN当成一个外部bridge连接在显示链路上热插拔事件会通过内核的DRM connector状态变化上报。Android这边应用层通常不是直接去轮询 /dev/videoX 有没有数据而是监听内核发上来的UEvent。内核驱动检测到HPD变化后会往用户空间发一条类似change/devices/platform/.../hdmirx的uevent带HPD1或HPD0的属性。Android应用可以通过UEventObserver捕获然后去重新打开设备或者重新配置视频通路。这就是整条热插拔链路的完整闭环。1.3 热插拔为什么会失败三个核心难点的拆解热插拔失败的原因看着五花八门但归纳起来逃不过三个核心难点。第一个是HPD状态检测不可靠。如果HPD引脚复用了别的功能比如被配置成普通GPIO、上拉电阻选错、或者驱动里没有注册对应的中断内核就感知不到插入和拔出事件。这类问题通常表现为完全没有UEvent上报dmesg里也没有任何打印。第二个是EDID读取失败。RK3588作为HDMI接收端Source设备插入后会通过DDC通道I2C向RK3588读取EDID只有RK3588正确返回了EDID数据Source才会开始输出视频信号。如果EDID读取失败或者校验出错Source端就会认为对面没有合法的接收设备HPD会被拉低视频信号自然出不来。常见的表现是插入后偶尔有画面偶尔没有或者拔掉重插后就再也出不来了。这类问题往往和DTS中HDMI RX节点的I2C地址、GPIO定义或者驱动中的EDID重试次数有关。第三个是拔出后视频流没有真正停止导致再插入时新的视频流无法覆盖旧状态。这个在实现上经常表现为驱动在HPD0时只是上报了事件但没有停止RX时钟和视频通路或者应用层收到UEvent后没有完整地释放设备、关闭buffer、停止采集线程。我在项目里遇到过好几种“拔出再插入后黑屏”的情况最后查下来基本都是拔出处理不干净导致第二次插入时硬件还停留在上一次的工作状态里。搞清楚了这三个难点剩下的工作就是顺着链路一一排查。下面我按实际操作顺序写。2. 前置排查先把硬件和系统状态确认准2.1 硬件检查清单原理图和实测步骤拿到一块HDMI IN异常的板子我建议先别急着改代码先用万用表测几个点省得后面白忙。重点测三处HPD引脚电压、5V检测引脚电压、HDMI座子的19脚到RK3588对应引脚的导通性。具体操作时可以用HDMI线接一个已知正常的Source设备比如笔记本或者机顶盒测量HPD脚在插入前后的电平变化。正常逻辑是设备插入后HPD应该从低变高Source检测到HPD拉高后才开始输出信号。实测中如果HPD始终为低硬件上肯定有问题。如果HPD有变化但软件没反应再回头查中断注册。这里还要特别提醒一下HDMI座子的机械结构。有些HDMI座子带锁扣有些是普通的反复插拔后19脚接触不良的情况并不少见。遇到“时好时坏”的板子先用好的HDMI线换着插几次排除座子虚焊。2.2 内核设备树确认hdmirx节点开没开、引脚对不对确认硬件没问题后进系统先看设备树。RK3588的SDK里HDMI RX相关设备树节点一般叫hdmirx或hdmi_rx里面通常包含status、pinctrl-0中的HPD引脚配置、memory-region或dma-coherent相关项。我可以给你一个典型片段具体字段名以你手上的SDK为准hdmirx { status okay; hpd-gpios gpio1 RK_PB6 GPIO_ACTIVE_HIGH; pinctrl-names default; pinctrl-0 hdmirx_hpd; memory-region hdmirx_reserved; };很多坑就出在这个hpd-gpios上。如果原理图上HPD没有接到这个GPIO或者复用到别的功能驱动注册的中断就永远不会触发。另外有些方案用内部PHY做HPD检测不需要单独配置GPIO那就要确认pinctrl有没有把对应引脚配置成HDMI RX功能而不是GPIO功能。判断方法其实很简单开机后看内核启动日志里有没有HDMI RX的初始化信息再跑一下cat /proc/interrupts | grep -i hdmi如果没有对应的中断注册或者中断号是0大概率是设备树节点没生效或者引脚配置不对。2.3 系统状态确认V4L2节点和DRM状态硬件和DTS没问题后进系统确认设备节点是否创建。走V4L2路线的话先看ls -l /dev/video*然后可以用v4l2-ctl查看设备能力v4l2-ctl -d /dev/video0 --all正常可以看到设备的driver名称是RK HDMI RX相关的。如果是走DRM路线可以用cat /sys/class/drm/card0-HDMI-A-2/status这里的输出如果是connected、disconnected或unknown能直观看出内核有没有检测到热插拔状态变化。这一步的作用是把问题范围切出个大概设备节点都不存在说明问题在驱动初始化或者DTS设备节点存在但没有状态变化说明问题在HPD检测链路设备节点和状态都有变化但应用没画面问题就在视频通路和Android层方向完全不一样排查效率就拉开了。3. 热插拔问题定位与代码修复实操3.1 热插拔事件从驱动到应用是怎么传的在讲具体修复方法之前我得先把热插拔事件的传递路径讲清楚。内核里的HDMI RX驱动一般会注册一个中断处理函数对应HPD或5V检测引脚。当插入事件触发时中断处理函数会在atomic context里置一个标志然后通过工作队列或者直接调用内核的drm_helper_hpd_irq_event或input_event机制把事件上报出去。如果是纯V4L2方案驱动可能在事件发生时通过v4l2_event_queue上报或者在poll时改变状态。Android系统层这边最常见的是通过对uevent的监听来感知状态。驱动在HPD变化时会调用kobject_uevent_env发一条ueventAndroid中的UEventObserver则注册监听/dev/uevent收到事件后再回调到Java层。这条链路看起来简单实际最容易断的就是中间某个过滤器把事件吞了或者驱动压根没发。所以排查时我第一个动作通常是抓ueventlogcat -s UEventObserver然后用示波器或者万用表触发一下HPD引脚看系统日志有没有打印。只要这一条有输出后面的链路就都好查。3.2 典型日志怎么看插入、拔出、EDID异常的表现下面我贴几个典型的日志片段你们可以对号入座。正常的插入流程dmesg里应该能看到类似这样的信息具体SDK打印格式会有差异[ 12.345678] hdmirx: hpd 1 [ 12.345680] hdmirx: start rx, edid ok [ 12.346000] hdmirx: resolution: 1920x1080p60 [ 12.346200] hdmirx: phy configured, rx started正常的拔出流程[ 26.123456] hdmirx: hpd 0 [ 26.123460] hdmirx: rx stop, disable phy如果你看到的是[ 26.123456] hdmirx: hpd 1 [ 26.123470] hdmirx: edid read timeout, retry 1 [ 28.123456] hdmirx: edid read timeout, retry 2这就是EDID读取或者DDC通道有问题需要排查I2C总线、上拉电阻、EDID数据本身。如果看日志发现hpd 1后有重复的hpd 1说明HPD引脚在抖动硬件上大概率需要加滤波电容或者驱动里要做去抖处理。我最常碰到的情况是插入有hpd 1拔出后日志里没有hpd 0。这说明HPD只有上升沿检测没有下降沿检测或者拔出时中断就被屏蔽了。这种问题要么改中断触发方式要么检查驱动里有没有在hpd为0时把中断关闭的逻辑。3.3 具体修改操作从DTS到驱动的完整流程如果确认HPD中断没有触发第一步是查DTS里中断配置。常见的interrupts字段可能写成interrupts GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH;但HPD这种外部插拔信号用边沿触发更合适比如interrupts GIC_SPI 234 IRQ_TYPE_EDGE_BOTH;如果是GPIO中断则在驱动里注册request_irq时使用IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING。很多SDK默认只注册了上升沿导致只能检测插入不能检测拔出这就是“拔了再插没反应”的典型原因。另外如果确认是外部Source设备HPD机制的问题也就是驱动已经正确检测到插入但是应用层没有反应那重点是确认UEvent是否带上了自定义属性。比如cat /sys/devices/platform/hdmirx/uevent正常情况下能看到HPD1 RESOLUTION1920x1080如果驱动发了事件但Android没收到检查Android侧的UEvent匹配字符串。一个常见错误是应用代码里写死了设备路径比如监听/devices/platform/ff160000.hdmirx/但实际SDK的设备路径是/devices/platform/hdmirx/。路径对不上事件就永远到不了你的回调。3.4 拔出后重启视频流的实现思路拔出事件处理干净这件事很关键。应用层在收到HPD0事件后不能只是简单停止预览还要按顺序做这几步停止采集线程禁止新的buffer入队调用VIDIOC_STREAMOFF停止视频流close()关闭设备节点如果有DRM pipeline还要把对应的plane和connector解绑。然后再收到HPD1事件时重新打开设备、重新设置格式、重新申请buffer、重新入队、重新流。我在实际项目里遇到过一种很隐蔽的坑应用层收到HPD0后只停止了预览没有关闭设备再次插入时直接复用旧设备句柄。表面上驱动状态已经变了但硬件内部还有一些寄存器没有完全复位导致新分辨率或者新时序下画面花屏或者黑屏。强制先close再open问题立刻消失。如果不想改应用层也可以在驱动里做文章拔出时把RX PHY和视频通路全部软复位插入时重新走一遍完整的初始化流程。但这种方式对驱动的侵入比较大而且不同SDK版本差异大我还是推荐优先保证应用层能正确处理UEvent驱动只负责可靠地报告状态。4. 常见故障现象与排查速查表4.1 插入完全没反应连中断都没有先检查HPD引脚电压用万用表在插入状态下量HPD脚如果为低查硬件上拉和连接关系。如果HPD电压正常查/proc/interrupts里对应中断是否增加次数没有则查DTS的GPIO复用配置和status是否为okay。还有一种情况HDMI RX的PHY电源没打开有些PHY需要额外的LDO供电电源没通也会导致PHY完全不起作用这也要查原理图。4.2 插入能识别拔掉再插没反应优先怀疑是事件链路断在拔出方向或者拔出时驱动没有正确停止RX。抓日志确认hpd 0有没有上报如果驱动里压根没有hpd 0打印大概率中断触发方式少了下降沿。另一种可能是拔出事件正常上报了但应用层收到后直接把采集线程挂了没有做重启流程第二次插入时应用已经死了看起来就是“再插没反应”。4.3 有画面但花屏、闪屏、无声音花屏先确认Source端输出的分辨率是不是在RK3588 HDMI RX的支持范围内。常见的是Source端输出4K60/4K120而板子的HDMI RX走的是MIPI CSI或ISP通路带宽不够需要把Source端降到4K30或1080P60才能稳定。闪屏则重点查HPD触点抖动以及HDMI线的屏蔽质量。无声音的问题很多时候不是驱动的事而是Android在插入HDMI IN时没有切换Audio Route需要查AudioPolicy和混合路径配置这属于另一个专门话题本文先不展开。4.4 EDID读取失败导致Source不输出Source端在HPD拉高后会去读EDID读不到或者读到的数据校验失败就会立刻把HPD拉低。排查时用逻辑分析仪抓DDC通道的I2C波形最直观能确认是RK3588这边没有应答还是应答数据CRC错误。如果波形正常但驱动报超时检查DDC通道的I2C时钟频率有些Source对DDC时序很敏感把时钟降一点就能解决。还有EDID内容本身的问题如果EDID里定义了Source不支持的时序某些设备也会拒绝对接这种情况可以尝试在驱动里强制替换成一份预置的EDID。4.5 UEvent能收到但应用回调不执行这种问题通常不是链路断而是应用进程被Android的权限机制拦截了。检查应用有没有注册动态广播接收器以及有没有在AndroidManifest.xml里声明对应的接收receiver。如果应用在后台Android 12以上对后台进程接收广播有限制需要用JobScheduler或者前台服务来保活。我把常见现象和优先级整理成一张表方便在实际项目里直接照着查现象优先级最高的排查点次要排查点插入完全无反应HPD硬件电路、中断注册DTS节点、PHY电源只有插入能识别中断触发方式、驱动状态机UEvent事件格式插拔几次后失效缓冲期清理、设备句柄复用驱动内寄存器未复位偶发无信号、闪屏HDMI线缆和座子接触EDID时序异常、HPD抖动分辨率高时花屏HDMI RX链路带宽Source端输出模式、PHY配置EDID读取超时DDC I2C时钟上拉电阻、EDID数据校验5. 那些容易被忽略的坑和我的经验笔记5.1 HPD引脚复用冲突改了DTS也不生效有次我在一个项目上改完HPD的GPIO配置编译烧录后依然检测不到热插拔。排查了很久最后发现是pinctrl里同时配置了另一个模块占用同一个引脚后加载的驱动把引脚抢占过去了。这个坑不好查因为设备树编译不报错运行日志也不明显。我的经验是改完HPD配置后先用一个模块测试脚本把对应引脚反复切换成GPIO input并读取电平确认这个引脚确实能被HPD中断驱动控制。如果怀疑被复用抢占可以在/sys/kernel/debug/pinctrl/下查看当前引脚owner这是个很实用的招。5.2 断电重启能好热插拔不行这类问题的本质往往是“冷启动时硬件初始化顺序恰好对了但热插拔时少了一个环节”。我遇到过的是PHY的PLL锁定时间和HPD事件发出的时间有竞态驱动在HPD中断里直接开始配置PHY但此时PHY的PLL还没稳定导致EDID读取失败。后来在插入流程里加了延时就解决了。如果你在驱动里刚收到HPD事件后立刻做EDID操作建议先用mdelay(20)或usleep_range(10000, 20000)稳定一下时钟再继续后续初始化。5.3 与CSI摄像头的资源冲突RK3588的HDMI RX在很多方案里是通过ISP或者MIPI通路传输数据这就意味着它可能和板载摄像头共用一部分带宽或者像素时钟。如果你在打开HDMI IN的同时打开了摄像头会出现互相抢占导致热插拔后画面无法恢复的情况。这个冲突在设备树里通常不会体现需要结合具体管脚和通路规划来看。一个比较好的实践是在硬件设计阶段就把HDMI RX占用的通路固定下来系统层做成互斥不要让两个通路同时跑。如果已经上了软件也要在应用层做一个资源管理保证视频采集通路在同一时间只有一个使用者。5.4 量产阶段要注意的“拔插寿命”问题热插拔不光是软件逻辑HDMI座子本身就是有插拔寿命的。量产阶段如果客户反馈“用一段时间后HDMI IN不亮了”先别怀疑代码先用新座子的板子测同一个Source设备如果正常基本就是座子磨损导致HPD或者DDC接触不良。HDMI IN设备通常7x24小时常年插拔座子的采购品质和焊接工艺直接影响长期可靠性。这块虽然不算软件热插拔范畴但做产品的人迟早会遇到。5.5 调试工具和日志的备选方案如果驱动没有现成的调试节点我常用两个招。一是直接改驱动加printk把HPD中断触发、EDID读取成功、RX启动完成这些关键点都打出来编译烧录后看串口日志。二是在应用层加一个定时器轮询/sys/class/drm/*/status虽然不优雅但作为临时验证手段特别管用可以快速确认到底是内核没上报还是应用没消费。另外提一嘴RK3588这个平台能做的东西很多同一个芯片上你可能又要做HDMI IN采集又要做NPU推理比如常见的rk3588部署yolov8做视频分析。这种综合项目里HDMI IN热插拔问题一旦处理不好下游的模型推理、显示叠加全部都会受影响。所以热插拔这个基础能力值得多花时间打磨它直接影响整个系统的稳定性和用户体验。如果你现在正在做RK3588 Android平台的HDMI IN功能建议按照文章的顺序走一遍硬件电压→DTS配置→中断日志→UEvent事件→应用重启流程大多数问题都能在这个闭环里找到答案。尤其是“拔掉再插入没反应”这一类99%是拔出方向的事件链路断了或者应用层没有做完整的停止和重启动作。把这两点抓死热插拔问题基本就能稳定下来了。
返回列表