ALSA: hda/cs35l56: Workaround bad dev-index on Lenovo Yoga Book 9i GenX

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>
This commit is contained in:
Richard Fitzgerald 2025-07-14 12:01:54 +01:00 committed by Takashi Iwai
parent 4722727373
commit 40b1c2f9b2

View 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;
ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval);
if (ret)
goto err;
/* Initialize things that could be overwritten by a fixup */
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;
}
sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id);
if (ret)
return ret;
if (cs35l56->index == -1) {
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;
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,