Zenbook UP6502ZD 上的扬声器没有声音 - 旧款 Cirrus 放大器经典问题

Zenbook UP6502ZD 上的扬声器没有声音 - 旧款 Cirrus 放大器经典问题

各位电脑爱好者大家好,

我最近购买了一台二手 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_id2cs35l41->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
                } }
            }
        })
    }
}

注意SPI1SPK1正确匹配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)

扬声器现在应该可以正常工作了!

相关内容