待机状态下拔插 HDMI 无输出问题

现象

  1. STB 正常待机(suspend)
  2. 待机状态下拔掉 HDMI 线
  3. 解除待机(resume,如遥控器 IR 唤醒)
  4. 再次插入 HDMI 线
  5. 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 判断之前