]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: hda/cs35l56: Workaround bad dev-index on Lenovo Yoga Book 9i GenX
authorRichard Fitzgerald <rf@opensource.cirrus.com>
Mon, 14 Jul 2025 11:01:54 +0000 (12:01 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 14 Jul 2025 12:09:51 +0000 (14:09 +0200)
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>
sound/pci/hda/cs35l56_hda.c

index 3f2fd32f4ad91633b3c4c13c0848d6618675c673..886c53184fec2365d54c8a1b6768f99d88c87174 100644 (file)
@@ -873,6 +873,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];
@@ -897,39 +943,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,