# 内屏（eDP）点亮：完整诊断记录

本文记录 **“跑码时内屏正常、进系统后黑屏”** 这一问题的完整排查过程与技术结论，供同机型（Raptor Lake-H + 无独显）遇到相同症状的人参考。

---

## 1. 症状

| 阶段 | 现象 |
|---|---|
| 开机跑码 | 内屏正常显示（Verbose 文字正常滚动） |
| 驱动接管瞬间 | 屏幕变黑 |
| 手电筒照射 | **有背光、无画面**（背光一直亮着） |
| 外接显示器 | 正常，可进系统 |
| 系统内 | `ioreg` 能看到内屏（1920×1080，主显示器） |

三条关键事实：**系统认得内屏、背光亮着、就是没有画面** —— 说明问题在“视频链路”，不在背光、不在面板识别。

---

## 2. 定位过程

### 2.1 排除背光

- 手电筒能看到背光常亮 → 背光已开
- PCH PWM 占空比已到 100%，但屏幕不亮 → 亮度不走传统 PWM 路径
- 面板 DPCD `0x721 = 0x84`：面板处于 **AUX 背光模式**（背光由 DPCD 寄存器控制），`0x722/0x723 = 0xBF00`（固件设定的约 74% 亮度）

**结论**：背光由面板固件通过 DPCD 锁存维持，与“是否有画面”无关。

### 2.2 锁定视频链路

内核日志给出决定性证据：

```
FB0: Hardware mode set failed, err = 0xe00002d8
[IGFB][ERROR][AUX] ddi = 0 Time out: 0x5d10023f
```

- `0xe00002d8` = `kIOReturnTimeout`，AUX 事务重试耗尽
- `0x5d10023f` 的 bit28 是 **TIMEOUT_ERROR**

即：**驱动做 modeset 时，所有 AUX 事务都超时，eDP 链路从未完成训练**，所以没有视频输出。

### 2.3 面板与 AUX 硬件是否正常？

用裸寄存器直接访问 DP AUX 通道（绕过驱动栈）做探针，得到：

| 探测 | 结果 |
|---|---|
| DPCD `0x000`–`0x00F`（接收器能力） | 可读，正常 |
| DPCD `0x202`（链路状态） | `0x01`（仅 CR 完成，未完成信道均衡） |
| DPCD `0x721`（背光模式） | `0x84`（AUX 背光模式锁存） |
| DPCD `0x722/0x723`（亮度） | `0xBF00` |
| 写入 `0x722 = 0xFFFF` | AUX 应答 ACK（status=0），但**寄存器值不变** |

结论：**面板与 AUX 通道本身是好的**；面板在链路未训练时对 DPCD 写入返回 ACK 但不落盘（冻结快照行为）。

### 2.4 为什么固件能、驱动不能？

- 固件（BIOS/UEFI）在开机早期已经完成 eDP 链路训练 → 跑码阶段有画面
- macOS 驱动接管后**重新训练**这条链路，但它的 AUX 事务要先经过电源井（power well）状态机
- 这台机器的黑苹果环境下，该状态机不工作 → 驱动侧 AUX 全部超时

裸寄存器直连 AUX 是成功的，说明**差别只在驱动封装的电源井流程**，不在硬件。

---

## 3. 失败的尝试（供参考，不要重走）

| 方案 | 结果 |
|---|---|
| 调整 DVMT / 显存补丁 | 无关，`FB0` 依然 AUX 超时 |
| 反汇编驱动，替换内部 AUX worker（vtable hook） | **会卡启动**。在 `processKext` 阶段改 vtable 影响早期启动路径，两次验证均死机/卡 verbose |
| 修改 DPCD 强制设置链路参数 | 写入被 ACK 但不生效（链路未训练时面板冻结 DPCD） |
| 等待电源井就绪后重试 | 超时依旧，状态机不推进 |

> 教训：**不要在启动关键期用 vtable 钩子改图形驱动内部函数**。代价是整个系统起不来。

---

## 4. 最终方案：`-wex-edp-keep`

来自 WhateverXe 上游 [issue #43](https://github.com/b00t0x/WhateverXe/issues/43)（2026-11）的社区方案。

核心思路：**既然固件已经把链路训练好了，就不要重训。**

驱动在 modeset 时会先检查链路是否已由固件训练完成：

```
若 已训练完成 → 返回 Partial modeset（只更新部分状态，不动链路）
```

加启动参数即可：

```diff
- amfi_get_out_of_my_way=1 msgbuf=1048576 -v
+ amfi_get_out_of_my_way=1 msgbuf=1048576 -v -wex-edp-keep
```

### 验证

```bash
ioreg -l | grep WEXEDPKeep
```

输出示例：

```
"WEXEDPKeep" = <01000000 04000000 04000000 00000000 ...>
```

字段含义：

| 字段 | 值 | 含义 |
|---|---|---|
| version | 1 | 记录结构版本 |
| call | 4 | 驱动共请求 4 次完整 modeset |
| kept | 4 | **4 次全部被拦截为 Partial modeset** |
| reasons | 0 | 拦截原因位图（0 = 均为“链路已训练”） |

同时内核日志中的 `Hardware mode set failed` 完全消失，内屏点亮，Metal 加速正常。

### 该方案的机制细节

1. `wrapDisplay13ReadAUX` 中捕获面板 DPCD 的链路能力（`capLinkCaps`）
2. 驱动在完整 modeset 时调用 `Display13EDPKeep` 决策
3. 满足条件（DDI < 2 且该端口配置为内屏、链路已训练）时返回 `Partial modeset`
4. 于是 `TRANS_DDI_FUNC_CTL` / `TRANSCONF` / 时序寄存器按请求更新，但**不重新训练链路**

---

## 5. 代价与限制

| 场景 | 结果 | 原因 |
|---|---|---|
| 睡眠唤醒 | 内屏黑到重启 | 唤醒触发完整 modeset 重训 |
| 手动关闭显示器 | 同上 | 同上 |
| 切换分辨率 / 刷新率 | 同上 | 同上 |
| 日常使用（不碰上述操作） | 完全正常 | — |

规避方式：系统设置里关闭显示器休眠、不手动关屏、分辨率保持默认。

---

## 6. 复现所需的最小配置

| 项目 | 值 |
|---|---|
| 核显 | `0xA720`（Raptor Lake-H/P Iris Xe） |
| 驱动 | WhateverXe ≥ 0.6.1（0.7.0 验证通过） |
| 补丁 | edp-keep 系列 3 个 commit（eDP 端口准入、VBT panel type `0xFF`、keep 决策钩子） |
| 启动参数 | `-wex-edp-keep` |
| 前提 | 固件阶段内屏已点亮（跑码有画面） |

若跑码阶段内屏就不亮，说明固件没有训练链路，此方案不适用。
