---
title: HUAWEI MateBook D 15 2022（BoF-XX-PCB）Ubuntu Linux 外放无声修复
tags:
  - linux
  - ubuntu
  - huawei
  - matebook-d15
  - es8336
  - hwsp0001
  - audio
categories:
  - linux
date: 2026-10-11 10:00:00
updated: 2026-10-21 19:29:00
desc: HUAWEI MateBook D 15 2022（BoF-XX-PCB）在 Ubuntu 中已识别 ES8336 声卡和 Speakers 输出，但内置扬声器依旧无声。本文根据 DMI、ACPI 与 I²C 检查，将问题定位到 HWSP0001 外置功放未正确初始化，记录实测寄存器配置、两颗功放在读回特性上的差异、播放音频对寄存器的影响，并说明如何修正由此引发的 systemd 开机自检失败，再用 systemd 在开机及睡眠恢复后自动初始化，同时保留 GNOME/PipeWire 的扬声器与蓝牙切换。
abbrlink: matebook-d15-bofxx-hwsp0001-no-speaker
---
> **AI 辅助声明**：这篇文章由 AI 协助整理排查过程和技术说明。机型、系统输出、I²C 读写结果与外放是否恢复，均来自这台电脑的实际检查；结论只对本文记录的 BoF-XX-PCB 设备成立。
>
> **2026-10-21 复核修订**：复查开机服务状态时发现原脚本的自检有一处遗漏，导致服务每次开机都报 failed（外放本身正常）。本次补充了两颗功放的分别实测读数、`0x59` 写入后瞬态行为的澄清，并补做了「播放是否扰动寄存器」的对照实验，同时记录了验证条件的修正方式。修订以本机重新测得的数据为准，原先只写单侧读数的表格已替换。

Ubuntu 上这台 HUAWEI MateBook D15 长期没有外放声音，这个问题我一直搁着。最近想到 Codex 的 Linux 桌面客户端已经推出一段时间了，我也该试着把这个老问题认真查一遍，于是从机器实际识别到的声卡和播放链路开始排查。

我手头正好有一份名字看起来很对口的 `HUAWEI_MateBook_D_15_2022_ISST_10.29.0.7767.zip`，但里面实际只有 `ISST_10.29.0.7767.exe`。它是 Windows 安装程序，不能当成 Linux 驱动加载。继续检查后发现，Linux 已经识别出 ES8336 播放设备和 PipeWire 的 Speakers 输出。真正的问题落在另一个设备上：主板还暴露了独立的 `HWSP0001` 功放，而它的初始化状态不对。把匹配的寄存器配置写回左右两颗功放后，我听到了外放声音；后来又把这一步做成开机和睡眠恢复时自动执行的服务。

<!--more-->

## 先把机器型号说清楚

