各位电脑爱好者大家好,
我最近购买了一台二手 Zenbook flip 15.6 OLED (UP6502ZD),配备第 12 代英特尔 CPU。我做的第一件事自然是安装 Ubuntu(在本例中是 Lubuntu 23.10,因为我有极简主义强迫症)。
令我惊讶的是(华硕真丢脸),这款笔记本电脑的扬声器开箱后无法使用。这是为什么呢?
嗯,出于某种莫名其妙的原因,华硕决定_DSD
从 BIOS 中 Cirrus Logic CSC3551 ADC 的 ACPI 表条目中删除(设备特定数据)部分。没有这个_DSD
部分,它就无法管理驱动扬声器的两个 CS35L41 放大器,因此扬声器不会输出声音。
我确信这完全是巧合,与供应商混淆或华硕、Cirrus 和微软勾结无关,Windows 驱动程序对参数进行了硬编码_DSD
。华硕似乎也没有心情更新受此问题影响的无数设备上的 BIOS。
遇到此问题的人通常会拥有正常工作的耳机、HDMI 和麦克风接口,因为只是扬声器驱动程序初始化出现问题。
Linux 社区和内核开发人员都非常清楚这个华硕扬声器问题。目前几乎每日提交补丁在内核邮件列表中提到了 Asus、扬声器和 CS35L41。对许多 Asus 笔记本电脑的支持正在逐步增加。
不幸的是,UP6502ZD 还没有受到关注,所以我试图通过修补内核并_DSD
在 ACPI 表中添加部分来让它自己工作。
我一直在关注本教程。旧方法是转储 ACPI 表,找到 CSC3551 部分,然后使用那里的信息构建自定义_DSD
部分,然后在启动时加载该部分以在内核加载之前覆盖表。
新方法(因为补丁已经可用)是修补内核,将_DSD
信息直接提供给 cs35l41 结构。
cs35l41_hda_property.c
我修补了 6.6.3,因为这是迄今为止的稳定版本,但这在这里并不重要。补丁中添加的功能如下所示这里被调用asus_rog_2023_spkr_id2
并手动填充_DSD
信息:
/*
* The CSC3551 is used in almost the entire ASUS ROG laptop range in 2023, this is likely to
* also include many non ROG labelled laptops. It is also used with either I2C connection or
* SPI connection. The SPI connected versions may be missing a chip select GPIO and require
* an DSD table patch.
*/
static int asus_rog_2023_spkr_id2(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
const char *hid)
{
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
/* check SPI or I2C address to assign the index */
cs35l41->index = (id == 0 || id == 0x40) ? 0 : 1;
cs35l41->channel_index = 0;
cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
hw_cfg->spk_pos = cs35l41->index;
hw_cfg->bst_type = CS35L41_EXT_BOOST;
hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
hw_cfg->gpio1.valid = true;
hw_cfg->gpio2.func = CS35L41_INTERRUPT;
hw_cfg->gpio2.valid = true;
if (strcmp(cs35l41->acpi_subsystem_id, "10431473") == 0
|| strcmp(cs35l41->acpi_subsystem_id, "10431483") == 0
|| strcmp(cs35l41->acpi_subsystem_id, "10431493") == 0
|| strcmp(cs35l41->acpi_subsystem_id, "10431DA2") == 0) {
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 1, GPIOD_OUT_HIGH);
} else {
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
}
hw_cfg->valid = true;
return 0;
}
子系统 ID10431DA2
对应于 UP6502ZD(我已将其添加进去)。请注意,该asus_rog_2023_spkr_id2
函数是从包含各种华硕型号的此表调用的:
static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CLSA0100", NULL, lenovo_legion_no_acpi },
{ "CLSA0101", NULL, lenovo_legion_no_acpi },
{ "CSC3551", "103C89C6", hp_vision_acpi_fix },
...
{ "CSC3551", "10431493", asus_rog_2023_spkr_id2 }, // ASUS GV601V - spi, reset gpio 1
{ "CSC3551", "10431573", asus_rog_2023_spkr_id2 }, // ASUS GZ301V - spi, reset gpio 0
...
{ "CSC3551", "10431DA2", asus_rog_2023_spkr_id2 }, // ASUS UP6502ZD - spi, reset gpio 0
{}
};
您可以从中看到的内容asus_rog_2023_spkr_id2
是cs35l41->reset_gpio
根据子系统 ID 进行硬编码的。不清楚作者如何确定要为每个型号选择哪个引脚。也许他们有一些额外的信息或可以访问规格表。
无论如何,我尝试了 0 和 1,并发现从启动输出来看,1 比 0 更有帮助。
这是我当前的输出dmesg | grep CSC
:
$ sudo dmesg | grep CSC
[ 11.706278] Serial bus multi instantiate pseudo device driver CSC3551:00: Instantiated 2 SPI devices.
[ 12.412121] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Using extra _DSD properties, bypassing _DSD in ACPI
[ 12.461060] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Cirrus Logic CS35L41 (35a40), Revision: B2
[ 12.484874] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Using extra _DSD properties, bypassing _DSD in ACPI
[ 12.484901] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Reset line busy, assuming shared reset
[ 12.587890] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Failed waiting for OTP_BOOT_DONE: -110
[ 12.599850] cs35l41-hda: probe of spi0-CSC3551:00-cs35l41-hda.1 failed with error -110
如您所见,似乎第一个放大器已初始化,但第二个放大器尚未初始化。但这可能是一个转移注意力的借口,也许两个放大器都已初始化,错误与OTP_BOOT_DONE
其他东西有关。
除了内核补丁之外,我还有一个针对芯片选择引脚的 ACPI 覆盖:
DefinitionBlock ("", "SSDT", 1, "CUSTOM", "CSC3551", 0x00000001)
{
External (_SB_.PC00.SPI1, DeviceObj)
External (_SB_.PC00.SPI1.SPK1, DeviceObj)
Scope (_SB.PC00.SPI1)
{
Name (_DSD, Package ()
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cs-gpios", Package () {
Zero, // Native CS
SPK1, Zero, Zero, Zero // GPIO CS
} }
}
})
}
}
注意SPI1
并SPK1
正确匹配acpidump
我的系统的参数。
我不明白的是这些cs-gpios
属性应该如何设置:这是可以从系统本身获取的东西,还是来自主板规格表和硬件配置的神奇数字。
因此,除了尝试随机设置“cs-gpios”引脚或进一步阅读内核源代码外,我不确定下一步该怎么做。任何帮助都将不胜感激。
干杯
阿什利
答案1
感谢这里尤其是 Alex Vinarskis,扬声器现在可以正常工作了。上面提到的补丁的问题是第二个放大器没有被初始化。
Alex 还想出了如何_DSD
直接从补丁更改最终属性,因此这是您需要添加到sound/pci/hda/csl41_hda_property.c
// ASUS UP6502ZD - spi, reset gpio 0
static int asus_up6502zd(struct cs35l41_hda *cs35l41, struct device *physdev, int id, const char *hid) {
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id);
/* check SPI or I2C address to assign the index */
cs35l41->index = id;
cs35l41->channel_index = 0;
cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, 2, -1);
hw_cfg->spk_pos = cs35l41->index ? 1 : 0;
hw_cfg->bst_type = CS35L41_EXT_BOOST;
hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
hw_cfg->gpio1.valid = true;
hw_cfg->gpio2.func = CS35L41_INTERRUPT;
hw_cfg->gpio2.valid = true;
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 1, GPIOD_OUT_HIGH);
if(cs35l41->index) {
struct spi_device *spi = to_spi_device(cs35l41->dev);
spi->cs_gpiod = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
}
hw_cfg->valid = true;
return 0;
}
然后在cs35l41_prop_model_table
该函数中添加一个指针:
{ "CSC3551", "10431DA2", asus_up6502zd}, // ASUS UP6502ZD
最后你需要启用ALC245_FIXUP_CS35L41_SPI_2
怪癖sound/pci/hda/patch_realtek.c
SND_PCI_QUIRK(0x1043, 0x1DA2, "ASUS UP6502ZD", ALC245_FIXUP_CS35L41_SPI_2)
扬声器现在应该可以正常工作了!