HP Spectre x360 Convertible devices expose invalid _ADR fields in the DSDT, which prevents codec drivers from probing. A possible solution is to override the DSDT, but that's just too painful for users. This patch suggests a simple DMI-based quirk to remap the existing invalid ADR information into valid ones. BugLink: https://github.com/thesofproject/linux/issues/2700 Co-developed-by: Bard Liao <yung-chuan.liao@xxxxxxxxxxxxxxx> Signed-off-by: Bard Liao <yung-chuan.liao@xxxxxxxxxxxxxxx> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> Reviewed-by: Kai Vehmanen <kai.vehmanen@xxxxxxxxxxxxxxx> Reviewed-by: Bard Liao <bard.liao@xxxxxxxxx> Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@xxxxxxxxx> --- drivers/soundwire/slave.c | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 180f38bd003b..abdc1ae94e9c 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -2,6 +2,7 @@ // Copyright(c) 2015-17 Intel Corporation. #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/of.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> @@ -91,10 +92,44 @@ int sdw_slave_add(struct sdw_bus *bus, #if IS_ENABLED(CONFIG_ACPI) +struct adr_remap { + u64 adr; + u64 remapped_adr; +}; + +/* + * HP Spectre 360 Convertible devices do not expose the correct _ADR + * in the DSDT. + * Remap the bad _ADR values to the ones reported by hardware + */ +static const struct adr_remap hp_spectre_360[] = { + { + 0x000010025D070100, + 0x000020025D071100 + }, + { + 0x000110025d070100, + 0x000120025D130800 + }, + {} +}; + +static const struct dmi_system_id adr_remap_quirk_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible"), + }, + .driver_data = (void *)hp_spectre_360, + }, + {} +}; + static bool find_slave(struct sdw_bus *bus, struct acpi_device *adev, struct sdw_slave_id *id) { + const struct dmi_system_id *dmi_id; unsigned long long addr; unsigned int link_id; acpi_status status; @@ -108,6 +143,21 @@ static bool find_slave(struct sdw_bus *bus, return false; } + /* check if any address remap quirk applies */ + dmi_id = dmi_first_match(adr_remap_quirk_table); + if (dmi_id) { + struct adr_remap *map = dmi_id->driver_data; + + for (map = dmi_id->driver_data; map->adr; map++) { + if (map->adr == addr) { + dev_dbg(bus->dev, "remapped _ADR 0x%llx as 0x%llx\n", + addr, map->remapped_adr); + addr = map->remapped_adr; + break; + } + } + } + /* Extract link id from ADR, Bit 51 to 48 (included) */ link_id = SDW_DISCO_LINK_ID(addr); -- 2.25.1