From: Richard Fitzgerald rf@opensource.cirrus.com
[ Upstream commit 40b1c2f9b299295ed0482e1fee6f46521e6e79e5 ]
The Lenovo Yoga Book 9i GenX has the wrong values in the cirrus,dev-index _DSD property. Add a fixup for this model to ignore the property and hardcode the index from the I2C bus address.
The error in the cirrus,dev-index property would prevent the second amp instance from probing. The component binding would never see all the required instances and so there would not be a binding between patch_realtek.c and the cs35l56 driver.
Signed-off-by: Richard Fitzgerald rf@opensource.cirrus.com Reported-by: Brian Howard blhoward2@gmail.com Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220228 Link: https://patch.msgid.link/20250714110154.204740-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai tiwai@suse.de Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
Now let me analyze the actual changes in the current commit more carefully. The commit adds:
1. A fixup function for the Yoga Book 9i GenX that hardcodes the device index based on I2C address 2. A fixup table mechanism to apply platform-specific workarounds 3. Modified the cs35l56_hda_read_acpi function to apply fixups before reading the device index
**Analysis and Backport Decision: YES**
This commit should be backported to stable kernel trees for the following reasons:
1. **Fixes a real bug affecting users**: The commit fixes a hardware initialization failure on the Lenovo Yoga Book 9i GenX laptop where the second amplifier would fail to probe due to incorrect cirrus,dev- index values in the ACPI _DSD property. This prevents audio from working properly on this device.
2. **Small and contained fix**: The changes are minimal and localized to: - Adding a platform-specific fixup function that maps I2C addresses to correct device indices - Adding infrastructure to apply platform fixups - Modifying the initialization flow to check for fixups before reading ACPI properties
3. **Low regression risk**: - The fixup only applies to one specific laptop model (17AA390B) - The fixup infrastructure is designed to be non-invasive - it only activates for specific subsystem IDs - The changes don't affect the behavior for any other devices
4. **Follows stable kernel criteria**: - Fixes a real bug that bothers people (audio not working on a consumer laptop) - The fix is obviously correct and tested (reported by a user and has a bug report) - Small change (adds ~60 lines, mostly the fixup infrastructure) - No new features, just a workaround for broken firmware
5. **Similar to previously backported commits**: Looking at the historical commits, we see that: - Commit 91191a6e50a2 ("ALSA: hda: cs35l56: Don't use the device index as a calibration index") was marked as backport-worthy (YES) and addressed a similar issue with device index handling - Multiple commits adding quirks for Lenovo devices with CS35L41/CS35L56 have been backported (marked YES)
6. **Hardware enablement**: This falls into the category of hardware enablement fixes which are generally considered appropriate for stable kernels when they fix broken hardware without risking existing functionality.
The commit message clearly indicates this is fixing a specific hardware issue where "The error in the cirrus,dev-index property would prevent the second amp instance from probing" which would result in no audio binding and broken audio on this laptop model.
sound/pci/hda/cs35l56_hda.c | 110 +++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 28 deletions(-)
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 7baf3b506eefe..7823f71012a8a 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -876,6 +876,52 @@ static int cs35l56_hda_system_resume(struct device *dev) return 0; }
+static int cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr) +{ + /* The cirrus,dev-index property has the wrong values */ + switch (*bus_addr) { + case 0x30: + cs35l56->index = 1; + return 0; + case 0x31: + cs35l56->index = 0; + return 0; + default: + /* There is a pseudo-address for broadcast to both amps - ignore it */ + dev_dbg(cs35l56->base.dev, "Ignoring I2C address %#x\n", *bus_addr); + return 0; + } +} + +static const struct { + const char *sub; + int (*fixup_fn)(struct cs35l56_hda *cs35l56, int *bus_addr); +} cs35l56_hda_fixups[] = { + { + .sub = "17AA390B", /* Lenovo Yoga Book 9i GenX */ + .fixup_fn = cs35l56_hda_fixup_yoga9, + }, +}; + +static int cs35l56_hda_apply_platform_fixups(struct cs35l56_hda *cs35l56, const char *sub, + int *bus_addr) +{ + int i; + + if (IS_ERR(sub)) + return 0; + + for (i = 0; i < ARRAY_SIZE(cs35l56_hda_fixups); i++) { + if (strcasecmp(cs35l56_hda_fixups[i].sub, sub) == 0) { + dev_dbg(cs35l56->base.dev, "Applying fixup for %s\n", + cs35l56_hda_fixups[i].sub); + return (cs35l56_hda_fixups[i].fixup_fn)(cs35l56, bus_addr); + } + } + + return 0; +} + static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) { u32 values[HDA_MAX_COMPONENTS]; @@ -900,39 +946,47 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) ACPI_COMPANION_SET(cs35l56->base.dev, adev); }
- property = "cirrus,dev-index"; - ret = device_property_count_u32(cs35l56->base.dev, property); - if (ret <= 0) - goto err; - - if (ret > ARRAY_SIZE(values)) { - ret = -EINVAL; - goto err; - } - nval = ret; + /* Initialize things that could be overwritten by a fixup */ + cs35l56->index = -1;
- ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); + sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); + ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id); if (ret) - goto err; + return ret;
- cs35l56->index = -1; - for (i = 0; i < nval; i++) { - if (values[i] == id) { - cs35l56->index = i; - break; - } - } - /* - * It's not an error for the ID to be missing: for I2C there can be - * an alias address that is not a real device. So reject silently. - */ if (cs35l56->index == -1) { - dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); - ret = -ENODEV; - goto err; - } + property = "cirrus,dev-index"; + ret = device_property_count_u32(cs35l56->base.dev, property); + if (ret <= 0) + goto err;
- sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); + if (ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); + if (ret) + goto err; + + for (i = 0; i < nval; i++) { + if (values[i] == id) { + cs35l56->index = i; + break; + } + } + + /* + * It's not an error for the ID to be missing: for I2C there can be + * an alias address that is not a real device. So reject silently. + */ + if (cs35l56->index == -1) { + dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + }
if (IS_ERR(sub)) { dev_info(cs35l56->base.dev,