对于拯救者 Y9000X 2022 用户,在安装 sof-firmware
后,尽管内置麦克风工作正常,但无法通过内置扬声器播放声音。本文尝试简单修补 Linux 内核,以避免残缺 ACPI 表带来的参数缺失,正确驱动 CS35L4L 音频放大器。
本文假定您使用 Arch Linux 和 linux-v6.8.5-arch1 内核,尽管您可以参考其过程 backport 到其它内核版本/发行版。
免责声明
由于作者并不了解 Linux 内核开发,纯粹是拿起生疏的工具尝试自食其力地解决问题,故难以保证本文方法的正确性和优雅性。
同时请在尝试本文方法前做好数据备份工作并准备好 Live CD,以防止发生数据损坏、无法启动等问题。
此外,错误的驱动配置可能损坏您的硬件,故请谨慎操作。如出现意外情况将由您自行承担相关损失,与作者无关。
检查错误
查看 CS35L4L 相关信息:
1 | journalctl -b -g 'csc|cs35l41' --case-sensitive=false --output short-monotonic |
发现 CS35L4L 驱动加载失败:
1 | Serial bus multi instantiate pseudo device driver CSC3551:00: Instantiated 2 I2C devices. |
CS35L4L 在 Linux 下具有驱动,但由于某些厂商并没有在 ACPI Table 中正确地描述音频设备,而是在其 Windows 的驱动中硬编码某些配置项(坏文明!),导致 Linux 下无法正确识别音频设备。
这里需要修改内核源码,以硬编码拯救者的音频设备信息。由于大量厂商的设备存在类似问题,内核树中已存在对 CS35L4L 参数硬编码的良好支持,只需要添加一条记录即可。
修补问题
查找 Subsystem ID
查看 ACPI Table:
1 | # sudo pacman -S acpica |
使用 /CSC3551<Enter>
搜索,可以找到相关信息:
1 | Name (_HID, "CSC3551") // _HID: Hardware ID |
即我们需要处理的设备 Subsystem ID 为 17AA386E
制作 Patch (v1)
从 https://github.com/archlinux/linux/tree/v6.8.5-arch1 查看内核源码,修改相关文件。
修改 sound/pci/hda/patch_realtek.c
和大部分机型不同,Y9000X 2022 IAH7 的两个放大芯片的中断 IRQ 相同,且中断引脚通过 APIC 连接。为了避免以下错误,不直接使用 generic_dsd_config
genirq: Flags mismatch irq 58. 00002088 (cs35l41 IRQ1 Controller) vs. 00002088 (cs35l41 IRQ1 Controller)
作为 workaround,我们自己写一个配置函数,关闭第二个放大器的中断功能:
1 | static int single_interrupt_dsd_config(struct cs35l41_hda *cs35l41, struct device *physdev, int id, const char *hid) |
添加到配置模型表:
1 | // array cs35l41_prop_model_table |
并配置引脚信息和输出模式:
1 | // array cs35l41_config_table |
修改 sound/pci/hda/patch_realtek.c
添加 SND_PCI_QUIRK
:
1 | // array alc269_fixup_tbl |
生成补丁文件 sound_17aa386e_fix.patch
1 | # i:修改前的源码,w:修改后的源码 |
1 | diff --git i/sound/pci/hda/cs35l41_hda_property.c w/sound/pci/hda/cs35l41_hda_property.c |
制作 Patch (v2)
问题分析
上述方法直接关闭了第二个放大器的中断功能,这可能导致某些状态下无法自动重新初始化放大器。
关注错误:
genirq: Flags mismatch irq 58. 00002088 (cs35l41 IRQ1 Controller) vs. 00002088 (cs35l41 IRQ1 Controller)
这非常令人困惑,因为日志给出的两个 Flags 是相同的。
进一步调查显示,错误由 __setup_irq
函数中的以下检查引发:
1 | if (irqd_trigger_type_was_set(&desc->irq_data)) { |
可以看到,检查使用的 oldtype
实际上是通过 irqd_get_trigger_type
获取的。
虽然所有的中断都是用标志位 0x00002088 = IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW
请求的。
但这里获取到的 oldtype
是 IRQF_TRIGGER_RISING (0x1)
(遵从了 DSDT 表中的配置) 而非 IRQF_TRIGGER_LOW (0x8)
,导致了错误。
由于我们的中断脚是连接到 APIC 的,关注 platform_get_irq_optional
:
1 | if (r && r->flags & IORESOURCE_BITS) { |
可以看到 DSDT 表中的信息将用于配置 trigger type。
问题解决
修改 sound/pci/hda/cs35l41_hda.c
,在请求中断时尊重已有的中断配置:
1 | irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg); |
之后我们就可以使用 generic_dsd_config
完成配置了。
补丁文件 sound_17aa386e_fix.patch
1 | diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c |
制作 Patch (v3)
修改日志
根据 LKML 上的讨论,spkid GPIO Index 应该设置为 2,而不是 1。
补丁文件 sound_17aa386e_fix.patch
1 | diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c |
构建内核
克隆 Arch Linux 内核包构建文件:
1 | git clone https://gitlab.archlinux.org/archlinux/packaging/packages/linux |
修改 PKGBUILD
, 添加 sound_17aa386e_fix.patch
到 source
数组:
1 | source=( |
同时添加 SKIP
到 sha256sums
和 b2sums
数组,以跳过校验。
linux
包本身存在批量应用 patch 的功能,故无需修改 prepare()
函数
1 | local src |
构建包:
1 | # 很慢,坐和放宽(sit back and relax) |
安装内核
1 | sudo pacman -U linux-6.8.5.arch1-1-x86_64.pkg.tar.zst |
重启
1 | sudo reboot |
注意事项
推荐提前设置好 ~/.config/pacman/makepkg.conf
文件,以加速构建过程,例如:
1 | MAKEFLAGS="-j$(nproc)" |
内核编译需要占用大量空间,请提前检查 BUILDDIR
空间是否足够。
参考资料
- https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/1984157
- https://wiki.archlinux.org/title/DSDT
- https://yadom.in/archives/asus-notebook-cirrus-amp-in-linux-fix.html
- https://lore.kernel.org/lkml/SY4P282MB18359DB2390AEFED26AC53A0E037A@SY4P282MB1835.AUSP282.PROD.OUTLOOK.COM/
另记
又通宵了一个晚上(虽然是因为我菜,对内核不了解🥹),困死了。果然晚上不适合开始任何非劳力型的工作。
正在试图将该补丁提交到 Linux Kernel 主线,相关讨论见:
- https://lore.kernel.org/lkml/TYCP286MB25352F3E995FED9CCE90F1F6C40B2@TYCP286MB2535.JPNP286.PROD.OUTLOOK.COM/T/
- https://lore.kernel.org/lkml/TYCP286MB253523D85F6E0ECAA3E03D58C40E2@TYCP286MB2535.JPNP286.PROD.OUTLOOK.COM/T/
- https://lore.kernel.org/lkml/TYCP286MB25357A4599E935F26A8AAB24C40E2@TYCP286MB2535.JPNP286.PROD.OUTLOOK.COM/T/