待机状态下拔插 HDMI 无输出问题
现象
- STB 正常待机(suspend)
- 待机状态下拔掉 HDMI 线
- 解除待机(resume,如遥控器 IR 唤醒)
- 再次插入 HDMI 线
- HDMI 无输出,电视显示无信号
复现步骤
待机 → 拔 HDMI → 唤醒 → 插 HDMI → 黑屏/无信号
根因:ALARM_EVENT 提前返回导致 IRQ 嵌套计数泄漏
disable_irq/enable_irq 是嵌套计数
Linux 内核的 disable_irq() / enable_irq() 使用深度计数器 desc->depth,不是布尔开关:
// kernel/irq/manage.c
void disable_irq(unsigned int irq) { irq_descs[irq]->depth++; } // depth > 0 → 中断屏蔽
void enable_irq(unsigned int irq) { irq_descs[irq]->depth--; } // depth == 0 → 中断打开
必须 一对一配对,否则 depth 永不归零,中断永久丢失。
旧代码的泄漏路径
rtk_hdmi_suspend() 无条件调用 disable_irq(hpd_irq)。rtk_hdmi_resume() 中,enable_irq(hpd_irq) 在 ALARM_EVENT 提前返回之后:
// 旧版 rtk_hdmi_resume():
hdmi->in_suspend = false;
if (rtk_pm_get_wakeup_reason() == ALARM_EVENT)
return 0; // ← 提前返回,跳过所有恢复
// 以下都不执行:
gpiod_set_debounce(...);
update_hpd_state(hdmi);
enable_irq(hdmi->hpd_irq); // ← 不会执行!
enable_irq(hdmi->rxsense_irq); // ← 不会执行!
mod_timer(&hdmi->rxsense_timer, ...); // ← 不会执行!
泄漏逐帧追溯
Suspend #1(正常待机):
disable_irq(hpd_irq) → depth: 0 → 1
Resume #1(ALARM_EVENT — RTC 定时巡检):
ALARM_EVENT → return 0 → enable_irq 没调
depth 仍 = 1 ← 泄漏!
Suspend #2(巡检完毕,再次待机):
disable_irq(hpd_irq) → depth: 1 → 2
Resume #2(用户 IR 遥控器唤醒):
enable_irq(hpd_irq) → depth: 2 → 1 ← 仍然屏蔽!
用户插回 HDMI:
HPD GPIO 上升沿 → IRQ 被屏蔽 → rtk_hdmi_hpd_irq() 不触发
→ hpd_work 不调度 → update_hpd_state() 不调用
→ drm_helper_hpd_irq_event() 不调用 → 无 modeset → 无输出
日志证据
[ 769.549851] **********rtk_drm_resume : ALARM_EVENT return 0 *********
[ 769.549857] **********ALARM_EVENT return 0 *********
ALARM_EVENT 时两份 resume 都提前返回。每 24 小时 RTC 巡检一次就泄漏一层。
修复方案
将 enable_irq() 和 RxSense 恢复移到 ALARM_EVENT 判断之前,确保所有唤醒路径都执行中断恢复:
// 新版 rtk_hdmi_resume():
hdmi->in_suspend = false;
// ① 先恢复中断(所有唤醒源都执行,避免嵌套计数泄漏)
enable_irq(hdmi->hpd_irq);
if (hdmi->rxsense_mode == RXSENSE_INTERRUPT_MODE)
enable_irq(hdmi->rxsense_irq);
else if (hdmi->rxsense_mode == RXSENSE_TIMER_MODE)
mod_timer(&hdmi->rxsense_timer, jiffies + msecs_to_jiffies(300));
// ② ALARM_EVENT 时跳过 HDMI 输出相关初始化(不读 HPD、不触发 DRM hotplug)
if (rtk_pm_get_wakeup_reason() == ALARM_EVENT)
return 0;
// 以下仅非 ALARM 唤醒执行
gpiod_set_debounce(...);
// ... HDCP 恢复 ...
update_hpd_state(hdmi);
return 0;
修复后的配对
Suspend #1:
disable_irq(hpd_irq) → depth: 0 → 1
Resume #1 (ALARM_EVENT):
enable_irq(hpd_irq) → depth: 1 → 0 ✅ 归零!
Suspend #2:
disable_irq(hpd_irq) → depth: 0 → 1
Resume #2 (IR_EVENT):
enable_irq(hpd_irq) → depth: 1 → 0 ✅
用户插回 HDMI:
HPD IRQ 正常触发 → hotplug → modeset → 输出 ✅
DRM 侧不受影响
DRM 的 poll_enable/poll_disable 是布尔量不是计数器,不累积:
Suspend: poll_enabled = false
Resume: poll_enabled = true (ALARM 跳过也不累积,下次能正确恢复)
rtk_drm_resume() 不需要修改。
相关文件
| 文件 | 说明 |
|---|---|
kernel/rtk/parker/common-kernel/drivers/gpu/drm/realtek/rtk_hdmi.c |
rtk_hdmi_resume() — 修复 IRQ 恢复顺序 |
kernel/rtk/parker/common-kernel/drivers/gpu/drm/realtek/rtk_drm_drv.c |
rtk_drm_resume() — 无问题,不需改 |
修改历史
| 日期 | 内容 |
|---|---|
| 2026-06-24 | 将 enable_irq() 和 RxSense 恢复移到 ALARM_EVENT 判断之前 |