diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 4d8f3b7024ae59..f66f869dff2e51 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -6,6 +6,7 @@ menuconfig SOUNDWIRE tristate "SoundWire support" depends on ACPI || OF + depends on SND_SOC_SDCA_OPTIONAL help SoundWire is a 2-Pin interface with data and clock line ratified by the MIPI Alliance. SoundWire is used for transporting data diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c index db040f43505994..53d1d707ca1a7b 100644 --- a/drivers/soundwire/amd_init.c +++ b/drivers/soundwire/amd_init.c @@ -177,7 +177,7 @@ EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT); void sdw_amd_exit(struct sdw_amd_ctx *ctx) { sdw_amd_cleanup(ctx); - kfree(ctx->ids); + kfree(ctx->peripherals); kfree(ctx); } EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT); @@ -204,10 +204,11 @@ int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) num_slaves++; } - ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); - if (!ctx->ids) + ctx->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves), + GFP_KERNEL); + if (!ctx->peripherals) return -ENOMEM; - ctx->num_slaves = num_slaves; + ctx->peripherals->num_peripherals = num_slaves; for (index = 0; index < ctx->count; index++) { if (!(ctx->link_mask & BIT(index))) continue; @@ -215,8 +216,7 @@ int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) if (amd_manager) { bus = &amd_manager->bus; list_for_each_entry(slave, &bus->slaves, node) { - ctx->ids[i].id = slave->id; - ctx->ids[i].link_id = bus->link_id; + ctx->peripherals->array[i] = slave; i++; } } diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index a09134b97cd666..12e7a98f319f8c 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -252,17 +252,16 @@ static struct sdw_intel_ctx num_slaves++; } - ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); - if (!ctx->ids) + ctx->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves), + GFP_KERNEL); + if (!ctx->peripherals) goto err; - - ctx->num_slaves = num_slaves; + ctx->peripherals->num_peripherals = num_slaves; i = 0; list_for_each_entry(link, &ctx->link_list, list) { bus = &link->cdns->bus; list_for_each_entry(slave, &bus->slaves, node) { - ctx->ids[i].id = slave->id; - ctx->ids[i].link_id = bus->link_id; + ctx->peripherals->array[i] = slave; i++; } } @@ -371,7 +370,7 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx) } sdw_intel_cleanup(ctx); - kfree(ctx->ids); + kfree(ctx->peripherals); kfree(ctx->ldev); kfree(ctx); } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f1a4df6cfebd9c..97cf8bcca04742 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "bus.h" #include "sysfs_local.h" @@ -70,6 +71,17 @@ int sdw_slave_add(struct sdw_bus *bus, list_add_tail(&slave->node, &bus->slaves); mutex_unlock(&bus->bus_lock); + /* + * The Soundwire driver probe may optionally register SDCA + * sub-devices, one per Function. This means the information + * on the SDCA revision and the number/type of Functions need + * to be extracted from platform firmware before the SoundWire + * driver probe, and as a consequence before the SoundWire + * device_register() below. + */ + sdca_lookup_interface_revision(slave); + sdca_lookup_functions(slave); + ret = device_register(&slave->dev); if (ret) { dev_err(bus->dev, "Failed to add slave: ret %d\n", ret); @@ -259,3 +271,5 @@ int sdw_of_find_slaves(struct sdw_bus *bus) return 0; } + +MODULE_IMPORT_NS(SND_SOC_SDCA); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index e059d5330b1193..f08855de52887f 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -10,6 +10,7 @@ #include #include #include +#include struct sdw_bus; struct sdw_slave; @@ -472,9 +473,9 @@ struct sdw_slave_id { __u8 sdw_version:4; }; -struct sdw_extended_slave_id { - int link_id; - struct sdw_slave_id id; +struct sdw_peripherals { + int num_peripherals; + struct sdw_slave *array[]; }; /* @@ -647,6 +648,7 @@ struct sdw_slave_ops { * @is_mockup_device: status flag used to squelch errors in the command/control * protocol for SoundWire mockup devices * @sdw_dev_lock: mutex used to protect callbacks/remove races + * @sdca_data: structure containing all device data for SDCA helpers */ struct sdw_slave { struct sdw_slave_id id; @@ -670,6 +672,7 @@ struct sdw_slave { bool first_interrupt_done; bool is_mockup_device; struct mutex sdw_dev_lock; /* protect callbacks/remove races */ + struct sdca_device_data sdca_data; }; #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index 28a4eb77717f98..585b4c58a8a6f2 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -115,19 +115,16 @@ struct sdw_amd_acpi_info { * struct sdw_amd_ctx - context allocated by the controller driver probe * * @count: link count - * @num_slaves: total number of devices exposed across all enabled links * @link_mask: bit-wise mask listing SoundWire links reported by the * Controller - * @ids: array of slave_id, representing Slaves exposed across all enabled - * links * @pdev: platform device structure + * @peripherals: array representing Peripherals exposed across all enabled links */ struct sdw_amd_ctx { int count; - int num_slaves; u32 link_mask; - struct sdw_extended_slave_id *ids; struct platform_device *pdev[AMD_SDW_MAX_MANAGER_COUNT]; + struct sdw_peripherals *peripherals; }; /** diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 734dc1fa3b5bc3..70e046ad5b0f55 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -286,31 +286,28 @@ struct hdac_bus; * hardware capabilities after all power dependencies are settled. * @link_mask: bit-wise mask listing SoundWire links reported by the * Controller - * @num_slaves: total number of devices exposed across all enabled links * @handle: ACPI parent handle * @ldev: information for each link (controller-specific and kept * opaque here) - * @ids: array of slave_id, representing Slaves exposed across all enabled - * links * @link_list: list to handle interrupts across all links * @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers. * @shim_mask: flags to track initialization of SHIM shared registers * @shim_base: sdw shim base. * @alh_base: sdw alh base. + * @peripherals: array representing Peripherals exposed across all enabled links */ struct sdw_intel_ctx { int count; void __iomem *mmio_base; u32 link_mask; - int num_slaves; acpi_handle handle; struct sdw_intel_link_dev **ldev; - struct sdw_extended_slave_id *ids; struct list_head link_list; struct mutex shim_lock; /* lock for access to shared SHIM registers */ u32 shim_mask; u32 shim_base; u32 alh_base; + struct sdw_peripherals *peripherals; }; /** diff --git a/include/sound/sdca.h b/include/sound/sdca.h new file mode 100644 index 00000000000000..0fe72010411b28 --- /dev/null +++ b/include/sound/sdca.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright(c) 2024 Intel Corporation + */ + +#ifndef __SDCA_H__ +#define __SDCA_H__ + +struct sdw_slave; + +#define SDCA_MAX_INTERRUPTS 31 /* the last bit is reserved for future extensions */ + +/** + * struct sdca_interrupt_source - interface between interrupt source and + * SoundWire SDCA interrupt handler + * + * @index: SDCA interrupt number in [0, SDCA_MAX_INTERRUPTS - 1] + * @context: source-specific information, used by @callback + * @callback: provided by interrupt source for source-specific handling. + */ +struct sdca_interrupt_source { + int index; + void *context; + void (*callback)(void *context); +}; + +/** + * struct sdca_interrupt_info - Peripheral device-level information + * used for interrupt handler + * + * @sources: array of pointers, addressed with an interrupt index + * matching @registered_source_mask bits. + * @irqs_lock: mutex protecting concurrent access to @sources, + * @registered_source_mask and reventing SDCA interrupts from being disabled + * on suspend while being handled. + * @enabled_interrupt_mask: mask indicating which interrupts from @registered_source_mask + * are currently enabled. + * @detected_interrupt_mask: bitfields set in interrupt handler, and accessible + * in deferred processing. + * @supported_hw_register_mask: Up to 4 registers may be implemented + */ +struct sdca_interrupt_info { + struct sdca_interrupt_source *sources[SDCA_MAX_INTERRUPTS]; + struct mutex irqs_lock; /* protects SDCA interrupts */ + u32 registered_source_mask; + u32 enabled_interrupt_mask; + u32 detected_interrupt_mask; + int supported_hw_register_mask; +}; + +#define SDCA_MAX_FUNCTION_COUNT 8 + +/** + * sdca_device_desc - short descriptor for an SDCA Function + * @adr: ACPI address (used for SDCA register access) + * @type: Function topology type + * @name: human-readable string + */ +struct sdca_function_desc { + u64 adr; + u32 type; + const char *name; +}; + +/** + * sdca_device_data - structure containing all SDCA related information + * @sdca_interface_revision: value read from _DSD property, mainly to check + * for changes between silicon versions + * @num_functions: total number of supported SDCA functions. Invalid/unsupported + * functions will be skipped. + * @sdca_func: array of function descriptors + * @interrupt_info: device-level interrupt configuration/handling + */ +struct sdca_device_data { + u32 interface_revision; + int num_functions; + struct sdca_function_desc sdca_func[SDCA_MAX_FUNCTION_COUNT]; + struct sdca_interrupt_info *interrupt_info; +}; + +enum sdca_quirk { + SDCA_QUIRKS_RT712_VB, +}; + +#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA) + +void sdca_lookup_functions(struct sdw_slave *slave); +void sdca_lookup_interface_revision(struct sdw_slave *slave); +bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk); + +#else + +static inline void sdca_lookup_functions(struct sdw_slave *slave) {} +static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {} +static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk) +{ + return false; +} + +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SDCA_IRQ_HANDLER) + +int sdca_interrupt_info_alloc(struct sdw_slave *slave); +void sdca_interrupt_info_release(struct sdw_slave *slave); +int sdca_interrupt_info_reset(struct sdw_slave *slave); +int sdca_interrupt_initialize(struct sdw_slave *slave, + int supported_hw_register_mask); +int sdca_interrupt_register_source(struct sdw_slave *slave, + struct sdca_interrupt_source *source); +int sdca_interrupt_enable(struct sdw_slave *slave, + u32 source_mask, + bool enable); +void sdca_interrupt_clear_history(struct sdw_slave *slave, u32 preserve_mask); +int sdca_interrupt_handler(struct sdw_slave *slave); + +#else + +static inline int sdca_interrupt_info_alloc(struct sdw_slave *slave) +{ + return 0; +} + +static inline void sdca_interrupt_info_release(struct sdw_slave *slave) {} + +static inline int sdca_interrupt_info_reset(struct sdw_slave *slave) +{ + return 0; +} + +static inline int sdca_interrupt_initialize(struct sdw_slave *slave, + int supported_hw_register_mask) +{ + return 0; +} + +static inline int sdca_interrupt_register_source(struct sdw_slave *slave, + struct sdca_interrupt_source *source) +{ + return 0; +} + +static inline int sdca_interrupt_enable(struct sdw_slave *slave, + u32 source_mask, + bool enable) +{ + return 0; +} + +static inline void sdca_interrupt_clear_history(struct sdw_slave *slave, u32 preserve_mask) {} + +static inline int sdca_interrupt_handler(struct sdw_slave *slave) +{ + return 0; +} + +#endif /* IS_ENABLED(CONFIG_SND_SOC_SDCA_IRQ_HANDLER) */ + +#endif diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h new file mode 100644 index 00000000000000..a01eec86b9a675 --- /dev/null +++ b/include/sound/sdca_function.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright(c) 2024 Intel Corporation + */ + +#ifndef __SDCA_FUNCTION_H__ +#define __SDCA_FUNCTION_H__ + +/* + * SDCA Function Types from SDCA specification v1.0a Section 5.1.2 + * all Function types not described are reserved + * Note that SIMPLE_AMP, SIMPLE_MIC and SIMPLE_JACK Function Types + * are NOT defined in SDCA 1.0a, but they were defined in earlier + * drafts and are planned for 1.1. + */ + +enum sdca_function_type { + SDCA_FUNCTION_TYPE_SMART_AMP = 0x01, /* Amplifier with protection features */ + SDCA_FUNCTION_TYPE_SIMPLE_AMP = 0x02, /* subset of SmartAmp */ + SDCA_FUNCTION_TYPE_SMART_MIC = 0x03, /* Smart microphone with acoustic triggers */ + SDCA_FUNCTION_TYPE_SIMPLE_MIC = 0x04, /* subset of SmartMic */ + SDCA_FUNCTION_TYPE_SPEAKER_MIC = 0x05, /* Combination of SmartMic and SmartAmp */ + SDCA_FUNCTION_TYPE_UAJ = 0x06, /* 3.5mm Universal Audio jack */ + SDCA_FUNCTION_TYPE_RJ = 0x07, /* Retaskable jack */ + SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08, /* Subset of UAJ */ + SDCA_FUNCTION_TYPE_HID = 0x0A, /* Human Interface Device, for e.g. buttons */ + SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F, /* Implementation-defined function */ +}; + +/* Human-readable names used for kernel logs and Function device registration/bind */ +#define SDCA_FUNCTION_TYPE_SMART_AMP_NAME "SmartAmp" +#define SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME "SimpleAmp" +#define SDCA_FUNCTION_TYPE_SMART_MIC_NAME "SmartMic" +#define SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME "SimpleMic" +#define SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME "SpeakerMic" +#define SDCA_FUNCTION_TYPE_UAJ_NAME "UAJ" +#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ" +#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack" +#define SDCA_FUNCTION_TYPE_HID_NAME "HID" + +enum sdca_entity0_controls { + SDCA_CONTROL_ENTITY_0_COMMIT_GROUP_MASK = 0x01, + SDCA_CONTROL_ENTITY_0_INTSTAT_CLEAR = 0x02, + SDCA_CONTROL_ENTITY_0_INT_ENABLE = 0x03, + SDCA_CONTROL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04, + SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY = 0x05, + SDCA_CONTROL_ENTITY_0_FUNCTION_MANUFACTURER_ID = 0x06, + SDCA_CONTROL_ENTITY_0_FUNCTION_ID = 0x07, + SDCA_CONTROL_ENTITY_0_FUNCTION_VERSION = 0x08 +}; + +#endif diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 60d3b86a4660ff..72e371a217676b 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -185,6 +185,10 @@ struct snd_soc_acpi_link_adr { * ACPI ID alone is not sufficient, wrong or misleading * @quirk_data: data used to uniquely identify a machine, usually a list of * audio codecs whose presence if checked with ACPI + * @machine_check: pointer to quirk function. The functionality is similar to + * the use of @machine_quirk, except that the return value is a boolean: the intent + * is to skip a machine if the additional hardware/firmware verification invalidates + * the initial selection in the snd_soc_acpi_mach table. * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled @@ -203,6 +207,7 @@ struct snd_soc_acpi_mach { const char *board; struct snd_soc_acpi_mach * (*machine_quirk)(void *arg); const void *quirk_data; + bool (*machine_check)(void *arg); void *pdata; struct snd_soc_acpi_mach_params mach_params; const char *sof_tplg_filename; @@ -233,7 +238,6 @@ static inline bool snd_soc_acpi_sof_parent(struct device *dev) bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, const struct snd_soc_acpi_link_adr *link, - struct sdw_extended_slave_id *ids, - int num_slaves); + struct sdw_peripherals *peripherals); #endif diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e87bd15a8b4393..8e01b421fe8d52 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -108,6 +108,7 @@ source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" +source "sound/soc/sdca/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 775bb38c2ed447..5307b0b62a932a 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += qcom/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ +obj-$(CONFIG_SND_SOC) += sdca/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c72d666d51bdf4..4365499c8f82a4 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -302,8 +302,7 @@ static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) link = mach->links; for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { if (!snd_soc_acpi_sdw_link_slaves_found(dev, link, - acp_data->sdw->ids, - acp_data->sdw->num_slaves)) + acp_data->sdw->peripherals)) break; } if (i == acp_data->info.count || !link->num_adr) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7092842480ef17..d650892a6fba77 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1728,6 +1728,7 @@ config SND_SOC_RT711_SDW config SND_SOC_RT711_SDCA_SDW tristate "Realtek RT711 SDCA Codec - SDW" depends on SOUNDWIRE + select SND_SOC_SDCA_IRQ_HANDLER select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index f5933d2e085e95..712a0a4ada0b95 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -146,21 +146,6 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; - if (status == SDW_SLAVE_ATTACHED) { - if (rt711->hs_jack) { - /* - * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then - * if the device attached again, we will need to set the setting back. - * It could avoid losing the jack detection interrupt. - * This also could sync with the cache value as the rt711_sdca_jack_init set. - */ - sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8); - } - } - /* * Perform initialization only if slave status is present and * hw_init flag is false @@ -238,9 +223,7 @@ static int rt711_sdca_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status) { struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); - int ret, stat; - int count = 0, retry = 3; - unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + int ret; dev_dbg(&slave->dev, "%s control_port_stat=%x, sdca_cascade=%x", __func__, @@ -248,90 +231,24 @@ static int rt711_sdca_interrupt_callback(struct sdw_slave *slave, if (cancel_delayed_work_sync(&rt711->jack_detect_work)) { dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); - /* avoid the HID owner doesn't change to device */ - if (rt711->scp_sdca_stat2) - scp_sdca_stat2 = rt711->scp_sdca_stat2; + /* + * Preserve status for HID, to make sure the UMP interrupt is handled + * and the ownership can be changed in the workqueue back to DEVICE + */ + sdca_interrupt_clear_history(slave, BIT(8)); + } else { + /* clear all history */ + sdca_interrupt_clear_history(slave, 0); } - /* - * The critical section below intentionally protects a rather large piece of code. - * We don't want to allow the system suspend to disable an interrupt while we are - * processing it, which could be problematic given the quirky SoundWire interrupt - * scheme. We do want however to prevent new workqueues from being scheduled if - * the disable_irq flag was set during system suspend. - */ - mutex_lock(&rt711->disable_irq_lock); - - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - rt711->scp_sdca_stat1 = ret; - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); + ret = sdca_interrupt_handler(slave); if (ret < 0) - goto io_error; - rt711->scp_sdca_stat2 = ret; - if (scp_sdca_stat2) - rt711->scp_sdca_stat2 |= scp_sdca_stat2; - - do { - /* clear flag */ - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { - ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT1, - SDW_SCP_SDCA_INTMASK_SDCA_0); - if (ret < 0) - goto io_error; - } - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); - if (ret < 0) - goto io_error; - if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { - ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT2, - SDW_SCP_SDCA_INTMASK_SDCA_8); - if (ret < 0) - goto io_error; - } - - /* check if flag clear or not */ - ret = sdw_read_no_pm(rt711->slave, SDW_DP0_INT); - if (ret < 0) - goto io_error; - sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; - - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; - - ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); - if (ret < 0) - goto io_error; - scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; - - stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + dev_err(&slave->dev, + "%s: error %d in sdca_interrupt_handler\n", + __func__, ret); - count++; - } while (stat != 0 && count < retry); - - if (stat) - dev_warn(&slave->dev, - "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, - rt711->scp_sdca_stat1, rt711->scp_sdca_stat2); - - if (status->sdca_cascade && !rt711->disable_irq) - mod_delayed_work(system_power_efficient_wq, - &rt711->jack_detect_work, msecs_to_jiffies(30)); - - mutex_unlock(&rt711->disable_irq_lock); - - return 0; - -io_error: - mutex_unlock(&rt711->disable_irq_lock); - pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); return ret; + } static const struct sdw_slave_ops rt711_sdca_slave_ops = { @@ -369,7 +286,8 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave) pm_runtime_disable(&slave->dev); mutex_destroy(&rt711->calibrate_mutex); - mutex_destroy(&rt711->disable_irq_lock); + + sdca_interrupt_info_release(slave); return 0; } @@ -400,28 +318,17 @@ static int __maybe_unused rt711_sdca_dev_system_suspend(struct device *dev) { struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); - int ret1, ret2; + int ret; if (!rt711_sdca->hw_init) return 0; - /* - * prevent new interrupts from being handled after the - * deferred work completes and before the parent disables - * interrupts on the link - */ - mutex_lock(&rt711_sdca->disable_irq_lock); - rt711_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt711_sdca->disable_irq_lock); - - if (ret1 < 0 || ret2 < 0) { + ret = sdca_interrupt_enable(rt711_sdca->slave, + BIT(8) | BIT(0), + false); + if (ret < 0) /* log but don't prevent suspend from happening */ - dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); - } + dev_dbg(&slave->dev, "could not disable SDCA interrupts\n:"); return rt711_sdca_dev_suspend(dev); } @@ -433,18 +340,18 @@ static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); unsigned long time; + int ret; if (!rt711->first_hw_init) return 0; if (!slave->unattach_request) { - mutex_lock(&rt711->disable_irq_lock); - if (rt711->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt711->disable_irq = false; - } - mutex_unlock(&rt711->disable_irq_lock); + ret = sdca_interrupt_enable(rt711->slave, + BIT(8) | BIT(0), + true); + if (ret < 0) + /* log but don't prevent resume from happening */ + dev_dbg(&slave->dev, "could not enable SDCA interrupts\n:"); goto regmap_sync; } @@ -486,3 +393,4 @@ module_sdw_driver(rt711_sdca_sdw_driver); MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver"); MODULE_AUTHOR("Shuming Fan "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SDCA_IRQ_HANDLER); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index dd6ccf17afd439..c6fb510b891994 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "rt711-sdca.h" @@ -310,8 +311,13 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work) { struct rt711_sdca_priv *rt711 = container_of(work, struct rt711_sdca_priv, jack_detect_work.work); + struct sdca_interrupt_info *interrupt_info; int btn_type = 0, ret; + interrupt_info = rt711->slave->sdca_data.interrupt_info; + if (!interrupt_info) + return; + if (!rt711->hs_jack) return; @@ -319,14 +325,14 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work) return; /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ - if (rt711->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + if (interrupt_info->detected_interrupt_mask & BIT(0)) { ret = rt711_sdca_headset_detect(rt711); if (ret < 0) return; } /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ - if (rt711->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + if (interrupt_info->detected_interrupt_mask & BIT(8)) btn_type = rt711_sdca_button_detect(rt711); if (rt711->jack_type == 0) @@ -337,8 +343,7 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work) dev_dbg(&rt711->slave->dev, "in %s, btn_type=0x%x\n", __func__, btn_type); dev_dbg(&rt711->slave->dev, - "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, - rt711->scp_sdca_stat1, rt711->scp_sdca_stat2); + "in %s, detected_interrupt_mask=0x%x\n", __func__, interrupt_info->detected_interrupt_mask); snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, SND_JACK_HEADSET | @@ -450,6 +455,8 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) { + int ret; + mutex_lock(&rt711->calibrate_mutex); if (rt711->hs_jack) { @@ -503,11 +510,15 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) break; } - /* set SCP_SDCA_IntMask1[0]=1 */ - sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - /* set SCP_SDCA_IntMask2[0]=1 */ - sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); + ret = sdca_interrupt_enable(rt711->slave, + BIT(0) | BIT(8), + true); + if (ret < 0) + dev_err(&rt711->slave->dev, + "%s: sdca_interrupt_enable failed: %d\n", + __func__, ret); + else + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); } else { /* disable HID 1/2 event */ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, @@ -1470,9 +1481,37 @@ static struct snd_soc_dai_driver rt711_sdca_dai[] = { } }; +/* this is called with the irqs_lock mutex held */ +static void jack_detection_callback(void *context) +{ + struct rt711_sdca_priv *rt711 = context; + + /* + * Invoke the delayed work unconditionally, the test on + * the SDCA_0 being enabled was done in the common handler + */ + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(30)); +} + +/* this is called with the irqs_lock mutex held */ +static void button_detection_callback(void *context) +{ + struct rt711_sdca_priv *rt711 = context; + + /* + * Invoke the delayed work unconditionally, the test on + * the SDCA_8 being enabled was done in the common handler + */ + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(30)); +} + int rt711_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave) { + struct sdca_interrupt_source *sdca_int_0; + struct sdca_interrupt_source *sdca_int_8; struct rt711_sdca_priv *rt711; int ret; @@ -1480,6 +1519,41 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, if (!rt711) return -ENOMEM; + ret = sdca_interrupt_info_alloc(slave); + if (ret < 0) + return ret; + + /* SDCA_INT1 and SDCA_INT2 are supported */ + ret = sdca_interrupt_initialize(slave, GENMASK(1, 0)); + if (ret < 0) + return ret; + + /* alloc and configure SDCA interrupt sources */ + sdca_int_0 = devm_kzalloc(dev, sizeof(*sdca_int_0), GFP_KERNEL); + if (!sdca_int_0) + return -ENOMEM; + + sdca_int_8 = devm_kzalloc(dev, sizeof(*sdca_int_8), GFP_KERNEL); + if (!sdca_int_8) + return -ENOMEM; + + sdca_int_0->index = 0; + sdca_int_0->context = rt711; + sdca_int_0->callback = jack_detection_callback; + + sdca_int_8->index = 8; + sdca_int_8->context = rt711; + sdca_int_8->callback = button_detection_callback; + + /* now register sources */ + ret = sdca_interrupt_register_source(slave, sdca_int_0); + if (ret < 0) + return ret; + + ret = sdca_interrupt_register_source(slave, sdca_int_8); + if (ret < 0) + return ret; + dev_set_drvdata(dev, rt711); rt711->slave = slave; rt711->regmap = regmap; @@ -1489,7 +1563,6 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, regcache_cache_only(rt711->mbq_regmap, true); mutex_init(&rt711->calibrate_mutex); - mutex_init(&rt711->disable_irq_lock); INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_sdca_jack_detect_handler); INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_sdca_btn_check_handler); @@ -1595,10 +1668,9 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) int ret = 0; unsigned int val; - rt711->disable_irq = false; - - if (rt711->hw_init) - return 0; + ret = sdca_interrupt_info_reset(slave); + if (ret < 0) + return ret; regcache_cache_only(rt711->regmap, false); regcache_cache_only(rt711->mbq_regmap, false); diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 15263dcb0314ac..91ea0d61ce7aaa 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -26,10 +26,7 @@ struct rt711_sdca_priv { struct delayed_work jack_detect_work; struct delayed_work jack_btn_check_work; struct mutex calibrate_mutex; /* for headset calibration */ - struct mutex disable_irq_lock; /* SDCA irq lock protection */ - bool disable_irq; int jack_type, jd_src; - unsigned int scp_sdca_stat1, scp_sdca_stat2; int hw_ver; bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute; bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 90d5aaddbd5b94..40802e24372b44 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -161,21 +161,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; - if (status == SDW_SLAVE_ATTACHED) { - if (rt712->hs_jack) { - /* - * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then - * if the device attached again, we will need to set the setting back. - * It could avoid losing the jack detection interrupt. - * This also could sync with the cache value as the rt712_sdca_jack_init set. - */ - sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8); - } - } - /* * Perform initialization only if slave status is present and * hw_init flag is false @@ -255,9 +240,7 @@ static int rt712_sdca_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); - int ret, stat; - int count = 0, retry = 3; - unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + int ret; dev_dbg(&slave->dev, "%s control_port_stat=%x, sdca_cascade=%x", __func__, @@ -265,89 +248,22 @@ static int rt712_sdca_interrupt_callback(struct sdw_slave *slave, if (cancel_delayed_work_sync(&rt712->jack_detect_work)) { dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); - /* avoid the HID owner doesn't change to device */ - if (rt712->scp_sdca_stat2) - scp_sdca_stat2 = rt712->scp_sdca_stat2; + /* + * Preserve status for HID, to make sure the UMP interrupt is handled + * and the ownership can be changed in the workqueue back to DEVICE + */ + sdca_interrupt_clear_history(slave, BIT(8)); + } else { + /* clear all history */ + sdca_interrupt_clear_history(slave, 0); } - /* - * The critical section below intentionally protects a rather large piece of code. - * We don't want to allow the system suspend to disable an interrupt while we are - * processing it, which could be problematic given the quirky SoundWire interrupt - * scheme. We do want however to prevent new workqueues from being scheduled if - * the disable_irq flag was set during system suspend. - */ - mutex_lock(&rt712->disable_irq_lock); - - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - rt712->scp_sdca_stat1 = ret; - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + ret = sdca_interrupt_handler(slave); if (ret < 0) - goto io_error; - rt712->scp_sdca_stat2 = ret; - if (scp_sdca_stat2) - rt712->scp_sdca_stat2 |= scp_sdca_stat2; - - do { - /* clear flag */ - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { - ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1, - SDW_SCP_SDCA_INTMASK_SDCA_0); - if (ret < 0) - goto io_error; - } - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); - if (ret < 0) - goto io_error; - if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { - ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2, - SDW_SCP_SDCA_INTMASK_SDCA_8); - if (ret < 0) - goto io_error; - } - - /* check if flag clear or not */ - ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT); - if (ret < 0) - goto io_error; - sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; - - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); - if (ret < 0) - goto io_error; - scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; - - ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); - if (ret < 0) - goto io_error; - scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + dev_err(&slave->dev, + "%s: error %d in sdca_interrupt_handler\n", + __func__, ret); - stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; - - count++; - } while (stat != 0 && count < retry); - - if (stat) - dev_warn(&slave->dev, - "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, - rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); - - if (status->sdca_cascade && !rt712->disable_irq) - mod_delayed_work(system_power_efficient_wq, - &rt712->jack_detect_work, msecs_to_jiffies(30)); - - mutex_unlock(&rt712->disable_irq_lock); - - return 0; - -io_error: - mutex_unlock(&rt712->disable_irq_lock); - pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); return ret; } @@ -386,7 +302,8 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave) pm_runtime_disable(&slave->dev); mutex_destroy(&rt712->calibrate_mutex); - mutex_destroy(&rt712->disable_irq_lock); + + sdca_interrupt_info_release(slave); return 0; } @@ -420,28 +337,17 @@ static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev) { struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); - int ret1, ret2; + int ret; if (!rt712_sdca->hw_init) return 0; - /* - * prevent new interrupts from being handled after the - * deferred work completes and before the parent disables - * interrupts on the link - */ - mutex_lock(&rt712_sdca->disable_irq_lock); - rt712_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt712_sdca->disable_irq_lock); - - if (ret1 < 0 || ret2 < 0) { + ret = sdca_interrupt_enable(rt712_sdca->slave, + BIT(8) | BIT(0), + false); + if (ret < 0) /* log but don't prevent suspend from happening */ - dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); - } + dev_dbg(&slave->dev, "could not disable SDCA interrupts\n:"); return rt712_sdca_dev_suspend(dev); } @@ -453,19 +359,18 @@ static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); unsigned long time; + int ret; if (!rt712->first_hw_init) return 0; if (!slave->unattach_request) { - mutex_lock(&rt712->disable_irq_lock); - if (rt712->disable_irq == true) { - - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt712->disable_irq = false; - } - mutex_unlock(&rt712->disable_irq_lock); + ret = sdca_interrupt_enable(rt712->slave, + BIT(8) | BIT(0), + true); + if (ret < 0) + /* log but don't prevent resume from happening */ + dev_dbg(&slave->dev, "could not enable SDCA interrupts\n:"); goto regmap_sync; } @@ -507,3 +412,5 @@ module_sdw_driver(rt712_sdca_sdw_driver); MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver"); MODULE_AUTHOR("Shuming Fan "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SDCA); +MODULE_IMPORT_NS(SND_SOC_SDCA_IRQ_HANDLER); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index e210c574bb74a1..d0c266275841f6 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -261,8 +262,13 @@ static void rt712_sdca_jack_detect_handler(struct work_struct *work) { struct rt712_sdca_priv *rt712 = container_of(work, struct rt712_sdca_priv, jack_detect_work.work); + struct sdca_interrupt_info *interrupt_info; int btn_type = 0, ret; + interrupt_info = rt712->slave->sdca_data.interrupt_info; + if (!interrupt_info) + return; + if (!rt712->hs_jack) return; @@ -270,14 +276,14 @@ static void rt712_sdca_jack_detect_handler(struct work_struct *work) return; /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ - if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + if (interrupt_info->detected_interrupt_mask & BIT(0)) { ret = rt712_sdca_headset_detect(rt712); if (ret < 0) return; } /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ - if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + if (interrupt_info->detected_interrupt_mask & BIT(8)) btn_type = rt712_sdca_button_detect(rt712); if (rt712->jack_type == 0) @@ -288,8 +294,7 @@ static void rt712_sdca_jack_detect_handler(struct work_struct *work) dev_dbg(&rt712->slave->dev, "in %s, btn_type=0x%x\n", __func__, btn_type); dev_dbg(&rt712->slave->dev, - "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, - rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); + "in %s, detected_interrupt_mask=0x%x\n", __func__, interrupt_info->detected_interrupt_mask); snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type, SND_JACK_HEADSET | @@ -402,6 +407,8 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) { + int ret; + mutex_lock(&rt712->calibrate_mutex); if (rt712->hs_jack) { @@ -431,11 +438,15 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) break; } - /* set SCP_SDCA_IntMask1[0]=1 */ - sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - /* set SCP_SDCA_IntMask2[0]=1 */ - sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__); + ret = sdca_interrupt_enable(rt712->slave, + BIT(0) | BIT(8), + true); + if (ret < 0) + dev_err(&rt712->slave->dev, + "%s: sdca_interrupt_enable failed: %d\n", + __func__, ret); + else + dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__); /* trigger GE interrupt */ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, @@ -1589,6 +1600,32 @@ static struct snd_soc_dai_driver rt712_sdca_dai[] = { } }; +/* this is called with the irqs_lock mutex held */ +static void jack_detection_callback(void *context) +{ + struct rt712_sdca_priv *rt712 = context; + + /* + * Invoke the delayed work unconditionally, the test on + * the SDCA_0 being enabled was done in the common handler + */ + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_detect_work, msecs_to_jiffies(30)); +} + +/* this is called with the irqs_lock mutex held */ +static void button_detection_callback(void *context) +{ + struct rt712_sdca_priv *rt712 = context; + + /* + * Invoke the delayed work unconditionally, the test on + * the SDCA_8 being enabled was done in the common handler + */ + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_detect_work, msecs_to_jiffies(30)); +} + static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = { { .name = "rt712-sdca-aif3", @@ -1607,6 +1644,8 @@ static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = { int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave) { + struct sdca_interrupt_source *sdca_int_0; + struct sdca_interrupt_source *sdca_int_8; struct rt712_sdca_priv *rt712; int ret; @@ -1614,6 +1653,41 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, if (!rt712) return -ENOMEM; + ret = sdca_interrupt_info_alloc(slave); + if (ret < 0) + return ret; + + /* SDCA_INT1 and SDCA_INT2 are supported */ + ret = sdca_interrupt_initialize(slave, GENMASK(1, 0)); + if (ret < 0) + return ret; + + /* alloc and configure SDCA interrupt sources */ + sdca_int_0 = devm_kzalloc(dev, sizeof(*sdca_int_0), GFP_KERNEL); + if (!sdca_int_0) + return -ENOMEM; + + sdca_int_8 = devm_kzalloc(dev, sizeof(*sdca_int_8), GFP_KERNEL); + if (!sdca_int_8) + return -ENOMEM; + + sdca_int_0->index = 0; + sdca_int_0->context = rt712; + sdca_int_0->callback = jack_detection_callback; + + sdca_int_8->index = 8; + sdca_int_8->context = rt712; + sdca_int_8->callback = button_detection_callback; + + /* now register sources */ + ret = sdca_interrupt_register_source(slave, sdca_int_0); + if (ret < 0) + return ret; + + ret = sdca_interrupt_register_source(slave, sdca_int_8); + if (ret < 0) + return ret; + dev_set_drvdata(dev, rt712); rt712->slave = slave; rt712->regmap = regmap; @@ -1623,7 +1697,6 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, regcache_cache_only(rt712->mbq_regmap, true); mutex_init(&rt712->calibrate_mutex); - mutex_init(&rt712->disable_irq_lock); INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler); INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler); @@ -1652,6 +1725,17 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, if (ret < 0) return ret; + /* only add the dmic component if a SMART_MIC function is exposed in ACPI */ + if (sdca_device_quirk_match(slave, SDCA_QUIRKS_RT712_VB)) { + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712_dmic, + rt712_sdca_dmic_dai, + ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; + rt712->dmic_function_found = true; + } + /* set autosuspend parameters */ pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); @@ -1799,14 +1883,13 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); - int ret = 0; unsigned int val; struct sdw_slave_prop *prop = &slave->prop; + int ret; - rt712->disable_irq = false; - - if (rt712->hw_init) - return 0; + ret = sdca_interrupt_info_reset(slave); + if (ret < 0) + return ret; regcache_cache_only(rt712->regmap, false); regcache_cache_only(rt712->mbq_regmap, false); @@ -1829,15 +1912,22 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) rt712->version_id = (val & 0x0f00) >> 8; dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id); - if (rt712->version_id == RT712_VA) + if (rt712->version_id == RT712_VA) { + if (rt712->dmic_function_found) { + dev_err(&slave->dev, "%s RT712 VA detected but SMART_MIC function exposed in ACPI\n", + __func__); + goto suspend; + } + rt712_sdca_va_io_init(rt712); - else { - /* multilanes and DMIC are supported by rt712vb */ - ret = devm_snd_soc_register_component(dev, - &soc_sdca_dev_rt712_dmic, rt712_sdca_dmic_dai, ARRAY_SIZE(rt712_sdca_dmic_dai)); - if (ret < 0) - return ret; + } else { + if (!rt712->dmic_function_found) { + dev_err(&slave->dev, "%s RT712 VB detected but no SMART_MIC function exposed in ACPI\n", + __func__); + goto suspend; + } + /* multilanes and DMIC are supported by rt712vb */ prop->lane_control_support = true; rt712_sdca_vb_io_init(rt712); } @@ -1862,10 +1952,12 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt712->hw_init = true; + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + +suspend: pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); - dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); return 0; } diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index 2169f2f726b9fb..82786f4240641f 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -28,14 +28,11 @@ struct rt712_sdca_priv { struct delayed_work jack_detect_work; struct delayed_work jack_btn_check_work; struct mutex calibrate_mutex; /* for headset calibration */ - struct mutex disable_irq_lock; /* SDCA irq lock protection */ - bool disable_irq; int jack_type; int jd_src; - unsigned int scp_sdca_stat1; - unsigned int scp_sdca_stat2; unsigned int hw_id; unsigned int version_id; + bool dmic_function_found; bool fu0f_dapm_mute; bool fu0f_mixer_l_mute; bool fu0f_mixer_r_mute; diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a32fb0a8d7d7cc..453a515ea40ffe 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -74,9 +74,14 @@ if SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL config SND_SOC_ACPI_INTEL_MATCH tristate select SND_SOC_ACPI if ACPI + select SND_SOC_ACPI_INTEL_SDCA_QUIRKS # this option controls the compilation of ACPI matching tables and # helpers and is not meant to be selected by the user. +config SND_SOC_ACPI_INTEL_SDCA_QUIRKS + tristate + imply SND_SOC_SDCA + endif ## SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL config SND_SOC_INTEL_KEEMBAY diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 91e146e2487da2..1698c63f58a0ce 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -18,5 +18,8 @@ snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-matc snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o +snd-soc-acpi-intel-sdca-quirks-y += soc-acpi-intel-sdca-quirks.o + obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o +obj-$(CONFIG_SND_SOC_ACPI_INTEL_SDCA_QUIRKS) += snd-soc-acpi-intel-sdca-quirks.o diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index d4435a34a3a3f4..6af653e1fb5015 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -9,7 +9,10 @@ #include #include #include +#include +#include #include "soc-acpi-intel-sdw-mockup-match.h" +#include "soc-acpi-intel-sdca-quirks.h" static const struct snd_soc_acpi_codecs mtl_rt5682_rt5682s_hp = { .num_codecs = 2, @@ -126,6 +129,27 @@ static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint rt712_vb_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + /* * RT722 is a multi-function codec, three endpoints are created for * its headset, amp and dmic functions. @@ -183,6 +207,15 @@ static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_vb_0_single_adr[] = { + { + .adr = 0x000030025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_vb_endpoints), + .endpoints = rt712_vb_endpoints, + .name_prefix = "rt712" + } +}; + static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { { .adr = 0x000330025D171201ull, @@ -356,6 +389,15 @@ static const struct snd_soc_acpi_link_adr mtl_712_l0[] = { {} }; +static const struct snd_soc_acpi_link_adr mtl_712_vb_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt712_vb_0_single_adr), + .adr_d = rt712_vb_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { { /* Jack Playback Endpoint */ .num = 0, @@ -767,6 +809,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", }, + { + .link_mask = BIT(0), + .links = mtl_712_vb_l0, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg", + }, { .link_mask = BIT(0), .links = mtl_712_l0, @@ -836,3 +885,5 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_sdw_machines); + +MODULE_IMPORT_NS(SND_SOC_ACPI_INTEL_SDCA_QUIRKS); diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c new file mode 100644 index 00000000000000..1f528fd910cfd1 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * soc-acpi-intel-sdca-quirks.c - tables and support for SDCA quirks + * + * Copyright (c) 2024, Intel Corporation. + * + */ + +#include +#include +#include +#include "soc-acpi-intel-sdca-quirks.h" + +/* + * Pretend machine quirk. The argument type is not the traditional + * 'struct snd_soc_acpi_mach' pointer but instead the sdw_intel_ctx + * which contains the peripheral information required for the + * SoundWire/SDCA filter on the SMART_MIC setup and interface + * revision. When the return value is false, the entry in the + * 'snd_soc_acpi_mach' table needs to be skipped. + */ +bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg) +{ + struct sdw_intel_ctx *ctx = arg; + int i; + + if (!ctx) + return false; + + for (i = 0; i < ctx->peripherals->num_peripherals; i++) { + if (sdca_device_quirk_match(ctx->peripherals->array[i], + SDCA_QUIRKS_RT712_VB)) + return true; + } + + return false; +} +EXPORT_SYMBOL_NS(snd_soc_acpi_intel_sdca_is_device_rt712_vb, SND_SOC_ACPI_INTEL_SDCA_QUIRKS); + +MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SDCA); diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h new file mode 100644 index 00000000000000..bead5ec6243f9b --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * soc-acpi-intel-sdca-quirks.h - tables and support for SDCA quirks + * + * Copyright (c) 2024, Intel Corporation. + * + */ + +#ifndef _SND_SOC_ACPI_INTEL_SDCA_QUIRKS +#define _SND_SOC_ACPI_INTEL_SDCA_QUIRKS + +bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); + +#endif diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig new file mode 100644 index 00000000000000..153628a2970b89 --- /dev/null +++ b/sound/soc/sdca/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_SOC_SDCA + tristate "ASoC SDCA library" + depends on ACPI + help + This option enables support for the MIPI SoundWire Device + Class for Audio (SDCA). + +config SND_SOC_SDCA_OPTIONAL + def_tristate SND_SOC_SDCA || !SND_SOC_SDCA + +config SND_SOC_SDCA_IRQ_HANDLER + tristate + depends on SOUNDWIRE + help + This option enables support for the SDCA IRQ handler. diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile new file mode 100644 index 00000000000000..ffe981306ede88 --- /dev/null +++ b/sound/soc/sdca/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +snd-soc-sdca-objs := sdca_functions.o sdca_device.o + +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o + +snd-soc-sdca-irq-handler-objs := sdca_interrupts.o + +obj-$(CONFIG_SND_SOC_SDCA_IRQ_HANDLER) += snd-soc-sdca-irq-handler.o diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c new file mode 100644 index 00000000000000..ef154087b7e0c4 --- /dev/null +++ b/sound/soc/sdca/sdca_device.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2024 Intel Corporation + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include + +void sdca_lookup_interface_revision(struct sdw_slave *slave) +{ + struct fwnode_handle *fwnode = slave->dev.fwnode; + + /* + * if this property is not present, then the sdca_interface_revision will + * remain zero, which will be considered as 'not defined' or 'invalid'. + */ + fwnode_property_read_u32(fwnode, "mipi-sdw-sdca-interface-revision", + &slave->sdca_data.interface_revision); +} +EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, SND_SOC_SDCA); + +bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk) +{ + struct sdw_slave_id *id = &slave->id; + int i; + + switch (quirk) { + case SDCA_QUIRKS_RT712_VB: + /* + * The RT712_VA relies on the v06r04 draft, and the + * RT712_VB on a more recent v08r01 draft. + */ + if (slave->sdca_data.interface_revision < 0x0801) + return false; + + if (id->mfg_id != 0x025d) + return false; + + if (id->part_id != 0x712 && + id->part_id != 0x713 && + id->part_id != 0x716 && + id->part_id != 0x717) + return false; + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + if (slave->sdca_data.sdca_func[i].type == + SDCA_FUNCTION_TYPE_SMART_MIC) + return true; + } + + break; + default: + break; + } + return false; +} +EXPORT_SYMBOL_NS(sdca_device_quirk_match, SND_SOC_SDCA); diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c new file mode 100644 index 00000000000000..e5810578505468 --- /dev/null +++ b/sound/soc/sdca/sdca_functions.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2024 Intel Corporation + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include + +static int patch_sdca_function_type(struct device *dev, + u32 interface_revision, + u32 *function_type, + const char **function_name) +{ + unsigned long function_type_patch = 0; + + /* + * Unfortunately early SDCA specifications used different indices for Functions, + * for backwards compatibility we have to reorder the values found + */ + if (interface_revision >= 0x0801) + goto skip_early_draft_order; + + switch (*function_type) { + case 1: + function_type_patch = SDCA_FUNCTION_TYPE_SMART_AMP; + break; + case 2: + function_type_patch = SDCA_FUNCTION_TYPE_SMART_MIC; + break; + case 3: + function_type_patch = SDCA_FUNCTION_TYPE_SPEAKER_MIC; + break; + case 4: + function_type_patch = SDCA_FUNCTION_TYPE_UAJ; + break; + case 5: + function_type_patch = SDCA_FUNCTION_TYPE_RJ; + break; + case 6: + function_type_patch = SDCA_FUNCTION_TYPE_HID; + break; + default: + dev_warn(dev, "%s: SDCA version %#x unsupported function type %d, skipped\n", + __func__, interface_revision, *function_type); + return -EINVAL; + } + +skip_early_draft_order: + if (function_type_patch) + *function_type = function_type_patch; + + /* now double-check the values */ + switch (*function_type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + *function_name = SDCA_FUNCTION_TYPE_SMART_AMP_NAME; + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + *function_name = SDCA_FUNCTION_TYPE_SMART_MIC_NAME; + break; + case SDCA_FUNCTION_TYPE_UAJ: + *function_name = SDCA_FUNCTION_TYPE_UAJ_NAME; + break; + case SDCA_FUNCTION_TYPE_HID: + *function_name = SDCA_FUNCTION_TYPE_HID_NAME; + break; + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + case SDCA_FUNCTION_TYPE_RJ: + case SDCA_FUNCTION_TYPE_IMP_DEF: + dev_warn(dev, "%s: found unsupported SDCA function type %d, skipped\n", + __func__, *function_type); + return -EINVAL; + default: + dev_err(dev, "%s: found invalid SDCA function type %d, skipped\n", + __func__, *function_type); + return -EINVAL; + } + + dev_info(dev, "%s: found SDCA function %s (type %d)\n", + __func__, *function_name, *function_type); + + return 0; +} + +static int find_sdca_function(struct acpi_device *adev, void *data) +{ + struct fwnode_handle *function_node = acpi_fwnode_handle(adev); + struct sdca_device_data *sdca_data = data; + struct device *dev = &adev->dev; + struct fwnode_handle *control5; /* used to identify function type */ + const char *function_name; + u32 function_type; + int func_index; + u64 addr; + int ret; + + if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) { + dev_err(dev, "%s: maximum number of functions exceeded\n", __func__); + return -EINVAL; + } + + /* + * The number of functions cannot exceed 8, we could use + * acpi_get_local_address() but the value is stored as u64 so + * we might as well avoid casts and intermediate levels + */ + ret = acpi_get_local_u64_address(adev->handle, &addr); + if (ret < 0) + return ret; + + if (!addr) { + dev_err(dev, "%s: no addr\n", __func__); + return -ENODEV; + } + + /* + * Extracting the topology type for an SDCA function is a + * convoluted process. + * The Function type is only visible as a result of a read + * from a control. In theory this would mean reading from the hardware, + * but the SDCA/DisCo specs defined the notion of "DC value" - a constant + * represented with a DSD subproperty. + * Drivers have to query the properties for the control + * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05) + */ + control5 = fwnode_get_named_child_node(function_node, + "mipi-sdca-control-0x5-subproperties"); + if (!control5) + return -ENODEV; + + ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value", + &function_type); + + fwnode_handle_put(control5); + + if (ret < 0) { + dev_err(dev, "%s: the function type can only be determined from ACPI information\n", + __func__); + return ret; + } + + ret = patch_sdca_function_type(dev, sdca_data->interface_revision, + &function_type, &function_name); + if (ret < 0) + return ret; + + /* store results */ + func_index = sdca_data->num_functions; + sdca_data->sdca_func[func_index].adr = addr; + sdca_data->sdca_func[func_index].type = function_type; + sdca_data->sdca_func[func_index].name = function_name; + sdca_data->num_functions++; + + return 0; +} + +void sdca_lookup_functions(struct sdw_slave *slave) +{ + struct device *dev = &slave->dev; + struct acpi_device *adev = to_acpi_device_node(dev->fwnode); + + acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data); +} +EXPORT_SYMBOL_NS(sdca_lookup_functions, SND_SOC_SDCA); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SDCA library"); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c new file mode 100644 index 00000000000000..300b0b9d96555c --- /dev/null +++ b/sound/soc/sdca/sdca_interrupts.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2024 Intel Corporation + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include + +/** + * sdca_interrupt_info_alloc() - Helper to allocate the 'struct sdca_interrupt_info' + * @slave: the SoundWire peripheral + * + * Note: this helper is intended to be called in the SoundWire driver probe() callback. + */ + +int sdca_interrupt_info_alloc(struct sdw_slave *slave) +{ + struct sdca_interrupt_info *interrupt_info; + struct device *dev = &slave->dev; + + interrupt_info = devm_kzalloc(dev, sizeof(*interrupt_info), GFP_KERNEL); + if (!interrupt_info) + return -ENOMEM; + + mutex_init(&interrupt_info->irqs_lock); + + slave->sdca_data.interrupt_info = interrupt_info; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_interrupt_info_alloc, SND_SOC_SDCA_IRQ_HANDLER); + +/** + * sdca_interrupt_info_release() - Helper to release the 'struct sdca_interrupt_info' + * @slave: the SoundWire peripheral + * + * Note: this helper is intended to be called in the SoundWire driver remove() callback. + */ +void sdca_interrupt_info_release(struct sdw_slave *slave) +{ + struct sdca_interrupt_info *interrupt_info; + + if (!slave->sdca_data.interrupt_info) + return; + + interrupt_info = slave->sdca_data.interrupt_info; + mutex_destroy(&interrupt_info->irqs_lock); + + /* no memory to free since we used devm_ in the alloc */ +} +EXPORT_SYMBOL_NS(sdca_interrupt_info_release, SND_SOC_SDCA_IRQ_HANDLER); + +/** + * sdca_interrupt_info_reset() - Helper to reset internal SDCA interrupt status + * @slave: the SoundWire peripheral + * + * Note: SoundWire peripherals could be reset and/or re-attach on the bus. + * This helper is intended to be called during the 'update_status' callback to + * reconcile the internal state of the 'struct sdca_interrupt_info' + */ +int sdca_interrupt_info_reset(struct sdw_slave *slave) +{ + struct sdca_interrupt_info *interrupt_info; + + if (!slave->sdca_data.interrupt_info) + return -ENODEV; + + interrupt_info = slave->sdca_data.interrupt_info; + + /* interrupts need to be re-enabled after a reset */ + interrupt_info->enabled_interrupt_mask = 0; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_interrupt_info_reset, SND_SOC_SDCA_IRQ_HANDLER); + +/** + * sdca_interrupt_initialize() - device-level initialization of SDCA + * interrupts + * @slave: the SoundWire peripheral + * @supported_hw_register_mask: One bit per supported SDCA interrupt register. + * Valid values as in GENMASK(3, 0) since there are up to 3 registers in + * hardware. + */ +int sdca_interrupt_initialize(struct sdw_slave *slave, + int supported_hw_register_mask) +{ + struct sdca_interrupt_info *interrupt_info; + struct device *dev = &slave->dev; + + if (!slave->sdca_data.interrupt_info) + return -ENODEV; + + if (supported_hw_register_mask & ~GENMASK(3, 0)) { + dev_err(dev, "%s: invalid supported_hw_register_mask: %#x\n", + __func__, supported_hw_register_mask); + return -EINVAL; + } + + interrupt_info = slave->sdca_data.interrupt_info; + interrupt_info->supported_hw_register_mask = supported_hw_register_mask; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_interrupt_initialize, SND_SOC_SDCA_IRQ_HANDLER); + +/** + * sdca_interrupt_register_source() - Helper to register a SDCA interrupt source interrupts + * @slave: the SoundWire peripheral + * @source: source-specific information and callback, providing an opaque interface + * with the bus interrupt handling core + * + * Note: to prevent race conditions, the code relies on the 'irq_lock' mutex + */ +int sdca_interrupt_register_source(struct sdw_slave *slave, + struct sdca_interrupt_source *source) +{ + struct sdca_interrupt_info *interrupt_info; + struct device *dev = &slave->dev; + int index; + + if (!slave->sdca_data.interrupt_info) + return -ENODEV; + + if (!source) + return -EINVAL; + + index = source->index; + if (index < 0 && index >= SDCA_MAX_INTERRUPTS) { + dev_err(dev, + "%s: invalid source index %d\n", + __func__, source->index); + return -EINVAL; + } + + if (!source->callback) { + dev_err(dev, + "%s: no callback provided for source index %d\n", + __func__, source->index); + return -EINVAL; + } + + if (!source->context) { + dev_err(dev, + "%s: no context provided for source index %d\n", + __func__, source->index); + return -EINVAL; + } + + interrupt_info = slave->sdca_data.interrupt_info; + + mutex_lock(&interrupt_info->irqs_lock); + + interrupt_info->sources[index] = source; + interrupt_info->registered_source_mask |= BIT(source->index); + + mutex_unlock(&interrupt_info->irqs_lock); + + return 0; +} +EXPORT_SYMBOL_NS(sdca_interrupt_register_source, SND_SOC_SDCA_IRQ_HANDLER); + +static int sdca_interrupt_register_mask_rmw(struct sdw_slave *slave, + int reg_index, + u8 byte_mask, + int enable) +{ + struct device *dev = &slave->dev; + u8 mask; + int ret; + + ret = sdw_read_no_pm(slave, SDW_SCP_SDCA_INTMASK1 + reg_index); + if (ret < 0) { + dev_err(dev, + "%s: read from SDW_SCP_SDCA_INT_%d failed: %d\n", + __func__, reg_index, ret); + goto io_error; + } + mask = ret; + + if (enable) + mask |= byte_mask; + else + mask &= byte_mask; + + ret = sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1 + reg_index, mask); + if (ret < 0) + dev_err(dev, + "%s: write to SDW_SCP_SDCA_INT_%d failed: %d\n", + __func__, reg_index, ret); + +io_error: + return ret; +} + +/** + * sdca_interrupt_enable() - Helper to enable/disable SDCA interrupt sources + * @slave: the SoundWire peripheral + * @source_mask: a bitmask of all interrupt sources to enable/disable + * @enable: boolean to enable/disable + * + * Note: to prevent race conditions, the code relies on the 'irq_lock' mutex + */ +int sdca_interrupt_enable(struct sdw_slave *slave, + u32 source_mask, + bool enable) +{ + struct sdca_interrupt_info *interrupt_info; + struct device *dev = &slave->dev; + unsigned long hw_register_mask; + int max_register; + int max_source; + int reg_index; + int ret = 0; + + if (!slave->sdca_data.interrupt_info) + return -ENODEV; + + interrupt_info = slave->sdca_data.interrupt_info; + + /* check first if the interrupt masks are consistent */ + hw_register_mask = interrupt_info->supported_hw_register_mask; + if (!hw_register_mask) { + dev_err(dev, + "%s: supported_hw_register mask not initialized\n", + __func__); + return -EINVAL; + } + + if (!source_mask) { + dev_err(dev, + "%s: source mask not set\n", + __func__); + return -EINVAL; + } + + max_register = __fls(hw_register_mask); /* __fls is zero-based, values should be 0..3 */ + max_source = __fls(source_mask); /* __fls is zero-based, values should be 0..31 */ + + if (max_source >= (8 << max_register)) { + dev_err(dev, + "%s: source mask %#x incompatible with supported registers %#lx\n", + __func__, source_mask, hw_register_mask); + return -EINVAL; + } + + /* now start the interrupt mask updates */ + mutex_lock(&interrupt_info->irqs_lock); + + for_each_set_bit(reg_index, &hw_register_mask, 32) { + int shift = reg_index << 3; + int source_mask_byte = (source_mask >> shift) & GENMASK(7, 0); + + if (!source_mask_byte) + continue; + + ret = sdca_interrupt_register_mask_rmw(slave, reg_index, + source_mask_byte, + enable); + if (ret < 0) + goto unlock; + } + + /* + * Almost done, keep track of the combined interrupt mask used to + * filter interrupts in the handler. + */ + if (enable) + interrupt_info->enabled_interrupt_mask |= source_mask; + else + interrupt_info->enabled_interrupt_mask &= ~source_mask; + +unlock: + mutex_unlock(&interrupt_info->irqs_lock); + + return ret; +} +EXPORT_SYMBOL_NS(sdca_interrupt_enable, SND_SOC_SDCA_IRQ_HANDLER); + +void sdca_interrupt_clear_history(struct sdw_slave *slave, u32 preserve_mask) +{ + struct sdca_interrupt_info *interrupt_info; + + interrupt_info = slave->sdca_data.interrupt_info; + + /* + * Clear all history except for the interrupts set in preserve_mask. + * This is very useful for SDCA UMP processing, where the + * interrupt is only thrown once when the ownership changes to + * HOST. If the processing happens in a work queue, and a new interrupt + * cancels the work queue, the interrupt will not be signaled again + */ + interrupt_info->detected_interrupt_mask &= preserve_mask; +} +EXPORT_SYMBOL_NS(sdca_interrupt_clear_history, SND_SOC_SDCA_IRQ_HANDLER); + +/* helper called with the 'irq_lock' mutex held */ +static int sdca_interrupt_register_handler(struct sdw_slave *slave, + struct sdca_interrupt_info *interrupt_info, + int reg_index) +{ + struct device *dev = &slave->dev; + unsigned long status_filtered; + u32 registered_source_mask; + unsigned long status2; + unsigned long status; + const int retry = 3; + int count = 0; + int bit; + int ret; + + ret = sdw_read_no_pm(slave, SDW_SCP_SDCA_INT1 + reg_index); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: read of SDW_SCP_SDCA_INT%d failed: %d\n", + __func__, reg_index + 1, ret); + goto io_error; + } + status = ret; + + if (!status) + return 0; + + /* + * SDCA interrupt status bit may be set even if the interrupt is not enabled. + * The interrupt handler should only take care of registered sources that the + * codec driver cares about + */ + registered_source_mask = interrupt_info->registered_source_mask >> (reg_index << 3); + registered_source_mask &= 0xff; + if (status & ~registered_source_mask) + dev_dbg_ratelimited(dev, + "SDW_SCP_SDCA_INT%d interrupt status %#lx, registered sources %#x, unregistered sources will be ignored\n", + reg_index + 1, status, registered_source_mask); + status_filtered = status & registered_source_mask; + + do { + /* + * Record detected interrupt sources, source-specific actions + * will be taken after all interrupts have been cleared. + */ + + for_each_set_bit(bit, &status_filtered, 8) { + int index = (reg_index << 3) + bit; + + interrupt_info->detected_interrupt_mask |= BIT(index); + } + + /* clear the interrupts, including unregistered ones, for this register */ + ret = sdw_write_no_pm(slave, + SDW_SCP_SDCA_INT1 + reg_index, + status); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: write to SDW_SCP_SDCA_INT%d failed: %d\n", + __func__, reg_index + 1, ret); + goto io_error; + } + + /* + * The SoundWire specification requires an additional read to make sure + * no interrupts are lost + */ + ret = sdw_read_no_pm(slave, SDW_SCP_SDCA_INT1 + reg_index); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: re-read of SDW_SCP_SDCA_INT%d failed: %d\n", + __func__, reg_index + 1, ret); + goto io_error; + } + status2 = ret; + + /* filter to limit loop to interrupts identified in the first status read */ + status &= status2; + status_filtered = status & registered_source_mask; + + count++; + } while (status_filtered && (count < retry)); + + if (count == retry) + dev_warn_ratelimited(dev, + "%s: Reached max_retry %d on SDW_SCP_SDCA_INT%d\n", + __func__, retry, reg_index + 1); +io_error: + return ret; +} + +int sdca_interrupt_handler(struct sdw_slave *slave) +{ + struct device *dev = &slave->dev; + struct sdca_interrupt_info *interrupt_info; + unsigned long registered_source_mask; + unsigned long enabled_register_mask; + unsigned long hw_register_mask; + unsigned long detected_mask; + int index; + int reg_index; + int ret = 0; + + interrupt_info = slave->sdca_data.interrupt_info; + if (!interrupt_info) + return -ENODEV; + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&interrupt_info->irqs_lock); + + /* check first if the interrupt masks are consistent */ + registered_source_mask = interrupt_info->registered_source_mask; + if (!registered_source_mask) { + dev_err(dev, "%s: no interrupt sources registered\n", + __func__); + ret = -EINVAL; + goto unlock; + } + + hw_register_mask = interrupt_info->supported_hw_register_mask; + if (!hw_register_mask) { + dev_err(dev, "%s: supported register mask not initialized\n", + __func__); + ret = -EINVAL; + goto unlock; + } + + /* + * Optimization: filter which registers needs to be checked to + * avoid useless reads. There could be cases where the device + * supports M interrupts but only N sources have been registered + * by Function drivers. + */ + enabled_register_mask = 0; + for_each_set_bit(reg_index, &hw_register_mask, 8) { + int shift = reg_index << 3; + int source_mask_byte = (registered_source_mask >> shift) & GENMASK(7, 0); + + if (!source_mask_byte) + continue; + enabled_register_mask |= BIT(reg_index); + } + + for_each_set_bit(reg_index, &enabled_register_mask, 8) { + ret = sdca_interrupt_register_handler(slave, + interrupt_info, + reg_index); + if (ret < 0) + goto unlock; + } + + /* + * Handle source-specific tasks. + */ + detected_mask = interrupt_info->detected_interrupt_mask; + for_each_set_bit(index, &detected_mask, 32) { + void *context = interrupt_info->sources[index]->context; + + /* + * There could be a racy window where the interrupts are disabled + * between the time the peripheral signals its alert status and + * the time where this interrupt handler is scheduled. + * In this case we don't invoke the callbacks since presumably a + * higher-level transition such as system suspend is going on. + */ + if (BIT(index) & interrupt_info->enabled_interrupt_mask) + interrupt_info->sources[index]->callback(context); + } + +unlock: + mutex_unlock(&interrupt_info->irqs_lock); + + return ret; +} +EXPORT_SYMBOL_NS(sdca_interrupt_handler, SND_SOC_SDCA_IRQ_HANDLER); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SDCA IRQ handler library"); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index a6070f822eb9e4..47fb807a697ab4 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -10,6 +10,7 @@ #include #include #include +#include #include static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { @@ -178,8 +179,15 @@ struct asoc_sdw_codec_info codec_info_list[] = { .widgets = generic_jack_widgets, .num_widgets = ARRAY_SIZE(generic_jack_widgets), }, + { + .direction = {false, true}, + .dai_name = "rt712-sdca-aif3", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, + }, }, - .dai_num = 1, + .dai_num = 2, }, { .part_id = 0x1713, @@ -1044,6 +1052,24 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks } EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, SND_SOC_SDW_UTILS); +static int asoc_sdw_get_dai_type(u32 type) { + switch (type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + return SOC_SDW_DAI_TYPE_AMP; + case SDCA_FUNCTION_TYPE_SMART_MIC: + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + return SOC_SDW_DAI_TYPE_MIC; + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: + case SDCA_FUNCTION_TYPE_SIMPLE_JACK: + return SOC_SDW_DAI_TYPE_JACK; + default: + return -EINVAL; + } +} + int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, @@ -1055,8 +1081,10 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; struct asoc_sdw_endpoint *soc_end = soc_ends; + struct snd_soc_dai_link_component dlc; + struct snd_soc_dai *codec_dai; int num_dais = 0; - int i, j; + int i, j, k; int ret; for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { @@ -1089,6 +1117,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, if (!codec_name) return -ENOMEM; + dlc.name = codec_name; dev_dbg(dev, "Adding prefix %s for %s\n", adr_dev->name_prefix, codec_name); @@ -1106,6 +1135,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; struct asoc_sdw_dailink *soc_dai; + struct sdw_slave *slave; int stream; adr_end = &adr_dev->endpoints[j]; @@ -1115,6 +1145,35 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, if (dai_info->quirk && !(dai_info->quirk & ctx->mc_quirk)) continue; + dlc.dai_name = dai_info->dai_name; + codec_dai = snd_soc_find_dai_with_mutex(&dlc); + if (!codec_dai) { + dev_err(dev, "codec dai %s not registered yet\n", + dlc.dai_name); + return -EPROBE_DEFER; + } + + slave = dev_to_sdw_dev(codec_dai->dev); + if (!slave) + return -EINVAL; + + for (k = 0; k < slave->sdca_data.num_functions; k++) { + int dai_type = asoc_sdw_get_dai_type( + slave->sdca_data.sdca_func[k].type); + dev_dbg(&slave->dev, "function: %s dai_type %d\n", + slave->sdca_data.sdca_func[k].name, dai_type); + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d found\n", + dai_type); + break; + } + } + if (k == slave->sdca_data.num_functions) { + dev_dbg(&slave->dev, "DAI type %d not found, skip endpoint\n", + dai_info->dai_type); + continue; + } + dev_dbg(dev, "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n", ffs(adr_link->mask) - 1, adr_dev->adr, diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 6d693b2ad5a35b..270f9777942f02 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -131,8 +131,7 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); /* Check if all Slaves defined on the link can be found */ bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, const struct snd_soc_acpi_link_adr *link, - struct sdw_extended_slave_id *ids, - int num_slaves) + struct sdw_peripherals *peripherals) { unsigned int part_id, link_id, unique_id, mfg_id, version; int i, j, k; @@ -146,22 +145,25 @@ bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, link_id = SDW_DISCO_LINK_ID(adr); version = SDW_VERSION(adr); - for (j = 0; j < num_slaves; j++) { + for (j = 0; j < peripherals->num_peripherals; j++) { + struct sdw_slave *peripheral = peripherals->array[j]; + /* find out how many identical parts were reported on that link */ - if (ids[j].link_id == link_id && - ids[j].id.part_id == part_id && - ids[j].id.mfg_id == mfg_id && - ids[j].id.sdw_version == version) + if (peripheral->bus->link_id == link_id && + peripheral->id.part_id == part_id && + peripheral->id.mfg_id == mfg_id && + peripheral->id.sdw_version == version) reported_part_count++; } - for (j = 0; j < num_slaves; j++) { + for (j = 0; j < peripherals->num_peripherals; j++) { + struct sdw_slave *peripheral = peripherals->array[j]; int expected_part_count = 0; - if (ids[j].link_id != link_id || - ids[j].id.part_id != part_id || - ids[j].id.mfg_id != mfg_id || - ids[j].id.sdw_version != version) + if (peripheral->bus->link_id != link_id || + peripheral->id.part_id != part_id || + peripheral->id.mfg_id != mfg_id || + peripheral->id.sdw_version != version) continue; /* find out how many identical parts are expected */ @@ -180,7 +182,7 @@ bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, */ unique_id = SDW_UNIQUE_ID(adr); if (reported_part_count == 1 || - ids[j].id.unique_id == unique_id) { + peripheral->id.unique_id == unique_id) { dev_dbg(dev, "found part_id %#x at link %d\n", part_id, link_id); break; } @@ -189,7 +191,7 @@ bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, part_id, reported_part_count, expected_part_count, link_id); } } - if (j == num_slaves) { + if (j == peripherals->num_peripherals) { dev_dbg(dev, "Slave part_id %#x not found\n", part_id); return false; } diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index dbcaac84cb73af..fc792956bb9762 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -145,8 +145,7 @@ static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev * link = mach->links; for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, - acp_data->sdw->ids, - acp_data->sdw->num_slaves)) + acp_data->sdw->peripherals)) break; } if (i == acp_data->info.count || !link->num_adr) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e4cb4ffc727048..38921c0db84eed 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1064,7 +1064,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev { struct snd_sof_pdata *pdata = sdev->pdata; const struct snd_soc_acpi_link_adr *link; - struct sdw_extended_slave_id *ids; + struct sdw_peripherals *peripherals; struct snd_soc_acpi_mach *mach; struct sof_intel_hda_dev *hdev; u32 link_mask; @@ -1083,7 +1083,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev return NULL; } - if (!hdev->sdw->num_slaves) { + if (!hdev->sdw->peripherals || !hdev->sdw->peripherals->num_peripherals) { dev_warn(sdev->dev, "No SoundWire peripheral detected in ACPI tables\n"); return NULL; } @@ -1119,13 +1119,13 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev * are not found on this link. */ if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, - hdev->sdw->ids, - hdev->sdw->num_slaves)) + hdev->sdw->peripherals)) break; } /* Found if all Slaves are checked */ if (i == hdev->info.count || !link->num_adr) - break; + if (!mach->machine_check || mach->machine_check(hdev->sdw)) + break; } if (mach && mach->link_mask) { mach->mach_params.links = mach->links; @@ -1136,10 +1136,13 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev } dev_info(sdev->dev, "No SoundWire machine driver found for the ACPI-reported configuration:\n"); - ids = hdev->sdw->ids; - for (i = 0; i < hdev->sdw->num_slaves; i++) + peripherals = hdev->sdw->peripherals; + for (i = 0; i < peripherals->num_peripherals; i++) dev_info(sdev->dev, "link %d mfg_id 0x%04x part_id 0x%04x version %#x\n", - ids[i].link_id, ids[i].id.mfg_id, ids[i].id.part_id, ids[i].id.sdw_version); + peripherals->array[i]->bus->link_id, + peripherals->array[i]->id.mfg_id, + peripherals->array[i]->id.part_id, + peripherals->array[i]->id.sdw_version); return NULL; }