[PATCH 3/4] ALSA: hda: cs35l41: Add support for CLSA0102

From: Ilya Pavlukhin

Date: Sun Sep 20 2026 - 05:30:32 EST


The Lenovo Yoga Slim 7 Carbon 14ACN6 (82L0) drives its bass speakers
with two CS35L41 amplifiers, described by ACPI HID CLSA0102 without a
_DSD. The node has the same layout as CLSA0100/CLSA0101: both amps in
one node, reset on GPIO index 0 and the interrupt on a GpioInt. Unlike
the Legion 7 there is no speaker ID GPIO, index 2 is the interrupt line.

The Windows driver runs the amps from an external VSPK supply
(ExternalVspkControl, boost converter disabled), so use
CS35L41_EXT_BOOST with the VSPK switch on GPIO1. With the internal
boost converter enabled the amps report "Amp short error" during
playback.

There is no _SUB in the ACPI node, so the firmware files are looked up
by the codec SSID, 17aa3856.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=215632
Assisted-by: Claude Opus 5
Signed-off-by: Ilya Pavlukhin <i.pavluhin@xxxxx>
---
.../hda/codecs/side-codecs/cs35l41_hda_i2c.c | 3 ++
.../codecs/side-codecs/cs35l41_hda_property.c | 28 +++++++++++++++++++
2 files changed, 31 insertions(+)

diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
index fdf406e92..59b82238f 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
@@ -23,6 +23,8 @@ static int cs35l41_hda_i2c_probe(struct i2c_client *clt)
device_name = "CLSA0100";
else if (strstr(dev_name(&clt->dev), "CLSA0101"))
device_name = "CLSA0101";
+ else if (strstr(dev_name(&clt->dev), "CLSA0102"))
+ device_name = "CLSA0102";
else if (strstr(dev_name(&clt->dev), "CSC3551"))
device_name = "CSC3551";
else
@@ -45,6 +47,7 @@ static const struct i2c_device_id cs35l41_hda_i2c_id[] = {
static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
{"CLSA0100", 0 },
{"CLSA0101", 0 },
+ {"CLSA0102", 0 },
{"CSC3551", 0 },
{}
};
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
index 416d7bf3e..c4f2bbed0 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
@@ -439,6 +439,33 @@ static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *phy
return 0;
}

+/*
+ * Device CLSA0102 (Lenovo Yoga Slim 7 Carbon 14ACN6) has the same ACPI layout as CLSA010(0/1):
+ * no _DSD, both amps in one node, reset on GPIO index 0. It has no speaker ID GPIO, the
+ * interrupt GPIO sits at index 2. The speakers use an external VSPK supply switched by GPIO1
+ * (the Windows driver sets ExternalVspkControl and keeps the boost converter off).
+ */
+static int lenovo_yoga_slim7_carbon_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev,
+ int id, const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+ cs35l41->speaker_id = -ENOENT;
+ 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;
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
const char *hid)
{
@@ -463,6 +490,7 @@ struct cs35l41_prop_model {
static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CLSA0100", NULL, lenovo_legion_no_acpi },
{ "CLSA0101", NULL, lenovo_legion_no_acpi },
+ { "CLSA0102", NULL, lenovo_yoga_slim7_carbon_no_acpi },
{ "CSC3551", "10251826", generic_dsd_config },
{ "CSC3551", "1025182C", generic_dsd_config },
{ "CSC3551", "10251844", generic_dsd_config },
--
2.43.0