华为官网支持设备列表把 `BoF` 对应到 **HUAWEI MateBook D 15 2022 12 代酷睿版**。这台机器装 Linux 后，DMI 返回的是更具体的 `BoF-XX` 和 `BoF-XX-PCB`，版本为 `M1010`。所以下文的结论以系统实际报告的主板名为准，不把所有 MateBook D15 都当成同一块板。[华为官方型号列表](https://consumer.huawei.com/cn/support/content/zh-cn15945089/)

| 项目 | 本机实测 |
| --- | --- |
| 产品线 | HUAWEI MateBook D 15 2022 12 代酷睿版（官方系列代码 BoF） |
| DMI 产品名 / 版本 | `BoF-XX` / `M1010` |
| DMI 主板名 | `BoF-XX-PCB` |
| 系统 | Ubuntu 26.04.1 LTS |
| 当前内核 | `7.0.0-38-generic` |
| 音频控制器 | Intel Alder Lake PCH-P HDA，PCI ID `8086:51c8` |
| codec / 声卡 | Everest ES8336，ALSA 卡 `sofessx8336` |
| 另一个音频相关设备 | ACPI `HWSP0001:00`，I²C bus 3 上的 `0x58`、`0x5b` |

这些字段都可以在本机读取。遇到同系列电脑时，也建议先记下自己的实际值：产品名一样，不代表 DMI、音频 codec、功放和接线方式都一样。

```bash title="查看本机型号、主板与声卡"
cat /sys/class/dmi/id/{sys_vendor,product_name,product_version,board_name,board_version}
lspci -nnk | grep -i -A 4 'audio'
cat /proc/asound/cards
wpctl status
```

## 声卡已经出现，为什么外放还是没声音

本机的音频控制器由 `sof-audio-pci-intel-tgl` 驱动。内核日志显示 SOF 固件 `intel/sof/sof-adl.ri` 和 `sof-adl-es8336-dmic2ch-ssp0.tplg` 拓扑均已加载，ALSA 也创建了 `sof-essx8336` 卡；PipeWire 里有内置 Speakers sink。`speaker-test` 可以打开 PCM 并持续播放测试音，但机身扬声器听不到声音。

这一步很关键：**“音频流写进了 PCM”不等于“扬声器已经发声”。** 它只能证明上层播放流和声卡设备能工作，模拟输出端、外置功放和扬声器本身仍要单独验证。

我也检查了之前那个 `sof-es8336-fix.conf`。启动日志确实出现 `quirk mask 0xa0`，但当时外放仍然无声，所以这份 quirk 配置没有解决实际问题。它不是本次最终修复；本次持久化脚本也不会改它。

继续看 ACPI 和 I²C 后，发现声卡之外还有 `HWSP0001:00`。它在 sysfs 中映射到 I²C bus 3，两个功放地址是 `0x58` 和 `0x5b`，并且没有已绑定的内核 I²C 驱动。读取两边的寄存器后，左右器件都显示：寄存器 `0x00` 为 `0x5a`，寄存器 `0x01` 为 `0x38`。在这台机器上，这组状态与静音故障同时出现。

## 让外放恢复的寄存器配置

我参考了 Linux 音频邮件列表里一份针对华为 `BOD-WXX9` 的 **RFC 实验补丁**，以及社区对另一块 D15 主板 `BOD-WXX9-PCB-B4` 的调查。两者都提到 `HWSP0001`，但目标主板和 I²C bus 编号并不是我的 BoF-XX。因此我没有照搬对方的 GPIO 编号或总线号，而是先在本机确认设备路径、地址和寄存器状态，再做临时寄存器初始化，并通过实际听音确认效果。[Linux 邮件列表 RFC](https://patchew.org/linux/20260614161028.10405-1-tgaraev653@gmail.com/)；[BOD-WXX9 社区调查](https://github.com/MaximushkaBed/huawei-matebook-d15-speaker-fix)

本机实测的配置如下。脚本会把这些值分别写到 `0x58` 和 `0x5b`。下表是写完配置、功放已初始化成功后的读回情况，**按两颗芯片分别列出**——这是 2026-10-21 复核时补充的实测，原版本只记了单侧读数：

| 寄存器 | 写入值 | `0x58` 读回 | `0x5b` 读回 | 备注 |
| --- | --- | --- | --- | --- |
| `0x01` | `0x69` | `0x69` | `0x69` | **成功判据**，稳定 |
| `0x03` | `0x16` | `0x0c` | `0x0c` | 不保留 |
| `0x04` | `0x80` | `0x80` | `0x80` | |
| `0x05` | `0x0c` | `0x0c` | `0x0c` | |
| `0x06` | `0x11` | `0x11` | `0x11` | |
| `0x07` | `0x93` | `0x93` | `0x93` | |
| `0x09` | `0x0b` | `0x0b` | `0x0b` | |
| `0x0b` | `0x4b` | `0x4b` | `0x4b` | |
| `0x0c` | `0x00` | `0x00` | `0x00` | |
| `0x0d` | `0x77` | `0x77` | `0x77` | |
| `0x0f` | `0x51` | `0x51` | `0x51` | |
| `0x10` | `0x58` | `0x58` | `0x58` | |
| `0x58` | `0x00` | **`0x08`** | `0x00` | 左侧读不回（右侧成因未验证） |
| `0x59` | `0x80` | `0x00` | `0x00` | 稳定态一致；写入后有瞬态，见下 |

需要特别说明三点，都是复核时实测出来的，比原版本更精确：

**第一，`0x03` 在两侧都不保留。** 写入 `0x16`，两颗芯片都读回 `0x0c`。

**第二，`0x58` 寄存器在左侧（`0x58` 那颗）读不回写入值。** 写入 `0x00` 后，`0x58` 读回 `0x08`，而 `0x5b` 读回 `0x00`。

这里要说清楚一个限度：`0x5b` 读回 `0x00` **并不能证明它保留了写入值**——写入值本身就是 `0x00`，无法区分「真的写进去了」和「该寄存器在此器件上不生效、复位默认值恰好也是 `0x00`」。要区分这两者，得写入一个非默认值再读回，而我没有做这个实验（往功放未知寄存器写非默认值有风险）。所以准确的说法是：`0x58` 器件上这个寄存器确定读不回，`0x5b` 器件上读回值与写入值一致但成因未验证。

原版本因为只记了单侧读数，把 `0x58` 和 `0x59` 一并笼统归为「读不回」；但复核后三者性质其实各不相同：`0x03` 是两侧都真的不保留，`0x58` 只在左器件读不回，`0x59` 则是写入后的瞬态问题。所以**读回行为同时取决于器件地址和寄存器，也取决于测量时机**，不能按寄存器名一刀切。

**第三，`0x59` 在稳定态恒为 `0x00`，只有紧跟写入之后的短暂窗口会读到别的值。** 这一步需要更正我最初的判断：先前看到 `0x59` 读到 `0x88`（`0x58` 侧）和 `0x80`（`0x5b` 侧），一度以为它是「读数随机变化的状态寄存器」。后来反复测量才发现，那些异常读数全部出现在**服务刚重启、刚写完 profile 的几秒内**；进入稳定态后再测七八次，两侧恒为 `0x00`。

也就是说，这属于**写入动作引起的瞬态**，并非随机漂移。但对脚本的结论没有改变：验证循环紧跟在写入之后执行，正好落在那个不可信窗口里，所以 `0x59` 仍然必须跳过校验。要利用它得先等状态沉降，而开机流程里加延时收益不足，不值得。

因为以上差异，持久化脚本对这三处分别处理：`0x03` 因两侧都读不回而跳过，`0x59` 因写入后的瞬态而跳过，`0x58` 只在左侧器件跳过、右侧继续严格校验。它保留这些写入，是因为本机完整应用这组配置后，寄存器 `0x01` 变为 `0x69`，且我实际听到了内置扬声器发声。这里记录的是本机观察，不推断这些寄存器在所有板子上的含义。

我当时重新播放 440 Hz 测试音，随后确认机身外放恢复。这才是本次成功判据；只看到 `i2cset` 没报错并不足以证明已经修好。

在桌面声音设置里先选中内置 Speakers 并确认没有静音，再用 ALSA 直接播放测试音。`speaker-test` 会一直循环，听完按 `Ctrl+C` 停止：

```bash title="向 ES8336 播放设备发送 440 Hz 测试音"
speaker-test -D 'plughw:CARD=sofessx8336,DEV=0' -c 2 -t sine -f 440
```

### 播放音频会不会扰动功放状态

有一个值得单独确认的问题：驱动在播放时是否会自己改写这些寄存器，从而与脚本写入的配置冲突。为此做了一次对照实验——在同一个会话里，播放前对两颗器件的 5 个关键寄存器做快照，播放 3 秒 440 Hz 正弦波，播放后再快照一次：

```bash
# 播放前 / 播放后各执行一次
for a in 0x58 0x5b; do
  for r in 0x00 0x01 0x03 0x58 0x59; do
    printf '%s %s = %s\n' "$a" "$r" "$(i2cget -y -f 3 "$a" "$r")"
  done
done

# 播放（需要以当前登录用户身份运行，root 下没有 PipeWire 会话）
XDG_RUNTIME_DIR=/run/user/1000 \
  timeout --signal=INT 3 speaker-test -D 'plughw:CARD=sofessx8336,DEV=0' -c 2 -t sine -f 440
```

结果：**10 个读数前后完全一致，一个字节都没变**，间隔数分钟复测仍相同。播放动作不会改写这些寄存器，脚本写入的配置在播放期间保持有效，两者不冲突。

| 寄存器 | `0x58` 器件 | `0x5b` 器件 |
| --- | --- | --- |
| `0x00` | `0x5a` → `0x5a` | `0x5a` → `0x5a` |
| `0x01` | `0x69` → `0x69` | `0x69` → `0x69` |
| `0x03` | `0x0c` → `0x0c` | `0x0c` → `0x0c` |
| `0x58` | `0x08` → `0x08` | `0x00` → `0x00` |
| `0x59` | `0x00` → `0x00` | `0x00` → `0x00` |

这次实验也顺便证实了上一条关于 `0x59` 的判断：稳定态下它稳定为 `0x00`，只有在刚写完 profile 之后才会短暂读到 `0x88` / `0x80`。

### 一个由此引发的自检失败（2026-10-21 修正）

原版本的验证逻辑写的是 `case $reg in 03|59) continue`，即只跳过 `0x03` 和 `0x59`。但如上所述，`0x58` 寄存器在左侧芯片上同样读不回，于是每次开机服务都会在这一行失败：

```text
verification failed at 0x58 register 0x58: got 0x08
```

这**不影响外放**——所有 I²C 写入都在验证之前就已完成，`0x01` 早已是 `0x69`，声音是正常的。它只是让 `systemctl status` 白报一个 failed，容易误导后来排查的人以为修复没生效。

修正办法是把跳过条件细到「地址 + 寄存器」组合，而不是按寄存器名全局跳过：

```bash
case $reg in 03|59) continue ;; esac
# 左侧 0x58 器件读不回 0x58，右侧 0x5b 读回值与写入值一致
case "$addr:$reg" in 0x58:58) continue ;; esac
```

这样 `0x5b` 侧的 `0x58` 仍然严格校验，不为了让它变绿而削弱原有的检查强度。改完后服务状态为 `active`，日志正常输出 `HWSP0001 restore profile applied and verified on I2C bus 3 (0x58, 0x5b)`。

> **注意**：以上修正记录的是本机测到的行为。`0x58` 寄存器在其它 BoF-XX 机器上是否同样不保留，没有第二台设备可验证，不应据此认为所有同型机都一致。

## 整理成 MIT 开源仓库，并做成持久修复

我把初始化脚本、systemd oneshot 服务、睡眠恢复钩子、安装/卸载脚本和硬件诊断说明整理到公开仓库：[matebook-bofxx-audio-fix](https://github.com/xingwangzhe/matebook-bofxx-audio-fix)，采用 MIT License。

脚本并不是无条件往 I²C 写数据。每次运行前，它都会核对：

| 检查项 | 必须匹配的本机状态 |
| --- | --- |
| 厂商、产品、主板 | `HUAWEI`、`BoF-XX`、`BoF-XX-PCB` |
| 功放设备路径 | `HWSP0001:00` 位于 bus 3 |
| I²C 驱动状态 | 设备没有绑定内核驱动，避免与驱动争用 |
| 两个 I²C 地址 | `0x58` 和 `0x5b` 的寄存器 `0x00` 都读为 `0x5a` |
| 初始/已修复状态 | 寄存器 `0x01` 只能是已观察到的 `0x38` 或 `0x69` |

核验不通过时，脚本记录错误并停止写入。通过时才向两个地址重放配置，然后逐项检查可读回的寄存器——`0x03`、`0x59` 以及左侧芯片的 `0x58` 会被跳过，原因见前文「让外放恢复的寄存器配置」。卸载入口只移除这个项目安装的 service、resume hook 和脚本；它不碰系统里其它音频配置，也不把功放寄存器写回旧的静音状态。

```bash title="安装 BoF-XX 外放修复"
git clone https://github.com/xingwangzhe/matebook-bofxx-audio-fix.git
cd matebook-bofxx-audio-fix
pkexec bash "$(pwd)/scripts/install.sh"
```

开机服务负责启动时初始化，systemd sleep hook 在睡眠恢复后调用同一个硬件检查脚本。原版本这里记的是「本机验证过服务启动成功」，但 2026-10-21 复核发现：服务其实是**每次开机都 failed**，原因是 `0x58` 寄存器的验证漏网（详见上文「一个由此引发的自检失败」）。修正验证条件后服务才真正 `active`。另外做过一次恢复路径模拟：将功放状态临时置回已观察到的静音值，再调用恢复 hook，确认寄存器状态重新恢复。**我没有实际让机器睡眠再唤醒来验证过**，所以这一项仍建议安装后自行完整验证。

卸载命令：

```bash title="移除此项目安装的自动修复"
pkexec /usr/local/sbin/matebook-hwsp-uninstall
```

## 蓝牙和扬声器输出仍由桌面音频管理

修复脚本只操作 `HWSP0001` 的 I²C 寄存器。它不运行 `wpctl` 或 `pactl`，不更改 WirePlumber/PipeWire 配置，不设置默认输出，也不禁用蓝牙。

安装后检查 `wpctl status`，本机仍分别列出了内置 Speakers sink 和一个蓝牙 sink；检查默认 sink 时，它仍指向蓝牙输出。也就是说，脚本没有把系统音频路由强行锁到内置扬声器。需要切换时，仍从 GNOME 的声音设置里选择蓝牙设备或内置扬声器。本文没有把“设备列出来”冒充成“我完整测试了每个蓝牙设备的音质”，蓝牙播放本身仍取决于设备连接和 PipeWire 状态。

## 其它华为主板遇到类似问题，先做这几步

华为笔记本的产品名不等于主板型号。D15、D14 等同一系列也可能使用不同平台、codec、功放、GPIO 和 ACPI 拓扑。建议按这个顺序排查：

| 步骤 | 先确认什么 | 不要据此直接推断什么 |
| --- | --- | --- |
| 1 | DMI 的厂商、产品名、主板名、版本 | 不要只凭“MateBook D15”套用别人的脚本 |
| 2 | `lspci -nnk`、`/proc/asound/cards`、`wpctl status` | 有声卡节点不等于物理扬声器一定有声 |
| 3 | 内核日志里的驱动、SOF firmware、topology、codec probe | 不要因为网上有人编译了内核，就先换内核 |
| 4 | ACPI 是否出现 `ESSX8336`、`HWSP0001` 等设备，sysfs 把设备映射到哪个 I²C bus | 设备名称相同也不保证 bus、GPIO、两颗地址相同 |
| 5 | 确认设备未被驱动占用后，按目标板资料做受控只读检查 | 不要用 `i2cset -f` 对陌生地址试写，更不要盲目电源循环 GPIO |
| 6 | 小范围验证一个有来源的寄存器配置，并用耳朵确认前后变化 | 一台机器恢复不代表相同品牌或同一产品线都适用 |

社区 BOD-WXX9 方案使用的是 bus 2 和 GPIO 267；本机的 `HWSP0001` 则映射到 bus 3。这个差异正好说明为什么不能只复制一个型号相似的命令。更重要的是，那份 Linux 内核邮件仍是 RFC 讨论，不是已经合并、适用于所有华为机器的官方驱动；RFC 自己也说明寄存器恢复配置是经验性的，并需要更多硬件验证。

如果你的电脑外放无声但声卡节点存在，先区分是 UCM/音量路由、codec GPIO、耳机检测，还是独立功放初始化。把 `wpctl status`、内核日志、DMI 和 ACPI/I²C 映射整理出来，往往比盲目重装所谓“Linux 声卡驱动”更快接近问题本身。

## 参考资料

- [华为官方支持设备列表：MateBook D 15 2022 12 代酷睿版与 BoF 产品代码](https://consumer.huawei.com/cn/support/content/zh-cn15945089/)
- [Linux sound 邮件列表：HWSP0001 支持 RFC](https://patchew.org/linux/20260614161028.10405-1-tgaraev653@gmail.com/)
- [BOD-WXX9-PCB-B4 社区功放排查和脚本](https://github.com/MaximushkaBed/huawei-matebook-d15-speaker-fix)
- [本次修复代码、安装/卸载脚本与 MIT License](https://github.com/xingwangzhe/matebook-bofxx-audio-fix)


---

**作者：**xingwangzhe

**本文链接：**[https://xingwangzhe.fun/posts/matebook-d15-bofxx-hwsp0001-no-speaker/](https://xingwangzhe.fun/posts/matebook-d15-bofxx-hwsp0001-no-speaker/)

本文采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可。