黑苹果上"扬声器正常、耳机正常、就是内置麦克风没声"是很常见的一类问题。大多数教程会让你去换 AppleALC 的 layout-id,但有一类机器换多少个 layout 都没用——因为它的麦克风根本不走 codec。
这篇博客分两部分:先说清楚黑苹果上内置麦克风的两种硬件路径和通用的驱动配置方法(以 TGLDmic 为例),再完整记录我这台机器踩到的坑、定位过程和最终修复。
结论先放这儿:
AppleALC + layout-id只解决模拟麦克风;PDM 数字麦克风需要把 Intel SOF 固件跑在 cAVS DSP 上,而 TGLDmic 在部分 Comet Lake 机型上会因为"平台没把 DSP 状态归位"卡在第一步。补丁很小,但要知道去哪儿补。
一、先搞清楚:你的麦克风接在哪
笔记本内置麦克风有两条完全不同的通路:
| 模拟麦克风 | PDM 数字麦克风 | |
|---|---|---|
| 物理连接 | 驻极体/MEMS → 模拟线 → HD Audio codec 的 ADC | MEMS 数字麦 → PDM 总线 → Intel SST/audio DSP |
| 谁在驱动 | AppleHDA + AppleALC(layout) | Intel SOF 固件,跑在 DSP 核上 |
| macOS 原生支持 | ✅ 有 | ❌ 没有(Mac 不用这架构) |
| 黑苹果解法 | 选对 layout-id / alc-layout-id | TGLDmic 这类社区驱动 |
四麦克风阵列、远场拾音、Dolby Atmos 这类卖点的机器,内部麦几乎都是第二种。这类机器上 AppleALC 能给你一个叫 “Internal Microphone” 的输入设备,但它没有声音——因为 codec 里根本没有接那条 ADC。
怎么判断自己属于哪一类
我用了三个互相独立的证据:
- Windows 侧驱动(最硬)。挂载 NTFS 分区只读,看
Windows/System32/DriverStore/FileRepository:出现intcaudiobus.inf、intcdmic.inf、lnvsst.inf这类包,说明这台机器的麦克风走 Intel SST 数字麦。 - Linux 侧 dmesg。Live USB 启动后看
dmesg | grep -i sof,出现DMICs detected in NHLT tables: 4之类,就是 PDM 阵列。 - macOS 侧"假麦克风"检测。选中 Built-in Input 录一段,用
ffmpeg看电平:
| |
我的机器结果:开头一声爆音(max -6 dB),之后全是 ±1 / ±2 的数字静音。看起来设备存在、有电平表,其实是假的。
只有第 1、2 条成立时才值得折腾 DSP 驱动;否则应该回去调 AppleALC。
二、TGLDmic 是什么,怎么配
TGLDmic 是一个社区项目,由两部分组成:
TGLDmic.kext:内核驱动。加载 Intel 官方签名的 SOF 固件(sof-tgl.ri/sof-cml.ri)到 cAVS DSP,按 Linux SOF 的 IPC 序列配置 DMIC 采集链路,再用 DMA 把 4 通道 48 kHz 音频搬到 ring buffer。TGLDmic.driver:Core Audio HAL 插件,把这个 ring buffer 包装成输入设备 “Internal Microphone (TGLDmic)"。
支持平台:
| 平台 | cAVS | PCI ID |
|---|---|---|
| Comet Lake / Cannon Lake / Coffee Lake | 1.8 | 8086:02C8 8086:9DC8 8086:06C8 8086:A348 |
| Tiger Lake | 2.5 | 8086:A0C8 8086:43C8 |
安装四步
TGLDmic.kext放进EFI/OC/Kexts/,在Kernel → Add里加在 AppleALC 之后;NVRAM → boot-args加-tgldmic;- 装 release 里的 pkg(装 HAL 插件);
- 系统设置 → 声音 → 输入,选 “Internal Microphone (TGLDmic)"。
扬声器和耳机仍然由 AppleALC 负责,两边是并行的,不需要二选一。
自检
| |
正常应该看到:
| |
TGLDmicPhase 是模式:1 = 只读观察,2 = 启动一次 DSP 自检,3 = 采集测试,4 = 常驻服务。日常用 4。
三、通用的排错顺序(不针对某台机器)
出问题时不要一上来就换固件、换 boot-args,按这个顺序看信息量最大:
- kext 有没有加载、认成哪个平台
ioreg -l -w0 -c TGLDmic里看TGLDmicVersion、Platform、BootResult。 - 失败在哪一步
BootResult后面括号里的值就是失败时寄存器的原始值,比日志还直接。 - 看最后 128 行日志
ioreg -l -w0 -c TGLDmic | tr ',' '\n' | grep -E '"(boot|fix|cap):'—— 驱动自己维护了一个环形缓冲区,log show里不一定能看到。 - 区分"软件没配好"和"硬件没给你” DSP 相关失败绝大多数是后者:固件没跑起来之前,改什么都白搭。
- 用 Linux 做对照
这个项目自带了
linux/capture-sof-dmic.sh,Ubuntu Live 跑一遍就能知道同一台机器上 Linux SOF 能不能点亮 DSP。能 → kext 的差距;不能 → BIOS/平台层,别在 macOS 里死磕。
常见失败点大致长这样(按流程顺序):
BootResult | 含义 |
|---|---|
FAIL ADSPCS unreadable | BAR4 没映射上 / 控制器不在 D0 |
FAIL core0 power up (CPA) | DSP 核没上电(本文的情况) |
FAIL ROM IPC ack / ROM init done | 核起来了,ROM 没响应 |
FAIL FW_ENTERED + ROM error 44 | 固件没通过 CSE 验签(多半用错/改过的固件) |
FAIL FW_READY | 固件跑起来了但没回就绪消息 |
看到一个失败值,先去和能用的机器比同一条日志,差异通常就藏在里面。
四、我这台机器:从 0x001d003c 说起
机器:联想 Comet Lake-U(i7-10710U),HDA 8086:02C8,SSID 17aa:380f,codec ALC257,SMBIOS 伪装 MacBookAir9,1,OpenCore REL-104,macOS 14.8.5。(从 EFI 里 SSDT-OCBAT1-lenovoPRO13 这套 ACPI 命名推测是联想小新 Pro 13 一代的配置,仅供参考。)
版本:TGLDmic v0.5.0。这里有个关键背景——Comet Lake 支持是 2026-10-30 的 v0.5.0 才合入的,作者验证的机器是 ThinkPad X1 Carbon Gen 7(ALC285,SSID 17aa:2292),和我的不是同一块主板。
现象
| |
日志:
| |
读懂 ADSPCS
cAVS 的 ADSPCS(DSP BAR + 0x04)每个核占 4 个位段:
| 位段 | 名称 | 含义 |
|---|---|---|
[7:0] | CRST | 核心复位 |
[15:8] | CSTALL | 核心停顿 |
[23:16] | SPA | 请求上电 |
[31:24] | CPA | 已上电(硬件置位) |
0x001d003c→ CRST=0x3c、CSTALL=0x00、SPA=0x1d、CPA=0x00- 参考机器的正常空闲值 →
0x00000f0f:4 个核都处于 stall + reset、未上电 - 这次写入 SPA 后的正常值 →
0x0f0f0f0f:SPA/CPA 全部置位
三个观察让方向变得很明确:
- 本机的初始值不是"干净空闲”,而是一个半复位的残留状态(core0 甚至不在 reset 里,SPA 却已经有位);
- 对 ADSPCS 的写入全部无效——连
corePowerDown()里的 CSTALL/CRST 都改不动它,每次重试读回的还是同一个0x001d003c; PPCTL是0x00000000,而参考机器的日志里是0x40000000。
固件一个字节都还没写进去,所以这不是 sof-cml.ri 的问题,也不是 AppleALC、boot-args、HAL 插件的问题:平台没把 DSP 从被按住的状态里放开。
五、对照 Linux SOF 源码找差异
TGLDmic 的启动序列是照着 Linux sound/soc/sof/intel/hda-loader.c 写的,但它省略了两步"通常 AppleHDA 已经替它做过"的操作:
PPCTL.GPROCEN = 1Linux 在hda_dsp_probe()里调用hda_dsp_ctrl_ppcap_enable(sdev, true),也就是给PPCTL的 bit30 置位(Global Processing Enable)。TGLDmic 只设置了 loader stream 自己的PROCEN位,从来没设过GPROCEN。- 一个干净的核状态
Linux 的
hda_dsp_pre_fw_run()会先检查 DSP 是否已经使能,若使能先完整掉电;然后关掉 clock/power gating。cl_dsp_init()再对host_managed_cores_mask(CML 是0xF)置 SPA、等 CPA。TGLDmic 只在CPA(CORE0)已经置位时才会尝试复位,本机 CPA=0,所以直接跳过了。
在作者那台 X1C7 上,AppleHDA 留下的状态恰好是干净的 0x0f0f,这两步没做也没事。在我的机器上不是,于是 SPA 写下去永远等不到 CPA。
六、补丁:在启动前把状态归位
思路很直接:把 Linux 会做的那两步补上,并加上足够的读回日志和重试。
| |
配合两处改动:
bootDsp()里在gating(false)之后、prepareLoaderStream()之前调用preBootFix();corePowerUp()改成最多重试 3 次,每次写 SPA 前后都打印ADSPCS,失败时重新preBootFix()再试。
另外把 gating(false) 之后 CGCTL/PGCTL/EM2 的读回值也打印出来——这能直接判断 PCI 配置空间的写到底生效没有:
| |
编译与安装
| |
装回 EFI 前先把原版备份出来,出问题一条命令就能退回:
| |
只改了 kext 本身,config.plist、boot-args、AppleALC、HAL 插件都不用动。
七、结果
重启后:
| |
启动日志:
| |
录音实测(两次独立会话,间隔十几秒,DSP 会自动重新上电):
| 会话 | 格式 | mean | max |
|---|---|---|---|
| 第 1 次 | 48 kHz 立体声 | -30.7 dB | -9.3 dB |
| 第 2 次 | 48 kHz 立体声 | -38.7 dB | -25.5 dB |
安静房间里的真实环境声,不是数字静音。扬声器和耳机不受影响。
一点诚实的说明
补丁里放了两步(GPROCEN + 强制复位),但第一次打补丁那轮启动的日志已经被 128 行环形缓冲区顶掉了,现在能看到的每一轮开机都已经是干净状态。所以我能确认的是:补丁兼容、机器从此每次都能正常启动;但我没法百分百断言是哪一步起的作用,也排除不了"换 kext 时那次完整断电顺带清掉了卡死状态"。
真要归因,做一次 A/B 就行:把原版 0.5.0 装回去、重启,看 0x001d003c 会不会回来。这一步我留着当后续实验——毕竟现在麦克风能用,能不折腾就不折腾。
八、给后来人的 checklist
- 先判断麦克风类型,再决定要不要折腾 DSP。Windows 驱动目录 / Linux dmesg / “假麦” 录音测试,三个里凑够两个再动手。
- 先读
BootResult的括号值。它是失败时的寄存器原值,信息量比任何描述都大。 - 和已知可用的机器比同一条日志。我这次的突破口就是
ADSPCS和PPCTL两行和作者机器对不上。 - 写入是否生效要读回。寄存器写下去、读回不变,说明问题根本不在你写的值上。
- 别在 DSP 没点亮时换固件。
ROM error 44才是固件/验签问题,CPA不上电跟固件无关。 - 保留原版 kext 和 patch 源码。上游更新会覆盖补丁;我这台机器一旦装回官方的 0.5.0,麦克风就回到原形。
- 失败时 TGLDmic 会抢默认输入,而且从它录音可能直接卡住(我实测 ffmpeg 打开设备两分钟无响应)。修好之前先把默认输入切回 Built-in Input,或者卸掉 HAL 插件。
- DSP 只在录音时上电,空闲时日志里
Streaming = No是正常现象,不代表坏了。
参考
- labrouss/TGLDmic —— 项目主页、安装与测试文档
- Linux 内核
sound/soc/sof/intel/:hda-ctrl.c(hda_dsp_ctrl_ppcap_enable)、hda-dsp.c(hda_dsp_core_power_up、hda_dsp_core_stall_reset)、hda-loader.c(cl_dsp_init)、hda.c(hda_dsp_pre_fw_run) - Intel High Definition Audio 规范中的
ADSPCS定义